Properly reflect RENAME ops in FUSE internal state
In response to a RENAME, we actually need to rename and move the virtual
node. To support this, filenames are now allocated separately, as reallocing
the whole node to accommodate a longer filename would break the direct
mapping of fhs and inodes to fuse pointers.
Change-Id: I71e5a965f875dedc5f58f9d182156734b29ca179
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 8bb0c82..de630f5 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -55,10 +55,6 @@
* kernel, you must rollback the refcount to reflect the reference the
* kernel did not actually acquire
*
- *
- * Bugs:
- *
- * - need to move/rename node on RENAME
*/
#define FUSE_TRACE 0
@@ -89,15 +85,15 @@
__u64 nid;
__u64 gen;
- struct node *next;
- struct node *child;
- struct node *all;
- struct node *parent;
+ struct node *next; /* per-dir sibling list */
+ struct node *child; /* first contained file by this dir */
+ struct node *all; /* global node list */
+ struct node *parent; /* containing directory */
__u32 refcount;
__u32 namelen;
- char name[1];
+ char *name;
};
struct fuse {
@@ -195,21 +191,30 @@
return 0;
}
+static void add_node_to_parent(struct node *node, struct node *parent) {
+ node->parent = parent;
+ node->next = parent->child;
+ parent->child = node;
+}
+
struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen)
{
struct node *node;
int namelen = strlen(name);
- node = calloc(1, sizeof(struct node) + namelen);
+ node = calloc(1, sizeof(struct node));
if (node == 0) {
return 0;
}
+ node->name = malloc(namelen + 1);
+ if (node->name == 0) {
+ free(node);
+ return 0;
+ }
node->nid = nid;
node->gen = gen;
- node->parent = parent;
- node->next = parent->child;
- parent->child = node;
+ add_node_to_parent(node, parent);
memcpy(node->name, name, namelen + 1);
node->namelen = namelen;
parent->refcount++;
@@ -217,6 +222,17 @@
return node;
}
+static char *rename_node(struct node *node, const char *name)
+{
+ node->namelen = strlen(name);
+ char *newname = realloc(node->name, node->namelen + 1);
+ if (newname == 0)
+ return 0;
+ node->name = newname;
+ memcpy(node->name, name, node->namelen + 1);
+ return node->name;
+}
+
void fuse_init(struct fuse *fuse, int fd, const char *path)
{
fuse->fd = fd;
@@ -233,8 +249,8 @@
fuse->root.all = 0;
fuse->root.refcount = 2;
- strcpy(fuse->root.name, path);
- fuse->root.namelen = strlen(fuse->root.name);
+ fuse->root.name = 0;
+ rename_node(&fuse->root, path);
}
static inline void *id_to_ptr(__u64 nid)
@@ -277,6 +293,27 @@
return 0;
}
+static struct node *remove_child(struct node *parent, __u64 nid)
+{
+ struct node *prev = 0;
+ struct node *node;
+
+ for (node = parent->child; node; node = node->next) {
+ if (node->nid == nid) {
+ if (prev) {
+ prev->next = node->next;
+ } else {
+ parent->child = node->next;
+ }
+ node->next = 0;
+ node->parent = 0;
+ return node;
+ }
+ prev = node;
+ }
+ return 0;
+}
+
struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name,
struct fuse_attr *attr)
{
@@ -332,8 +369,9 @@
node->next = 0;
/* TODO: remove debugging - poison memory */
- memset(node, 0xef, sizeof(*node) + strlen(node->name));
-
+ memset(node->name, 0xef, node->namelen);
+ free(node->name);
+ memset(node, 0xef, sizeof(*node));
free(node);
}
}
@@ -433,7 +471,7 @@
struct fuse_getattr_in *req = data;
struct fuse_attr_out out;
- TRACE("GETATTR flags=%x fh=%llx\n",req->getattr_flags, req->fh);
+ TRACE("GETATTR flags=%x fh=%llx\n", req->getattr_flags, req->fh);
memset(&out, 0, sizeof(out));
node_get_attr(node, &out.attr);
@@ -529,19 +567,40 @@
char *newname = oldname + strlen(oldname) + 1;
char *oldpath, oldbuffer[PATH_BUFFER_SIZE];
char *newpath, newbuffer[PATH_BUFFER_SIZE];
- struct node *newnode;
+ struct node *target;
+ struct node *newparent;
int res;
- newnode = lookup_by_inode(fuse, req->newdir);
- if (!newnode) {
+ TRACE("RENAME %s->%s @ %llx\n", oldname, newname, hdr->nodeid);
+
+ target = lookup_child_by_name(node, oldname);
+ if (!target) {
fuse_status(fuse, hdr->unique, -ENOENT);
return;
}
-
oldpath = node_get_path(node, oldbuffer, oldname);
- newpath = node_get_path(newnode, newbuffer, newname);
+
+ newparent = lookup_by_inode(fuse, req->newdir);
+ if (!newparent) {
+ fuse_status(fuse, hdr->unique, -ENOENT);
+ return;
+ }
+ newpath = node_get_path(newparent, newbuffer, newname);
+
+ if (!remove_child(node, target->nid)) {
+ ERROR("RENAME remove_child not found");
+ fuse_status(fuse, hdr->unique, -ENOENT);
+ return;
+ }
+ if (!rename_node(target, newname)) {
+ fuse_status(fuse, hdr->unique, -ENOMEM);
+ return;
+ }
+ add_node_to_parent(target, newparent);
res = rename(oldpath, newpath);
+ TRACE("RENAME result %d\n", res);
+
fuse_status(fuse, hdr->unique, res ? -errno : 0);
return;
}