fs: Remove Samsung implementation of sdcardfs
Remove Samsung version of sdcardfs before we use AOSP source Change-Id: I33710450b91d8cfde38a27967b0527e6a72fb440
This commit is contained in:
parent
c31902fbe0
commit
ec57310b8e
|
@ -17,7 +17,6 @@ if BLOCK
|
|||
source "fs/ext2/Kconfig"
|
||||
source "fs/ext3/Kconfig"
|
||||
source "fs/ext4/Kconfig"
|
||||
source "fs/sdcardfs/Kconfig"
|
||||
|
||||
config FS_XIP
|
||||
# execute in place
|
||||
|
|
|
@ -69,7 +69,6 @@ obj-$(CONFIG_EXT2_FS) += ext2/
|
|||
# We place ext4 after ext2 so plain ext2 root fs's are mounted using ext2
|
||||
# unless explicitly requested by rootfstype
|
||||
obj-$(CONFIG_EXT4_FS) += ext4/
|
||||
obj-$(CONFIG_SDCARD_FS) += sdcardfs/
|
||||
obj-$(CONFIG_JBD) += jbd/
|
||||
obj-$(CONFIG_JBD2) += jbd2/
|
||||
obj-$(CONFIG_CRAMFS) += cramfs/
|
||||
|
|
|
@ -2215,12 +2215,7 @@ extern int search_dir(struct buffer_head *bh,
|
|||
struct inode *dir,
|
||||
const struct qstr *d_name,
|
||||
unsigned int offset,
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
struct ext4_dir_entry_2 **res_dir,
|
||||
char *ci_name_buf);
|
||||
#else
|
||||
struct ext4_dir_entry_2 **res_dir);
|
||||
#endif
|
||||
extern int ext4_generic_delete_entry(handle_t *handle,
|
||||
struct inode *dir,
|
||||
struct ext4_dir_entry_2 *de_del,
|
||||
|
|
|
@ -1640,13 +1640,8 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir,
|
|||
inline_start = (void *)ext4_raw_inode(&iloc)->i_block +
|
||||
EXT4_INLINE_DOTDOT_SIZE;
|
||||
inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE;
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
ret = search_dir(iloc.bh, inline_start, inline_size,
|
||||
dir, d_name, 0, res_dir, NULL);
|
||||
#else
|
||||
ret = search_dir(iloc.bh, inline_start, inline_size,
|
||||
dir, d_name, 0, res_dir);
|
||||
#endif
|
||||
if (ret == 1)
|
||||
goto out_find;
|
||||
if (ret < 0)
|
||||
|
@ -1658,13 +1653,8 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir,
|
|||
inline_start = ext4_get_inline_xattr_pos(dir, &iloc);
|
||||
inline_size = ext4_get_inline_size(dir) - EXT4_MIN_INLINE_DATA_SIZE;
|
||||
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
ret = search_dir(iloc.bh, inline_start, inline_size,
|
||||
dir, d_name, 0, res_dir, NULL);
|
||||
#else
|
||||
ret = search_dir(iloc.bh, inline_start, inline_size,
|
||||
dir, d_name, 0, res_dir);
|
||||
#endif
|
||||
if (ret == 1)
|
||||
goto out_find;
|
||||
|
||||
|
|
121
fs/ext4/namei.c
121
fs/ext4/namei.c
|
@ -30,10 +30,6 @@
|
|||
#include <linux/time.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/stat.h>
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
#include <linux/namei.h>
|
||||
#include <linux/dcache.h>
|
||||
#endif
|
||||
#include <linux/string.h>
|
||||
#include <linux/quotaops.h>
|
||||
#include <linux/buffer_head.h>
|
||||
|
@ -1049,20 +1045,11 @@ static inline int search_dirblock(struct buffer_head *bh,
|
|||
struct inode *dir,
|
||||
const struct qstr *d_name,
|
||||
unsigned int offset,
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
struct ext4_dir_entry_2 ** res_dir,
|
||||
char *ci_name_buf)
|
||||
{
|
||||
return search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir,
|
||||
d_name, offset, res_dir, ci_name_buf);
|
||||
}
|
||||
#else
|
||||
struct ext4_dir_entry_2 ** res_dir)
|
||||
{
|
||||
return search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir,
|
||||
d_name, offset, res_dir);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Directory block splitting, compacting
|
||||
|
@ -1153,18 +1140,6 @@ static inline int ext4_match (int len, const char * const name,
|
|||
return !memcmp(name, de->name, len);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
static inline int ext4_ci_match (int len, const char * const name,
|
||||
struct ext4_dir_entry_2 * de)
|
||||
{
|
||||
if (len != de->name_len)
|
||||
return 0;
|
||||
if (!de->inode)
|
||||
return 0;
|
||||
return !strncasecmp(name, de->name, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Returns 0 if not found, -1 on failure, and 1 on success
|
||||
*/
|
||||
|
@ -1174,12 +1149,7 @@ int search_dir(struct buffer_head *bh,
|
|||
struct inode *dir,
|
||||
const struct qstr *d_name,
|
||||
unsigned int offset,
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
struct ext4_dir_entry_2 **res_dir,
|
||||
char *ci_name_buf)
|
||||
#else
|
||||
struct ext4_dir_entry_2 **res_dir)
|
||||
#endif
|
||||
{
|
||||
struct ext4_dir_entry_2 * de;
|
||||
char * dlimit;
|
||||
|
@ -1193,31 +1163,6 @@ int search_dir(struct buffer_head *bh,
|
|||
/* this code is executed quadratically often */
|
||||
/* do minimal checking `by hand' */
|
||||
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
if ((char *) de + namelen <= dlimit) {
|
||||
if (ci_name_buf) {
|
||||
if (ext4_ci_match (namelen, name, de)) {
|
||||
/* found a match - just to be sure, do a full check */
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
|
||||
bh->b_size, offset))
|
||||
return -1;
|
||||
*res_dir = de;
|
||||
memcpy(ci_name_buf, de->name, namelen);
|
||||
ci_name_buf[namelen] = '\0';
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (ext4_match (namelen, name, de)) {
|
||||
/* found a match - just to be sure, do a full check */
|
||||
if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
|
||||
bh->b_size, offset))
|
||||
return -1;
|
||||
*res_dir = de;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
if ((char *) de + namelen <= dlimit &&
|
||||
ext4_match (namelen, name, de)) {
|
||||
/* found a match - just to be sure, do a full check */
|
||||
|
@ -1227,7 +1172,7 @@ int search_dir(struct buffer_head *bh,
|
|||
*res_dir = de;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* prevent looping on a bad block */
|
||||
de_len = ext4_rec_len_from_disk(de->rec_len,
|
||||
dir->i_sb->s_blocksize);
|
||||
|
@ -1269,12 +1214,7 @@ static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block,
|
|||
static struct buffer_head * ext4_find_entry (struct inode *dir,
|
||||
const struct qstr *d_name,
|
||||
struct ext4_dir_entry_2 **res_dir,
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
int *inlined,
|
||||
char *ci_name_buf)
|
||||
#else
|
||||
int *inlined)
|
||||
#endif
|
||||
{
|
||||
struct super_block *sb;
|
||||
struct buffer_head *bh_use[NAMEI_RA_SIZE];
|
||||
|
@ -1382,14 +1322,8 @@ restart:
|
|||
goto next;
|
||||
}
|
||||
set_buffer_verified(bh);
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
i = search_dirblock(bh, dir, d_name,
|
||||
block << EXT4_BLOCK_SIZE_BITS(sb), res_dir,
|
||||
ci_name_buf);
|
||||
#else
|
||||
i = search_dirblock(bh, dir, d_name,
|
||||
block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);
|
||||
#endif
|
||||
if (i == 1) {
|
||||
EXT4_I(dir)->i_dir_start_lookup = block;
|
||||
ret = bh;
|
||||
|
@ -1441,15 +1375,9 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
|
|||
*err = PTR_ERR(bh);
|
||||
goto errout;
|
||||
}
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
retval = search_dirblock(bh, dir, d_name,
|
||||
block << EXT4_BLOCK_SIZE_BITS(sb),
|
||||
res_dir, NULL);
|
||||
#else
|
||||
retval = search_dirblock(bh, dir, d_name,
|
||||
block << EXT4_BLOCK_SIZE_BITS(sb),
|
||||
res_dir);
|
||||
#endif
|
||||
if (retval == 1) { /* Success! */
|
||||
dx_release(frames);
|
||||
return bh;
|
||||
|
@ -1484,23 +1412,11 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
|
|||
struct inode *inode;
|
||||
struct ext4_dir_entry_2 *de;
|
||||
struct buffer_head *bh;
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
struct qstr ci_name;
|
||||
char ci_name_buf[EXT4_NAME_LEN+1];
|
||||
#endif
|
||||
|
||||
if (dentry->d_name.len > EXT4_NAME_LEN)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
ci_name_buf[0] = '\0';
|
||||
if (flags & LOOKUP_CASE_INSENSITIVE)
|
||||
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, ci_name_buf);
|
||||
else
|
||||
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, NULL);
|
||||
#else
|
||||
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
|
||||
#endif
|
||||
inode = NULL;
|
||||
if (bh) {
|
||||
__u32 ino = le32_to_cpu(de->inode);
|
||||
|
@ -1530,16 +1446,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
|
|||
return ERR_PTR(-EIO);
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
if (ci_name_buf[0] != '\0') {
|
||||
ci_name.name = ci_name_buf;
|
||||
ci_name.len = dentry->d_name.len;
|
||||
return d_add_ci(dentry, inode, &ci_name);
|
||||
} else
|
||||
return d_splice_alias(inode, dentry);
|
||||
#else
|
||||
return d_splice_alias(inode, dentry);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -1550,11 +1457,7 @@ struct dentry *ext4_get_parent(struct dentry *child)
|
|||
struct ext4_dir_entry_2 * de;
|
||||
struct buffer_head *bh;
|
||||
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
bh = ext4_find_entry(child->d_inode, &dotdot, &de, NULL, NULL);
|
||||
#else
|
||||
bh = ext4_find_entry(child->d_inode, &dotdot, &de, NULL);
|
||||
#endif
|
||||
if (!bh)
|
||||
return ERR_PTR(-ENOENT);
|
||||
ino = le32_to_cpu(de->inode);
|
||||
|
@ -2776,11 +2679,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
dquot_initialize(dentry->d_inode);
|
||||
|
||||
retval = -ENOENT;
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, NULL);
|
||||
#else
|
||||
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
|
||||
#endif
|
||||
if (!bh)
|
||||
goto end_rmdir;
|
||||
|
||||
|
@ -2847,11 +2746,7 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
|
|||
dquot_initialize(dentry->d_inode);
|
||||
|
||||
retval = -ENOENT;
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, NULL);
|
||||
#else
|
||||
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
|
||||
#endif
|
||||
if (!bh)
|
||||
goto end_unlink;
|
||||
|
||||
|
@ -3116,11 +3011,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
|
||||
ext4_handle_sync(handle);
|
||||
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
old_bh = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de, NULL, NULL);
|
||||
#else
|
||||
old_bh = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de, NULL);
|
||||
#endif
|
||||
/*
|
||||
* Check for inode number is _not_ due to possible IO errors.
|
||||
* We might rmdir the source, keep it as pwd of some process
|
||||
|
@ -3133,13 +3024,8 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
goto end_rename;
|
||||
|
||||
new_inode = new_dentry->d_inode;
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
new_bh = ext4_find_entry(new_dir, &new_dentry->d_name,
|
||||
&new_de, &new_inlined, NULL);
|
||||
#else
|
||||
new_bh = ext4_find_entry(new_dir, &new_dentry->d_name,
|
||||
&new_de, &new_inlined);
|
||||
#endif
|
||||
if (new_bh) {
|
||||
if (!new_inode) {
|
||||
brelse(new_bh);
|
||||
|
@ -3221,13 +3107,8 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
struct buffer_head *old_bh2;
|
||||
struct ext4_dir_entry_2 *old_de2;
|
||||
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
old_bh2 = ext4_find_entry(old_dir, &old_dentry->d_name,
|
||||
&old_de2, NULL, NULL);
|
||||
#else
|
||||
old_bh2 = ext4_find_entry(old_dir, &old_dentry->d_name,
|
||||
&old_de2, NULL);
|
||||
#endif
|
||||
if (old_bh2) {
|
||||
retval = ext4_delete_entry(handle, old_dir,
|
||||
old_de2, old_bh2);
|
||||
|
|
60
fs/namei.c
60
fs/namei.c
|
@ -473,24 +473,6 @@ void path_put(const struct path *path)
|
|||
}
|
||||
EXPORT_SYMBOL(path_put);
|
||||
|
||||
/**
|
||||
* path_connected - Verify that a path->dentry is below path->mnt.mnt_root
|
||||
* @path: nameidate to verify
|
||||
*
|
||||
* Rename can sometimes move a file or directory outside of a bind
|
||||
* mount, path_connected allows those cases to be detected.
|
||||
*/
|
||||
static bool path_connected(const struct path *path)
|
||||
{
|
||||
struct vfsmount *mnt = path->mnt;
|
||||
|
||||
/* Only bind mounts can have disconnected paths */
|
||||
if (mnt->mnt_root == mnt->mnt_sb->s_root)
|
||||
return true;
|
||||
|
||||
return is_subdir(path->dentry, mnt->mnt_root);
|
||||
}
|
||||
|
||||
/*
|
||||
* Path walking has 2 modes, rcu-walk and ref-walk (see
|
||||
* Documentation/filesystems/path-lookup.txt). In situations when we can't
|
||||
|
@ -1166,8 +1148,6 @@ static int follow_dotdot_rcu(struct nameidata *nd)
|
|||
goto failed;
|
||||
nd->path.dentry = parent;
|
||||
nd->seq = seq;
|
||||
if (unlikely(!path_connected(&nd->path)))
|
||||
goto failed;
|
||||
break;
|
||||
}
|
||||
if (!follow_up_rcu(&nd->path))
|
||||
|
@ -1251,7 +1231,7 @@ static void follow_mount(struct path *path)
|
|||
}
|
||||
}
|
||||
|
||||
static int follow_dotdot(struct nameidata *nd)
|
||||
static void follow_dotdot(struct nameidata *nd)
|
||||
{
|
||||
set_root(nd);
|
||||
|
||||
|
@ -1266,10 +1246,6 @@ static int follow_dotdot(struct nameidata *nd)
|
|||
/* rare case of legitimate dget_parent()... */
|
||||
nd->path.dentry = dget_parent(nd->path.dentry);
|
||||
dput(old);
|
||||
if (unlikely(!path_connected(&nd->path))) {
|
||||
path_put(&nd->path);
|
||||
return -ENOENT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!follow_up(&nd->path))
|
||||
|
@ -1277,7 +1253,6 @@ static int follow_dotdot(struct nameidata *nd)
|
|||
}
|
||||
follow_mount(&nd->path);
|
||||
nd->inode = nd->path.dentry->d_inode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1501,7 +1476,7 @@ static inline int handle_dots(struct nameidata *nd, int type)
|
|||
if (follow_dotdot_rcu(nd))
|
||||
return -ECHILD;
|
||||
} else
|
||||
return follow_dotdot(nd);
|
||||
follow_dotdot(nd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -2932,10 +2907,6 @@ opened:
|
|||
goto exit_fput;
|
||||
}
|
||||
out:
|
||||
if (unlikely(error > 0)) {
|
||||
WARN_ON(1);
|
||||
error = -EINVAL;
|
||||
}
|
||||
if (got_write)
|
||||
mnt_drop_write(nd->path.mnt);
|
||||
path_put(&save_parent);
|
||||
|
@ -3384,8 +3355,6 @@ static long do_rmdir(int dfd, const char __user *pathname)
|
|||
struct dentry *dentry;
|
||||
struct nameidata nd;
|
||||
unsigned int lookup_flags = 0;
|
||||
char *path_buf = NULL;
|
||||
char *propagate_path = NULL;
|
||||
retry:
|
||||
name = user_path_parent(dfd, pathname, &nd, lookup_flags);
|
||||
if (IS_ERR(name))
|
||||
|
@ -3420,23 +3389,11 @@ retry:
|
|||
error = security_path_rmdir(&nd.path, dentry);
|
||||
if (error)
|
||||
goto exit3;
|
||||
if (nd.path.dentry->d_sb->s_op->unlink_callback) {
|
||||
path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
|
||||
propagate_path = dentry_path_raw(dentry, path_buf, PATH_MAX);
|
||||
}
|
||||
error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
|
||||
exit3:
|
||||
dput(dentry);
|
||||
exit2:
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
if (path_buf && !error) {
|
||||
nd.path.dentry->d_sb->s_op->unlink_callback(nd.path.dentry->d_sb,
|
||||
propagate_path);
|
||||
}
|
||||
if (path_buf) {
|
||||
kfree(path_buf);
|
||||
path_buf = NULL;
|
||||
}
|
||||
mnt_drop_write(nd.path.mnt);
|
||||
exit1:
|
||||
path_put(&nd.path);
|
||||
|
@ -3499,8 +3456,6 @@ static long do_unlinkat(int dfd, const char __user *pathname)
|
|||
struct nameidata nd;
|
||||
struct inode *inode = NULL;
|
||||
unsigned int lookup_flags = 0;
|
||||
char *path_buf = NULL;
|
||||
char *propagate_path = NULL;
|
||||
retry:
|
||||
name = user_path_parent(dfd, pathname, &nd, lookup_flags);
|
||||
if (IS_ERR(name))
|
||||
|
@ -3525,10 +3480,6 @@ retry:
|
|||
inode = dentry->d_inode;
|
||||
if (!inode)
|
||||
goto slashes;
|
||||
if (inode->i_sb->s_op->unlink_callback) {
|
||||
path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
|
||||
propagate_path = dentry_path_raw(dentry, path_buf, PATH_MAX);
|
||||
}
|
||||
ihold(inode);
|
||||
error = security_path_unlink(&nd.path, dentry);
|
||||
if (error)
|
||||
|
@ -3538,13 +3489,6 @@ exit2:
|
|||
dput(dentry);
|
||||
}
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
if (path_buf && !error) {
|
||||
inode->i_sb->s_op->unlink_callback(inode->i_sb, propagate_path);
|
||||
}
|
||||
if (path_buf) {
|
||||
kfree(path_buf);
|
||||
path_buf = NULL;
|
||||
}
|
||||
if (inode)
|
||||
iput(inode); /* truncate the inode here */
|
||||
mnt_drop_write(nd.path.mnt);
|
||||
|
|
|
@ -738,8 +738,6 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
|
|||
struct fsnotify_group *group;
|
||||
struct inode *inode;
|
||||
struct path path;
|
||||
struct path alteredpath;
|
||||
struct path *canonical_path = &path;
|
||||
struct fd f;
|
||||
int ret;
|
||||
unsigned flags = 0;
|
||||
|
@ -767,22 +765,13 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
|
|||
if (ret)
|
||||
goto fput_and_out;
|
||||
|
||||
/* support stacked filesystems */
|
||||
if(path.dentry && path.dentry->d_op) {
|
||||
if (path.dentry->d_op->d_canonical_path) {
|
||||
path.dentry->d_op->d_canonical_path(&path, &alteredpath);
|
||||
canonical_path = &alteredpath;
|
||||
path_put(&path);
|
||||
}
|
||||
}
|
||||
|
||||
/* inode held in place by reference to path; group by fget on fd */
|
||||
inode = canonical_path->dentry->d_inode;
|
||||
inode = path.dentry->d_inode;
|
||||
group = f.file->private_data;
|
||||
|
||||
/* create/update an inode mark */
|
||||
ret = inotify_update_watch(group, inode, mask);
|
||||
path_put(canonical_path);
|
||||
path_put(&path);
|
||||
fput_and_out:
|
||||
fdput(f);
|
||||
return ret;
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
config SDCARD_FS
|
||||
tristate "sdcard file system"
|
||||
default y
|
||||
help
|
||||
Sdcardfs is based on Wrapfs file system.
|
||||
|
||||
config SDCARD_FS_FADV_NOACTIVE
|
||||
bool "sdcardfs fadvise noactive support"
|
||||
depends on FADV_NOACTIVE
|
||||
default y
|
||||
help
|
||||
Sdcardfs supports fadvise noactive mode.
|
||||
|
||||
config SDCARD_FS_CI_SEARCH
|
||||
tristate "sdcardfs case-insensitive search support"
|
||||
depends on SDCARD_FS
|
||||
default y
|
||||
|
||||
config SDCARD_FS_XATTR
|
||||
bool "Sdcardfs extended attribute"
|
||||
default y
|
||||
depends on SDCARD_FS
|
||||
help
|
||||
Modification of sdcard file system for xattr
|
|
@ -1,8 +0,0 @@
|
|||
ifeq ($(CONFIG_SDCARD_FS_XATTR),y)
|
||||
EXTRA_CFLAGS += -DSDCARD_FS_XATTR
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_SDCARD_FS) += sdcardfs.o
|
||||
|
||||
sdcardfs-y := dentry.o file.o inode.o main.o super.o lookup.o mmap.o packagelist.o derived_perm.o
|
||||
sdcardfs-$(CONFIG_SDCARD_FS_XATTR) += xattr.o
|
|
@ -1,199 +0,0 @@
|
|||
/*
|
||||
* fs/sdcardfs/dentry.c
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||
* Sunghwan Yun, Sungjong Seo
|
||||
*
|
||||
* This program has been developed as a stackable file system based on
|
||||
* the WrapFS which written by
|
||||
*
|
||||
* Copyright (c) 1998-2011 Erez Zadok
|
||||
* Copyright (c) 2009 Shrikar Archak
|
||||
* Copyright (c) 2003-2011 Stony Brook University
|
||||
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#include "sdcardfs.h"
|
||||
#include "linux/ctype.h"
|
||||
|
||||
/*
|
||||
* returns: -ERRNO if error (returned to user)
|
||||
* 0: tell VFS to invalidate dentry
|
||||
* 1: dentry is valid
|
||||
*/
|
||||
static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
int err = 1;
|
||||
struct path parent_lower_path, lower_path;
|
||||
struct dentry *parent_dentry = NULL;
|
||||
struct dentry *parent_lower_dentry = NULL;
|
||||
struct dentry *lower_cur_parent_dentry = NULL;
|
||||
struct dentry *lower_dentry = NULL;
|
||||
|
||||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (IS_ROOT(dentry)) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return 1;
|
||||
}
|
||||
if (dentry->d_flags & DCACHE_WILL_INVALIDATE) {
|
||||
dentry->d_flags &= ~DCACHE_WILL_INVALIDATE;
|
||||
__d_drop(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
|
||||
/* check uninitialized obb_dentry and
|
||||
* whether the base obbpath has been changed or not */
|
||||
if (is_obbpath_invalid(dentry)) {
|
||||
d_drop(dentry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
parent_dentry = dget_parent(dentry);
|
||||
sdcardfs_get_lower_path(parent_dentry, &parent_lower_path);
|
||||
sdcardfs_get_real_lower(dentry, &lower_path);
|
||||
parent_lower_dentry = parent_lower_path.dentry;
|
||||
lower_dentry = lower_path.dentry;
|
||||
lower_cur_parent_dentry = dget_parent(lower_dentry);
|
||||
|
||||
spin_lock(&lower_dentry->d_lock);
|
||||
if (d_unhashed(lower_dentry)) {
|
||||
spin_unlock(&lower_dentry->d_lock);
|
||||
d_drop(dentry);
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
spin_unlock(&lower_dentry->d_lock);
|
||||
|
||||
if (parent_lower_dentry != lower_cur_parent_dentry) {
|
||||
d_drop(dentry);
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dentry == lower_dentry) {
|
||||
err = 0;
|
||||
panic("sdcardfs: dentry is equal to lower_dentry\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dentry < lower_dentry) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
spin_lock(&lower_dentry->d_lock);
|
||||
} else {
|
||||
spin_lock(&lower_dentry->d_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
if (dentry->d_name.len != lower_dentry->d_name.len) {
|
||||
__d_drop(dentry);
|
||||
err = 0;
|
||||
} else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name,
|
||||
dentry->d_name.len) != 0) {
|
||||
__d_drop(dentry);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
if (dentry < lower_dentry) {
|
||||
spin_unlock(&lower_dentry->d_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
} else {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&lower_dentry->d_lock);
|
||||
}
|
||||
|
||||
out:
|
||||
dput(parent_dentry);
|
||||
dput(lower_cur_parent_dentry);
|
||||
sdcardfs_put_lower_path(parent_dentry, &parent_lower_path);
|
||||
sdcardfs_put_real_lower(dentry, &lower_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void sdcardfs_d_release(struct dentry *dentry)
|
||||
{
|
||||
/* release and reset the lower paths */
|
||||
if(has_graft_path(dentry)) {
|
||||
sdcardfs_put_reset_orig_path(dentry);
|
||||
}
|
||||
sdcardfs_put_reset_lower_path(dentry);
|
||||
free_dentry_private_data(dentry);
|
||||
return;
|
||||
}
|
||||
|
||||
static int sdcardfs_hash_ci(const struct dentry *dentry,
|
||||
const struct inode *inode, struct qstr *qstr)
|
||||
{
|
||||
/*
|
||||
* This function is copy of vfat_hashi.
|
||||
* FIXME Should we support national language?
|
||||
* Refer to vfat_hashi()
|
||||
* struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io;
|
||||
*/
|
||||
const unsigned char *name;
|
||||
unsigned int len;
|
||||
unsigned long hash;
|
||||
|
||||
name = qstr->name;
|
||||
//len = vfat_striptail_len(qstr);
|
||||
len = qstr->len;
|
||||
|
||||
hash = init_name_hash();
|
||||
while (len--)
|
||||
//hash = partial_name_hash(nls_tolower(t, *name++), hash);
|
||||
hash = partial_name_hash(tolower(*name++), hash);
|
||||
qstr->hash = end_name_hash(hash);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Case insensitive compare of two vfat names.
|
||||
*/
|
||||
static int sdcardfs_cmp_ci(const struct dentry *parent,
|
||||
const struct inode *pinode,
|
||||
const struct dentry *dentry, const struct inode *inode,
|
||||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
/* This function is copy of vfat_cmpi */
|
||||
// FIXME Should we support national language?
|
||||
//struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io;
|
||||
//unsigned int alen, blen;
|
||||
|
||||
/* A filename cannot end in '.' or we treat it like it has none */
|
||||
/*
|
||||
alen = vfat_striptail_len(name);
|
||||
blen = __vfat_striptail_len(len, str);
|
||||
if (alen == blen) {
|
||||
if (nls_strnicmp(t, name->name, str, alen) == 0)
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
if (name->len == len) {
|
||||
if (strncasecmp(name->name, str, len) == 0)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void sdcardfs_canonical_path(const struct path *path, struct path *actual_path) {
|
||||
sdcardfs_get_real_lower(path->dentry, actual_path);
|
||||
}
|
||||
|
||||
const struct dentry_operations sdcardfs_ci_dops = {
|
||||
.d_revalidate = sdcardfs_d_revalidate,
|
||||
.d_release = sdcardfs_d_release,
|
||||
.d_hash = sdcardfs_hash_ci,
|
||||
.d_compare = sdcardfs_cmp_ci,
|
||||
.d_canonical_path = sdcardfs_canonical_path,
|
||||
};
|
||||
|
|
@ -1,355 +0,0 @@
|
|||
/*
|
||||
* fs/sdcardfs/derived_perm.c
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||
* Sunghwan Yun, Sungjong Seo
|
||||
*
|
||||
* This program has been developed as a stackable file system based on
|
||||
* the WrapFS which written by
|
||||
*
|
||||
* Copyright (c) 1998-2011 Erez Zadok
|
||||
* Copyright (c) 2009 Shrikar Archak
|
||||
* Copyright (c) 2003-2011 Stony Brook University
|
||||
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#include "sdcardfs.h"
|
||||
|
||||
/* 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->under_android = pi->under_android;
|
||||
}
|
||||
|
||||
/* 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->under_android = under_android;
|
||||
}
|
||||
|
||||
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);
|
||||
#ifdef CONFIG_SDP
|
||||
struct sdcardfs_dentry_info *parent_dinfo = SDCARDFS_D(parent);
|
||||
#endif
|
||||
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);
|
||||
|
||||
//printk(KERN_INFO "sdcardfs: derived: %s, %s, %d\n", parent->d_name.name,
|
||||
// dentry->d_name.name, parent_info->perm);
|
||||
|
||||
/* 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);
|
||||
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->under_android = true;
|
||||
} else if (!strcasecmp(dentry->d_name.name, "knox")) {
|
||||
info->perm = PERM_ANDROID_KNOX;
|
||||
info->d_gid = AID_SDCARD_R;
|
||||
info->under_android = false;
|
||||
}
|
||||
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 */
|
||||
} 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;
|
||||
/** KNOX permission */
|
||||
case PERM_ANDROID_KNOX:
|
||||
info->perm = PERM_ANDROID_KNOX_USER;
|
||||
info->userid = simple_strtoul(dentry->d_name.name, NULL, 10);
|
||||
info->d_gid = AID_SDCARD_R;
|
||||
info->under_android = false;
|
||||
break;
|
||||
|
||||
case PERM_ANDROID_KNOX_USER:
|
||||
if (!strcasecmp(dentry->d_name.name, "Android")) {
|
||||
info->perm = PERM_ANDROID_KNOX_ANDROID;
|
||||
info->under_android = false;
|
||||
}
|
||||
break;
|
||||
case PERM_ANDROID_KNOX_ANDROID:
|
||||
if (!strcasecmp(dentry->d_name.name, "data")) {
|
||||
info->perm = PERM_ANDROID_KNOX_DATA;
|
||||
info->under_android = false;
|
||||
} else if (!strcasecmp(dentry->d_name.name, "shared")) {
|
||||
info->perm = PERM_ANDROID_KNOX_SHARED;
|
||||
info->d_gid = AID_SDCARD_RW;
|
||||
info->d_uid = multiuser_get_uid(parent_info->userid, 0);
|
||||
info->under_android = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case PERM_ANDROID_KNOX_SHARED:
|
||||
break;
|
||||
|
||||
case PERM_ANDROID_KNOX_DATA:
|
||||
appid = get_appid(sbi->pkgl_id, dentry->d_name.name);
|
||||
info->perm = PERM_ANDROID_KNOX_PACKAGE_DATA;
|
||||
if (appid != 0) {
|
||||
info->d_uid = multiuser_get_uid(parent_info->userid, appid);
|
||||
} else {
|
||||
info->d_uid = multiuser_get_uid(parent_info->userid, 0);
|
||||
}
|
||||
info->under_android = false;
|
||||
break;
|
||||
case PERM_ANDROID_KNOX_PACKAGE_DATA:
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_SDP
|
||||
if((parent_info->perm == PERM_PRE_ROOT) && (parent_dinfo->under_knox) && (parent_dinfo->userid >= 0)) {
|
||||
info->userid = parent_dinfo->userid;
|
||||
}
|
||||
|
||||
if(parent_dinfo->under_knox) {
|
||||
if(parent_dinfo->permission == PERMISSION_UNDER_ANDROID) {
|
||||
if (parent_dinfo->appid != 0){
|
||||
info->d_uid = multiuser_get_uid(parent_info->userid, parent_dinfo->appid);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* set vfs_inode from sdcardfs_inode */
|
||||
void fix_derived_permission(struct inode *inode) {
|
||||
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(inode->i_sb);
|
||||
struct sdcardfs_mount_options *opts = &sbi->options;
|
||||
mode_t visible_mode;
|
||||
mode_t owner_mode;
|
||||
mode_t filtered_mode;
|
||||
|
||||
inode->i_uid = info->d_uid;
|
||||
|
||||
if (info->d_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. */
|
||||
inode->i_gid = AID_SDCARD_RW;
|
||||
} else {
|
||||
inode->i_gid = multiuser_get_uid(info->userid, info->d_gid);
|
||||
}
|
||||
|
||||
visible_mode = 00775 & ~opts->mask;
|
||||
if (info->perm == PERM_PRE_ROOT) {
|
||||
/* Top of multi-user view should always be visible to ensure
|
||||
* secondary users can traverse inside. */
|
||||
visible_mode = 00711;
|
||||
} else if (info->perm == PERM_ANDROID_KNOX_PACKAGE_DATA
|
||||
&& !info->under_android) {
|
||||
visible_mode = visible_mode & ~00006;
|
||||
} else if (info->under_android) {
|
||||
if (info->d_gid == AID_SDCARD_RW) {
|
||||
visible_mode = visible_mode & ~00006;
|
||||
} else {
|
||||
visible_mode = visible_mode & ~00007;
|
||||
}
|
||||
}
|
||||
|
||||
owner_mode = inode->i_mode & 0700;
|
||||
filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
|
||||
inode->i_mode = ((inode->i_mode & S_IFMT) | filtered_mode);
|
||||
}
|
||||
|
||||
/* main function for updating derived permission */
|
||||
inline void update_derived_permission(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *parent;
|
||||
|
||||
if(!dentry || !dentry->d_inode) {
|
||||
printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__);
|
||||
return;
|
||||
}
|
||||
/* FIXME:
|
||||
* 1. need to check whether the dentry is updated or not
|
||||
* 2. remove the root dentry update
|
||||
*/
|
||||
if(IS_ROOT(dentry)) {
|
||||
//setup_default_pre_root_state(dentry->d_inode);
|
||||
} else {
|
||||
parent = dget_parent(dentry);
|
||||
if(parent) {
|
||||
get_derived_permission(parent, dentry);
|
||||
dput(parent);
|
||||
}
|
||||
}
|
||||
fix_derived_permission(dentry->d_inode);
|
||||
}
|
||||
|
||||
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);
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
|
||||
if(parent_info->perm == PERM_ANDROID &&
|
||||
!strcasecmp(dentry->d_name.name, "obb") &&
|
||||
sbi->options.multi_user) {
|
||||
ret = 1;
|
||||
}
|
||||
dput(parent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int is_obbpath_invalid(struct dentry *dent)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sdcardfs_dentry_info *di = SDCARDFS_D(dent);
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb);
|
||||
char *path_buf, *obbpath_s;
|
||||
|
||||
/* check the base obbpath has been changed.
|
||||
* this routine can check an uninitialized obb dentry as well.
|
||||
* regarding the uninitialized obb, refer to the sdcardfs_mkdir() */
|
||||
spin_lock(&di->lock);
|
||||
if(di->orig_path.dentry) {
|
||||
if(!di->lower_path.dentry) {
|
||||
ret = 1;
|
||||
} else {
|
||||
path_get(&di->lower_path);
|
||||
//lower_parent = lock_parent(lower_path->dentry);
|
||||
|
||||
path_buf = kmalloc(PATH_MAX, GFP_ATOMIC);
|
||||
if(!path_buf) {
|
||||
ret = 1;
|
||||
printk(KERN_ERR "sdcardfs: "
|
||||
"fail to allocate path_buf in %s.\n", __func__);
|
||||
} else {
|
||||
obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX);
|
||||
if (d_unhashed(di->lower_path.dentry) ||
|
||||
strcasecmp(sbi->obbpath_s, obbpath_s)) {
|
||||
ret = 1;
|
||||
}
|
||||
kfree(path_buf);
|
||||
}
|
||||
|
||||
//unlock_dir(lower_parent);
|
||||
path_put(&di->lower_path);
|
||||
}
|
||||
}
|
||||
spin_unlock(&di->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int is_base_obbpath(struct dentry *dentry)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dentry *parent = dget_parent(dentry);
|
||||
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
|
||||
spin_lock(&SDCARDFS_D(dentry)->lock);
|
||||
/* if multi_user is true */
|
||||
if(sbi->options.multi_user && parent_info->perm == PERM_PRE_ROOT &&
|
||||
!strcasecmp(dentry->d_name.name, "obb")) {
|
||||
ret = 1;
|
||||
}
|
||||
/* if multi_user is false, /Android/obb is the base obbpath */
|
||||
else if (!sbi->options.multi_user && parent_info->perm == PERM_ANDROID &&
|
||||
!strcasecmp(dentry->d_name.name, "obb")) {
|
||||
ret = 1;
|
||||
}
|
||||
spin_unlock(&SDCARDFS_D(dentry)->lock);
|
||||
dput(parent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The lower_path will be stored to the dentry's orig_path
|
||||
* and the base obbpath will be copyed to the lower_path variable.
|
||||
* if an error returned, there's no change in the lower_path
|
||||
* returns: -ERRNO if error (0: no error) */
|
||||
int setup_obb_dentry(struct dentry *dentry, struct path *lower_path)
|
||||
{
|
||||
int err = 0;
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
struct path obbpath;
|
||||
|
||||
/* A local obb dentry must have its own orig_path to support rmdir
|
||||
* and mkdir of itself. Usually, we expect that the sbi->obbpath
|
||||
* is avaiable on this stage. */
|
||||
sdcardfs_set_orig_path(dentry, lower_path);
|
||||
|
||||
err = kern_path(sbi->obbpath_s,
|
||||
LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath);
|
||||
|
||||
if(!err) {
|
||||
/* the obbpath base has been found */
|
||||
printk(KERN_INFO "sdcardfs: "
|
||||
"the sbi->obbpath is found\n");
|
||||
pathcpy(lower_path, &obbpath);
|
||||
} else {
|
||||
/* if the sbi->obbpath is not available, we can optionally
|
||||
* setup the lower_path with its orig_path.
|
||||
* but, the current implementation just returns an error
|
||||
* because the sdcard daemon also regards this case as
|
||||
* a lookup fail. */
|
||||
printk(KERN_INFO "sdcardfs: "
|
||||
"the sbi->obbpath is not available\n");
|
||||
}
|
||||
return err;
|
||||
}
|
|
@ -1,359 +0,0 @@
|
|||
/*
|
||||
* fs/sdcardfs/file.c
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||
* Sunghwan Yun, Sungjong Seo
|
||||
*
|
||||
* This program has been developed as a stackable file system based on
|
||||
* the WrapFS which written by
|
||||
*
|
||||
* Copyright (c) 1998-2011 Erez Zadok
|
||||
* Copyright (c) 2009 Shrikar Archak
|
||||
* Copyright (c) 2003-2011 Stony Brook University
|
||||
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#include "sdcardfs.h"
|
||||
#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
|
||||
#include <linux/backing-dev.h>
|
||||
#endif
|
||||
|
||||
static ssize_t sdcardfs_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int err;
|
||||
struct file *lower_file;
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
|
||||
struct backing_dev_info *bdi;
|
||||
#endif
|
||||
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
|
||||
#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE
|
||||
if (file->f_mode & FMODE_NOACTIVE) {
|
||||
if (!(lower_file->f_mode & FMODE_NOACTIVE)) {
|
||||
bdi = lower_file->f_mapping->backing_dev_info;
|
||||
lower_file->f_ra.ra_pages = bdi->ra_pages * 2;
|
||||
spin_lock(&lower_file->f_lock);
|
||||
lower_file->f_mode |= FMODE_NOACTIVE;
|
||||
spin_unlock(&lower_file->f_lock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
err = vfs_read(lower_file, buf, count, ppos);
|
||||
/* update our inode atime upon a successful lower read */
|
||||
if (err >= 0)
|
||||
fsstack_copy_attr_atime(dentry->d_inode,
|
||||
lower_file->f_path.dentry->d_inode);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t sdcardfs_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int err = 0;
|
||||
struct file *lower_file;
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
|
||||
/* check disk space */
|
||||
if (!check_min_free_space(dentry, count, 0)) {
|
||||
printk(KERN_INFO "No minimum free space.\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
err = vfs_write(lower_file, buf, count, ppos);
|
||||
/* update our inode times+sizes upon a successful lower write */
|
||||
if (err >= 0) {
|
||||
fsstack_copy_inode_size(dentry->d_inode,
|
||||
lower_file->f_path.dentry->d_inode);
|
||||
fsstack_copy_attr_times(dentry->d_inode,
|
||||
lower_file->f_path.dentry->d_inode);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sdcardfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
int err = 0;
|
||||
struct file *lower_file = NULL;
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
|
||||
lower_file->f_pos = file->f_pos;
|
||||
err = iterate_dir(lower_file, ctx);
|
||||
file->f_pos = lower_file->f_pos;
|
||||
if (err >= 0) /* copy the atime */
|
||||
fsstack_copy_attr_atime(dentry->d_inode,
|
||||
lower_file->f_path.dentry->d_inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
long err = -ENOTTY;
|
||||
struct file *lower_file;
|
||||
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
|
||||
/* XXX: use vfs_ioctl if/when VFS exports it */
|
||||
if (!lower_file || !lower_file->f_op)
|
||||
goto out;
|
||||
if (lower_file->f_op->unlocked_ioctl)
|
||||
err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
long err = -ENOTTY;
|
||||
struct file *lower_file;
|
||||
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
|
||||
/* XXX: use vfs_ioctl if/when VFS exports it */
|
||||
if (!lower_file || !lower_file->f_op)
|
||||
goto out;
|
||||
if (lower_file->f_op->compat_ioctl)
|
||||
err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
int err = 0;
|
||||
bool willwrite;
|
||||
struct file *lower_file;
|
||||
const struct vm_operations_struct *saved_vm_ops = NULL;
|
||||
/* this might be deferred to mmap's writepage */
|
||||
willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags);
|
||||
|
||||
/*
|
||||
* File systems which do not implement ->writepage may use
|
||||
* generic_file_readonly_mmap as their ->mmap op. If you call
|
||||
* generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL.
|
||||
* But we cannot call the lower ->mmap op, so we can't tell that
|
||||
* writeable mappings won't work. Therefore, our only choice is to
|
||||
* check if the lower file system supports the ->writepage, and if
|
||||
* not, return EINVAL (the same error that
|
||||
* generic_file_readonly_mmap returns in that case).
|
||||
*/
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
if (willwrite && !lower_file->f_mapping->a_ops->writepage) {
|
||||
err = -EINVAL;
|
||||
printk(KERN_ERR "sdcardfs: lower file system does not "
|
||||
"support writeable mmap\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* find and save lower vm_ops.
|
||||
*
|
||||
* XXX: the VFS should have a cleaner way of finding the lower vm_ops
|
||||
*/
|
||||
if (!SDCARDFS_F(file)->lower_vm_ops) {
|
||||
err = lower_file->f_op->mmap(lower_file, vma);
|
||||
if (err) {
|
||||
printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */
|
||||
err = do_munmap(current->mm, vma->vm_start,
|
||||
vma->vm_end - vma->vm_start);
|
||||
if (err) {
|
||||
printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Next 3 lines are all I need from generic_file_mmap. I definitely
|
||||
* don't want its test for ->readpage which returns -ENOEXEC.
|
||||
*/
|
||||
file_accessed(file);
|
||||
vma->vm_ops = &sdcardfs_vm_ops;
|
||||
|
||||
file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */
|
||||
if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */
|
||||
SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sdcardfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int err = 0;
|
||||
struct file *lower_file = NULL;
|
||||
struct path lower_path;
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct dentry *parent = dget_parent(dentry);
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
const struct cred *saved_cred = NULL;
|
||||
|
||||
/* don't open unhashed/deleted files */
|
||||
if (d_unhashed(dentry)) {
|
||||
err = -ENOENT;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
|
||||
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);
|
||||
err = -EACCES;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(sbi, saved_cred);
|
||||
|
||||
file->f_mode |= FMODE_NONMAPPABLE;
|
||||
file->private_data =
|
||||
kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);
|
||||
if (!SDCARDFS_F(file)) {
|
||||
err = -ENOMEM;
|
||||
goto out_revert_cred;
|
||||
}
|
||||
|
||||
/* open lower object and link sdcardfs's file struct to lower's */
|
||||
sdcardfs_copy_lower_path(file->f_path.dentry, &lower_path);
|
||||
lower_file = dentry_open(&lower_path, file->f_flags, current_cred());
|
||||
if (IS_ERR(lower_file)) {
|
||||
err = PTR_ERR(lower_file);
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
if (lower_file) {
|
||||
sdcardfs_set_lower_file(file, NULL);
|
||||
fput(lower_file); /* fput calls dput for lower_dentry */
|
||||
}
|
||||
} else {
|
||||
sdcardfs_set_lower_file(file, lower_file);
|
||||
}
|
||||
|
||||
if (err)
|
||||
kfree(SDCARDFS_F(file));
|
||||
else {
|
||||
mutex_lock(&inode->i_mutex);
|
||||
sdcardfs_copy_inode_attr(inode, sdcardfs_lower_inode(inode));
|
||||
fix_derived_permission(inode);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
}
|
||||
|
||||
out_revert_cred:
|
||||
REVERT_CRED(saved_cred);
|
||||
out_err:
|
||||
dput(parent);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sdcardfs_flush(struct file *file, fl_owner_t id)
|
||||
{
|
||||
int err = 0;
|
||||
struct file *lower_file = NULL;
|
||||
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
if (lower_file && lower_file->f_op && lower_file->f_op->flush)
|
||||
err = lower_file->f_op->flush(lower_file, id);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* release all lower object references & free the file info structure */
|
||||
static int sdcardfs_file_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct file *lower_file;
|
||||
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
if (lower_file) {
|
||||
sdcardfs_set_lower_file(file, NULL);
|
||||
fput(lower_file);
|
||||
}
|
||||
|
||||
kfree(SDCARDFS_F(file));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sdcardfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
{
|
||||
int err;
|
||||
struct file *lower_file;
|
||||
struct path lower_path;
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||
err = vfs_fsync_range(lower_file, start, end, datasync);
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sdcardfs_fasync(int fd, struct file *file, int flag)
|
||||
{
|
||||
int err = 0;
|
||||
struct file *lower_file = NULL;
|
||||
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
if (lower_file->f_op && lower_file->f_op->fasync)
|
||||
err = lower_file->f_op->fasync(fd, lower_file, flag);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct file *sdcardfs_get_lower_file(struct file *f)
|
||||
{
|
||||
return sdcardfs_lower_file(f);
|
||||
}
|
||||
|
||||
const struct file_operations sdcardfs_main_fops = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = sdcardfs_read,
|
||||
.write = sdcardfs_write,
|
||||
.unlocked_ioctl = sdcardfs_unlocked_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = sdcardfs_compat_ioctl,
|
||||
#endif
|
||||
.mmap = sdcardfs_mmap,
|
||||
.open = sdcardfs_open,
|
||||
.flush = sdcardfs_flush,
|
||||
.release = sdcardfs_file_release,
|
||||
.fsync = sdcardfs_fsync,
|
||||
.fasync = sdcardfs_fasync,
|
||||
.get_lower_file = sdcardfs_get_lower_file,
|
||||
};
|
||||
|
||||
/* trimmed directory options */
|
||||
const struct file_operations sdcardfs_dir_fops = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
.iterate = sdcardfs_readdir,
|
||||
.unlocked_ioctl = sdcardfs_unlocked_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = sdcardfs_compat_ioctl,
|
||||
#endif
|
||||
.open = sdcardfs_open,
|
||||
.release = sdcardfs_file_release,
|
||||
.flush = sdcardfs_flush,
|
||||
.fsync = sdcardfs_fsync,
|
||||
.fasync = sdcardfs_fasync,
|
||||
.get_lower_file = sdcardfs_get_lower_file,
|
||||
};
|
|
@ -1,894 +0,0 @@
|
|||
/*
|
||||
* fs/sdcardfs/inode.c
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||
* Sunghwan Yun, Sungjong Seo
|
||||
*
|
||||
* This program has been developed as a stackable file system based on
|
||||
* the WrapFS which written by
|
||||
*
|
||||
* Copyright (c) 1998-2011 Erez Zadok
|
||||
* Copyright (c) 2009 Shrikar Archak
|
||||
* Copyright (c) 2003-2011 Stony Brook University
|
||||
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#include "sdcardfs.h"
|
||||
|
||||
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
|
||||
const struct cred * override_fsids(uid_t fsuid, gid_t fsgid)
|
||||
{
|
||||
struct cred * cred;
|
||||
const struct cred * old_cred;
|
||||
|
||||
cred = prepare_creds();
|
||||
if (!cred)
|
||||
return NULL;
|
||||
|
||||
cred->fsuid = fsuid;
|
||||
cred->fsgid = fsgid;
|
||||
|
||||
old_cred = override_creds(cred);
|
||||
|
||||
return old_cred;
|
||||
}
|
||||
|
||||
/* Do not directly use this function, use REVERT_CRED() instead. */
|
||||
void revert_fsids(const struct cred * old_cred)
|
||||
{
|
||||
const struct cred * cur_cred;
|
||||
|
||||
cur_cred = current->cred;
|
||||
revert_creds(old_cred);
|
||||
put_cred(cur_cred);
|
||||
}
|
||||
|
||||
static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode, bool excl)
|
||||
{
|
||||
int err = 0;
|
||||
struct dentry *lower_dentry;
|
||||
struct dentry *lower_parent_dentry = NULL;
|
||||
struct path lower_path;
|
||||
const struct cred *saved_cred = NULL;
|
||||
|
||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
||||
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);
|
||||
err = -EACCES;
|
||||
goto out_eacces;
|
||||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
|
||||
|
||||
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 0664 */
|
||||
mode = (mode & S_IFMT) | 00664;
|
||||
err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, true);
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
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);
|
||||
|
||||
out:
|
||||
mnt_drop_write(lower_path.mnt);
|
||||
out_unlock:
|
||||
unlock_dir(lower_parent_dentry);
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
REVERT_CRED(saved_cred);
|
||||
out_eacces:
|
||||
return err;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *new_dentry)
|
||||
{
|
||||
struct dentry *lower_old_dentry;
|
||||
struct dentry *lower_new_dentry;
|
||||
struct dentry *lower_dir_dentry;
|
||||
u64 file_size_save;
|
||||
int err;
|
||||
struct path lower_old_path, lower_new_path;
|
||||
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
|
||||
|
||||
file_size_save = i_size_read(old_dentry->d_inode);
|
||||
sdcardfs_get_lower_path(old_dentry, &lower_old_path);
|
||||
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
|
||||
lower_old_dentry = lower_old_path.dentry;
|
||||
lower_new_dentry = lower_new_path.dentry;
|
||||
lower_dir_dentry = lock_parent(lower_new_dentry);
|
||||
|
||||
err = mnt_want_write(lower_new_path.mnt);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
|
||||
lower_new_dentry);
|
||||
if (err || !lower_new_dentry->d_inode)
|
||||
goto out;
|
||||
|
||||
err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path);
|
||||
if (err)
|
||||
goto out;
|
||||
fsstack_copy_attr_times(dir, lower_new_dentry->d_inode);
|
||||
fsstack_copy_inode_size(dir, lower_new_dentry->d_inode);
|
||||
set_nlink(old_dentry->d_inode,
|
||||
sdcardfs_lower_inode(old_dentry->d_inode)->i_nlink);
|
||||
i_size_write(new_dentry->d_inode, file_size_save);
|
||||
out:
|
||||
mnt_drop_write(lower_new_path.mnt);
|
||||
out_unlock:
|
||||
unlock_dir(lower_dir_dentry);
|
||||
sdcardfs_put_lower_path(old_dentry, &lower_old_path);
|
||||
sdcardfs_put_lower_path(new_dentry, &lower_new_path);
|
||||
REVERT_CRED();
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int err;
|
||||
struct dentry *lower_dentry;
|
||||
struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
|
||||
struct dentry *lower_dir_dentry;
|
||||
struct path lower_path;
|
||||
const struct cred *saved_cred = NULL;
|
||||
|
||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
||||
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);
|
||||
err = -EACCES;
|
||||
goto out_eacces;
|
||||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
|
||||
|
||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||
lower_dentry = lower_path.dentry;
|
||||
dget(lower_dentry);
|
||||
lower_dir_dentry = lock_parent(lower_dentry);
|
||||
|
||||
err = mnt_want_write(lower_path.mnt);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
err = vfs_unlink(lower_dir_inode, lower_dentry);
|
||||
|
||||
/*
|
||||
* Note: unlinking on top of NFS can cause silly-renamed files.
|
||||
* Trying to delete such files results in EBUSY from NFS
|
||||
* below. Silly-renamed files will get deleted by NFS later on, so
|
||||
* we just need to detect them here and treat such EBUSY errors as
|
||||
* if the upper file was successfully deleted.
|
||||
*/
|
||||
if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED)
|
||||
err = 0;
|
||||
if (err)
|
||||
goto out;
|
||||
fsstack_copy_attr_times(dir, lower_dir_inode);
|
||||
fsstack_copy_inode_size(dir, lower_dir_inode);
|
||||
set_nlink(dentry->d_inode,
|
||||
sdcardfs_lower_inode(dentry->d_inode)->i_nlink);
|
||||
dentry->d_inode->i_ctime = dir->i_ctime;
|
||||
d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */
|
||||
out:
|
||||
mnt_drop_write(lower_path.mnt);
|
||||
out_unlock:
|
||||
unlock_dir(lower_dir_dentry);
|
||||
dput(lower_dentry);
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
REVERT_CRED(saved_cred);
|
||||
out_eacces:
|
||||
return err;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
const char *symname)
|
||||
{
|
||||
int err = 0;
|
||||
struct dentry *lower_dentry;
|
||||
struct dentry *lower_parent_dentry = NULL;
|
||||
struct path lower_path;
|
||||
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
|
||||
|
||||
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;
|
||||
err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, symname);
|
||||
if (err)
|
||||
goto out;
|
||||
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);
|
||||
|
||||
out:
|
||||
mnt_drop_write(lower_path.mnt);
|
||||
out_unlock:
|
||||
unlock_dir(lower_parent_dentry);
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
REVERT_CRED();
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int touch(char *abs_path, mode_t mode) {
|
||||
struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode);
|
||||
if (IS_ERR(filp)) {
|
||||
if (PTR_ERR(filp) == -EEXIST) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n",
|
||||
abs_path, PTR_ERR(filp));
|
||||
return PTR_ERR(filp);
|
||||
}
|
||||
}
|
||||
filp_close(filp, current->files);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
||||
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);
|
||||
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) {
|
||||
unlock_dir(lower_parent_dentry);
|
||||
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) {
|
||||
unlock_dir(lower_parent_dentry);
|
||||
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) {
|
||||
unlock_dir(lower_parent_dentry);
|
||||
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);
|
||||
|
||||
unlock_dir(lower_parent_dentry);
|
||||
|
||||
if (!strcasecmp(dentry->d_name.name, "obb") && (pi->perm == PERM_ANDROID))
|
||||
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(&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:
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
out_revert:
|
||||
REVERT_CRED(saved_cred);
|
||||
out_eacces:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct dentry *lower_dentry;
|
||||
struct dentry *lower_dir_dentry;
|
||||
int err;
|
||||
struct path lower_path;
|
||||
const struct cred *saved_cred = NULL;
|
||||
//char *path_s = NULL;
|
||||
|
||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
||||
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);
|
||||
err = -EACCES;
|
||||
goto out_eacces;
|
||||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
|
||||
|
||||
/* sdcardfs_get_real_lower(): in case of remove an user's obb dentry
|
||||
* the dentry on the original path should be deleted. */
|
||||
sdcardfs_get_real_lower(dentry, &lower_path);
|
||||
|
||||
lower_dentry = lower_path.dentry;
|
||||
lower_dir_dentry = lock_parent(lower_dentry);
|
||||
|
||||
err = mnt_want_write(lower_path.mnt);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
d_drop(dentry); /* drop our dentry on success (why not VFS's job?) */
|
||||
if (dentry->d_inode)
|
||||
clear_nlink(dentry->d_inode);
|
||||
fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
|
||||
fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode);
|
||||
set_nlink(dir, lower_dir_dentry->d_inode->i_nlink);
|
||||
|
||||
out:
|
||||
mnt_drop_write(lower_path.mnt);
|
||||
out_unlock:
|
||||
unlock_dir(lower_dir_dentry);
|
||||
sdcardfs_put_real_lower(dentry, &lower_path);
|
||||
REVERT_CRED(saved_cred);
|
||||
out_eacces:
|
||||
return err;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
dev_t dev)
|
||||
{
|
||||
int err = 0;
|
||||
struct dentry *lower_dentry;
|
||||
struct dentry *lower_parent_dentry = NULL;
|
||||
struct path lower_path;
|
||||
|
||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb));
|
||||
|
||||
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;
|
||||
err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
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);
|
||||
|
||||
out:
|
||||
mnt_drop_write(lower_path.mnt);
|
||||
out_unlock:
|
||||
unlock_dir(lower_parent_dentry);
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
REVERT_CRED();
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The locking rules in sdcardfs_rename are complex. We could use a simpler
|
||||
* superblock-level name-space lock for renames and copy-ups.
|
||||
*/
|
||||
static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry)
|
||||
{
|
||||
int err = 0;
|
||||
struct dentry *lower_old_dentry = NULL;
|
||||
struct dentry *lower_new_dentry = NULL;
|
||||
struct dentry *lower_old_dir_dentry = NULL;
|
||||
struct dentry *lower_new_dir_dentry = NULL;
|
||||
struct dentry *trap = NULL;
|
||||
struct dentry *new_parent = NULL;
|
||||
struct path lower_old_path, lower_new_path;
|
||||
const struct cred *saved_cred = NULL;
|
||||
|
||||
if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) ||
|
||||
!check_caller_access_to_name(new_dir, new_dentry->d_name.name)) {
|
||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||
" new_dentry: %s, task:%s\n",
|
||||
__func__, new_dentry->d_name.name, current->comm);
|
||||
err = -EACCES;
|
||||
goto out_eacces;
|
||||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred);
|
||||
|
||||
sdcardfs_get_real_lower(old_dentry, &lower_old_path);
|
||||
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
|
||||
lower_old_dentry = lower_old_path.dentry;
|
||||
lower_new_dentry = lower_new_path.dentry;
|
||||
lower_old_dir_dentry = dget_parent(lower_old_dentry);
|
||||
lower_new_dir_dentry = dget_parent(lower_new_dentry);
|
||||
|
||||
trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
|
||||
/* source should not be ancestor of target */
|
||||
if (trap == lower_old_dentry) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/* target should not be ancestor of source */
|
||||
if (trap == lower_new_dentry) {
|
||||
err = -ENOTEMPTY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = mnt_want_write(lower_old_path.mnt);
|
||||
if (err)
|
||||
goto out;
|
||||
err = mnt_want_write(lower_new_path.mnt);
|
||||
if (err)
|
||||
goto out_drop_old_write;
|
||||
|
||||
err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
|
||||
lower_new_dir_dentry->d_inode, lower_new_dentry);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* Copy attrs from lower dir, but i_uid/i_gid */
|
||||
sdcardfs_copy_inode_attr(new_dir, lower_new_dir_dentry->d_inode);
|
||||
fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode);
|
||||
fix_derived_permission(new_dir);
|
||||
if (new_dir != old_dir) {
|
||||
sdcardfs_copy_inode_attr(old_dir, lower_old_dir_dentry->d_inode);
|
||||
fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode);
|
||||
fix_derived_permission(old_dir);
|
||||
/* update the derived permission of the old_dentry
|
||||
* with its new parent
|
||||
*/
|
||||
new_parent = dget_parent(new_dentry);
|
||||
if(new_parent) {
|
||||
if(old_dentry->d_inode) {
|
||||
get_derived_permission(new_parent, old_dentry);
|
||||
fix_derived_permission(old_dentry->d_inode);
|
||||
}
|
||||
dput(new_parent);
|
||||
}
|
||||
}
|
||||
spin_lock(&old_dentry->d_lock);
|
||||
old_dentry->d_flags |= DCACHE_WILL_INVALIDATE;
|
||||
spin_unlock(&old_dentry->d_lock);
|
||||
|
||||
out_err:
|
||||
mnt_drop_write(lower_new_path.mnt);
|
||||
out_drop_old_write:
|
||||
mnt_drop_write(lower_old_path.mnt);
|
||||
out:
|
||||
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
|
||||
dput(lower_old_dir_dentry);
|
||||
dput(lower_new_dir_dentry);
|
||||
sdcardfs_put_real_lower(old_dentry, &lower_old_path);
|
||||
sdcardfs_put_lower_path(new_dentry, &lower_new_path);
|
||||
REVERT_CRED(saved_cred);
|
||||
out_eacces:
|
||||
return err;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int sdcardfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
|
||||
{
|
||||
int err;
|
||||
struct dentry *lower_dentry;
|
||||
struct path lower_path;
|
||||
/* XXX readlink does not requires overriding credential */
|
||||
|
||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||
lower_dentry = lower_path.dentry;
|
||||
if (!lower_dentry->d_inode->i_op ||
|
||||
!lower_dentry->d_inode->i_op->readlink) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = lower_dentry->d_inode->i_op->readlink(lower_dentry,
|
||||
buf, bufsiz);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
fsstack_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode);
|
||||
|
||||
out:
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
static void *sdcardfs_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
char *buf;
|
||||
int len = PAGE_SIZE, err;
|
||||
mm_segment_t old_fs;
|
||||
|
||||
/* This is freed by the put_link method assuming a successful call. */
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
buf = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read the symlink, and then we will follow it */
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
err = sdcardfs_readlink(dentry, buf, len);
|
||||
set_fs(old_fs);
|
||||
if (err < 0) {
|
||||
kfree(buf);
|
||||
buf = ERR_PTR(err);
|
||||
} else {
|
||||
buf[err] = '\0';
|
||||
}
|
||||
out:
|
||||
nd_set_link(nd, buf);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* this @nd *IS* still used */
|
||||
static void sdcardfs_put_link(struct dentry *dentry, struct nameidata *nd,
|
||||
void *cookie)
|
||||
{
|
||||
char *buf = nd_get_link(nd);
|
||||
if (!IS_ERR(buf)) /* free the char* */
|
||||
kfree(buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sdcardfs_permission(struct inode *inode, int mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Permission check on sdcardfs inode.
|
||||
* Calling process should have AID_SDCARD_RW permission
|
||||
*/
|
||||
err = generic_permission(inode, mask);
|
||||
|
||||
/* XXX
|
||||
* Original sdcardfs code calls inode_permission(lower_inode,.. )
|
||||
* for checking inode permission. But doing such things here seems
|
||||
* duplicated work, because the functions called after this func,
|
||||
* such as vfs_create, vfs_unlink, vfs_rename, and etc,
|
||||
* does exactly same thing, i.e., they calls inode_permission().
|
||||
* So we just let they do the things.
|
||||
* If there are any security hole, just uncomment following if block.
|
||||
*/
|
||||
#if 0
|
||||
if (!err) {
|
||||
/*
|
||||
* Permission check on lower_inode(=EXT4).
|
||||
* we check it with AID_MEDIA_RW permission
|
||||
*/
|
||||
struct inode *lower_inode;
|
||||
OVERRIDE_CRED(SDCARDFS_SB(inode->sb));
|
||||
|
||||
lower_inode = sdcardfs_lower_inode(inode);
|
||||
err = inode_permission(lower_inode, mask);
|
||||
|
||||
REVERT_CRED();
|
||||
}
|
||||
#endif
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
parent = dget_parent(dentry);
|
||||
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
|
||||
{
|
||||
int err = 0;
|
||||
struct dentry *lower_dentry;
|
||||
struct inode *inode;
|
||||
struct inode *lower_inode;
|
||||
struct path lower_path;
|
||||
struct iattr lower_ia;
|
||||
struct dentry *parent;
|
||||
|
||||
inode = dentry->d_inode;
|
||||
|
||||
/*
|
||||
* Check if user has permission to change inode. We don't check if
|
||||
* this user can change the lower inode: that should happen when
|
||||
* calling notify_change on the lower inode.
|
||||
*/
|
||||
err = inode_change_ok(inode, ia);
|
||||
|
||||
/* no vfs_XXX operations required, cred overriding will be skipped. wj*/
|
||||
if (!err) {
|
||||
/* check the Android group ID */
|
||||
parent = dget_parent(dentry);
|
||||
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
|
||||
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);
|
||||
err = -EACCES;
|
||||
}
|
||||
dput(parent);
|
||||
}
|
||||
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||
lower_dentry = lower_path.dentry;
|
||||
lower_inode = sdcardfs_lower_inode(inode);
|
||||
|
||||
/* prepare our own lower struct iattr (with the lower file) */
|
||||
memcpy(&lower_ia, ia, sizeof(lower_ia));
|
||||
if (ia->ia_valid & ATTR_FILE)
|
||||
lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file);
|
||||
|
||||
lower_ia.ia_valid &= ~(ATTR_UID | ATTR_GID | ATTR_MODE);
|
||||
|
||||
/*
|
||||
* If shrinking, first truncate upper level to cancel writing dirty
|
||||
* pages beyond the new eof; and also if its' maxbytes is more
|
||||
* limiting (fail with -EFBIG before making any change to the lower
|
||||
* level). There is no need to vmtruncate the upper level
|
||||
* afterwards in the other cases: we fsstack_copy_inode_size from
|
||||
* the lower level.
|
||||
*/
|
||||
if (ia->ia_valid & ATTR_SIZE) {
|
||||
loff_t oldsize;
|
||||
err = inode_newsize_ok(inode, ia->ia_size);
|
||||
if (err)
|
||||
goto out;
|
||||
/* This code from truncate_setsize(). We need to add spin_lock
|
||||
* to avoid race condition with fsstack_copy_inode_size() */
|
||||
oldsize = i_size_read(inode);
|
||||
if (sizeof(ia->ia_size) > sizeof(long))
|
||||
spin_lock(&inode->i_lock);
|
||||
i_size_write(inode, ia->ia_size);
|
||||
if (sizeof(ia->ia_size) > sizeof(long))
|
||||
spin_unlock(&inode->i_lock);
|
||||
if (ia->ia_size > oldsize)
|
||||
pagecache_isize_extended(inode, oldsize, ia->ia_size);
|
||||
truncate_pagecache(inode, oldsize, ia->ia_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* mode change is for clearing setuid/setgid bits. Allow lower fs
|
||||
* to interpret this in its own way.
|
||||
*/
|
||||
if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
|
||||
lower_ia.ia_valid &= ~ATTR_MODE;
|
||||
|
||||
/* notify the (possibly copied-up) lower inode */
|
||||
/*
|
||||
* Note: we use lower_dentry->d_inode, because lower_inode may be
|
||||
* unlinked (no inode->i_sb and i_ino==0. This happens if someone
|
||||
* tries to open(), unlink(), then ftruncate() a file.
|
||||
*/
|
||||
mutex_lock(&lower_dentry->d_inode->i_mutex);
|
||||
err = notify_change(lower_dentry, &lower_ia); /* note: lower_ia */
|
||||
mutex_unlock(&lower_dentry->d_inode->i_mutex);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* get attributes from the lower inode, i_mutex held */
|
||||
sdcardfs_copy_inode_attr(inode, lower_inode);
|
||||
/* update derived permission of the upper inode */
|
||||
fix_derived_permission(inode);
|
||||
|
||||
/*
|
||||
* Not running fsstack_copy_inode_size(inode, lower_inode), because
|
||||
* VFS should update our inode size, and notify_change on
|
||||
* lower_inode should update its size.
|
||||
*/
|
||||
|
||||
out:
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
out_err:
|
||||
return err;
|
||||
}
|
||||
|
||||
const struct inode_operations sdcardfs_symlink_iops = {
|
||||
.permission = sdcardfs_permission,
|
||||
.setattr = sdcardfs_setattr,
|
||||
#ifdef SDCARD_FS_XATTR
|
||||
.setxattr = sdcardfs_setxattr,
|
||||
.getxattr = sdcardfs_getxattr,
|
||||
.listxattr = sdcardfs_listxattr,
|
||||
.removexattr = sdcardfs_removexattr,
|
||||
#endif // SDCARD_FS_XATTR
|
||||
/* XXX Following operations are implemented,
|
||||
* but FUSE(sdcard) or FAT does not support them
|
||||
* These methods are *NOT* perfectly tested.
|
||||
.readlink = sdcardfs_readlink,
|
||||
.follow_link = sdcardfs_follow_link,
|
||||
.put_link = sdcardfs_put_link,
|
||||
*/
|
||||
};
|
||||
|
||||
const struct inode_operations sdcardfs_dir_iops = {
|
||||
.create = sdcardfs_create,
|
||||
.lookup = sdcardfs_lookup,
|
||||
.permission = sdcardfs_permission,
|
||||
.unlink = sdcardfs_unlink,
|
||||
.mkdir = sdcardfs_mkdir,
|
||||
.rmdir = sdcardfs_rmdir,
|
||||
.rename = sdcardfs_rename,
|
||||
.setattr = sdcardfs_setattr,
|
||||
.getattr = sdcardfs_getattr,
|
||||
#ifdef SDCARD_FS_XATTR
|
||||
.setxattr = sdcardfs_setxattr,
|
||||
.getxattr = sdcardfs_getxattr,
|
||||
.listxattr = sdcardfs_listxattr,
|
||||
.removexattr = sdcardfs_removexattr,
|
||||
#endif // SDCARD_FS_XATTR
|
||||
/* XXX Following operations are implemented,
|
||||
* but FUSE(sdcard) or FAT does not support them
|
||||
* These methods are *NOT* perfectly tested.
|
||||
.symlink = sdcardfs_symlink,
|
||||
.link = sdcardfs_link,
|
||||
.mknod = sdcardfs_mknod,
|
||||
*/
|
||||
};
|
||||
|
||||
const struct inode_operations sdcardfs_main_iops = {
|
||||
.permission = sdcardfs_permission,
|
||||
.setattr = sdcardfs_setattr,
|
||||
.getattr = sdcardfs_getattr,
|
||||
#ifdef SDCARD_FS_XATTR
|
||||
.setxattr = sdcardfs_setxattr,
|
||||
.getxattr = sdcardfs_getxattr,
|
||||
.listxattr = sdcardfs_listxattr,
|
||||
.removexattr = sdcardfs_removexattr,
|
||||
#endif // SDCARDFS_XATTR
|
||||
};
|
|
@ -1,381 +0,0 @@
|
|||
/*
|
||||
* fs/sdcardfs/lookup.c
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||
* Sunghwan Yun, Sungjong Seo
|
||||
*
|
||||
* This program has been developed as a stackable file system based on
|
||||
* the WrapFS which written by
|
||||
*
|
||||
* Copyright (c) 1998-2011 Erez Zadok
|
||||
* Copyright (c) 2009 Shrikar Archak
|
||||
* Copyright (c) 2003-2011 Stony Brook University
|
||||
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#include "sdcardfs.h"
|
||||
#include "linux/delay.h"
|
||||
|
||||
/* The dentry cache is just so we have properly sized dentries */
|
||||
static struct kmem_cache *sdcardfs_dentry_cachep;
|
||||
|
||||
int sdcardfs_init_dentry_cache(void)
|
||||
{
|
||||
sdcardfs_dentry_cachep =
|
||||
kmem_cache_create("sdcardfs_dentry",
|
||||
sizeof(struct sdcardfs_dentry_info),
|
||||
0, SLAB_RECLAIM_ACCOUNT, NULL);
|
||||
|
||||
return sdcardfs_dentry_cachep ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
void sdcardfs_destroy_dentry_cache(void)
|
||||
{
|
||||
if (sdcardfs_dentry_cachep)
|
||||
kmem_cache_destroy(sdcardfs_dentry_cachep);
|
||||
}
|
||||
|
||||
void free_dentry_private_data(struct dentry *dentry)
|
||||
{
|
||||
if (!dentry || !dentry->d_fsdata)
|
||||
return;
|
||||
kmem_cache_free(sdcardfs_dentry_cachep, dentry->d_fsdata);
|
||||
dentry->d_fsdata = NULL;
|
||||
}
|
||||
|
||||
/* allocate new dentry private data */
|
||||
int new_dentry_private_data(struct dentry *dentry)
|
||||
{
|
||||
struct sdcardfs_dentry_info *info = SDCARDFS_D(dentry);
|
||||
|
||||
/* use zalloc to init dentry_info.lower_path */
|
||||
info = kmem_cache_zalloc(sdcardfs_dentry_cachep, GFP_ATOMIC);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&info->lock);
|
||||
dentry->d_fsdata = info;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdcardfs_inode_test(struct inode *inode, void *candidate_lower_inode)
|
||||
{
|
||||
/* if a lower_inode should have many upper inodes, (like obb)
|
||||
sdcardfs_iget() will offer many inodes
|
||||
because test func always will return fail although they have same hash */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdcardfs_inode_set(struct inode *inode, void *lower_inode)
|
||||
{
|
||||
/* we do actual inode initialization in sdcardfs_iget */
|
||||
return 0;
|
||||
}
|
||||
|
||||
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, don't need to hold i_mutex here */
|
||||
sdcardfs_copy_inode_attr(inode, lower_inode);
|
||||
fsstack_copy_inode_size(inode, lower_inode);
|
||||
|
||||
fix_derived_permission(inode);
|
||||
|
||||
unlock_new_inode(inode);
|
||||
return inode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Connect a sdcardfs inode dentry/inode with several lower ones. This is
|
||||
* the classic stackable file system "vnode interposition" action.
|
||||
*
|
||||
* @dentry: sdcardfs's dentry which interposes on lower one
|
||||
* @sb: sdcardfs's super_block
|
||||
* @lower_path: the lower path (caller does path_get/put)
|
||||
*/
|
||||
int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
|
||||
struct path *lower_path)
|
||||
{
|
||||
int err = 0;
|
||||
struct inode *inode;
|
||||
struct inode *lower_inode;
|
||||
struct super_block *lower_sb;
|
||||
|
||||
lower_inode = lower_path->dentry->d_inode;
|
||||
lower_sb = sdcardfs_lower_super(sb);
|
||||
|
||||
/* check that the lower file system didn't cross a mount point */
|
||||
if (lower_inode->i_sb != lower_sb) {
|
||||
err = -EXDEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We allocate our new inode below by calling sdcardfs_iget,
|
||||
* which will initialize some of the new inode's fields
|
||||
*/
|
||||
|
||||
/* inherit lower inode number for sdcardfs's inode */
|
||||
inode = sdcardfs_iget(sb, lower_inode);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto out;
|
||||
}
|
||||
|
||||
d_add(dentry, inode);
|
||||
update_derived_permission(dentry);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main driver function for sdcardfs's lookup.
|
||||
*
|
||||
* Returns: NULL (ok), ERR_PTR if an error occurred.
|
||||
* Fills in lower_parent_path with <dentry,mnt> on success.
|
||||
*/
|
||||
static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
|
||||
unsigned int flags, struct path *lower_parent_path)
|
||||
{
|
||||
int err = 0;
|
||||
struct vfsmount *lower_dir_mnt;
|
||||
struct dentry *lower_dir_dentry = NULL;
|
||||
struct dentry *lower_dentry;
|
||||
const char *name;
|
||||
struct path lower_path;
|
||||
struct qstr this;
|
||||
struct sdcardfs_sb_info *sbi;
|
||||
|
||||
sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
/* must initialize dentry operations */
|
||||
d_set_d_op(dentry, &sdcardfs_ci_dops);
|
||||
|
||||
if (IS_ROOT(dentry))
|
||||
goto out;
|
||||
|
||||
name = dentry->d_name.name;
|
||||
|
||||
/* now start the actual lookup procedure */
|
||||
lower_dir_dentry = lower_parent_path->dentry;
|
||||
lower_dir_mnt = lower_parent_path->mnt;
|
||||
|
||||
/* Use vfs_path_lookup to check if the dentry exists or not */
|
||||
if (sbi->options.lower_fs == LOWER_FS_EXT4) {
|
||||
err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name,
|
||||
LOOKUP_CASE_INSENSITIVE, &lower_path);
|
||||
} else if (sbi->options.lower_fs == LOWER_FS_FAT) {
|
||||
err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0,
|
||||
&lower_path);
|
||||
}
|
||||
|
||||
/* no error: handle positive dentries */
|
||||
if (!err) {
|
||||
/* check if the dentry is an obb dentry
|
||||
* if true, the lower_inode must be replaced with
|
||||
* the inode of the graft path */
|
||||
|
||||
if(need_graft_path(dentry)) {
|
||||
|
||||
/* setup_obb_dentry()
|
||||
* The lower_path will be stored to the dentry's orig_path
|
||||
* and the base obbpath will be copyed to the lower_path variable.
|
||||
* if an error returned, there's no change in the lower_path
|
||||
* returns: -ERRNO if error (0: no error) */
|
||||
err = setup_obb_dentry(dentry, &lower_path);
|
||||
|
||||
if(err) {
|
||||
/* if the sbi->obbpath is not available, we can optionally
|
||||
* setup the lower_path with its orig_path.
|
||||
* but, the current implementation just returns an error
|
||||
* because the sdcard daemon also regards this case as
|
||||
* a lookup fail. */
|
||||
printk(KERN_INFO "sdcardfs: base obbpath is not available\n");
|
||||
sdcardfs_put_reset_orig_path(dentry);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
sdcardfs_set_lower_path(dentry, &lower_path);
|
||||
err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path);
|
||||
if (err) /* path_put underlying path on error */
|
||||
sdcardfs_put_reset_lower_path(dentry);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't consider ENOENT an error, and we want to return a
|
||||
* negative dentry.
|
||||
*/
|
||||
if (err && err != -ENOENT)
|
||||
goto out;
|
||||
|
||||
/* instatiate a new negative dentry */
|
||||
this.name = name;
|
||||
this.len = strlen(name);
|
||||
lower_dentry = d_hash_and_lookup(lower_dir_dentry, &this);
|
||||
if (unlikely(IS_ERR(lower_dentry))) {
|
||||
err = PTR_ERR(lower_dentry);
|
||||
goto out;
|
||||
}
|
||||
if (lower_dentry)
|
||||
goto setup_lower;
|
||||
|
||||
lower_dentry = d_alloc(lower_dir_dentry, &this);
|
||||
if (!lower_dentry) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
d_add(lower_dentry, NULL); /* instantiate and hash */
|
||||
|
||||
setup_lower:
|
||||
lower_path.dentry = lower_dentry;
|
||||
lower_path.mnt = mntget(lower_dir_mnt);
|
||||
sdcardfs_set_lower_path(dentry, &lower_path);
|
||||
|
||||
/*
|
||||
* If the intent is to create a file, then don't return an error, so
|
||||
* the VFS will continue the process of making this negative dentry
|
||||
* into a positive one.
|
||||
*/
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/*
|
||||
* On success:
|
||||
* fills dentry object appropriate values and returns NULL.
|
||||
* On fail (== error)
|
||||
* returns error ptr
|
||||
*
|
||||
* @dir : Parent inode. It is locked (dir->i_mutex)
|
||||
* @dentry : Target dentry to lookup. we should set each of fields.
|
||||
* (dentry->d_name is initialized already)
|
||||
* @nd : nameidata of parent inode
|
||||
*/
|
||||
struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct dentry *ret = NULL, *parent;
|
||||
struct path lower_parent_path;
|
||||
int err = 0;
|
||||
const struct cred *saved_cred = NULL;
|
||||
|
||||
parent = dget_parent(dentry);
|
||||
|
||||
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
|
||||
ret = ERR_PTR(-EACCES);
|
||||
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);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* save current_cred and override it */
|
||||
OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred);
|
||||
|
||||
sdcardfs_get_lower_path(parent, &lower_parent_path);
|
||||
|
||||
/* allocate dentry private data. We free it in ->d_release */
|
||||
err = new_dentry_private_data(dentry);
|
||||
if (err) {
|
||||
ret = ERR_PTR(err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path);
|
||||
if (IS_ERR(ret))
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
if (ret)
|
||||
dentry = ret;
|
||||
if (dentry->d_inode) {
|
||||
fsstack_copy_attr_times(dentry->d_inode,
|
||||
sdcardfs_lower_inode(dentry->d_inode));
|
||||
/* get drived permission */
|
||||
get_derived_permission(parent, dentry);
|
||||
fix_derived_permission(dentry->d_inode);
|
||||
}
|
||||
/* update parent directory's atime */
|
||||
fsstack_copy_attr_atime(parent->d_inode,
|
||||
sdcardfs_lower_inode(parent->d_inode));
|
||||
|
||||
out:
|
||||
sdcardfs_put_lower_path(parent, &lower_parent_path);
|
||||
REVERT_CRED(saved_cred);
|
||||
out_err:
|
||||
dput(parent);
|
||||
return ret;
|
||||
}
|
|
@ -1,447 +0,0 @@
|
|||
/*
|
||||
* fs/sdcardfs/main.c
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||
* Sunghwan Yun, Sungjong Seo
|
||||
*
|
||||
* This program has been developed as a stackable file system based on
|
||||
* the WrapFS which written by
|
||||
*
|
||||
* Copyright (c) 1998-2011 Erez Zadok
|
||||
* Copyright (c) 2009 Shrikar Archak
|
||||
* Copyright (c) 2003-2011 Stony Brook University
|
||||
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#include "sdcardfs.h"
|
||||
#include "version.h"
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/parser.h>
|
||||
#include "../internal.h"
|
||||
|
||||
enum {
|
||||
Opt_low_uid,
|
||||
Opt_low_gid,
|
||||
Opt_gid,
|
||||
Opt_userid,
|
||||
Opt_debug,
|
||||
Opt_lower_fs,
|
||||
Opt_reserved_mb,
|
||||
Opt_mask,
|
||||
Opt_multi_user,
|
||||
Opt_label,
|
||||
Opt_type,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
static const match_table_t sdcardfs_tokens = {
|
||||
{Opt_low_uid, "low_uid=%u"},
|
||||
{Opt_low_gid, "low_gid=%u"},
|
||||
{Opt_gid, "gid=%u"},
|
||||
{Opt_userid, "userid=%u"},
|
||||
{Opt_debug, "debug"},
|
||||
{Opt_lower_fs, "lower_fs=%s"},
|
||||
{Opt_reserved_mb, "reserved_mb=%u"},
|
||||
{Opt_mask, "mask=%o"},
|
||||
{Opt_multi_user, "multi_user"},
|
||||
{Opt_label, "label=%s"},
|
||||
{Opt_type, "type=%s"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
static int parse_options(struct super_block *sb, char *options, int silent,
|
||||
int *debug, struct sdcardfs_mount_options *opts)
|
||||
{
|
||||
char *p;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int option;
|
||||
char *string_option;
|
||||
char *label;
|
||||
|
||||
/* by default, we use AID_MEDIA_RW as low_uid, low_gid */
|
||||
opts->fs_low_uid = AID_MEDIA_RW;
|
||||
opts->fs_low_gid = AID_MEDIA_RW;
|
||||
/* by default, userid is 0, gid is AID_EVERYBODY */
|
||||
opts->gid = AID_EVERYBODY;
|
||||
opts->userid = 0;
|
||||
/* by default, we use LOWER_FS_EXT4 as lower fs type */
|
||||
opts->lower_fs = LOWER_FS_EXT4;
|
||||
/* by default, 0MB is reserved */
|
||||
opts->reserved_mb = 0;
|
||||
/* by default, mask is 0 */
|
||||
opts->mask = 0;
|
||||
/* by default, multi_user is false */
|
||||
opts->multi_user = false;
|
||||
opts->label = NULL;
|
||||
opts->type = TYPE_NONE;
|
||||
|
||||
*debug = 0;
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token;
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, sdcardfs_tokens, args);
|
||||
|
||||
switch (token) {
|
||||
case Opt_debug:
|
||||
*debug = 1;
|
||||
break;
|
||||
case Opt_low_uid:
|
||||
if (match_int(&args[0], &option))
|
||||
return 0;
|
||||
opts->fs_low_uid = option;
|
||||
break;
|
||||
case Opt_low_gid:
|
||||
if (match_int(&args[0], &option))
|
||||
return 0;
|
||||
opts->fs_low_gid = option;
|
||||
break;
|
||||
case Opt_gid:
|
||||
if (match_int(&args[0], &option))
|
||||
goto invalid_option;
|
||||
opts->gid = option;
|
||||
break;
|
||||
case Opt_userid:
|
||||
if (match_int(&args[0], &option))
|
||||
goto invalid_option;
|
||||
opts->userid = option;
|
||||
break;
|
||||
case Opt_lower_fs:
|
||||
string_option = match_strdup(&args[0]);
|
||||
if (!string_option)
|
||||
return -ENOMEM;
|
||||
if (!strcmp("ext4", string_option)) {
|
||||
opts->lower_fs = LOWER_FS_EXT4;
|
||||
} else if (!strcmp("fat", string_option)) {
|
||||
opts->lower_fs = LOWER_FS_FAT;
|
||||
} else {
|
||||
kfree(string_option);
|
||||
goto invalid_option;
|
||||
}
|
||||
kfree(string_option);
|
||||
break;
|
||||
case Opt_reserved_mb:
|
||||
if (match_int(&args[0], &option))
|
||||
return 0;
|
||||
opts->reserved_mb = option;
|
||||
break;
|
||||
case Opt_mask:
|
||||
if (match_octal(&args[0], &option))
|
||||
goto invalid_option;
|
||||
opts->mask = option;
|
||||
break;
|
||||
case Opt_multi_user:
|
||||
opts->multi_user = true;
|
||||
break;
|
||||
case Opt_label:
|
||||
label = match_strdup(&args[0]);
|
||||
if (!label)
|
||||
return -ENOMEM;
|
||||
opts->label = label;
|
||||
break;
|
||||
case Opt_type:
|
||||
string_option = match_strdup(&args[0]);
|
||||
if (!string_option)
|
||||
return -ENOMEM;
|
||||
if (!strcmp("default", string_option)) {
|
||||
opts->type = TYPE_DEFAULT;
|
||||
} else if (!strcmp("read", string_option)) {
|
||||
opts->type = TYPE_READ;
|
||||
} else if (!strcmp("write", string_option)) {
|
||||
opts->type = TYPE_WRITE;
|
||||
} else {
|
||||
kfree(string_option);
|
||||
goto invalid_option;
|
||||
}
|
||||
kfree(string_option);
|
||||
break;
|
||||
/* unknown option */
|
||||
default:
|
||||
invalid_option:
|
||||
if (!silent) {
|
||||
printk( KERN_ERR "Unrecognized mount option \"%s\" "
|
||||
"or missing value", p);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (*debug) {
|
||||
printk( KERN_INFO "sdcardfs : options - debug:%d\n", *debug);
|
||||
printk( KERN_INFO "sdcardfs : options - uid:%d\n",
|
||||
opts->fs_low_uid);
|
||||
printk( KERN_INFO "sdcardfs : options - gid:%d\n",
|
||||
opts->fs_low_gid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* our custom d_alloc_root work-alike
|
||||
*
|
||||
* we can't use d_alloc_root if we want to use our own interpose function
|
||||
* unchanged, so we simply call our own "fake" d_alloc_root
|
||||
*/
|
||||
static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb)
|
||||
{
|
||||
struct dentry *ret = NULL;
|
||||
|
||||
if (sb) {
|
||||
static const struct qstr name = {
|
||||
.name = "/",
|
||||
.len = 1
|
||||
};
|
||||
|
||||
ret = __d_alloc(sb, &name);
|
||||
if (ret) {
|
||||
d_set_d_op(ret, &sdcardfs_ci_dops);
|
||||
ret->d_parent = ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is no need to lock the sdcardfs_super_info's rwsem as there is no
|
||||
* way anyone can have a reference to the superblock at this point in time.
|
||||
*/
|
||||
static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
|
||||
void *raw_data, int silent)
|
||||
{
|
||||
int err = 0;
|
||||
int debug;
|
||||
struct super_block *lower_sb;
|
||||
struct path lower_path;
|
||||
struct sdcardfs_sb_info *sb_info;
|
||||
void *pkgl_id;
|
||||
|
||||
printk(KERN_INFO "sdcardfs: version %s\n", SDCARDFS_VERSION);
|
||||
|
||||
if (!dev_name) {
|
||||
printk(KERN_ERR
|
||||
"sdcardfs: read_super: missing dev_name argument\n");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name);
|
||||
printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data);
|
||||
|
||||
/* parse lower path */
|
||||
err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
|
||||
&lower_path);
|
||||
if (err) {
|
||||
printk(KERN_ERR "sdcardfs: error accessing "
|
||||
"lower directory '%s'\n", dev_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* allocate superblock private data */
|
||||
sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL);
|
||||
if (!SDCARDFS_SB(sb)) {
|
||||
printk(KERN_CRIT "sdcardfs: read_super: out of memory\n");
|
||||
err = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
sb_info = sb->s_fs_info;
|
||||
|
||||
/* parse options */
|
||||
err = parse_options(sb, raw_data, silent, &debug, &sb_info->options);
|
||||
if (err) {
|
||||
printk(KERN_ERR "sdcardfs: invalid options or out of memory\n");
|
||||
goto out_freesbi;
|
||||
}
|
||||
|
||||
pkgl_id = packagelist_create();
|
||||
if(IS_ERR(pkgl_id))
|
||||
goto out_freesbi;
|
||||
else
|
||||
sb_info->pkgl_id = pkgl_id;
|
||||
|
||||
/* set the lower superblock field of upper superblock */
|
||||
lower_sb = lower_path.dentry->d_sb;
|
||||
atomic_inc(&lower_sb->s_active);
|
||||
sdcardfs_set_lower_super(sb, lower_sb);
|
||||
|
||||
sb->s_stack_depth = lower_sb->s_stack_depth + 1;
|
||||
if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
|
||||
pr_err("sdcardfs: maximum fs stacking depth exceeded\n");
|
||||
err = -EINVAL;
|
||||
goto out_sput;
|
||||
}
|
||||
|
||||
/* 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_magic = SDCARDFS_SUPER_MAGIC;
|
||||
if (sb_info->options.type != TYPE_NONE)
|
||||
sb->s_op = &sdcardfs_multimount_sops;
|
||||
else
|
||||
sb->s_op = &sdcardfs_sops;
|
||||
|
||||
/* see comment next to the definition of sdcardfs_d_alloc_root */
|
||||
sb->s_root = sdcardfs_d_alloc_root(sb);
|
||||
if (!sb->s_root) {
|
||||
err = -ENOMEM;
|
||||
goto out_sput;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* set the lower dentries for s_root */
|
||||
sdcardfs_set_lower_path(sb->s_root, &lower_path);
|
||||
|
||||
/* call interpose to create the upper level inode */
|
||||
err = sdcardfs_interpose(sb->s_root, sb, &lower_path);
|
||||
if (!err) {
|
||||
/* setup permission policy */
|
||||
if(sb_info->options.multi_user){
|
||||
setup_derived_state(sb->s_root->d_inode,
|
||||
PERM_PRE_ROOT, sb_info->options.userid, AID_ROOT, sb_info->options.gid, false);
|
||||
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
|
||||
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
|
||||
err = prepare_dir(sb_info->obbpath_s,
|
||||
sb_info->options.fs_low_uid,
|
||||
sb_info->options.fs_low_gid, 00775);
|
||||
} else {
|
||||
setup_derived_state(sb->s_root->d_inode,
|
||||
PERM_ROOT, sb_info->options.userid, AID_ROOT, sb_info->options.gid, false);
|
||||
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
|
||||
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
|
||||
}
|
||||
fix_derived_permission(sb->s_root->d_inode);
|
||||
|
||||
sb_info->devpath = kzalloc(PATH_MAX, GFP_KERNEL);
|
||||
if(sb_info->devpath && dev_name)
|
||||
strncpy(sb_info->devpath, dev_name, strlen(dev_name));
|
||||
|
||||
if (!silent && !err)
|
||||
printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n",
|
||||
dev_name, lower_sb->s_type->name);
|
||||
goto out;
|
||||
}
|
||||
/* else error: fall through */
|
||||
|
||||
free_dentry_private_data(sb->s_root);
|
||||
out_freeroot:
|
||||
dput(sb->s_root);
|
||||
out_sput:
|
||||
/* drop refs we took earlier */
|
||||
atomic_dec(&lower_sb->s_active);
|
||||
packagelist_destroy(sb_info->pkgl_id);
|
||||
out_freesbi:
|
||||
kfree(SDCARDFS_SB(sb));
|
||||
sb->s_fs_info = NULL;
|
||||
out_free:
|
||||
path_put(&lower_path);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* A feature which supports mount_nodev() with options */
|
||||
static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data,
|
||||
int (*fill_super)(struct super_block *, const char *, void *, int))
|
||||
|
||||
{
|
||||
int error;
|
||||
struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);
|
||||
|
||||
if (IS_ERR(s))
|
||||
return ERR_CAST(s);
|
||||
|
||||
s->s_flags = flags;
|
||||
|
||||
error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0);
|
||||
if (error) {
|
||||
deactivate_locked_super(s);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
s->s_flags |= MS_ACTIVE;
|
||||
return dget(s->s_root);
|
||||
}
|
||||
|
||||
struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags,
|
||||
const char *dev_name, void *raw_data)
|
||||
{
|
||||
/*
|
||||
* dev_name is a lower_path_name,
|
||||
* raw_data is a option string.
|
||||
*/
|
||||
return mount_nodev_with_options(fs_type, flags, dev_name,
|
||||
raw_data, sdcardfs_read_super);
|
||||
}
|
||||
|
||||
static struct file_system_type sdcardfs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = SDCARDFS_NAME,
|
||||
.mount = sdcardfs_mount,
|
||||
.kill_sb = generic_shutdown_super,
|
||||
.fs_flags = 0,
|
||||
};
|
||||
|
||||
static int __init init_sdcardfs_fs(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
pr_info("Registering sdcardfs " SDCARDFS_VERSION "\n");
|
||||
|
||||
err = sdcardfs_init_inode_cache();
|
||||
if (err)
|
||||
goto out;
|
||||
err = sdcardfs_init_dentry_cache();
|
||||
if (err)
|
||||
goto out;
|
||||
err = packagelist_init();
|
||||
if (err)
|
||||
goto out;
|
||||
err = register_filesystem(&sdcardfs_fs_type);
|
||||
out:
|
||||
if (err) {
|
||||
sdcardfs_destroy_inode_cache();
|
||||
sdcardfs_destroy_dentry_cache();
|
||||
packagelist_exit();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit exit_sdcardfs_fs(void)
|
||||
{
|
||||
sdcardfs_destroy_inode_cache();
|
||||
sdcardfs_destroy_dentry_cache();
|
||||
packagelist_exit();
|
||||
unregister_filesystem(&sdcardfs_fs_type);
|
||||
pr_info("Completed sdcardfs module unload\n");
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Woojoong Lee, Daeho Jeong, Kitae Lee, Yeongjin Gil"
|
||||
" System Memory Lab., Samsung Electronics");
|
||||
MODULE_DESCRIPTION("Sdcardfs " SDCARDFS_VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(init_sdcardfs_fs);
|
||||
module_exit(exit_sdcardfs_fs);
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
* fs/sdcardfs/mmap.c
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||
* Sunghwan Yun, Sungjong Seo
|
||||
*
|
||||
* This program has been developed as a stackable file system based on
|
||||
* the WrapFS which written by
|
||||
*
|
||||
* Copyright (c) 1998-2011 Erez Zadok
|
||||
* Copyright (c) 2009 Shrikar Archak
|
||||
* Copyright (c) 2003-2011 Stony Brook University
|
||||
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#include "sdcardfs.h"
|
||||
|
||||
static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
{
|
||||
int err;
|
||||
struct file *file, *lower_file;
|
||||
const struct vm_operations_struct *lower_vm_ops;
|
||||
struct vm_area_struct lower_vma;
|
||||
|
||||
memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
|
||||
file = lower_vma.vm_file;
|
||||
lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
|
||||
BUG_ON(!lower_vm_ops);
|
||||
|
||||
lower_file = sdcardfs_lower_file(file);
|
||||
/*
|
||||
* XXX: vm_ops->fault may be called in parallel. Because we have to
|
||||
* resort to temporarily changing the vma->vm_file to point to the
|
||||
* lower file, a concurrent invocation of sdcardfs_fault could see a
|
||||
* different value. In this workaround, we keep a different copy of
|
||||
* the vma structure in our stack, so we never expose a different
|
||||
* value of the vma->vm_file called to us, even temporarily. A
|
||||
* better fix would be to change the calling semantics of ->fault to
|
||||
* take an explicit file pointer.
|
||||
*/
|
||||
lower_vma.vm_file = lower_file;
|
||||
err = lower_vm_ops->fault(&lower_vma, vmf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t sdcardfs_direct_IO(int rw, struct kiocb *iocb,
|
||||
const struct iovec *iov, loff_t offset,
|
||||
unsigned long nr_segs)
|
||||
{
|
||||
/*
|
||||
* This function returns zero on purpose in order to support direct IO.
|
||||
* __dentry_open checks a_ops->direct_IO and returns EINVAL if it is null.
|
||||
*
|
||||
* However, this function won't be called by certain file operations
|
||||
* including generic fs functions. * reads and writes are delivered to
|
||||
* the lower file systems and the direct IOs will be handled by them.
|
||||
*
|
||||
* NOTE: exceptionally, on the recent kernels (since Linux 3.8.x),
|
||||
* swap_writepage invokes this function directly.
|
||||
*/
|
||||
printk(KERN_INFO "%s, operation is not supported\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: the default address_space_ops for sdcardfs is empty. We cannot set
|
||||
* our inode->i_mapping->a_ops to NULL because too many code paths expect
|
||||
* the a_ops vector to be non-NULL.
|
||||
*/
|
||||
const struct address_space_operations sdcardfs_aops = {
|
||||
/* empty on purpose */
|
||||
.direct_IO = sdcardfs_direct_IO,
|
||||
};
|
||||
|
||||
const struct vm_operations_struct sdcardfs_vm_ops = {
|
||||
.fault = sdcardfs_fault,
|
||||
};
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* fs/sdcardfs/multiuser.h
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||
* Sunghwan Yun, Sungjong Seo
|
||||
*
|
||||
* This program has been developed as a stackable file system based on
|
||||
* the WrapFS which written by
|
||||
*
|
||||
* Copyright (c) 1998-2011 Erez Zadok
|
||||
* Copyright (c) 2009 Shrikar Archak
|
||||
* Copyright (c) 2003-2011 Stony Brook University
|
||||
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#define MULTIUSER_APP_PER_USER_RANGE 100000
|
||||
|
||||
typedef uid_t userid_t;
|
||||
typedef uid_t appid_t;
|
||||
|
||||
static inline userid_t multiuser_get_user_id(uid_t uid) {
|
||||
return uid / MULTIUSER_APP_PER_USER_RANGE;
|
||||
}
|
||||
|
||||
static inline appid_t multiuser_get_app_id(uid_t uid) {
|
||||
return uid % MULTIUSER_APP_PER_USER_RANGE;
|
||||
}
|
||||
|
||||
static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
|
||||
return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
|
||||
}
|
||||
|
|
@ -1,359 +0,0 @@
|
|||
/*
|
||||
* fs/sdcardfs/packagelist.c
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||
* Sunghwan Yun, Sungjong Seo
|
||||
*
|
||||
* This program has been developed as a stackable file system based on
|
||||
* the WrapFS which written by
|
||||
*
|
||||
* Copyright (c) 1998-2011 Erez Zadok
|
||||
* Copyright (c) 2009 Shrikar Archak
|
||||
* Copyright (c) 2003-2011 Stony Brook University
|
||||
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#include "sdcardfs.h"
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/inotify.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define STRING_BUF_SIZE (512)
|
||||
|
||||
struct hashtable_entry {
|
||||
struct hlist_node hlist;
|
||||
void *key;
|
||||
int value;
|
||||
};
|
||||
|
||||
struct packagelist_data {
|
||||
DECLARE_HASHTABLE(package_to_appid,8);
|
||||
struct mutex hashtable_lock;
|
||||
struct task_struct *thread_id;
|
||||
char read_buf[STRING_BUF_SIZE];
|
||||
char event_buf[STRING_BUF_SIZE];
|
||||
char app_name_buf[STRING_BUF_SIZE];
|
||||
char gids_buf[STRING_BUF_SIZE];
|
||||
};
|
||||
|
||||
static struct kmem_cache *hashtable_entry_cachep;
|
||||
|
||||
/* Path to system-provided mapping of package name to appIds */
|
||||
static const char* const kpackageslist_file = "/data/system/packages.list";
|
||||
/* Supplementary groups to execute with */
|
||||
static const gid_t kgroups[1] = { AID_PACKAGE_INFO };
|
||||
|
||||
static unsigned int str_hash(void *key) {
|
||||
int i;
|
||||
unsigned int h = strlen(key);
|
||||
char *data = (char *)key;
|
||||
|
||||
for (i = 0; i < strlen(key); i++) {
|
||||
h = h * 31 + *data;
|
||||
data++;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
appid_t get_appid(void *pkgl_id, const char *app_name)
|
||||
{
|
||||
struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id;
|
||||
struct hashtable_entry *hash_cur;
|
||||
unsigned int hash = str_hash((void *)app_name);
|
||||
appid_t ret_id;
|
||||
|
||||
//printk(KERN_INFO "sdcardfs: %s: %s, %u\n", __func__, (char *)app_name, hash);
|
||||
mutex_lock(&pkgl_dat->hashtable_lock);
|
||||
hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
|
||||
//printk(KERN_INFO "sdcardfs: %s: %s\n", __func__, (char *)hash_cur->key);
|
||||
if (!strcasecmp(app_name, hash_cur->key)) {
|
||||
ret_id = (appid_t)hash_cur->value;
|
||||
mutex_unlock(&pkgl_dat->hashtable_lock);
|
||||
//printk(KERN_INFO "=> app_id: %d\n", (int)ret_id);
|
||||
return ret_id;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&pkgl_dat->hashtable_lock);
|
||||
//printk(KERN_INFO "=> app_id: %d\n", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Kernel has already enforced everything we returned through
|
||||
* derive_permissions_locked(), so this is used to lock down access
|
||||
* even further, such as enforcing that apps hold sdcard_rw. */
|
||||
int check_caller_access_to_name(struct inode *parent_node, const char* name) {
|
||||
/* Always block security-sensitive files at root */
|
||||
if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) {
|
||||
if (!strcasecmp(name, "autorun.inf")
|
||||
|| !strcasecmp(name, ".android_secure")
|
||||
|| !strcasecmp(name, "android_secure")) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Root always has access; access for any other UIDs should always
|
||||
* be controlled through packages.list. */
|
||||
if (current_fsuid() == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* No extra permissions to enforce */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This function is used when file opening. The open flags must be
|
||||
* checked before calling check_caller_access_to_name() */
|
||||
int open_flags_to_access_mode(int open_flags) {
|
||||
if((open_flags & O_ACCMODE) == O_RDONLY) {
|
||||
return 0; /* R_OK */
|
||||
} else if ((open_flags & O_ACCMODE) == O_WRONLY) {
|
||||
return 1; /* W_OK */
|
||||
} else {
|
||||
/* Probably O_RDRW, but treat as default to be safe */
|
||||
return 1; /* R_OK | W_OK */
|
||||
}
|
||||
}
|
||||
|
||||
static int insert_str_to_int(struct packagelist_data *pkgl_dat, void *key, int value) {
|
||||
struct hashtable_entry *hash_cur;
|
||||
struct hashtable_entry *new_entry;
|
||||
unsigned int hash = str_hash(key);
|
||||
|
||||
//printk(KERN_INFO "sdcardfs: %s: %s: %d, %u\n", __func__, (char *)key, value, hash);
|
||||
hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
|
||||
if (!strcasecmp(key, hash_cur->key)) {
|
||||
hash_cur->value = value;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL);
|
||||
if (!new_entry)
|
||||
return -ENOMEM;
|
||||
new_entry->key = kstrdup(key, GFP_KERNEL);
|
||||
new_entry->value = value;
|
||||
hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remove_str_to_int(struct hashtable_entry *h_entry) {
|
||||
//printk(KERN_INFO "sdcardfs: %s: %s: %d\n", __func__, (char *)h_entry->key, h_entry->value);
|
||||
kfree(h_entry->key);
|
||||
kmem_cache_free(hashtable_entry_cachep, h_entry);
|
||||
}
|
||||
|
||||
/*static void remove_int_to_null(struct hashtable_entry *h_entry) {
|
||||
//printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)h_entry->key, h_entry->value);
|
||||
kmem_cache_free(hashtable_entry_cachep, h_entry);
|
||||
}*/
|
||||
|
||||
static void remove_all_hashentrys(struct packagelist_data *pkgl_dat)
|
||||
{
|
||||
struct hashtable_entry *hash_cur;
|
||||
struct hlist_node *h_t;
|
||||
int i;
|
||||
|
||||
hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist)
|
||||
remove_str_to_int(hash_cur);
|
||||
|
||||
hash_init(pkgl_dat->package_to_appid);
|
||||
}
|
||||
|
||||
static int read_package_list(struct packagelist_data *pkgl_dat) {
|
||||
int ret;
|
||||
int fd;
|
||||
int read_amount;
|
||||
|
||||
printk(KERN_INFO "sdcardfs: read_package_list\n");
|
||||
|
||||
mutex_lock(&pkgl_dat->hashtable_lock);
|
||||
|
||||
remove_all_hashentrys(pkgl_dat);
|
||||
|
||||
fd = sys_open(kpackageslist_file, O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
printk(KERN_ERR "sdcardfs: failed to open package list\n");
|
||||
mutex_unlock(&pkgl_dat->hashtable_lock);
|
||||
return fd;
|
||||
}
|
||||
|
||||
while ((read_amount = sys_read(fd, pkgl_dat->read_buf,
|
||||
sizeof(pkgl_dat->read_buf))) > 0) {
|
||||
int appid;
|
||||
int one_line_len = 0;
|
||||
int additional_read;
|
||||
|
||||
while (one_line_len < read_amount) {
|
||||
if (pkgl_dat->read_buf[one_line_len] == '\n') {
|
||||
one_line_len++;
|
||||
break;
|
||||
}
|
||||
one_line_len++;
|
||||
}
|
||||
additional_read = read_amount - one_line_len;
|
||||
if (additional_read > 0)
|
||||
sys_lseek(fd, -additional_read, SEEK_CUR);
|
||||
|
||||
if (sscanf(pkgl_dat->read_buf, "%s %d %*d %*s %*s %s",
|
||||
pkgl_dat->app_name_buf, &appid,
|
||||
pkgl_dat->gids_buf) == 3) {
|
||||
ret = insert_str_to_int(pkgl_dat, pkgl_dat->app_name_buf, appid);
|
||||
if (ret) {
|
||||
sys_close(fd);
|
||||
mutex_unlock(&pkgl_dat->hashtable_lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sys_close(fd);
|
||||
mutex_unlock(&pkgl_dat->hashtable_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int packagelist_reader(void *thread_data)
|
||||
{
|
||||
struct packagelist_data *pkgl_dat = (struct packagelist_data *)thread_data;
|
||||
struct inotify_event *event;
|
||||
bool active = false;
|
||||
int event_pos;
|
||||
int event_size;
|
||||
int res = 0;
|
||||
int nfd;
|
||||
|
||||
allow_signal(SIGINT);
|
||||
|
||||
nfd = sys_inotify_init();
|
||||
if (nfd < 0) {
|
||||
printk(KERN_ERR "sdcardfs: inotify_init failed: %d\n", nfd);
|
||||
return nfd;
|
||||
}
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
if (signal_pending(current)) {
|
||||
msleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!active) {
|
||||
res = sys_inotify_add_watch(nfd, kpackageslist_file, IN_DELETE_SELF);
|
||||
if (res < 0) {
|
||||
if (res == -ENOENT || res == -EACCES) {
|
||||
/* Framework may not have created yet, sleep and retry */
|
||||
printk(KERN_ERR "sdcardfs: missing packages.list; retrying\n");
|
||||
ssleep(2);
|
||||
printk(KERN_ERR "sdcardfs: missing packages.list_end; retrying\n");
|
||||
continue;
|
||||
} else {
|
||||
printk(KERN_ERR "sdcardfs: inotify_add_watch failed: %d\n", res);
|
||||
goto interruptable_sleep;
|
||||
}
|
||||
}
|
||||
/* Watch above will tell us about any future changes, so
|
||||
* read the current state. */
|
||||
res = read_package_list(pkgl_dat);
|
||||
if (res) {
|
||||
printk(KERN_ERR "sdcardfs: read_package_list failed: %d\n", res);
|
||||
goto interruptable_sleep;
|
||||
}
|
||||
active = true;
|
||||
}
|
||||
|
||||
event_pos = 0;
|
||||
res = sys_read(nfd, pkgl_dat->event_buf, sizeof(pkgl_dat->event_buf));
|
||||
if (res < (int) sizeof(*event)) {
|
||||
if (res == -EINTR)
|
||||
continue;
|
||||
printk(KERN_ERR "sdcardfs: failed to read inotify event: %d\n", res);
|
||||
goto interruptable_sleep;
|
||||
}
|
||||
|
||||
while (res >= (int) sizeof(*event)) {
|
||||
event = (struct inotify_event *) (pkgl_dat->event_buf + event_pos);
|
||||
|
||||
printk(KERN_INFO "sdcardfs: inotify event: %08x\n", event->mask);
|
||||
if ((event->mask & IN_IGNORED) == IN_IGNORED) {
|
||||
/* Previously watched file was deleted, probably due to move
|
||||
* that swapped in new data; re-arm the watch and read. */
|
||||
active = false;
|
||||
}
|
||||
|
||||
event_size = sizeof(*event) + event->len;
|
||||
res -= event_size;
|
||||
event_pos += event_size;
|
||||
}
|
||||
continue;
|
||||
|
||||
interruptable_sleep:
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule();
|
||||
}
|
||||
flush_signals(current);
|
||||
sys_close(nfd);
|
||||
return res;
|
||||
}
|
||||
|
||||
void * packagelist_create(void)
|
||||
{
|
||||
struct packagelist_data *pkgl_dat;
|
||||
struct task_struct *packagelist_thread;
|
||||
|
||||
pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO);
|
||||
if (!pkgl_dat) {
|
||||
printk(KERN_ERR "sdcardfs: creating kthread failed\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
mutex_init(&pkgl_dat->hashtable_lock);
|
||||
hash_init(pkgl_dat->package_to_appid);
|
||||
|
||||
packagelist_thread = kthread_run(packagelist_reader, (void *)pkgl_dat, "pkgld");
|
||||
if (IS_ERR(packagelist_thread)) {
|
||||
printk(KERN_ERR "sdcardfs: creating kthread failed\n");
|
||||
kfree(pkgl_dat);
|
||||
return packagelist_thread;
|
||||
}
|
||||
pkgl_dat->thread_id = packagelist_thread;
|
||||
|
||||
printk(KERN_INFO "sdcardfs: created packagelist pkgld/%d\n",
|
||||
(int)pkgl_dat->thread_id->pid);
|
||||
|
||||
return (void *)pkgl_dat;
|
||||
}
|
||||
|
||||
void packagelist_destroy(void *pkgl_id)
|
||||
{
|
||||
struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id;
|
||||
pid_t pkgl_pid = pkgl_dat->thread_id->pid;
|
||||
|
||||
force_sig_info(SIGINT, SEND_SIG_PRIV, pkgl_dat->thread_id);
|
||||
kthread_stop(pkgl_dat->thread_id);
|
||||
remove_all_hashentrys(pkgl_dat);
|
||||
printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld/%d\n", (int)pkgl_pid);
|
||||
kfree(pkgl_dat);
|
||||
}
|
||||
|
||||
int packagelist_init(void)
|
||||
{
|
||||
hashtable_entry_cachep =
|
||||
kmem_cache_create("packagelist_hashtable_entry",
|
||||
sizeof(struct hashtable_entry), 0, 0, NULL);
|
||||
if (!hashtable_entry_cachep) {
|
||||
printk(KERN_ERR "sdcardfs: failed creating pkgl_hashtable entry slab cache\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void packagelist_exit(void)
|
||||
{
|
||||
if (hashtable_entry_cachep)
|
||||
kmem_cache_destroy(hashtable_entry_cachep);
|
||||
}
|
|
@ -1,567 +0,0 @@
|
|||
/*
|
||||
* fs/sdcardfs/sdcardfs.h
|
||||
*
|
||||
* The sdcardfs v2.0
|
||||
* This file system replaces the sdcard daemon on Android
|
||||
* On version 2.0, some of the daemon functions have been ported
|
||||
* to support the multi-user concepts of Android 4.4
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||
* Sunghwan Yun, Sungjong Seo
|
||||
*
|
||||
* This program has been developed as a stackable file system based on
|
||||
* the WrapFS which written by
|
||||
*
|
||||
* Copyright (c) 1998-2011 Erez Zadok
|
||||
* Copyright (c) 2009 Shrikar Archak
|
||||
* Copyright (c) 2003-2011 Stony Brook University
|
||||
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#ifndef _SDCARDFS_H_
|
||||
#define _SDCARDFS_H_
|
||||
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/fs_stack.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include "multiuser.h"
|
||||
|
||||
/* the file system magic number */
|
||||
#define SDCARDFS_SUPER_MAGIC 0xb550ca10
|
||||
|
||||
/* the file system name */
|
||||
#define SDCARDFS_NAME "sdcardfs"
|
||||
|
||||
/* sdcardfs root inode number */
|
||||
#define SDCARDFS_ROOT_INO 1
|
||||
|
||||
/* useful for tracking code reachability */
|
||||
#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__)
|
||||
|
||||
#define SDCARDFS_DIRENT_SIZE 256
|
||||
|
||||
/* temporary static uid settings for development */
|
||||
#define AID_ROOT 0 /* uid for accessing /mnt/sdcard & extSdcard */
|
||||
#define AID_MEDIA_RW 1023 /* internal media storage write access */
|
||||
|
||||
#define AID_SDCARD_RW 1015 /* external storage write access */
|
||||
#define AID_SDCARD_R 1028 /* external storage read access */
|
||||
#define AID_SDCARD_PICS 1033 /* external storage photos access */
|
||||
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
|
||||
#define AID_SDCARD_ALL 1035 /* access all users external storage */
|
||||
|
||||
#define AID_PACKAGE_INFO 1027
|
||||
#define AID_EVERYBODY 9997
|
||||
|
||||
/* OVERRIDE_CRED() and REVERT_CRED()
|
||||
* OVERRID_CRED()
|
||||
* backup original task->cred
|
||||
* and modifies task->cred->fsuid/fsgid to specified value.
|
||||
* REVERT_CRED()
|
||||
* restore original task->cred->fsuid/fsgid.
|
||||
* These two macro should be used in pair, and OVERRIDE_CRED() should be
|
||||
* placed at the beginning of a function, right after variable declaration.
|
||||
*/
|
||||
#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \
|
||||
saved_cred = override_fsids(sdcardfs_sbi->options.fs_low_uid, \
|
||||
sdcardfs_sbi->options.fs_low_gid); \
|
||||
if (!saved_cred) { return -ENOMEM; }
|
||||
|
||||
#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \
|
||||
saved_cred = override_fsids(sdcardfs_sbi->options.fs_low_uid, \
|
||||
sdcardfs_sbi->options.fs_low_gid); \
|
||||
if (!saved_cred) { return ERR_PTR(-ENOMEM); }
|
||||
|
||||
#define OVERRIDE_ROOT_CRED(saved_cred) \
|
||||
saved_cred = override_fsids(0, 0); \
|
||||
if (!saved_cred) { return -ENOMEM; }
|
||||
|
||||
#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
|
||||
|
||||
#define DEBUG_CRED() \
|
||||
printk("KAKJAGI: %s:%d fsuid %d fsgid %d\n", \
|
||||
__FUNCTION__, __LINE__, \
|
||||
(int)current->cred->fsuid, \
|
||||
(int)current->cred->fsgid);
|
||||
|
||||
/* Permission mode for a specific node. Controls how file permissions
|
||||
* are derived for children nodes. */
|
||||
typedef enum {
|
||||
/* Nothing special; this node should just inherit from its parent. */
|
||||
PERM_INHERIT,
|
||||
/* This node is one level above a normal root; used for legacy layouts
|
||||
* which use the first level to represent user_id. */
|
||||
PERM_PRE_ROOT,
|
||||
/* This node is "/" */
|
||||
PERM_ROOT,
|
||||
/* This node is "/Android" */
|
||||
PERM_ANDROID,
|
||||
/* This node is "/Android/data" */
|
||||
PERM_ANDROID_DATA,
|
||||
/* This node is "/Android/obb" */
|
||||
PERM_ANDROID_OBB,
|
||||
/* This node is "/Android/media" */
|
||||
PERM_ANDROID_MEDIA,
|
||||
/* knox folder */
|
||||
PERM_ANDROID_KNOX,
|
||||
/* knox user folder*/
|
||||
PERM_ANDROID_KNOX_USER,
|
||||
/* knox Android folder*/
|
||||
PERM_ANDROID_KNOX_ANDROID,
|
||||
/* knox shared folder */
|
||||
PERM_ANDROID_KNOX_SHARED,
|
||||
/* knox data folder */
|
||||
PERM_ANDROID_KNOX_DATA,
|
||||
/* knox package data folder */
|
||||
PERM_ANDROID_KNOX_PACKAGE_DATA
|
||||
} perm_t;
|
||||
|
||||
typedef enum {
|
||||
LOWER_FS_EXT4,
|
||||
LOWER_FS_FAT,
|
||||
} lower_fs_t;
|
||||
|
||||
typedef enum {
|
||||
TYPE_NONE,
|
||||
TYPE_DEFAULT,
|
||||
TYPE_READ,
|
||||
TYPE_WRITE,
|
||||
} type_t;
|
||||
|
||||
struct sdcardfs_sb_info;
|
||||
struct sdcardfs_mount_options;
|
||||
|
||||
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
|
||||
const struct cred * override_fsids(uid_t fsuid, gid_t fsgid);
|
||||
/* Do not directly use this function, use REVERT_CRED() instead. */
|
||||
void revert_fsids(const struct cred * old_cred);
|
||||
|
||||
/* operations vectors defined in specific files */
|
||||
extern const struct file_operations sdcardfs_main_fops;
|
||||
extern const struct file_operations sdcardfs_dir_fops;
|
||||
extern const struct inode_operations sdcardfs_main_iops;
|
||||
extern const struct inode_operations sdcardfs_dir_iops;
|
||||
extern const struct inode_operations sdcardfs_symlink_iops;
|
||||
extern const struct super_operations sdcardfs_sops;
|
||||
extern const struct super_operations sdcardfs_multimount_sops;
|
||||
extern const struct dentry_operations sdcardfs_ci_dops;
|
||||
extern const struct address_space_operations sdcardfs_aops, sdcardfs_dummy_aops;
|
||||
extern const struct vm_operations_struct sdcardfs_vm_ops;
|
||||
|
||||
extern int sdcardfs_init_inode_cache(void);
|
||||
extern void sdcardfs_destroy_inode_cache(void);
|
||||
extern int sdcardfs_init_dentry_cache(void);
|
||||
extern void sdcardfs_destroy_dentry_cache(void);
|
||||
extern int new_dentry_private_data(struct dentry *dentry);
|
||||
extern void free_dentry_private_data(struct dentry *dentry);
|
||||
extern struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags);
|
||||
extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
|
||||
struct path *lower_path);
|
||||
|
||||
#ifdef SDCARD_FS_XATTR
|
||||
extern int sdcardfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
|
||||
extern ssize_t sdcardfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size);
|
||||
extern ssize_t sdcardfs_listxattr(struct dentry *dentry, char *list, size_t size);
|
||||
extern int sdcardfs_removexattr(struct dentry *dentry, const char *name);
|
||||
#endif // SDCARD_FS_XATTR
|
||||
/* file private data */
|
||||
struct sdcardfs_file_info {
|
||||
struct file *lower_file;
|
||||
const struct vm_operations_struct *lower_vm_ops;
|
||||
};
|
||||
|
||||
/* sdcardfs inode data in memory */
|
||||
struct sdcardfs_inode_info {
|
||||
struct inode *lower_inode;
|
||||
/* state derived based on current position in hierachy
|
||||
*/
|
||||
perm_t perm;
|
||||
userid_t userid;
|
||||
uid_t d_uid;
|
||||
gid_t d_gid;
|
||||
bool under_android;
|
||||
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
||||
/* sdcardfs dentry data in memory */
|
||||
struct sdcardfs_dentry_info {
|
||||
spinlock_t lock; /* protects lower_path */
|
||||
struct path lower_path;
|
||||
struct path orig_path;
|
||||
#ifdef CONFIG_SDP
|
||||
int under_knox;
|
||||
int userid;
|
||||
#define PERMISSION_PRE_ROOT 0
|
||||
#define PERMISSION_ROOT 1
|
||||
#define PERMISSION_ANDROID 2
|
||||
#define PERMISSION_UNDER_ANDROID 3
|
||||
int permission;
|
||||
appid_t appid;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct sdcardfs_mount_options {
|
||||
uid_t fs_low_uid;
|
||||
gid_t fs_low_gid;
|
||||
gid_t gid;
|
||||
userid_t userid;
|
||||
lower_fs_t lower_fs;
|
||||
unsigned int reserved_mb;
|
||||
mode_t mask;
|
||||
bool multi_user;
|
||||
char *label;
|
||||
type_t type;
|
||||
};
|
||||
|
||||
/* sdcardfs super-block data in memory */
|
||||
struct sdcardfs_sb_info {
|
||||
struct super_block *lower_sb;
|
||||
/* derived perm policy : some of options have been added
|
||||
* to sdcardfs_mount_options (Android 4.4 support) */
|
||||
struct sdcardfs_mount_options options;
|
||||
spinlock_t lock; /* protects obbpath */
|
||||
char *obbpath_s;
|
||||
struct path obbpath;
|
||||
void *pkgl_id;
|
||||
char *devpath;
|
||||
};
|
||||
|
||||
/*
|
||||
* inode to private data
|
||||
*
|
||||
* Since we use containers and the struct inode is _inside_ the
|
||||
* sdcardfs_inode_info structure, SDCARDFS_I will always (given a non-NULL
|
||||
* inode pointer), return a valid non-NULL pointer.
|
||||
*/
|
||||
static inline struct sdcardfs_inode_info *SDCARDFS_I(const struct inode *inode)
|
||||
{
|
||||
return container_of(inode, struct sdcardfs_inode_info, vfs_inode);
|
||||
}
|
||||
|
||||
/* dentry to private data */
|
||||
#define SDCARDFS_D(dent) ((struct sdcardfs_dentry_info *)(dent)->d_fsdata)
|
||||
|
||||
/* superblock to private data */
|
||||
#define SDCARDFS_SB(super) ((struct sdcardfs_sb_info *)(super)->s_fs_info)
|
||||
|
||||
/* file to private Data */
|
||||
#define SDCARDFS_F(file) ((struct sdcardfs_file_info *)((file)->private_data))
|
||||
|
||||
/* file to lower file */
|
||||
static inline struct file *sdcardfs_lower_file(const struct file *f)
|
||||
{
|
||||
return SDCARDFS_F(f)->lower_file;
|
||||
}
|
||||
|
||||
static inline void sdcardfs_set_lower_file(struct file *f, struct file *val)
|
||||
{
|
||||
SDCARDFS_F(f)->lower_file = val;
|
||||
}
|
||||
|
||||
/* inode to lower inode. */
|
||||
static inline struct inode *sdcardfs_lower_inode(const struct inode *i)
|
||||
{
|
||||
return SDCARDFS_I(i)->lower_inode;
|
||||
}
|
||||
|
||||
static inline void sdcardfs_set_lower_inode(struct inode *i, struct inode *val)
|
||||
{
|
||||
SDCARDFS_I(i)->lower_inode = val;
|
||||
}
|
||||
|
||||
/* copy the inode attrs from src to dest except uid and gid */
|
||||
static inline void sdcardfs_copy_inode_attr(struct inode *dest, const struct inode *src)
|
||||
{
|
||||
dest->i_mode = src->i_mode;
|
||||
dest->i_rdev = src->i_rdev;
|
||||
dest->i_atime = src->i_atime;
|
||||
dest->i_mtime = src->i_mtime;
|
||||
dest->i_ctime = src->i_ctime;
|
||||
dest->i_blkbits = src->i_blkbits;
|
||||
dest->i_flags = src->i_flags;
|
||||
set_nlink(dest, src->i_nlink);
|
||||
}
|
||||
|
||||
/* superblock to lower superblock */
|
||||
static inline struct super_block *sdcardfs_lower_super(
|
||||
const struct super_block *sb)
|
||||
{
|
||||
return SDCARDFS_SB(sb)->lower_sb;
|
||||
}
|
||||
|
||||
static inline void sdcardfs_set_lower_super(struct super_block *sb,
|
||||
struct super_block *val)
|
||||
{
|
||||
SDCARDFS_SB(sb)->lower_sb = val;
|
||||
}
|
||||
|
||||
/* path based (dentry/mnt) macros */
|
||||
static inline void pathcpy(struct path *dst, const struct path *src)
|
||||
{
|
||||
dst->dentry = src->dentry;
|
||||
dst->mnt = src->mnt;
|
||||
}
|
||||
|
||||
/* sdcardfs_get_pname functions calls path_get()
|
||||
* therefore, the caller must call "proper" path_put functions
|
||||
*/
|
||||
#define SDCARDFS_DENT_FUNC(pname) \
|
||||
static inline void sdcardfs_get_##pname(const struct dentry *dent, \
|
||||
struct path *pname) \
|
||||
{ \
|
||||
spin_lock(&SDCARDFS_D(dent)->lock); \
|
||||
pathcpy(pname, &SDCARDFS_D(dent)->pname); \
|
||||
path_get(pname); \
|
||||
spin_unlock(&SDCARDFS_D(dent)->lock); \
|
||||
return; \
|
||||
} \
|
||||
static inline void sdcardfs_put_##pname(const struct dentry *dent, \
|
||||
struct path *pname) \
|
||||
{ \
|
||||
path_put(pname); \
|
||||
return; \
|
||||
} \
|
||||
static inline void sdcardfs_set_##pname(const struct dentry *dent, \
|
||||
struct path *pname) \
|
||||
{ \
|
||||
spin_lock(&SDCARDFS_D(dent)->lock); \
|
||||
pathcpy(&SDCARDFS_D(dent)->pname, pname); \
|
||||
spin_unlock(&SDCARDFS_D(dent)->lock); \
|
||||
return; \
|
||||
} \
|
||||
static inline void sdcardfs_reset_##pname(const struct dentry *dent) \
|
||||
{ \
|
||||
spin_lock(&SDCARDFS_D(dent)->lock); \
|
||||
SDCARDFS_D(dent)->pname.dentry = NULL; \
|
||||
SDCARDFS_D(dent)->pname.mnt = NULL; \
|
||||
spin_unlock(&SDCARDFS_D(dent)->lock); \
|
||||
return; \
|
||||
} \
|
||||
static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \
|
||||
{ \
|
||||
struct path pname; \
|
||||
spin_lock(&SDCARDFS_D(dent)->lock); \
|
||||
if(SDCARDFS_D(dent)->pname.dentry) { \
|
||||
pathcpy(&pname, &SDCARDFS_D(dent)->pname); \
|
||||
SDCARDFS_D(dent)->pname.dentry = NULL; \
|
||||
SDCARDFS_D(dent)->pname.mnt = NULL; \
|
||||
spin_unlock(&SDCARDFS_D(dent)->lock); \
|
||||
path_put(&pname); \
|
||||
} else \
|
||||
spin_unlock(&SDCARDFS_D(dent)->lock); \
|
||||
return; \
|
||||
}
|
||||
|
||||
SDCARDFS_DENT_FUNC(lower_path)
|
||||
SDCARDFS_DENT_FUNC(orig_path)
|
||||
|
||||
static inline void sdcardfs_copy_lower_path(const struct dentry *dent,
|
||||
struct path *lower_path)
|
||||
{
|
||||
spin_lock(&SDCARDFS_D(dent)->lock);
|
||||
pathcpy(lower_path, &SDCARDFS_D(dent)->lower_path);
|
||||
spin_unlock(&SDCARDFS_D(dent)->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int has_graft_path(const struct dentry *dent)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&SDCARDFS_D(dent)->lock);
|
||||
if (SDCARDFS_D(dent)->orig_path.dentry != NULL)
|
||||
ret = 1;
|
||||
spin_unlock(&SDCARDFS_D(dent)->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void sdcardfs_get_real_lower(const struct dentry *dent,
|
||||
struct path *real_lower)
|
||||
{
|
||||
/* in case of a local obb dentry
|
||||
* the orig_path should be returned
|
||||
*/
|
||||
if(has_graft_path(dent))
|
||||
sdcardfs_get_orig_path(dent, real_lower);
|
||||
else
|
||||
sdcardfs_get_lower_path(dent, real_lower);
|
||||
}
|
||||
|
||||
static inline void sdcardfs_put_real_lower(const struct dentry *dent,
|
||||
struct path *real_lower)
|
||||
{
|
||||
if(has_graft_path(dent))
|
||||
sdcardfs_put_orig_path(dent, real_lower);
|
||||
else
|
||||
sdcardfs_put_lower_path(dent, real_lower);
|
||||
}
|
||||
|
||||
/* for packagelist.c */
|
||||
extern appid_t get_appid(void *pkgl_id, const char *app_name);
|
||||
extern int check_caller_access_to_name(struct inode *parent_node, const char* name);
|
||||
extern int open_flags_to_access_mode(int open_flags);
|
||||
extern void *packagelist_create(void);
|
||||
extern void packagelist_destroy(void *pkgl_id);
|
||||
extern int packagelist_init(void);
|
||||
extern void packagelist_exit(void);
|
||||
|
||||
/* for derived_perm.c */
|
||||
extern void setup_derived_state(struct inode *inode, perm_t perm,
|
||||
userid_t userid, uid_t uid, gid_t gid, bool under_android);
|
||||
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
|
||||
extern void fix_derived_permission(struct inode *inode);
|
||||
extern void update_derived_permission(struct dentry *dentry);
|
||||
extern int need_graft_path(struct dentry *dentry);
|
||||
extern int is_base_obbpath(struct dentry *dentry);
|
||||
extern int is_obbpath_invalid(struct dentry *dentry);
|
||||
extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path);
|
||||
|
||||
/* locking helpers */
|
||||
static inline struct dentry *lock_parent(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *dir = dget_parent(dentry);
|
||||
mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
return dir;
|
||||
}
|
||||
|
||||
static inline void unlock_dir(struct dentry *dir)
|
||||
{
|
||||
mutex_unlock(&dir->d_inode->i_mutex);
|
||||
dput(dir);
|
||||
}
|
||||
|
||||
static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t mode)
|
||||
{
|
||||
int err;
|
||||
struct dentry *dent;
|
||||
struct path path;
|
||||
struct iattr attrs;
|
||||
|
||||
dent = kern_path_create(AT_FDCWD, path_s, &path, LOOKUP_DIRECTORY);
|
||||
|
||||
if (IS_ERR(dent)) {
|
||||
err = PTR_ERR(dent);
|
||||
if (err == -EEXIST)
|
||||
err = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mnt_want_write(path.mnt);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = vfs_mkdir(path.dentry->d_inode, dent, mode);
|
||||
if (err) {
|
||||
if (err == -EEXIST)
|
||||
err = 0;
|
||||
goto out_drop;
|
||||
}
|
||||
|
||||
attrs.ia_uid = uid;
|
||||
attrs.ia_gid = gid;
|
||||
attrs.ia_valid = ATTR_UID | ATTR_GID;
|
||||
mutex_lock(&dent->d_inode->i_mutex);
|
||||
notify_change(dent, &attrs);
|
||||
mutex_unlock(&dent->d_inode->i_mutex);
|
||||
|
||||
out_drop:
|
||||
mnt_drop_write(path.mnt);
|
||||
|
||||
out:
|
||||
dput(dent);
|
||||
/* parent dentry locked by kern_path_create */
|
||||
mutex_unlock(&path.dentry->d_inode->i_mutex);
|
||||
path_put(&path);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return 1, if a disk has enough free space, otherwise 0.
|
||||
* We assume that any files can not be overwritten.
|
||||
*/
|
||||
static inline int check_min_free_space(struct dentry *dentry, size_t size, int dir)
|
||||
{
|
||||
int err;
|
||||
struct path lower_path;
|
||||
struct kstatfs statfs;
|
||||
u64 avail;
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
|
||||
if (sbi->options.reserved_mb) {
|
||||
/* Get fs stat of lower filesystem. */
|
||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||
err = vfs_statfs(&lower_path, &statfs);
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
|
||||
if (unlikely(err))
|
||||
goto out_invalid;
|
||||
|
||||
/* Invalid statfs informations. */
|
||||
if (unlikely(statfs.f_bsize == 0))
|
||||
goto out_invalid;
|
||||
|
||||
/* if you are checking directory, set size to f_bsize. */
|
||||
if (unlikely(dir))
|
||||
size = statfs.f_bsize;
|
||||
|
||||
/* available size */
|
||||
avail = statfs.f_bavail * statfs.f_bsize;
|
||||
|
||||
/* not enough space */
|
||||
if ((u64)size > avail)
|
||||
goto out_nospc;
|
||||
|
||||
/* enough space */
|
||||
if ((avail - size) > (sbi->options.reserved_mb * 1024 * 1024))
|
||||
return 1;
|
||||
goto out_nospc;
|
||||
} else
|
||||
return 1;
|
||||
|
||||
out_invalid:
|
||||
printk(KERN_INFO "statfs : invalid return\n");
|
||||
printk(KERN_INFO "vfs_statfs error# : %d\n", err);
|
||||
printk(KERN_INFO "statfs.f_type : 0x%X\n", (u32)statfs.f_type);
|
||||
printk(KERN_INFO "statfs.f_blocks : %llu blocks\n", statfs.f_blocks);
|
||||
printk(KERN_INFO "statfs.f_bfree : %llu blocks\n", statfs.f_bfree);
|
||||
printk(KERN_INFO "statfs.f_files : %llu\n", statfs.f_files);
|
||||
printk(KERN_INFO "statfs.f_ffree : %llu\n", statfs.f_ffree);
|
||||
printk(KERN_INFO "statfs.f_fsid.val[1] : 0x%X\n", (u32)statfs.f_fsid.val[1]);
|
||||
printk(KERN_INFO "statfs.f_fsid.val[0] : 0x%X\n", (u32)statfs.f_fsid.val[0]);
|
||||
printk(KERN_INFO "statfs.f_namelen : %ld\n", statfs.f_namelen);
|
||||
printk(KERN_INFO "statfs.f_frsize : %ld\n", statfs.f_frsize);
|
||||
printk(KERN_INFO "statfs.f_flags : %ld\n", statfs.f_flags);
|
||||
printk(KERN_INFO "sdcardfs reserved_mb : %u\n", sbi->options.reserved_mb);
|
||||
if (sbi->devpath)
|
||||
printk(KERN_INFO "sdcardfs source path : %s\n", sbi->devpath);
|
||||
|
||||
out_nospc:
|
||||
printk_ratelimited(KERN_INFO "statfs.f_bavail : %llu blocks / "
|
||||
"statfs.f_bsize : %ld bytes / "
|
||||
"required size : %llu byte\n"
|
||||
,statfs.f_bavail, statfs.f_bsize, (u64)size);
|
||||
return 0;
|
||||
}
|
||||
#endif /* not _SDCARDFS_H_ */
|
|
@ -1,300 +0,0 @@
|
|||
/*
|
||||
* fs/sdcardfs/super.c
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun,
|
||||
* Sunghwan Yun, Sungjong Seo
|
||||
*
|
||||
* This program has been developed as a stackable file system based on
|
||||
* the WrapFS which written by
|
||||
*
|
||||
* Copyright (c) 1998-2011 Erez Zadok
|
||||
* Copyright (c) 2009 Shrikar Archak
|
||||
* Copyright (c) 2003-2011 Stony Brook University
|
||||
* Copyright (c) 2003-2011 The Research Foundation of SUNY
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#include "sdcardfs.h"
|
||||
|
||||
/*
|
||||
* The inode cache is used with alloc_inode for both our inode info and the
|
||||
* vfs inode.
|
||||
*/
|
||||
static struct kmem_cache *sdcardfs_inode_cachep;
|
||||
|
||||
/* final actions when unmounting a file system */
|
||||
static void sdcardfs_put_super(struct super_block *sb)
|
||||
{
|
||||
struct sdcardfs_sb_info *spd;
|
||||
struct super_block *s;
|
||||
|
||||
spd = SDCARDFS_SB(sb);
|
||||
if (!spd)
|
||||
return;
|
||||
|
||||
printk(KERN_ERR "sdcardfs: umounted dev_name %s\n",
|
||||
spd->devpath ? spd->devpath : "");
|
||||
if(spd->devpath)
|
||||
kfree(spd->devpath);
|
||||
|
||||
if(spd->obbpath_s) {
|
||||
kfree(spd->obbpath_s);
|
||||
path_put(&spd->obbpath);
|
||||
}
|
||||
|
||||
if(spd->options.label)
|
||||
kfree(spd->options.label);
|
||||
|
||||
/* decrement lower super references */
|
||||
s = sdcardfs_lower_super(sb);
|
||||
sdcardfs_set_lower_super(sb, NULL);
|
||||
atomic_dec(&s->s_active);
|
||||
|
||||
if(spd->pkgl_id)
|
||||
packagelist_destroy(spd->pkgl_id);
|
||||
|
||||
kfree(spd);
|
||||
sb->s_fs_info = NULL;
|
||||
}
|
||||
|
||||
static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
{
|
||||
int err;
|
||||
struct path lower_path;
|
||||
u32 min_blocks;
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||
|
||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||
err = vfs_statfs(&lower_path, buf);
|
||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||
|
||||
if (sbi->options.reserved_mb) {
|
||||
/* Invalid statfs informations. */
|
||||
if (buf->f_bsize == 0) {
|
||||
printk(KERN_ERR "Returned block size is zero.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
min_blocks = ((sbi->options.reserved_mb * 1024 * 1024)/buf->f_bsize);
|
||||
buf->f_blocks -= min_blocks;
|
||||
|
||||
if (buf->f_bavail > min_blocks)
|
||||
buf->f_bavail -= min_blocks;
|
||||
else
|
||||
buf->f_bavail = 0;
|
||||
|
||||
/* Make reserved blocks invisiable to media storage */
|
||||
buf->f_bfree = buf->f_bavail;
|
||||
}
|
||||
|
||||
/* set return buf to our f/s to avoid confusing user-level utils */
|
||||
buf->f_type = SDCARDFS_SUPER_MAGIC;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* @flags: numeric mount options
|
||||
* @options: mount options string
|
||||
*/
|
||||
static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* The VFS will take care of "ro" and "rw" flags among others. We
|
||||
* can safely accept a few flags (RDONLY, MANDLOCK), and honor
|
||||
* SILENT, but anything else left over is an error.
|
||||
*/
|
||||
if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) {
|
||||
printk(KERN_ERR
|
||||
"sdcardfs: remount flags 0x%x unsupported\n", *flags);
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by iput() when the inode reference count reached zero
|
||||
* and the inode is not hashed anywhere. Used to clear anything
|
||||
* that needs to be, before the inode is completely destroyed and put
|
||||
* on the inode free list.
|
||||
*/
|
||||
static void sdcardfs_evict_inode(struct inode *inode)
|
||||
{
|
||||
struct inode *lower_inode;
|
||||
|
||||
truncate_inode_pages(&inode->i_data, 0);
|
||||
clear_inode(inode);
|
||||
/*
|
||||
* Decrement a reference to a lower_inode, which was incremented
|
||||
* by our read_inode when it was created initially.
|
||||
*/
|
||||
lower_inode = sdcardfs_lower_inode(inode);
|
||||
sdcardfs_set_lower_inode(inode, NULL);
|
||||
iput(lower_inode);
|
||||
}
|
||||
|
||||
static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct sdcardfs_inode_info *i;
|
||||
|
||||
i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL);
|
||||
if (!i)
|
||||
return NULL;
|
||||
|
||||
/* memset everything up to the inode to 0 */
|
||||
memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode));
|
||||
|
||||
i->vfs_inode.i_version = 1;
|
||||
return &i->vfs_inode;
|
||||
}
|
||||
|
||||
static void sdcardfs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
|
||||
}
|
||||
|
||||
/* sdcardfs inode cache constructor */
|
||||
static void init_once(void *obj)
|
||||
{
|
||||
struct sdcardfs_inode_info *i = obj;
|
||||
|
||||
inode_init_once(&i->vfs_inode);
|
||||
}
|
||||
|
||||
int sdcardfs_init_inode_cache(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
sdcardfs_inode_cachep =
|
||||
kmem_cache_create("sdcardfs_inode_cache",
|
||||
sizeof(struct sdcardfs_inode_info), 0,
|
||||
SLAB_RECLAIM_ACCOUNT, init_once);
|
||||
if (!sdcardfs_inode_cachep)
|
||||
err = -ENOMEM;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* sdcardfs inode cache destructor */
|
||||
void sdcardfs_destroy_inode_cache(void)
|
||||
{
|
||||
if (sdcardfs_inode_cachep)
|
||||
kmem_cache_destroy(sdcardfs_inode_cachep);
|
||||
}
|
||||
|
||||
static long sdcardfs_propagate_lookup(struct super_block *sb, char* pathname) {
|
||||
long ret = 0;
|
||||
char *propagate_path = NULL;
|
||||
struct sdcardfs_sb_info *sbi;
|
||||
struct path sibling_path;
|
||||
const struct cred *saved_cred = NULL;
|
||||
|
||||
sbi = SDCARDFS_SB(sb);
|
||||
OVERRIDE_ROOT_CRED(saved_cred);
|
||||
propagate_path = kmalloc(PATH_MAX, GFP_KERNEL);
|
||||
if (!propagate_path) {
|
||||
REVERT_CRED(saved_cred);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (sbi->options.type != TYPE_NONE && sbi->options.type != TYPE_DEFAULT) {
|
||||
snprintf(propagate_path, PATH_MAX, "/mnt/runtime/default/%s%s",
|
||||
sbi->options.label, pathname);
|
||||
ret = (long)kern_path(propagate_path, LOOKUP_FOLLOW, &sibling_path);
|
||||
if (!ret)
|
||||
path_put(&sibling_path);
|
||||
}
|
||||
|
||||
if (sbi->options.type != TYPE_NONE && sbi->options.type != TYPE_READ) {
|
||||
snprintf(propagate_path, PATH_MAX, "/mnt/runtime/read/%s%s",
|
||||
sbi->options.label, pathname);
|
||||
ret = (long)kern_path(propagate_path, LOOKUP_FOLLOW, &sibling_path);
|
||||
if (!ret)
|
||||
path_put(&sibling_path);
|
||||
}
|
||||
|
||||
if (sbi->options.type != TYPE_NONE && sbi->options.type != TYPE_WRITE) {
|
||||
snprintf(propagate_path, PATH_MAX, "/mnt/runtime/write/%s%s",
|
||||
sbi->options.label, pathname);
|
||||
ret = (long)kern_path(propagate_path, LOOKUP_FOLLOW, &sibling_path);
|
||||
if (!ret)
|
||||
path_put(&sibling_path);
|
||||
}
|
||||
|
||||
if (sbi->options.type != TYPE_NONE) {
|
||||
snprintf(propagate_path, PATH_MAX, "/storage/%s%s",
|
||||
sbi->options.label, pathname);
|
||||
ret = (long)kern_path(propagate_path, LOOKUP_FOLLOW, &sibling_path);
|
||||
if (!ret)
|
||||
path_put(&sibling_path);
|
||||
}
|
||||
REVERT_CRED(saved_cred);
|
||||
kfree(propagate_path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used only in nfs, to kill any pending RPC tasks, so that subsequent
|
||||
* code can actually succeed and won't leave tasks that need handling.
|
||||
*/
|
||||
static void sdcardfs_umount_begin(struct super_block *sb)
|
||||
{
|
||||
struct super_block *lower_sb;
|
||||
|
||||
lower_sb = sdcardfs_lower_super(sb);
|
||||
if (lower_sb && lower_sb->s_op && lower_sb->s_op->umount_begin)
|
||||
lower_sb->s_op->umount_begin(lower_sb);
|
||||
}
|
||||
|
||||
static int sdcardfs_show_options(struct seq_file *m, struct dentry *root)
|
||||
{
|
||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
|
||||
struct sdcardfs_mount_options *opts = &sbi->options;
|
||||
|
||||
if (opts->fs_low_uid != 0)
|
||||
seq_printf(m, ",low_uid=%u", opts->fs_low_uid);
|
||||
if (opts->fs_low_gid != 0)
|
||||
seq_printf(m, ",low_gid=%u", opts->fs_low_gid);
|
||||
if (opts->gid != 0)
|
||||
seq_printf(m, ",gid=%u", opts->gid);
|
||||
if (opts->userid != 0)
|
||||
seq_printf(m, ",userid=%u", opts->userid);
|
||||
if (opts->multi_user)
|
||||
seq_printf(m, ",multi_user");
|
||||
if (opts->mask != 0)
|
||||
seq_printf(m, ",mask=%04o", opts->mask);
|
||||
if (opts->reserved_mb != 0)
|
||||
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
const struct super_operations sdcardfs_sops = {
|
||||
.put_super = sdcardfs_put_super,
|
||||
.statfs = sdcardfs_statfs,
|
||||
.remount_fs = sdcardfs_remount_fs,
|
||||
.evict_inode = sdcardfs_evict_inode,
|
||||
.umount_begin = sdcardfs_umount_begin,
|
||||
.show_options = sdcardfs_show_options,
|
||||
.alloc_inode = sdcardfs_alloc_inode,
|
||||
.destroy_inode = sdcardfs_destroy_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
};
|
||||
|
||||
const struct super_operations sdcardfs_multimount_sops = {
|
||||
.put_super = sdcardfs_put_super,
|
||||
.statfs = sdcardfs_statfs,
|
||||
.remount_fs = sdcardfs_remount_fs,
|
||||
.evict_inode = sdcardfs_evict_inode,
|
||||
.umount_begin = sdcardfs_umount_begin,
|
||||
.show_options = sdcardfs_show_options,
|
||||
.alloc_inode = sdcardfs_alloc_inode,
|
||||
.destroy_inode = sdcardfs_destroy_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.unlink_callback = sdcardfs_propagate_lookup,
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* The sdcardfs
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co. Ltd
|
||||
* Authors: Daeho Jeong, Woojoong Lee, Kitae Lee, Yeongjin Gil
|
||||
*
|
||||
* Revision History
|
||||
* 2014.06.24 : Release Version 2.1.0
|
||||
* - Add sdcardfs version
|
||||
* - Add kernel log when put_super
|
||||
* 2014.07.21 : Release Version 2.1.1
|
||||
* - Add sdcardfs_copy_inode_attr() to fix permission issue
|
||||
* - Delete mmap_sem lock in sdcardfs_setattr() to avoid deadlock
|
||||
* 2014.11.12 : Release Version 2.1.2
|
||||
* - Add get_lower_file function pointer in file_operations
|
||||
* 2014.11.25 : Release Version 2.1.3
|
||||
* - Add error handling routine in sdcardfs_d_revalidate
|
||||
* when dentry is equal to lower_dentry
|
||||
* 2015.03.25 : Release Version 2.1.4
|
||||
* - Add FMODE_NONMAPPABLE, FMODE_NONCACHEABLE flag to file->f_mode
|
||||
* - Modify do_mmap_pgoff because of new f_mode flags
|
||||
* 2015.07. : Release Version 3.0.0
|
||||
* 2015.11.24 : Release Version 3.1.0
|
||||
* - Add unlink_callback(), get_lower_inode()
|
||||
* - Add mount option type, label
|
||||
* 2016.02. : Release Version 3.2.0
|
||||
* - remove get_lower_inode(), make sdcardfs use only unlink_callback()
|
||||
* - modify name hash creation because it's different with vfat's
|
||||
* - obb will be used only multi_user option is enabled
|
||||
* - modify sdcardfs_setattr because it changes i_size without spinlock
|
||||
* it can make race condition with fsstack_copy_inode_size()
|
||||
*/
|
||||
|
||||
#define SDCARDFS_VERSION "3.2.0"
|
|
@ -1,89 +0,0 @@
|
|||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/dcache.h>
|
||||
#include "sdcardfs.h"
|
||||
|
||||
static struct dentry *
|
||||
sdcardfs_dentry_to_lower(struct dentry *dentry)
|
||||
{
|
||||
struct dentry* ret;
|
||||
|
||||
ret = ((struct sdcardfs_dentry_info *)dentry->d_fsdata)->lower_path.dentry;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
sdcardfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dentry *lower_dentry;
|
||||
|
||||
lower_dentry = sdcardfs_dentry_to_lower(dentry);
|
||||
if (!lower_dentry->d_inode->i_op->setxattr) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = vfs_setxattr(lower_dentry, name, value, size, flags);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sdcardfs_getxattr_lower(struct dentry *lower_dentry, const char *name, void *value, size_t size)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!lower_dentry->d_inode->i_op->getxattr) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
rc = lower_dentry->d_inode->i_op->getxattr(lower_dentry, name, value,
|
||||
size);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
sdcardfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
|
||||
{
|
||||
ssize_t ret;
|
||||
ret = sdcardfs_getxattr_lower(sdcardfs_dentry_to_lower(dentry), name,
|
||||
value, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
sdcardfs_listxattr(struct dentry *dentry, char *list, size_t size)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dentry *lower_dentry;
|
||||
|
||||
lower_dentry = sdcardfs_dentry_to_lower(dentry);
|
||||
if (!lower_dentry->d_inode->i_op->listxattr) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
rc = lower_dentry->d_inode->i_op->listxattr(lower_dentry, list, size);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
sdcardfs_removexattr(struct dentry *dentry, const char *name)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dentry *lower_dentry;
|
||||
|
||||
lower_dentry = sdcardfs_dentry_to_lower(dentry);
|
||||
if (!lower_dentry->d_inode->i_op->removexattr) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
mutex_lock(&lower_dentry->d_inode->i_mutex);
|
||||
rc = lower_dentry->d_inode->i_op->removexattr(lower_dentry, name);
|
||||
mutex_unlock(&lower_dentry->d_inode->i_mutex);
|
||||
out:
|
||||
return rc;
|
||||
}
|
|
@ -158,7 +158,6 @@ struct dentry_operations {
|
|||
char *(*d_dname)(struct dentry *, char *, int);
|
||||
struct vfsmount *(*d_automount)(struct path *);
|
||||
int (*d_manage)(struct dentry *, bool);
|
||||
void (*d_canonical_path)(const struct path *, struct path *);
|
||||
} ____cacheline_aligned;
|
||||
|
||||
/*
|
||||
|
@ -210,7 +209,6 @@ struct dentry_operations {
|
|||
(DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT)
|
||||
|
||||
#define DCACHE_DENTRY_KILLED 0x100000
|
||||
#define DCACHE_WILL_INVALIDATE 0x80000000 /* will be invalidated */
|
||||
|
||||
#define DCACHE_ENCRYPTED_WITH_KEY 0x04000000 /* dir is encrypted with a valid key */
|
||||
|
||||
|
|
|
@ -124,9 +124,6 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
|
|||
/* File is opened with O_PATH; almost nothing can be done with it */
|
||||
#define FMODE_PATH ((__force fmode_t)0x4000)
|
||||
|
||||
/* File hasn't page cache and can't be mmaped, for stackable filesystem */
|
||||
#define FMODE_NONMAPPABLE ((__force fmode_t)0x400000)
|
||||
|
||||
/* File page don't need to be cached, for stackable filesystem's lower file */
|
||||
#define FMODE_NONCACHEABLE ((__force fmode_t)0x800000)
|
||||
|
||||
|
|
|
@ -55,9 +55,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
|
|||
#define LOOKUP_JUMPED 0x1000
|
||||
#define LOOKUP_ROOT 0x2000
|
||||
#define LOOKUP_EMPTY 0x4000
|
||||
#ifdef CONFIG_SDCARD_FS_CI_SEARCH
|
||||
#define LOOKUP_CASE_INSENSITIVE 0x8000
|
||||
#endif
|
||||
|
||||
extern int user_path_at(int, const char __user *, unsigned, struct path *);
|
||||
extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);
|
||||
|
|
Loading…
Reference in New Issue