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

aufs: new inode



As a part of looking-up, construct a virtual inode.
After branch-management (add/del branches), the inode has to be
refreshed to represent a revealed file.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 8d4d76d3
......@@ -220,6 +220,11 @@ int au_xino_init_br(struct super_block *sb, struct au_branch *br, ino_t hino,
ino_t au_xino_new_ino(struct super_block *sb);
void au_xino_delete_inode(struct inode *inode, const int unlinked);
void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex,
ino_t h_ino, int idx);
int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
int *idx);
int au_xino_path(struct seq_file *seq, struct file *file);
/* ---------------------------------------------------------------------- */
......
......@@ -101,7 +101,7 @@ void au_dpri_inode(struct inode *inode)
iinfo = au_ii(inode);
dpri("i-1: btop %d, bbot %d, gen %d\n",
iinfo->ii_btop, iinfo->ii_bbot, au_iigen(inode));
iinfo->ii_btop, iinfo->ii_bbot, au_iigen(inode, NULL));
if (iinfo->ii_btop < 0)
return;
hn = 0;
......
......@@ -223,7 +223,7 @@ static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
sigen = au_sigen(sb);
if (unlikely(au_is_bad_inode(inode)
|| IS_DEADDIR(inode)
|| sigen != au_iigen(inode)))
|| sigen != au_iigen(inode, NULL)))
goto out_iput;
dentry = NULL;
......
......@@ -257,6 +257,14 @@ static inline int au_test_fs_notime(struct super_block *sb)
;
}
/* temporary support for i#1 in cramfs */
static inline int au_test_fs_unique_ino(struct inode *inode)
{
if (au_test_cramfs(inode->i_sb))
return inode->i_ino != 1;
return 1;
}
/* ---------------------------------------------------------------------- */
/*
......
......@@ -110,6 +110,10 @@ void au_update_iigen(struct inode *inode, int half)
iigen = &iinfo->ii_generation;
spin_lock(&iigen->ig_spin);
iigen->ig_generation = sigen;
if (half)
au_ig_fset(iigen->ig_flags, HALF_REFRESHED);
else
au_ig_fclr(iigen->ig_flags, HALF_REFRESHED);
spin_unlock(&iigen->ig_spin);
}
......
......@@ -7,6 +7,7 @@
* inode functions
*/
#include <linux/iversion.h>
#include "aufs.h"
struct inode *au_igrab(struct inode *inode)
......@@ -18,6 +19,424 @@ struct inode *au_igrab(struct inode *inode)
return inode;
}
static void au_refresh_hinode_attr(struct inode *inode, int do_version)
{
au_cpup_attr_all(inode, /*force*/0);
au_update_iigen(inode, /*half*/1);
if (do_version)
inode_inc_iversion(inode);
}
static int au_ii_refresh(struct inode *inode, int *update)
{
int err, e, nbr;
umode_t type;
aufs_bindex_t bindex, new_bindex;
struct super_block *sb;
struct au_iinfo *iinfo;
struct au_hinode *p, *q, tmp;
AuDebugOn(au_is_bad_inode(inode));
IiMustWriteLock(inode);
*update = 0;
sb = inode->i_sb;
nbr = au_sbbot(sb) + 1;
type = inode->i_mode & S_IFMT;
iinfo = au_ii(inode);
err = au_hinode_realloc(iinfo, nbr, /*may_shrink*/0);
if (unlikely(err))
goto out;
AuDebugOn(iinfo->ii_btop < 0);
p = au_hinode(iinfo, iinfo->ii_btop);
for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot;
bindex++, p++) {
if (!p->hi_inode)
continue;
AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT));
new_bindex = au_br_index(sb, p->hi_id);
if (new_bindex == bindex)
continue;
if (new_bindex < 0) {
*update = 1;
au_hiput(p);
p->hi_inode = NULL;
continue;
}
if (new_bindex < iinfo->ii_btop)
iinfo->ii_btop = new_bindex;
if (iinfo->ii_bbot < new_bindex)
iinfo->ii_bbot = new_bindex;
/* swap two lower inode, and loop again */
q = au_hinode(iinfo, new_bindex);
tmp = *q;
*q = *p;
*p = tmp;
if (tmp.hi_inode) {
bindex--;
p--;
}
}
au_update_ibrange(inode, /*do_put_zero*/0);
au_hinode_realloc(iinfo, nbr, /*may_shrink*/1); /* harmless if err */
e = au_dy_irefresh(inode);
if (unlikely(e && !err))
err = e;
out:
AuTraceErr(err);
return err;
}
int au_refresh_hinode_self(struct inode *inode)
{
int err, update;
err = au_ii_refresh(inode, &update);
if (!err)
au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode));
AuTraceErr(err);
return err;
}
int au_refresh_hinode(struct inode *inode, struct dentry *dentry)
{
int err, e, update;
unsigned int flags;
umode_t mode;
aufs_bindex_t bindex, bbot;
unsigned char isdir;
struct au_hinode *p;
struct au_iinfo *iinfo;
err = au_ii_refresh(inode, &update);
if (unlikely(err))
goto out;
update = 0;
iinfo = au_ii(inode);
p = au_hinode(iinfo, iinfo->ii_btop);
mode = (inode->i_mode & S_IFMT);
isdir = S_ISDIR(mode);
flags = au_hi_flags(inode, isdir);
bbot = au_dbbot(dentry);
for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) {
struct inode *h_i, *h_inode;
struct dentry *h_d;
h_d = au_h_dptr(dentry, bindex);
if (!h_d || d_is_negative(h_d))
continue;
h_inode = d_inode(h_d);
AuDebugOn(mode != (h_inode->i_mode & S_IFMT));
if (iinfo->ii_btop <= bindex && bindex <= iinfo->ii_bbot) {
h_i = au_h_iptr(inode, bindex);
if (h_i) {
if (h_i == h_inode)
continue;
err = -EIO;
break;
}
}
if (bindex < iinfo->ii_btop)
iinfo->ii_btop = bindex;
if (iinfo->ii_bbot < bindex)
iinfo->ii_bbot = bindex;
au_set_h_iptr(inode, bindex, au_igrab(h_inode), flags);
update = 1;
}
au_update_ibrange(inode, /*do_put_zero*/0);
e = au_dy_irefresh(inode);
if (unlikely(e && !err))
err = e;
if (!err)
au_refresh_hinode_attr(inode, update && isdir);
out:
AuTraceErr(err);
return err;
}
static int set_inode(struct inode *inode, struct dentry *dentry)
{
int err;
unsigned int flags;
umode_t mode;
aufs_bindex_t bindex, btop, btail;
unsigned char isdir;
struct dentry *h_dentry;
struct inode *h_inode;
struct au_iinfo *iinfo;
IiMustWriteLock(inode);
err = 0;
isdir = 0;
btop = au_dbtop(dentry);
h_dentry = au_h_dptr(dentry, btop);
h_inode = d_inode(h_dentry);
mode = h_inode->i_mode;
switch (mode & S_IFMT) {
case S_IFREG:
btail = au_dbtail(dentry);
#if 0 /* re-commit later */
inode->i_op = aufs_iop + AuIop_OTHER;
inode->i_fop = &aufs_file_fop;
#endif
err = au_dy_iaop(inode, btop, h_inode);
if (unlikely(err))
goto out;
break;
case S_IFDIR:
isdir = 1;
btail = au_dbtaildir(dentry);
/* inode->i_op = aufs_iop + AuIop_DIR; re-commit later */
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 */
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 */
init_special_inode(inode, mode, h_inode->i_rdev);
break;
default:
AuIOErr("Unknown file type 0%o\n", mode);
err = -EIO;
goto out;
}
/* do not set hnotify for whiteouted dirs (SHWH mode) */
flags = au_hi_flags(inode, isdir);
iinfo = au_ii(inode);
iinfo->ii_btop = btop;
iinfo->ii_bbot = btail;
for (bindex = btop; bindex <= btail; bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (h_dentry)
au_set_h_iptr(inode, bindex,
au_igrab(d_inode(h_dentry)), flags);
}
au_cpup_attr_all(inode, /*force*/1);
out:
return err;
}
/*
* successful returns with iinfo write_locked
* minus: errno
* zero: success, matched
* plus: no error, but unmatched
*/
static int reval_inode(struct inode *inode, struct dentry *dentry)
{
int err;
unsigned int gen, igflags;
aufs_bindex_t bindex, bbot;
struct inode *h_inode, *h_dinode;
struct dentry *h_dentry;
/*
* before this function, if aufs got any iinfo lock, it must be only
* one, the parent dir.
* it can happen by UDBA and the obsoleted inode number.
*/
err = -EIO;
if (unlikely(inode->i_ino == parent_ino(dentry)))
goto out;
err = 1;
ii_write_lock_new_child(inode);
h_dentry = au_h_dptr(dentry, au_dbtop(dentry));
h_dinode = d_inode(h_dentry);
bbot = au_ibbot(inode);
for (bindex = au_ibtop(inode); bindex <= bbot; bindex++) {
h_inode = au_h_iptr(inode, bindex);
if (!h_inode || h_inode != h_dinode)
continue;
err = 0;
gen = au_iigen(inode, &igflags);
if (gen == au_digen(dentry)
&& !au_ig_ftest(igflags, HALF_REFRESHED))
break;
/* fully refresh inode using dentry */
err = au_refresh_hinode(inode, dentry);
if (!err)
au_update_iigen(inode, /*half*/0);
break;
}
if (unlikely(err))
ii_write_unlock(inode);
out:
return err;
}
int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
unsigned int d_type, ino_t *ino)
{
int err, idx;
const int isnondir = d_type != DT_DIR;
/* prevent hardlinked inode number from race condition */
if (isnondir) {
err = au_xinondir_enter(sb, bindex, h_ino, &idx);
if (unlikely(err))
goto out;
}
err = au_xino_read(sb, bindex, h_ino, ino);
if (unlikely(err))
goto out_xinondir;
if (!*ino) {
err = -EIO;
*ino = au_xino_new_ino(sb);
if (unlikely(!*ino))
goto out_xinondir;
err = au_xino_write(sb, bindex, h_ino, *ino);
if (unlikely(err))
goto out_xinondir;
}
out_xinondir:
if (isnondir && idx >= 0)
au_xinondir_leave(sb, bindex, h_ino, idx);
out:
return err;
}
/* successful returns with iinfo write_locked */
/* todo: return with unlocked? */
struct inode *au_new_inode(struct dentry *dentry, int must_new)
{
struct inode *inode, *h_inode;
struct dentry *h_dentry;
struct super_block *sb;
ino_t h_ino, ino;
int err, idx, hlinked;
aufs_bindex_t btop;
sb = dentry->d_sb;
btop = au_dbtop(dentry);
h_dentry = au_h_dptr(dentry, btop);
h_inode = d_inode(h_dentry);
h_ino = h_inode->i_ino;
hlinked = !d_is_dir(h_dentry) && h_inode->i_nlink > 1;
new_ino:
/*
* stop 'race'-ing between hardlinks under different
* parents.
*/
if (hlinked) {
err = au_xinondir_enter(sb, btop, h_ino, &idx);
inode = ERR_PTR(err);
if (unlikely(err))
goto out;
}
err = au_xino_read(sb, btop, h_ino, &ino);
inode = ERR_PTR(err);
if (unlikely(err))
goto out_xinondir;
if (!ino) {
ino = au_xino_new_ino(sb);
if (unlikely(!ino)) {
inode = ERR_PTR(-EIO);
goto out_xinondir;
}
}
AuDbg("i%lu\n", (unsigned long)ino);
inode = au_iget_locked(sb, ino);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out_xinondir;
AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
if (inode->i_state & I_NEW) {
ii_write_lock_new_child(inode);
err = set_inode(inode, dentry);
if (!err) {
unlock_new_inode(inode);
goto out_xinondir; /* success */
}
/*
* iget_failed() calls iput(), but we need to call
* ii_write_unlock() after iget_failed(). so dirty hack for
* i_count.
*/
atomic_inc(&inode->i_count);
iget_failed(inode);
ii_write_unlock(inode);
au_xino_write(sb, btop, h_ino, /*ino*/0);
/* ignore this error */
goto out_iput;
} else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) {
/*
* horrible race condition between lookup, readdir and copyup
* (or something).
*/
if (hlinked && idx >= 0)
au_xinondir_leave(sb, btop, h_ino, idx);
err = reval_inode(inode, dentry);
if (unlikely(err < 0)) {
hlinked = 0;
goto out_iput;
}
if (!err)
goto out; /* success */
else if (hlinked && idx >= 0) {
err = au_xinondir_enter(sb, btop, h_ino, &idx);
if (unlikely(err)) {
iput(inode);
inode = ERR_PTR(err);
goto out;
}
}
}
if (unlikely(au_test_fs_unique_ino(h_inode)))
AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir,"
" b%d, %s, %pd, hi%lu, i%lu.\n",
btop, au_sbtype(h_dentry->d_sb), dentry,
(unsigned long)h_ino, (unsigned long)ino);
ino = 0;
err = au_xino_write(sb, btop, h_ino, /*ino*/0);
if (!err) {
iput(inode);
if (hlinked && idx >= 0)
au_xinondir_leave(sb, btop, h_ino, idx);
goto new_ino;
}
out_iput:
iput(inode);
inode = ERR_PTR(err);
out_xinondir:
if (hlinked && idx >= 0)
au_xinondir_leave(sb, btop, h_ino, idx);
out:
return inode;
}
/* ---------------------------------------------------------------------- */
int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
......
......@@ -39,9 +39,17 @@ struct au_hinode {
struct dentry *hi_whdentry;
};
/* ig_flags */
#define AuIG_HALF_REFRESHED 1
#define au_ig_ftest(flags, name) ((flags) & AuIG_##name)
#define au_ig_fset(flags, name) \
do { (flags) |= AuIG_##name; } while (0)
#define au_ig_fclr(flags, name) \
do { (flags) &= ~AuIG_##name; } while (0)
struct au_iigen {
spinlock_t ig_spin;
__u32 ig_generation;
__u32 ig_generation, ig_flags;
};
struct au_iinfo {
......@@ -106,6 +114,11 @@ static inline struct au_iinfo *au_ii(struct inode *inode)
/* inode.c */
struct inode *au_igrab(struct inode *inode);
int au_refresh_hinode_self(struct inode *inode);
int au_refresh_hinode(struct inode *inode, struct dentry *dentry);
int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
unsigned int d_type, ino_t *ino);
struct inode *au_new_inode(struct dentry *dentry, int must_new);
int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
struct inode *inode);
int au_test_h_perm(struct inode *h_inode, int mask);
......@@ -252,7 +265,7 @@ static inline void au_icntnr_init(struct au_icntnr *c)
#endif
}
static inline unsigned int au_iigen(struct inode *inode)
static inline unsigned int au_iigen(struct inode *inode, unsigned int *igflags)
{
unsigned int gen;
struct au_iinfo *iinfo;
......@@ -261,6 +274,8 @@ static inline unsigned int au_iigen(struct inode *inode)
iinfo = au_ii(inode);
iigen = &iinfo->ii_generation;
spin_lock(&iigen->ig_spin);
if (igflags)
*igflags = iigen->ig_flags;
gen = iigen->ig_generation;
spin_unlock(&iigen->ig_spin);
......@@ -296,7 +311,7 @@ static inline int au_iigen_test(struct inode *inode, unsigned int sigen)
int err;
err = 0;
if (unlikely(inode && au_iigen(inode) != sigen))
if (unlikely(inode && au_iigen(inode, NULL) != sigen))
err = -EIO;
return err;
......
......@@ -1806,6 +1806,103 @@ void au_xino_delete_inode(struct inode *inode, const int unlinked)
/* ---------------------------------------------------------------------- */
static int au_xinondir_find(struct au_xino *xi, ino_t h_ino)
{
int found, total, i;
found = -1;
total = xi->xi_nondir.total;
for (i = 0; i < total; i++) {
if (xi->xi_nondir.array[i] != h_ino)
continue;
found = i;
break;
}
return found;
}
static int au_xinondir_expand(struct au_xino *xi)
{
int err, sz;
ino_t *p;
BUILD_BUG_ON(KMALLOC_MAX_SIZE > INT_MAX);
err = -ENOMEM;
sz = xi->xi_nondir.total * sizeof(ino_t);
if (unlikely(sz > KMALLOC_MAX_SIZE / 2))
goto out;
p = au_kzrealloc(xi->xi_nondir.array, sz, sz << 1, GFP_ATOMIC,
/*may_shrink*/0);
if (p) {
xi->xi_nondir.array = p;
xi->xi_nondir.total <<= 1;
AuDbg("xi_nondir.total %d\n", xi->xi_nondir.total);
err = 0;
}
out:
return err;
}
void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex,
ino_t h_ino, int idx)
{
struct au_xino *xi;
AuDebugOn(!au_opt_test(au_mntflags(sb), XINO));
xi = au_sbr(sb, bindex)->br_xino;
AuDebugOn(idx < 0 || xi->xi_nondir.total <= idx);
spin_lock(&xi->xi_nondir.spin);
AuDebugOn(xi->xi_nondir.array[idx] != h_ino);
xi->xi_nondir.array[idx] = 0;
spin_unlock(&xi->xi_nondir.spin);
wake_up_all(&xi->xi_nondir.wqh);
}
int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
int *idx)
{
int err, found, empty;
struct au_xino *xi;
err = 0;
*idx = -1;
if (!au_opt_test(au_mntflags(sb), XINO))
goto out; /* no xino */
xi = au_sbr(sb, bindex)->br_xino;
again:
spin_lock(&xi->xi_nondir.spin);
found = au_xinondir_find(xi, h_ino);
if (found == -1) {
empty = au_xinondir_find(xi, /*h_ino*/0);
if (empty == -1) {
empty = xi->xi_nondir.total;
err = au_xinondir_expand(xi);
if (unlikely(err))
goto out_unlock;
}
xi->xi_nondir.array[empty] = h_ino;
*idx = empty;
} else {
spin_unlock(&xi->xi_nondir.spin);
wait_event(xi->xi_nondir.wqh,
xi->xi_nondir.array[found] != h_ino);
goto again;
}
out_unlock:
spin_unlock(&xi->xi_nondir.spin);
out:
return err;
}
/* ---------------------------------------------------------------------- */
int au_xino_path(struct seq_file *seq, struct file *file)
{
int err;
......
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