diff options
Diffstat (limited to 'ubuntu')
39 files changed, 1441 insertions, 635 deletions
diff --git a/ubuntu/aufs/BOM b/ubuntu/aufs/BOM index efa81f9de29..bdad0cd19c5 100644 --- a/ubuntu/aufs/BOM +++ b/ubuntu/aufs/BOM @@ -1,2 +1,2 @@ URL: http://git.c3sl.ufpr.br/pub/scm/aufs/aufs2-standalone.git -COMMIT: 097bf62d6f49619359d34bf17f242df38562489a +COMMIT: c5021514085a5d96364e096dbd34cadb2251abfd diff --git a/ubuntu/aufs/Makefile b/ubuntu/aufs/Makefile index 3d6b50f84ba..46615fe113e 100644 --- a/ubuntu/aufs/Makefile +++ b/ubuntu/aufs/Makefile @@ -1,3 +1,4 @@ + include ${src}/magic.mk ifeq (${CONFIG_AUFS_FS},m) include ${src}/conf.mk diff --git a/ubuntu/aufs/branch.c b/ubuntu/aufs/branch.c index 4687a35d43f..25c523de95e 100644 --- a/ubuntu/aufs/branch.c +++ b/ubuntu/aufs/branch.c @@ -33,6 +33,8 @@ static void au_br_do_free(struct au_branch *br) struct au_wbr *wbr; struct au_dykey **key; + au_hnotify_fin_br(br); + if (br->br_xino.xi_file) fput(br->br_xino.xi_file); mutex_destroy(&br->br_xino.xi_nondir_mtx); @@ -124,13 +126,17 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, if (unlikely(!add_branch)) goto out; + err = au_hnotify_init_br(add_branch, perm); + if (unlikely(err)) + goto out_br; + add_branch->br_wbr = NULL; if (au_br_writable(perm)) { /* may be freed separately at changing the branch permission */ add_branch->br_wbr = kmalloc(sizeof(*add_branch->br_wbr), GFP_NOFS); if (unlikely(!add_branch->br_wbr)) - goto out_br; + goto out_hnotify; } err = au_sbr_realloc(au_sbi(sb), new_nbranch); @@ -143,6 +149,8 @@ static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, kfree(add_branch->br_wbr); +out_hnotify: + au_hnotify_fin_br(add_branch); out_br: kfree(add_branch); out: @@ -548,11 +556,18 @@ static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, for (j = 0; !err && j < ndentry; j++) { d = dpage->dentries[j]; AuDebugOn(!atomic_read(&d->d_count)); - inode = d->d_inode; - if (au_digen(d) == sigen && au_iigen(inode) == sigen) + if (!au_digen_test(d, sigen)) { di_read_lock_child(d, AuLock_IR); - else { + if (unlikely(au_dbrange_test(d))) { + di_read_unlock(d, AuLock_IR); + continue; + } + } else { di_write_lock_child(d); + if (unlikely(au_dbrange_test(d))) { + di_write_unlock(d); + continue; + } err = au_reval_dpath(d, sigen); if (!err) di_downgrade_lock(d, AuLock_IR); @@ -562,14 +577,18 @@ static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, } } + /* AuDbgDentry(d); */ + inode = d->d_inode; bstart = au_dbstart(d); bend = au_dbend(d); if (bstart <= bindex && bindex <= bend && au_h_dptr(d, bindex) - && (!S_ISDIR(inode->i_mode) || bstart == bend)) { + && ((inode && !S_ISDIR(inode->i_mode)) + || bstart == bend)) { err = -EBUSY; AuVerbose(verbose, "busy %.*s\n", AuDLNPair(d)); + AuDbgDentry(d); } di_read_unlock(d, AuLock_IR); } @@ -606,7 +625,8 @@ static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, ii_read_lock_child(i); else { ii_write_lock_child(i); - err = au_refresh_hinode_self(i, /*do_attr*/1); + err = au_refresh_hinode_self(i); + au_iigen_dec(i); if (!err) ii_downgrade_lock(i); else { @@ -830,7 +850,7 @@ static void au_warn_ima(void) { #ifdef CONFIG_IMA /* since it doesn't support mark_files_ro() */ - AuWarn1("RW -> RO makes IMA to produce wrong message"); + AuWarn1("RW -> RO makes IMA to produce wrong message\n"); #endif } @@ -893,7 +913,6 @@ static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) unsigned long long ull, max; aufs_bindex_t br_id; struct file *file, *hf, **array; - struct dentry *dentry; struct inode *inode; struct au_hfile *hfile; @@ -906,8 +925,6 @@ static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) br_id = au_sbr_id(sb, bindex); for (ull = 0; ull < max; ull++) { file = array[ull]; - dentry = file->f_dentry; - inode = dentry->d_inode; /* AuDbg("%.*s\n", AuDLNPair(file->f_dentry)); */ fi_read_lock(file); @@ -919,6 +936,7 @@ static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) goto out_array; } + inode = file->f_dentry->d_inode; hfile = &au_fi(file)->fi_htop; hf = hfile->hf_file; if (!S_ISREG(inode->i_mode) @@ -951,7 +969,9 @@ static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) hfile = &au_fi(file)->fi_htop; hf = hfile->hf_file; /* fi_read_unlock(file); */ + spin_lock(&hf->f_lock); hf->f_mode &= ~FMODE_WRITE; + spin_unlock(&hf->f_lock); if (!file_check_writeable(hf)) { file_release_write(hf); mnt_drop_write(hf->f_vfsmnt); diff --git a/ubuntu/aufs/branch.h b/ubuntu/aufs/branch.h index ddbfbb81008..a273e60a597 100644 --- a/ubuntu/aufs/branch.h +++ b/ubuntu/aufs/branch.h @@ -81,6 +81,11 @@ struct au_branch { blkcnt_t br_xino_upper; /* watermark in blocks */ atomic_t br_xino_running; +#ifdef CONFIG_AUFS_HFSNOTIFY + struct fsnotify_group *br_hfsn_group; + struct fsnotify_ops br_hfsn_ops; +#endif + #ifdef CONFIG_SYSFS /* an entry under sysfs per mount-point */ char br_name[8]; diff --git a/ubuntu/aufs/cpup.c b/ubuntu/aufs/cpup.c index 962fd507147..3f667dd6acc 100644 --- a/ubuntu/aufs/cpup.c +++ b/ubuntu/aufs/cpup.c @@ -576,8 +576,9 @@ static int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, if (dst_inode) { if (unlikely(!plink)) { err = -EIO; - AuIOErr("i%lu exists on a upper branch " - "but plink is disabled\n", inode->i_ino); + AuIOErr("hi%lu(i%lu) exists on b%d " + "but plink is disabled\n", + dst_inode->i_ino, inode->i_ino, bdst); goto out; } @@ -591,7 +592,8 @@ static int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, if (unlikely(!h_src->d_inode)) { err = -EIO; AuIOErr("i%lu exists on a upper branch " - "but plink is broken\n", inode->i_ino); + "but not pseudo-linked\n", + inode->i_ino); dput(h_src); goto out; } @@ -816,13 +818,15 @@ static int au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, h_d_dst = hdp[0 + bdst].hd_dentry; dinfo->di_bstart = bdst; hdp[0 + bdst].hd_dentry = wh_dentry; - h_d_start = hdp[0 + bstart].hd_dentry; - if (file) + if (file) { + h_d_start = hdp[0 + bstart].hd_dentry; hdp[0 + bstart].hd_dentry = au_hf_top(file)->f_dentry; + } err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME, /*h_parent*/NULL); - if (!err && file) { - err = au_reopen_nondir(file); + if (file) { + if (!err) + err = au_reopen_nondir(file); hdp[0 + bstart].hd_dentry = h_d_start; } hdp[0 + bdst].hd_dentry = h_d_dst; diff --git a/ubuntu/aufs/dcsub.c b/ubuntu/aufs/dcsub.c index d5ab3525791..d9755f77859 100644 --- a/ubuntu/aufs/dcsub.c +++ b/ubuntu/aufs/dcsub.c @@ -98,7 +98,8 @@ static int au_dpages_append(struct au_dcsub_pages *dpages, dpages->ndpage++; } - dpage->dentries[dpage->ndentry++] = dget(dentry); + /* d_count can be zero */ + dpage->dentries[dpage->ndentry++] = dget_locked(dentry); return 0; /* success */ out: @@ -120,8 +121,7 @@ repeat: resume: if (this_parent->d_sb == sb && !IS_ROOT(this_parent) - && atomic_read(&this_parent->d_count) - && this_parent->d_inode + && au_di(this_parent) && (!test || test(this_parent, arg))) { err = au_dpages_append(dpages, this_parent, GFP_ATOMIC); if (unlikely(err)) @@ -133,14 +133,12 @@ resume: struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); next = tmp->next; - if (/*d_unhashed(dentry) || */!dentry->d_inode) - continue; if (!list_empty(&dentry->d_subdirs)) { this_parent = dentry; goto repeat; } if (dentry->d_sb == sb - && atomic_read(&dentry->d_count) + && au_di(dentry) && (!test || test(dentry, arg))) { err = au_dpages_append(dpages, dentry, GFP_ATOMIC); if (unlikely(err)) @@ -185,6 +183,18 @@ out: return err; } +static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg) +{ + return au_di(dentry) && dentry->d_sb == arg; +} + +int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, + struct dentry *dentry, int do_include) +{ + return au_dcsub_pages_rev(dpages, dentry, do_include, + au_dcsub_dpages_aufs, dentry->d_sb); +} + int au_test_subdir(struct dentry *d1, struct dentry *d2) { struct path path[2] = { diff --git a/ubuntu/aufs/dcsub.h b/ubuntu/aufs/dcsub.h index be25a134621..13b4fb2ca84 100644 --- a/ubuntu/aufs/dcsub.h +++ b/ubuntu/aufs/dcsub.h @@ -26,6 +26,7 @@ #ifdef __KERNEL__ #include <linux/dcache.h> +#include <linux/fs.h> #include <linux/types.h> struct dentry; @@ -50,6 +51,8 @@ int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, au_dpages_test test, void *arg); int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, int do_include, au_dpages_test test, void *arg); +int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, + struct dentry *dentry, int do_include); int au_test_subdir(struct dentry *d1, struct dentry *d2); /* ---------------------------------------------------------------------- */ @@ -59,5 +62,39 @@ static inline int au_d_removed(struct dentry *d) return !IS_ROOT(d) && d_unhashed(d); } +static inline int au_d_hashed_positive(struct dentry *d) +{ + int err; + struct inode *inode = d->d_inode; + err = 0; + if (unlikely(d_unhashed(d) || !inode || !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->d_inode; + if (unlikely(au_d_removed(d) || !inode || !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->d_inode))) + err = -ENOENT; + return err; +} + #endif /* __KERNEL__ */ #endif /* __AUFS_DCSUB_H__ */ diff --git a/ubuntu/aufs/debug.c b/ubuntu/aufs/debug.c index ec8dc079edc..fd3ebd8481d 100644 --- a/ubuntu/aufs/debug.c +++ b/ubuntu/aufs/debug.c @@ -335,24 +335,63 @@ void au_dbg_iattr(struct iattr *ia) /* ---------------------------------------------------------------------- */ +void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) +{ + struct inode *h_inode, *inode = dentry->d_inode; + struct dentry *h_dentry; + aufs_bindex_t bindex, bend, bi; + + if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) + return; + + bend = au_dbend(dentry); + bi = au_ibend(inode); + if (bi < bend) + bend = bi; + bindex = au_dbstart(dentry); + bi = au_ibstart(inode); + if (bi > bindex) + bindex = bi; + + for (; bindex <= bend; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + h_inode = au_h_iptr(inode, bindex); + if (unlikely(h_inode != h_dentry->d_inode)) { + int old = au_debug_test(); + if (!old) + au_debug(1); + AuDbg("b%d, %s:%d\n", bindex, func, line); + AuDbgDentry(dentry); + AuDbgInode(inode); + if (!old) + au_debug(0); + BUG(); + } + } +} + void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen) { struct dentry *parent; parent = dget_parent(dentry); - AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode) - || IS_ROOT(dentry) - || au_digen(parent) != sigen); + AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode)); + AuDebugOn(IS_ROOT(dentry)); + AuDebugOn(au_digen_test(parent, sigen)); dput(parent); } void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen) { struct dentry *parent; + struct inode *inode; parent = dget_parent(dentry); - AuDebugOn(S_ISDIR(dentry->d_inode->i_mode) - || au_digen(parent) != sigen); + inode = dentry->d_inode; + AuDebugOn(inode && S_ISDIR(dentry->d_inode->i_mode)); + AuDebugOn(au_digen_test(parent, sigen)); dput(parent); } @@ -365,13 +404,13 @@ void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) err = au_dpages_init(&dpages, GFP_NOFS); AuDebugOn(err); - err = au_dcsub_pages_rev(&dpages, parent, /*do_include*/1, NULL, NULL); + err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1); AuDebugOn(err); for (i = dpages.ndpage - 1; !err && i >= 0; i--) { dpage = dpages.dpages + i; dentries = dpage->dentries; for (j = dpage->ndentry - 1; !err && j >= 0; j--) - AuDebugOn(au_digen(dentries[j]) != sigen); + AuDebugOn(au_digen_test(dentries[j], sigen)); } au_dpages_free(&dpages); } diff --git a/ubuntu/aufs/debug.h b/ubuntu/aufs/debug.h index c6b2626dee7..cfe4413181b 100644 --- a/ubuntu/aufs/debug.h +++ b/ubuntu/aufs/debug.h @@ -132,6 +132,8 @@ void au_dbg_sleep_jiffy(int jiffy); struct iattr; void au_dbg_iattr(struct iattr *ia); +#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__) +void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line); void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen); void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen); void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen); @@ -196,6 +198,7 @@ void au_debug_sbinfo_init(struct au_sbinfo *sbinfo); AuInfo("%s\n", sym); \ } while (0) #else +AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry) AuStubVoid(au_dbg_verify_dir_parent, struct dentry *dentry, unsigned int sigen) AuStubVoid(au_dbg_verify_nondir_parent, struct dentry *dentry, unsigned int sigen) diff --git a/ubuntu/aufs/dentry.c b/ubuntu/aufs/dentry.c index 2743e26f5ee..8daf4a36aad 100644 --- a/ubuntu/aufs/dentry.c +++ b/ubuntu/aufs/dentry.c @@ -267,9 +267,11 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type, } err = npositive; if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) - && au_dbstart(dentry) < 0)) - /* both of real entry and whiteout found */ + && au_dbstart(dentry) < 0)) { err = -EIO; + AuIOErr("both of real entry and whiteout found, %.*s, err %d\n", + AuDLNPair(dentry), err); + } out_parent: dput(parent); @@ -320,8 +322,8 @@ int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex) goto out; if (unlikely(h_dentry->d_inode)) { err = -EIO; - AuIOErr("b%d %.*s should be negative.\n", - bindex, AuDLNPair(h_dentry)); + AuIOErr("%.*s should be negative on b%d.\n", + AuDLNPair(h_dentry), bindex); dput(h_dentry); goto out; } @@ -437,32 +439,30 @@ int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, /* ---------------------------------------------------------------------- */ -static void au_do_refresh_hdentry(struct au_hdentry *p, struct au_dinfo *dinfo, - struct dentry *parent) +static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) { - struct dentry *h_d, *h_dp; - struct au_hdentry tmp, *q; - struct super_block *sb; + int err; aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; + struct au_hdentry tmp, *p, *q; + struct au_dinfo *dinfo; + struct super_block *sb; - AuRwMustWriteLock(&dinfo->di_rwsem); + DiMustWriteLock(dentry); + sb = dentry->d_sb; + dinfo = au_di(dentry); bend = dinfo->di_bend; bwh = dinfo->di_bwh; bdiropq = dinfo->di_bdiropq; + p = dinfo->di_hdentry + dinfo->di_bstart; for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { - h_d = p->hd_dentry; - if (!h_d) + if (!p->hd_dentry) continue; - h_dp = dget_parent(h_d); - if (h_dp == au_h_dptr(parent, bindex)) { - dput(h_dp); + new_bindex = au_br_index(sb, p->hd_id); + if (new_bindex == bindex) continue; - } - new_bindex = au_find_dbindex(parent, h_dp); - dput(h_dp); if (dinfo->di_bwh == bindex) bwh = new_bindex; if (dinfo->di_bdiropq == bindex) @@ -484,7 +484,6 @@ static void au_do_refresh_hdentry(struct au_hdentry *p, struct au_dinfo *dinfo, } } - sb = parent->d_sb; dinfo->di_bwh = -1; if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) dinfo->di_bwh = bwh; @@ -495,6 +494,9 @@ static void au_do_refresh_hdentry(struct au_hdentry *p, struct au_dinfo *dinfo, && au_sbr_whable(sb, bdiropq)) dinfo->di_bdiropq = bdiropq; + err = -EIO; + dinfo->di_bstart = -1; + dinfo->di_bend = -1; bend = au_dbend(parent); p = dinfo->di_hdentry; for (bindex = 0; bindex <= bend; bindex++, p++) @@ -503,61 +505,318 @@ static void au_do_refresh_hdentry(struct au_hdentry *p, struct au_dinfo *dinfo, break; } - p = dinfo->di_hdentry + bend; - for (bindex = bend; bindex >= 0; bindex--, p--) - if (p->hd_dentry) { - dinfo->di_bend = bindex; - break; + if (dinfo->di_bstart >= 0) { + p = dinfo->di_hdentry + bend; + for (bindex = bend; bindex >= 0; bindex--, p--) + if (p->hd_dentry) { + dinfo->di_bend = bindex; + err = 0; + break; + } + } + + return err; +} + +static void au_do_hide(struct dentry *dentry) +{ + struct inode *inode; + + inode = dentry->d_inode; + if (inode) { + if (!S_ISDIR(inode->i_mode)) { + if (inode->i_nlink && !d_unhashed(dentry)) + drop_nlink(inode); + } else { + clear_nlink(inode); + /* stop next lookup */ + inode->i_flags |= S_DEAD; } + smp_mb(); /* necessary? */ + } + d_drop(dentry); +} + +static int au_hide_children(struct dentry *parent) +{ + int err, i, j, ndentry; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry *dentry; + + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_dcsub_pages(&dpages, parent, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + /* in reverse order */ + for (i = dpages.ndpage - 1; i >= 0; i--) { + dpage = dpages.dpages + i; + ndentry = dpage->ndentry; + for (j = ndentry - 1; j >= 0; j--) { + dentry = dpage->dentries[j]; + if (dentry != parent) + au_do_hide(dentry); + } + } + +out_dpages: + au_dpages_free(&dpages); +out: + return err; +} + +static void au_hide(struct dentry *dentry) +{ + int err; + struct inode *inode; + + AuDbgDentry(dentry); + inode = dentry->d_inode; + if (inode && S_ISDIR(inode->i_mode)) { + /* shrink_dcache_parent(dentry); */ + err = au_hide_children(dentry); + if (unlikely(err)) + AuIOErr("%.*s, failed hiding children, ignored %d\n", + AuDLNPair(dentry), err); + } + au_do_hide(dentry); } /* - * returns the number of found lower positive dentries, - * otherwise an error. + * By adding a dirty branch, a cached dentry may be affected in various ways. + * + * a dirty branch is added + * - on the top of layers + * - in the middle of layers + * - to the bottom of layers + * + * on the added branch there exists + * - a whiteout + * - a diropq + * - a same named entry + * + exist + * * negative --> positive + * * positive --> positive + * - type is unchanged + * - type is changed + * + doesn't exist + * * negative --> negative + * * positive --> negative (rejected by au_br_del() for non-dir case) + * - none */ -int au_refresh_hdentry(struct dentry *dentry, mode_t type) +static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, + struct au_dinfo *tmp) { - int npositive, err; + int err; + aufs_bindex_t bindex, bend; + struct { + struct dentry *dentry; + struct inode *inode; + mode_t mode; + } orig_h, tmp_h; + struct au_hdentry *hd; + struct inode *inode, *h_inode; + struct dentry *h_dentry; + + err = 0; + AuDebugOn(dinfo->di_bstart < 0); + orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; + orig_h.inode = orig_h.dentry->d_inode; + orig_h.mode = 0; + if (orig_h.inode) + orig_h.mode = orig_h.inode->i_mode & S_IFMT; + memset(&tmp_h, 0, sizeof(tmp_h)); + if (tmp->di_bstart >= 0) { + tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; + tmp_h.inode = tmp_h.dentry->d_inode; + if (tmp_h.inode) + tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; + } + + inode = dentry->d_inode; + if (!orig_h.inode) { + AuDbg("nagative originally\n"); + if (inode) { + au_hide(dentry); + goto out; + } + AuDebugOn(inode); + AuDebugOn(dinfo->di_bstart != dinfo->di_bend); + AuDebugOn(dinfo->di_bdiropq != -1); + + if (!tmp_h.inode) { + AuDbg("negative --> negative\n"); + /* should have only one negative lower */ + if (tmp->di_bstart >= 0 + && tmp->di_bstart < dinfo->di_bstart) { + AuDebugOn(tmp->di_bstart != tmp->di_bend); + AuDebugOn(dinfo->di_bstart != dinfo->di_bend); + au_set_h_dptr(dentry, dinfo->di_bstart, NULL); + au_di_cp(dinfo, tmp); + hd = tmp->di_hdentry + tmp->di_bstart; + au_set_h_dptr(dentry, tmp->di_bstart, + dget(hd->hd_dentry)); + } + au_dbg_verify_dinode(dentry); + } else { + AuDbg("negative --> positive\n"); + /* + * similar to the behaviour of creating with bypassing + * aufs. + * unhash it in order to force an error in the + * succeeding create operation. + * we should not set S_DEAD here. + */ + d_drop(dentry); + /* au_di_swap(tmp, dinfo); */ + au_dbg_verify_dinode(dentry); + } + } else { + AuDbg("positive originally\n"); + /* inode may be NULL */ + AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); + if (!tmp_h.inode) { + AuDbg("positive --> negative\n"); + /* or bypassing aufs */ + au_hide(dentry); + if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) + dinfo->di_bwh = tmp->di_bwh; + if (inode) + err = au_refresh_hinode_self(inode); + au_dbg_verify_dinode(dentry); + } else if (orig_h.mode == tmp_h.mode) { + AuDbg("positive --> positive, same type\n"); + if (!S_ISDIR(orig_h.mode) + && dinfo->di_bstart > tmp->di_bstart) { + /* + * similar to the behaviour of removing and + * creating. + */ + au_hide(dentry); + if (inode) + err = au_refresh_hinode_self(inode); + au_dbg_verify_dinode(dentry); + } else { + /* fill empty slots */ + if (dinfo->di_bstart > tmp->di_bstart) + dinfo->di_bstart = tmp->di_bstart; + if (dinfo->di_bend < tmp->di_bend) + dinfo->di_bend = tmp->di_bend; + dinfo->di_bwh = tmp->di_bwh; + dinfo->di_bdiropq = tmp->di_bdiropq; + hd = tmp->di_hdentry; + bend = dinfo->di_bend; + for (bindex = tmp->di_bstart; bindex <= bend; + bindex++) { + if (au_h_dptr(dentry, bindex)) + continue; + h_dentry = hd[bindex].hd_dentry; + if (!h_dentry) + continue; + h_inode = h_dentry->d_inode; + AuDebugOn(!h_inode); + AuDebugOn(orig_h.mode + != (h_inode->i_mode + & S_IFMT)); + au_set_h_dptr(dentry, bindex, + dget(h_dentry)); + } + err = au_refresh_hinode(inode, dentry); + au_dbg_verify_dinode(dentry); + } + } else { + AuDbg("positive --> positive, different type\n"); + /* similar to the behaviour of removing and creating */ + au_hide(dentry); + if (inode) + err = au_refresh_hinode_self(inode); + au_dbg_verify_dinode(dentry); + } + } + +out: + return err; +} + +int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) +{ + int err, ebrange; unsigned int sigen; - aufs_bindex_t bstart; - struct au_dinfo *dinfo; + struct au_dinfo *dinfo, *tmp; struct super_block *sb; - struct dentry *parent; + struct inode *inode; DiMustWriteLock(dentry); + AuDebugOn(IS_ROOT(dentry)); + AuDebugOn(!parent->d_inode); sb = dentry->d_sb; - AuDebugOn(IS_ROOT(dentry)); + inode = dentry->d_inode; sigen = au_sigen(sb); - parent = dget_parent(dentry); - AuDebugOn(au_digen(parent) != sigen - || au_iigen(parent->d_inode) != sigen); + err = au_digen_test(parent, sigen); + if (unlikely(err)) + goto out; dinfo = au_di(dentry); err = au_di_realloc(dinfo, au_sbend(sb) + 1); - npositive = err; if (unlikely(err)) goto out; - au_do_refresh_hdentry(dinfo->di_hdentry + dinfo->di_bstart, dinfo, - parent); + ebrange = au_dbrange_test(dentry); + if (!ebrange) + ebrange = au_do_refresh_hdentry(dentry, parent); - npositive = 0; - bstart = au_dbstart(parent); - if (type != S_IFDIR && dinfo->di_bstart == bstart) - goto out_dgen; /* success */ + if (d_unhashed(dentry) || ebrange) { + AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); + if (inode) + err = au_refresh_hinode_self(inode); + au_dbg_verify_dinode(dentry); + if (!err) + goto out_dgen; /* success */ + goto out; + } - npositive = au_lkup_dentry(dentry, bstart, type, /*nd*/NULL); - if (npositive < 0) + /* temporary dinfo */ + AuDbgDentry(dentry); + err = -ENOMEM; + tmp = au_di_alloc(sb, AuLsc_DI_TMP); + if (unlikely(!tmp)) + goto out; + au_di_swap(tmp, dinfo); + /* returns the number of positive dentries */ + /* + * if current working dir is removed, it returns an error. + * but the dentry is legal. + */ + err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0, /*nd*/NULL); + AuDbgDentry(dentry); + au_di_swap(tmp, dinfo); + if (err == -ENOENT) + err = 0; + if (err >= 0) { + /* compare/refresh by dinfo */ + AuDbgDentry(dentry); + err = au_refresh_by_dinfo(dentry, dinfo, tmp); + au_dbg_verify_dinode(dentry); + AuTraceErr(err); + } + au_rw_write_unlock(&tmp->di_rwsem); + au_di_free(tmp); + if (unlikely(err)) goto out; - if (dinfo->di_bwh >= 0 && dinfo->di_bwh <= dinfo->di_bstart) - d_drop(dentry); out_dgen: au_update_digen(dentry); out: - dput(parent); - AuTraceErr(npositive); - return npositive; + if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { + AuIOErr("failed refreshing %.*s, %d\n", + AuDLNPair(dentry), err); + AuDbgDentry(dentry); + } + AuTraceErr(err); + return err; } static noinline_for_stack @@ -706,29 +965,23 @@ static int h_d_revalidate(struct dentry *dentry, struct inode *inode, return err; } +/* todo: consolidate with do_refresh() and au_reval_for_attr() */ static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) { int err; struct dentry *parent; - struct inode *inode; - inode = dentry->d_inode; - if (au_digen(dentry) == sigen && au_iigen(inode) == sigen) + if (!au_digen_test(dentry, sigen)) return 0; parent = dget_parent(dentry); di_read_lock_parent(parent, AuLock_IR); - AuDebugOn(au_digen(parent) != sigen - || au_iigen(parent->d_inode) != sigen); + AuDebugOn(au_digen_test(parent, sigen)); au_dbg_verify_gen(parent, sigen); - - /* returns a number of positive dentries */ - err = au_refresh_hdentry(dentry, inode->i_mode & S_IFMT); - if (err >= 0) - err = au_refresh_hinode(inode, dentry); - + err = au_refresh_dentry(dentry, parent); di_read_unlock(parent, AuLock_IR); dput(parent); + AuTraceErr(err); return err; } @@ -738,36 +991,35 @@ int au_reval_dpath(struct dentry *dentry, unsigned int sigen) struct dentry *d, *parent; struct inode *inode; - if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS)) + if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) return simple_reval_dpath(dentry, sigen); /* slow loop, keep it simple and stupid */ /* cf: au_cpup_dirs() */ err = 0; parent = NULL; - while (au_digen(dentry) != sigen - || au_iigen(dentry->d_inode) != sigen) { + while (au_digen_test(dentry, sigen)) { d = dentry; while (1) { dput(parent); parent = dget_parent(d); - if (au_digen(parent) == sigen - && au_iigen(parent->d_inode) == sigen) + if (!au_digen_test(parent, sigen)) break; d = parent; } inode = d->d_inode; if (d != dentry) - di_write_lock_child(d); + di_write_lock_child2(d); /* someone might update our dentry while we were sleeping */ - if (au_digen(d) != sigen || au_iigen(d->d_inode) != sigen) { + if (au_digen_test(d, sigen)) { + /* + * todo: consolidate with simple_reval_dpath(), + * do_refresh() and au_reval_for_attr(). + */ di_read_lock_parent(parent, AuLock_IR); - /* returns a number of positive dentries */ - err = au_refresh_hdentry(d, inode->i_mode & S_IFMT); - if (err >= 0) - err = au_refresh_hinode(inode, d); + err = au_refresh_dentry(d, parent); di_read_unlock(parent, AuLock_IR); } @@ -792,6 +1044,10 @@ static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) struct super_block *sb; struct inode *inode; + valid = 0; + if (unlikely(!au_di(dentry))) + goto out; + valid = 1; sb = dentry->d_sb; inode = dentry->d_inode; @@ -803,38 +1059,48 @@ static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); if (unlikely(err)) { valid = err; + AuTraceErr(err); goto out; } - sigen = au_sigen(sb); - if (au_digen(dentry) != sigen) { - AuDebugOn(IS_ROOT(dentry)); - if (inode) - err = au_reval_dpath(dentry, sigen); - if (unlikely(err)) - goto out_dgrade; + if (unlikely(au_dbrange_test(dentry))) { + err = -EINVAL; + AuTraceErr(err); + goto out_dgrade; } - if (inode && au_iigen(inode) != sigen) { + + sigen = au_sigen(sb); + if (au_digen_test(dentry, sigen)) { AuDebugOn(IS_ROOT(dentry)); - err = au_refresh_hinode(inode, dentry); - if (unlikely(err)) + err = au_reval_dpath(dentry, sigen); + if (unlikely(err)) { + AuTraceErr(err); goto out_dgrade; + } } di_downgrade_lock(dentry, AuLock_IR); err = -EINVAL; + if (inode && (IS_DEADDIR(inode) || !inode->i_nlink)) + goto out_inval; + do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); if (do_udba && inode) { aufs_bindex_t bstart = au_ibstart(inode); + struct inode *h_inode; - if (bstart >= 0 - && au_test_higen(inode, au_h_iptr(inode, bstart))) - goto out_inval; + if (bstart >= 0) { + h_inode = au_h_iptr(inode, bstart); + if (h_inode && au_test_higen(inode, h_inode)) + goto out_inval; + } } err = h_d_revalidate(dentry, inode, nd, do_udba); - if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) - /* both of real entry and whiteout found */ + if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { err = -EIO; + AuDbg("both of real entry and whiteout found, %.*s, err %d\n", + AuDLNPair(dentry), err); + } goto out_inval; out_dgrade: @@ -844,14 +1110,16 @@ out_inval: AuTraceErr(err); valid = !err; out: - if (!valid) + if (!valid) { AuDbg("%.*s invalid, %d\n", AuDLNPair(dentry), valid); + d_drop(dentry); + } return valid; } static void aufs_d_release(struct dentry *dentry) { - if (dentry->d_fsdata) { + if (au_di(dentry)) { au_di_fin(dentry); au_hn_di_reinit(dentry); } diff --git a/ubuntu/aufs/dentry.h b/ubuntu/aufs/dentry.h index 2f2bb81db56..4865f5e0903 100644 --- a/ubuntu/aufs/dentry.h +++ b/ubuntu/aufs/dentry.h @@ -29,10 +29,9 @@ #include <linux/aufs_type.h> #include "rwsem.h" -/* make a single member structure for future use */ -/* todo: remove this structure */ struct au_hdentry { struct dentry *hd_dentry; + aufs_bindex_t hd_id; }; struct au_dinfo { @@ -58,11 +57,15 @@ int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type, struct nameidata *nd); int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex); -int au_refresh_hdentry(struct dentry *dentry, mode_t type); +int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); int au_reval_dpath(struct dentry *dentry, unsigned int sigen); /* dinfo.c */ void au_di_init_once(void *_di); +struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc); +void au_di_free(struct au_dinfo *dinfo); +void au_di_swap(struct au_dinfo *a, struct au_dinfo *b); +void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src); int au_di_init(struct dentry *dentry); void au_di_fin(struct dentry *dentry); int au_di_realloc(struct au_dinfo *dinfo, int nbr); @@ -82,6 +85,8 @@ aufs_bindex_t au_dbtaildir(struct dentry *dentry); void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_dentry); +int au_digen_test(struct dentry *dentry, unsigned int sigen); +int au_dbrange_test(struct dentry *dentry); void au_update_digen(struct dentry *dentry); void au_update_dbrange(struct dentry *dentry, int do_put_zero); void au_update_dbstart(struct dentry *dentry); @@ -104,7 +109,8 @@ enum { AuLsc_DI_CHILD3, /* copyup dirs */ AuLsc_DI_PARENT, AuLsc_DI_PARENT2, - AuLsc_DI_PARENT3 + AuLsc_DI_PARENT3, + AuLsc_DI_TMP /* temp for replacing dinfo */ }; /* diff --git a/ubuntu/aufs/dinfo.c b/ubuntu/aufs/dinfo.c index 4a638dc92b5..fff8cdd2771 100644 --- a/ubuntu/aufs/dinfo.c +++ b/ubuntu/aufs/dinfo.c @@ -31,60 +31,118 @@ void au_di_init_once(void *_dinfo) au_rw_class(&dinfo->di_rwsem, &aufs_di); } -int au_di_init(struct dentry *dentry) +struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) { struct au_dinfo *dinfo; - struct super_block *sb; - int nbr; + int nbr, i; dinfo = au_cache_alloc_dinfo(); if (unlikely(!dinfo)) goto out; - sb = dentry->d_sb; nbr = au_sbend(sb) + 1; if (nbr <= 0) nbr = 1; dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); - if (unlikely(!dinfo->di_hdentry)) - goto out_dinfo; - - atomic_set(&dinfo->di_generation, au_sigen(sb)); - /* smp_mb(); */ /* atomic_set */ - au_rw_write_lock_nested(&dinfo->di_rwsem, AuLsc_DI_CHILD); - dinfo->di_bstart = -1; - dinfo->di_bend = -1; - dinfo->di_bwh = -1; - dinfo->di_bdiropq = -1; - - dentry->d_fsdata = dinfo; - dentry->d_op = &aufs_dop; - return 0; /* success */ + if (dinfo->di_hdentry) { + au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); + dinfo->di_bstart = -1; + dinfo->di_bend = -1; + dinfo->di_bwh = -1; + dinfo->di_bdiropq = -1; + for (i = 0; i < nbr; i++) + dinfo->di_hdentry[i].hd_id = -1; + goto out; + } -out_dinfo: au_cache_free_dinfo(dinfo); + dinfo = NULL; + out: - return -ENOMEM; + return dinfo; } -void au_di_fin(struct dentry *dentry) +void au_di_free(struct au_dinfo *dinfo) { - struct au_dinfo *di; struct au_hdentry *p; aufs_bindex_t bend, bindex; /* dentry may not be revalidated */ - di = dentry->d_fsdata; - bindex = di->di_bstart; + bindex = dinfo->di_bstart; if (bindex >= 0) { - bend = di->di_bend; - p = di->di_hdentry + bindex; + bend = dinfo->di_bend; + p = dinfo->di_hdentry + bindex; while (bindex++ <= bend) au_hdput(p++); } - kfree(di->di_hdentry); - AuRwDestroy(&di->di_rwsem); - au_cache_free_dinfo(di); + kfree(dinfo->di_hdentry); + au_cache_free_dinfo(dinfo); +} + +void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) +{ + struct au_hdentry *p; + aufs_bindex_t bi; + + AuRwMustWriteLock(&a->di_rwsem); + AuRwMustWriteLock(&b->di_rwsem); + +#define DiSwap(v, name) \ + do { \ + v = a->di_##name; \ + a->di_##name = b->di_##name; \ + b->di_##name = v; \ + } while (0) + + DiSwap(p, hdentry); + DiSwap(bi, bstart); + DiSwap(bi, bend); + DiSwap(bi, bwh); + DiSwap(bi, bdiropq); + /* smp_mb(); */ + +#undef DiSwap +} + +void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src) +{ + AuRwMustWriteLock(&dst->di_rwsem); + AuRwMustWriteLock(&src->di_rwsem); + + dst->di_bstart = src->di_bstart; + dst->di_bend = src->di_bend; + dst->di_bwh = src->di_bwh; + dst->di_bdiropq = src->di_bdiropq; + /* smp_mb(); */ +} + +int au_di_init(struct dentry *dentry) +{ + int err; + struct super_block *sb; + struct au_dinfo *dinfo; + + err = 0; + sb = dentry->d_sb; + dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); + if (dinfo) { + atomic_set(&dinfo->di_generation, au_sigen(sb)); + /* smp_mb(); */ /* atomic_set */ + dentry->d_op = &aufs_dop; + dentry->d_fsdata = dinfo; + } else + err = -ENOMEM; + + return err; +} + +void au_di_fin(struct dentry *dentry) +{ + struct au_dinfo *dinfo; + + dinfo = au_di(dentry); + AuRwDestroy(&dinfo->di_rwsem); + au_di_free(dinfo); } int au_di_realloc(struct au_dinfo *dinfo, int nbr) @@ -175,10 +233,13 @@ void di_read_lock(struct dentry *d, int flags, unsigned int lsc) void di_read_unlock(struct dentry *d, int flags) { if (d->d_inode) { - if (au_ftest_lock(flags, IW)) + if (au_ftest_lock(flags, IW)) { + au_dbg_verify_dinode(d); ii_write_unlock(d->d_inode); - else if (au_ftest_lock(flags, IR)) + } else if (au_ftest_lock(flags, IR)) { + au_dbg_verify_dinode(d); ii_read_unlock(d->d_inode); + } } au_rw_read_unlock(&au_di(d)->di_rwsem); } @@ -199,6 +260,7 @@ void di_write_lock(struct dentry *d, unsigned int lsc) void di_write_unlock(struct dentry *d) { + au_dbg_verify_dinode(d); if (d->d_inode) ii_write_unlock(d->d_inode); au_rw_write_unlock(&au_di(d)->di_rwsem); @@ -295,11 +357,46 @@ void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_dentry) { struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; + struct au_branch *br; DiMustWriteLock(dentry); au_hdput(hd); hd->hd_dentry = h_dentry; + if (h_dentry) { + br = au_sbr(dentry->d_sb, bindex); + hd->hd_id = br->br_id; + } +} + +int au_dbrange_test(struct dentry *dentry) +{ + int err; + aufs_bindex_t bstart, bend; + + err = 0; + bstart = au_dbstart(dentry); + bend = au_dbend(dentry); + if (bstart >= 0) + AuDebugOn(bend < 0 && bstart > bend); + else { + err = -EIO; + AuDebugOn(bend >= 0); + } + + return err; +} + +int au_digen_test(struct dentry *dentry, unsigned int sigen) +{ + int err; + + err = 0; + if (unlikely(au_digen(dentry) != sigen + || au_iigen_test(dentry->d_inode, sigen))) + err = -EIO; + + return err; } void au_update_digen(struct dentry *dentry) diff --git a/ubuntu/aufs/dir.c b/ubuntu/aufs/dir.c index d97ec02e0eb..f2d9750c42f 100644 --- a/ubuntu/aufs/dir.c +++ b/ubuntu/aufs/dir.c @@ -145,8 +145,11 @@ static int do_open_dir(struct file *file, int flags) FiMustWriteLock(file); - err = 0; dentry = file->f_dentry; + err = au_alive_dir(dentry); + if (unlikely(err)) + goto out; + file->f_version = dentry->d_inode->i_version; bindex = au_dbstart(dentry); au_set_fbstart(file, bindex); @@ -176,6 +179,7 @@ static int do_open_dir(struct file *file, int flags) au_set_fbstart(file, -1); au_set_fbend_dir(file, -1); +out: return err; } @@ -388,7 +392,9 @@ static int aufs_readdir(struct file *file, void *dirent, filldir_t filldir) err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); if (unlikely(err)) goto out; - err = au_vdir_init(file); + err = au_alive_dir(dentry); + if (!err) + err = au_vdir_init(file); di_downgrade_lock(dentry, AuLock_IR); if (unlikely(err)) goto out_unlock; diff --git a/ubuntu/aufs/export.c b/ubuntu/aufs/export.c index 05a9a75c65f..b638ba60098 100644 --- a/ubuntu/aufs/export.c +++ b/ubuntu/aufs/export.c @@ -236,7 +236,7 @@ static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, } spin_unlock(&dcache_lock); } - if (unlikely(dentry && sigen != au_digen(dentry))) { + if (unlikely(dentry && au_digen_test(dentry, sigen))) { dput(dentry); dentry = ERR_PTR(-ESTALE); } @@ -292,28 +292,32 @@ static struct vfsmount *au_mnt_get(struct super_block *sb) struct au_nfsd_si_lock { unsigned int sigen; - aufs_bindex_t br_id; + aufs_bindex_t bindex, br_id; unsigned char force_lock; }; -static aufs_bindex_t si_nfsd_read_lock(struct super_block *sb, - struct au_nfsd_si_lock *nsi_lock) +static int si_nfsd_read_lock(struct super_block *sb, + struct au_nfsd_si_lock *nsi_lock) { + int err; aufs_bindex_t bindex; si_read_lock(sb, AuLock_FLUSH); /* branch id may be wrapped around */ + err = 0; bindex = au_br_index(sb, nsi_lock->br_id); if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) goto out; /* success */ + err = -ESTALE; + bindex = -1; if (!nsi_lock->force_lock) si_read_unlock(sb); - bindex = -1; out: - return bindex; + nsi_lock->bindex = bindex; + return err; } struct find_name_by_ino { @@ -470,9 +474,8 @@ out: } static -struct dentry *decode_by_path(struct super_block *sb, aufs_bindex_t bindex, - ino_t ino, __u32 *fh, int fh_len, - struct au_nfsd_si_lock *nsi_lock) +struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, + int fh_len, struct au_nfsd_si_lock *nsi_lock) { struct dentry *dentry, *h_parent, *root; struct super_block *h_sb; @@ -482,8 +485,7 @@ struct dentry *decode_by_path(struct super_block *sb, aufs_bindex_t bindex, int err; struct path path; - br = au_sbr(sb, bindex); - /* au_br_get(br); */ + br = au_sbr(sb, nsi_lock->bindex); h_mnt = br->br_mnt; h_sb = h_mnt->mnt_sb; /* todo: call lower fh_to_dentry()? fh_to_parent()? */ @@ -511,7 +513,7 @@ struct dentry *decode_by_path(struct super_block *sb, aufs_bindex_t bindex, root = sb->s_root; path.mnt = h_mnt; di_read_lock_parent(root, !AuLock_IR); - path.dentry = au_h_dptr(root, bindex); + path.dentry = au_h_dptr(root, nsi_lock->bindex); di_read_unlock(root, !AuLock_IR); p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); dentry = (void *)p; @@ -547,7 +549,6 @@ out_pathname: out_h_parent: dput(h_parent); out: - /* au_br_put(br); */ AuTraceErrPtr(dentry); return dentry; } @@ -560,8 +561,8 @@ aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, { struct dentry *dentry; __u32 *fh = fid->raw; + struct au_branch *br; ino_t ino, dir_ino; - aufs_bindex_t bindex; struct au_nfsd_si_lock nsi_lock = { .force_lock = 0 }; @@ -574,8 +575,8 @@ aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, nsi_lock.br_id = fh[Fh_br_id]; /* branch id may be wrapped around */ - bindex = si_nfsd_read_lock(sb, &nsi_lock); - if (unlikely(bindex < 0)) + br = NULL; + if (unlikely(si_nfsd_read_lock(sb, &nsi_lock))) goto out; nsi_lock.force_lock = 1; @@ -593,6 +594,8 @@ aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, goto accept; /* is the parent dir cached? */ + br = au_sbr(sb, nsi_lock.bindex); + atomic_inc(&br->br_count); dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; @@ -600,7 +603,7 @@ aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, goto accept; /* lookup path */ - dentry = decode_by_path(sb, bindex, ino, fh, fh_len, &nsi_lock); + dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (unlikely(!dentry)) @@ -608,12 +611,15 @@ aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, goto out_unlock; accept: - if (dentry->d_inode->i_generation == fh[Fh_igen]) + if (!au_digen_test(dentry, au_sigen(sb)) + && dentry->d_inode->i_generation == fh[Fh_igen]) goto out_unlock; /* success */ dput(dentry); dentry = ERR_PTR(-ESTALE); out_unlock: + if (br) + atomic_dec(&br->br_count); si_read_unlock(sb); out: AuTraceErrPtr(dentry); @@ -670,19 +676,21 @@ static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, goto out; } - err = -EIO; h_parent = NULL; - sb = dentry->d_sb; - aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR); - parent = dget_parent(dentry); - di_read_lock_parent(parent, !AuLock_IR); + err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR | AuLock_GEN); + if (unlikely(err)) + goto out; + inode = dentry->d_inode; AuDebugOn(!inode); + sb = dentry->d_sb; #ifdef CONFIG_AUFS_DEBUG if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) AuWarn1("NFS-exporting requires xino\n"); #endif - + err = -EIO; + parent = dget_parent(dentry); + di_read_lock_parent(parent, !AuLock_IR); bend = au_dbtaildir(parent); for (bindex = au_dbstart(parent); bindex <= bend; bindex++) { h_parent = au_h_dptr(parent, bindex); diff --git a/ubuntu/aufs/f_op.c b/ubuntu/aufs/f_op.c index 829a67e3c2d..ff0f44bd201 100644 --- a/ubuntu/aufs/f_op.c +++ b/ubuntu/aufs/f_op.c @@ -37,8 +37,11 @@ int au_do_open_nondir(struct file *file, int flags) FiMustWriteLock(file); - err = 0; dentry = file->f_dentry; + err = au_d_alive(dentry); + if (unlikely(err)) + goto out; + finfo = au_fi(file); memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); finfo->fi_hvmop = NULL; @@ -53,6 +56,8 @@ int au_do_open_nondir(struct file *file, int flags) /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ } + +out: return err; } @@ -376,7 +381,7 @@ static struct file *au_safe_file(struct vm_area_struct *vma) struct file *file; file = vma->vm_file; - if (file->private_data && au_test_aufs(file->f_dentry->d_sb)) + if (au_fi(file) && au_test_aufs(file->f_dentry->d_sb)) return file; return NULL; } diff --git a/ubuntu/aufs/file.c b/ubuntu/aufs/file.c index 15e38ce31c6..2ba0c000f16 100644 --- a/ubuntu/aufs/file.c +++ b/ubuntu/aufs/file.c @@ -65,7 +65,7 @@ struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, h_file = ERR_PTR(-EACCES); exec_flag = flags & vfsub_fmode_to_uint(FMODE_EXEC); if (exec_flag && (br->br_mnt->mnt_flags & MNT_NOEXEC)) - goto out; + goto out; /* drop flags for writing */ if (au_test_ro(sb, bindex, dentry->d_inode)) @@ -202,14 +202,20 @@ static int au_ready_to_write_wh(struct file *file, loff_t len, aufs_bindex_t bcpup) { int err; - struct inode *inode; - struct dentry *dentry, *hi_wh; + struct inode *inode, *h_inode; + struct dentry *dentry, *h_dentry, *hi_wh; dentry = file->f_dentry; au_update_dbstart(dentry); inode = dentry->d_inode; + h_inode = NULL; + if (au_dbstart(dentry) <= bcpup && au_dbend(dentry) >= bcpup) { + h_dentry = au_h_dptr(dentry, bcpup); + if (h_dentry) + h_inode = h_dentry->d_inode; + } hi_wh = au_hi_wh(inode, bcpup); - if (!hi_wh) + if (!hi_wh && !h_inode) err = au_sio_cpup_wh(dentry, bcpup, len, file); else /* already copied-up after unlink */ @@ -229,7 +235,7 @@ static int au_ready_to_write_wh(struct file *file, loff_t len, int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) { int err; - aufs_bindex_t bstart, bcpup; + aufs_bindex_t bstart, bcpup, dbstart; struct dentry *dentry, *parent, *h_dentry; struct inode *h_inode, *inode; struct super_block *sb; @@ -246,7 +252,7 @@ int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) goto out; } - /* need to cpup */ + /* need to cpup or reopen */ parent = dget_parent(dentry); di_write_lock_parent(parent); err = AuWbrCopyup(au_sbi(sb), dentry); @@ -255,7 +261,7 @@ int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) goto out_dgrade; err = 0; - if (!au_h_dptr(parent, bcpup)) { + if (!d_unhashed(dentry) && !au_h_dptr(parent, bcpup)) { err = au_cpup_dirs(dentry, bcpup); if (unlikely(err)) goto out_dgrade; @@ -268,25 +274,43 @@ int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) h_dentry = au_hf_top(file)->f_dentry; h_inode = h_dentry->d_inode; - mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); - h_file = au_h_open_pre(dentry, bstart); - if (IS_ERR(h_file)) { - err = PTR_ERR(h_file); - h_file = NULL; - } else if (d_unhashed(dentry) /* || d_unhashed(h_dentry) */ - /* || !h_inode->i_nlink */) { + dbstart = au_dbstart(dentry); + if (dbstart <= bcpup) { + h_dentry = au_h_dptr(dentry, bcpup); + AuDebugOn(!h_dentry); + h_inode = h_dentry->d_inode; + AuDebugOn(!h_inode); + bstart = bcpup; + } + + if (dbstart <= bcpup /* just reopen */ + || !d_unhashed(dentry) /* copyup and reopen */ + ) { + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + h_file = au_h_open_pre(dentry, bstart); + if (IS_ERR(h_file)) { + err = PTR_ERR(h_file); + h_file = NULL; + } else { + di_downgrade_lock(parent, AuLock_IR); + if (dbstart > bcpup) + err = au_sio_cpup_simple(dentry, bcpup, len, + AuCpup_DTIME); + if (!err) + err = au_reopen_nondir(file); + } + mutex_unlock(&h_inode->i_mutex); + au_h_open_post(dentry, bstart, h_file); + } else { /* copyup as wh and reopen */ + /* + * since writable hfsplus branch is not supported, + * h_open_pre/post() are unnecessary. + */ + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); err = au_ready_to_write_wh(file, len, bcpup); di_downgrade_lock(parent, AuLock_IR); - } else { - di_downgrade_lock(parent, AuLock_IR); - if (!au_h_dptr(dentry, bcpup)) - err = au_sio_cpup_simple(dentry, bcpup, len, - AuCpup_DTIME); - if (!err) - err = au_reopen_nondir(file); + mutex_unlock(&h_inode->i_mutex); } - mutex_unlock(&h_inode->i_mutex); - au_h_open_post(dentry, bstart, h_file); if (!err) { au_pin_set_parent_lflag(pin, /*lflag*/0); @@ -351,7 +375,7 @@ static int au_file_refresh_by_inode(struct file *file, int *need_reopen) sb = dentry->d_sb; inode = dentry->d_inode; bstart = au_ibstart(inode); - if (bstart == finfo->fi_btop) + if (bstart == finfo->fi_btop || IS_ROOT(dentry)) goto out; parent = dget_parent(dentry); @@ -549,16 +573,13 @@ int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), } AuDbg("sigen %d, figen %d\n", sigen, figen); - if (sigen != au_digen(dentry) - || sigen != au_iigen(inode)) { + if (au_digen_test(dentry, sigen)) { err = au_reval_dpath(dentry, sigen); - if (unlikely(err < 0)) - goto out; - AuDebugOn(au_digen(dentry) != sigen - || au_iigen(inode) != sigen); + AuDebugOn(!err && au_digen_test(dentry, sigen)); } - err = refresh_file(file, reopen); + if (!err) + err = refresh_file(file, reopen); if (!err) { if (!wlock) { di_downgrade_lock(dentry, AuLock_IR); diff --git a/ubuntu/aufs/hfsnotify.c b/ubuntu/aufs/hfsnotify.c index 621b5380704..eaed1f12395 100644 --- a/ubuntu/aufs/hfsnotify.c +++ b/ubuntu/aufs/hfsnotify.c @@ -25,7 +25,6 @@ /* FS_IN_IGNORED is unnecessary */ static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE | FS_CREATE | FS_EVENT_ON_CHILD); -static struct fsnotify_group *au_hfsn_group; static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq); static void au_hfsn_free_mark(struct fsnotify_mark *mark) @@ -38,10 +37,18 @@ static void au_hfsn_free_mark(struct fsnotify_mark *mark) wake_up_all(&au_hfsn_wq); } -static int au_hfsn_alloc(struct au_hnotify *hn, struct inode *h_inode) +static int au_hfsn_alloc(struct au_hinode *hinode) { + struct au_hnotify *hn; + struct super_block *sb; + struct au_branch *br; struct fsnotify_mark *mark; + aufs_bindex_t bindex; + hn = hinode->hi_notify; + sb = hn->hn_aufs_inode->i_sb; + bindex = au_br_index(sb, hinode->hi_id); + br = au_sbr(sb, bindex); hn->hn_mark_dead = 0; mark = &hn->hn_mark; fsnotify_init_mark(mark, au_hfsn_free_mark); @@ -50,14 +57,16 @@ static int au_hfsn_alloc(struct au_hnotify *hn, struct inode *h_inode) * by udba rename or rmdir, aufs assign a new inode to the known * h_inode, so specify 1 to allow dups. */ - return fsnotify_add_mark(mark, au_hfsn_group, h_inode, /*mnt*/NULL, - /*allow_dups*/1); + return fsnotify_add_mark(mark, br->br_hfsn_group, hinode->hi_inode, + /*mnt*/NULL, /*allow_dups*/1); } -static void au_hfsn_free(struct au_hnotify *hn) +static void au_hfsn_free(struct au_hinode *hinode) { + struct au_hnotify *hn; struct fsnotify_mark *mark; + hn = hinode->hi_notify; mark = &hn->hn_mark; fsnotify_destroy_mark(mark); fsnotify_put_mark(mark); @@ -167,6 +176,7 @@ out: return err; } +/* isn't it waste to ask every registered 'group'? */ /* copied from linux/fs/notify/inotify/inotify_fsnotiry.c */ /* it should be exported to modules */ static bool au_hfsn_should_send_event(struct fsnotify_group *group, @@ -186,31 +196,52 @@ static struct fsnotify_ops au_hfsn_ops = { /* ---------------------------------------------------------------------- */ -static int __init au_hfsn_init(void) +static void au_hfsn_fin_br(struct au_branch *br) +{ + if (br->br_hfsn_group) + fsnotify_put_group(br->br_hfsn_group); +} + +static int au_hfsn_init_br(struct au_branch *br, int perm) +{ + br->br_hfsn_group = NULL; + br->br_hfsn_ops = au_hfsn_ops; + return 0; +} + +static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) { int err; err = 0; - au_hfsn_group = fsnotify_alloc_group(&au_hfsn_ops); - if (IS_ERR(au_hfsn_group)) { - err = PTR_ERR(au_hfsn_group); + if (udba != AuOpt_UDBA_HNOTIFY + || !au_br_hnotifyable(perm)) { + au_hfsn_fin_br(br); + br->br_hfsn_group = NULL; + goto out; + } + + if (br->br_hfsn_group) + goto out; + + br->br_hfsn_group = fsnotify_alloc_group(&br->br_hfsn_ops); + if (IS_ERR(br->br_hfsn_group)) { + err = PTR_ERR(br->br_hfsn_group); pr_err("fsnotify_alloc_group() failed, %d\n", err); + br->br_hfsn_group = NULL; } +out: AuTraceErr(err); return err; } -static void au_hfsn_fin(void) -{ - fsnotify_put_group(au_hfsn_group); -} - const struct au_hnotify_op au_hnotify_op = { .ctl = au_hfsn_ctl, .alloc = au_hfsn_alloc, .free = au_hfsn_free, - .fin = au_hfsn_fin, - .init = au_hfsn_init + .reset_br = au_hfsn_reset_br, + .fin_br = au_hfsn_fin_br, + .init_br = au_hfsn_init_br }; diff --git a/ubuntu/aufs/hnotify.c b/ubuntu/aufs/hnotify.c index 535a5ceeec9..68279f9fa04 100644 --- a/ubuntu/aufs/hnotify.c +++ b/ubuntu/aufs/hnotify.c @@ -22,8 +22,7 @@ #include "aufs.h" -int au_hn_alloc(struct au_hinode *hinode, struct inode *inode, - struct inode *h_inode) +int au_hn_alloc(struct au_hinode *hinode, struct inode *inode) { int err; struct au_hnotify *hn; @@ -32,10 +31,11 @@ int au_hn_alloc(struct au_hinode *hinode, struct inode *inode, hn = au_cache_alloc_hnotify(); if (hn) { hn->hn_aufs_inode = inode; - err = au_hnotify_op.alloc(hn, h_inode); - if (!err) - hinode->hi_notify = hn; - else { + hinode->hi_notify = hn; + err = au_hnotify_op.alloc(hinode); + AuTraceErr(err); + if (unlikely(err)) { + hinode->hi_notify = NULL; au_cache_free_hnotify(hn); /* * The upper dir was removed by udba, but the same named @@ -48,6 +48,7 @@ int au_hn_alloc(struct au_hinode *hinode, struct inode *inode, } } + AuTraceErr(err); return err; } @@ -57,7 +58,7 @@ void au_hn_free(struct au_hinode *hinode) hn = hinode->hi_notify; if (hn) { - au_hnotify_op.free(hn); + au_hnotify_op.free(hinode); au_cache_free_hnotify(hn); hinode->hi_notify = NULL; } @@ -170,7 +171,6 @@ static int hn_gen_tree(struct dentry *dentry) if (IS_ROOT(d)) continue; - d_drop(d); au_digen_dec(d); if (d->d_inode) /* todo: reset children xino? @@ -216,15 +216,12 @@ static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, && memcmp(dname->name, name, nlen)) continue; err = 0; - spin_lock(&d->d_lock); - __d_drop(d); au_digen_dec(d); - spin_unlock(&d->d_lock); break; } spin_unlock(&dcache_lock); } else { - au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIRS); + au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR); d = d_find_alias(inode); if (!d) { au_iigen_dec(inode); @@ -257,12 +254,11 @@ static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir) err = 0; if (!isdir) { - d_drop(dentry); au_digen_dec(dentry); if (inode) au_iigen_dec(inode); } else { - au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS); + au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR); if (inode) err = hn_gen_tree(dentry); } @@ -376,12 +372,12 @@ static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, dname = &d->d_name; if (dname->len != nlen || memcmp(dname->name, name, nlen)) continue; - if (!atomic_read(&d->d_count) || !d->d_fsdata) { - spin_lock(&d->d_lock); - __d_drop(d); - spin_unlock(&d->d_lock); + if (au_di(d)) + au_digen_dec(d); + else + continue; + if (!atomic_read(&d->d_count)) continue; - } dentry = dget(d); break; @@ -498,7 +494,7 @@ static void au_hn_bh(void *_args) args.h_nlen = a->h_child_nlen; err = hn_job(&args); if (dentry) { - if (dentry->d_fsdata) + if (au_di(dentry)) di_write_unlock(dentry); dput(dentry); } @@ -520,12 +516,11 @@ static void au_hn_bh(void *_args) ii_write_unlock(a->dir); out: - au_nwt_done(&sbinfo->si_nowait); - si_write_unlock(sb); - iput(a->h_child_inode); iput(a->h_dir); iput(a->dir); + si_write_unlock(sb); + au_nwt_done(&sbinfo->si_nowait); kfree(a); } @@ -634,6 +629,38 @@ out: return err; } +/* ---------------------------------------------------------------------- */ + +int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm) +{ + int err; + + AuDebugOn(!(udba & AuOptMask_UDBA)); + + err = 0; + if (au_hnotify_op.reset_br) + err = au_hnotify_op.reset_br(udba, br, perm); + + return err; +} + +int au_hnotify_init_br(struct au_branch *br, int perm) +{ + int err; + + err = 0; + if (au_hnotify_op.init_br) + err = au_hnotify_op.init_br(br, perm); + + return err; +} + +void au_hnotify_fin_br(struct au_branch *br) +{ + if (au_hnotify_op.fin_br) + au_hnotify_op.fin_br(br); +} + static void au_hn_destroy_cache(void) { kmem_cache_destroy(au_cachep[AuCache_HNOTIFY]); @@ -647,7 +674,9 @@ int __init au_hnotify_init(void) err = -ENOMEM; au_cachep[AuCache_HNOTIFY] = AuCache(au_hnotify); if (au_cachep[AuCache_HNOTIFY]) { - err = au_hnotify_op.init(); + err = 0; + if (au_hnotify_op.init) + err = au_hnotify_op.init(); if (unlikely(err)) au_hn_destroy_cache(); } @@ -657,7 +686,8 @@ int __init au_hnotify_init(void) void au_hnotify_fin(void) { - au_hnotify_op.fin(); + if (au_hnotify_op.fin) + au_hnotify_op.fin(); /* cf. au_cache_fin() */ if (au_cachep[AuCache_HNOTIFY]) au_hn_destroy_cache(); diff --git a/ubuntu/aufs/i_op.c b/ubuntu/aufs/i_op.c index bbda6e4a2f2..dddcaca16a9 100644 --- a/ubuntu/aufs/i_op.c +++ b/ubuntu/aufs/i_op.c @@ -95,6 +95,11 @@ static int aufs_permission(struct inode *inode, int mask) 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(); @@ -170,11 +175,18 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, if (unlikely(err)) goto out_si; + npositive = 0; /* suppress a warning */ parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); - npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, nd); + err = au_alive_dir(parent); + if (!err) + err = au_digen_test(parent, au_sigen(sb)); + if (!err) { + npositive = au_lkup_dentry(dentry, au_dbstart(parent), + /*type*/0, nd); + err = npositive; + } di_read_unlock(parent, AuLock_IR); - err = npositive; ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; @@ -211,10 +223,9 @@ static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, struct dentry *h_parent; struct inode *h_dir; - if (add_entry) { - au_update_dbstart(dentry); + if (add_entry) IMustLock(parent->d_inode); - } else + else di_write_lock_parent(parent); err = 0; @@ -231,8 +242,11 @@ static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, err = au_lkup_neg(dentry, bcpup); /* todo: no unlock here */ mutex_unlock(&h_dir->i_mutex); - if (bstart < bcpup && au_dbstart(dentry) < 0) { - au_set_dbstart(dentry, 0); + + AuDbg("bcpup %d\n", bcpup); + if (!err) { + if (!dentry->d_inode) + au_set_h_dptr(dentry, bstart, NULL); au_update_dbrange(dentry, /*do_put_zero*/0); } } @@ -242,6 +256,7 @@ static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, if (!err) err = bcpup; /* success */ + AuTraceErr(err); return err; } @@ -296,15 +311,22 @@ int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, bcpup = args->force_btgt; AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode)); } + AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); err = bcpup; if (bcpup == bstart) goto out; /* success */ - else if (bstart < bcpup) - au_update_dbrange(dentry, /*do_put_zero*/1); /* copyup the new parent into the branch we process */ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); + if (err >= 0) { + if (!dentry->d_inode) { + au_set_h_dptr(dentry, bstart, NULL); + au_set_dbstart(dentry, bcpup); + au_set_dbend(dentry, bcpup); + } + AuDebugOn(add_entry && !au_h_dptr(dentry, bcpup)); + } out: dput(parent); @@ -455,6 +477,7 @@ int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, * unhashed. * for ->setattr(), ia->ia_file is passed from ftruncate only. */ +/* todo: consolidate with do_refresh() and simple_reval_dpath() */ static int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) { int err; @@ -463,13 +486,10 @@ static int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) err = 0; inode = dentry->d_inode; - if (au_digen(dentry) != sigen || au_iigen(inode) != sigen) { + if (au_digen_test(dentry, sigen)) { parent = dget_parent(dentry); di_read_lock_parent(parent, AuLock_IR); - /* returns a number of positive dentries */ - err = au_refresh_hdentry(dentry, inode->i_mode & S_IFMT); - if (err >= 0) - err = au_refresh_hinode(inode, dentry); + err = au_refresh_dentry(dentry, parent); di_read_unlock(parent, AuLock_IR); dput(parent); } @@ -515,7 +535,7 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, au_fset_wrdir(wr_dir_args.flags, ISDIR); /* plink or hi_wh() case */ ibstart = au_ibstart(inode); - if (bstart != ibstart) + if (bstart != ibstart && !au_test_ro(inode->i_sb, ibstart, inode)) wr_dir_args.force_btgt = ibstart; err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); if (unlikely(err < 0)) @@ -692,6 +712,8 @@ static int aufs_setattr(struct dentry *dentry, struct iattr *ia) out_unlock: mutex_unlock(&a->h_inode->i_mutex); au_unpin(&a->pin); + if (unlikely(err)) + au_update_dbstart(dentry); out_dentry: di_write_unlock(dentry); if (file) { @@ -753,12 +775,18 @@ static int aufs_getattr(struct vfsmount *mnt __maybe_unused, /* support fstat(2) */ if (!au_d_removed(dentry) && !udba_none) { unsigned int sigen = au_sigen(sb); - if (au_digen(dentry) == sigen && au_iigen(inode) == sigen) + err = au_digen_test(dentry, sigen); + if (!err) { di_read_lock_child(dentry, AuLock_IR); - else { + err = au_dbrange_test(dentry); + if (unlikely(err)) + goto out_unlock; + } else { AuDebugOn(IS_ROOT(dentry)); di_write_lock_child(dentry); - err = au_reval_for_attr(dentry, sigen); + err = au_dbrange_test(dentry); + if (!err) + err = au_reval_for_attr(dentry, sigen); di_downgrade_lock(dentry, AuLock_IR); if (unlikely(err)) goto out_unlock; @@ -839,10 +867,15 @@ static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) { int err; - aufs_read_lock(dentry, AuLock_IR); - err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz); + err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); + if (unlikely(err)) + goto out; + err = au_d_hashed_positive(dentry); + if (!err) + err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz); aufs_read_unlock(dentry, AuLock_IR); +out: return err; } @@ -860,11 +893,17 @@ static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd) if (unlikely(!buf.k)) goto out; - aufs_read_lock(dentry, AuLock_IR); - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = h_readlink(dentry, au_dbstart(dentry), buf.u, PATH_MAX); - set_fs(old_fs); + err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); + if (unlikely(err)) + goto out_name; + + err = au_d_hashed_positive(dentry); + if (!err) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = h_readlink(dentry, au_dbstart(dentry), buf.u, PATH_MAX); + set_fs(old_fs); + } aufs_read_unlock(dentry, AuLock_IR); if (err >= 0) { @@ -873,8 +912,9 @@ static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd) nd_set_link(nd, buf.k); return NULL; /* success */ } - __putname(buf.k); +out_name: + __putname(buf.k); out: path_put(&nd->path); AuTraceErr(err); diff --git a/ubuntu/aufs/i_op_add.c b/ubuntu/aufs/i_op_add.c index 18635718325..cd11360361d 100644 --- a/ubuntu/aufs/i_op_add.c +++ b/ubuntu/aufs/i_op_add.c @@ -81,6 +81,18 @@ out: return err; } +static int au_d_may_add(struct dentry *dentry) +{ + int err; + + err = 0; + if (unlikely(d_unhashed(dentry))) + err = -ENOENT; + if (unlikely(dentry->d_inode)) + err = -EEXIST; + return err; +} + /* * simple tests for the adding inode operations. * following the checks in vfs, plus the parent-child relationship. @@ -236,13 +248,18 @@ static int add_simple(struct inode *dir, struct dentry *dentry, IMustLock(dir); parent = dentry->d_parent; /* dir inode is locked */ - aufs_read_lock(dentry, AuLock_DW); + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); + if (unlikely(err)) + goto out; + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; di_write_lock_parent(parent); wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin, &wr_dir_args); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) - goto out; + goto out_parent; bstart = au_dbstart(dentry); h_path.dentry = au_h_dptr(dentry, bstart); @@ -275,19 +292,20 @@ static int add_simple(struct inode *dir, struct dentry *dentry, err = -EIO; } au_dtime_revert(&dt); - d_drop(dentry); } au_unpin(&pin); dput(wh_dentry); -out: +out_parent: + di_write_unlock(parent); +out_unlock: if (unlikely(err)) { au_update_dbstart(dentry); d_drop(dentry); } - di_write_unlock(parent); aufs_read_unlock(dentry, AuLock_DW); +out: return err; } @@ -456,19 +474,22 @@ int aufs_link(struct dentry *src_dentry, struct inode *dir, inode = src_dentry->d_inode; IMustLock(inode); - err = -ENOENT; - if (unlikely(!inode->i_nlink)) - goto out; - err = -ENOMEM; a = kzalloc(sizeof(*a), GFP_NOFS); if (unlikely(!a)) goto out; a->parent = dentry->d_parent; /* dir inode is locked */ - err = aufs_read_and_write_lock2(dentry, src_dentry, AuLock_NOPLM); + err = aufs_read_and_write_lock2(dentry, src_dentry, + AuLock_NOPLM | AuLock_GEN); if (unlikely(err)) goto out_kfree; + err = au_d_hashed_positive(src_dentry); + if (unlikely(err)) + goto out_unlock; + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; a->src_parent = dget_parent(src_dentry); wr_dir_args.force_btgt = au_dbstart(src_dentry); @@ -479,7 +500,7 @@ int aufs_link(struct dentry *src_dentry, struct inode *dir, &wr_dir_args); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) - goto out_unlock; + goto out_parent; err = 0; sb = dentry->d_sb; @@ -539,34 +560,32 @@ int aufs_link(struct dentry *src_dentry, struct inode *dir, au_cpup_attr_timesizes(dir); inc_nlink(inode); inode->i_ctime = dir->i_ctime; - if (!d_unhashed(a->h_path.dentry)) - d_instantiate(dentry, au_igrab(inode)); - else + d_instantiate(dentry, au_igrab(inode)); + if (d_unhashed(a->h_path.dentry)) /* some filesystem calls d_drop() */ d_drop(dentry); goto out_unpin; /* success */ out_revert: rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, /*force*/0); - if (!rerr) - goto out_dt; - AuIOErr("%.*s reverting failed(%d, %d)\n", - AuDLNPair(dentry), err, rerr); - err = -EIO; -out_dt: - d_drop(dentry); + if (unlikely(rerr)) { + AuIOErr("%.*s reverting failed(%d, %d)\n", + AuDLNPair(dentry), err, rerr); + err = -EIO; + } au_dtime_revert(&dt); out_unpin: au_unpin(&a->pin); out_wh: dput(wh_dentry); +out_parent: + di_write_unlock(a->parent); + dput(a->src_parent); out_unlock: if (unlikely(err)) { au_update_dbstart(dentry); d_drop(dentry); } - di_write_unlock(a->parent); - dput(a->src_parent); aufs_read_and_write_unlock2(dentry, src_dentry); out_kfree: kfree(a); @@ -599,14 +618,20 @@ int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) if (unlikely(!a)) goto out; - aufs_read_lock(dentry, AuLock_DW); + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); + if (unlikely(err)) + goto out_free; + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; + parent = dentry->d_parent; /* dir inode is locked */ di_write_lock_parent(parent); wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, &a->pin, &wr_dir_args); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) - goto out_free; + goto out_parent; sb = dentry->d_sb; bindex = au_dbstart(dentry); @@ -614,7 +639,7 @@ int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) h_path.mnt = au_sbr_mnt(sb, bindex); err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); if (unlikely(err)) - goto out_unlock; + goto out_unpin; /* make the dir opaque */ diropq = 0; @@ -634,7 +659,7 @@ int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) err = epilog(dir, bindex, wh_dentry, dentry); if (!err) { inc_nlink(dir); - goto out_unlock; /* success */ + goto out_unpin; /* success */ } /* revert */ @@ -658,18 +683,19 @@ out_dir: AuDLNPair(dentry), err, rerr); err = -EIO; } - d_drop(dentry); au_dtime_revert(&a->dt); -out_unlock: +out_unpin: au_unpin(&a->pin); dput(wh_dentry); -out_free: +out_parent: + di_write_unlock(parent); +out_unlock: if (unlikely(err)) { au_update_dbstart(dentry); d_drop(dentry); } - di_write_unlock(parent); aufs_read_unlock(dentry, AuLock_DW); +out_free: kfree(a); out: return err; diff --git a/ubuntu/aufs/i_op_del.c b/ubuntu/aufs/i_op_del.c index 5b31bbd8876..1019bf91aea 100644 --- a/ubuntu/aufs/i_op_del.c +++ b/ubuntu/aufs/i_op_del.c @@ -59,24 +59,20 @@ int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) goto out; need_wh = 1; } else { - aufs_bindex_t old_bend, new_bend, bdiropq = -1; - - old_bend = au_dbend(dentry); - if (isdir) { - bdiropq = au_dbdiropq(dentry); - au_set_dbdiropq(dentry, -1); - } - need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0, - /*nd*/NULL); - err = need_wh; - if (isdir) - au_set_dbdiropq(dentry, bdiropq); - if (unlikely(err < 0)) - goto out; - new_bend = au_dbend(dentry); - if (!need_wh && old_bend != new_bend) { - au_set_h_dptr(dentry, new_bend, NULL); - au_set_dbend(dentry, old_bend); + struct au_dinfo *dinfo, *tmp; + + need_wh = -ENOMEM; + dinfo = au_di(dentry); + tmp = au_di_alloc(sb, AuLsc_DI_TMP); + if (tmp) { + au_di_cp(tmp, dinfo); + au_di_swap(tmp, dinfo); + /* returns the number of positive dentries */ + need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0, + /*nd*/NULL); + au_di_swap(tmp, dinfo); + au_rw_write_unlock(&tmp->di_rwsem); + au_di_free(tmp); } } AuDbg("need_wh %d\n", need_wh); @@ -268,10 +264,6 @@ static void epilog(struct inode *dir, struct dentry *dentry, d_drop(dentry); inode->i_ctime = dir->i_ctime; - if (atomic_read(&dentry->d_count) == 1) { - au_set_h_dptr(dentry, au_dbstart(dentry), NULL); - au_update_dbstart(dentry); - } if (au_ibstart(dir) == bindex) au_cpup_attr_timesizes(dir); dir->i_version++; @@ -315,22 +307,28 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry) struct dentry *parent, *wh_dentry; IMustLock(dir); + + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); + if (unlikely(err)) + goto out; + err = au_d_hashed_positive(dentry); + if (unlikely(err)) + goto out_unlock; inode = dentry->d_inode; - if (unlikely(!inode)) - return -ENOENT; /* possible? */ IMustLock(inode); - - aufs_read_lock(dentry, AuLock_DW); - parent = dentry->d_parent; /* dir inode is locked */ - di_write_lock_parent(parent); + err = -EISDIR; + if (unlikely(S_ISDIR(inode->i_mode))) + goto out_unlock; /* possible? */ bstart = au_dbstart(dentry); bwh = au_dbwh(dentry); bindex = -1; + parent = dentry->d_parent; /* dir inode is locked */ + di_write_lock_parent(parent); wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt, &pin); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) - goto out; + goto out_parent; h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); h_path.dentry = au_h_dptr(dentry, bstart); @@ -356,7 +354,7 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry) } else /* todo: this timestamp may be reverted later */ inode->i_ctime = h_dir->i_ctime; - goto out_unlock; /* success */ + goto out_unpin; /* success */ } /* revert */ @@ -368,13 +366,15 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry) err = rerr; } -out_unlock: +out_unpin: au_unpin(&pin); dput(wh_dentry); dput(h_path.dentry); -out: +out_parent: di_write_unlock(parent); +out_unlock: aufs_read_unlock(dentry, AuLock_DW); +out: return err; } @@ -389,13 +389,22 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry) struct au_whtmp_rmdir *args; IMustLock(dir); - inode = dentry->d_inode; - err = -ENOENT; /* possible? */ - if (unlikely(!inode)) + + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); + if (unlikely(err)) goto out; + + /* VFS already unhashes it */ + inode = dentry->d_inode; + err = -ENOENT; + if (unlikely(!inode || !inode->i_nlink + || IS_DEADDIR(inode))) + goto out_unlock; IMustLock(inode); + err = -ENOTDIR; + if (unlikely(!S_ISDIR(inode->i_mode))) + goto out_unlock; /* possible? */ - aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH); err = -ENOMEM; args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); if (unlikely(!args)) @@ -405,7 +414,7 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry) di_write_lock_parent(parent); err = au_test_empty(dentry, &args->whlist); if (unlikely(err)) - goto out_args; + goto out_parent; bstart = au_dbstart(dentry); bwh = au_dbwh(dentry); @@ -413,7 +422,7 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry) wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt, &pin); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) - goto out_args; + goto out_parent; h_dentry = au_h_dptr(dentry, bstart); dget(h_dentry); @@ -434,7 +443,7 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry) } if (!err) { - clear_nlink(inode); + vfsub_dead_dir(inode); au_set_dbdiropq(dentry, -1); epilog(dir, dentry, bindex); @@ -460,7 +469,7 @@ out_unpin: au_unpin(&pin); dput(wh_dentry); dput(h_dentry); -out_args: +out_parent: di_write_unlock(parent); if (args) au_whtmp_rmdir_free(args); diff --git a/ubuntu/aufs/i_op_ren.c b/ubuntu/aufs/i_op_ren.c index 89efe679fea..e1d23928cf7 100644 --- a/ubuntu/aufs/i_op_ren.c +++ b/ubuntu/aufs/i_op_ren.c @@ -79,7 +79,7 @@ struct au_ren_args { struct au_hinode *src_hinode; struct path h_path; struct au_nhash whlist; - aufs_bindex_t btgt; + aufs_bindex_t btgt, src_bwh, src_bdiropq; unsigned int flags; @@ -110,6 +110,7 @@ static void au_ren_rev_diropq(int err, struct au_ren_args *a) au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); rerr = au_diropq_remove(a->src_dentry, a->btgt); au_hn_imtx_unlock(a->src_hinode); + au_set_dbdiropq(a->src_dentry, a->src_bdiropq); if (rerr) RevertFailure("remove diropq %.*s", AuDLNPair(a->src_dentry)); } @@ -180,31 +181,10 @@ static void au_ren_rev_whsrc(int err, struct au_ren_args *a) a->h_path.dentry = a->src_wh_dentry; rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); + au_set_dbwh(a->src_dentry, a->src_bwh); if (rerr) RevertFailure("unlink %.*s", AuDLNPair(a->src_wh_dentry)); } - -static void au_ren_rev_drop(struct au_ren_args *a) -{ - struct dentry *d, *h_d; - int i; - aufs_bindex_t bend, bindex; - - for (i = 0; i < AuSrcDst; i++) { - d = a->sd[i].dentry; - d_drop(d); - bend = au_dbend(d); - for (bindex = au_dbstart(d); bindex <= bend; bindex++) { - h_d = au_h_dptr(d, bindex); - if (h_d) - d_drop(h_d); - } - } - - au_update_dbstart(a->dst_dentry); - if (a->thargs) - d_drop(a->h_dst); -} #undef RevertFailure /* ---------------------------------------------------------------------- */ @@ -254,6 +234,9 @@ static int au_ren_or_cpup(struct au_ren_args *a) au_set_dbstart(d, a->src_bstart); } } + if (!err && a->h_dst) + /* it will be set to dinfo later */ + dget(a->h_dst); return err; } @@ -292,6 +275,7 @@ static int au_ren_diropq(struct au_ren_args *a) struct dentry *diropq; err = 0; + a->src_bdiropq = au_dbdiropq(a->src_dentry); a->src_hinode = au_hi(a->src_inode, a->btgt); au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); diropq = au_diropq_create(a->src_dentry, a->btgt); @@ -320,6 +304,8 @@ static int do_rename(struct au_ren_args *a) /* create whiteout for src_dentry */ if (au_ftest_ren(a->flags, WHSRC)) { + a->src_bwh = au_dbwh(a->src_dentry); + AuDebugOn(a->src_bwh >= 0); a->src_wh_dentry = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); err = PTR_ERR(a->src_wh_dentry); @@ -425,6 +411,7 @@ out_rename: au_ren_rev_rename(err, a); else au_ren_rev_cpup(err, a); + dput(a->h_dst); out_whtmp: if (a->thargs) au_ren_rev_whtmp(err, a); @@ -434,7 +421,6 @@ out_whdst: out_whsrc: if (a->src_wh_dentry) au_ren_rev_whsrc(err, a); - au_ren_rev_drop(a); out_success: dput(a->src_wh_dentry); dput(a->dst_wh_dentry); @@ -685,11 +671,8 @@ static void au_ren_refresh_dir(struct au_ren_args *a) /* is this updating defined in POSIX? */ au_cpup_attr_timesizes(a->src_inode); au_cpup_attr_nlink(dir, /*force*/1); - if (a->dst_inode) { - clear_nlink(a->dst_inode); - au_cpup_attr_timesizes(a->dst_inode); - } } + if (au_ibstart(dir) == a->btgt) au_cpup_attr_timesizes(dir); @@ -711,6 +694,31 @@ static void au_ren_refresh(struct au_ren_args *a) struct inode *i, *h_i; struct super_block *sb; + d = a->dst_dentry; + d_drop(d); + if (a->h_dst) + /* already dget-ed by au_ren_or_cpup() */ + au_set_h_dptr(d, a->btgt, a->h_dst); + + i = a->dst_inode; + if (i) { + if (!au_ftest_ren(a->flags, ISDIR)) + vfsub_drop_nlink(i); + else { + vfsub_dead_dir(i); + au_cpup_attr_timesizes(i); + } + au_update_dbrange(d, /*do_put_zero*/1); + } else { + bend = a->btgt; + for (bindex = au_dbstart(d); bindex < bend; bindex++) + au_set_h_dptr(d, bindex, NULL); + bend = au_dbend(d); + for (bindex = a->btgt + 1; bindex <= bend; bindex++) + au_set_h_dptr(d, bindex, NULL); + au_update_dbrange(d, /*do_put_zero*/0); + } + d = a->src_dentry; au_set_dbwh(d, -1); bend = au_dbend(d); @@ -866,7 +874,7 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, } err = -ENOTDIR; - flags = AuLock_FLUSH | AuLock_NOPLM; + flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; if (S_ISDIR(a->src_inode->i_mode)) { au_fset_ren(a->flags, ISDIR); if (unlikely(a->dst_inode && !S_ISDIR(a->dst_inode->i_mode))) @@ -879,13 +887,33 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, if (unlikely(err)) goto out_free; + err = au_d_hashed_positive(a->src_dentry); + if (unlikely(err)) + goto out_unlock; + err = -ENOENT; + if (a->dst_inode) { + /* + * If it is a dir, VFS unhash dst_dentry before this + * function. It means we cannot rely upon d_unhashed(). + */ + if (unlikely(!a->dst_inode->i_nlink)) + goto out_unlock; + if (!S_ISDIR(a->dst_inode->i_mode)) { + err = au_d_hashed_positive(a->dst_dentry); + if (unlikely(err)) + goto out_unlock; + } else if (unlikely(IS_DEADDIR(a->dst_inode))) + goto out_unlock; + } else if (unlikely(d_unhashed(a->dst_dentry))) + goto out_unlock; + au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ di_write_lock_parent(a->dst_parent); /* which branch we process */ err = au_ren_wbr(a); if (unlikely(err < 0)) - goto out_unlock; + goto out_parent; a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); a->h_path.mnt = a->br->br_mnt; @@ -959,17 +987,24 @@ out_hdir: au_ren_unlock(a); out_children: au_nhash_wh_free(&a->whlist); -out_unlock: - if (unlikely(err && au_ftest_ren(a->flags, ISDIR))) { - au_update_dbstart(a->dst_dentry); - d_drop(a->dst_dentry); + if (err && a->dst_inode && a->dst_bstart != a->btgt) { + AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); + au_set_h_dptr(a->dst_dentry, a->btgt, NULL); + au_set_dbstart(a->dst_dentry, a->dst_bstart); } +out_parent: if (!err) d_move(a->src_dentry, a->dst_dentry); + else { + au_update_dbstart(a->dst_dentry); + if (!a->dst_inode) + d_drop(a->dst_dentry); + } if (au_ftest_ren(a->flags, ISSAMEDIR)) di_write_unlock(a->dst_parent); else di_write_unlock2(a->src_parent, a->dst_parent); +out_unlock: aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); out_free: iput(a->dst_inode); diff --git a/ubuntu/aufs/iinfo.c b/ubuntu/aufs/iinfo.c index 6bccfdf18e4..571b21d25fc 100644 --- a/ubuntu/aufs/iinfo.c +++ b/ubuntu/aufs/iinfo.c @@ -75,6 +75,9 @@ void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, struct super_block *sb = inode->i_sb; struct au_branch *br; + AuDebugOn(inode->i_mode + && (h_inode->i_mode & S_IFMT) + != (inode->i_mode & S_IFMT)); if (bindex == iinfo->ii_bstart) au_cpup_igen(inode, h_inode); br = au_sbr(sb, bindex); @@ -88,7 +91,7 @@ void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, if (au_ftest_hi(flags, HNOTIFY) && au_br_hnotifyable(br->br_perm)) { - err = au_hn_alloc(hinode, inode, h_inode); + err = au_hn_alloc(hinode, inode); if (unlikely(err)) AuIOErr1("au_hn_alloc() %d\n", err); } @@ -117,16 +120,15 @@ void au_update_iigen(struct inode *inode) void au_update_ibrange(struct inode *inode, int do_put_zero) { struct au_iinfo *iinfo; + aufs_bindex_t bindex, bend; iinfo = au_ii(inode); - if (!iinfo || iinfo->ii_bstart < 0) + if (!iinfo) return; IiMustWriteLock(inode); - if (do_put_zero) { - aufs_bindex_t bindex; - + if (do_put_zero && iinfo->ii_bstart >= 0) { for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) { struct inode *h_i; @@ -138,20 +140,20 @@ void au_update_ibrange(struct inode *inode, int do_put_zero) } iinfo->ii_bstart = -1; - while (++iinfo->ii_bstart <= iinfo->ii_bend) - if (iinfo->ii_hinode[0 + iinfo->ii_bstart].hi_inode) - break; - if (iinfo->ii_bstart > iinfo->ii_bend) { - iinfo->ii_bstart = -1; - iinfo->ii_bend = -1; - return; - } - - iinfo->ii_bend++; - while (0 <= --iinfo->ii_bend) - if (iinfo->ii_hinode[0 + iinfo->ii_bend].hi_inode) + iinfo->ii_bend = -1; + bend = au_sbend(inode->i_sb); + for (bindex = 0; bindex <= bend; bindex++) + if (iinfo->ii_hinode[0 + bindex].hi_inode) { + iinfo->ii_bstart = bindex; break; - AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend || iinfo->ii_bend < 0); + } + if (iinfo->ii_bstart >= 0) + for (bindex = bend; bindex >= iinfo->ii_bstart; bindex--) + if (iinfo->ii_hinode[0 + bindex].hi_inode) { + iinfo->ii_bend = bindex; + break; + } + AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend); } /* ---------------------------------------------------------------------- */ diff --git a/ubuntu/aufs/inode.c b/ubuntu/aufs/inode.c index fe4ee9bd626..9fe4d50e20f 100644 --- a/ubuntu/aufs/inode.c +++ b/ubuntu/aufs/inode.c @@ -39,37 +39,39 @@ static void au_refresh_hinode_attr(struct inode *inode, int do_version) inode->i_version++; } -int au_refresh_hinode_self(struct inode *inode, int do_attr) +static int au_ii_refresh(struct inode *inode, int *update) { int err, e; + umode_t type; aufs_bindex_t bindex, new_bindex; - unsigned char update; - struct au_hinode *p, *q, tmp; struct super_block *sb; struct au_iinfo *iinfo; + struct au_hinode *p, *q, tmp; IiMustWriteLock(inode); - update = 0; + *update = 0; sb = inode->i_sb; + type = inode->i_mode & S_IFMT; iinfo = au_ii(inode); err = au_ii_realloc(iinfo, au_sbend(sb) + 1); if (unlikely(err)) goto out; + AuDebugOn(iinfo->ii_bstart < 0); p = iinfo->ii_hinode + iinfo->ii_bstart; - err = 0; for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; 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; + *update = 1; au_hiput(p); p->hi_inode = NULL; continue; @@ -93,30 +95,43 @@ int au_refresh_hinode_self(struct inode *inode, int do_attr) e = au_dy_irefresh(inode); if (unlikely(e && !err)) err = e; - if (do_attr) - au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); 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; + int err, e, update; unsigned int flags; + umode_t mode; aufs_bindex_t bindex, bend; - unsigned char isdir, update; + unsigned char isdir; struct au_hinode *p; struct au_iinfo *iinfo; - err = au_refresh_hinode_self(inode, /*do_attr*/0); + err = au_ii_refresh(inode, &update); if (unlikely(err)) goto out; update = 0; iinfo = au_ii(inode); p = iinfo->ii_hinode + iinfo->ii_bstart; - isdir = S_ISDIR(inode->i_mode); + mode = (inode->i_mode & S_IFMT); + isdir = S_ISDIR(mode); flags = au_hi_flags(inode, isdir); bend = au_dbend(dentry); for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { @@ -127,6 +142,7 @@ int au_refresh_hinode(struct inode *inode, struct dentry *dentry) if (!h_d || !h_d->d_inode) continue; + AuDebugOn(mode != (h_d->d_inode->i_mode & S_IFMT)); if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { h_i = au_h_iptr(inode, bindex); if (h_i) { @@ -147,7 +163,8 @@ int au_refresh_hinode(struct inode *inode, struct dentry *dentry) e = au_dy_irefresh(inode); if (unlikely(e && !err)) err = e; - au_refresh_hinode_attr(inode, update && isdir); + if (!err) + au_refresh_hinode_attr(inode, update && isdir); out: AuTraceErr(err); @@ -227,15 +244,18 @@ out: return err; } -/* successful returns with iinfo write_locked */ -static int reval_inode(struct inode *inode, struct dentry *dentry, int *matched) +/* + * 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; aufs_bindex_t bindex, bend; struct inode *h_inode, *h_dinode; - *matched = 0; - /* * before this function, if aufs got any iinfo lock, it must be only * one, the parent dir. @@ -245,16 +265,15 @@ static int reval_inode(struct inode *inode, struct dentry *dentry, int *matched) if (unlikely(inode->i_ino == parent_ino(dentry))) goto out; - err = 0; + err = 1; ii_write_lock_new_child(inode); h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; bend = au_ibend(inode); for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); if (h_inode && h_inode == h_dinode) { - *matched = 1; err = 0; - if (au_iigen(inode) != au_digen(dentry)) + if (au_iigen_test(inode, au_digen(dentry))) err = au_refresh_hinode(inode, dentry); break; } @@ -307,7 +326,7 @@ struct inode *au_new_inode(struct dentry *dentry, int must_new) struct super_block *sb; struct mutex *mtx; ino_t h_ino, ino; - int err, match; + int err; aufs_bindex_t bstart; sb = dentry->d_sb; @@ -355,23 +374,33 @@ new_ino: goto out; /* success */ } - ii_write_unlock(inode); + /* + * 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); - goto out_err; - } else if (!must_new) { + ii_write_unlock(inode); + au_xino_write(sb, bstart, 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 (mtx) mutex_unlock(mtx); - err = reval_inode(inode, dentry, &match); + err = reval_inode(inode, dentry); + if (unlikely(err < 0)) { + mtx = NULL; + goto out_iput; + } + if (!err) { mtx = NULL; goto out; /* success */ - } else if (match) { - mtx = NULL; - goto out_iput; } else if (mtx) mutex_lock(mtx); } @@ -392,7 +421,6 @@ new_ino: out_iput: iput(inode); -out_err: inode = ERR_PTR(err); out: if (mtx) diff --git a/ubuntu/aufs/inode.h b/ubuntu/aufs/inode.h index ecb91be37d2..9f004a03d93 100644 --- a/ubuntu/aufs/inode.h +++ b/ubuntu/aufs/inode.h @@ -109,7 +109,7 @@ 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 do_attr); +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); @@ -305,6 +305,13 @@ AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem); /* ---------------------------------------------------------------------- */ +static inline void au_icntnr_init(struct au_icntnr *c) +{ +#ifdef CONFIG_AUFS_DEBUG + c->vfs_inode.i_mode = 0; +#endif +} + static inline unsigned int au_iigen(struct inode *inode) { return atomic_read(&au_ii(inode)->ii_generation); @@ -324,9 +331,18 @@ static inline int au_test_higen(struct inode *inode, struct inode *h_inode) static inline void au_iigen_dec(struct inode *inode) { -#ifdef CONFIG_AUFS_HNOTIFY atomic_dec(&au_ii(inode)->ii_generation); -#endif +} + +static inline int au_iigen_test(struct inode *inode, unsigned int sigen) +{ + int err; + + err = 0; + if (unlikely(inode && au_iigen(inode) != sigen)) + err = -EIO; + + return err; } /* ---------------------------------------------------------------------- */ @@ -436,24 +452,31 @@ static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent) /* ---------------------------------------------------------------------- */ +struct au_branch; #ifdef CONFIG_AUFS_HNOTIFY struct au_hnotify_op { void (*ctl)(struct au_hinode *hinode, int do_set); - int (*alloc)(struct au_hnotify *hn, struct inode *h_inode); - void (*free)(struct au_hnotify *hn); + int (*alloc)(struct au_hinode *hinode); + void (*free)(struct au_hinode *hinode); void (*fin)(void); int (*init)(void); + + int (*reset_br)(unsigned int udba, struct au_branch *br, int perm); + void (*fin_br)(struct au_branch *br); + int (*init_br)(struct au_branch *br, int perm); }; /* hnotify.c */ -int au_hn_alloc(struct au_hinode *hinode, struct inode *inode, - struct inode *h_inode); +int au_hn_alloc(struct au_hinode *hinode, struct inode *inode); void au_hn_free(struct au_hinode *hinode); void au_hn_ctl(struct au_hinode *hinode, int do_set); void au_hn_reset(struct inode *inode, unsigned int flags); int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, struct qstr *h_child_qstr, struct inode *h_child_inode); +int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm); +int au_hnotify_init_br(struct au_branch *br, int perm); +void au_hnotify_fin_br(struct au_branch *br); int __init au_hnotify_init(void); void au_hnotify_fin(void); @@ -469,8 +492,7 @@ void au_hn_init(struct au_hinode *hinode) #else static inline int au_hn_alloc(struct au_hinode *hinode __maybe_unused, - struct inode *inode __maybe_unused, - struct inode *h_inode __maybe_unused) + struct inode *inode __maybe_unused) { return -EOPNOTSUPP; } @@ -480,6 +502,12 @@ AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused, int do_set __maybe_unused) AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused, unsigned int flags __maybe_unused) +AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused, + struct au_branch *br __maybe_unused, + int perm __maybe_unused) +AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused, + int perm __maybe_unused) +AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused) AuStubInt0(__init au_hnotify_init, void) AuStubVoid(au_hnotify_fin, void) AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused) diff --git a/ubuntu/aufs/opts.c b/ubuntu/aufs/opts.c index bdb2712f628..b41f5a1944c 100644 --- a/ubuntu/aufs/opts.c +++ b/ubuntu/aufs/opts.c @@ -299,7 +299,7 @@ static int au_wbr_mfs_sec(substring_t *arg, char *str, int n, err; err = 0; - if (!match_int(arg, &n) && 0 <= n && n <= MAX_SEC_IN_JIFFIES) + if (!match_int(arg, &n) && 0 <= n && n <= AUFS_MFS_MAX_SEC) create->mfs_second = n; else { pr_err("bad integer in %s\n", str); @@ -334,7 +334,7 @@ au_wbr_create_val(char *str, struct au_opt_wbr_create *create) /*FALLTHROUGH*/ case AuWbrCreate_MFS: case AuWbrCreate_PMFS: - create->mfs_second = AUFS_MFS_SECOND_DEF; + create->mfs_second = AUFS_MFS_DEF_SEC; break; case AuWbrCreate_MFSV: case AuWbrCreate_PMFSV: @@ -929,9 +929,16 @@ int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) break; case Opt_rdcache: - if (unlikely(match_int(&a->args[0], &opt->rdcache) - || opt->rdcache > MAX_SEC_IN_JIFFIES)) + if (unlikely(match_int(&a->args[0], &n))) { + pr_err("bad integer in %s\n", opt_str); + break; + } + if (unlikely(n > AUFS_RDCACHE_MAX)) { + pr_err("rdcache must be smaller than %d\n", + AUFS_RDCACHE_MAX); break; + } + opt->rdcache = n; err = 0; opt->type = token; break; @@ -1265,8 +1272,7 @@ static int au_opt_br(struct super_block *sb, struct au_opt *opt, au_ftest_opts(opts->flags, REMOUNT)); if (!err) { err = 1; - au_fset_opts(opts->flags, REFRESH_DIR); - au_fset_opts(opts->flags, REFRESH_NONDIR); + au_fset_opts(opts->flags, REFRESH); } break; @@ -1277,8 +1283,7 @@ static int au_opt_br(struct super_block *sb, struct au_opt *opt, if (!err) { err = 1; au_fset_opts(opts->flags, TRUNC_XIB); - au_fset_opts(opts->flags, REFRESH_DIR); - au_fset_opts(opts->flags, REFRESH_NONDIR); + au_fset_opts(opts->flags, REFRESH); } break; @@ -1289,10 +1294,8 @@ static int au_opt_br(struct super_block *sb, struct au_opt *opt, &do_refresh); if (!err) { err = 1; - if (do_refresh) { - au_fset_opts(opts->flags, REFRESH_DIR); - au_fset_opts(opts->flags, REFRESH_NONDIR); - } + if (do_refresh) + au_fset_opts(opts->flags, REFRESH); } break; } @@ -1451,10 +1454,11 @@ int au_opts_mount(struct super_block *sb, struct au_opts *opts) { int err; unsigned int tmp; - aufs_bindex_t bend; + aufs_bindex_t bindex, bend; struct au_opt *opt; struct au_opt_xino *opt_xino, xino; struct au_sbinfo *sbinfo; + struct au_branch *br; SiMustWriteLock(sb); @@ -1515,8 +1519,18 @@ int au_opts_mount(struct super_block *sb, struct au_opts *opts) } /* restore udba */ + tmp &= AuOptMask_UDBA; sbinfo->si_mntflags &= ~AuOptMask_UDBA; - sbinfo->si_mntflags |= (tmp & AuOptMask_UDBA); + sbinfo->si_mntflags |= tmp; + bend = au_sbend(sb); + for (bindex = 0; bindex <= bend; bindex++) { + br = au_sbr(sb, bindex); + err = au_hnotify_reset_br(tmp, br, br->br_perm); + if (unlikely(err)) + AuIOErr("hnotify failed on br %d, %d, ignored\n", + bindex, err); + /* go on even if err */ + } if (au_opt_test(tmp, UDBA_HNOTIFY)) { struct inode *dir = sb->s_root->d_inode; au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); @@ -1565,9 +1579,9 @@ int au_opts_remount(struct super_block *sb, struct au_opts *opts) } /* will be handled by the caller */ - if (!au_ftest_opts(opts->flags, REFRESH_DIR) + if (!au_ftest_opts(opts->flags, REFRESH) && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO))) - au_fset_opts(opts->flags, REFRESH_DIR); + au_fset_opts(opts->flags, REFRESH); AuDbg("status 0x%x\n", opts->flags); return err; diff --git a/ubuntu/aufs/opts.h b/ubuntu/aufs/opts.h index 03ce3351c49..e788621447d 100644 --- a/ubuntu/aufs/opts.h +++ b/ubuntu/aufs/opts.h @@ -170,10 +170,9 @@ struct au_opt { /* opts flags */ #define AuOpts_REMOUNT 1 -#define AuOpts_REFRESH_DIR (1 << 1) -#define AuOpts_REFRESH_NONDIR (1 << 2) -#define AuOpts_TRUNC_XIB (1 << 3) -#define AuOpts_REFRESH_DYAOP (1 << 4) +#define AuOpts_REFRESH (1 << 1) +#define AuOpts_TRUNC_XIB (1 << 2) +#define AuOpts_REFRESH_DYAOP (1 << 3) #define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) #define au_fset_opts(flags, name) \ do { (flags) |= AuOpts_##name; } while (0) diff --git a/ubuntu/aufs/plink.c b/ubuntu/aufs/plink.c index dd7eb1e49f9..d3995bcf5a5 100644 --- a/ubuntu/aufs/plink.c +++ b/ubuntu/aufs/plink.c @@ -67,10 +67,8 @@ int au_plink_maint(struct super_block *sb, int flags) goto out; if (au_ftest_lock(flags, NOPLMW)) { - /* - * todo: debug by lockdep, if there is no i_mutex lock in VFS, - * we don't need to wait. - */ + /* if there is no i_mutex lock in VFS, we don't need to wait */ + /* AuDebugOn(!lockdep_depth(current)); */ while (sbi->si_plink_maint_pid) { si_read_unlock(sb); /* gave up wake_up_bit() */ diff --git a/ubuntu/aufs/rdu.c b/ubuntu/aufs/rdu.c index fedbc384a1a..627232894e0 100644 --- a/ubuntu/aufs/rdu.c +++ b/ubuntu/aufs/rdu.c @@ -162,14 +162,14 @@ static int au_rdu(struct file *file, struct aufs_rdu *rdu) if (unlikely(err)) goto out; #endif - err = -ENOENT; - if (unlikely(IS_DEADDIR(inode))) - goto out_mtx; arg.sb = inode->i_sb; err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM); if (unlikely(err)) goto out_mtx; + err = au_alive_dir(dentry); + if (unlikely(err)) + goto out_si; /* todo: reval? */ fi_read_lock(file); @@ -215,6 +215,7 @@ static int au_rdu(struct file *file, struct aufs_rdu *rdu) out_unlock: fi_read_unlock(file); +out_si: si_read_unlock(arg.sb); out_mtx: mutex_unlock(&inode->i_mutex); diff --git a/ubuntu/aufs/sbinfo.c b/ubuntu/aufs/sbinfo.c index 1eefcb816b3..42783dcd873 100644 --- a/ubuntu/aufs/sbinfo.c +++ b/ubuntu/aufs/sbinfo.c @@ -87,7 +87,6 @@ int au_si_alloc(struct super_block *sb) INIT_RADIX_TREE(&sbinfo->au_si_pid.tree, GFP_ATOMIC | __GFP_NOFAIL); atomic_long_set(&sbinfo->si_ninodes, 0); - atomic_long_set(&sbinfo->si_nfiles, 0); sbinfo->si_bend = -1; @@ -221,15 +220,26 @@ int si_write_lock(struct super_block *sb, int flags) int aufs_read_lock(struct dentry *dentry, int flags) { int err; + struct super_block *sb; + + sb = dentry->d_sb; + err = si_read_lock(sb, flags); + if (unlikely(err)) + goto out; + + if (au_ftest_lock(flags, DW)) + di_write_lock_child(dentry); + else + di_read_lock_child(dentry, flags); - err = si_read_lock(dentry->d_sb, flags); - if (!err) { - if (au_ftest_lock(flags, DW)) - di_write_lock_child(dentry); - else - di_read_lock_child(dentry, flags); + if (au_ftest_lock(flags, GEN)) { + err = au_digen_test(dentry, au_sigen(sb)); + AuDebugOn(!err && au_dbrange_test(dentry)); + if (unlikely(err)) + aufs_read_unlock(dentry, flags); } +out: return err; } @@ -257,10 +267,29 @@ void aufs_write_unlock(struct dentry *dentry) int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) { int err; + unsigned int sigen; + struct super_block *sb; + + sb = d1->d_sb; + err = si_read_lock(sb, flags); + if (unlikely(err)) + goto out; - err = si_read_lock(d1->d_sb, flags); - if (!err) - di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); + di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); + + if (au_ftest_lock(flags, GEN)) { + sigen = au_sigen(sb); + err = au_digen_test(d1, sigen); + AuDebugOn(!err && au_dbrange_test(d1)); + if (!err) { + err = au_digen_test(d2, sigen); + AuDebugOn(!err && au_dbrange_test(d2)); + } + if (unlikely(err)) + aufs_read_and_write_unlock2(d1, d2); + } + +out: return err; } diff --git a/ubuntu/aufs/super.c b/ubuntu/aufs/super.c index 7aba5f24309..008702ddecb 100644 --- a/ubuntu/aufs/super.c +++ b/ubuntu/aufs/super.c @@ -38,6 +38,7 @@ static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused) c = au_cache_alloc_icntnr(); if (c) { + au_icntnr_init(c); c->vfs_inode.i_version = 1; /* sigen(sb); */ c->iinfo.ii_hinode = NULL; return &c->vfs_inode; @@ -196,7 +197,7 @@ out: /* seq_file will re-call me in case of too long string */ static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt) { - int err, n; + int err; unsigned int mnt_flags, v; struct super_block *sb; struct au_sbinfo *sbinfo; @@ -254,8 +255,8 @@ static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt) AuUInt(DIRWH, dirwh, sbinfo->si_dirwh); - n = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC; - AuUInt(RDCACHE, rdcache, n); + v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC; + AuUInt(RDCACHE, rdcache, v); AuUInt(RDBLK, rdblk, sbinfo->si_rdblk); AuUInt(RDHASH, rdhash, sbinfo->si_rdhash); @@ -476,92 +477,84 @@ void au_iarray_free(struct inode **a, unsigned long long max) /* * refresh dentry and inode at remount time. */ -static int do_refresh(struct dentry *dentry, mode_t type, - unsigned int dir_flags) +/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */ +static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags, + struct dentry *parent) { int err; - struct dentry *parent; di_write_lock_child(dentry); - parent = dget_parent(dentry); di_read_lock_parent(parent, AuLock_IR); - - /* returns the number of positive dentries */ - err = au_refresh_hdentry(dentry, type); - if (err >= 0) { - struct inode *inode = dentry->d_inode; - err = au_refresh_hinode(inode, dentry); - if (!err && type == S_IFDIR) - au_hn_reset(inode, dir_flags); - } - if (unlikely(err)) - pr_err("unrecoverable error %d, %.*s\n", - err, AuDLNPair(dentry)); - + err = au_refresh_dentry(dentry, parent); + if (!err && dir_flags) + au_hn_reset(dentry->d_inode, dir_flags); di_read_unlock(parent, AuLock_IR); - dput(parent); di_write_unlock(dentry); return err; } -static int test_dir(struct dentry *dentry, void *arg __maybe_unused) +static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen, + struct au_sbinfo *sbinfo, + const unsigned int dir_flags) { - return S_ISDIR(dentry->d_inode->i_mode); + int err; + struct dentry *parent; + struct inode *inode; + + err = 0; + parent = dget_parent(dentry); + if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) { + inode = dentry->d_inode; + if (inode) { + if (!S_ISDIR(inode->i_mode)) + err = au_do_refresh(dentry, /*dir_flags*/0, + parent); + else { + err = au_do_refresh(dentry, dir_flags, parent); + if (unlikely(err)) + au_fset_si(sbinfo, FAILED_REFRESH_DIR); + } + } else + err = au_do_refresh(dentry, /*dir_flags*/0, parent); + AuDbgDentry(dentry); + } + dput(parent); + + AuTraceErr(err); + return err; } -/* gave up consolidating with refresh_nondir() */ -static int refresh_dir(struct dentry *root, unsigned int sigen) +static int au_refresh_d(struct super_block *sb) { int err, i, j, ndentry, e; + unsigned int sigen; struct au_dcsub_pages dpages; struct au_dpage *dpage; - struct dentry **dentries; - struct inode *inode; - const unsigned int flags = au_hi_flags(root->d_inode, /*isdir*/1); - - err = 0; - list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list) - if (S_ISDIR(inode->i_mode) && au_iigen(inode) != sigen) { - ii_write_lock_child(inode); - e = au_refresh_hinode_self(inode, /*do_attr*/1); - ii_write_unlock(inode); - if (unlikely(e)) { - AuDbg("e %d, i%lu\n", e, inode->i_ino); - if (!err) - err = e; - /* go on even if err */ - } - } + struct dentry **dentries, *d; + struct au_sbinfo *sbinfo; + struct dentry *root = sb->s_root; + const unsigned int dir_flags = au_hi_flags(root->d_inode, /*isdir*/1); - e = au_dpages_init(&dpages, GFP_NOFS); - if (unlikely(e)) { - if (!err) - err = e; + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) goto out; - } - e = au_dcsub_pages(&dpages, root, test_dir, NULL); - if (unlikely(e)) { - if (!err) - err = e; + err = au_dcsub_pages(&dpages, root, NULL, NULL); + if (unlikely(err)) goto out_dpages; - } - for (i = 0; !e && i < dpages.ndpage; i++) { + sigen = au_sigen(sb); + sbinfo = au_sbi(sb); + for (i = 0; i < dpages.ndpage; i++) { dpage = dpages.dpages + i; dentries = dpage->dentries; ndentry = dpage->ndentry; - for (j = 0; !e && j < ndentry; j++) { - struct dentry *d; - + for (j = 0; j < ndentry; j++) { d = dentries[j]; - au_dbg_verify_dir_parent(d, sigen); - if (au_digen(d) != sigen) { - e = do_refresh(d, S_IFDIR, flags); - if (unlikely(e && !err)) - err = e; - /* break on err */ - } + e = au_do_refresh_d(d, sigen, sbinfo, dir_flags); + if (unlikely(e && !err)) + err = e; + /* go on even err */ } } @@ -571,113 +564,82 @@ out: return err; } -static int test_nondir(struct dentry *dentry, void *arg __maybe_unused) +static int au_refresh_i(struct super_block *sb) { - return !S_ISDIR(dentry->d_inode->i_mode); -} + int err, e; + unsigned int sigen; + unsigned long long max, ull; + struct inode *inode, **array; -static int refresh_nondir(struct dentry *root, unsigned int sigen, - int do_dentry) -{ - int err, i, j, ndentry, e; - struct au_dcsub_pages dpages; - struct au_dpage *dpage; - struct dentry **dentries; - struct inode *inode; + array = au_iarray_alloc(sb, &max); + err = PTR_ERR(array); + if (IS_ERR(array)) + goto out; err = 0; - list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list) - if (!S_ISDIR(inode->i_mode) && au_iigen(inode) != sigen) { + sigen = au_sigen(sb); + for (ull = 0; ull < max; ull++) { + inode = array[ull]; + if (au_iigen(inode) != sigen) { ii_write_lock_child(inode); - e = au_refresh_hinode_self(inode, /*do_attr*/1); + e = au_refresh_hinode_self(inode); ii_write_unlock(inode); if (unlikely(e)) { - AuDbg("e %d, i%lu\n", e, inode->i_ino); + pr_err("error %d, i%lu\n", e, inode->i_ino); if (!err) err = e; /* go on even if err */ } } - - if (!do_dentry) - goto out; - - e = au_dpages_init(&dpages, GFP_NOFS); - if (unlikely(e)) { - if (!err) - err = e; - goto out; - } - e = au_dcsub_pages(&dpages, root, test_nondir, NULL); - if (unlikely(e)) { - if (!err) - err = e; - goto out_dpages; } - for (i = 0; i < dpages.ndpage; i++) { - dpage = dpages.dpages + i; - dentries = dpage->dentries; - ndentry = dpage->ndentry; - for (j = 0; j < ndentry; j++) { - struct dentry *d; + au_iarray_free(array, max); - d = dentries[j]; - au_dbg_verify_nondir_parent(d, sigen); - inode = d->d_inode; - if (inode && au_digen(d) != sigen) { - e = do_refresh(d, inode->i_mode & S_IFMT, - /*dir_flags*/0); - if (unlikely(e && !err)) - err = e; - /* go on even err */ - } - } - } - -out_dpages: - au_dpages_free(&dpages); out: return err; } -static void au_remount_refresh(struct super_block *sb, unsigned int flags) +static void au_remount_refresh(struct super_block *sb) { - int err; - unsigned int sigen; - struct au_sbinfo *sbinfo; + int err, e; + unsigned int udba; + aufs_bindex_t bindex, bend; struct dentry *root; struct inode *inode; + struct au_branch *br; au_sigen_inc(sb); - sigen = au_sigen(sb); - sbinfo = au_sbi(sb); - au_fclr_si(sbinfo, FAILED_REFRESH_DIRS); + au_fclr_si(au_sbi(sb), FAILED_REFRESH_DIR); root = sb->s_root; DiMustNoWaiters(root); inode = root->d_inode; IiMustNoWaiters(inode); - au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1)); - di_write_unlock(root); - - err = refresh_dir(root, sigen); - if (unlikely(err)) { - au_fset_si(sbinfo, FAILED_REFRESH_DIRS); - pr_warning("Refreshing directories failed, ignored (%d)\n", - err); - } - if (au_ftest_opts(flags, REFRESH_NONDIR)) { - err = refresh_nondir(root, sigen, !err); + udba = au_opt_udba(sb); + bend = au_sbend(sb); + for (bindex = 0; bindex <= bend; bindex++) { + br = au_sbr(sb, bindex); + err = au_hnotify_reset_br(udba, br, br->br_perm); if (unlikely(err)) - pr_warning("Refreshing non-directories failed, ignored" - "(%d)\n", err); + AuIOErr("hnotify failed on br %d, %d, ignored\n", + bindex, err); + /* go on even if err */ } + au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1)); + di_write_unlock(root); + err = au_refresh_d(sb); + e = au_refresh_i(sb); + if (unlikely(e && !err)) + err = e; /* aufs_write_lock() calls ..._child() */ di_write_lock_child(root); - au_cpup_attr_all(root->d_inode, /*force*/1); + + au_cpup_attr_all(inode, /*force*/1); + + if (unlikely(err)) + AuIOErr("refresh failed, ignored, %d\n", err); } /* stop extra interpretation of errno in mount(8), and strange error messages */ @@ -742,9 +704,8 @@ static int aufs_remount_fs(struct super_block *sb, int *flags, char *data) err = au_opts_remount(sb, &opts); au_opts_free(&opts); - if (au_ftest_opts(opts.flags, REFRESH_DIR) - || au_ftest_opts(opts.flags, REFRESH_NONDIR)) - au_remount_refresh(sb, opts.flags); + if (au_ftest_opts(opts.flags, REFRESH)) + au_remount_refresh(sb); if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) { mntflags = au_mntflags(sb); @@ -932,7 +893,7 @@ static void aufs_kill_sb(struct super_block *sb) sbinfo->si_wbr_create_ops->fin(sb); if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) { au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE); - au_remount_refresh(sb, /*flags*/0); + au_remount_refresh(sb); } if (au_opt_test(sbinfo->si_mntflags, PLINK)) au_plink_put(sb, /*verbose*/1); diff --git a/ubuntu/aufs/super.h b/ubuntu/aufs/super.h index 0cc087beadb..1930ce71a66 100644 --- a/ubuntu/aufs/super.h +++ b/ubuntu/aufs/super.h @@ -179,7 +179,7 @@ struct au_sbinfo { * then try refreshing dirs at access time again. * if it is false, refreshing dirs at access time is unnecesary */ -#define AuSi_FAILED_REFRESH_DIRS 1 +#define AuSi_FAILED_REFRESH_DIR 1 static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi, unsigned int flag) { @@ -212,6 +212,7 @@ static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi, #define AuLock_DIR (1 << 4) /* target is a dir */ #define AuLock_NOPLM (1 << 5) /* return err in plm mode */ #define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */ +#define AuLock_GEN (1 << 7) /* test digen/iigen */ #define au_ftest_lock(flags, name) ((flags) & AuLock_##name) #define au_fset_lock(flags, name) \ do { (flags) |= AuLock_##name; } while (0) diff --git a/ubuntu/aufs/sysrq.c b/ubuntu/aufs/sysrq.c index c18829b97eb..452fe3a25fc 100644 --- a/ubuntu/aufs/sysrq.c +++ b/ubuntu/aufs/sysrq.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> /* #include <linux/sysrq.h> */ +#include <linux/writeback.h> #include "aufs.h" /* ---------------------------------------------------------------------- */ @@ -42,18 +43,45 @@ static void sysrq_sb(struct super_block *sb) printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); printk(KERN_WARNING AUFS_NAME ": superblock\n"); au_dpri_sb(sb); + +#if 0 printk(KERN_WARNING AUFS_NAME ": root dentry\n"); au_dpri_dentry(sb->s_root); printk(KERN_WARNING AUFS_NAME ": root inode\n"); au_dpri_inode(sb->s_root->d_inode); +#endif + #if 0 - struct inode *i; - printk(KERN_WARNING AUFS_NAME ": isolated inode\n"); - spin_lock(&inode_lock); - list_for_each_entry(i, &sb->s_inodes, i_sb_list) - if (list_empty(&i->i_dentry)) - au_dpri_inode(i); - spin_unlock(&inode_lock); + do { + int err, i, j, ndentry; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + + err = au_dpages_init(&dpages, GFP_ATOMIC); + if (unlikely(err)) + break; + err = au_dcsub_pages(&dpages, sb->s_root, NULL, NULL); + if (!err) + for (i = 0; i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + ndentry = dpage->ndentry; + for (j = 0; j < ndentry; j++) + au_dpri_dentry(dpage->dentries[j]); + } + au_dpages_free(&dpages); + } while (0); +#endif + +#if 1 + { + struct inode *i; + printk(KERN_WARNING AUFS_NAME ": isolated inode\n"); + spin_lock(&inode_lock); + list_for_each_entry(i, &sb->s_inodes, i_sb_list) + if (1 || list_empty(&i->i_dentry)) + au_dpri_inode(i); + spin_unlock(&inode_lock); + } #endif printk(KERN_WARNING AUFS_NAME ": files\n"); lg_global_lock(files_lglock); @@ -80,10 +108,12 @@ static void au_sysrq(int key __maybe_unused) { struct au_sbinfo *sbinfo; + lockdep_off(); spin_lock(&au_sbilist.spin); list_for_each_entry(sbinfo, &au_sbilist.head, si_list) sysrq_sb(sbinfo->si_sb); spin_unlock(&au_sbilist.spin); + lockdep_on(); } static struct sysrq_key_op au_sysrq_op = { diff --git a/ubuntu/aufs/vfsub.h b/ubuntu/aufs/vfsub.h index 4b44827c428..9a9f2669b34 100644 --- a/ubuntu/aufs/vfsub.h +++ b/ubuntu/aufs/vfsub.h @@ -91,6 +91,13 @@ static inline void vfsub_drop_nlink(struct inode *inode) drop_nlink(inode); } +static inline void vfsub_dead_dir(struct inode *inode) +{ + AuDebugOn(!S_ISDIR(inode->i_mode)); + inode->i_flags |= S_DEAD; + clear_nlink(inode); +} + /* ---------------------------------------------------------------------- */ int vfsub_update_h_iattr(struct path *h_path, int *did); diff --git a/ubuntu/aufs/wbr_policy.c b/ubuntu/aufs/wbr_policy.c index bb57ae520fc..9e4f1e48b4e 100644 --- a/ubuntu/aufs/wbr_policy.c +++ b/ubuntu/aufs/wbr_policy.c @@ -232,8 +232,7 @@ static int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex) if (unlikely(err)) goto out; parent = dget_parent(dentry); - err = au_dcsub_pages_rev(&dpages, parent, /*do_include*/0, /*test*/NULL, - /*arg*/NULL); + err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0); if (unlikely(err)) goto out_free; diff --git a/ubuntu/aufs/whout.c b/ubuntu/aufs/whout.c index 284b8cc7c35..bdc989bb9d2 100644 --- a/ubuntu/aufs/whout.c +++ b/ubuntu/aufs/whout.c @@ -123,6 +123,7 @@ struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, int i; char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN_MIN + 1], *name, *p; + /* strict atomic_t is unnecessary here */ static unsigned short cnt; struct qstr qs; @@ -607,8 +608,8 @@ out: if (wbr) atomic_dec(&wbr->wbr_wh_running); atomic_dec(&a->br->br_count); - au_nwt_done(&au_sbi(a->sb)->si_nowait); si_write_unlock(a->sb); + au_nwt_done(&au_sbi(a->sb)->si_nowait); kfree(arg); if (unlikely(err)) AuIOErr("err %d\n", err); @@ -901,6 +902,7 @@ struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp) } whtmp->dir = NULL; + whtmp->br = NULL; whtmp->wh_dentry = NULL; /* no estimation for dir size */ rdhash = au_sbi(sb)->si_rdhash; @@ -918,6 +920,8 @@ out: void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) { + if (whtmp->br) + atomic_dec(&whtmp->br->br_count); dput(whtmp->wh_dentry); iput(whtmp->dir); au_nhash_wh_free(&whtmp->whlist); @@ -969,7 +973,6 @@ int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, h_tmp.dentry = wh_dentry; h_tmp.mnt = br->br_mnt; err = vfsub_rmdir(h_dir, &h_tmp); - /* d_drop(h_dentry); */ } if (!err) { @@ -1029,10 +1032,9 @@ static void call_rmdir_whtmp(void *args) out: /* mutex_unlock(&a->dir->i_mutex); */ - atomic_dec(&a->br->br_count); - au_nwt_done(&au_sbi(sb)->si_nowait); - si_read_unlock(sb); au_whtmp_rmdir_free(a); + si_read_unlock(sb); + au_nwt_done(&au_sbi(sb)->si_nowait); if (unlikely(err)) AuIOErr("err %d\n", err); } diff --git a/ubuntu/aufs/wkq.c b/ubuntu/aufs/wkq.c index 9739d3a1377..c2a31f99211 100644 --- a/ubuntu/aufs/wkq.c +++ b/ubuntu/aufs/wkq.c @@ -160,6 +160,10 @@ int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args) } +/* + * Note: dget/dput() in func for aufs dentries are not supported. It will be a + * problem in a concurrent umounting. + */ int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb) { int err; diff --git a/ubuntu/aufs/xino.c b/ubuntu/aufs/xino.c index 84e745a1310..7c21a4aa00c 100644 --- a/ubuntu/aufs/xino.c +++ b/ubuntu/aufs/xino.c @@ -320,8 +320,8 @@ static void xino_do_trunc(void *_args) pr_warning("err b%d, (%d)\n", bindex, err); atomic_dec(&br->br_xino_running); atomic_dec(&br->br_count); - au_nwt_done(&au_sbi(sb)->si_nowait); si_write_unlock(sb); + au_nwt_done(&au_sbi(sb)->si_nowait); kfree(args); } diff --git a/ubuntu/include/linux/aufs_type.h b/ubuntu/include/linux/aufs_type.h index c9e2845a13c..6a67e6788e0 100644 --- a/ubuntu/include/linux/aufs_type.h +++ b/ubuntu/include/linux/aufs_type.h @@ -24,7 +24,7 @@ #include <linux/limits.h> #include <linux/types.h> -#define AUFS_VERSION "2.1-standalone.tree-37-rcN-20101122" +#define AUFS_VERSION "2.1-standalone.tree-37-rcN-20101220" /* todo? move this to linux-2.6.19/include/magic.h */ #define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') @@ -73,11 +73,13 @@ typedef __s16 aufs_bindex_t; #define AUFS_XINO_TRUNC_STEP 4 /* blocks */ #define AUFS_DIRWH_DEF 3 #define AUFS_RDCACHE_DEF 10 /* seconds */ +#define AUFS_RDCACHE_MAX 3600 /* seconds */ #define AUFS_RDBLK_DEF 512 /* bytes */ #define AUFS_RDHASH_DEF 32 #define AUFS_WKQ_NAME AUFS_NAME "d" #define AUFS_WKQ_PRE_NAME AUFS_WKQ_NAME "_pre" -#define AUFS_MFS_SECOND_DEF 30 /* seconds */ +#define AUFS_MFS_DEF_SEC 30 /* seconds */ +#define AUFS_MFS_MAX_SEC 3600 /* seconds */ #define AUFS_PLINK_WARN 100 /* number of plinks */ /* pseudo-link maintenace under /proc */ |