static int find_lower_writable(struct au_mvd_args *a) { struct super_block *sb; aufs_bindex_t bindex, bend; struct au_branch *br; sb = a->sb; bindex = a->mvd_bsrc; bend = au_sbend(sb); if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) for (bindex++; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); if (au_br_fhsm(br->br_perm) && (!(au_br_sb(br)->s_flags & MS_RDONLY))) return bindex; } else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) for (bindex++; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); if (!au_br_rdonly(br)) return bindex; } else for (bindex++; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); if (!(au_br_sb(br)->s_flags & MS_RDONLY)) { if (au_br_rdonly(br)) a->mvdown.flags |= AUFS_MVDOWN_ROLOWER_R; return bindex; } } return -1; }
static void au_do_dir_ts(void *arg) { struct au_dir_ts_arg *a = arg; struct au_dtime dt; struct path h_path; struct inode *dir, *h_dir; struct super_block *sb; struct au_branch *br; struct au_hinode *hdir; int err; aufs_bindex_t btop, bindex; sb = a->dentry->d_sb; if (d_really_is_negative(a->dentry)) goto out; /* no dir->i_mutex lock */ aufs_read_lock(a->dentry, AuLock_DW); /* noflush */ dir = d_inode(a->dentry); btop = au_ibtop(dir); bindex = au_br_index(sb, a->brid); if (bindex < btop) goto out_unlock; br = au_sbr(sb, bindex); h_path.dentry = au_h_dptr(a->dentry, bindex); if (!h_path.dentry) goto out_unlock; h_path.mnt = au_br_mnt(br); au_dtime_store(&dt, a->dentry, &h_path); br = au_sbr(sb, btop); if (!au_br_writable(br->br_perm)) goto out_unlock; h_path.dentry = au_h_dptr(a->dentry, btop); h_path.mnt = au_br_mnt(br); err = vfsub_mnt_want_write(h_path.mnt); if (err) goto out_unlock; hdir = au_hi(dir, btop); au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); h_dir = au_h_iptr(dir, btop); if (h_dir->i_nlink && timespec_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { dt.dt_h_path = h_path; au_dtime_revert(&dt); } au_hn_inode_unlock(hdir); vfsub_mnt_drop_write(h_path.mnt); au_cpup_attr_timesizes(dir); out_unlock: aufs_read_unlock(a->dentry, AuLock_DW); out: dput(a->dentry); au_nwt_done(&au_sbi(sb)->si_nowait); kfree(arg); }
static int au_wbr_fd(struct path *path) { int err, fd; aufs_bindex_t wbi, bindex, bend; struct file *h_file; struct super_block *sb; struct dentry *root; struct au_branch *wbr; err = get_unused_fd(); if (unlikely(err < 0)) goto out; fd = err; wbi = 0; sb = path->dentry->d_sb; root = sb->s_root; aufs_read_lock(root, AuLock_IR); wbr = au_sbr(sb, wbi); if (!(path->mnt->mnt_flags & MNT_READONLY) && !au_br_writable(wbr->br_perm)) { bend = au_sbend(sb); for (bindex = 1; bindex <= bend; bindex++) { wbr = au_sbr(sb, bindex); if (au_br_writable(wbr->br_perm)) { wbi = bindex; break; } } wbr = au_sbr(sb, wbi); } AuDbg("wbi %d\n", wbi); h_file = au_h_open(root, wbi, O_RDONLY | O_DIRECTORY | O_LARGEFILE, NULL); aufs_read_unlock(root, AuLock_IR); err = PTR_ERR(h_file); if (IS_ERR(h_file)) goto out_fd; atomic_dec(&wbr->br_count); /* cf. au_h_open() */ fd_install(fd, h_file); err = fd; goto out; /* success */ out_fd: put_unused_fd(fd); out: return err; }
static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) { for (; bindex >= 0; bindex--) if (!au_br_rdonly(au_sbr(sb, bindex))) return bindex; return -EROFS; }
/* an exception for the policy other than tdp */ static int au_wbr_create_exp(struct dentry *dentry) { int err; aufs_bindex_t bwh, bdiropq; struct dentry *parent; err = -1; bwh = au_dbwh(dentry); parent = dget_parent(dentry); bdiropq = au_dbdiropq(parent); if (bwh >= 0) { if (bdiropq >= 0) err = min(bdiropq, bwh); else err = bwh; AuDbg("%d\n", err); } else if (bdiropq >= 0) { err = bdiropq; AuDbg("%d\n", err); } dput(parent); if (err >= 0) err = au_wbr_nonopq(dentry, err); if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) err = -1; AuDbg("%d\n", err); return err; }
void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force) { int err; struct au_sbinfo *sbinfo; struct au_fhsm *fhsm; struct au_branch *br; struct au_br_fhsm *bf; AuDbg("b%d, force %d\n", bindex, force); SiMustAnyLock(sb); sbinfo = au_sbi(sb); fhsm = &sbinfo->si_fhsm; if (!au_ftest_si(sbinfo, FHSM) || fhsm->fhsm_bottom == bindex) return; br = au_sbr(sb, bindex); bf = br->br_fhsm; AuDebugOn(!bf); mutex_lock(&bf->bf_lock); if (force || au_fhsm_pid(fhsm) || au_fhsm_test_jiffy(sbinfo, br)) err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0, /*do_notify*/1); mutex_unlock(&bf->bf_lock); }
static int au_wbr_create_mfs(struct dentry *dentry, int isdir __maybe_unused) { int err; struct super_block *sb; struct au_wbr_mfs *mfs; err = au_wbr_create_exp(dentry); if (err >= 0) goto out; sb = dentry->d_sb; mfs = &au_sbi(sb)->si_wbr_mfs; mutex_lock(&mfs->mfs_lock); if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) || mfs->mfs_bindex < 0 || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) au_mfs(dentry); mutex_unlock(&mfs->mfs_lock); err = mfs->mfs_bindex; if (err >= 0) err = au_wbr_nonopq(dentry, err); out: AuDbg("b%d\n", err); return err; }
/* * initial procedure of adding a new entry. * prepare writable branch and the parent dir, lock it, * and 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; struct au_branch *br; int err; unsigned int udba; aufs_bindex_t bcpup; AuDbg("%.*s\n", AuDLNPair(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; udba = au_opt_udba(sb); err = au_pin(pin, dentry, bcpup, udba, AuPin_DI_LOCKED | AuPin_MNT_WRITE); wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out; h_parent = au_pinned_h_parent(pin); if (udba != AuOpt_UDBA_NONE && au_dbstart(dentry) == bcpup) err = au_may_add(dentry, bcpup, h_parent, au_ftest_wrdir(wr_dir_args->flags, ISDIR)); else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) err = -ENAMETOOLONG; wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out_unpin; br = au_sbr(sb, bcpup); if (dt) { struct path tmp = { .dentry = h_parent, .mnt = au_br_mnt(br) }; au_dtime_store(dt, au_pinned_parent(pin), &tmp); } wh_dentry = NULL; if (bcpup != au_dbwh(dentry)) goto out; /* success */ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); out_unpin: if (IS_ERR(wh_dentry)) au_unpin(pin); out: return wh_dentry; }
/* * the lifetime of branch is independent from the entry under sysfs. * sysfs handles the lifetime of the entry, and never call ->show() after it is * unlinked. */ static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, aufs_bindex_t bindex) { int err; struct path path; struct dentry *root; struct au_branch *br; char *perm; AuDbg("b%d\n", bindex); err = 0; root = sb->s_root; di_read_lock_parent(root, !AuLock_IR); br = au_sbr(sb, bindex); path.mnt = br->br_mnt; path.dentry = au_h_dptr(root, bindex); au_seq_path(seq, &path); di_read_unlock(root, !AuLock_IR); perm = au_optstr_br_perm(br->br_perm); if (perm) { err = seq_printf(seq, "=%s\n", perm); kfree(perm); if (err == -1) err = -E2BIG; } else err = -ENOMEM; return err; }
static ssize_t aufs_aio_write_sp(struct kiocb *kio, const struct iovec *iov, unsigned long nv, loff_t pos) { ssize_t err; aufs_bindex_t bstart; unsigned char wbr; struct super_block *sb; struct file *file, *h_file; file = kio->ki_filp; sb = file->f_dentry->d_sb; si_read_lock(sb, AuLock_FLUSH); fi_read_lock(file); bstart = au_fbstart(file); h_file = au_hf_top(file); fi_read_unlock(file); wbr = !!au_br_writable(au_sbr(sb, bstart)->br_perm); si_read_unlock(sb); /* do not change the file in kio */ AuDebugOn(!h_file->f_op || !h_file->f_op->aio_write); err = h_file->f_op->aio_write(kio, iov, nv, pos); if (err > 0 && wbr) file_update_time(h_file); return err; }
static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) { int err; struct dentry *parent; struct super_block *sb; struct au_wbr_mfs *mfs; err = au_wbr_create_exp(dentry); if (err >= 0) goto out; sb = dentry->d_sb; parent = NULL; if (au_ftest_wbr(flags, PARENT)) parent = dget_parent(dentry); mfs = &au_sbi(sb)->si_wbr_mfs; mutex_lock(&mfs->mfs_lock); if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) || mfs->mfs_bindex < 0 || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) au_mfs(dentry, parent); mutex_unlock(&mfs->mfs_lock); err = mfs->mfs_bindex; dput(parent); if (err >= 0) err = au_wbr_nonopq(dentry, err); out: AuDbg("b%d\n", err); return err; }
int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, unsigned int d_type, ino_t *ino) { int err; struct mutex *mtx; const int isdir = (d_type == DT_DIR); /* prevent hardlinks from race condition */ mtx = NULL; if (!isdir) { mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx; mutex_lock(mtx); } err = au_xino_read(sb, bindex, h_ino, ino); if (unlikely(err)) goto out; if (!*ino) { err = -EIO; *ino = au_xino_new_ino(sb); if (unlikely(!*ino)) goto out; err = au_xino_write(sb, bindex, h_ino, *ino); if (unlikely(err)) goto out; } out: if (!isdir) mutex_unlock(mtx); return err; }
/* copy-down the file */ static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct au_cp_generic cpg = { .dentry = a->dentry, .bdst = a->mvd_bdst, .bsrc = a->mvd_bsrc, .len = -1, .pin = &a->pin, .flags = AuCpup_DTIME | AuCpup_HOPEN }; AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) au_fset_cpup(cpg.flags, OVERWRITE); if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) au_fset_cpup(cpg.flags, RWDST); err = au_sio_cpdown_simple(&cpg); if (unlikely(err)) AU_MVD_PR(dmsg, "cpdown failed\n"); AuTraceErr(err); return err; } /* * unlink the whiteout on bdst if exist which may be created by UDBA while we * were sleeping */ static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct path h_path; struct au_branch *br; br = au_sbr(a->sb, a->mvd_bdst); h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); err = PTR_ERR(h_path.dentry); if (IS_ERR(h_path.dentry)) { AU_MVD_PR(dmsg, "wh_lkup failed\n"); goto out; } err = 0; if (h_path.dentry->d_inode) { h_path.mnt = au_br_mnt(br); err = vfsub_unlink(a->mvd_h_dst_parent->d_inode, &h_path, /*force*/0); if (unlikely(err)) AU_MVD_PR(dmsg, "wh_unlink failed\n"); } dput(h_path.dentry); 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); h_inode = h_dentry->d_inode; if (dentry->d_inode) { err = -ENOENT; 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; } } else { /* rename(2) case */ err = -EIO; if (unlikely(h_inode)) 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_test_h_perm(h_parent->d_inode, MAY_EXEC | MAY_WRITE))) goto out; h_latest = au_sio_lkup_one(&dentry->d_name, h_parent, au_sbr(dentry->d_sb, bindex)); err = -EIO; if (IS_ERR(h_latest)) goto out; if (h_latest == h_dentry) err = 0; dput(h_latest); out: return err; }
/* Since mvdown succeeded, we ignore an error of this function */ static void au_do_stfs(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct au_branch *br; a->mvdown.flags |= AUFS_MVDOWN_STFS_FAILED; br = au_sbr(a->sb, a->mvd_bsrc); err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_UPPER].stfs); if (!err) { br = au_sbr(a->sb, a->mvd_bdst); a->mvdown.stbr[AUFS_MVDOWN_LOWER].brid = br->br_id; err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_LOWER].stfs); } if (!err) a->mvdown.flags &= ~AUFS_MVDOWN_STFS_FAILED; else AU_MVD_PR(dmsg, "statfs failed (%d), ignored\n", err); }
static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { ssize_t err; int readable; aufs_bindex_t nfhsm, bindex, bend; struct au_sbinfo *sbinfo; struct au_fhsm *fhsm; struct au_branch *br; struct super_block *sb; err = 0; sbinfo = file->private_data; fhsm = &sbinfo->si_fhsm; need_data: spin_lock_irq(&fhsm->fhsm_wqh.lock); if (!atomic_read(&fhsm->fhsm_readable)) { if (vfsub_file_flags(file) & O_NONBLOCK) err = -EAGAIN; else err = wait_event_interruptible_locked_irq (fhsm->fhsm_wqh, atomic_read(&fhsm->fhsm_readable)); } spin_unlock_irq(&fhsm->fhsm_wqh.lock); if (unlikely(err)) goto out; /* sb may already be dead */ au_rw_read_lock(&sbinfo->si_rwsem); readable = atomic_read(&fhsm->fhsm_readable); if (readable > 0) { sb = sbinfo->si_sb; AuDebugOn(!sb); /* exclude the bottom branch */ nfhsm = 0; bend = au_fhsm_bottom(sb); for (bindex = 0; bindex < bend; bindex++) { br = au_sbr(sb, bindex); if (au_br_fhsm(br->br_perm)) nfhsm++; } err = -EMSGSIZE; if (nfhsm * sizeof(struct aufs_stbr) <= count) { atomic_set(&fhsm->fhsm_readable, 0); err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf, count); } } au_rw_read_unlock(&sbinfo->si_rwsem); if (!readable) goto need_data; out: return err; }
int au_reopen_nondir(struct file *file) { int err; aufs_bindex_t bstart; struct dentry *dentry; struct file *h_file, *h_file_tmp; dentry = file->f_dentry; AuDebugOn(au_special_file(dentry->d_inode->i_mode)); bstart = au_dbstart(dentry); h_file_tmp = NULL; if (au_fbstart(file) == bstart) { h_file = au_hf_top(file); if (file->f_mode == h_file->f_mode) return 0; /* success */ h_file_tmp = h_file; get_file(h_file_tmp); au_set_h_fptr(file, bstart, NULL); } AuDebugOn(au_fi(file)->fi_hdir); /* * it can happen * file exists on both of rw and ro * open --> dbstart and fbstart are both 0 * prepend a branch as rw, "rw" become ro * remove rw/file * delete the top branch, "rw" becomes rw again * --> dbstart is 1, fbstart is still 0 * write --> fbstart is 0 but dbstart is 1 */ /* AuDebugOn(au_fbstart(file) < bstart); */ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, file, /*force_wr*/0); err = PTR_ERR(h_file); if (IS_ERR(h_file)) { if (h_file_tmp) { atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); au_set_h_fptr(file, bstart, h_file_tmp); h_file_tmp = NULL; } goto out; /* todo: close all? */ } err = 0; au_set_fbstart(file, bstart); au_set_h_fptr(file, bstart, h_file); au_update_figen(file); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ out: if (h_file_tmp) fput(h_file_tmp); return err; }
/* most free space */ static void au_mfs(struct dentry *dentry) { struct super_block *sb; struct au_branch *br; struct au_wbr_mfs *mfs; aufs_bindex_t bindex, bend; int err; unsigned long long b, bavail; struct path h_path; /* reduce the stack usage */ struct kstatfs *st; st = kmalloc(sizeof(*st), GFP_NOFS); if (unlikely(!st)) { AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); return; } bavail = 0; sb = dentry->d_sb; mfs = &au_sbi(sb)->si_wbr_mfs; MtxMustLock(&mfs->mfs_lock); mfs->mfs_bindex = -EROFS; mfs->mfsrr_bytes = 0; bend = au_sbend(sb); for (bindex = 0; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); if (au_br_rdonly(br)) continue; /* sb->s_root for NFS is unreliable */ h_path.mnt = br->br_mnt; h_path.dentry = h_path.mnt->mnt_root; err = vfs_statfs(&h_path, st); if (unlikely(err)) { AuWarn1("failed statfs, b%d, %d\n", bindex, err); continue; } /* when the available size is equal, select the lower one */ BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) || sizeof(b) < sizeof(st->f_bsize)); b = st->f_bavail * st->f_bsize; br->br_wbr->wbr_bytes = b; if (b >= bavail) { bavail = b; mfs->mfs_bindex = bindex; mfs->mfs_jiffy = jiffies; } } mfs->mfsrr_bytes = bavail; AuDbg("b%d\n", mfs->mfs_bindex); kfree(st); }
void au_fhsm_wrote_all(struct super_block *sb, int force) { aufs_bindex_t bindex, bend; struct au_branch *br; /* exclude the bottom */ bend = au_fhsm_bottom(sb); for (bindex = 0; bindex < bend; bindex++) { br = au_sbr(sb, bindex); if (au_br_fhsm(br->br_perm)) au_fhsm_wrote(sb, bindex, force); } }
void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) { struct au_finfo *finfo = au_fi(file); struct au_hfile *hf; hf = finfo->fi_hfile + bindex; if (hf->hf_file) au_hfput(hf, file); if (val) { hf->hf_file = val; hf->hf_br = au_sbr(file->f_dentry->d_sb, bindex); } }
/* top down parent */ static int au_wbr_create_tdp(struct dentry *dentry, unsigned int flags __maybe_unused) { int err; aufs_bindex_t bstart, bindex; struct super_block *sb; struct dentry *parent, *h_parent; sb = dentry->d_sb; bstart = au_dbstart(dentry); err = bstart; if (!au_br_rdonly(au_sbr(sb, bstart))) goto out; err = -EROFS; parent = dget_parent(dentry); for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { h_parent = au_h_dptr(parent, bindex); if (!h_parent || !h_parent->d_inode) continue; if (!au_br_rdonly(au_sbr(sb, bindex))) { err = bindex; break; } } dput(parent); /* bottom up here */ if (unlikely(err < 0)) { err = au_wbr_bu(sb, bstart - 1); if (err >= 0) err = au_wbr_nonopq(dentry, err); } out: AuDbg("b%d\n", err); return err; }
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; } }
static noinline_for_stack int au_do_h_d_reval(struct dentry *h_dentry, struct nameidata *nd, struct dentry *dentry, aufs_bindex_t bindex) { int err, valid; int (*reval)(struct dentry *, struct nameidata *); err = 0; reval = NULL; if (h_dentry->d_op) reval = h_dentry->d_op->d_revalidate; if (!reval) goto out; AuDbg("b%d\n", bindex); if (au_test_fs_null_nd(h_dentry->d_sb)) /* it may return tri-state */ valid = reval(h_dentry, NULL); else { struct nameidata h_nd; int locked; struct dentry *parent; au_h_nd(&h_nd, nd); parent = nd->path.dentry; locked = (nd && nd->path.dentry != dentry); if (locked) di_read_lock_parent(parent, AuLock_IR); BUG_ON(bindex > au_dbend(parent)); h_nd.path.dentry = au_h_dptr(parent, bindex); BUG_ON(!h_nd.path.dentry); h_nd.path.mnt = au_sbr(parent->d_sb, bindex)->br_mnt; path_get(&h_nd.path); valid = reval(h_dentry, &h_nd); path_put(&h_nd.path); if (locked) di_read_unlock(parent, AuLock_IR); } if (unlikely(valid < 0)) err = valid; else if (!valid) err = -EINVAL; out: AuTraceErr(err); return err; }
/* * the lifetime of branch is independent from the entry under sysfs. * sysfs handles the lifetime of the entry, and never call ->show() after it is * unlinked. */ static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, aufs_bindex_t bindex) { struct path path; struct dentry *root; struct au_branch *br; AuDbg("b%d\n", bindex); root = sb->s_root; di_read_lock_parent(root, !AuLock_IR); br = au_sbr(sb, bindex); path.mnt = br->br_mnt; path.dentry = au_h_dptr(root, bindex); au_seq_path(seq, &path); di_read_unlock(root, !AuLock_IR); seq_printf(seq, "=%s\n", au_optstr_br_perm(br->br_perm)); return 0; }
/* 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; }
/* mainly for link(2) and rename(2) */ int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) { aufs_bindex_t bdiropq, bwh; struct dentry *parent; struct au_branch *br; parent = dentry->d_parent; IMustLock(parent->d_inode); /* dir is locked */ bdiropq = au_dbdiropq(parent); bwh = au_dbwh(dentry); br = au_sbr(dentry->d_sb, btgt); if (au_br_rdonly(br) || (0 <= bdiropq && bdiropq < btgt) || (0 <= bwh && bwh < btgt)) btgt = -1; AuDbg("btgt %d\n", btgt); return btgt; }
void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) { struct au_finfo *finfo = au_fi(file); struct au_hfile *hf; struct au_fidir *fidir; fidir = finfo->fi_hdir; if (!fidir) { AuDebugOn(finfo->fi_btop != bindex); hf = &finfo->fi_htop; } else hf = fidir->fd_hfile + bindex; if (hf && hf->hf_file) au_hfput(hf, file); if (val) { FiMustWriteLock(file); hf->hf_file = val; hf->hf_br = au_sbr(file->f_dentry->d_sb, bindex); } }
/* lookup the plink-ed @inode under the branch at @bindex */ struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) { struct dentry *h_dentry, *h_parent; struct au_branch *br; struct inode *h_dir; char a[PLINK_NAME_LEN]; struct qstr tgtname = { .name = a }; br = au_sbr(inode->i_sb, bindex); h_parent = br->br_wbr->wbr_plink; h_dir = h_parent->d_inode; tgtname.len = plink_name(a, sizeof(a), inode, bindex); /* always superio. */ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); h_dentry = au_sio_lkup_one(&tgtname, h_parent, br); mutex_unlock(&h_dir->i_mutex); return h_dentry; }
static int au_wbr_create_rr(struct dentry *dentry, int isdir) { int err, nbr; unsigned int u; aufs_bindex_t bindex, bend; struct super_block *sb; atomic_t *next; err = au_wbr_create_exp(dentry); if (err >= 0) goto out; sb = dentry->d_sb; next = &au_sbi(sb)->si_wbr_rr_next; bend = au_sbend(sb); nbr = bend + 1; for (bindex = 0; bindex <= bend; bindex++) { if (!isdir) { err = atomic_dec_return(next) + 1; /* modulo for 0 is meaningless */ if (unlikely(!err)) err = atomic_dec_return(next) + 1; } else err = atomic_read(next); AuDbg("%d\n", err); u = err; err = u % nbr; AuDbg("%d\n", err); if (!au_br_rdonly(au_sbr(sb, err))) break; err = -EROFS; } if (err >= 0) err = au_wbr_nonopq(dentry, err); out: AuDbg("%d\n", err); return err; }
/* * when removing a dir, rename it to a unique temporary whiteout-ed name first * in order to be revertible and save time for removing many child whiteouts * under the dir. * returns 1 when there are too many child whiteout and caller should remove * them asynchronously. returns 0 when the number of children is enough small to * remove now or the branch fs is a remote fs. * otherwise return an error. */ static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, struct au_nhash *whlist, struct inode *dir) { int rmdir_later, err, dirwh; struct dentry *h_dentry; struct super_block *sb; struct inode *inode; sb = dentry->d_sb; SiMustAnyLock(sb); h_dentry = au_h_dptr(dentry, bindex); err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); if (unlikely(err)) goto out; /* stop monitoring */ inode = d_inode(dentry); au_hn_free(au_hi(inode, bindex)); if (!au_test_fs_remote(h_dentry->d_sb)) { dirwh = au_sbi(sb)->si_dirwh; rmdir_later = (dirwh <= 1); if (!rmdir_later) rmdir_later = au_nhash_test_longer_wh(whlist, bindex, dirwh); if (rmdir_later) return rmdir_later; } err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); if (unlikely(err)) { AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", h_dentry, bindex, err); err = 0; } out: AuTraceErr(err); return err; }