Commit ed4978f6 authored by J. R. Okajima's avatar J. R. Okajima
Browse files

aufs: inode op, lookup



Implement dir.i_op->lookup() and ->permission().
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent ab70d2ad
......@@ -13,6 +13,7 @@
#ifdef __KERNEL__
#include <linux/dcache.h>
#include <linux/fs.h>
struct au_dpage {
int ndentry;
......@@ -40,6 +41,52 @@ int au_test_subdir(struct dentry *d1, struct dentry *d2);
/* ---------------------------------------------------------------------- */
/*
* todo: in linux-3.13, several similar (but faster) helpers are added to
* include/linux/dcache.h. Try them (in the future).
*/
static inline int au_d_hashed_positive(struct dentry *d)
{
int err;
struct inode *inode = d_inode(d);
err = 0;
if (unlikely(d_unhashed(d)
|| d_is_negative(d)
|| !inode->i_nlink))
err = -ENOENT;
return err;
}
static inline int au_d_alive(struct dentry *d)
{
int err;
struct inode *inode;
err = 0;
if (!IS_ROOT(d))
err = au_d_hashed_positive(d);
else {
inode = d_inode(d);
if (unlikely(d_unlinked(d)
|| d_is_negative(d)
|| !inode->i_nlink))
err = -ENOENT;
}
return err;
}
static inline int au_alive_dir(struct dentry *d)
{
int err;
err = au_d_alive(d);
if (unlikely(err || IS_DEADDIR(d_inode(d))))
err = -ENOENT;
return err;
}
static inline int au_qstreq(struct qstr *a, struct qstr *b)
{
return a->len == b->len
......
......@@ -7,8 +7,231 @@
* inode operations (except add/del/rename)
*/
#include <linux/device_cgroup.h>
#include <linux/fs_stack.h>
#include <linux/namei.h>
#include <linux/security.h>
#include "aufs.h"
static int h_permission(struct inode *h_inode, int mask,
struct path *h_path, int brperm)
{
int err;
const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));
err = -EPERM;
if (write_mask && IS_IMMUTABLE(h_inode))
goto out;
err = -EACCES;
if (((mask & MAY_EXEC)
&& S_ISREG(h_inode->i_mode)
&& (path_noexec(h_path)
|| !(h_inode->i_mode & 0111))))
goto out;
/*
* - skip the lower fs test in the case of write to ro branch.
* - nfs dir permission write check is optimized, but a policy for
* link/rename requires a real check.
*/
if ((write_mask && !au_br_writable(brperm))
|| (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode)
&& write_mask && !(mask & MAY_READ))
|| !h_inode->i_op->permission) {
/* AuLabel(generic_permission); */
err = generic_permission(h_inode, mask);
} else {
/* AuLabel(h_inode->permission); */
err = h_inode->i_op->permission(h_inode, mask);
AuTraceErr(err);
}
if (!err)
err = devcgroup_inode_permission(h_inode, mask);
if (!err)
err = security_inode_permission(h_inode, mask);
#if 0
if (!err) {
/* todo: do we need to call ima_path_check()? */
struct path h_path = {
.dentry =
.mnt = h_mnt
};
err = ima_path_check(&h_path,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
IMA_COUNT_LEAVE);
}
#endif
out:
return err;
}
static int aufs_permission(struct inode *inode, int mask)
{
int err;
aufs_bindex_t bindex, bbot;
const unsigned char isdir = !!S_ISDIR(inode->i_mode),
write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));
struct inode *h_inode;
struct super_block *sb;
struct au_branch *br;
/* todo: support rcu-walk? */
if (mask & MAY_NOT_BLOCK)
return -ECHILD;
sb = inode->i_sb;
si_read_lock(sb, AuLock_FLUSH);
ii_read_lock_child(inode);
#if 0
err = au_iigen_test(inode, au_sigen(sb));
if (unlikely(err))
goto out;
#endif
if (!isdir
|| write_mask) {
err = au_busy_or_stale();
h_inode = au_h_iptr(inode, au_ibtop(inode));
if (unlikely(!h_inode
|| (h_inode->i_mode & S_IFMT)
!= (inode->i_mode & S_IFMT)))
goto out;
err = 0;
bindex = au_ibtop(inode);
br = au_sbr(sb, bindex);
err = h_permission(h_inode, mask, &br->br_path, br->br_perm);
if (write_mask
&& !err
&& !special_file(h_inode->i_mode)) {
/* test whether the upper writable branch exists */
err = -EROFS;
for (; bindex >= 0; bindex--)
if (!au_br_rdonly(au_sbr(sb, bindex))) {
err = 0;
break;
}
}
goto out;
}
/* non-write to dir */
err = 0;
bbot = au_ibbot(inode);
for (bindex = au_ibtop(inode); !err && bindex <= bbot; bindex++) {
h_inode = au_h_iptr(inode, bindex);
if (h_inode) {
err = au_busy_or_stale();
if (unlikely(!S_ISDIR(h_inode->i_mode)))
break;
br = au_sbr(sb, bindex);
err = h_permission(h_inode, mask, &br->br_path,
br->br_perm);
}
}
out:
ii_read_unlock(inode);
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags)
{
struct dentry *ret, *parent;
struct inode *inode;
struct super_block *sb;
int err, npositive;
IMustLock(dir);
/* todo: support rcu-walk? */
ret = ERR_PTR(-ECHILD);
if (flags & LOOKUP_RCU)
goto out;
ret = ERR_PTR(-ENAMETOOLONG);
if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
goto out;
sb = dir->i_sb;
err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
ret = ERR_PTR(err);
if (unlikely(err))
goto out;
err = au_di_init(dentry);
ret = ERR_PTR(err);
if (unlikely(err))
goto out_si;
inode = NULL;
npositive = 0; /* suppress a warning */
parent = dentry->d_parent; /* dir inode is locked */
di_read_lock_parent(parent, AuLock_IR);
err = au_alive_dir(parent);
if (!err)
err = au_digen_test(parent, au_sigen(sb));
if (!err) {
/* regardless LOOKUP_CREATE, always ALLOW_NEG */
npositive = au_lkup_dentry(dentry, au_dbtop(parent),
AuLkup_ALLOW_NEG);
err = npositive;
}
di_read_unlock(parent, AuLock_IR);
ret = ERR_PTR(err);
if (unlikely(err < 0))
goto out_unlock;
if (npositive) {
inode = au_new_inode(dentry, /*must_new*/0);
if (IS_ERR(inode)) {
ret = (void *)inode;
inode = NULL;
goto out_unlock;
}
}
if (inode)
atomic_inc(&inode->i_count);
ret = d_splice_alias(inode, dentry);
#if 0
if (unlikely(d_need_lookup(dentry))) {
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_NEED_LOOKUP;
spin_unlock(&dentry->d_lock);
} else
#endif
if (inode) {
if (!IS_ERR(ret)) {
iput(inode);
if (ret && ret != dentry)
ii_write_unlock(inode);
} else {
ii_write_unlock(inode);
iput(inode);
inode = NULL;
}
}
out_unlock:
di_write_unlock(dentry);
out_si:
si_read_unlock(sb);
out:
return ret;
}
/* ---------------------------------------------------------------------- */
void au_pin_hdir_unlock(struct au_pin *p)
{
if (p->hdir)
......@@ -219,3 +442,19 @@ int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
udba, flags);
return au_do_pin(pin);
}
/* ---------------------------------------------------------------------- */
struct inode_operations aufs_iop[] = {
[AuIop_SYMLINK] = {
.permission = aufs_permission
},
[AuIop_DIR] = {
.lookup = aufs_lookup,
.permission = aufs_permission
},
[AuIop_OTHER] = {
.permission = aufs_permission
}
};
......@@ -185,8 +185,8 @@ static int set_inode(struct inode *inode, struct dentry *dentry)
switch (mode & S_IFMT) {
case S_IFREG:
btail = au_dbtail(dentry);
#if 0 /* re-commit later */
inode->i_op = aufs_iop + AuIop_OTHER;
#if 0 /* re-commit later */
inode->i_fop = &aufs_file_fop;
#endif
err = au_dy_iaop(inode, btop, h_inode);
......@@ -196,19 +196,19 @@ static int set_inode(struct inode *inode, struct dentry *dentry)
case S_IFDIR:
isdir = 1;
btail = au_dbtaildir(dentry);
/* inode->i_op = aufs_iop + AuIop_DIR; re-commit later */
inode->i_op = aufs_iop + AuIop_DIR;
inode->i_fop = &simple_dir_operations; /* re-commit later */
break;
case S_IFLNK:
btail = au_dbtail(dentry);
/* inode->i_op = aufs_iop + AuIop_SYMLINK; re-commit later */
inode->i_op = aufs_iop + AuIop_SYMLINK;
break;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
case S_IFSOCK:
btail = au_dbtail(dentry);
/* inode->i_op = aufs_iop + AuIop_OTHER; re-commit later */
inode->i_op = aufs_iop + AuIop_OTHER;
init_special_inode(inode, mode, h_inode->i_rdev);
break;
default:
......
......@@ -127,6 +127,14 @@ int au_test_h_perm(struct inode *h_inode, int mask);
int au_test_h_perm_sio(struct inode *h_inode, int mask);
/* i_op.c */
enum {
AuIop_SYMLINK,
AuIop_DIR,
AuIop_OTHER,
AuIop_Last
};
extern struct inode_operations aufs_iop[AuIop_Last];
struct dentry *au_pinned_h_parent(struct au_pin *pin);
void au_pin_init(struct au_pin *pin, struct dentry *dentry,
aufs_bindex_t bindex, int lsc_di, int lsc_hi,
......
......@@ -739,7 +739,7 @@ static int alloc_root(struct super_block *sb)
if (IS_ERR(inode))
goto out;
inode->i_op = &simple_dir_inode_operations; /* replace later */
inode->i_op = aufs_iop + AuIop_DIR;
inode->i_fop = &simple_dir_operations; /* replace later */
inode->i_mode = S_IFDIR;
set_nlink(inode, 2);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment