/* copy derived state from parent inode */ static void inherit_derived_state(struct inode *parent, struct inode *child) { struct sdcardfs_inode_info *pi = SDCARDFS_I(parent); struct sdcardfs_inode_info *ci = SDCARDFS_I(child); ci->perm = PERM_INHERIT; ci->userid = pi->userid; ci->d_uid = pi->d_uid; ci->d_gid = pi->d_gid; ci->d_under_android = pi->d_under_android; }
void setup_derived_state_for_multiuser_gid(struct inode *inode, perm_t perm, userid_t userid, uid_t uid, gid_t gid, bool under_android) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); setup_derived_state(inode, perm, userid, uid, gid, under_android); info->d_gid = multiuser_get_uid(userid, gid); }
/* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, uid_t uid, gid_t gid, bool under_android) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); info->perm = perm; info->userid = userid; info->d_uid = uid; info->d_gid = gid; info->d_under_android = under_android; }
static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { struct dentry *lower_dentry; struct inode *inode; struct inode *lower_inode; struct path lower_path; struct dentry *parent; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct sdcardfs_inode_info *info; parent = dget_parent(dentry); if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, sbi->options.derive, 0, 0)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); dput(parent); return -EACCES; } dput(parent); inode = dentry->d_inode; sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; lower_inode = sdcardfs_lower_inode(inode); info = SDCARDFS_I(inode); if (!strcmp(dentry->d_name.name, "ApkScript")) printk(KERN_ERR "dj enter_getattr_Apk--lower_inode->i_mode=%o, inode->i_mode=%o, info->d_mode=%o\n",lower_inode->i_mode, inode->i_mode, info->d_mode); if(!strcmp(dentry->d_name.name, "ShellScript")) printk(KERN_ERR "dj enter_getattr_Shell--lower_inode->i_mode=%o, inode->i_mode=%o, info->d_mode=%o\n",lower_inode->i_mode, inode->i_mode, info->d_mode); /* need to get inode->i_mutex */ mutex_lock(&inode->i_mutex); sdcardfs_copy_inode_attr(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); /* if the dentry has been moved from other location * so, on this stage, its derived permission must be * rechecked from its private field. */ fix_derived_permission(inode); mutex_unlock(&inode->i_mutex); generic_fillattr(inode, stat); if (!strcmp(dentry->d_name.name, "ApkScript")) printk(KERN_ERR "dj_end_getattr_apk stat->stmode=%o, inode->i_mode=%o, info->d_mode=%o\n",stat->mode, inode->i_mode, info->d_mode); if(!strcmp(dentry->d_name.name, "ShellScript")) printk(KERN_ERR "dj_end_getattr_shell stat->stmode=%o, inode->i_mode=%o, info->d_mode=%o\n",stat->mode, inode->i_mode, info->d_mode); sdcardfs_put_lower_path(dentry, &lower_path); return 0; }
int need_graft_path(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); if(parent_info->perm == PERM_ANDROID && !strcasecmp(dentry->d_name.name, "obb")) { ret = 1; } dput(parent); return ret; }
static void sdcardfs_destroy_inode(struct inode *inode) { kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode)); }
void get_derived_permission(struct dentry *parent, struct dentry *dentry) { struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); appid_t appid; /* By default, each inode inherits from its parent. * the properties are maintained on its private fields * because the inode attributes will be modified with that of * its lower inode. * The derived state will be updated on the last * stage of each system call by fix_derived_permission(inode). */ inherit_derived_state(parent->d_inode, dentry->d_inode); /* Derive custom permissions based on parent and current node */ switch (parent_info->perm) { case PERM_INHERIT: /* Already inherited above */ break; case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ info->perm = PERM_ROOT; info->userid = simple_strtoul(dentry->d_name.name, NULL, 10); if (sbi->options.sdfs_gid == AID_SDCARD_RW) info->d_gid = sbi->options.sdfs_gid; else info->d_gid = multiuser_get_uid(info->userid, sbi->options.sdfs_gid); break; case PERM_ROOT: /* Assume masked off by default. */ if (!strcasecmp(dentry->d_name.name, "Android")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID; info->d_under_android = true; } break; case PERM_ANDROID: if (!strcasecmp(dentry->d_name.name, "data")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_DATA; } else if (!strcasecmp(dentry->d_name.name, "obb")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_OBB; // FIXME : this feature will be implemented later. /* Single OBB directory is always shared */ // ex. Fuse daemon.. // node->graft_path = fuse->obb_path; // node->graft_pathlen = strlen(fuse->obb_path); } else if (!strcasecmp(dentry->d_name.name, "media")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_MEDIA; } break; /* same policy will be applied on PERM_ANDROID_DATA * and PERM_ANDROID_OBB */ case PERM_ANDROID_DATA: case PERM_ANDROID_OBB: case PERM_ANDROID_MEDIA: appid = get_appid(sbi->pkgl_id, dentry->d_name.name); if (appid != 0) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } break; } }
static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { int err = 0; int make_nomedia_in_obb = 0; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); char *page_buf; char *nomedia_dir_name; char *nomedia_fullpath; int fullpath_namelen; int touch_err = 0; char *(*d_absolute_path_new)(const struct path *, char *, int) = (void *)kallsyms_lookup_name("d_absolute_path"); int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { err = -EACCES; goto out_eacces; } /* save current_cred and override it */ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); /* check disk space */ if (!check_min_free_space(dentry, 0, 1)) { printk(KERN_INFO "sdcardfs: No minimum free space.\n"); err = -ENOSPC; goto out_revert; } /* the lower_dentry is negative here */ sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); err = mnt_want_write(lower_path.mnt); if (err) goto out_unlock; /* set last 16bytes of mode field to 0775 */ mode = (mode & S_IFMT) | 00775; err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); if (err) goto out; /* if it is a local obb dentry, setup it with the base obbpath */ if(need_graft_path(dentry)) { err = setup_obb_dentry(dentry, &lower_path); if(err) { /* if the sbi->obbpath is not available, the lower_path won't be * changed by setup_obb_dentry() but the lower path is saved to * its orig_path. this dentry will be revalidated later. * but now, the lower_path should be NULL */ sdcardfs_put_reset_lower_path(dentry); /* the newly created lower path which saved to its orig_path or * the lower_path is the base obbpath. * therefore, an additional path_get is required */ path_get(&lower_path); } else make_nomedia_in_obb = 1; } err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); /* update number of links on parent directory */ set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); if ((sbi->options.derive == DERIVE_UNIFIED) && (!strcasecmp(dentry->d_name.name, "obb")) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { page_buf = (char *)__get_free_page(GFP_KERNEL); if (!page_buf) { printk(KERN_ERR "sdcardfs: failed to allocate page buf\n"); goto out; } nomedia_dir_name = d_absolute_path_new(&lower_path, page_buf, PAGE_SIZE); if (IS_ERR(nomedia_dir_name)) { free_page((unsigned long)page_buf); printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n"); goto out; } fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1; fullpath_namelen += strlen("/.nomedia"); nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL); if (!nomedia_fullpath) { free_page((unsigned long)page_buf); printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n"); goto out; } strcpy(nomedia_fullpath, nomedia_dir_name); free_page((unsigned long)page_buf); strcat(nomedia_fullpath, "/.nomedia"); touch_err = touch(nomedia_fullpath, 0664); if (touch_err) { printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n", nomedia_fullpath, touch_err); kfree(nomedia_fullpath); goto out; } kfree(nomedia_fullpath); } out: mnt_drop_write(lower_path.mnt); out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); out_revert: REVERT_CRED(saved_cred); out_eacces: return err; }
static struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode) { struct sdcardfs_inode_info *info; struct inode *inode; /* the new inode to return */ int err; inode = iget5_locked(sb, /* our superblock */ /* * hashval: we use inode number, but we can * also use "(unsigned long)lower_inode" * instead. */ lower_inode->i_ino, /* hashval */ sdcardfs_inode_test, /* inode comparison function */ sdcardfs_inode_set, /* inode init function */ lower_inode); /* data passed to test+set fxns */ if (!inode) { err = -EACCES; iput(lower_inode); return ERR_PTR(err); } /* if found a cached inode, then just return it */ if (!(inode->i_state & I_NEW)) return inode; /* initialize new inode */ info = SDCARDFS_I(inode); inode->i_ino = lower_inode->i_ino; if (!igrab(lower_inode)) { err = -ESTALE; return ERR_PTR(err); } sdcardfs_set_lower_inode(inode, lower_inode); inode->i_version++; /* use different set of inode ops for symlinks & directories */ if (S_ISDIR(lower_inode->i_mode)) inode->i_op = &sdcardfs_dir_iops; else if (S_ISLNK(lower_inode->i_mode)) inode->i_op = &sdcardfs_symlink_iops; else inode->i_op = &sdcardfs_main_iops; /* use different set of file ops for directories */ if (S_ISDIR(lower_inode->i_mode)) inode->i_fop = &sdcardfs_dir_fops; else inode->i_fop = &sdcardfs_main_fops; inode->i_mapping->a_ops = &sdcardfs_aops; inode->i_atime.tv_sec = 0; inode->i_atime.tv_nsec = 0; inode->i_mtime.tv_sec = 0; inode->i_mtime.tv_nsec = 0; inode->i_ctime.tv_sec = 0; inode->i_ctime.tv_nsec = 0; /* properly initialize special inodes */ if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) || S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode)) init_special_inode(inode, lower_inode->i_mode, lower_inode->i_rdev); /* all well, copy inode attributes */ fsstack_copy_attr_all(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); fix_derived_permission(inode); unlock_new_inode(inode); return inode; }