Merge branch 'lsk-v4.4-android' of git://android.git.linaro.org/kernel/linaro-android into linux-linaro-lsk-v4.4-android
This commit is contained in:
commit
d8715e9813
33 changed files with 1762 additions and 533 deletions
|
@ -2048,7 +2048,7 @@ static void binder_transaction(struct binder_proc *proc,
|
||||||
if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) {
|
if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) {
|
||||||
binder_user_error("%d:%d got transaction with unaligned buffers size, %lld\n",
|
binder_user_error("%d:%d got transaction with unaligned buffers size, %lld\n",
|
||||||
proc->pid, thread->pid,
|
proc->pid, thread->pid,
|
||||||
extra_buffers_size);
|
(u64)extra_buffers_size);
|
||||||
return_error = BR_FAILED_REPLY;
|
return_error = BR_FAILED_REPLY;
|
||||||
goto err_bad_offset;
|
goto err_bad_offset;
|
||||||
}
|
}
|
||||||
|
|
12
fs/attr.c
12
fs/attr.c
|
@ -187,7 +187,7 @@ EXPORT_SYMBOL(setattr_copy);
|
||||||
* the file open for write, as there can be no conflicting delegation in
|
* the file open for write, as there can be no conflicting delegation in
|
||||||
* that case.
|
* that case.
|
||||||
*/
|
*/
|
||||||
int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
|
int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
|
||||||
{
|
{
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
umode_t mode = inode->i_mode;
|
umode_t mode = inode->i_mode;
|
||||||
|
@ -277,7 +277,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (inode->i_op->setattr)
|
if (mnt && inode->i_op->setattr2)
|
||||||
|
error = inode->i_op->setattr2(mnt, dentry, attr);
|
||||||
|
else if (inode->i_op->setattr)
|
||||||
error = inode->i_op->setattr(dentry, attr);
|
error = inode->i_op->setattr(dentry, attr);
|
||||||
else
|
else
|
||||||
error = simple_setattr(dentry, attr);
|
error = simple_setattr(dentry, attr);
|
||||||
|
@ -290,4 +292,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(notify_change2);
|
||||||
|
|
||||||
|
int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
|
||||||
|
{
|
||||||
|
return notify_change2(NULL, dentry, attr, delegated_inode);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(notify_change);
|
EXPORT_SYMBOL(notify_change);
|
||||||
|
|
|
@ -720,7 +720,7 @@ void do_coredump(const siginfo_t *siginfo)
|
||||||
goto close_fail;
|
goto close_fail;
|
||||||
if (!(cprm.file->f_mode & FMODE_CAN_WRITE))
|
if (!(cprm.file->f_mode & FMODE_CAN_WRITE))
|
||||||
goto close_fail;
|
goto close_fail;
|
||||||
if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file))
|
if (do_truncate2(cprm.file->f_path.mnt, cprm.file->f_path.dentry, 0, 0, cprm.file))
|
||||||
goto close_fail;
|
goto close_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1132,7 +1132,7 @@ EXPORT_SYMBOL(flush_old_exec);
|
||||||
void would_dump(struct linux_binprm *bprm, struct file *file)
|
void would_dump(struct linux_binprm *bprm, struct file *file)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
if (inode_permission(inode, MAY_READ) < 0) {
|
if (inode_permission2(file->f_path.mnt, inode, MAY_READ) < 0) {
|
||||||
struct user_namespace *old, *user_ns;
|
struct user_namespace *old, *user_ns;
|
||||||
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
|
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ void set_fs_pwd(struct fs_struct *fs, const struct path *path)
|
||||||
if (old_pwd.dentry)
|
if (old_pwd.dentry)
|
||||||
path_put(&old_pwd);
|
path_put(&old_pwd);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(set_fs_pwd);
|
||||||
|
|
||||||
static inline int replace_path(struct path *p, const struct path *old, const struct path *new)
|
static inline int replace_path(struct path *p, const struct path *old, const struct path *new)
|
||||||
{
|
{
|
||||||
|
@ -89,6 +90,7 @@ void free_fs_struct(struct fs_struct *fs)
|
||||||
path_put(&fs->pwd);
|
path_put(&fs->pwd);
|
||||||
kmem_cache_free(fs_cachep, fs);
|
kmem_cache_free(fs_cachep, fs);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(free_fs_struct);
|
||||||
|
|
||||||
void exit_fs(struct task_struct *tsk)
|
void exit_fs(struct task_struct *tsk)
|
||||||
{
|
{
|
||||||
|
@ -127,6 +129,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
|
||||||
}
|
}
|
||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(copy_fs_struct);
|
||||||
|
|
||||||
int unshare_fs_struct(void)
|
int unshare_fs_struct(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1721,7 +1721,7 @@ int dentry_needs_remove_privs(struct dentry *dentry)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dentry_needs_remove_privs);
|
EXPORT_SYMBOL(dentry_needs_remove_privs);
|
||||||
|
|
||||||
static int __remove_privs(struct dentry *dentry, int kill)
|
static int __remove_privs(struct vfsmount *mnt, struct dentry *dentry, int kill)
|
||||||
{
|
{
|
||||||
struct iattr newattrs;
|
struct iattr newattrs;
|
||||||
|
|
||||||
|
@ -1730,7 +1730,7 @@ static int __remove_privs(struct dentry *dentry, int kill)
|
||||||
* Note we call this on write, so notify_change will not
|
* Note we call this on write, so notify_change will not
|
||||||
* encounter any conflicting delegations:
|
* encounter any conflicting delegations:
|
||||||
*/
|
*/
|
||||||
return notify_change(dentry, &newattrs, NULL);
|
return notify_change2(mnt, dentry, &newattrs, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1752,7 +1752,7 @@ int file_remove_privs(struct file *file)
|
||||||
if (kill < 0)
|
if (kill < 0)
|
||||||
return kill;
|
return kill;
|
||||||
if (kill)
|
if (kill)
|
||||||
error = __remove_privs(dentry, kill);
|
error = __remove_privs(file->f_path.mnt, dentry, kill);
|
||||||
if (!error)
|
if (!error)
|
||||||
inode_has_no_xattr(inode);
|
inode_has_no_xattr(inode);
|
||||||
|
|
||||||
|
|
|
@ -84,9 +84,11 @@ extern struct file *get_empty_filp(void);
|
||||||
* super.c
|
* super.c
|
||||||
*/
|
*/
|
||||||
extern int do_remount_sb(struct super_block *, int, void *, int);
|
extern int do_remount_sb(struct super_block *, int, void *, int);
|
||||||
|
extern int do_remount_sb2(struct vfsmount *, struct super_block *, int,
|
||||||
|
void *, int);
|
||||||
extern bool trylock_super(struct super_block *sb);
|
extern bool trylock_super(struct super_block *sb);
|
||||||
extern struct dentry *mount_fs(struct file_system_type *,
|
extern struct dentry *mount_fs(struct file_system_type *,
|
||||||
int, const char *, void *);
|
int, const char *, struct vfsmount *, void *);
|
||||||
extern struct super_block *user_get_super(dev_t);
|
extern struct super_block *user_get_super(dev_t);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
179
fs/namei.c
179
fs/namei.c
|
@ -373,9 +373,11 @@ EXPORT_SYMBOL(generic_permission);
|
||||||
* flag in inode->i_opflags, that says "this has not special
|
* flag in inode->i_opflags, that says "this has not special
|
||||||
* permission function, use the fast case".
|
* permission function, use the fast case".
|
||||||
*/
|
*/
|
||||||
static inline int do_inode_permission(struct inode *inode, int mask)
|
static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask)
|
||||||
{
|
{
|
||||||
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
|
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
|
||||||
|
if (likely(mnt && inode->i_op->permission2))
|
||||||
|
return inode->i_op->permission2(mnt, inode, mask);
|
||||||
if (likely(inode->i_op->permission))
|
if (likely(inode->i_op->permission))
|
||||||
return inode->i_op->permission(inode, mask);
|
return inode->i_op->permission(inode, mask);
|
||||||
|
|
||||||
|
@ -399,7 +401,7 @@ static inline int do_inode_permission(struct inode *inode, int mask)
|
||||||
* This does not check for a read-only file system. You probably want
|
* This does not check for a read-only file system. You probably want
|
||||||
* inode_permission().
|
* inode_permission().
|
||||||
*/
|
*/
|
||||||
int __inode_permission(struct inode *inode, int mask)
|
int __inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
@ -411,7 +413,7 @@ int __inode_permission(struct inode *inode, int mask)
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = do_inode_permission(inode, mask);
|
retval = do_inode_permission(mnt, inode, mask);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
@ -419,7 +421,14 @@ int __inode_permission(struct inode *inode, int mask)
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
return security_inode_permission(inode, mask);
|
retval = security_inode_permission(inode, mask);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(__inode_permission2);
|
||||||
|
|
||||||
|
int __inode_permission(struct inode *inode, int mask)
|
||||||
|
{
|
||||||
|
return __inode_permission2(NULL, inode, mask);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__inode_permission);
|
EXPORT_SYMBOL(__inode_permission);
|
||||||
|
|
||||||
|
@ -455,14 +464,20 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
|
||||||
*
|
*
|
||||||
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
|
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
|
||||||
*/
|
*/
|
||||||
int inode_permission(struct inode *inode, int mask)
|
int inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
retval = sb_permission(inode->i_sb, inode, mask);
|
retval = sb_permission(inode->i_sb, inode, mask);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
return __inode_permission(inode, mask);
|
return __inode_permission2(mnt, inode, mask);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(inode_permission2);
|
||||||
|
|
||||||
|
int inode_permission(struct inode *inode, int mask)
|
||||||
|
{
|
||||||
|
return inode_permission2(NULL, inode, mask);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(inode_permission);
|
EXPORT_SYMBOL(inode_permission);
|
||||||
|
|
||||||
|
@ -1645,13 +1660,13 @@ static int lookup_slow(struct nameidata *nd, struct path *path)
|
||||||
static inline int may_lookup(struct nameidata *nd)
|
static inline int may_lookup(struct nameidata *nd)
|
||||||
{
|
{
|
||||||
if (nd->flags & LOOKUP_RCU) {
|
if (nd->flags & LOOKUP_RCU) {
|
||||||
int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
|
int err = inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
|
||||||
if (err != -ECHILD)
|
if (err != -ECHILD)
|
||||||
return err;
|
return err;
|
||||||
if (unlazy_walk(nd, NULL, 0))
|
if (unlazy_walk(nd, NULL, 0))
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
}
|
}
|
||||||
return inode_permission(nd->inode, MAY_EXEC);
|
return inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int handle_dots(struct nameidata *nd, int type)
|
static inline int handle_dots(struct nameidata *nd, int type)
|
||||||
|
@ -2005,11 +2020,12 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
|
||||||
nd->depth = 0;
|
nd->depth = 0;
|
||||||
if (flags & LOOKUP_ROOT) {
|
if (flags & LOOKUP_ROOT) {
|
||||||
struct dentry *root = nd->root.dentry;
|
struct dentry *root = nd->root.dentry;
|
||||||
|
struct vfsmount *mnt = nd->root.mnt;
|
||||||
struct inode *inode = root->d_inode;
|
struct inode *inode = root->d_inode;
|
||||||
if (*s) {
|
if (*s) {
|
||||||
if (!d_can_lookup(root))
|
if (!d_can_lookup(root))
|
||||||
return ERR_PTR(-ENOTDIR);
|
return ERR_PTR(-ENOTDIR);
|
||||||
retval = inode_permission(inode, MAY_EXEC);
|
retval = inode_permission2(mnt, inode, MAY_EXEC);
|
||||||
if (retval)
|
if (retval)
|
||||||
return ERR_PTR(retval);
|
return ERR_PTR(retval);
|
||||||
}
|
}
|
||||||
|
@ -2280,13 +2296,14 @@ EXPORT_SYMBOL(vfs_path_lookup);
|
||||||
/**
|
/**
|
||||||
* lookup_one_len - filesystem helper to lookup single pathname component
|
* lookup_one_len - filesystem helper to lookup single pathname component
|
||||||
* @name: pathname component to lookup
|
* @name: pathname component to lookup
|
||||||
|
* @mnt: mount we are looking up on
|
||||||
* @base: base directory to lookup from
|
* @base: base directory to lookup from
|
||||||
* @len: maximum length @len should be interpreted to
|
* @len: maximum length @len should be interpreted to
|
||||||
*
|
*
|
||||||
* Note that this routine is purely a helper for filesystem usage and should
|
* Note that this routine is purely a helper for filesystem usage and should
|
||||||
* not be called by generic code.
|
* not be called by generic code.
|
||||||
*/
|
*/
|
||||||
struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
struct dentry *lookup_one_len2(const char *name, struct vfsmount *mnt, struct dentry *base, int len)
|
||||||
{
|
{
|
||||||
struct qstr this;
|
struct qstr this;
|
||||||
unsigned int c;
|
unsigned int c;
|
||||||
|
@ -2320,12 +2337,18 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = inode_permission(base->d_inode, MAY_EXEC);
|
err = inode_permission2(mnt, base->d_inode, MAY_EXEC);
|
||||||
if (err)
|
if (err)
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
|
|
||||||
return __lookup_hash(&this, base, 0);
|
return __lookup_hash(&this, base, 0);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(lookup_one_len2);
|
||||||
|
|
||||||
|
struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
|
||||||
|
{
|
||||||
|
return lookup_one_len2(name, NULL, base, len);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(lookup_one_len);
|
EXPORT_SYMBOL(lookup_one_len);
|
||||||
|
|
||||||
int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
|
int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
|
||||||
|
@ -2552,7 +2575,7 @@ EXPORT_SYMBOL(__check_sticky);
|
||||||
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
|
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
|
||||||
* nfs_async_unlink().
|
* nfs_async_unlink().
|
||||||
*/
|
*/
|
||||||
static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
|
static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *victim, bool isdir)
|
||||||
{
|
{
|
||||||
struct inode *inode = d_backing_inode(victim);
|
struct inode *inode = d_backing_inode(victim);
|
||||||
int error;
|
int error;
|
||||||
|
@ -2564,7 +2587,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
|
||||||
BUG_ON(victim->d_parent->d_inode != dir);
|
BUG_ON(victim->d_parent->d_inode != dir);
|
||||||
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
|
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
|
||||||
|
|
||||||
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
if (IS_APPEND(dir))
|
if (IS_APPEND(dir))
|
||||||
|
@ -2595,14 +2618,14 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
|
||||||
* 3. We should have write and exec permissions on dir
|
* 3. We should have write and exec permissions on dir
|
||||||
* 4. We can't do it if dir is immutable (done in permission())
|
* 4. We can't do it if dir is immutable (done in permission())
|
||||||
*/
|
*/
|
||||||
static inline int may_create(struct inode *dir, struct dentry *child)
|
static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child)
|
||||||
{
|
{
|
||||||
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
|
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
|
||||||
if (child->d_inode)
|
if (child->d_inode)
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
if (IS_DEADDIR(dir))
|
if (IS_DEADDIR(dir))
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
return inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
return inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2649,10 +2672,10 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(unlock_rename);
|
EXPORT_SYMBOL(unlock_rename);
|
||||||
|
|
||||||
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry,
|
||||||
bool want_excl)
|
umode_t mode, bool want_excl)
|
||||||
{
|
{
|
||||||
int error = may_create(dir, dentry);
|
int error = may_create(mnt, dir, dentry);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
@ -2668,11 +2691,19 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||||
fsnotify_create(dir, dentry);
|
fsnotify_create(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_create2);
|
||||||
|
|
||||||
|
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||||
|
bool want_excl)
|
||||||
|
{
|
||||||
|
return vfs_create2(NULL, dir, dentry, mode, want_excl);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_create);
|
EXPORT_SYMBOL(vfs_create);
|
||||||
|
|
||||||
static int may_open(struct path *path, int acc_mode, int flag)
|
static int may_open(struct path *path, int acc_mode, int flag)
|
||||||
{
|
{
|
||||||
struct dentry *dentry = path->dentry;
|
struct dentry *dentry = path->dentry;
|
||||||
|
struct vfsmount *mnt = path->mnt;
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
@ -2701,7 +2732,7 @@ static int may_open(struct path *path, int acc_mode, int flag)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = inode_permission(inode, acc_mode);
|
error = inode_permission2(mnt, inode, acc_mode);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
@ -2736,7 +2767,7 @@ static int handle_truncate(struct file *filp)
|
||||||
if (!error)
|
if (!error)
|
||||||
error = security_path_truncate(path);
|
error = security_path_truncate(path);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
error = do_truncate(path->dentry, 0,
|
error = do_truncate2(path->mnt, path->dentry, 0,
|
||||||
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
|
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
|
||||||
filp);
|
filp);
|
||||||
}
|
}
|
||||||
|
@ -2757,7 +2788,7 @@ static int may_o_create(struct path *dir, struct dentry *dentry, umode_t mode)
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
|
error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
@ -2943,6 +2974,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
|
||||||
bool got_write, int *opened)
|
bool got_write, int *opened)
|
||||||
{
|
{
|
||||||
struct dentry *dir = nd->path.dentry;
|
struct dentry *dir = nd->path.dentry;
|
||||||
|
struct vfsmount *mnt = nd->path.mnt;
|
||||||
struct inode *dir_inode = dir->d_inode;
|
struct inode *dir_inode = dir->d_inode;
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
int error;
|
int error;
|
||||||
|
@ -2990,7 +3022,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
|
||||||
error = security_path_mknod(&nd->path, dentry, mode, 0);
|
error = security_path_mknod(&nd->path, dentry, mode, 0);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_dput;
|
goto out_dput;
|
||||||
error = vfs_create(dir->d_inode, dentry, mode,
|
error = vfs_create2(mnt, dir->d_inode, dentry, mode,
|
||||||
nd->flags & LOOKUP_EXCL);
|
nd->flags & LOOKUP_EXCL);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_dput;
|
goto out_dput;
|
||||||
|
@ -3252,7 +3284,7 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
|
||||||
goto out;
|
goto out;
|
||||||
dir = path.dentry->d_inode;
|
dir = path.dentry->d_inode;
|
||||||
/* we want directory to be writable */
|
/* we want directory to be writable */
|
||||||
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
|
error = inode_permission2(path.mnt, dir, MAY_WRITE | MAY_EXEC);
|
||||||
if (error)
|
if (error)
|
||||||
goto out2;
|
goto out2;
|
||||||
if (!dir->i_op->tmpfile) {
|
if (!dir->i_op->tmpfile) {
|
||||||
|
@ -3486,9 +3518,9 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(user_path_create);
|
EXPORT_SYMBOL(user_path_create);
|
||||||
|
|
||||||
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
||||||
{
|
{
|
||||||
int error = may_create(dir, dentry);
|
int error = may_create(mnt, dir, dentry);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
@ -3512,6 +3544,12 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
||||||
fsnotify_create(dir, dentry);
|
fsnotify_create(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_mknod2);
|
||||||
|
|
||||||
|
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
||||||
|
{
|
||||||
|
return vfs_mknod2(NULL, dir, dentry, mode, dev);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_mknod);
|
EXPORT_SYMBOL(vfs_mknod);
|
||||||
|
|
||||||
static int may_mknod(umode_t mode)
|
static int may_mknod(umode_t mode)
|
||||||
|
@ -3554,10 +3592,10 @@ retry:
|
||||||
goto out;
|
goto out;
|
||||||
switch (mode & S_IFMT) {
|
switch (mode & S_IFMT) {
|
||||||
case 0: case S_IFREG:
|
case 0: case S_IFREG:
|
||||||
error = vfs_create(path.dentry->d_inode,dentry,mode,true);
|
error = vfs_create2(path.mnt, path.dentry->d_inode,dentry,mode,true);
|
||||||
break;
|
break;
|
||||||
case S_IFCHR: case S_IFBLK:
|
case S_IFCHR: case S_IFBLK:
|
||||||
error = vfs_mknod(path.dentry->d_inode,dentry,mode,
|
error = vfs_mknod2(path.mnt, path.dentry->d_inode,dentry,mode,
|
||||||
new_decode_dev(dev));
|
new_decode_dev(dev));
|
||||||
break;
|
break;
|
||||||
case S_IFIFO: case S_IFSOCK:
|
case S_IFIFO: case S_IFSOCK:
|
||||||
|
@ -3578,9 +3616,9 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
|
||||||
return sys_mknodat(AT_FDCWD, filename, mode, dev);
|
return sys_mknodat(AT_FDCWD, filename, mode, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
int vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
{
|
{
|
||||||
int error = may_create(dir, dentry);
|
int error = may_create(mnt, dir, dentry);
|
||||||
unsigned max_links = dir->i_sb->s_max_links;
|
unsigned max_links = dir->i_sb->s_max_links;
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
|
@ -3602,6 +3640,12 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
fsnotify_mkdir(dir, dentry);
|
fsnotify_mkdir(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_mkdir2);
|
||||||
|
|
||||||
|
int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
|
{
|
||||||
|
return vfs_mkdir2(NULL, dir, dentry, mode);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_mkdir);
|
EXPORT_SYMBOL(vfs_mkdir);
|
||||||
|
|
||||||
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
|
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
|
||||||
|
@ -3620,7 +3664,7 @@ retry:
|
||||||
mode &= ~current_umask();
|
mode &= ~current_umask();
|
||||||
error = security_path_mkdir(&path, dentry, mode);
|
error = security_path_mkdir(&path, dentry, mode);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
|
error = vfs_mkdir2(path.mnt, path.dentry->d_inode, dentry, mode);
|
||||||
done_path_create(&path, dentry);
|
done_path_create(&path, dentry);
|
||||||
if (retry_estale(error, lookup_flags)) {
|
if (retry_estale(error, lookup_flags)) {
|
||||||
lookup_flags |= LOOKUP_REVAL;
|
lookup_flags |= LOOKUP_REVAL;
|
||||||
|
@ -3659,9 +3703,9 @@ void dentry_unhash(struct dentry *dentry)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dentry_unhash);
|
EXPORT_SYMBOL(dentry_unhash);
|
||||||
|
|
||||||
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
|
int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
int error = may_delete(dir, dentry, 1);
|
int error = may_delete(mnt, dir, dentry, 1);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
@ -3696,6 +3740,12 @@ out:
|
||||||
d_delete(dentry);
|
d_delete(dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_rmdir2);
|
||||||
|
|
||||||
|
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
|
{
|
||||||
|
return vfs_rmdir2(NULL, dir, dentry);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_rmdir);
|
EXPORT_SYMBOL(vfs_rmdir);
|
||||||
|
|
||||||
static long do_rmdir(int dfd, const char __user *pathname)
|
static long do_rmdir(int dfd, const char __user *pathname)
|
||||||
|
@ -3741,7 +3791,7 @@ retry:
|
||||||
error = security_path_rmdir(&path, dentry);
|
error = security_path_rmdir(&path, dentry);
|
||||||
if (error)
|
if (error)
|
||||||
goto exit3;
|
goto exit3;
|
||||||
error = vfs_rmdir(path.dentry->d_inode, dentry);
|
error = vfs_rmdir2(path.mnt, path.dentry->d_inode, dentry);
|
||||||
exit3:
|
exit3:
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
exit2:
|
exit2:
|
||||||
|
@ -3780,10 +3830,10 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
|
||||||
* be appropriate for callers that expect the underlying filesystem not
|
* be appropriate for callers that expect the underlying filesystem not
|
||||||
* to be NFS exported.
|
* to be NFS exported.
|
||||||
*/
|
*/
|
||||||
int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
|
int vfs_unlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
|
||||||
{
|
{
|
||||||
struct inode *target = dentry->d_inode;
|
struct inode *target = dentry->d_inode;
|
||||||
int error = may_delete(dir, dentry, 0);
|
int error = may_delete(mnt, dir, dentry, 0);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
@ -3818,6 +3868,12 @@ out:
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_unlink2);
|
||||||
|
|
||||||
|
int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
|
||||||
|
{
|
||||||
|
return vfs_unlink2(NULL, dir, dentry, delegated_inode);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_unlink);
|
EXPORT_SYMBOL(vfs_unlink);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3865,7 +3921,7 @@ retry_deleg:
|
||||||
error = security_path_unlink(&path, dentry);
|
error = security_path_unlink(&path, dentry);
|
||||||
if (error)
|
if (error)
|
||||||
goto exit2;
|
goto exit2;
|
||||||
error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode);
|
error = vfs_unlink2(path.mnt, path.dentry->d_inode, dentry, &delegated_inode);
|
||||||
exit2:
|
exit2:
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
}
|
}
|
||||||
|
@ -3915,9 +3971,9 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
|
||||||
return do_unlinkat(AT_FDCWD, pathname);
|
return do_unlinkat(AT_FDCWD, pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
|
int vfs_symlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, const char *oldname)
|
||||||
{
|
{
|
||||||
int error = may_create(dir, dentry);
|
int error = may_create(mnt, dir, dentry);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
@ -3934,6 +3990,12 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
|
||||||
fsnotify_create(dir, dentry);
|
fsnotify_create(dir, dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_symlink2);
|
||||||
|
|
||||||
|
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
|
||||||
|
{
|
||||||
|
return vfs_symlink2(NULL, dir, dentry, oldname);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_symlink);
|
EXPORT_SYMBOL(vfs_symlink);
|
||||||
|
|
||||||
SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
|
SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
|
||||||
|
@ -3956,7 +4018,7 @@ retry:
|
||||||
|
|
||||||
error = security_path_symlink(&path, dentry, from->name);
|
error = security_path_symlink(&path, dentry, from->name);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
|
error = vfs_symlink2(path.mnt, path.dentry->d_inode, dentry, from->name);
|
||||||
done_path_create(&path, dentry);
|
done_path_create(&path, dentry);
|
||||||
if (retry_estale(error, lookup_flags)) {
|
if (retry_estale(error, lookup_flags)) {
|
||||||
lookup_flags |= LOOKUP_REVAL;
|
lookup_flags |= LOOKUP_REVAL;
|
||||||
|
@ -3991,7 +4053,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
|
||||||
* be appropriate for callers that expect the underlying filesystem not
|
* be appropriate for callers that expect the underlying filesystem not
|
||||||
* to be NFS exported.
|
* to be NFS exported.
|
||||||
*/
|
*/
|
||||||
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
|
int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
|
||||||
{
|
{
|
||||||
struct inode *inode = old_dentry->d_inode;
|
struct inode *inode = old_dentry->d_inode;
|
||||||
unsigned max_links = dir->i_sb->s_max_links;
|
unsigned max_links = dir->i_sb->s_max_links;
|
||||||
|
@ -4000,7 +4062,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
|
||||||
if (!inode)
|
if (!inode)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
error = may_create(dir, new_dentry);
|
error = may_create(mnt, dir, new_dentry);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
@ -4043,6 +4105,12 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
|
||||||
fsnotify_link(dir, inode, new_dentry);
|
fsnotify_link(dir, inode, new_dentry);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_link2);
|
||||||
|
|
||||||
|
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
|
||||||
|
{
|
||||||
|
return vfs_link2(NULL, old_dentry, dir, new_dentry, delegated_inode);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_link);
|
EXPORT_SYMBOL(vfs_link);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -4098,7 +4166,7 @@ retry:
|
||||||
error = security_path_link(old_path.dentry, &new_path, new_dentry);
|
error = security_path_link(old_path.dentry, &new_path, new_dentry);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_dput;
|
goto out_dput;
|
||||||
error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
|
error = vfs_link2(old_path.mnt, old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
|
||||||
out_dput:
|
out_dput:
|
||||||
done_path_create(&new_path, new_dentry);
|
done_path_create(&new_path, new_dentry);
|
||||||
if (delegated_inode) {
|
if (delegated_inode) {
|
||||||
|
@ -4173,7 +4241,8 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
|
||||||
* ->i_mutex on parents, which works but leads to some truly excessive
|
* ->i_mutex on parents, which works but leads to some truly excessive
|
||||||
* locking].
|
* locking].
|
||||||
*/
|
*/
|
||||||
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
int vfs_rename2(struct vfsmount *mnt,
|
||||||
|
struct inode *old_dir, struct dentry *old_dentry,
|
||||||
struct inode *new_dir, struct dentry *new_dentry,
|
struct inode *new_dir, struct dentry *new_dentry,
|
||||||
struct inode **delegated_inode, unsigned int flags)
|
struct inode **delegated_inode, unsigned int flags)
|
||||||
{
|
{
|
||||||
|
@ -4192,19 +4261,19 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
if (vfs_select_inode(old_dentry, 0) == vfs_select_inode(new_dentry, 0))
|
if (vfs_select_inode(old_dentry, 0) == vfs_select_inode(new_dentry, 0))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error = may_delete(old_dir, old_dentry, is_dir);
|
error = may_delete(mnt, old_dir, old_dentry, is_dir);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (!target) {
|
if (!target) {
|
||||||
error = may_create(new_dir, new_dentry);
|
error = may_create(mnt, new_dir, new_dentry);
|
||||||
} else {
|
} else {
|
||||||
new_is_dir = d_is_dir(new_dentry);
|
new_is_dir = d_is_dir(new_dentry);
|
||||||
|
|
||||||
if (!(flags & RENAME_EXCHANGE))
|
if (!(flags & RENAME_EXCHANGE))
|
||||||
error = may_delete(new_dir, new_dentry, is_dir);
|
error = may_delete(mnt, new_dir, new_dentry, is_dir);
|
||||||
else
|
else
|
||||||
error = may_delete(new_dir, new_dentry, new_is_dir);
|
error = may_delete(mnt, new_dir, new_dentry, new_is_dir);
|
||||||
}
|
}
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
@ -4221,12 +4290,12 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
*/
|
*/
|
||||||
if (new_dir != old_dir) {
|
if (new_dir != old_dir) {
|
||||||
if (is_dir) {
|
if (is_dir) {
|
||||||
error = inode_permission(source, MAY_WRITE);
|
error = inode_permission2(mnt, source, MAY_WRITE);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
if ((flags & RENAME_EXCHANGE) && new_is_dir) {
|
if ((flags & RENAME_EXCHANGE) && new_is_dir) {
|
||||||
error = inode_permission(target, MAY_WRITE);
|
error = inode_permission2(mnt, target, MAY_WRITE);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -4309,6 +4378,14 @@ out:
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(vfs_rename2);
|
||||||
|
|
||||||
|
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
|
struct inode *new_dir, struct dentry *new_dentry,
|
||||||
|
struct inode **delegated_inode, unsigned int flags)
|
||||||
|
{
|
||||||
|
return vfs_rename2(NULL, old_dir, old_dentry, new_dir, new_dentry, delegated_inode, flags);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(vfs_rename);
|
EXPORT_SYMBOL(vfs_rename);
|
||||||
|
|
||||||
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
|
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
|
||||||
|
@ -4422,7 +4499,7 @@ retry_deleg:
|
||||||
&new_path, new_dentry, flags);
|
&new_path, new_dentry, flags);
|
||||||
if (error)
|
if (error)
|
||||||
goto exit5;
|
goto exit5;
|
||||||
error = vfs_rename(old_path.dentry->d_inode, old_dentry,
|
error = vfs_rename2(old_path.mnt, old_path.dentry->d_inode, old_dentry,
|
||||||
new_path.dentry->d_inode, new_dentry,
|
new_path.dentry->d_inode, new_dentry,
|
||||||
&delegated_inode, flags);
|
&delegated_inode, flags);
|
||||||
exit5:
|
exit5:
|
||||||
|
@ -4467,7 +4544,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
|
||||||
|
|
||||||
int vfs_whiteout(struct inode *dir, struct dentry *dentry)
|
int vfs_whiteout(struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
int error = may_create(dir, dentry);
|
int error = may_create(NULL, dir, dentry);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
|
|
@ -577,6 +577,7 @@ int sb_prepare_remount_readonly(struct super_block *sb)
|
||||||
|
|
||||||
static void free_vfsmnt(struct mount *mnt)
|
static void free_vfsmnt(struct mount *mnt)
|
||||||
{
|
{
|
||||||
|
kfree(mnt->mnt.data);
|
||||||
kfree_const(mnt->mnt_devname);
|
kfree_const(mnt->mnt_devname);
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
free_percpu(mnt->mnt_pcp);
|
free_percpu(mnt->mnt_pcp);
|
||||||
|
@ -966,11 +967,21 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
|
||||||
if (!mnt)
|
if (!mnt)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
mnt->mnt.data = NULL;
|
||||||
|
if (type->alloc_mnt_data) {
|
||||||
|
mnt->mnt.data = type->alloc_mnt_data();
|
||||||
|
if (!mnt->mnt.data) {
|
||||||
|
mnt_free_id(mnt);
|
||||||
|
free_vfsmnt(mnt);
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (flags & MS_KERNMOUNT)
|
if (flags & MS_KERNMOUNT)
|
||||||
mnt->mnt.mnt_flags = MNT_INTERNAL;
|
mnt->mnt.mnt_flags = MNT_INTERNAL;
|
||||||
|
|
||||||
root = mount_fs(type, flags, name, data);
|
root = mount_fs(type, flags, name, &mnt->mnt, data);
|
||||||
if (IS_ERR(root)) {
|
if (IS_ERR(root)) {
|
||||||
|
kfree(mnt->mnt.data);
|
||||||
mnt_free_id(mnt);
|
mnt_free_id(mnt);
|
||||||
free_vfsmnt(mnt);
|
free_vfsmnt(mnt);
|
||||||
return ERR_CAST(root);
|
return ERR_CAST(root);
|
||||||
|
@ -998,6 +1009,14 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
||||||
if (!mnt)
|
if (!mnt)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
if (sb->s_op->clone_mnt_data) {
|
||||||
|
mnt->mnt.data = sb->s_op->clone_mnt_data(old->mnt.data);
|
||||||
|
if (!mnt->mnt.data) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE))
|
if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE))
|
||||||
mnt->mnt_group_id = 0; /* not a peer of original */
|
mnt->mnt_group_id = 0; /* not a peer of original */
|
||||||
else
|
else
|
||||||
|
@ -1066,6 +1085,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
||||||
return mnt;
|
return mnt;
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
|
kfree(mnt->mnt.data);
|
||||||
mnt_free_id(mnt);
|
mnt_free_id(mnt);
|
||||||
free_vfsmnt(mnt);
|
free_vfsmnt(mnt);
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
|
@ -2234,8 +2254,14 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
|
||||||
err = change_mount_flags(path->mnt, flags);
|
err = change_mount_flags(path->mnt, flags);
|
||||||
else if (!capable(CAP_SYS_ADMIN))
|
else if (!capable(CAP_SYS_ADMIN))
|
||||||
err = -EPERM;
|
err = -EPERM;
|
||||||
else
|
else {
|
||||||
err = do_remount_sb(sb, flags, data, 0);
|
err = do_remount_sb2(path->mnt, sb, flags, data, 0);
|
||||||
|
namespace_lock();
|
||||||
|
lock_mount_hash();
|
||||||
|
propagate_remount(mnt);
|
||||||
|
unlock_mount_hash();
|
||||||
|
namespace_unlock();
|
||||||
|
}
|
||||||
if (!err) {
|
if (!err) {
|
||||||
lock_mount_hash();
|
lock_mount_hash();
|
||||||
mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;
|
mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;
|
||||||
|
|
|
@ -488,7 +488,7 @@ static int fanotify_find_path(int dfd, const char __user *filename,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* you can only watch an inode if you have read permissions on it */
|
/* you can only watch an inode if you have read permissions on it */
|
||||||
ret = inode_permission(path->dentry->d_inode, MAY_READ);
|
ret = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
|
||||||
if (ret)
|
if (ret)
|
||||||
path_put(path);
|
path_put(path);
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -337,7 +337,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
/* you can only watch an inode if you have read permissions on it */
|
/* you can only watch an inode if you have read permissions on it */
|
||||||
error = inode_permission(path->dentry->d_inode, MAY_READ);
|
error = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
|
||||||
if (error)
|
if (error)
|
||||||
path_put(path);
|
path_put(path);
|
||||||
return error;
|
return error;
|
||||||
|
|
37
fs/open.c
37
fs/open.c
|
@ -34,8 +34,8 @@
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
|
int do_truncate2(struct vfsmount *mnt, struct dentry *dentry, loff_t length,
|
||||||
struct file *filp)
|
unsigned int time_attrs, struct file *filp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct iattr newattrs;
|
struct iattr newattrs;
|
||||||
|
@ -60,17 +60,24 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
|
||||||
|
|
||||||
mutex_lock(&dentry->d_inode->i_mutex);
|
mutex_lock(&dentry->d_inode->i_mutex);
|
||||||
/* Note any delegations or leases have already been broken: */
|
/* Note any delegations or leases have already been broken: */
|
||||||
ret = notify_change(dentry, &newattrs, NULL);
|
ret = notify_change2(mnt, dentry, &newattrs, NULL);
|
||||||
mutex_unlock(&dentry->d_inode->i_mutex);
|
mutex_unlock(&dentry->d_inode->i_mutex);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
|
||||||
|
struct file *filp)
|
||||||
|
{
|
||||||
|
return do_truncate2(NULL, dentry, length, time_attrs, filp);
|
||||||
|
}
|
||||||
|
|
||||||
long vfs_truncate(struct path *path, loff_t length)
|
long vfs_truncate(struct path *path, loff_t length)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
struct vfsmount *mnt;
|
||||||
long error;
|
long error;
|
||||||
|
|
||||||
inode = path->dentry->d_inode;
|
inode = path->dentry->d_inode;
|
||||||
|
mnt = path->mnt;
|
||||||
|
|
||||||
/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
|
/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
|
||||||
if (S_ISDIR(inode->i_mode))
|
if (S_ISDIR(inode->i_mode))
|
||||||
|
@ -82,7 +89,7 @@ long vfs_truncate(struct path *path, loff_t length)
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
error = inode_permission(inode, MAY_WRITE);
|
error = inode_permission2(mnt, inode, MAY_WRITE);
|
||||||
if (error)
|
if (error)
|
||||||
goto mnt_drop_write_and_out;
|
goto mnt_drop_write_and_out;
|
||||||
|
|
||||||
|
@ -106,7 +113,7 @@ long vfs_truncate(struct path *path, loff_t length)
|
||||||
if (!error)
|
if (!error)
|
||||||
error = security_path_truncate(path);
|
error = security_path_truncate(path);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = do_truncate(path->dentry, length, 0, NULL);
|
error = do_truncate2(mnt, path->dentry, length, 0, NULL);
|
||||||
|
|
||||||
put_write_and_out:
|
put_write_and_out:
|
||||||
put_write_access(inode);
|
put_write_access(inode);
|
||||||
|
@ -155,6 +162,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
|
struct vfsmount *mnt;
|
||||||
struct fd f;
|
struct fd f;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
@ -171,6 +179,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
|
||||||
small = 0;
|
small = 0;
|
||||||
|
|
||||||
dentry = f.file->f_path.dentry;
|
dentry = f.file->f_path.dentry;
|
||||||
|
mnt = f.file->f_path.mnt;
|
||||||
inode = dentry->d_inode;
|
inode = dentry->d_inode;
|
||||||
error = -EINVAL;
|
error = -EINVAL;
|
||||||
if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))
|
if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))
|
||||||
|
@ -190,7 +199,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
|
||||||
if (!error)
|
if (!error)
|
||||||
error = security_path_truncate(&f.file->f_path);
|
error = security_path_truncate(&f.file->f_path);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
|
error = do_truncate2(mnt, dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
|
||||||
sb_end_write(inode->i_sb);
|
sb_end_write(inode->i_sb);
|
||||||
out_putf:
|
out_putf:
|
||||||
fdput(f);
|
fdput(f);
|
||||||
|
@ -340,6 +349,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||||
struct cred *override_cred;
|
struct cred *override_cred;
|
||||||
struct path path;
|
struct path path;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
struct vfsmount *mnt;
|
||||||
int res;
|
int res;
|
||||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||||
|
|
||||||
|
@ -370,6 +380,7 @@ retry:
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
inode = d_backing_inode(path.dentry);
|
inode = d_backing_inode(path.dentry);
|
||||||
|
mnt = path.mnt;
|
||||||
|
|
||||||
if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
|
if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
|
||||||
/*
|
/*
|
||||||
|
@ -381,7 +392,7 @@ retry:
|
||||||
goto out_path_release;
|
goto out_path_release;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = inode_permission(inode, mode | MAY_ACCESS);
|
res = inode_permission2(mnt, inode, mode | MAY_ACCESS);
|
||||||
/* SuS v2 requires we report a read only fs too */
|
/* SuS v2 requires we report a read only fs too */
|
||||||
if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
|
if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
|
||||||
goto out_path_release;
|
goto out_path_release;
|
||||||
|
@ -425,7 +436,7 @@ retry:
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
||||||
if (error)
|
if (error)
|
||||||
goto dput_and_out;
|
goto dput_and_out;
|
||||||
|
|
||||||
|
@ -445,6 +456,7 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
|
||||||
{
|
{
|
||||||
struct fd f = fdget_raw(fd);
|
struct fd f = fdget_raw(fd);
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
struct vfsmount *mnt;
|
||||||
int error = -EBADF;
|
int error = -EBADF;
|
||||||
|
|
||||||
error = -EBADF;
|
error = -EBADF;
|
||||||
|
@ -452,12 +464,13 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
inode = file_inode(f.file);
|
inode = file_inode(f.file);
|
||||||
|
mnt = f.file->f_path.mnt;
|
||||||
|
|
||||||
error = -ENOTDIR;
|
error = -ENOTDIR;
|
||||||
if (!S_ISDIR(inode->i_mode))
|
if (!S_ISDIR(inode->i_mode))
|
||||||
goto out_putf;
|
goto out_putf;
|
||||||
|
|
||||||
error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);
|
error = inode_permission2(mnt, inode, MAY_EXEC | MAY_CHDIR);
|
||||||
if (!error)
|
if (!error)
|
||||||
set_fs_pwd(current->fs, &f.file->f_path);
|
set_fs_pwd(current->fs, &f.file->f_path);
|
||||||
out_putf:
|
out_putf:
|
||||||
|
@ -476,7 +489,7 @@ retry:
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
|
||||||
if (error)
|
if (error)
|
||||||
goto dput_and_out;
|
goto dput_and_out;
|
||||||
|
|
||||||
|
@ -516,7 +529,7 @@ retry_deleg:
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
|
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
|
||||||
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
|
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
|
||||||
error = notify_change(path->dentry, &newattrs, &delegated_inode);
|
error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
if (delegated_inode) {
|
if (delegated_inode) {
|
||||||
|
@ -596,7 +609,7 @@ retry_deleg:
|
||||||
mutex_lock(&inode->i_mutex);
|
mutex_lock(&inode->i_mutex);
|
||||||
error = security_path_chown(path, uid, gid);
|
error = security_path_chown(path, uid, gid);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = notify_change(path->dentry, &newattrs, &delegated_inode);
|
error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
if (delegated_inode) {
|
if (delegated_inode) {
|
||||||
error = break_deleg_wait(&delegated_inode);
|
error = break_deleg_wait(&delegated_inode);
|
||||||
|
|
29
fs/pnode.c
29
fs/pnode.c
|
@ -458,3 +458,32 @@ int propagate_umount(struct list_head *list)
|
||||||
__propagate_umount(mnt);
|
__propagate_umount(mnt);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterates over all slaves, and slaves of slaves.
|
||||||
|
*/
|
||||||
|
static struct mount *next_descendent(struct mount *root, struct mount *cur)
|
||||||
|
{
|
||||||
|
if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list))
|
||||||
|
return first_slave(cur);
|
||||||
|
do {
|
||||||
|
if (cur->mnt_slave.next != &cur->mnt_master->mnt_slave_list)
|
||||||
|
return next_slave(cur);
|
||||||
|
cur = cur->mnt_master;
|
||||||
|
} while (cur != root);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void propagate_remount(struct mount *mnt)
|
||||||
|
{
|
||||||
|
struct mount *m = mnt;
|
||||||
|
struct super_block *sb = mnt->mnt.mnt_sb;
|
||||||
|
|
||||||
|
if (sb->s_op->copy_mnt_data) {
|
||||||
|
m = next_descendent(mnt, m);
|
||||||
|
while (m) {
|
||||||
|
sb->s_op->copy_mnt_data(m->mnt.data, mnt->mnt.data);
|
||||||
|
m = next_descendent(mnt, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ int propagate_mnt(struct mount *, struct mountpoint *, struct mount *,
|
||||||
int propagate_umount(struct list_head *);
|
int propagate_umount(struct list_head *);
|
||||||
int propagate_mount_busy(struct mount *, int);
|
int propagate_mount_busy(struct mount *, int);
|
||||||
void propagate_mount_unlock(struct mount *);
|
void propagate_mount_unlock(struct mount *);
|
||||||
|
void propagate_remount(struct mount *);
|
||||||
void mnt_release_group_id(struct mount *);
|
void mnt_release_group_id(struct mount *);
|
||||||
int get_dominating_id(struct mount *mnt, const struct path *root);
|
int get_dominating_id(struct mount *mnt, const struct path *root);
|
||||||
unsigned int mnt_get_count(struct mount *mnt);
|
unsigned int mnt_get_count(struct mount *mnt);
|
||||||
|
|
|
@ -118,7 +118,9 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
show_mnt_opts(m, mnt);
|
show_mnt_opts(m, mnt);
|
||||||
if (sb->s_op->show_options)
|
if (sb->s_op->show_options2)
|
||||||
|
err = sb->s_op->show_options2(mnt, m, mnt_path.dentry);
|
||||||
|
else if (sb->s_op->show_options)
|
||||||
err = sb->s_op->show_options(m, mnt_path.dentry);
|
err = sb->s_op->show_options(m, mnt_path.dentry);
|
||||||
seq_puts(m, " 0 0\n");
|
seq_puts(m, " 0 0\n");
|
||||||
out:
|
out:
|
||||||
|
@ -178,7 +180,9 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
|
||||||
err = show_sb_opts(m, sb);
|
err = show_sb_opts(m, sb);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
if (sb->s_op->show_options)
|
if (sb->s_op->show_options2) {
|
||||||
|
err = sb->s_op->show_options2(mnt, m, mnt->mnt_root);
|
||||||
|
} else if (sb->s_op->show_options)
|
||||||
err = sb->s_op->show_options(m, mnt->mnt_root);
|
err = sb->s_op->show_options(m, mnt->mnt_root);
|
||||||
seq_putc(m, '\n');
|
seq_putc(m, '\n');
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -30,11 +30,14 @@ static void inherit_derived_state(struct inode *parent, struct inode *child)
|
||||||
ci->userid = pi->userid;
|
ci->userid = pi->userid;
|
||||||
ci->d_uid = pi->d_uid;
|
ci->d_uid = pi->d_uid;
|
||||||
ci->under_android = pi->under_android;
|
ci->under_android = pi->under_android;
|
||||||
|
ci->under_cache = pi->under_cache;
|
||||||
|
ci->under_obb = pi->under_obb;
|
||||||
|
set_top(ci, pi->top);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* helper function for derived state */
|
/* helper function for derived state */
|
||||||
void setup_derived_state(struct inode *inode, perm_t perm,
|
void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
|
||||||
userid_t userid, uid_t uid, bool under_android)
|
uid_t uid, bool under_android, struct inode *top)
|
||||||
{
|
{
|
||||||
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
|
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
|
||||||
|
|
||||||
|
@ -42,92 +45,301 @@ void setup_derived_state(struct inode *inode, perm_t perm,
|
||||||
info->userid = userid;
|
info->userid = userid;
|
||||||
info->d_uid = uid;
|
info->d_uid = uid;
|
||||||
info->under_android = under_android;
|
info->under_android = under_android;
|
||||||
|
info->under_cache = false;
|
||||||
|
info->under_obb = false;
|
||||||
|
set_top(info, top);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */
|
/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */
|
||||||
void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry)
|
void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name)
|
||||||
{
|
{
|
||||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry));
|
||||||
struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode);
|
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
|
||||||
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
|
|
||||||
appid_t appid;
|
appid_t appid;
|
||||||
|
struct qstr q_Android = QSTR_LITERAL("Android");
|
||||||
|
struct qstr q_data = QSTR_LITERAL("data");
|
||||||
|
struct qstr q_obb = QSTR_LITERAL("obb");
|
||||||
|
struct qstr q_media = QSTR_LITERAL("media");
|
||||||
|
struct qstr q_cache = QSTR_LITERAL("cache");
|
||||||
|
|
||||||
/* By default, each inode inherits from its parent.
|
/* By default, each inode inherits from its parent.
|
||||||
* the properties are maintained on its private fields
|
* the properties are maintained on its private fields
|
||||||
* because the inode attributes will be modified with that of
|
* because the inode attributes will be modified with that of
|
||||||
* its lower inode.
|
* its lower inode.
|
||||||
* The derived state will be updated on the last
|
* These values are used by our custom permission call instead
|
||||||
* stage of each system call by fix_derived_permission(inode).
|
* of using the inode permissions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
inherit_derived_state(parent->d_inode, dentry->d_inode);
|
inherit_derived_state(d_inode(parent), d_inode(dentry));
|
||||||
|
|
||||||
|
/* Files don't get special labels */
|
||||||
|
if (!S_ISDIR(d_inode(dentry)->i_mode))
|
||||||
|
return;
|
||||||
/* Derive custom permissions based on parent and current node */
|
/* Derive custom permissions based on parent and current node */
|
||||||
switch (parent_info->perm) {
|
switch (parent_info->perm) {
|
||||||
case PERM_INHERIT:
|
case PERM_INHERIT:
|
||||||
|
case PERM_ANDROID_PACKAGE_CACHE:
|
||||||
/* Already inherited above */
|
/* Already inherited above */
|
||||||
break;
|
break;
|
||||||
case PERM_PRE_ROOT:
|
case PERM_PRE_ROOT:
|
||||||
/* Legacy internal layout places users at top level */
|
/* Legacy internal layout places users at top level */
|
||||||
info->perm = PERM_ROOT;
|
info->perm = PERM_ROOT;
|
||||||
info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10);
|
info->userid = simple_strtoul(name->name, NULL, 10);
|
||||||
|
set_top(info, &info->vfs_inode);
|
||||||
break;
|
break;
|
||||||
case PERM_ROOT:
|
case PERM_ROOT:
|
||||||
/* Assume masked off by default. */
|
/* Assume masked off by default. */
|
||||||
if (!strcasecmp(newdentry->d_name.name, "Android")) {
|
if (qstr_case_eq(name, &q_Android)) {
|
||||||
/* App-specific directories inside; let anyone traverse */
|
/* App-specific directories inside; let anyone traverse */
|
||||||
info->perm = PERM_ANDROID;
|
info->perm = PERM_ANDROID;
|
||||||
info->under_android = true;
|
info->under_android = true;
|
||||||
|
set_top(info, &info->vfs_inode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PERM_ANDROID:
|
case PERM_ANDROID:
|
||||||
if (!strcasecmp(newdentry->d_name.name, "data")) {
|
if (qstr_case_eq(name, &q_data)) {
|
||||||
/* App-specific directories inside; let anyone traverse */
|
/* App-specific directories inside; let anyone traverse */
|
||||||
info->perm = PERM_ANDROID_DATA;
|
info->perm = PERM_ANDROID_DATA;
|
||||||
} else if (!strcasecmp(newdentry->d_name.name, "obb")) {
|
set_top(info, &info->vfs_inode);
|
||||||
|
} else if (qstr_case_eq(name, &q_obb)) {
|
||||||
/* App-specific directories inside; let anyone traverse */
|
/* App-specific directories inside; let anyone traverse */
|
||||||
info->perm = PERM_ANDROID_OBB;
|
info->perm = PERM_ANDROID_OBB;
|
||||||
|
info->under_obb = true;
|
||||||
|
set_top(info, &info->vfs_inode);
|
||||||
/* Single OBB directory is always shared */
|
/* Single OBB directory is always shared */
|
||||||
} else if (!strcasecmp(newdentry->d_name.name, "media")) {
|
} else if (qstr_case_eq(name, &q_media)) {
|
||||||
/* App-specific directories inside; let anyone traverse */
|
/* App-specific directories inside; let anyone traverse */
|
||||||
info->perm = PERM_ANDROID_MEDIA;
|
info->perm = PERM_ANDROID_MEDIA;
|
||||||
|
set_top(info, &info->vfs_inode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PERM_ANDROID_DATA:
|
|
||||||
case PERM_ANDROID_OBB:
|
case PERM_ANDROID_OBB:
|
||||||
|
case PERM_ANDROID_DATA:
|
||||||
case PERM_ANDROID_MEDIA:
|
case PERM_ANDROID_MEDIA:
|
||||||
appid = get_appid(sbi->pkgl_id, newdentry->d_name.name);
|
info->perm = PERM_ANDROID_PACKAGE;
|
||||||
if (appid != 0) {
|
appid = get_appid(name->name);
|
||||||
|
if (appid != 0 && !is_excluded(name->name, parent_info->userid)) {
|
||||||
info->d_uid = multiuser_get_uid(parent_info->userid, appid);
|
info->d_uid = multiuser_get_uid(parent_info->userid, appid);
|
||||||
}
|
}
|
||||||
|
set_top(info, &info->vfs_inode);
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID_PACKAGE:
|
||||||
|
if (qstr_case_eq(name, &q_cache)) {
|
||||||
|
info->perm = PERM_ANDROID_PACKAGE_CACHE;
|
||||||
|
info->under_cache = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_derived_permission(struct dentry *parent, struct dentry *dentry)
|
void get_derived_permission(struct dentry *parent, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
get_derived_permission_new(parent, dentry, dentry);
|
get_derived_permission_new(parent, dentry, &dentry->d_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_derive_permissions_recursive(struct dentry *parent) {
|
static appid_t get_type(const char *name) {
|
||||||
|
const char *ext = strrchr(name, '.');
|
||||||
|
appid_t id;
|
||||||
|
|
||||||
|
if (ext && ext[0]) {
|
||||||
|
ext = &ext[1];
|
||||||
|
id = get_ext_gid(ext);
|
||||||
|
return id?:AID_MEDIA_RW;
|
||||||
|
}
|
||||||
|
return AID_MEDIA_RW;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fixup_lower_ownership(struct dentry* dentry, const char *name) {
|
||||||
|
struct path path;
|
||||||
|
struct inode *inode;
|
||||||
|
struct inode *delegated_inode = NULL;
|
||||||
|
int error;
|
||||||
|
struct sdcardfs_inode_info *info;
|
||||||
|
struct sdcardfs_inode_info *info_top;
|
||||||
|
perm_t perm;
|
||||||
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
uid_t uid = sbi->options.fs_low_uid;
|
||||||
|
gid_t gid = sbi->options.fs_low_gid;
|
||||||
|
struct iattr newattrs;
|
||||||
|
|
||||||
|
info = SDCARDFS_I(d_inode(dentry));
|
||||||
|
perm = info->perm;
|
||||||
|
if (info->under_obb) {
|
||||||
|
perm = PERM_ANDROID_OBB;
|
||||||
|
} else if (info->under_cache) {
|
||||||
|
perm = PERM_ANDROID_PACKAGE_CACHE;
|
||||||
|
} else if (perm == PERM_INHERIT) {
|
||||||
|
info_top = SDCARDFS_I(grab_top(info));
|
||||||
|
perm = info_top->perm;
|
||||||
|
release_top(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (perm) {
|
||||||
|
case PERM_ROOT:
|
||||||
|
case PERM_ANDROID:
|
||||||
|
case PERM_ANDROID_DATA:
|
||||||
|
case PERM_ANDROID_MEDIA:
|
||||||
|
case PERM_ANDROID_PACKAGE:
|
||||||
|
case PERM_ANDROID_PACKAGE_CACHE:
|
||||||
|
uid = multiuser_get_uid(info->userid, uid);
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID_OBB:
|
||||||
|
uid = AID_MEDIA_OBB;
|
||||||
|
break;
|
||||||
|
case PERM_PRE_ROOT:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (perm) {
|
||||||
|
case PERM_ROOT:
|
||||||
|
case PERM_ANDROID:
|
||||||
|
case PERM_ANDROID_DATA:
|
||||||
|
case PERM_ANDROID_MEDIA:
|
||||||
|
if (S_ISDIR(d_inode(dentry)->i_mode))
|
||||||
|
gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
|
||||||
|
else
|
||||||
|
gid = multiuser_get_uid(info->userid, get_type(name));
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID_OBB:
|
||||||
|
gid = AID_MEDIA_OBB;
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID_PACKAGE:
|
||||||
|
if (info->d_uid != 0)
|
||||||
|
gid = multiuser_get_ext_gid(info->userid, info->d_uid);
|
||||||
|
else
|
||||||
|
gid = multiuser_get_uid(info->userid, uid);
|
||||||
|
break;
|
||||||
|
case PERM_ANDROID_PACKAGE_CACHE:
|
||||||
|
if (info->d_uid != 0)
|
||||||
|
gid = multiuser_get_cache_gid(info->userid, info->d_uid);
|
||||||
|
else
|
||||||
|
gid = multiuser_get_uid(info->userid, uid);
|
||||||
|
break;
|
||||||
|
case PERM_PRE_ROOT:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdcardfs_get_lower_path(dentry, &path);
|
||||||
|
inode = d_inode(path.dentry);
|
||||||
|
if (d_inode(path.dentry)->i_gid.val != gid || d_inode(path.dentry)->i_uid.val != uid) {
|
||||||
|
retry_deleg:
|
||||||
|
newattrs.ia_valid = ATTR_GID | ATTR_UID | ATTR_FORCE;
|
||||||
|
newattrs.ia_uid = make_kuid(current_user_ns(), uid);
|
||||||
|
newattrs.ia_gid = make_kgid(current_user_ns(), gid);
|
||||||
|
if (!S_ISDIR(inode->i_mode))
|
||||||
|
newattrs.ia_valid |=
|
||||||
|
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
|
||||||
|
mutex_lock(&inode->i_mutex);
|
||||||
|
error = security_path_chown(&path, newattrs.ia_uid, newattrs.ia_gid);
|
||||||
|
if (!error)
|
||||||
|
error = notify_change2(path.mnt, path.dentry, &newattrs, &delegated_inode);
|
||||||
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
if (delegated_inode) {
|
||||||
|
error = break_deleg_wait(&delegated_inode);
|
||||||
|
if (!error)
|
||||||
|
goto retry_deleg;
|
||||||
|
}
|
||||||
|
if (error)
|
||||||
|
pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) {
|
||||||
|
if (info->perm == PERM_ROOT)
|
||||||
|
return (limit->flags & BY_USERID)?info->userid == limit->userid:1;
|
||||||
|
if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int needs_fixup(perm_t perm) {
|
||||||
|
if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB
|
||||||
|
|| perm == PERM_ANDROID_MEDIA)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) {
|
||||||
|
struct dentry *child;
|
||||||
|
struct sdcardfs_inode_info *info;
|
||||||
|
if (!dget(dentry))
|
||||||
|
return;
|
||||||
|
if (!d_inode(dentry)) {
|
||||||
|
dput(dentry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
info = SDCARDFS_I(d_inode(dentry));
|
||||||
|
|
||||||
|
if (needs_fixup(info->perm)) {
|
||||||
|
spin_lock(&dentry->d_lock);
|
||||||
|
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
|
||||||
|
dget(child);
|
||||||
|
if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) {
|
||||||
|
if (d_inode(child)) {
|
||||||
|
get_derived_permission(dentry, child);
|
||||||
|
fixup_tmp_permissions(d_inode(child));
|
||||||
|
dput(child);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dput(child);
|
||||||
|
}
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
} else if (descendant_may_need_fixup(info, limit)) {
|
||||||
|
spin_lock(&dentry->d_lock);
|
||||||
|
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
|
||||||
|
fixup_perms_recursive(child, limit);
|
||||||
|
}
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
}
|
||||||
|
dput(dentry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void drop_recursive(struct dentry *parent) {
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
|
struct sdcardfs_inode_info *info;
|
||||||
|
if (!d_inode(parent))
|
||||||
|
return;
|
||||||
|
info = SDCARDFS_I(d_inode(parent));
|
||||||
|
spin_lock(&parent->d_lock);
|
||||||
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
|
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
|
||||||
if (dentry->d_inode) {
|
if (d_inode(dentry)) {
|
||||||
mutex_lock(&dentry->d_inode->i_mutex);
|
if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) {
|
||||||
|
drop_recursive(dentry);
|
||||||
|
d_drop(dentry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&parent->d_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fixup_top_recursive(struct dentry *parent) {
|
||||||
|
struct dentry *dentry;
|
||||||
|
struct sdcardfs_inode_info *info;
|
||||||
|
|
||||||
|
if (!d_inode(parent))
|
||||||
|
return;
|
||||||
|
info = SDCARDFS_I(d_inode(parent));
|
||||||
|
spin_lock(&parent->d_lock);
|
||||||
|
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
|
||||||
|
if (d_inode(dentry)) {
|
||||||
|
if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) {
|
||||||
get_derived_permission(parent, dentry);
|
get_derived_permission(parent, dentry);
|
||||||
fix_derived_permission(dentry->d_inode);
|
fixup_tmp_permissions(d_inode(dentry));
|
||||||
get_derive_permissions_recursive(dentry);
|
fixup_top_recursive(dentry);
|
||||||
mutex_unlock(&dentry->d_inode->i_mutex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
spin_unlock(&parent->d_lock);
|
||||||
|
}
|
||||||
|
|
||||||
/* main function for updating derived permission */
|
/* main function for updating derived permission */
|
||||||
inline void update_derived_permission_lock(struct dentry *dentry)
|
inline void update_derived_permission_lock(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct dentry *parent;
|
struct dentry *parent;
|
||||||
|
|
||||||
if(!dentry || !dentry->d_inode) {
|
if(!dentry || !d_inode(dentry)) {
|
||||||
printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__);
|
printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -135,9 +347,8 @@ inline void update_derived_permission_lock(struct dentry *dentry)
|
||||||
* 1. need to check whether the dentry is updated or not
|
* 1. need to check whether the dentry is updated or not
|
||||||
* 2. remove the root dentry update
|
* 2. remove the root dentry update
|
||||||
*/
|
*/
|
||||||
mutex_lock(&dentry->d_inode->i_mutex);
|
|
||||||
if(IS_ROOT(dentry)) {
|
if(IS_ROOT(dentry)) {
|
||||||
//setup_default_pre_root_state(dentry->d_inode);
|
//setup_default_pre_root_state(d_inode(dentry));
|
||||||
} else {
|
} else {
|
||||||
parent = dget_parent(dentry);
|
parent = dget_parent(dentry);
|
||||||
if(parent) {
|
if(parent) {
|
||||||
|
@ -145,19 +356,19 @@ inline void update_derived_permission_lock(struct dentry *dentry)
|
||||||
dput(parent);
|
dput(parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fix_derived_permission(dentry->d_inode);
|
fixup_tmp_permissions(d_inode(dentry));
|
||||||
mutex_unlock(&dentry->d_inode->i_mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int need_graft_path(struct dentry *dentry)
|
int need_graft_path(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct dentry *parent = dget_parent(dentry);
|
struct dentry *parent = dget_parent(dentry);
|
||||||
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
|
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
|
||||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
struct qstr obb = QSTR_LITERAL("obb");
|
||||||
|
|
||||||
if(parent_info->perm == PERM_ANDROID &&
|
if(parent_info->perm == PERM_ANDROID &&
|
||||||
!strcasecmp(dentry->d_name.name, "obb")) {
|
qstr_case_eq(&dentry->d_name, &obb)) {
|
||||||
|
|
||||||
/* /Android/obb is the base obbpath of DERIVED_UNIFIED */
|
/* /Android/obb is the base obbpath of DERIVED_UNIFIED */
|
||||||
if(!(sbi->options.multiuser == false
|
if(!(sbi->options.multiuser == false
|
||||||
|
@ -194,7 +405,7 @@ int is_obbpath_invalid(struct dentry *dent)
|
||||||
} else {
|
} else {
|
||||||
obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX);
|
obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX);
|
||||||
if (d_unhashed(di->lower_path.dentry) ||
|
if (d_unhashed(di->lower_path.dentry) ||
|
||||||
strcasecmp(sbi->obbpath_s, obbpath_s)) {
|
!str_case_eq(sbi->obbpath_s, obbpath_s)) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
kfree(path_buf);
|
kfree(path_buf);
|
||||||
|
@ -212,17 +423,18 @@ int is_base_obbpath(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct dentry *parent = dget_parent(dentry);
|
struct dentry *parent = dget_parent(dentry);
|
||||||
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
|
struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
|
||||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
struct qstr q_obb = QSTR_LITERAL("obb");
|
||||||
|
|
||||||
spin_lock(&SDCARDFS_D(dentry)->lock);
|
spin_lock(&SDCARDFS_D(dentry)->lock);
|
||||||
if (sbi->options.multiuser) {
|
if (sbi->options.multiuser) {
|
||||||
if(parent_info->perm == PERM_PRE_ROOT &&
|
if(parent_info->perm == PERM_PRE_ROOT &&
|
||||||
!strcasecmp(dentry->d_name.name, "obb")) {
|
qstr_case_eq(&dentry->d_name, &q_obb)) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
} else if (parent_info->perm == PERM_ANDROID &&
|
} else if (parent_info->perm == PERM_ANDROID &&
|
||||||
!strcasecmp(dentry->d_name.name, "obb")) {
|
qstr_case_eq(&dentry->d_name, &q_obb)) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
spin_unlock(&SDCARDFS_D(dentry)->lock);
|
spin_unlock(&SDCARDFS_D(dentry)->lock);
|
||||||
|
|
|
@ -216,7 +216,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
|
if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
|
||||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||||
" dentry: %s, task:%s\n",
|
" dentry: %s, task:%s\n",
|
||||||
__func__, dentry->d_name.name, current->comm);
|
__func__, dentry->d_name.name, current->comm);
|
||||||
|
@ -225,7 +225,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save current_cred and override it */
|
/* save current_cred and override it */
|
||||||
OVERRIDE_CRED(sbi, saved_cred);
|
OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode));
|
||||||
|
|
||||||
file->private_data =
|
file->private_data =
|
||||||
kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);
|
kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);
|
||||||
|
|
|
@ -19,18 +19,24 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "sdcardfs.h"
|
#include "sdcardfs.h"
|
||||||
|
#include <linux/fs_struct.h>
|
||||||
|
|
||||||
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
|
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
|
||||||
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi)
|
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info)
|
||||||
{
|
{
|
||||||
struct cred * cred;
|
struct cred * cred;
|
||||||
const struct cred * old_cred;
|
const struct cred * old_cred;
|
||||||
|
uid_t uid;
|
||||||
|
|
||||||
cred = prepare_creds();
|
cred = prepare_creds();
|
||||||
if (!cred)
|
if (!cred)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid);
|
if (info->under_obb)
|
||||||
|
uid = AID_MEDIA_OBB;
|
||||||
|
else
|
||||||
|
uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid);
|
||||||
|
cred->fsuid = make_kuid(&init_user_ns, uid);
|
||||||
cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
|
cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
|
||||||
|
|
||||||
old_cred = override_creds(cred);
|
old_cred = override_creds(cred);
|
||||||
|
@ -53,11 +59,14 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct dentry *lower_dentry;
|
struct dentry *lower_dentry;
|
||||||
|
struct vfsmount *lower_dentry_mnt;
|
||||||
struct dentry *lower_parent_dentry = NULL;
|
struct dentry *lower_parent_dentry = NULL;
|
||||||
struct path lower_path;
|
struct path lower_path;
|
||||||
const struct cred *saved_cred = NULL;
|
const struct cred *saved_cred = NULL;
|
||||||
|
struct fs_struct *saved_fs;
|
||||||
|
struct fs_struct *copied_fs;
|
||||||
|
|
||||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
if(!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||||
" dentry: %s, task:%s\n",
|
" dentry: %s, task:%s\n",
|
||||||
__func__, dentry->d_name.name, current->comm);
|
__func__, dentry->d_name.name, current->comm);
|
||||||
|
@ -66,15 +75,26 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save current_cred and override it */
|
/* save current_cred and override it */
|
||||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
|
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
|
||||||
|
|
||||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
lower_dentry = lower_path.dentry;
|
lower_dentry = lower_path.dentry;
|
||||||
|
lower_dentry_mnt = lower_path.mnt;
|
||||||
lower_parent_dentry = lock_parent(lower_dentry);
|
lower_parent_dentry = lock_parent(lower_dentry);
|
||||||
|
|
||||||
/* set last 16bytes of mode field to 0664 */
|
/* set last 16bytes of mode field to 0664 */
|
||||||
mode = (mode & S_IFMT) | 00664;
|
mode = (mode & S_IFMT) | 00664;
|
||||||
err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl);
|
|
||||||
|
/* temporarily change umask for lower fs write */
|
||||||
|
saved_fs = current->fs;
|
||||||
|
copied_fs = copy_fs_struct(current->fs);
|
||||||
|
if (!copied_fs) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
current->fs = copied_fs;
|
||||||
|
current->fs->umask = 0;
|
||||||
|
err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -83,8 +103,12 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
|
||||||
goto out;
|
goto out;
|
||||||
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
|
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
|
||||||
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
|
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
|
||||||
|
fixup_lower_ownership(dentry, dentry->d_name.name);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
current->fs = saved_fs;
|
||||||
|
free_fs_struct(copied_fs);
|
||||||
|
out_unlock:
|
||||||
unlock_dir(lower_parent_dentry);
|
unlock_dir(lower_parent_dentry);
|
||||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
REVERT_CRED(saved_cred);
|
REVERT_CRED(saved_cred);
|
||||||
|
@ -138,12 +162,13 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct dentry *lower_dentry;
|
struct dentry *lower_dentry;
|
||||||
|
struct vfsmount *lower_mnt;
|
||||||
struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
|
struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
|
||||||
struct dentry *lower_dir_dentry;
|
struct dentry *lower_dir_dentry;
|
||||||
struct path lower_path;
|
struct path lower_path;
|
||||||
const struct cred *saved_cred = NULL;
|
const struct cred *saved_cred = NULL;
|
||||||
|
|
||||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
if(!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||||
" dentry: %s, task:%s\n",
|
" dentry: %s, task:%s\n",
|
||||||
__func__, dentry->d_name.name, current->comm);
|
__func__, dentry->d_name.name, current->comm);
|
||||||
|
@ -152,14 +177,15 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save current_cred and override it */
|
/* save current_cred and override it */
|
||||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
|
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
|
||||||
|
|
||||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
lower_dentry = lower_path.dentry;
|
lower_dentry = lower_path.dentry;
|
||||||
|
lower_mnt = lower_path.mnt;
|
||||||
dget(lower_dentry);
|
dget(lower_dentry);
|
||||||
lower_dir_dentry = lock_parent(lower_dentry);
|
lower_dir_dentry = lock_parent(lower_dentry);
|
||||||
|
|
||||||
err = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
|
err = vfs_unlink2(lower_mnt, lower_dir_inode, lower_dentry, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: unlinking on top of NFS can cause silly-renamed files.
|
* Note: unlinking on top of NFS can cause silly-renamed files.
|
||||||
|
@ -240,18 +266,19 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
|
||||||
int err;
|
int err;
|
||||||
int make_nomedia_in_obb = 0;
|
int make_nomedia_in_obb = 0;
|
||||||
struct dentry *lower_dentry;
|
struct dentry *lower_dentry;
|
||||||
|
struct vfsmount *lower_mnt;
|
||||||
struct dentry *lower_parent_dentry = NULL;
|
struct dentry *lower_parent_dentry = NULL;
|
||||||
struct path lower_path;
|
struct path lower_path;
|
||||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
const struct cred *saved_cred = NULL;
|
const struct cred *saved_cred = NULL;
|
||||||
struct sdcardfs_inode_info *pi = SDCARDFS_I(dir);
|
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;
|
int touch_err = 0;
|
||||||
|
struct fs_struct *saved_fs;
|
||||||
|
struct fs_struct *copied_fs;
|
||||||
|
struct qstr q_obb = QSTR_LITERAL("obb");
|
||||||
|
struct qstr q_data = QSTR_LITERAL("data");
|
||||||
|
|
||||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
if(!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||||
" dentry: %s, task:%s\n",
|
" dentry: %s, task:%s\n",
|
||||||
__func__, dentry->d_name.name, current->comm);
|
__func__, dentry->d_name.name, current->comm);
|
||||||
|
@ -260,7 +287,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save current_cred and override it */
|
/* save current_cred and override it */
|
||||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
|
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
|
||||||
|
|
||||||
/* check disk space */
|
/* check disk space */
|
||||||
if (!check_min_free_space(dentry, 0, 1)) {
|
if (!check_min_free_space(dentry, 0, 1)) {
|
||||||
|
@ -272,14 +299,28 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
|
||||||
/* the lower_dentry is negative here */
|
/* the lower_dentry is negative here */
|
||||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
lower_dentry = lower_path.dentry;
|
lower_dentry = lower_path.dentry;
|
||||||
|
lower_mnt = lower_path.mnt;
|
||||||
lower_parent_dentry = lock_parent(lower_dentry);
|
lower_parent_dentry = lock_parent(lower_dentry);
|
||||||
|
|
||||||
/* set last 16bytes of mode field to 0775 */
|
/* set last 16bytes of mode field to 0775 */
|
||||||
mode = (mode & S_IFMT) | 00775;
|
mode = (mode & S_IFMT) | 00775;
|
||||||
err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode);
|
|
||||||
|
|
||||||
if (err)
|
/* temporarily change umask for lower fs write */
|
||||||
|
saved_fs = current->fs;
|
||||||
|
copied_fs = copy_fs_struct(current->fs);
|
||||||
|
if (!copied_fs) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
unlock_dir(lower_parent_dentry);
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
current->fs = copied_fs;
|
||||||
|
current->fs->umask = 0;
|
||||||
|
err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
unlock_dir(lower_parent_dentry);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* if it is a local obb dentry, setup it with the base obbpath */
|
/* if it is a local obb dentry, setup it with the base obbpath */
|
||||||
if(need_graft_path(dentry)) {
|
if(need_graft_path(dentry)) {
|
||||||
|
@ -301,58 +342,38 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
|
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
|
||||||
if (err)
|
if (err) {
|
||||||
|
unlock_dir(lower_parent_dentry);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
|
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
|
||||||
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
|
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
|
||||||
/* update number of links on parent directory */
|
/* update number of links on parent directory */
|
||||||
set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);
|
set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);
|
||||||
|
fixup_lower_ownership(dentry, dentry->d_name.name);
|
||||||
if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb"))
|
unlock_dir(lower_parent_dentry);
|
||||||
|
if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb))
|
||||||
&& (pi->perm == PERM_ANDROID) && (pi->userid == 0))
|
&& (pi->perm == PERM_ANDROID) && (pi->userid == 0))
|
||||||
make_nomedia_in_obb = 1;
|
make_nomedia_in_obb = 1;
|
||||||
|
|
||||||
/* When creating /Android/data and /Android/obb, mark them as .nomedia */
|
/* When creating /Android/data and /Android/obb, mark them as .nomedia */
|
||||||
if (make_nomedia_in_obb ||
|
if (make_nomedia_in_obb ||
|
||||||
((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) {
|
((pi->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) {
|
||||||
|
REVERT_CRED(saved_cred);
|
||||||
page_buf = (char *)__get_free_page(GFP_KERNEL);
|
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry)));
|
||||||
if (!page_buf) {
|
set_fs_pwd(current->fs, &lower_path);
|
||||||
printk(KERN_ERR "sdcardfs: failed to allocate page buf\n");
|
touch_err = touch(".nomedia", 0664);
|
||||||
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) {
|
if (touch_err) {
|
||||||
printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n",
|
printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n",
|
||||||
nomedia_fullpath, touch_err);
|
lower_path.dentry->d_name.name, touch_err);
|
||||||
kfree(nomedia_fullpath);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
kfree(nomedia_fullpath);
|
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
unlock_dir(lower_parent_dentry);
|
current->fs = saved_fs;
|
||||||
|
free_fs_struct(copied_fs);
|
||||||
|
out_unlock:
|
||||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
out_revert:
|
out_revert:
|
||||||
REVERT_CRED(saved_cred);
|
REVERT_CRED(saved_cred);
|
||||||
|
@ -364,11 +385,12 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct dentry *lower_dentry;
|
struct dentry *lower_dentry;
|
||||||
struct dentry *lower_dir_dentry;
|
struct dentry *lower_dir_dentry;
|
||||||
|
struct vfsmount *lower_mnt;
|
||||||
int err;
|
int err;
|
||||||
struct path lower_path;
|
struct path lower_path;
|
||||||
const struct cred *saved_cred = NULL;
|
const struct cred *saved_cred = NULL;
|
||||||
|
|
||||||
if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
|
if(!check_caller_access_to_name(dir, &dentry->d_name)) {
|
||||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||||
" dentry: %s, task:%s\n",
|
" dentry: %s, task:%s\n",
|
||||||
__func__, dentry->d_name.name, current->comm);
|
__func__, dentry->d_name.name, current->comm);
|
||||||
|
@ -377,16 +399,17 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save current_cred and override it */
|
/* save current_cred and override it */
|
||||||
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
|
OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
|
||||||
|
|
||||||
/* sdcardfs_get_real_lower(): in case of remove an user's obb dentry
|
/* sdcardfs_get_real_lower(): in case of remove an user's obb dentry
|
||||||
* the dentry on the original path should be deleted. */
|
* the dentry on the original path should be deleted. */
|
||||||
sdcardfs_get_real_lower(dentry, &lower_path);
|
sdcardfs_get_real_lower(dentry, &lower_path);
|
||||||
|
|
||||||
lower_dentry = lower_path.dentry;
|
lower_dentry = lower_path.dentry;
|
||||||
|
lower_mnt = lower_path.mnt;
|
||||||
lower_dir_dentry = lock_parent(lower_dentry);
|
lower_dir_dentry = lock_parent(lower_dentry);
|
||||||
|
|
||||||
err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry);
|
err = vfs_rmdir2(lower_mnt, d_inode(lower_dir_dentry), lower_dentry);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -450,13 +473,13 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
struct dentry *lower_new_dentry = NULL;
|
struct dentry *lower_new_dentry = NULL;
|
||||||
struct dentry *lower_old_dir_dentry = NULL;
|
struct dentry *lower_old_dir_dentry = NULL;
|
||||||
struct dentry *lower_new_dir_dentry = NULL;
|
struct dentry *lower_new_dir_dentry = NULL;
|
||||||
|
struct vfsmount *lower_mnt = NULL;
|
||||||
struct dentry *trap = NULL;
|
struct dentry *trap = NULL;
|
||||||
struct dentry *new_parent = NULL;
|
|
||||||
struct path lower_old_path, lower_new_path;
|
struct path lower_old_path, lower_new_path;
|
||||||
const struct cred *saved_cred = NULL;
|
const struct cred *saved_cred = NULL;
|
||||||
|
|
||||||
if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) ||
|
if(!check_caller_access_to_name(old_dir, &old_dentry->d_name) ||
|
||||||
!check_caller_access_to_name(new_dir, new_dentry->d_name.name)) {
|
!check_caller_access_to_name(new_dir, &new_dentry->d_name)) {
|
||||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||||
" new_dentry: %s, task:%s\n",
|
" new_dentry: %s, task:%s\n",
|
||||||
__func__, new_dentry->d_name.name, current->comm);
|
__func__, new_dentry->d_name.name, current->comm);
|
||||||
|
@ -465,12 +488,13 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save current_cred and override it */
|
/* save current_cred and override it */
|
||||||
OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred);
|
OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir));
|
||||||
|
|
||||||
sdcardfs_get_real_lower(old_dentry, &lower_old_path);
|
sdcardfs_get_real_lower(old_dentry, &lower_old_path);
|
||||||
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
|
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
|
||||||
lower_old_dentry = lower_old_path.dentry;
|
lower_old_dentry = lower_old_path.dentry;
|
||||||
lower_new_dentry = lower_new_path.dentry;
|
lower_new_dentry = lower_new_path.dentry;
|
||||||
|
lower_mnt = lower_old_path.mnt;
|
||||||
lower_old_dir_dentry = dget_parent(lower_old_dentry);
|
lower_old_dir_dentry = dget_parent(lower_old_dentry);
|
||||||
lower_new_dir_dentry = dget_parent(lower_new_dentry);
|
lower_new_dir_dentry = dget_parent(lower_new_dentry);
|
||||||
|
|
||||||
|
@ -486,7 +510,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry,
|
err = vfs_rename2(lower_mnt,
|
||||||
|
d_inode(lower_old_dir_dentry), lower_old_dentry,
|
||||||
d_inode(lower_new_dir_dentry), lower_new_dentry,
|
d_inode(lower_new_dir_dentry), lower_new_dentry,
|
||||||
NULL, 0);
|
NULL, 0);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -499,25 +524,11 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
if (new_dir != old_dir) {
|
if (new_dir != old_dir) {
|
||||||
sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry));
|
sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry));
|
||||||
fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry));
|
fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry));
|
||||||
|
|
||||||
/* update the derived permission of the old_dentry
|
|
||||||
* with its new parent
|
|
||||||
*/
|
|
||||||
new_parent = dget_parent(new_dentry);
|
|
||||||
if(new_parent) {
|
|
||||||
if(d_inode(old_dentry)) {
|
|
||||||
update_derived_permission_lock(old_dentry);
|
|
||||||
}
|
}
|
||||||
dput(new_parent);
|
get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name);
|
||||||
}
|
fixup_tmp_permissions(d_inode(old_dentry));
|
||||||
}
|
fixup_lower_ownership(old_dentry, new_dentry->d_name.name);
|
||||||
/* At this point, not all dentry information has been moved, so
|
drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */
|
||||||
* we pass along new_dentry for the name.*/
|
|
||||||
mutex_lock(&d_inode(old_dentry)->i_mutex);
|
|
||||||
get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry);
|
|
||||||
fix_derived_permission(d_inode(old_dentry));
|
|
||||||
get_derive_permissions_recursive(old_dentry);
|
|
||||||
mutex_unlock(&d_inode(old_dentry)->i_mutex);
|
|
||||||
out:
|
out:
|
||||||
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
|
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
|
||||||
dput(lower_old_dir_dentry);
|
dput(lower_old_dir_dentry);
|
||||||
|
@ -586,16 +597,63 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int sdcardfs_permission(struct inode *inode, int mask)
|
static int sdcardfs_permission_wrn(struct inode *inode, int mask)
|
||||||
|
{
|
||||||
|
WARN(1, "sdcardfs does not support permission. Use permission2.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_attrs(struct inode *dest, const struct inode *src)
|
||||||
|
{
|
||||||
|
dest->i_mode = src->i_mode;
|
||||||
|
dest->i_uid = src->i_uid;
|
||||||
|
dest->i_gid = src->i_gid;
|
||||||
|
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;
|
||||||
|
#ifdef CONFIG_FS_POSIX_ACL
|
||||||
|
dest->i_acl = src->i_acl;
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SECURITY
|
||||||
|
dest->i_security = src->i_security;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int mask)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
struct inode tmp;
|
||||||
|
struct inode *top = grab_top(SDCARDFS_I(inode));
|
||||||
|
|
||||||
|
if (!top) {
|
||||||
|
release_top(SDCARDFS_I(inode));
|
||||||
|
WARN(1, "Top value was null!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Permission check on sdcardfs inode.
|
* Permission check on sdcardfs inode.
|
||||||
* Calling process should have AID_SDCARD_RW permission
|
* Calling process should have AID_SDCARD_RW permission
|
||||||
|
* Since generic_permission only needs i_mode, i_uid,
|
||||||
|
* i_gid, and i_sb, we can create a fake inode to pass
|
||||||
|
* this information down in.
|
||||||
|
*
|
||||||
|
* The underlying code may attempt to take locks in some
|
||||||
|
* cases for features we're not using, but if that changes,
|
||||||
|
* locks must be dealt with to avoid undefined behavior.
|
||||||
*/
|
*/
|
||||||
err = generic_permission(inode, mask);
|
copy_attrs(&tmp, inode);
|
||||||
|
tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
|
||||||
|
tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
|
||||||
|
tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
|
||||||
|
release_top(SDCARDFS_I(inode));
|
||||||
|
tmp.i_sb = inode->i_sb;
|
||||||
|
if (IS_POSIXACL(inode))
|
||||||
|
printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__);
|
||||||
|
err = generic_permission(&tmp, mask);
|
||||||
/* XXX
|
/* XXX
|
||||||
* Original sdcardfs code calls inode_permission(lower_inode,.. )
|
* Original sdcardfs code calls inode_permission(lower_inode,.. )
|
||||||
* for checking inode permission. But doing such things here seems
|
* for checking inode permission. But doing such things here seems
|
||||||
|
@ -624,30 +682,70 @@ static int sdcardfs_permission(struct inode *inode, int mask)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
|
static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia)
|
||||||
|
{
|
||||||
|
WARN(1, "sdcardfs does not support setattr. User setattr2.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct iattr *ia)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct dentry *lower_dentry;
|
struct dentry *lower_dentry;
|
||||||
|
struct vfsmount *lower_mnt;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct inode *lower_inode;
|
struct inode *lower_inode;
|
||||||
struct path lower_path;
|
struct path lower_path;
|
||||||
struct iattr lower_ia;
|
struct iattr lower_ia;
|
||||||
struct dentry *parent;
|
struct dentry *parent;
|
||||||
|
struct inode tmp;
|
||||||
|
struct inode *top;
|
||||||
|
const struct cred *saved_cred = NULL;
|
||||||
|
|
||||||
inode = d_inode(dentry);
|
inode = d_inode(dentry);
|
||||||
|
top = grab_top(SDCARDFS_I(inode));
|
||||||
|
|
||||||
|
if (!top) {
|
||||||
|
release_top(SDCARDFS_I(inode));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Permission check on sdcardfs inode.
|
||||||
|
* Calling process should have AID_SDCARD_RW permission
|
||||||
|
* Since generic_permission only needs i_mode, i_uid,
|
||||||
|
* i_gid, and i_sb, we can create a fake inode to pass
|
||||||
|
* this information down in.
|
||||||
|
*
|
||||||
|
* The underlying code may attempt to take locks in some
|
||||||
|
* cases for features we're not using, but if that changes,
|
||||||
|
* locks must be dealt with to avoid undefined behavior.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
copy_attrs(&tmp, inode);
|
||||||
|
tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
|
||||||
|
tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
|
||||||
|
tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
|
||||||
|
tmp.i_size = i_size_read(inode);
|
||||||
|
release_top(SDCARDFS_I(inode));
|
||||||
|
tmp.i_sb = inode->i_sb;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if user has permission to change inode. We don't check if
|
* Check if user has permission to change inode. We don't check if
|
||||||
* this user can change the lower inode: that should happen when
|
* this user can change the lower inode: that should happen when
|
||||||
* calling notify_change on the lower inode.
|
* calling notify_change on the lower inode.
|
||||||
*/
|
*/
|
||||||
err = inode_change_ok(inode, ia);
|
/* prepare our own lower struct iattr (with the lower file) */
|
||||||
|
memcpy(&lower_ia, ia, sizeof(lower_ia));
|
||||||
|
/* Allow touch updating timestamps. A previous permission check ensures
|
||||||
|
* we have write access. Changes to mode, owner, and group are ignored*/
|
||||||
|
ia->ia_valid |= ATTR_FORCE;
|
||||||
|
err = inode_change_ok(&tmp, ia);
|
||||||
|
|
||||||
/* no vfs_XXX operations required, cred overriding will be skipped. wj*/
|
|
||||||
if (!err) {
|
if (!err) {
|
||||||
/* check the Android group ID */
|
/* check the Android group ID */
|
||||||
parent = dget_parent(dentry);
|
parent = dget_parent(dentry);
|
||||||
if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) {
|
if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
|
||||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||||
" dentry: %s, task:%s\n",
|
" dentry: %s, task:%s\n",
|
||||||
__func__, dentry->d_name.name, current->comm);
|
__func__, dentry->d_name.name, current->comm);
|
||||||
|
@ -659,12 +757,14 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
|
/* save current_cred and override it */
|
||||||
|
OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode));
|
||||||
|
|
||||||
sdcardfs_get_lower_path(dentry, &lower_path);
|
sdcardfs_get_lower_path(dentry, &lower_path);
|
||||||
lower_dentry = lower_path.dentry;
|
lower_dentry = lower_path.dentry;
|
||||||
|
lower_mnt = lower_path.mnt;
|
||||||
lower_inode = sdcardfs_lower_inode(inode);
|
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)
|
if (ia->ia_valid & ATTR_FILE)
|
||||||
lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file);
|
lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file);
|
||||||
|
|
||||||
|
@ -681,7 +781,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
|
||||||
if (current->mm)
|
if (current->mm)
|
||||||
down_write(¤t->mm->mmap_sem);
|
down_write(¤t->mm->mmap_sem);
|
||||||
if (ia->ia_valid & ATTR_SIZE) {
|
if (ia->ia_valid & ATTR_SIZE) {
|
||||||
err = inode_newsize_ok(inode, ia->ia_size);
|
err = inode_newsize_ok(&tmp, ia->ia_size);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (current->mm)
|
if (current->mm)
|
||||||
up_write(¤t->mm->mmap_sem);
|
up_write(¤t->mm->mmap_sem);
|
||||||
|
@ -704,7 +804,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
|
||||||
* tries to open(), unlink(), then ftruncate() a file.
|
* tries to open(), unlink(), then ftruncate() a file.
|
||||||
*/
|
*/
|
||||||
mutex_lock(&d_inode(lower_dentry)->i_mutex);
|
mutex_lock(&d_inode(lower_dentry)->i_mutex);
|
||||||
err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */
|
err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */
|
||||||
NULL);
|
NULL);
|
||||||
mutex_unlock(&d_inode(lower_dentry)->i_mutex);
|
mutex_unlock(&d_inode(lower_dentry)->i_mutex);
|
||||||
if (current->mm)
|
if (current->mm)
|
||||||
|
@ -723,10 +823,35 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
|
||||||
|
|
||||||
out:
|
out:
|
||||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
|
REVERT_CRED(saved_cred);
|
||||||
out_err:
|
out_err:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat)
|
||||||
|
{
|
||||||
|
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
|
||||||
|
struct inode *top = grab_top(info);
|
||||||
|
if (!top)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
stat->dev = inode->i_sb->s_dev;
|
||||||
|
stat->ino = inode->i_ino;
|
||||||
|
stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
|
||||||
|
stat->nlink = inode->i_nlink;
|
||||||
|
stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
|
||||||
|
stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
|
||||||
|
stat->rdev = inode->i_rdev;
|
||||||
|
stat->size = i_size_read(inode);
|
||||||
|
stat->atime = inode->i_atime;
|
||||||
|
stat->mtime = inode->i_mtime;
|
||||||
|
stat->ctime = inode->i_ctime;
|
||||||
|
stat->blksize = (1 << inode->i_blkbits);
|
||||||
|
stat->blocks = inode->i_blocks;
|
||||||
|
release_top(info);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||||
struct kstat *stat)
|
struct kstat *stat)
|
||||||
{
|
{
|
||||||
|
@ -735,9 +860,10 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||||
struct inode *lower_inode;
|
struct inode *lower_inode;
|
||||||
struct path lower_path;
|
struct path lower_path;
|
||||||
struct dentry *parent;
|
struct dentry *parent;
|
||||||
|
int err;
|
||||||
|
|
||||||
parent = dget_parent(dentry);
|
parent = dget_parent(dentry);
|
||||||
if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) {
|
if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
|
||||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||||
" dentry: %s, task:%s\n",
|
" dentry: %s, task:%s\n",
|
||||||
__func__, dentry->d_name.name, current->comm);
|
__func__, dentry->d_name.name, current->comm);
|
||||||
|
@ -752,19 +878,17 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||||
lower_dentry = lower_path.dentry;
|
lower_dentry = lower_path.dentry;
|
||||||
lower_inode = sdcardfs_lower_inode(inode);
|
lower_inode = sdcardfs_lower_inode(inode);
|
||||||
|
|
||||||
|
|
||||||
sdcardfs_copy_and_fix_attrs(inode, lower_inode);
|
sdcardfs_copy_and_fix_attrs(inode, lower_inode);
|
||||||
fsstack_copy_inode_size(inode, lower_inode);
|
fsstack_copy_inode_size(inode, lower_inode);
|
||||||
|
|
||||||
|
err = sdcardfs_fillattr(mnt, inode, stat);
|
||||||
generic_fillattr(inode, stat);
|
|
||||||
sdcardfs_put_lower_path(dentry, &lower_path);
|
sdcardfs_put_lower_path(dentry, &lower_path);
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct inode_operations sdcardfs_symlink_iops = {
|
const struct inode_operations sdcardfs_symlink_iops = {
|
||||||
.permission = sdcardfs_permission,
|
.permission2 = sdcardfs_permission,
|
||||||
.setattr = sdcardfs_setattr,
|
.setattr2 = sdcardfs_setattr,
|
||||||
/* XXX Following operations are implemented,
|
/* XXX Following operations are implemented,
|
||||||
* but FUSE(sdcard) or FAT does not support them
|
* but FUSE(sdcard) or FAT does not support them
|
||||||
* These methods are *NOT* perfectly tested.
|
* These methods are *NOT* perfectly tested.
|
||||||
|
@ -777,14 +901,14 @@ const struct inode_operations sdcardfs_symlink_iops = {
|
||||||
const struct inode_operations sdcardfs_dir_iops = {
|
const struct inode_operations sdcardfs_dir_iops = {
|
||||||
.create = sdcardfs_create,
|
.create = sdcardfs_create,
|
||||||
.lookup = sdcardfs_lookup,
|
.lookup = sdcardfs_lookup,
|
||||||
#if 0
|
.permission = sdcardfs_permission_wrn,
|
||||||
.permission = sdcardfs_permission,
|
.permission2 = sdcardfs_permission,
|
||||||
#endif
|
|
||||||
.unlink = sdcardfs_unlink,
|
.unlink = sdcardfs_unlink,
|
||||||
.mkdir = sdcardfs_mkdir,
|
.mkdir = sdcardfs_mkdir,
|
||||||
.rmdir = sdcardfs_rmdir,
|
.rmdir = sdcardfs_rmdir,
|
||||||
.rename = sdcardfs_rename,
|
.rename = sdcardfs_rename,
|
||||||
.setattr = sdcardfs_setattr,
|
.setattr = sdcardfs_setattr_wrn,
|
||||||
|
.setattr2 = sdcardfs_setattr,
|
||||||
.getattr = sdcardfs_getattr,
|
.getattr = sdcardfs_getattr,
|
||||||
/* XXX Following operations are implemented,
|
/* XXX Following operations are implemented,
|
||||||
* but FUSE(sdcard) or FAT does not support them
|
* but FUSE(sdcard) or FAT does not support them
|
||||||
|
@ -796,7 +920,9 @@ const struct inode_operations sdcardfs_dir_iops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct inode_operations sdcardfs_main_iops = {
|
const struct inode_operations sdcardfs_main_iops = {
|
||||||
.permission = sdcardfs_permission,
|
.permission = sdcardfs_permission_wrn,
|
||||||
.setattr = sdcardfs_setattr,
|
.permission2 = sdcardfs_permission,
|
||||||
|
.setattr = sdcardfs_setattr_wrn,
|
||||||
|
.setattr2 = sdcardfs_setattr,
|
||||||
.getattr = sdcardfs_getattr,
|
.getattr = sdcardfs_getattr,
|
||||||
};
|
};
|
||||||
|
|
|
@ -179,7 +179,7 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
|
||||||
struct inode *lower_inode;
|
struct inode *lower_inode;
|
||||||
struct super_block *lower_sb;
|
struct super_block *lower_sb;
|
||||||
|
|
||||||
lower_inode = lower_path->dentry->d_inode;
|
lower_inode = d_inode(lower_path->dentry);
|
||||||
lower_sb = sdcardfs_lower_super(sb);
|
lower_sb = sdcardfs_lower_super(sb);
|
||||||
|
|
||||||
/* check that the lower file system didn't cross a mount point */
|
/* check that the lower file system didn't cross a mount point */
|
||||||
|
@ -219,9 +219,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
|
||||||
struct vfsmount *lower_dir_mnt;
|
struct vfsmount *lower_dir_mnt;
|
||||||
struct dentry *lower_dir_dentry = NULL;
|
struct dentry *lower_dir_dentry = NULL;
|
||||||
struct dentry *lower_dentry;
|
struct dentry *lower_dentry;
|
||||||
const char *name;
|
const struct qstr *name;
|
||||||
struct path lower_path;
|
struct path lower_path;
|
||||||
struct qstr this;
|
|
||||||
struct sdcardfs_sb_info *sbi;
|
struct sdcardfs_sb_info *sbi;
|
||||||
|
|
||||||
sbi = SDCARDFS_SB(dentry->d_sb);
|
sbi = SDCARDFS_SB(dentry->d_sb);
|
||||||
|
@ -231,15 +230,39 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
|
||||||
if (IS_ROOT(dentry))
|
if (IS_ROOT(dentry))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
name = dentry->d_name.name;
|
name = &dentry->d_name;
|
||||||
|
|
||||||
/* now start the actual lookup procedure */
|
/* now start the actual lookup procedure */
|
||||||
lower_dir_dentry = lower_parent_path->dentry;
|
lower_dir_dentry = lower_parent_path->dentry;
|
||||||
lower_dir_mnt = lower_parent_path->mnt;
|
lower_dir_mnt = lower_parent_path->mnt;
|
||||||
|
|
||||||
/* Use vfs_path_lookup to check if the dentry exists or not */
|
/* Use vfs_path_lookup to check if the dentry exists or not */
|
||||||
err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0,
|
err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name->name, 0,
|
||||||
&lower_path);
|
&lower_path);
|
||||||
|
/* check for other cases */
|
||||||
|
if (err == -ENOENT) {
|
||||||
|
struct dentry *child;
|
||||||
|
struct dentry *match = NULL;
|
||||||
|
mutex_lock(&d_inode(lower_dir_dentry)->i_mutex);
|
||||||
|
spin_lock(&lower_dir_dentry->d_lock);
|
||||||
|
list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) {
|
||||||
|
if (child && d_inode(child)) {
|
||||||
|
if (qstr_case_eq(&child->d_name, name)) {
|
||||||
|
match = dget(child);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&lower_dir_dentry->d_lock);
|
||||||
|
mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex);
|
||||||
|
if (match) {
|
||||||
|
err = vfs_path_lookup(lower_dir_dentry,
|
||||||
|
lower_dir_mnt,
|
||||||
|
match->d_name.name, 0,
|
||||||
|
&lower_path);
|
||||||
|
dput(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* no error: handle positive dentries */
|
/* no error: handle positive dentries */
|
||||||
if (!err) {
|
if (!err) {
|
||||||
|
@ -283,14 +306,11 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* instatiate a new negative dentry */
|
/* instatiate a new negative dentry */
|
||||||
this.name = name;
|
lower_dentry = d_lookup(lower_dir_dentry, name);
|
||||||
this.len = strlen(name);
|
|
||||||
this.hash = full_name_hash(this.name, this.len);
|
|
||||||
lower_dentry = d_lookup(lower_dir_dentry, &this);
|
|
||||||
if (lower_dentry)
|
if (lower_dentry)
|
||||||
goto setup_lower;
|
goto setup_lower;
|
||||||
|
|
||||||
lower_dentry = d_alloc(lower_dir_dentry, &this);
|
lower_dentry = d_alloc(lower_dir_dentry, name);
|
||||||
if (!lower_dentry) {
|
if (!lower_dentry) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -335,7 +355,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
|
|
||||||
parent = dget_parent(dentry);
|
parent = dget_parent(dentry);
|
||||||
|
|
||||||
if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
|
if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
|
||||||
ret = ERR_PTR(-EACCES);
|
ret = ERR_PTR(-EACCES);
|
||||||
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
|
||||||
" dentry: %s, task:%s\n",
|
" dentry: %s, task:%s\n",
|
||||||
|
@ -344,7 +364,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save current_cred and override it */
|
/* save current_cred and override it */
|
||||||
OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred);
|
OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
|
||||||
|
|
||||||
sdcardfs_get_lower_path(parent, &lower_parent_path);
|
sdcardfs_get_lower_path(parent, &lower_parent_path);
|
||||||
|
|
||||||
|
@ -362,18 +382,17 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
}
|
}
|
||||||
if (ret)
|
if (ret)
|
||||||
dentry = ret;
|
dentry = ret;
|
||||||
if (dentry->d_inode) {
|
if (d_inode(dentry)) {
|
||||||
fsstack_copy_attr_times(dentry->d_inode,
|
fsstack_copy_attr_times(d_inode(dentry),
|
||||||
sdcardfs_lower_inode(dentry->d_inode));
|
sdcardfs_lower_inode(d_inode(dentry)));
|
||||||
/* get drived permission */
|
/* get derived permission */
|
||||||
mutex_lock(&dentry->d_inode->i_mutex);
|
|
||||||
get_derived_permission(parent, dentry);
|
get_derived_permission(parent, dentry);
|
||||||
fix_derived_permission(dentry->d_inode);
|
fixup_tmp_permissions(d_inode(dentry));
|
||||||
mutex_unlock(&dentry->d_inode->i_mutex);
|
fixup_lower_ownership(dentry, dentry->d_name.name);
|
||||||
}
|
}
|
||||||
/* update parent directory's atime */
|
/* update parent directory's atime */
|
||||||
fsstack_copy_attr_atime(parent->d_inode,
|
fsstack_copy_attr_atime(d_inode(parent),
|
||||||
sdcardfs_lower_inode(parent->d_inode));
|
sdcardfs_lower_inode(d_inode(parent)));
|
||||||
|
|
||||||
out:
|
out:
|
||||||
sdcardfs_put_lower_path(parent, &lower_parent_path);
|
sdcardfs_put_lower_path(parent, &lower_parent_path);
|
||||||
|
|
|
@ -28,7 +28,6 @@ enum {
|
||||||
Opt_fsgid,
|
Opt_fsgid,
|
||||||
Opt_gid,
|
Opt_gid,
|
||||||
Opt_debug,
|
Opt_debug,
|
||||||
Opt_lower_fs,
|
|
||||||
Opt_mask,
|
Opt_mask,
|
||||||
Opt_multiuser, // May need?
|
Opt_multiuser, // May need?
|
||||||
Opt_userid,
|
Opt_userid,
|
||||||
|
@ -49,7 +48,8 @@ static const match_table_t sdcardfs_tokens = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static int parse_options(struct super_block *sb, char *options, int silent,
|
static int parse_options(struct super_block *sb, char *options, int silent,
|
||||||
int *debug, struct sdcardfs_mount_options *opts)
|
int *debug, struct sdcardfs_vfsmount_options *vfsopts,
|
||||||
|
struct sdcardfs_mount_options *opts)
|
||||||
{
|
{
|
||||||
char *p;
|
char *p;
|
||||||
substring_t args[MAX_OPT_ARGS];
|
substring_t args[MAX_OPT_ARGS];
|
||||||
|
@ -58,10 +58,10 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
||||||
/* by default, we use AID_MEDIA_RW as uid, gid */
|
/* by default, we use AID_MEDIA_RW as uid, gid */
|
||||||
opts->fs_low_uid = AID_MEDIA_RW;
|
opts->fs_low_uid = AID_MEDIA_RW;
|
||||||
opts->fs_low_gid = AID_MEDIA_RW;
|
opts->fs_low_gid = AID_MEDIA_RW;
|
||||||
opts->mask = 0;
|
vfsopts->mask = 0;
|
||||||
opts->multiuser = false;
|
opts->multiuser = false;
|
||||||
opts->fs_user_id = 0;
|
opts->fs_user_id = 0;
|
||||||
opts->gid = 0;
|
vfsopts->gid = 0;
|
||||||
/* by default, 0MB is reserved */
|
/* by default, 0MB is reserved */
|
||||||
opts->reserved_mb = 0;
|
opts->reserved_mb = 0;
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
||||||
case Opt_gid:
|
case Opt_gid:
|
||||||
if (match_int(&args[0], &option))
|
if (match_int(&args[0], &option))
|
||||||
return 0;
|
return 0;
|
||||||
opts->gid = option;
|
vfsopts->gid = option;
|
||||||
break;
|
break;
|
||||||
case Opt_userid:
|
case Opt_userid:
|
||||||
if (match_int(&args[0], &option))
|
if (match_int(&args[0], &option))
|
||||||
|
@ -104,7 +104,7 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
||||||
case Opt_mask:
|
case Opt_mask:
|
||||||
if (match_int(&args[0], &option))
|
if (match_int(&args[0], &option))
|
||||||
return 0;
|
return 0;
|
||||||
opts->mask = option;
|
vfsopts->mask = option;
|
||||||
break;
|
break;
|
||||||
case Opt_multiuser:
|
case Opt_multiuser:
|
||||||
opts->multiuser = true;
|
opts->multiuser = true;
|
||||||
|
@ -135,6 +135,65 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int parse_options_remount(struct super_block *sb, char *options, int silent,
|
||||||
|
struct sdcardfs_vfsmount_options *vfsopts)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
substring_t args[MAX_OPT_ARGS];
|
||||||
|
int option;
|
||||||
|
int debug;
|
||||||
|
|
||||||
|
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_gid:
|
||||||
|
if (match_int(&args[0], &option))
|
||||||
|
return 0;
|
||||||
|
vfsopts->gid = option;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case Opt_mask:
|
||||||
|
if (match_int(&args[0], &option))
|
||||||
|
return 0;
|
||||||
|
vfsopts->mask = option;
|
||||||
|
break;
|
||||||
|
case Opt_multiuser:
|
||||||
|
case Opt_userid:
|
||||||
|
case Opt_fsuid:
|
||||||
|
case Opt_fsgid:
|
||||||
|
case Opt_reserved_mb:
|
||||||
|
printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p);
|
||||||
|
break;
|
||||||
|
/* unknown option */
|
||||||
|
default:
|
||||||
|
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 - gid:%d\n", vfsopts->gid);
|
||||||
|
printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/*
|
/*
|
||||||
* our custom d_alloc_root work-alike
|
* our custom d_alloc_root work-alike
|
||||||
|
@ -172,14 +231,15 @@ EXPORT_SYMBOL_GPL(sdcardfs_super_list);
|
||||||
* There is no need to lock the sdcardfs_super_info's rwsem as there is no
|
* 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.
|
* 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,
|
static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
|
||||||
void *raw_data, int silent)
|
const char *dev_name, void *raw_data, int silent)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
int debug;
|
int debug;
|
||||||
struct super_block *lower_sb;
|
struct super_block *lower_sb;
|
||||||
struct path lower_path;
|
struct path lower_path;
|
||||||
struct sdcardfs_sb_info *sb_info;
|
struct sdcardfs_sb_info *sb_info;
|
||||||
|
struct sdcardfs_vfsmount_options *mnt_opt = mnt->data;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
|
||||||
printk(KERN_INFO "sdcardfs version 2.0\n");
|
printk(KERN_INFO "sdcardfs version 2.0\n");
|
||||||
|
@ -193,6 +253,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
|
||||||
|
|
||||||
printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name);
|
printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name);
|
||||||
printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data);
|
printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data);
|
||||||
|
printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt);
|
||||||
|
|
||||||
/* parse lower path */
|
/* parse lower path */
|
||||||
err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
|
err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
|
||||||
|
@ -212,7 +273,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
|
||||||
|
|
||||||
sb_info = sb->s_fs_info;
|
sb_info = sb->s_fs_info;
|
||||||
/* parse options */
|
/* parse options */
|
||||||
err = parse_options(sb, raw_data, silent, &debug, &sb_info->options);
|
err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options);
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_ERR "sdcardfs: invalid options\n");
|
printk(KERN_ERR "sdcardfs: invalid options\n");
|
||||||
goto out_freesbi;
|
goto out_freesbi;
|
||||||
|
@ -236,7 +297,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
|
||||||
sb->s_op = &sdcardfs_sops;
|
sb->s_op = &sdcardfs_sops;
|
||||||
|
|
||||||
/* get a new inode and allocate our root dentry */
|
/* get a new inode and allocate our root dentry */
|
||||||
inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0);
|
inode = sdcardfs_iget(sb, d_inode(lower_path.dentry), 0);
|
||||||
if (IS_ERR(inode)) {
|
if (IS_ERR(inode)) {
|
||||||
err = PTR_ERR(inode);
|
err = PTR_ERR(inode);
|
||||||
goto out_sput;
|
goto out_sput;
|
||||||
|
@ -268,16 +329,16 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
|
||||||
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
|
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
|
||||||
mutex_lock(&sdcardfs_super_list_lock);
|
mutex_lock(&sdcardfs_super_list_lock);
|
||||||
if(sb_info->options.multiuser) {
|
if(sb_info->options.multiuser) {
|
||||||
setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false);
|
setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root));
|
||||||
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
|
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
|
||||||
/*err = prepare_dir(sb_info->obbpath_s,
|
/*err = prepare_dir(sb_info->obbpath_s,
|
||||||
sb_info->options.fs_low_uid,
|
sb_info->options.fs_low_uid,
|
||||||
sb_info->options.fs_low_gid, 00755);*/
|
sb_info->options.fs_low_gid, 00755);*/
|
||||||
} else {
|
} else {
|
||||||
setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false);
|
setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root));
|
||||||
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
|
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
|
||||||
}
|
}
|
||||||
fix_derived_permission(sb->s_root->d_inode);
|
fixup_tmp_permissions(d_inode(sb->s_root));
|
||||||
sb_info->sb = sb;
|
sb_info->sb = sb;
|
||||||
list_add(&sb_info->list, &sdcardfs_super_list);
|
list_add(&sb_info->list, &sdcardfs_super_list);
|
||||||
mutex_unlock(&sdcardfs_super_list_lock);
|
mutex_unlock(&sdcardfs_super_list_lock);
|
||||||
|
@ -306,9 +367,9 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A feature which supports mount_nodev() with options */
|
/* A feature which supports mount_nodev() with options */
|
||||||
static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
|
static struct dentry *mount_nodev_with_options(struct vfsmount *mnt,
|
||||||
int flags, const char *dev_name, void *data,
|
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 (*fill_super)(struct vfsmount *, struct super_block *, const char *, void *, int))
|
||||||
|
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
@ -319,7 +380,7 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
|
||||||
|
|
||||||
s->s_flags = flags;
|
s->s_flags = flags;
|
||||||
|
|
||||||
error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0);
|
error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0);
|
||||||
if (error) {
|
if (error) {
|
||||||
deactivate_locked_super(s);
|
deactivate_locked_super(s);
|
||||||
return ERR_PTR(error);
|
return ERR_PTR(error);
|
||||||
|
@ -328,17 +389,29 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
|
||||||
return dget(s->s_root);
|
return dget(s->s_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags,
|
static struct dentry *sdcardfs_mount(struct vfsmount *mnt,
|
||||||
|
struct file_system_type *fs_type, int flags,
|
||||||
const char *dev_name, void *raw_data)
|
const char *dev_name, void *raw_data)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* dev_name is a lower_path_name,
|
* dev_name is a lower_path_name,
|
||||||
* raw_data is a option string.
|
* raw_data is a option string.
|
||||||
*/
|
*/
|
||||||
return mount_nodev_with_options(fs_type, flags, dev_name,
|
return mount_nodev_with_options(mnt, fs_type, flags, dev_name,
|
||||||
raw_data, sdcardfs_read_super);
|
raw_data, sdcardfs_read_super);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags,
|
||||||
|
const char *dev_name, void *raw_data)
|
||||||
|
{
|
||||||
|
WARN(1, "sdcardfs does not support mount. Use mount2.\n");
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *sdcardfs_alloc_mnt_data(void) {
|
||||||
|
return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
|
||||||
|
}
|
||||||
|
|
||||||
void sdcardfs_kill_sb(struct super_block *sb) {
|
void sdcardfs_kill_sb(struct super_block *sb) {
|
||||||
struct sdcardfs_sb_info *sbi;
|
struct sdcardfs_sb_info *sbi;
|
||||||
if (sb->s_magic == SDCARDFS_SUPER_MAGIC) {
|
if (sb->s_magic == SDCARDFS_SUPER_MAGIC) {
|
||||||
|
@ -353,7 +426,9 @@ void sdcardfs_kill_sb(struct super_block *sb) {
|
||||||
static struct file_system_type sdcardfs_fs_type = {
|
static struct file_system_type sdcardfs_fs_type = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.name = SDCARDFS_NAME,
|
.name = SDCARDFS_NAME,
|
||||||
.mount = sdcardfs_mount,
|
.mount = sdcardfs_mount_wrn,
|
||||||
|
.mount2 = sdcardfs_mount,
|
||||||
|
.alloc_mnt_data = sdcardfs_alloc_mnt_data,
|
||||||
.kill_sb = sdcardfs_kill_sb,
|
.kill_sb = sdcardfs_kill_sb,
|
||||||
.fs_flags = 0,
|
.fs_flags = 0,
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,20 +18,32 @@
|
||||||
* General Public License.
|
* General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define MULTIUSER_APP_PER_USER_RANGE 100000
|
#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
|
||||||
|
#define AID_APP_START 10000 /* first app user */
|
||||||
|
#define AID_APP_END 19999 /* last app user */
|
||||||
|
#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
|
||||||
|
#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
|
||||||
|
#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
|
||||||
|
|
||||||
typedef uid_t userid_t;
|
typedef uid_t userid_t;
|
||||||
typedef uid_t appid_t;
|
typedef uid_t appid_t;
|
||||||
|
|
||||||
static inline userid_t multiuser_get_user_id(uid_t uid) {
|
static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) {
|
||||||
return uid / MULTIUSER_APP_PER_USER_RANGE;
|
return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline appid_t multiuser_get_app_id(uid_t uid) {
|
static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
|
||||||
return uid % MULTIUSER_APP_PER_USER_RANGE;
|
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
|
||||||
|
return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
|
static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) {
|
||||||
return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
|
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
|
||||||
|
return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -65,17 +65,26 @@
|
||||||
#define AID_SDCARD_PICS 1033 /* external storage photos access */
|
#define AID_SDCARD_PICS 1033 /* external storage photos access */
|
||||||
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
|
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
|
||||||
#define AID_SDCARD_ALL 1035 /* access all users external storage */
|
#define AID_SDCARD_ALL 1035 /* access all users external storage */
|
||||||
|
#define AID_MEDIA_OBB 1059 /* obb files */
|
||||||
|
|
||||||
|
#define AID_SDCARD_IMAGE 1057
|
||||||
|
|
||||||
#define AID_PACKAGE_INFO 1027
|
#define AID_PACKAGE_INFO 1027
|
||||||
|
|
||||||
#define fix_derived_permission(x) \
|
|
||||||
|
/*
|
||||||
|
* Permissions are handled by our permission function.
|
||||||
|
* We don't want anyone who happens to look at our inode value to prematurely
|
||||||
|
* block access, so store more permissive values. These are probably never
|
||||||
|
* used.
|
||||||
|
*/
|
||||||
|
#define fixup_tmp_permissions(x) \
|
||||||
do { \
|
do { \
|
||||||
(x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \
|
(x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \
|
||||||
(x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x))); \
|
(x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \
|
||||||
(x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\
|
(x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
/* OVERRIDE_CRED() and REVERT_CRED()
|
/* OVERRIDE_CRED() and REVERT_CRED()
|
||||||
* OVERRID_CRED()
|
* OVERRID_CRED()
|
||||||
* backup original task->cred
|
* backup original task->cred
|
||||||
|
@ -85,12 +94,12 @@
|
||||||
* These two macro should be used in pair, and OVERRIDE_CRED() should be
|
* These two macro should be used in pair, and OVERRIDE_CRED() should be
|
||||||
* placed at the beginning of a function, right after variable declaration.
|
* placed at the beginning of a function, right after variable declaration.
|
||||||
*/
|
*/
|
||||||
#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \
|
#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \
|
||||||
saved_cred = override_fsids(sdcardfs_sbi); \
|
saved_cred = override_fsids(sdcardfs_sbi, info); \
|
||||||
if (!saved_cred) { return -ENOMEM; }
|
if (!saved_cred) { return -ENOMEM; }
|
||||||
|
|
||||||
#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \
|
#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \
|
||||||
saved_cred = override_fsids(sdcardfs_sbi); \
|
saved_cred = override_fsids(sdcardfs_sbi, info); \
|
||||||
if (!saved_cred) { return ERR_PTR(-ENOMEM); }
|
if (!saved_cred) { return ERR_PTR(-ENOMEM); }
|
||||||
|
|
||||||
#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
|
#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
|
||||||
|
@ -121,13 +130,18 @@ typedef enum {
|
||||||
PERM_ANDROID_OBB,
|
PERM_ANDROID_OBB,
|
||||||
/* This node is "/Android/media" */
|
/* This node is "/Android/media" */
|
||||||
PERM_ANDROID_MEDIA,
|
PERM_ANDROID_MEDIA,
|
||||||
|
/* This node is "/Android/[data|media|obb]/[package]" */
|
||||||
|
PERM_ANDROID_PACKAGE,
|
||||||
|
/* This node is "/Android/[data|media|obb]/[package]/cache" */
|
||||||
|
PERM_ANDROID_PACKAGE_CACHE,
|
||||||
} perm_t;
|
} perm_t;
|
||||||
|
|
||||||
struct sdcardfs_sb_info;
|
struct sdcardfs_sb_info;
|
||||||
struct sdcardfs_mount_options;
|
struct sdcardfs_mount_options;
|
||||||
|
struct sdcardfs_inode_info;
|
||||||
|
|
||||||
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
|
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
|
||||||
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi);
|
const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info);
|
||||||
/* Do not directly use this function, use REVERT_CRED() instead. */
|
/* Do not directly use this function, use REVERT_CRED() instead. */
|
||||||
void revert_fsids(const struct cred * old_cred);
|
void revert_fsids(const struct cred * old_cred);
|
||||||
|
|
||||||
|
@ -169,6 +183,10 @@ struct sdcardfs_inode_info {
|
||||||
userid_t userid;
|
userid_t userid;
|
||||||
uid_t d_uid;
|
uid_t d_uid;
|
||||||
bool under_android;
|
bool under_android;
|
||||||
|
bool under_cache;
|
||||||
|
bool under_obb;
|
||||||
|
/* top folder for ownership */
|
||||||
|
struct inode *top;
|
||||||
|
|
||||||
struct inode vfs_inode;
|
struct inode vfs_inode;
|
||||||
};
|
};
|
||||||
|
@ -185,12 +203,18 @@ struct sdcardfs_mount_options {
|
||||||
uid_t fs_low_uid;
|
uid_t fs_low_uid;
|
||||||
gid_t fs_low_gid;
|
gid_t fs_low_gid;
|
||||||
userid_t fs_user_id;
|
userid_t fs_user_id;
|
||||||
gid_t gid;
|
|
||||||
mode_t mask;
|
|
||||||
bool multiuser;
|
bool multiuser;
|
||||||
unsigned int reserved_mb;
|
unsigned int reserved_mb;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sdcardfs_vfsmount_options {
|
||||||
|
gid_t gid;
|
||||||
|
mode_t mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int parse_options_remount(struct super_block *sb, char *options, int silent,
|
||||||
|
struct sdcardfs_vfsmount_options *vfsopts);
|
||||||
|
|
||||||
/* sdcardfs super-block data in memory */
|
/* sdcardfs super-block data in memory */
|
||||||
struct sdcardfs_sb_info {
|
struct sdcardfs_sb_info {
|
||||||
struct super_block *sb;
|
struct super_block *sb;
|
||||||
|
@ -321,9 +345,44 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \
|
||||||
SDCARDFS_DENT_FUNC(lower_path)
|
SDCARDFS_DENT_FUNC(lower_path)
|
||||||
SDCARDFS_DENT_FUNC(orig_path)
|
SDCARDFS_DENT_FUNC(orig_path)
|
||||||
|
|
||||||
static inline int get_gid(struct sdcardfs_inode_info *info) {
|
static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo)
|
||||||
struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
|
{
|
||||||
if (sb_info->options.gid == AID_SDCARD_RW) {
|
return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* grab a refererence if we aren't linking to ourself */
|
||||||
|
static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top)
|
||||||
|
{
|
||||||
|
struct inode *old_top = NULL;
|
||||||
|
BUG_ON(IS_ERR_OR_NULL(top));
|
||||||
|
if (info->top && info->top != &info->vfs_inode) {
|
||||||
|
old_top = info->top;
|
||||||
|
}
|
||||||
|
if (top != &info->vfs_inode)
|
||||||
|
igrab(top);
|
||||||
|
info->top = top;
|
||||||
|
iput(old_top);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct inode *grab_top(struct sdcardfs_inode_info *info)
|
||||||
|
{
|
||||||
|
struct inode *top = info->top;
|
||||||
|
if (top) {
|
||||||
|
return igrab(top);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void release_top(struct sdcardfs_inode_info *info)
|
||||||
|
{
|
||||||
|
iput(info->top);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) {
|
||||||
|
struct sdcardfs_vfsmount_options *opts = mnt->data;
|
||||||
|
|
||||||
|
if (opts->gid == AID_SDCARD_RW) {
|
||||||
/* As an optimization, certain trusted system components only run
|
/* As an optimization, certain trusted system components only run
|
||||||
* as owner but operate across all users. Since we're now handing
|
* 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
|
* out the sdcard_rw GID only to trusted apps, we're okay relaxing
|
||||||
|
@ -331,14 +390,15 @@ static inline int get_gid(struct sdcardfs_inode_info *info) {
|
||||||
* assigned to app directories are still multiuser aware. */
|
* assigned to app directories are still multiuser aware. */
|
||||||
return AID_SDCARD_RW;
|
return AID_SDCARD_RW;
|
||||||
} else {
|
} else {
|
||||||
return multiuser_get_uid(info->userid, sb_info->options.gid);
|
return multiuser_get_uid(info->userid, opts->gid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static inline int get_mode(struct sdcardfs_inode_info *info) {
|
static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) {
|
||||||
int owner_mode;
|
int owner_mode;
|
||||||
int filtered_mode;
|
int filtered_mode;
|
||||||
struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
|
struct sdcardfs_vfsmount_options *opts = mnt->data;
|
||||||
int visible_mode = 0775 & ~sb_info->options.mask;
|
int visible_mode = 0775 & ~opts->mask;
|
||||||
|
|
||||||
|
|
||||||
if (info->perm == PERM_PRE_ROOT) {
|
if (info->perm == PERM_PRE_ROOT) {
|
||||||
/* Top of multi-user view should always be visible to ensure
|
/* Top of multi-user view should always be visible to ensure
|
||||||
|
@ -348,7 +408,7 @@ static inline int get_mode(struct sdcardfs_inode_info *info) {
|
||||||
/* Block "other" access to Android directories, since only apps
|
/* Block "other" access to Android directories, since only apps
|
||||||
* belonging to a specific user should be in there; we still
|
* belonging to a specific user should be in there; we still
|
||||||
* leave +x open for the default view. */
|
* leave +x open for the default view. */
|
||||||
if (sb_info->options.gid == AID_SDCARD_RW) {
|
if (opts->gid == AID_SDCARD_RW) {
|
||||||
visible_mode = visible_mode & ~0006;
|
visible_mode = visible_mode & ~0006;
|
||||||
} else {
|
} else {
|
||||||
visible_mode = visible_mode & ~0007;
|
visible_mode = visible_mode & ~0007;
|
||||||
|
@ -396,20 +456,34 @@ extern struct mutex sdcardfs_super_list_lock;
|
||||||
extern struct list_head sdcardfs_super_list;
|
extern struct list_head sdcardfs_super_list;
|
||||||
|
|
||||||
/* for packagelist.c */
|
/* for packagelist.c */
|
||||||
extern appid_t get_appid(void *pkgl_id, const char *app_name);
|
extern appid_t get_appid(const char *app_name);
|
||||||
extern int check_caller_access_to_name(struct inode *parent_node, const char* name);
|
extern appid_t get_ext_gid(const char *app_name);
|
||||||
|
extern appid_t is_excluded(const char *app_name, userid_t userid);
|
||||||
|
extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr* name);
|
||||||
extern int open_flags_to_access_mode(int open_flags);
|
extern int open_flags_to_access_mode(int open_flags);
|
||||||
extern int packagelist_init(void);
|
extern int packagelist_init(void);
|
||||||
extern void packagelist_exit(void);
|
extern void packagelist_exit(void);
|
||||||
|
|
||||||
/* for derived_perm.c */
|
/* for derived_perm.c */
|
||||||
extern void setup_derived_state(struct inode *inode, perm_t perm,
|
#define BY_NAME (1 << 0)
|
||||||
userid_t userid, uid_t uid, bool under_android);
|
#define BY_USERID (1 << 1)
|
||||||
|
struct limit_search {
|
||||||
|
unsigned int flags;
|
||||||
|
const char *name;
|
||||||
|
size_t length;
|
||||||
|
userid_t userid;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
|
||||||
|
uid_t uid, bool under_android, struct inode *top);
|
||||||
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
|
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
|
||||||
extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry);
|
extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name);
|
||||||
extern void get_derive_permissions_recursive(struct dentry *parent);
|
extern void drop_recursive(struct dentry *parent);
|
||||||
|
extern void fixup_top_recursive(struct dentry *parent);
|
||||||
|
extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
|
||||||
|
|
||||||
extern void update_derived_permission_lock(struct dentry *dentry);
|
extern void update_derived_permission_lock(struct dentry *dentry);
|
||||||
|
void fixup_lower_ownership(struct dentry* dentry, const char *name);
|
||||||
extern int need_graft_path(struct dentry *dentry);
|
extern int need_graft_path(struct dentry *dentry);
|
||||||
extern int is_base_obbpath(struct dentry *dentry);
|
extern int is_base_obbpath(struct dentry *dentry);
|
||||||
extern int is_obbpath_invalid(struct dentry *dentry);
|
extern int is_obbpath_invalid(struct dentry *dentry);
|
||||||
|
@ -444,7 +518,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = vfs_mkdir(d_inode(parent.dentry), dent, mode);
|
err = vfs_mkdir2(parent.mnt, d_inode(parent.dentry), dent, mode);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err == -EEXIST)
|
if (err == -EEXIST)
|
||||||
err = 0;
|
err = 0;
|
||||||
|
@ -455,7 +529,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m
|
||||||
attrs.ia_gid = make_kgid(&init_user_ns, gid);
|
attrs.ia_gid = make_kgid(&init_user_ns, gid);
|
||||||
attrs.ia_valid = ATTR_UID | ATTR_GID;
|
attrs.ia_valid = ATTR_UID | ATTR_GID;
|
||||||
mutex_lock(&d_inode(dent)->i_mutex);
|
mutex_lock(&d_inode(dent)->i_mutex);
|
||||||
notify_change(dent, &attrs, NULL);
|
notify_change2(parent.mnt, dent, &attrs, NULL);
|
||||||
mutex_unlock(&d_inode(dent)->i_mutex);
|
mutex_unlock(&d_inode(dent)->i_mutex);
|
||||||
|
|
||||||
out_dput:
|
out_dput:
|
||||||
|
@ -513,12 +587,16 @@ static inline int check_min_free_space(struct dentry *dentry, size_t size, int d
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copies attrs and maintains sdcardfs managed attrs */
|
/*
|
||||||
|
* Copies attrs and maintains sdcardfs managed attrs
|
||||||
|
* Since our permission check handles all special permissions, set those to be open
|
||||||
|
*/
|
||||||
static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src)
|
static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src)
|
||||||
{
|
{
|
||||||
dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest));
|
dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG |
|
||||||
|
S_IROTH | S_IXOTH; /* 0775 */
|
||||||
dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid);
|
dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid);
|
||||||
dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest)));
|
dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW);
|
||||||
dest->i_rdev = src->i_rdev;
|
dest->i_rdev = src->i_rdev;
|
||||||
dest->i_atime = src->i_atime;
|
dest->i_atime = src->i_atime;
|
||||||
dest->i_mtime = src->i_mtime;
|
dest->i_mtime = src->i_mtime;
|
||||||
|
@ -527,4 +605,17 @@ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct
|
||||||
dest->i_flags = src->i_flags;
|
dest->i_flags = src->i_flags;
|
||||||
set_nlink(dest, src->i_nlink);
|
set_nlink(dest, src->i_nlink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool str_case_eq(const char *s1, const char *s2)
|
||||||
|
{
|
||||||
|
return !strcasecmp(s1, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2)
|
||||||
|
{
|
||||||
|
return q1->len == q2->len && str_case_eq(q1->name, q2->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1)
|
||||||
|
|
||||||
#endif /* not _SDCARDFS_H_ */
|
#endif /* not _SDCARDFS_H_ */
|
||||||
|
|
|
@ -108,6 +108,50 @@ static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @mnt: mount point we are remounting
|
||||||
|
* @sb: superblock we are remounting
|
||||||
|
* @flags: numeric mount options
|
||||||
|
* @options: mount options string
|
||||||
|
*/
|
||||||
|
static int sdcardfs_remount_fs2(struct vfsmount *mnt, 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 | MS_REMOUNT)) != 0) {
|
||||||
|
printk(KERN_ERR
|
||||||
|
"sdcardfs: remount flags 0x%x unsupported\n", *flags);
|
||||||
|
err = -EINVAL;
|
||||||
|
}
|
||||||
|
printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt);
|
||||||
|
err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data);
|
||||||
|
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* sdcardfs_clone_mnt_data(void *data) {
|
||||||
|
struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
|
||||||
|
struct sdcardfs_vfsmount_options* old = data;
|
||||||
|
if(!opt) return NULL;
|
||||||
|
opt->gid = old->gid;
|
||||||
|
opt->mask = old->mask;
|
||||||
|
return opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdcardfs_copy_mnt_data(void *data, void *newdata) {
|
||||||
|
struct sdcardfs_vfsmount_options* old = data;
|
||||||
|
struct sdcardfs_vfsmount_options* new = newdata;
|
||||||
|
old->gid = new->gid;
|
||||||
|
old->mask = new->mask;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called by iput() when the inode reference count reached zero
|
* Called by iput() when the inode reference count reached zero
|
||||||
* and the inode is not hashed anywhere. Used to clear anything
|
* and the inode is not hashed anywhere. Used to clear anything
|
||||||
|
@ -126,6 +170,7 @@ static void sdcardfs_evict_inode(struct inode *inode)
|
||||||
*/
|
*/
|
||||||
lower_inode = sdcardfs_lower_inode(inode);
|
lower_inode = sdcardfs_lower_inode(inode);
|
||||||
sdcardfs_set_lower_inode(inode, NULL);
|
sdcardfs_set_lower_inode(inode, NULL);
|
||||||
|
set_top(SDCARDFS_I(inode), inode);
|
||||||
iput(lower_inode);
|
iput(lower_inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,19 +235,24 @@ static void sdcardfs_umount_begin(struct super_block *sb)
|
||||||
lower_sb->s_op->umount_begin(lower_sb);
|
lower_sb->s_op->umount_begin(lower_sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdcardfs_show_options(struct seq_file *m, struct dentry *root)
|
static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root)
|
||||||
{
|
{
|
||||||
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
|
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
|
||||||
struct sdcardfs_mount_options *opts = &sbi->options;
|
struct sdcardfs_mount_options *opts = &sbi->options;
|
||||||
|
struct sdcardfs_vfsmount_options *vfsopts = mnt->data;
|
||||||
|
|
||||||
if (opts->fs_low_uid != 0)
|
if (opts->fs_low_uid != 0)
|
||||||
seq_printf(m, ",uid=%u", opts->fs_low_uid);
|
seq_printf(m, ",fsuid=%u", opts->fs_low_uid);
|
||||||
if (opts->fs_low_gid != 0)
|
if (opts->fs_low_gid != 0)
|
||||||
seq_printf(m, ",gid=%u", opts->fs_low_gid);
|
seq_printf(m, ",fsgid=%u", opts->fs_low_gid);
|
||||||
|
if (vfsopts->gid != 0)
|
||||||
|
seq_printf(m, ",gid=%u", vfsopts->gid);
|
||||||
if (opts->multiuser)
|
if (opts->multiuser)
|
||||||
seq_printf(m, ",multiuser");
|
seq_printf(m, ",multiuser");
|
||||||
|
if (vfsopts->mask)
|
||||||
|
seq_printf(m, ",mask=%u", vfsopts->mask);
|
||||||
|
if (opts->fs_user_id)
|
||||||
|
seq_printf(m, ",userid=%u", opts->fs_user_id);
|
||||||
if (opts->reserved_mb != 0)
|
if (opts->reserved_mb != 0)
|
||||||
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
|
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
|
||||||
|
|
||||||
|
@ -213,9 +263,12 @@ const struct super_operations sdcardfs_sops = {
|
||||||
.put_super = sdcardfs_put_super,
|
.put_super = sdcardfs_put_super,
|
||||||
.statfs = sdcardfs_statfs,
|
.statfs = sdcardfs_statfs,
|
||||||
.remount_fs = sdcardfs_remount_fs,
|
.remount_fs = sdcardfs_remount_fs,
|
||||||
|
.remount_fs2 = sdcardfs_remount_fs2,
|
||||||
|
.clone_mnt_data = sdcardfs_clone_mnt_data,
|
||||||
|
.copy_mnt_data = sdcardfs_copy_mnt_data,
|
||||||
.evict_inode = sdcardfs_evict_inode,
|
.evict_inode = sdcardfs_evict_inode,
|
||||||
.umount_begin = sdcardfs_umount_begin,
|
.umount_begin = sdcardfs_umount_begin,
|
||||||
.show_options = sdcardfs_show_options,
|
.show_options2 = sdcardfs_show_options,
|
||||||
.alloc_inode = sdcardfs_alloc_inode,
|
.alloc_inode = sdcardfs_alloc_inode,
|
||||||
.destroy_inode = sdcardfs_destroy_inode,
|
.destroy_inode = sdcardfs_destroy_inode,
|
||||||
.drop_inode = generic_delete_inode,
|
.drop_inode = generic_delete_inode,
|
||||||
|
|
26
fs/super.c
26
fs/super.c
|
@ -703,7 +703,8 @@ rescan:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* do_remount_sb - asks filesystem to change mount options.
|
* do_remount_sb2 - asks filesystem to change mount options.
|
||||||
|
* @mnt: mount we are looking at
|
||||||
* @sb: superblock in question
|
* @sb: superblock in question
|
||||||
* @flags: numeric part of options
|
* @flags: numeric part of options
|
||||||
* @data: the rest of options
|
* @data: the rest of options
|
||||||
|
@ -711,7 +712,7 @@ rescan:
|
||||||
*
|
*
|
||||||
* Alters the mount options of a mounted file system.
|
* Alters the mount options of a mounted file system.
|
||||||
*/
|
*/
|
||||||
int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
|
int do_remount_sb2(struct vfsmount *mnt, struct super_block *sb, int flags, void *data, int force)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
int remount_ro;
|
int remount_ro;
|
||||||
|
@ -753,7 +754,16 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sb->s_op->remount_fs) {
|
if (mnt && sb->s_op->remount_fs2) {
|
||||||
|
retval = sb->s_op->remount_fs2(mnt, sb, &flags, data);
|
||||||
|
if (retval) {
|
||||||
|
if (!force)
|
||||||
|
goto cancel_readonly;
|
||||||
|
/* If forced remount, go ahead despite any errors */
|
||||||
|
WARN(1, "forced remount of a %s fs returned %i\n",
|
||||||
|
sb->s_type->name, retval);
|
||||||
|
}
|
||||||
|
} else if (sb->s_op->remount_fs) {
|
||||||
retval = sb->s_op->remount_fs(sb, &flags, data);
|
retval = sb->s_op->remount_fs(sb, &flags, data);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
if (!force)
|
if (!force)
|
||||||
|
@ -785,6 +795,11 @@ cancel_readonly:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
|
||||||
|
{
|
||||||
|
return do_remount_sb2(NULL, sb, flags, data, force);
|
||||||
|
}
|
||||||
|
|
||||||
static void do_emergency_remount(struct work_struct *work)
|
static void do_emergency_remount(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct super_block *sb, *p = NULL;
|
struct super_block *sb, *p = NULL;
|
||||||
|
@ -1104,7 +1119,7 @@ struct dentry *mount_single(struct file_system_type *fs_type,
|
||||||
EXPORT_SYMBOL(mount_single);
|
EXPORT_SYMBOL(mount_single);
|
||||||
|
|
||||||
struct dentry *
|
struct dentry *
|
||||||
mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
|
mount_fs(struct file_system_type *type, int flags, const char *name, struct vfsmount *mnt, void *data)
|
||||||
{
|
{
|
||||||
struct dentry *root;
|
struct dentry *root;
|
||||||
struct super_block *sb;
|
struct super_block *sb;
|
||||||
|
@ -1121,6 +1136,9 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
|
||||||
goto out_free_secdata;
|
goto out_free_secdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type->mount2)
|
||||||
|
root = type->mount2(mnt, type, flags, name, data);
|
||||||
|
else
|
||||||
root = type->mount(type, flags, name, data);
|
root = type->mount(type, flags, name, data);
|
||||||
if (IS_ERR(root)) {
|
if (IS_ERR(root)) {
|
||||||
error = PTR_ERR(root);
|
error = PTR_ERR(root);
|
||||||
|
|
|
@ -91,7 +91,7 @@ static int utimes_common(struct path *path, struct timespec *times)
|
||||||
}
|
}
|
||||||
retry_deleg:
|
retry_deleg:
|
||||||
mutex_lock(&inode->i_mutex);
|
mutex_lock(&inode->i_mutex);
|
||||||
error = notify_change(path->dentry, &newattrs, &delegated_inode);
|
error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
if (delegated_inode) {
|
if (delegated_inode) {
|
||||||
error = break_deleg_wait(&delegated_inode);
|
error = break_deleg_wait(&delegated_inode);
|
||||||
|
|
|
@ -1539,13 +1539,21 @@ extern bool inode_owner_or_capable(const struct inode *inode);
|
||||||
* VFS helper functions..
|
* VFS helper functions..
|
||||||
*/
|
*/
|
||||||
extern int vfs_create(struct inode *, struct dentry *, umode_t, bool);
|
extern int vfs_create(struct inode *, struct dentry *, umode_t, bool);
|
||||||
|
extern int vfs_create2(struct vfsmount *, struct inode *, struct dentry *, umode_t, bool);
|
||||||
extern int vfs_mkdir(struct inode *, struct dentry *, umode_t);
|
extern int vfs_mkdir(struct inode *, struct dentry *, umode_t);
|
||||||
|
extern int vfs_mkdir2(struct vfsmount *, struct inode *, struct dentry *, umode_t);
|
||||||
extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
|
extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
|
||||||
|
extern int vfs_mknod2(struct vfsmount *, struct inode *, struct dentry *, umode_t, dev_t);
|
||||||
extern int vfs_symlink(struct inode *, struct dentry *, const char *);
|
extern int vfs_symlink(struct inode *, struct dentry *, const char *);
|
||||||
|
extern int vfs_symlink2(struct vfsmount *, struct inode *, struct dentry *, const char *);
|
||||||
extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **);
|
extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **);
|
||||||
|
extern int vfs_link2(struct vfsmount *, struct dentry *, struct inode *, struct dentry *, struct inode **);
|
||||||
extern int vfs_rmdir(struct inode *, struct dentry *);
|
extern int vfs_rmdir(struct inode *, struct dentry *);
|
||||||
|
extern int vfs_rmdir2(struct vfsmount *, struct inode *, struct dentry *);
|
||||||
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
|
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
|
||||||
|
extern int vfs_unlink2(struct vfsmount *, struct inode *, struct dentry *, struct inode **);
|
||||||
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
|
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
|
||||||
|
extern int vfs_rename2(struct vfsmount *, struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
|
||||||
extern int vfs_whiteout(struct inode *, struct dentry *);
|
extern int vfs_whiteout(struct inode *, struct dentry *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1671,6 +1679,7 @@ struct inode_operations {
|
||||||
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
|
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
|
||||||
const char * (*follow_link) (struct dentry *, void **);
|
const char * (*follow_link) (struct dentry *, void **);
|
||||||
int (*permission) (struct inode *, int);
|
int (*permission) (struct inode *, int);
|
||||||
|
int (*permission2) (struct vfsmount *, struct inode *, int);
|
||||||
struct posix_acl * (*get_acl)(struct inode *, int);
|
struct posix_acl * (*get_acl)(struct inode *, int);
|
||||||
|
|
||||||
int (*readlink) (struct dentry *, char __user *,int);
|
int (*readlink) (struct dentry *, char __user *,int);
|
||||||
|
@ -1688,6 +1697,7 @@ struct inode_operations {
|
||||||
int (*rename2) (struct inode *, struct dentry *,
|
int (*rename2) (struct inode *, struct dentry *,
|
||||||
struct inode *, struct dentry *, unsigned int);
|
struct inode *, struct dentry *, unsigned int);
|
||||||
int (*setattr) (struct dentry *, struct iattr *);
|
int (*setattr) (struct dentry *, struct iattr *);
|
||||||
|
int (*setattr2) (struct vfsmount *, struct dentry *, struct iattr *);
|
||||||
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
|
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
|
||||||
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
|
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
|
||||||
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
|
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
|
||||||
|
@ -1733,9 +1743,13 @@ struct super_operations {
|
||||||
int (*unfreeze_fs) (struct super_block *);
|
int (*unfreeze_fs) (struct super_block *);
|
||||||
int (*statfs) (struct dentry *, struct kstatfs *);
|
int (*statfs) (struct dentry *, struct kstatfs *);
|
||||||
int (*remount_fs) (struct super_block *, int *, char *);
|
int (*remount_fs) (struct super_block *, int *, char *);
|
||||||
|
int (*remount_fs2) (struct vfsmount *, struct super_block *, int *, char *);
|
||||||
|
void *(*clone_mnt_data) (void *);
|
||||||
|
void (*copy_mnt_data) (void *, void *);
|
||||||
void (*umount_begin) (struct super_block *);
|
void (*umount_begin) (struct super_block *);
|
||||||
|
|
||||||
int (*show_options)(struct seq_file *, struct dentry *);
|
int (*show_options)(struct seq_file *, struct dentry *);
|
||||||
|
int (*show_options2)(struct vfsmount *,struct seq_file *, struct dentry *);
|
||||||
int (*show_devname)(struct seq_file *, struct dentry *);
|
int (*show_devname)(struct seq_file *, struct dentry *);
|
||||||
int (*show_path)(struct seq_file *, struct dentry *);
|
int (*show_path)(struct seq_file *, struct dentry *);
|
||||||
int (*show_stats)(struct seq_file *, struct dentry *);
|
int (*show_stats)(struct seq_file *, struct dentry *);
|
||||||
|
@ -1967,6 +1981,9 @@ struct file_system_type {
|
||||||
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
|
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
|
||||||
struct dentry *(*mount) (struct file_system_type *, int,
|
struct dentry *(*mount) (struct file_system_type *, int,
|
||||||
const char *, void *);
|
const char *, void *);
|
||||||
|
struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int,
|
||||||
|
const char *, void *);
|
||||||
|
void *(*alloc_mnt_data) (void);
|
||||||
void (*kill_sb) (struct super_block *);
|
void (*kill_sb) (struct super_block *);
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
struct file_system_type * next;
|
struct file_system_type * next;
|
||||||
|
@ -2246,6 +2263,8 @@ struct filename {
|
||||||
extern long vfs_truncate(struct path *, loff_t);
|
extern long vfs_truncate(struct path *, loff_t);
|
||||||
extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
|
extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
|
||||||
struct file *filp);
|
struct file *filp);
|
||||||
|
extern int do_truncate2(struct vfsmount *, struct dentry *, loff_t start,
|
||||||
|
unsigned int time_attrs, struct file *filp);
|
||||||
extern int vfs_fallocate(struct file *file, int mode, loff_t offset,
|
extern int vfs_fallocate(struct file *file, int mode, loff_t offset,
|
||||||
loff_t len);
|
loff_t len);
|
||||||
extern long do_sys_open(int dfd, const char __user *filename, int flags,
|
extern long do_sys_open(int dfd, const char __user *filename, int flags,
|
||||||
|
@ -2470,8 +2489,11 @@ extern void emergency_remount(void);
|
||||||
extern sector_t bmap(struct inode *, sector_t);
|
extern sector_t bmap(struct inode *, sector_t);
|
||||||
#endif
|
#endif
|
||||||
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
|
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
|
||||||
|
extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *, struct inode **);
|
||||||
extern int inode_permission(struct inode *, int);
|
extern int inode_permission(struct inode *, int);
|
||||||
|
extern int inode_permission2(struct vfsmount *, struct inode *, int);
|
||||||
extern int __inode_permission(struct inode *, int);
|
extern int __inode_permission(struct inode *, int);
|
||||||
|
extern int __inode_permission2(struct vfsmount *, struct inode *, int);
|
||||||
extern int generic_permission(struct inode *, int);
|
extern int generic_permission(struct inode *, int);
|
||||||
extern int __check_sticky(struct inode *dir, struct inode *inode);
|
extern int __check_sticky(struct inode *dir, struct inode *inode);
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ struct vfsmount {
|
||||||
struct dentry *mnt_root; /* root of the mounted tree */
|
struct dentry *mnt_root; /* root of the mounted tree */
|
||||||
struct super_block *mnt_sb; /* pointer to superblock */
|
struct super_block *mnt_sb; /* pointer to superblock */
|
||||||
int mnt_flags;
|
int mnt_flags;
|
||||||
|
void *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct file; /* forward dec */
|
struct file; /* forward dec */
|
||||||
|
|
|
@ -79,6 +79,7 @@ extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
|
||||||
const char *, unsigned int, struct path *);
|
const char *, unsigned int, struct path *);
|
||||||
|
|
||||||
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
|
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
|
||||||
|
extern struct dentry *lookup_one_len2(const char *, struct vfsmount *mnt, struct dentry *, int);
|
||||||
|
|
||||||
extern int follow_down_one(struct path *);
|
extern int follow_down_one(struct path *);
|
||||||
extern int follow_down(struct path *);
|
extern int follow_down(struct path *);
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
|
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
|
||||||
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
|
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
|
||||||
|
|
||||||
#define SDCARDFS_SUPER_MAGIC 0xb550ca10
|
#define SDCARDFS_SUPER_MAGIC 0x5dca2df5
|
||||||
|
|
||||||
#define SMB_SUPER_MAGIC 0x517B
|
#define SMB_SUPER_MAGIC 0x517B
|
||||||
#define CGROUP_SUPER_MAGIC 0x27e0eb
|
#define CGROUP_SUPER_MAGIC 0x27e0eb
|
||||||
|
|
10
ipc/mqueue.c
10
ipc/mqueue.c
|
@ -747,7 +747,7 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir,
|
||||||
}
|
}
|
||||||
|
|
||||||
mode &= ~current_umask();
|
mode &= ~current_umask();
|
||||||
ret = vfs_create(dir, path->dentry, mode, true);
|
ret = vfs_create2(path->mnt, dir, path->dentry, mode, true);
|
||||||
path->dentry->d_fsdata = NULL;
|
path->dentry->d_fsdata = NULL;
|
||||||
if (ret)
|
if (ret)
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
|
@ -763,7 +763,7 @@ static struct file *do_open(struct path *path, int oflag)
|
||||||
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
|
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
acc = oflag2acc[oflag & O_ACCMODE];
|
acc = oflag2acc[oflag & O_ACCMODE];
|
||||||
if (inode_permission(d_inode(path->dentry), acc))
|
if (inode_permission2(path->mnt, d_inode(path->dentry), acc))
|
||||||
return ERR_PTR(-EACCES);
|
return ERR_PTR(-EACCES);
|
||||||
return dentry_open(path, oflag, current_cred());
|
return dentry_open(path, oflag, current_cred());
|
||||||
}
|
}
|
||||||
|
@ -796,7 +796,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
|
||||||
ro = mnt_want_write(mnt); /* we'll drop it in any case */
|
ro = mnt_want_write(mnt); /* we'll drop it in any case */
|
||||||
error = 0;
|
error = 0;
|
||||||
mutex_lock(&d_inode(root)->i_mutex);
|
mutex_lock(&d_inode(root)->i_mutex);
|
||||||
path.dentry = lookup_one_len(name->name, root, strlen(name->name));
|
path.dentry = lookup_one_len2(name->name, mnt, root, strlen(name->name));
|
||||||
if (IS_ERR(path.dentry)) {
|
if (IS_ERR(path.dentry)) {
|
||||||
error = PTR_ERR(path.dentry);
|
error = PTR_ERR(path.dentry);
|
||||||
goto out_putfd;
|
goto out_putfd;
|
||||||
|
@ -867,7 +867,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
|
||||||
if (err)
|
if (err)
|
||||||
goto out_name;
|
goto out_name;
|
||||||
mutex_lock_nested(&d_inode(mnt->mnt_root)->i_mutex, I_MUTEX_PARENT);
|
mutex_lock_nested(&d_inode(mnt->mnt_root)->i_mutex, I_MUTEX_PARENT);
|
||||||
dentry = lookup_one_len(name->name, mnt->mnt_root,
|
dentry = lookup_one_len2(name->name, mnt, mnt->mnt_root,
|
||||||
strlen(name->name));
|
strlen(name->name));
|
||||||
if (IS_ERR(dentry)) {
|
if (IS_ERR(dentry)) {
|
||||||
err = PTR_ERR(dentry);
|
err = PTR_ERR(dentry);
|
||||||
|
@ -879,7 +879,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
|
||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
} else {
|
} else {
|
||||||
ihold(inode);
|
ihold(inode);
|
||||||
err = vfs_unlink(d_inode(dentry->d_parent), dentry, NULL);
|
err = vfs_unlink2(mnt, d_inode(dentry->d_parent), dentry, NULL);
|
||||||
}
|
}
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode,
|
||||||
dir = d_inode(parent);
|
dir = d_inode(parent);
|
||||||
|
|
||||||
mutex_lock(&dir->i_mutex);
|
mutex_lock(&dir->i_mutex);
|
||||||
dentry = lookup_one_len(name, parent, strlen(name));
|
dentry = lookup_one_len2(name, mount, parent, strlen(name));
|
||||||
if (IS_ERR(dentry))
|
if (IS_ERR(dentry))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
|
@ -498,6 +498,7 @@ int security_path_chown(struct path *path, kuid_t uid, kgid_t gid)
|
||||||
return 0;
|
return 0;
|
||||||
return call_int_hook(path_chown, 0, path, uid, gid);
|
return call_int_hook(path_chown, 0, path, uid, gid);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(security_path_chown);
|
||||||
|
|
||||||
int security_path_chroot(struct path *path)
|
int security_path_chroot(struct path *path)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue