示例#1
0
static int esdfs_permission(struct inode *inode, int mask)
{
	struct esdfs_sb_info *sbi = ESDFS_SB(inode->i_sb);
	struct inode *lower_inode;
	int err;

	/* First, check the upper permissions */
	err = generic_permission(inode, mask);

	/* Basic checking of the lower inode (can't override creds here) */
	lower_inode = esdfs_lower_inode(inode);
	if (lower_inode->i_uid != sbi->lower_perms.uid ||
	    lower_inode->i_gid != sbi->lower_perms.gid ||
	    S_ISSOCK(lower_inode->i_mode) ||
	    S_ISLNK(lower_inode->i_mode) ||
	    S_ISBLK(lower_inode->i_mode) ||
	    S_ISCHR(lower_inode->i_mode) ||
	    S_ISFIFO(lower_inode->i_mode))
		err = -EACCES;

	/* Finally, check the derived permissions */
	if (!err && ESDFS_DERIVE_PERMS(ESDFS_SB(inode->i_sb)))
		err = esdfs_check_derived_permission(inode, mask);

	return err;
}
示例#2
0
static int esdfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
	int err = 0;
	struct dentry *lower_dentry;
	struct dentry *lower_parent_dentry = NULL;
	struct path lower_path;
	int mask;
	const struct cred *creds =
			esdfs_override_creds(ESDFS_SB(dir->i_sb), &mask);
	if (!creds)
		return -ENOMEM;

	esdfs_get_lower_path(dentry, &lower_path);
	lower_dentry = lower_path.dentry;
	lower_parent_dentry = lock_parent(lower_dentry);

	mode |= S_IFDIR;
	esdfs_set_lower_mode(ESDFS_SB(dir->i_sb), &mode);

	err = mnt_want_write(lower_path.mnt);
	if (err)
		goto out_unlock;
	err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode);
	if (err)
		goto out;

	err = esdfs_interpose(dentry, dir->i_sb, &lower_path);
	if (err)
		goto out;

	fsstack_copy_attr_times(dir, esdfs_lower_inode(dir));
	fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode);
	/* update number of links on parent directory */
	set_nlink(dir, esdfs_lower_inode(dir)->i_nlink);

	if (ESDFS_DERIVE_PERMS(ESDFS_SB(dir->i_sb)))
		err = esdfs_derive_mkdir_contents(dentry);

out:
	mnt_drop_write(lower_path.mnt);
out_unlock:
	unlock_dir(lower_parent_dentry);
	esdfs_put_lower_path(dentry, &lower_path);
	esdfs_revert_creds(creds, &mask);
	return err;
}
示例#3
0
/*
 * There is no need to lock the esdfs_super_info's rwsem as there is no
 * way anyone can have a reference to the superblock at this point in time.
 */
static int esdfs_read_super(struct super_block *sb, const char *dev_name,
		void *raw_data, int silent)
{
	int err = 0;
	struct super_block *lower_sb;
	struct path lower_path;
	struct esdfs_sb_info *sbi;
	struct inode *inode;

	if (!dev_name) {
		esdfs_msg(sb, KERN_ERR, "missing dev_name argument\n");
		err = -EINVAL;
		goto out;
	}

	/* parse lower path */
	err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
			&lower_path);
	if (err) {
		esdfs_msg(sb, KERN_ERR, "error accessing lower directory '%s'\n",
			dev_name);
		goto out;
	}

	/* allocate superblock private data */
	sb->s_fs_info = kzalloc(sizeof(struct esdfs_sb_info), GFP_KERNEL);
	sbi = ESDFS_SB(sb);
	if (!sbi) {
		esdfs_msg(sb, KERN_CRIT, "read_super: out of memory\n");
		err = -ENOMEM;
		goto out_pput;
	}

	/* set defaults and then parse the mount options */
	memcpy(&sbi->lower_perms,
	       &esdfs_perms_table[ESDFS_PERMS_LOWER_DEFAULT],
	       sizeof(struct esdfs_perms));
	memcpy(&sbi->upper_perms,
	       &esdfs_perms_table[ESDFS_PERMS_UPPER_LEGACY],
	       sizeof(struct esdfs_perms));
	err = parse_options(sb, (char *)raw_data);
	if (err)
		goto out_free;

	/* set the lower superblock field of upper superblock */
	lower_sb = lower_path.dentry->d_sb;
	atomic_inc(&lower_sb->s_active);
	esdfs_set_lower_super(sb, lower_sb);

	/* inherit maxbytes from lower file system */
	sb->s_maxbytes = lower_sb->s_maxbytes;

	/*
	 * Our c/m/atime granularity is 1 ns because we may stack on file
	 * systems whose granularity is as good.
	 */
	sb->s_time_gran = 1;

	sb->s_op = &esdfs_sops;

	/* get a new inode and allocate our root dentry */
	inode = esdfs_iget(sb, lower_path.dentry->d_inode);
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
		goto out_sput;
	}
	sb->s_root = d_make_root(inode);
	if (!sb->s_root) {
		err = -ENOMEM;
		goto out_iput;
	}
	d_set_d_op(sb->s_root, &esdfs_dops);

	/* link the upper and lower dentries */
	sb->s_root->d_fsdata = NULL;
	err = new_dentry_private_data(sb->s_root);
	if (err)
		goto out_freeroot;

	/* if get here: cannot have error */

	/* set the lower dentries for s_root */
	esdfs_set_lower_path(sb->s_root, &lower_path);
#ifdef CONFIG_SECURITY_SELINUX
	security_secctx_to_secid(ESDFS_LOWER_SECCTX,
				 strlen(ESDFS_LOWER_SECCTX),
				 &sbi->lower_secid);
#endif
	/*
	 * No need to call interpose because we already have a positive
	 * dentry, which was instantiated by d_make_root.  Just need to
	 * d_rehash it.
	 */
	d_rehash(sb->s_root);
	if (!silent)
		esdfs_msg(sb, KERN_INFO, "mounted on top of %s type %s\n",
			dev_name, lower_sb->s_type->name);

	if (!ESDFS_DERIVE_PERMS(sbi))
		goto out;

	/* let user know that we ignore this option in derived mode */
	if (memcmp(&sbi->upper_perms,
		   &esdfs_perms_table[ESDFS_PERMS_UPPER_LEGACY],
		   sizeof(struct esdfs_perms)))
		esdfs_msg(sb, KERN_WARNING, "'upper' mount option ignored in derived mode\n");

	/* all derived modes start with the same, basic root */
	memcpy(&sbi->upper_perms,
	       &esdfs_perms_table[ESDFS_PERMS_UPPER_DERIVED],
	       sizeof(struct esdfs_perms));

	/*
	 * In Android 3.0 all user conent in the emulated storage tree was
	 * stored in /data/media.  Android 4.2 introduced multi-user support,
	 * which required that the primary user's content be migrated from
	 * /data/media to /data/media/0.  The framework then uses bind mounts
	 * to create per-process namespaces to isolate each user's tree at
	 * /data/media/N.  This approach of having each user in a common root
	 * is now considered "legacy" by the sdcard service.
	 */
	if (test_opt(sbi, DERIVE_LEGACY)) {
		ESDFS_I(inode)->tree = ESDFS_TREE_ROOT_LEGACY;
		sbi->obb_parent = dget(sb->s_root);
	/*
	 * Android 4.4 reorganized this sturcture yet again, so that the
	 * primary user's content was again at the root.  Secondary users'
	 * content is found in Android/user/N.  Emulated internal storage still
	 * seems to use the legacy tree, but secondary external storage uses
	 * this method.
	 */
	} else if (test_opt(sbi, DERIVE_UNIFIED))
		ESDFS_I(inode)->tree = ESDFS_TREE_ROOT;
	/*
	 * Later versions of Android organize user content using quantum
	 * entanglement, which has a low probability of being supported by
	 * this driver.
	 */
	else
		esdfs_msg(sb, KERN_WARNING, "unsupported derived permissions mode\n");

	/* initialize root inode */
	esdfs_derive_perms(sb->s_root);

	goto out;

out_freeroot:
	dput(sb->s_root);
out_iput:
	iput(inode);
out_sput:
	/* drop refs we took earlier */
	atomic_dec(&lower_sb->s_active);
out_free:
	kfree(ESDFS_SB(sb));
	sb->s_fs_info = NULL;
out_pput:
	path_put(&lower_path);

out:
	return err;
}