void au_cpup_attr_nlink(struct inode *inode, int force) { struct inode *h_inode; struct super_block *sb; aufs_bindex_t bindex, bend; LKTRTrace("i%lu\n", inode->i_ino); /* todo? IMustLock(inode); */ AuDebugOn(!inode->i_mode); sb = inode->i_sb; bindex = au_ibstart(inode); h_inode = au_h_iptr(inode, bindex); if (!force && !S_ISDIR(h_inode->i_mode) && au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(sb, inode)) return; inode->i_nlink = h_inode->i_nlink; /* * fewer nlink makes find(1) noisy, but larger nlink doesn't. * it may includes whplink directory. */ if (S_ISDIR(h_inode->i_mode)) { bend = au_ibend(inode); for (bindex++; bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); if (h_inode) au_add_nlink(inode, h_inode); } } }
/* dentry and super_block lock. call at entry point */ 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); if (au_ftest_lock(flags, GEN)) { err = au_digen_test(dentry, au_sigen(sb)); if (!au_opt_test(au_mntflags(sb), UDBA_NONE)) AuDebugOn(!err && au_dbrange_test(dentry)); else if (!err) err = au_dbrange_test(dentry); if (unlikely(err)) aufs_read_unlock(dentry, flags); } out: return err; }
int au_xigen_inc(struct inode *inode) { int err; loff_t pos; ssize_t sz; __u32 igen; struct super_block *sb; struct au_sbinfo *sbinfo; LKTRTrace("i%lu\n", (unsigned long)inode->i_ino); err = 0; sb = inode->i_sb; if (unlikely(!au_opt_test_xino(au_mntflags(sb)))) goto out; pos = inode->i_ino; pos *= sizeof(igen); igen = inode->i_generation + 1; sbinfo = au_sbi(sb); sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, sizeof(igen), &pos); if (sz == sizeof(igen)) goto out; /* success */ err = sz; if (unlikely(sz >= 0)) { err = -EIO; AuIOErr("xigen error (%zd)\n", sz); } out: AuTraceErr(err); return err; }
void au_dtime_revert(struct au_dtime *dt) { struct iattr attr; int err; struct au_hin_ignore ign[2]; struct vfsub_args vargs; LKTRTrace("%.*s\n", AuDLNPair(dt->dt_dentry)); attr.ia_atime = dt->dt_atime; attr.ia_mtime = dt->dt_mtime; attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET | ATTR_ATIME | ATTR_ATIME_SET; vfsub_args_init(&vargs, ign, au_test_dlgt(au_mntflags(dt->dt_dentry->d_sb)), 0); /* * IN_ATTRIB should be divided into * IN_ATTRIB_ATIME, IN_ATTRIB_MTIME ..., * and define all ORed new IN_ATTRIB macro. */ vfsub_ign_hinode(&vargs, IN_ATTRIB, dt->dt_hinode); vfsub_ign_hinode(&vargs, IN_ATTRIB, dt->dt_hdir); err = vfsub_notify_change(dt->dt_h_dentry, &attr, &vargs); if (unlikely(err)) AuWarn("restoring timestamps failed(%d). ignored\n", err); }
/* make sure the file is idle */ static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a) { int err, plinked; err = 0; plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); if (au_dbstart(a->dentry) == a->mvd_bsrc && au_dcount(a->dentry) == 1 && atomic_read(&a->inode->i_count) == 1 /* && a->mvd_h_src_inode->i_nlink == 1 */ && (!plinked || !au_plink_test(a->inode)) && a->inode->i_nlink == 1) goto out; err = -EBUSY; AU_MVD_PR(dmsg, "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", a->mvd_bsrc, au_dbstart(a->dentry), au_dcount(a->dentry), atomic_read(&a->inode->i_count), a->inode->i_nlink, a->mvd_h_src_inode->i_nlink, plinked, plinked ? au_plink_test(a->inode) : 0); out: AuTraceErr(err); return err; }
/* * simple tests for the del-entry operations. * following the checks in vfs, plus the parent-child relationship. */ int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_parent, int isdir) { int err; umode_t h_mode; struct dentry *h_dentry, *h_latest; struct inode *h_inode; h_dentry = au_h_dptr(dentry, bindex); if (d_really_is_positive(dentry)) { err = -ENOENT; if (unlikely(d_is_negative(h_dentry))) goto out; h_inode = d_inode(h_dentry); if (unlikely(!h_inode->i_nlink)) goto out; h_mode = h_inode->i_mode; if (!isdir) { err = -EISDIR; if (unlikely(S_ISDIR(h_mode))) goto out; } else if (unlikely(!S_ISDIR(h_mode))) { err = -ENOTDIR; goto out; } } else { /* rename(2) case */ err = -EIO; if (unlikely(d_is_positive(h_dentry))) goto out; } err = -ENOENT; /* expected parent dir is locked */ if (unlikely(h_parent != h_dentry->d_parent)) goto out; err = 0; /* * rmdir a dir may break the consistency on some filesystem. * let's try heavy test. */ err = -EACCES; if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1) && au_test_h_perm(d_inode(h_parent), MAY_EXEC | MAY_WRITE))) goto out; h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); err = -EIO; if (IS_ERR(h_latest)) goto out; if (h_latest == h_dentry) err = 0; dput(h_latest); out: return err; }
static int au_ready_to_write_wh(struct file *file, loff_t len, aufs_bindex_t bcpup) { int err; 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 && !h_inode) err = au_sio_cpup_wh(dentry, bcpup, len, file); else /* already copied-up after unlink */ err = au_reopen_wh(file, bcpup, hi_wh); if (!err && inode->i_nlink > 1 && au_opt_test(au_mntflags(dentry->d_sb), PLINK)) au_plink_append(inode, bcpup, au_h_dptr(dentry, bcpup)); return err; }
static int au_test_shwh(struct super_block *sb, const struct qstr *name) { if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) return -EPERM; return 0; }
void au_cpup_attr_nlink(struct inode *inode, int force) { struct inode *h_inode; struct super_block *sb; aufs_bindex_t bindex, bend; sb = inode->i_sb; bindex = au_ibstart(inode); h_inode = au_h_iptr(inode, bindex); if (!force && !S_ISDIR(h_inode->i_mode) && au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(inode)) return; /* * 0 can happen in revalidating. * h_inode->i_mutex is not held, but it is harmless since once i_nlink * reaches 0, it will never become positive. */ set_nlink(inode, h_inode->i_nlink); /* * fewer nlink makes find(1) noisy, but larger nlink doesn't. * it may includes whplink directory. */ if (S_ISDIR(h_inode->i_mode)) { bend = au_ibend(inode); for (bindex++; bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); if (h_inode) au_add_nlink(inode, h_inode); } } }
/* * if valid returns 1, otherwise 0. */ static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { int valid, err; unsigned int sigen; unsigned char do_udba; struct super_block *sb; struct inode *inode; err = -EINVAL; sb = dentry->d_sb; inode = dentry->d_inode; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW); 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; AuDebugOn(au_digen(dentry) != sigen); } if (inode && au_iigen(inode) != sigen) { AuDebugOn(IS_ROOT(dentry)); err = au_refresh_hinode(inode, dentry); if (unlikely(err)) goto out_dgrade; AuDebugOn(au_iigen(inode) != sigen); } di_downgrade_lock(dentry, AuLock_IR); AuDebugOn(au_digen(dentry) != sigen); AuDebugOn(inode && au_iigen(inode) != sigen); err = -EINVAL; do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); if (do_udba && inode) { aufs_bindex_t bstart = au_ibstart(inode); if (bstart >= 0 && au_test_higen(inode, au_h_iptr(inode, bstart))) goto out; } 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 */ err = -EIO; goto out; out_dgrade: di_downgrade_lock(dentry, AuLock_IR); out: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); valid = !err; if (!valid) AuDbg("%.*s invalid\n", AuDLNPair(dentry)); return valid; }
static void au_ren_refresh(struct au_ren_args *a) { aufs_bindex_t bend, bindex; struct dentry *d, *h_d; 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); for (bindex = a->btgt + 1; bindex <= bend; bindex++) { h_d = au_h_dptr(d, bindex); if (h_d) au_set_h_dptr(d, bindex, NULL); } au_set_dbend(d, a->btgt); sb = d->d_sb; i = a->src_inode; if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) return; /* success */ bend = au_ibend(i); for (bindex = a->btgt + 1; bindex <= bend; bindex++) { h_i = au_h_iptr(i, bindex); if (h_i) { au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); /* ignore this error */ au_set_h_iptr(i, bindex, NULL, 0); } } au_set_ibend(i, a->btgt); }
int au_xigen_new(struct inode *inode) { int err; loff_t pos; ssize_t sz; struct super_block *sb; struct au_sbinfo *sbinfo; struct file *file; LKTRTrace("i%lu\n", inode->i_ino); err = 0; /* todo: dirty, at mount time */ if (inode->i_ino == AUFS_ROOT_INO) goto out; sb = inode->i_sb; if (unlikely(!au_opt_test_xino(au_mntflags(sb)))) goto out; err = -EFBIG; pos = inode->i_ino; if (unlikely(Au_LOFF_MAX / sizeof(inode->i_generation) - 1 < pos)) { AuIOErr1("too large i%lld\n", pos); goto out; } pos *= sizeof(inode->i_generation); err = 0; sbinfo = au_sbi(sb); file = sbinfo->si_xigen; BUG_ON(!file); if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(inode->i_generation)) { inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, sizeof(inode->i_generation), &pos); } else sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, sizeof(inode->i_generation), &pos); if (sz == sizeof(inode->i_generation)) goto out; /* success */ err = sz; if (unlikely(sz >= 0)) { err = -EIO; AuIOErr("xigen error (%zd)\n", sz); } out: AuTraceErr(err); return err; }
int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) { int err; SiMustAnyLock(sb); err = 0; if (au_opt_test(au_mntflags(sb), XINO)) { err = au_xino_path(seq, au_sbi(sb)->si_xib); seq_putc(seq, '\n'); } return err; }
void au_plink_list(struct super_block *sb) { struct au_sbinfo *sbinfo; struct list_head *plink_list; struct pseudo_link *plink; SiMustAnyLock(sb); sbinfo = au_sbi(sb); AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); plink_list = &sbinfo->si_plink.head; rcu_read_lock(); list_for_each_entry_rcu(plink, plink_list, list) AuDbg("%lu\n", plink->inode->i_ino); rcu_read_unlock(); }
/* * extended version of au_h_dptr(). * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or * error. */ struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex) { struct dentry *h_dentry; struct inode *inode, *h_inode; inode = dentry->d_inode; AuDebugOn(!inode); h_dentry = NULL; if (au_dbstart(dentry) <= bindex && bindex <= au_dbend(dentry)) h_dentry = au_h_dptr(dentry, bindex); if (h_dentry && !au_d_linkable(h_dentry)) { dget(h_dentry); goto out; /* success */ } AuDebugOn(bindex < au_ibstart(inode)); AuDebugOn(au_ibend(inode) < bindex); h_inode = au_h_iptr(inode, bindex); h_dentry = d_find_alias(h_inode); if (h_dentry) { if (!IS_ERR(h_dentry)) { if (!au_d_linkable(h_dentry)) goto out; /* success */ dput(h_dentry); } else goto out; } if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) { h_dentry = au_plink_lkup(inode, bindex); AuDebugOn(!h_dentry); if (!IS_ERR(h_dentry)) { if (!au_d_hashed_positive(h_dentry)) goto out; /* success */ dput(h_dentry); h_dentry = NULL; } } out: AuDbgDentry(h_dentry); return h_dentry; }
static struct dentry * au_h_dget_any(struct dentry *dentry, aufs_bindex_t *bindex) { struct dentry *h_dentry; struct inode *inode, *h_inode; struct super_block *sb; aufs_bindex_t ib, db; /* must be positive dentry */ inode = dentry->d_inode; LKTRTrace("%.*s, i%lu\n", AuDLNPair(dentry), inode->i_ino); sb = dentry->d_sb; db = au_dbstart(dentry); ib = au_ibstart(inode); if (db == ib) { *bindex = db; h_dentry = dget(au_h_dptr(dentry, db)); if (h_dentry) goto out; /* success */ } *bindex = ib; h_inode = au_h_iptr(inode, ib); h_dentry = d_find_alias(h_inode); if (h_dentry) goto out; /* success */ #if 0 if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(sb, inode)) { h_dentry = au_plink_lkup(sb, ib, inode); if (IS_ERR(h_dentry)) goto out; AuDebugOn(!h_dentry->d_inode); goto out; /* success */ } #endif h_dentry = dget(au_hi_wh(inode, ib)); out: AuTraceErrPtr(h_dentry); return h_dentry; }
/* intialize a new branch */ static int au_br_init(struct au_branch *br, struct super_block *sb, struct au_opt_add *add) { int err; err = 0; memset(&br->br_xino, 0, sizeof(br->br_xino)); mutex_init(&br->br_xino.xi_nondir_mtx); br->br_perm = add->perm; br->br_mnt = add->path.mnt; /* set first, mntget() later */ spin_lock_init(&br->br_dykey_lock); memset(br->br_dykey, 0, sizeof(br->br_dykey)); atomic_set(&br->br_count, 0); br->br_xino_upper = AUFS_XINO_TRUNC_INIT; atomic_set(&br->br_xino_running, 0); br->br_id = au_new_br_id(sb); AuDebugOn(br->br_id < 0); if (au_br_writable(add->perm)) { err = au_wbr_init(br, sb, add->perm, &add->path); if (unlikely(err)) goto out_err; } if (au_opt_test(au_mntflags(sb), XINO)) { err = au_xino_br(sb, br, add->path.dentry->d_inode->i_ino, au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1); if (unlikely(err)) { AuDebugOn(br->br_xino.xi_file); goto out_err; } } sysaufs_br_init(br); mntget(add->path.mnt); goto out; /* success */ out_err: br->br_mnt = NULL; out: return err; }
int aufs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st) { int err; aufs_bindex_t bindex; struct inode *inode; struct dentry *h_dentry; struct super_block *sb; unsigned int mnt_flags; LKTRTrace("%.*s\n", AuDLNPair(dentry)); inode = dentry->d_inode; sb = dentry->d_sb; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR); /* todo: refine it */ mnt_flags = au_mntflags(sb); if (au_opt_test(mnt_flags, PLINK) && au_plink_test(sb, inode)) goto plinked; h_dentry = au_h_dget_any(dentry, &bindex); err = PTR_ERR(h_dentry); if (IS_ERR(h_dentry)) goto out; err = -ENOENT; if (h_dentry->d_inode) err = vfsub_getattr(au_sbr_mnt(sb, bindex), h_dentry, st, au_test_dlgt(mnt_flags)); dput(h_dentry); if (!err) { au_cpup_attr_all(inode); plinked: generic_fillattr(inode, st); } out: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); return err; }
/* * initialize the whiteout base file/dir for @br. */ int au_wh_init(struct au_branch *br, struct super_block *sb) { int err, i; const unsigned char do_plink = !!au_opt_test(au_mntflags(sb), PLINK); struct inode *h_dir; struct path path = br->br_path; struct dentry *h_root = path.dentry; struct au_wbr *wbr = br->br_wbr; static const struct qstr base_name[] = { [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, sizeof(AUFS_BASE_NAME) - 1), [AuBrWh_PLINK] = QSTR_INIT(AUFS_PLINKDIR_NAME, sizeof(AUFS_PLINKDIR_NAME) - 1), [AuBrWh_ORPH] = QSTR_INIT(AUFS_ORPHDIR_NAME, sizeof(AUFS_ORPHDIR_NAME) - 1) }; struct au_wh_base base[] = { [AuBrWh_BASE] = { .name = base_name + AuBrWh_BASE, .dentry = NULL },
/* is the inode pseudo-linked? */ int au_plink_test(struct inode *inode) { int found; struct au_sbinfo *sbinfo; struct list_head *plink_list; struct pseudo_link *plink; sbinfo = au_sbi(inode->i_sb); AuRwMustAnyLock(&sbinfo->si_rwsem); AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK)); found = 0; plink_list = &sbinfo->si_plink.head; rcu_read_lock(); list_for_each_entry_rcu(plink, plink_list, list) if (plink->inode == inode) { found = 1; break; } rcu_read_unlock(); return found; }
void au_cpup_attr_nlink(struct inode *inode, int force) { struct inode *h_inode; struct super_block *sb; aufs_bindex_t bindex, bend; sb = inode->i_sb; bindex = au_ibstart(inode); h_inode = au_h_iptr(inode, bindex); if (!force && !S_ISDIR(h_inode->i_mode) && au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(inode)) return; /* * 0 can happen in revalidating. * h_inode->i_mutex may not be held here, but it is harmless since once * i_nlink reaches 0, it will never become positive except O_TMPFILE * case. * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause * the incorrect link count. */ set_nlink(inode, h_inode->i_nlink); /* * fewer nlink makes find(1) noisy, but larger nlink doesn't. * it may includes whplink directory. */ if (S_ISDIR(h_inode->i_mode)) { bend = au_ibend(inode); for (bindex++; bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); if (h_inode) au_add_nlink(inode, h_inode); } } }
void au_xigen_inc(struct inode *inode) { loff_t pos; ssize_t sz; __u32 igen; struct super_block *sb; struct au_sbinfo *sbinfo; sb = inode->i_sb; AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); sbinfo = au_sbi(sb); pos = inode->i_ino; pos *= sizeof(igen); igen = inode->i_generation + 1; sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, sizeof(igen), &pos); if (sz == sizeof(igen)) return; /* success */ if (unlikely(sz >= 0)) AuIOErr("xigen error (%zd)\n", sz); }
static int au_file_refresh_by_inode(struct file *file, int *need_reopen) { int err; aufs_bindex_t bstart; struct au_pin pin; struct au_finfo *finfo; struct dentry *dentry, *parent, *hi_wh; struct inode *inode; struct super_block *sb; FiMustWriteLock(file); err = 0; finfo = au_fi(file); dentry = file->f_dentry; sb = dentry->d_sb; inode = dentry->d_inode; bstart = au_ibstart(inode); if (bstart == finfo->fi_btop || IS_ROOT(dentry)) goto out; parent = dget_parent(dentry); if (au_test_ro(sb, bstart, inode)) { di_read_lock_parent(parent, !AuLock_IR); err = AuWbrCopyup(au_sbi(sb), dentry); bstart = err; di_read_unlock(parent, !AuLock_IR); if (unlikely(err < 0)) goto out_parent; err = 0; } di_read_lock_parent(parent, AuLock_IR); hi_wh = au_hi_wh(inode, bstart); if (!S_ISDIR(inode->i_mode) && au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(inode) && !d_unhashed(dentry)) { err = au_test_and_cpup_dirs(dentry, bstart); if (unlikely(err)) goto out_unlock; /* always superio. */ err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (!err) err = au_sio_cpup_simple(dentry, bstart, -1, AuCpup_DTIME); au_unpin(&pin); } else if (hi_wh) { /* already copied-up after unlink */ err = au_reopen_wh(file, bstart, hi_wh); *need_reopen = 0; } out_unlock: di_read_unlock(parent, AuLock_IR); out_parent: dput(parent); out: return err; }
unsigned int au_opt_udba(struct super_block *sb) { return au_mntflags(sb) & AuOptMask_UDBA; }
static int set_inode(struct inode *inode, struct dentry *dentry) { int err; unsigned int flags; umode_t mode; aufs_bindex_t bindex, bstart, btail; unsigned char isdir; struct dentry *h_dentry; struct inode *h_inode; struct au_iinfo *iinfo; IiMustWriteLock(inode); err = 0; isdir = 0; bstart = au_dbstart(dentry); h_inode = au_h_dptr(dentry, bstart)->d_inode; mode = h_inode->i_mode; switch (mode & S_IFMT) { case S_IFREG: btail = au_dbtail(dentry); inode->i_op = &aufs_iop; inode->i_fop = &aufs_file_fop; inode->i_mapping->a_ops = &aufs_aop; break; case S_IFDIR: isdir = 1; btail = au_dbtaildir(dentry); inode->i_op = &aufs_dir_iop; inode->i_fop = &aufs_dir_fop; break; case S_IFLNK: btail = au_dbtail(dentry); inode->i_op = &aufs_symlink_iop; break; case S_IFBLK: case S_IFCHR: case S_IFIFO: case S_IFSOCK: btail = au_dbtail(dentry); inode->i_op = &aufs_iop; au_init_special_fop(inode, mode, h_inode->i_rdev); break; default: AuIOErr("Unknown file type 0%o\n", mode); err = -EIO; goto out; } /* do not set inotify for whiteouted dirs (SHWH mode) */ flags = au_hi_flags(inode, isdir); if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) && au_ftest_hi(flags, HINOTIFY) && dentry->d_name.len > AUFS_WH_PFX_LEN && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) au_fclr_hi(flags, HINOTIFY); iinfo = au_ii(inode); iinfo->ii_bstart = bstart; iinfo->ii_bend = btail; for (bindex = bstart; bindex <= btail; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (h_dentry) au_set_h_iptr(inode, bindex, au_igrab(h_dentry->d_inode), flags); } au_cpup_attr_all(inode, /*force*/1); out: return err; }
/* * simple tests for the adding inode operations. * following the checks in vfs, plus the parent-child relationship. */ int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_parent, int isdir, struct au_ndx *ndx) { int err, exist; struct dentry *h_dentry; struct inode *h_inode; umode_t h_mode; LKTRTrace("%.*s/%.*s, b%d, dir %d\n", AuDLNPair(h_parent), AuDLNPair(dentry), bindex, isdir); exist = !!dentry->d_inode; h_dentry = au_h_dptr(dentry, bindex); h_inode = h_dentry->d_inode; if (!exist) { err = -EEXIST; if (unlikely(h_inode)) goto out; } else { /* rename(2) case */ err = -EIO; if (unlikely(!h_inode || !h_inode->i_nlink)) goto out; h_mode = h_inode->i_mode; if (!isdir) { err = -EISDIR; if (unlikely(S_ISDIR(h_mode))) goto out; } else if (unlikely(!S_ISDIR(h_mode))) { err = -ENOTDIR; goto out; } } err = -EIO; /* expected parent dir is locked */ if (unlikely(h_parent != h_dentry->d_parent)) goto out; err = 0; if (au_opt_test(au_mntflags(dentry->d_sb), UDBA_INOTIFY)) { struct dentry *h_latest; struct qstr *qstr = &dentry->d_name; err = -EACCES; if (unlikely(au_test_h_perm (h_parent->d_inode, MAY_EXEC | MAY_WRITE, au_ftest_ndx(ndx->flags, DLGT)))) goto out; h_latest = au_sio_lkup_one(qstr->name, h_parent, qstr->len, ndx); err = PTR_ERR(h_latest); if (IS_ERR(h_latest)) goto out; err = -EIO; dput(h_latest); /* fuse d_revalidate always return 0 for negative dentries */ if (h_latest == h_dentry || au_test_fuse(h_dentry->d_sb)) err = 0; } out: AuTraceErr(err); return err; }
/* * final procedure of adding a new entry, except link(2). * remove whiteout, instantiate, copyup the parent dir's times and size * and update version. * if it failed, re-create the removed whiteout. */ static int epilog(struct inode *dir, aufs_bindex_t bindex, struct dentry *wh_dentry, struct dentry *dentry) { int err, rerr; aufs_bindex_t bwh; struct inode *inode, *h_dir; struct dentry *wh; struct au_ndx ndx; struct super_block *sb; LKTRTrace("wh %p, %.*s\n", wh_dentry, AuDLNPair(dentry)); sb = dentry->d_sb; bwh = -1; if (wh_dentry) { h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ IMustLock(h_dir); AuDebugOn(au_h_iptr(dir, bindex) != h_dir); bwh = au_dbwh(dentry); err = au_wh_unlink_dentry(au_hi(dir, bindex), wh_dentry, dentry, /*dlgt*/0); if (unlikely(err)) goto out; } inode = au_new_inode(dentry, /*must_new*/1); if (!IS_ERR(inode)) { d_instantiate(dentry, inode); dir = dentry->d_parent->d_inode; /* dir inode is locked */ IMustLock(dir); /* or always cpup dir mtime? */ if (au_ibstart(dir) == au_dbstart(dentry)) au_cpup_attr_timesizes(dir); dir->i_version++; return 0; /* success */ } err = PTR_ERR(inode); if (!wh_dentry) goto out; /* revert */ ndx.flags = 0; if (au_test_dlgt(au_mntflags(sb))) au_fset_ndx(ndx.flags, DLGT); ndx.nfsmnt = au_nfsmnt(sb, bwh); ndx.nd = NULL; /* ndx.br = NULL; */ /* dir inode is locked */ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent, &ndx); rerr = PTR_ERR(wh); if (IS_ERR(wh)) { AuIOErr("%.*s reverting whiteout failed(%d, %d)\n", AuDLNPair(dentry), err, rerr); err = -EIO; } else dput(wh); out: AuTraceErr(err); return err; }
/* * initial procedure of adding a new entry. * prepare writable branch and the parent dir, lock it, * lookup whiteout for the new entry. */ static struct dentry* lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, struct dentry *src_dentry, struct au_pin *pin, struct au_wr_dir_args *wr_dir_args) { struct dentry *wh_dentry, *h_parent; struct super_block *sb; int err; unsigned int mnt_flags; unsigned char pin_flags; aufs_bindex_t bstart, bcpup; struct au_ndx ndx; LKTRTrace("%.*s, src %p\n", AuDLNPair(dentry), src_dentry); bstart = au_dbstart(dentry); err = au_wr_dir(dentry, src_dentry, wr_dir_args); bcpup = err; wh_dentry = ERR_PTR(err); if (unlikely(err < 0)) goto out; sb = dentry->d_sb; mnt_flags = au_mntflags(sb); pin_flags = AuPin_DI_LOCKED | AuPin_MNT_WRITE; if (dt && au_opt_test(mnt_flags, UDBA_INOTIFY)) au_fset_pin(pin_flags, DO_GPARENT); err = au_pin(pin, dentry, bcpup, pin_flags); wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out; ndx.nfsmnt = au_nfsmnt(sb, bcpup); ndx.flags = 0; if (au_test_dlgt(mnt_flags)) au_fset_ndx(ndx.flags, DLGT); ndx.nd = NULL; /* ndx.br = NULL; */ /* ndx.nd_file = NULL; */ h_parent = au_pinned_h_parent(pin); if (!au_opt_test(mnt_flags, UDBA_NONE) && au_dbstart(dentry) == bcpup) { struct nameidata nd; if (ndx.nfsmnt) { /* todo: dirty? */ ndx.nd = &nd; ndx.br = au_sbr(sb, bcpup); memset(&nd, 0, sizeof(nd)); nd.flags = LOOKUP_CREATE; nd.intent.open.flags = O_EXCL; } err = au_may_add(dentry, bcpup, h_parent, au_ftest_wrdir(wr_dir_args->flags, ISDIR), &ndx); wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out_unpin; ndx.nd = NULL; ndx.br = NULL; } if (dt) au_dtime_store(dt, au_pinned_parent(pin), h_parent, au_pinned_hdir(pin), au_pinned_hgdir(pin)); wh_dentry = NULL; if (/* bcpup != bstart || */ bcpup != au_dbwh(dentry)) goto out; /* success */ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, &ndx); out_unpin: if (IS_ERR(wh_dentry)) au_unpin(pin); out: AuTraceErrPtr(wh_dentry); return wh_dentry; }
static int do_rename(struct au_ren_args *a) { int err; struct dentry *d, *h_d; /* prepare workqueue args for asynchronous rmdir */ h_d = a->dst_h_dentry; if (au_ftest_ren(a->flags, ISDIR) && h_d->d_inode) { err = -ENOMEM; a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); if (unlikely(!a->thargs)) goto out; a->h_dst = dget(h_d); } /* 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); if (IS_ERR(a->src_wh_dentry)) goto out_thargs; } /* lookup whiteout for dentry */ if (au_ftest_ren(a->flags, WHDST)) { h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, a->br); err = PTR_ERR(h_d); if (IS_ERR(h_d)) goto out_whsrc; if (!h_d->d_inode) dput(h_d); else a->dst_wh_dentry = h_d; } /* rename dentry to tmpwh */ if (a->thargs) { err = au_whtmp_ren(a->dst_h_dentry, a->br); if (unlikely(err)) goto out_whdst; d = a->dst_dentry; au_set_h_dptr(d, a->btgt, NULL); err = au_lkup_neg(d, a->btgt); if (unlikely(err)) goto out_whtmp; a->dst_h_dentry = au_h_dptr(d, a->btgt); } /* cpup src */ if (a->dst_h_dentry->d_inode && a->src_bstart != a->btgt) { struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; struct file *h_file; mutex_lock_nested(h_mtx, AuLsc_I_CHILD); AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); h_file = au_h_open_pre(a->src_dentry, a->src_bstart); if (IS_ERR(h_file)) { err = PTR_ERR(h_file); h_file = NULL; } else err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1, !AuCpup_DTIME); mutex_unlock(h_mtx); au_h_open_post(a->src_dentry, a->src_bstart, h_file); if (unlikely(err)) goto out_whtmp; } /* rename by vfs_rename or cpup */ d = a->dst_dentry; if (au_ftest_ren(a->flags, ISDIR) && (a->dst_wh_dentry || au_dbdiropq(d) == a->btgt /* hide the lower to keep xino */ || a->btgt < au_dbend(d) || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) au_fset_ren(a->flags, DIROPQ); err = au_ren_or_cpup(a); if (unlikely(err)) /* leave the copied-up one */ goto out_whtmp; /* make dir opaque */ if (au_ftest_ren(a->flags, DIROPQ)) { err = au_ren_diropq(a); if (unlikely(err)) goto out_rename; } /* update target timestamps */ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ a->src_inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; /* remove whiteout for dentry */ if (a->dst_wh_dentry) { a->h_path.dentry = a->dst_wh_dentry; err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, a->dst_dentry); if (unlikely(err)) goto out_diropq; } /* remove whtmp */ if (a->thargs) au_ren_del_whtmp(a); /* ignore this error */ err = 0; goto out_success; out_diropq: if (au_ftest_ren(a->flags, DIROPQ)) au_ren_rev_diropq(err, a); out_rename: if (!au_ftest_ren(a->flags, CPUP)) 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); out_whdst: dput(a->dst_wh_dentry); a->dst_wh_dentry = NULL; out_whsrc: if (a->src_wh_dentry) au_ren_rev_whsrc(err, a); out_success: dput(a->src_wh_dentry); dput(a->dst_wh_dentry); out_thargs: if (a->thargs) { dput(a->h_dst); au_whtmp_rmdir_free(a->thargs); a->thargs = NULL; } out: return err; }
int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, struct inode *_dst_dir, struct dentry *_dst_dentry) { int err, flags; /* reduce stack space */ struct au_ren_args *a; AuDbg("%.*s, %.*s\n", AuDLNPair(_src_dentry), AuDLNPair(_dst_dentry)); IMustLock(_src_dir); IMustLock(_dst_dir); err = -ENOMEM; BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); a = kzalloc(sizeof(*a), GFP_NOFS); if (unlikely(!a)) goto out; a->src_dir = _src_dir; a->src_dentry = _src_dentry; a->src_inode = a->src_dentry->d_inode; a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ a->dst_dir = _dst_dir; a->dst_dentry = _dst_dentry; a->dst_inode = a->dst_dentry->d_inode; a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ if (a->dst_inode) { IMustLock(a->dst_inode); au_igrab(a->dst_inode); } err = -ENOTDIR; 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))) goto out_free; err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, AuLock_DIR | flags); } else err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, flags); 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_parent; a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); a->h_path.mnt = a->br->br_mnt; /* are they available to be renamed */ err = au_ren_may_dir(a); if (unlikely(err)) goto out_children; /* prepare the writable parent dir on the same branch */ if (a->dst_bstart == a->btgt) { au_fset_ren(a->flags, WHDST); } else { err = au_cpup_dirs(a->dst_dentry, a->btgt); if (unlikely(err)) goto out_children; } if (a->src_dir != a->dst_dir) { /* * this temporary unlock is safe, * because both dir->i_mutex are locked. */ di_write_unlock(a->dst_parent); di_write_lock_parent(a->src_parent); err = au_wr_dir_need_wh(a->src_dentry, au_ftest_ren(a->flags, ISDIR), &a->btgt); di_write_unlock(a->src_parent); di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); au_fclr_ren(a->flags, ISSAMEDIR); } else err = au_wr_dir_need_wh(a->src_dentry, au_ftest_ren(a->flags, ISDIR), &a->btgt); if (unlikely(err < 0)) goto out_children; if (err) au_fset_ren(a->flags, WHSRC); /* lock them all */ err = au_ren_lock(a); if (unlikely(err)) goto out_children; if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) err = au_may_ren(a); else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) err = -ENAMETOOLONG; if (unlikely(err)) goto out_hdir; /* store timestamps to be revertible */ au_ren_dt(a); /* here we go */ err = do_rename(a); if (unlikely(err)) goto out_dt; /* update dir attributes */ au_ren_refresh_dir(a); /* dput/iput all lower dentries */ au_ren_refresh(a); goto out_hdir; /* success */ out_dt: au_ren_rev_dt(err, a); out_hdir: au_ren_unlock(a); out_children: au_nhash_wh_free(&a->whlist); 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); if (a->thargs) au_whtmp_rmdir_free(a->thargs); kfree(a); out: AuTraceErr(err); return err; }