Use readstr() for #device text buffers
[akaros.git] / kern / drivers / dev / gtfs.c
index 1983a57..9f82c77 100644 (file)
@@ -46,7 +46,10 @@ struct gtfs {
  * be_{length,mode,mtime} should be what the remote server thinks they are -
  * especially for length and mode.  The invariant is that e.g. the file's length
  * == be_length, and the qlock protects that invariant.  We don't care as much
- * about mtime, since some 9p servers just change that on their own. */
+ * about mtime, since some 9p servers just change that on their own.
+ *
+ * Also note that you can't trust be_length for directories.  You'll often get
+ * 4096 or 0, depending on the 9p server you're talking to. */
 struct gtfs_priv {
        struct chan                                     *be_walk;       /* never opened */
        struct chan                                     *be_read;
@@ -311,10 +314,6 @@ static size_t __gtfs_fsf_read(struct fs_file *f, void *ubuf, size_t n,
 {
        struct gtfs_priv *gp = fsf_to_gtfs_priv(f);
 
-       if (off >= gp->be_length) {
-               /* We can skip the RPC, since we know it will return zero (EOF). */
-               return 0;
-       }
        if (!gp->be_read)
                gp->be_read = cclone_and_open(gp->be_walk, O_READ);
        return devtab[gp->be_read->type].read(gp->be_read, ubuf, n, off);
@@ -505,6 +504,15 @@ static void gtfs_tf_create(struct tree_file *parent, struct tree_file *child,
        poperror();
 }
 
+static void gtfs_wstat_rename(struct fs_file *f, const char *name)
+{
+       struct dir dir;
+
+       init_empty_dir(&dir);
+       dir.name = (char*)name;
+       wstat_dir(f, &dir);
+}
+
 static void gtfs_tf_rename(struct tree_file *tf, struct tree_file *old_parent,
                            struct tree_file *new_parent, const char *name,
                            int flags)
@@ -512,9 +520,16 @@ static void gtfs_tf_rename(struct tree_file *tf, struct tree_file *old_parent,
        struct chan *tf_c = tf_to_gtfs_priv(tf)->be_walk;
        struct chan *np_c = tf_to_gtfs_priv(new_parent)->be_walk;
 
-       if (!devtab[tf_c->type].rename)
+       if (!devtab[tf_c->type].rename) {
+               /* 9p can handle intra-directory renames, though some Akaros #devices
+                * might throw. */
+               if (old_parent == new_parent) {
+                       gtfs_wstat_rename(&tf->file, name);
+                       return;
+               }
                error(EXDEV, "%s: %s doesn't support rename", devname(),
                      devtab[tf_c->type].name);
+       }
        devtab[tf_c->type].rename(tf_c, np_c, name, flags);
 }
 
@@ -522,10 +537,13 @@ static bool gtfs_tf_has_children(struct tree_file *parent)
 {
        struct dir dir[1];
 
-       assert(!tree_file_is_dir(parent));      /* TF bug */
-       /* any read should work, but there might be issues asking for something
-        * smaller than a dir. */
-       return gtfs_fsf_read(&parent->file, dir, sizeof(struct dir), 0) > 0;
+       assert(tree_file_is_dir(parent));       /* TF bug */
+       /* Any read should work, but there might be issues asking for something
+        * smaller than a dir.
+        *
+        * Note we use the unlocked read here.  The fs_file's qlock is held by our
+        * caller, and we reuse that qlock for the sync for reading/writing. */
+       return __gtfs_fsf_read(&parent->file, dir, sizeof(struct dir), 0) > 0;
 }
 
 struct tree_file_ops gtfs_tf_ops = {
@@ -799,7 +817,6 @@ static void gtfs_sync_gtfs(struct gtfs *gtfs)
        tfs_frontend_for_each(&gtfs->tfs, gtfs_sync_tf);
 }
 
-/* chan_ctl or something can hook into these functions */
 static void gtfs_sync_chan(struct chan *c)
 {
        gtfs_sync_tf(chan_to_tree_file(c));
@@ -822,7 +839,7 @@ static unsigned long gtfs_chan_ctl(struct chan *c, int op, unsigned long a1,
                        gtfs_sync_chan(c);
                return 0;
        default:
-               error(EINVAL, "%s does not support %d", __func__, op);
+               return tree_chan_ctl(c, op, a1, a2, a3, a4);
        }
 }
 
@@ -843,6 +860,7 @@ struct dev gtfs_devtab __devtab = {
        .write = tree_chan_write,
        .bwrite = devbwrite,
        .remove = gtfs_remove,
+       .rename = tree_chan_rename,
        .wstat = gtfs_wstat,
        .power = devpower,
        .chaninfo = devchaninfo,