diff options
author | Jeff Sharkey <jsharkey@android.com> | 2015-07-16 09:13:52 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2015-07-16 15:30:45 -0700 |
commit | ed2fe57c2509d0d784ba7dbce1deef21afb2a612 (patch) | |
tree | 7c99f8b6871f3271724138b824381dc99baa43bf /sdcard | |
parent | 0fdee3938ed58dac86755d04bc2a7d280ba158e7 (diff) | |
download | system_core-ed2fe57c2509d0d784ba7dbce1deef21afb2a612.zip system_core-ed2fe57c2509d0d784ba7dbce1deef21afb2a612.tar.gz system_core-ed2fe57c2509d0d784ba7dbce1deef21afb2a612.tar.bz2 |
Use single tree for multiple storage views.
Instead of having each view build and maintain its own tree
representing the underlying storage, switch to building a single tree
that each view augments with GID/mode specific behavior.
This has the nice property of a single file always having the same
node ID when presented across multiple views, giving us a firm handle
that we can use to invalidate kernel caches.
Specifically, when a file is deleted through one view, we now tell
the kernel to invalidate that file in the other two views.
Bug: 22477678, 22375891
Change-Id: I3ff041d549d41040839cde9773504719a508219f
Diffstat (limited to 'sdcard')
-rw-r--r-- | sdcard/sdcard.c | 211 |
1 files changed, 140 insertions, 71 deletions
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 4b8e0c0..ba4d70c 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -151,7 +151,6 @@ struct node { perm_t perm; userid_t userid; uid_t uid; - gid_t gid; mode_t mode; struct node *next; /* per-dir sibling list */ @@ -192,22 +191,13 @@ struct fuse_global { gid_t gid; bool multi_user; - Hashmap* package_to_appid; -}; - -/* Single FUSE mount */ -struct fuse { - struct fuse_global* global; - char source_path[PATH_MAX]; - char dest_path[PATH_MAX]; char obb_path[PATH_MAX]; + Hashmap* package_to_appid; + __u64 next_generation; - int fd; struct node root; - gid_t gid; - mode_t mask; /* Used to allocate unique inode numbers for fuse nodes. We use * a simple counter based scheme where inode numbers from deleted @@ -227,6 +217,22 @@ struct fuse { * Accesses must be guarded by |lock|. */ __u32 inode_ctr; + + struct fuse* fuse_default; + struct fuse* fuse_read; + struct fuse* fuse_write; +}; + +/* Single FUSE mount */ +struct fuse { + struct fuse_global* global; + + char dest_path[PATH_MAX]; + + int fd; + + gid_t gid; + mode_t mask; }; /* Private data used by a single FUSE handler */ @@ -386,8 +392,8 @@ static char* find_file_within(const char* path, const char* name, return actual; } -static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const struct node* node) -{ +static void attr_from_stat(struct fuse* fuse, struct fuse_attr *attr, + const struct stat *s, const struct node* node) { attr->ino = node->ino; attr->size = s->st_size; attr->blocks = s->st_blocks; @@ -401,12 +407,26 @@ static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const s attr->nlink = s->st_nlink; attr->uid = node->uid; - attr->gid = node->gid; + + if (fuse->gid == AID_SDCARD_RW) { + /* As an optimization, certain trusted system components only run + * as owner but operate across all users. Since we're now handing + * out the sdcard_rw GID only to trusted apps, we're okay relaxing + * the user boundary enforcement for the default view. The UIDs + * assigned to app directories are still multiuser aware. */ + attr->gid = AID_SDCARD_RW; + } else { + attr->gid = multiuser_get_uid(node->userid, fuse->gid); + } /* Filter requested mode based on underlying file, and * pass through file type. */ + int visible_mode = node->mode; + if (node->perm != PERM_PRE_ROOT) { + visible_mode = visible_mode & ~fuse->mask; + } int owner_mode = s->st_mode & 0700; - int filtered_mode = node->mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); + int filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); attr->mode = (attr->mode & S_IFMT) | filtered_mode; } @@ -432,7 +452,6 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, node->perm = PERM_INHERIT; node->userid = parent->userid; node->uid = parent->uid; - node->gid = parent->gid; node->mode = parent->mode; /* Derive custom permissions based on parent and current node */ @@ -444,16 +463,6 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, /* Legacy internal layout places users at top level */ node->perm = PERM_ROOT; node->userid = strtoul(node->name, NULL, 10); - if (fuse->gid == AID_SDCARD_RW) { - /* As an optimization, certain trusted system components only run - * as owner but operate across all users. Since we're now handing - * out the sdcard_rw GID only to trusted apps, we're okay relaxing - * the user boundary enforcement for the default view. The UIDs - * assigned to app directories are still multiuser aware. */ - node->gid = fuse->gid; - } else { - node->gid = multiuser_get_uid(node->userid, fuse->gid); - } node->mode = 0771; break; case PERM_ROOT: @@ -475,8 +484,8 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, node->perm = PERM_ANDROID_OBB; node->mode = 0771; /* Single OBB directory is always shared */ - node->graft_path = fuse->obb_path; - node->graft_pathlen = strlen(fuse->obb_path); + node->graft_path = fuse->global->obb_path; + node->graft_pathlen = strlen(fuse->global->obb_path); } else if (!strcasecmp(node->name, "media")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID_MEDIA; @@ -493,8 +502,6 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, node->mode = 0770; break; } - - node->mode = node->mode & ~fuse->mask; } /* Kernel has already enforced everything we returned through @@ -535,7 +542,7 @@ struct node *create_node_locked(struct fuse* fuse, // Detect overflows in the inode counter. "4 billion nodes should be enough // for everybody". - if (fuse->inode_ctr == 0) { + if (fuse->global->inode_ctr == 0) { ERROR("No more inode numbers available"); return NULL; } @@ -561,8 +568,8 @@ struct node *create_node_locked(struct fuse* fuse, } node->namelen = namelen; node->nid = ptr_to_id(node); - node->ino = fuse->inode_ctr++; - node->gen = fuse->next_generation++; + node->ino = fuse->global->inode_ctr++; + node->gen = fuse->global->next_generation++; node->deleted = false; @@ -616,7 +623,7 @@ static int rename_node_locked(struct node *node, const char *name, static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid) { if (nid == FUSE_ROOT_ID) { - return &fuse->root; + return &fuse->global->root; } else { return id_to_ptr(nid); } @@ -708,7 +715,7 @@ static int fuse_reply_entry(struct fuse* fuse, __u64 unique, return -ENOMEM; } memset(&out, 0, sizeof(out)); - attr_from_stat(&out.attr, &s, node); + attr_from_stat(fuse, &out.attr, &s, node); out.attr_valid = 10; out.entry_valid = 10; out.nodeid = node->nid; @@ -728,12 +735,43 @@ static int fuse_reply_attr(struct fuse* fuse, __u64 unique, const struct node* n return -errno; } memset(&out, 0, sizeof(out)); - attr_from_stat(&out.attr, &s, node); + attr_from_stat(fuse, &out.attr, &s, node); out.attr_valid = 10; fuse_reply(fuse, unique, &out, sizeof(out)); return NO_STATUS; } +static void fuse_notify_delete(struct fuse* fuse, const __u64 parent, + const __u64 child, const char* name) { + struct fuse_out_header hdr; + struct fuse_notify_delete_out data; + struct iovec vec[3]; + size_t namelen = strlen(name); + int res; + + hdr.len = sizeof(hdr) + sizeof(data) + namelen + 1; + hdr.error = FUSE_NOTIFY_DELETE; + hdr.unique = 0; + + data.parent = parent; + data.child = child; + data.namelen = namelen; + data.padding = 0; + + vec[0].iov_base = &hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = &data; + vec[1].iov_len = sizeof(data); + vec[2].iov_base = (void*) name; + vec[2].iov_len = namelen + 1; + + res = writev(fuse->fd, vec, 3); + /* Ignore ENOENT, since other views may not have seen the entry */ + if (res < 0 && errno != ENOENT) { + ERROR("*** NOTIFY FAILED *** %d\n", errno); + } +} + static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header *hdr, const char* name) { @@ -931,7 +969,7 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, } if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) { char nomedia[PATH_MAX]; - snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->obb_path); + snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->global->obb_path); if (touch(nomedia, 0664) != 0) { ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno)); return -ENOENT; @@ -972,6 +1010,20 @@ static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, child_node->deleted = true; } pthread_mutex_unlock(&fuse->global->lock); + if (parent_node && child_node) { + /* Tell all other views that node is gone */ + TRACE("[%d] fuse_notify_delete parent=%"PRIx64", child=%"PRIx64", name=%s\n", + handler->token, (uint64_t) parent_node->nid, (uint64_t) child_node->nid, name); + if (fuse != fuse->global->fuse_default) { + fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_read) { + fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_write) { + fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name); + } + } return 0; } @@ -1006,6 +1058,20 @@ static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler, child_node->deleted = true; } pthread_mutex_unlock(&fuse->global->lock); + if (parent_node && child_node) { + /* Tell all other views that node is gone */ + TRACE("[%d] fuse_notify_delete parent=%"PRIx64", child=%"PRIx64", name=%s\n", + handler->token, (uint64_t) parent_node->nid, (uint64_t) child_node->nid, name); + if (fuse != fuse->global->fuse_default) { + fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_read) { + fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name); + } + if (fuse != fuse->global->fuse_write) { + fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name); + } + } return 0; } @@ -1201,12 +1267,12 @@ static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler, pthread_mutex_lock(&fuse->global->lock); TRACE("[%d] STATFS\n", handler->token); - res = get_node_path_locked(&fuse->root, path, sizeof(path)); + res = get_node_path_locked(&fuse->global->root, path, sizeof(path)); pthread_mutex_unlock(&fuse->global->lock); if (res < 0) { return -ENOENT; } - if (statfs(fuse->root.name, &stat) < 0) { + if (statfs(fuse->global->root.name, &stat) < 0) { return -errno; } memset(&out, 0, sizeof(out)); @@ -1690,30 +1756,9 @@ static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) { return -1; } - fuse->next_generation = 0; - fuse->inode_ctr = 1; fuse->gid = gid; fuse->mask = mask; - memset(&fuse->root, 0, sizeof(fuse->root)); - fuse->root.nid = FUSE_ROOT_ID; /* 1 */ - fuse->root.refcount = 2; - fuse->root.namelen = strlen(fuse->source_path); - fuse->root.name = strdup(fuse->source_path); - fuse->root.userid = 0; - fuse->root.uid = AID_ROOT; - fuse->root.gid = fuse->gid; - - if (fuse->global->multi_user) { - fuse->root.perm = PERM_PRE_ROOT; - fuse->root.mode = 0711; - snprintf(fuse->obb_path, sizeof(fuse->obb_path), "%s/obb", fuse->source_path); - } else { - fuse->root.perm = PERM_ROOT; - fuse->root.mode = 0771 & ~mask; - snprintf(fuse->obb_path, sizeof(fuse->obb_path), "%s/Android/obb", fuse->source_path); - } - return 0; } @@ -1743,23 +1788,49 @@ static void run(const char* source_path, const char* label, uid_t uid, global.uid = uid; global.gid = gid; global.multi_user = multi_user; + global.next_generation = 0; + global.inode_ctr = 1; - fuse_default.global = &global; - strcpy(fuse_default.source_path, source_path); - snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime_default/%s", label); + memset(&global.root, 0, sizeof(global.root)); + global.root.nid = FUSE_ROOT_ID; /* 1 */ + global.root.refcount = 2; + global.root.namelen = strlen(source_path); + global.root.name = strdup(source_path); + global.root.userid = 0; + global.root.uid = AID_ROOT; - fuse_read.global = &global; - strcpy(fuse_read.source_path, source_path); - snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime_read/%s", label); + strcpy(global.source_path, source_path); + + if (multi_user) { + global.root.perm = PERM_PRE_ROOT; + global.root.mode = 0711; + snprintf(global.obb_path, sizeof(global.obb_path), "%s/obb", source_path); + } else { + global.root.perm = PERM_ROOT; + global.root.mode = 0771; + snprintf(global.obb_path, sizeof(global.obb_path), "%s/Android/obb", source_path); + } + fuse_default.global = &global; + fuse_read.global = &global; fuse_write.global = &global; - strcpy(fuse_write.source_path, source_path); + + global.fuse_default = &fuse_default; + global.fuse_read = &fuse_read; + global.fuse_write = &fuse_write; + + snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime_default/%s", label); + snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime_read/%s", label); snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime_write/%s", label); handler_default.fuse = &fuse_default; handler_read.fuse = &fuse_read; handler_write.fuse = &fuse_write; + handler_default.token = 0; + handler_read.token = 1; + handler_write.token = 2; + umask(0); if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006) @@ -1784,9 +1855,7 @@ static void run(const char* source_path, const char* label, uid_t uid, } if (multi_user) { - fs_prepare_dir(fuse_default.obb_path, 0775, uid, gid); - fs_prepare_dir(fuse_read.obb_path, 0775, uid, gid); - fs_prepare_dir(fuse_write.obb_path, 0775, uid, gid); + fs_prepare_dir(global.obb_path, 0775, uid, gid); } if (pthread_create(&thread_default, NULL, start_handler, &handler_default) |