summaryrefslogtreecommitdiffstats
path: root/sdcard
diff options
context:
space:
mode:
authorPaul Eastham <eastham@google.com>2010-10-14 11:04:26 -0700
committerPaul Eastham <eastham@google.com>2010-10-14 11:04:26 -0700
commit11ccdb3be67b22f677065715ace68aabf371acc7 (patch)
treea623e33f50d3a5d61f0d76ff7fe97ab96f3e2a61 /sdcard
parenta4176578804e1b300f7652919161be035cf3cfd2 (diff)
downloadsystem_core-11ccdb3be67b22f677065715ace68aabf371acc7.tar.gz
system_core-11ccdb3be67b22f677065715ace68aabf371acc7.tar.bz2
system_core-11ccdb3be67b22f677065715ace68aabf371acc7.zip
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
Diffstat (limited to 'sdcard')
-rw-r--r--sdcard/sdcard.c105
1 files changed, 82 insertions, 23 deletions
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 8bb0c82cb..de630f5f6 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 @@ struct node {
__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 @@ int node_get_attr(struct node *node, struct fuse_attr *attr)
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 @@ struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64
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 @@ void fuse_init(struct fuse *fuse, int fd, const char *path)
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 @@ struct node *lookup_child_by_inode(struct node *node, __u64 nid)
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 @@ void node_release(struct node *node)
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 @@ void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *da
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 @@ void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *da
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;
}