/* * decide if a new whiteout for @dentry is necessary or not. * when it is necessary, prepare the parent dir for the upper branch whose * branch index is @bcpup for creation. the actual creation of the whiteout will * be done by caller. * return value: * 0: wh is unnecessary * plus: wh is necessary * minus: error */ int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) { int need_wh, err; aufs_bindex_t bstart; struct super_block *sb; sb = dentry->d_sb; bstart = au_dbstart(dentry); if (*bcpup < 0) { *bcpup = bstart; if (au_test_ro(sb, bstart, dentry->d_inode)) { err = AuWbrCopyup(au_sbi(sb), dentry); *bcpup = err; if (unlikely(err < 0)) goto out; } } else AuDebugOn(bstart < *bcpup || au_test_ro(sb, *bcpup, dentry->d_inode)); AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); if (*bcpup != bstart) { err = au_cpup_dirs(dentry, *bcpup); if (unlikely(err)) 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); } } AuDbg("need_wh %d\n", need_wh); err = need_wh; out: return err; }
/* * decide if a new whiteout for @dentry is necessary or not. * when it is necessary, prepare the parent dir for the upper branch whose * branch index is @bcpup for creation. the actual creation of the whiteout will * be done by caller. * return value: * 0: wh is unnecessary * plus: wh is necessary * minus: error */ int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) { int need_wh, err; aufs_bindex_t bstart; struct super_block *sb; sb = dentry->d_sb; bstart = au_dbstart(dentry); if (*bcpup < 0) { *bcpup = bstart; if (au_test_ro(sb, bstart, dentry->d_inode)) { err = AuWbrCopyup(au_sbi(sb), dentry); *bcpup = err; if (unlikely(err < 0)) goto out; } } else AuDebugOn(bstart < *bcpup || au_test_ro(sb, *bcpup, dentry->d_inode)); AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); if (*bcpup != bstart) { err = au_cpup_dirs(dentry, *bcpup); if (unlikely(err)) goto out; need_wh = 1; } else { 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); err = need_wh; out: return err; }
static void aufs_inotify(struct inotify_watch *watch, u32 wd __maybe_unused, u32 mask, u32 cookie __maybe_unused, const char *h_child_name, struct inode *h_child_inode) { struct au_hnotify *hnotify; struct qstr h_child_qstr = { .name = h_child_name }; /* if IN_UNMOUNT happens, there must be another bug */ AuDebugOn(mask & IN_UNMOUNT); if (mask & (IN_IGNORED | IN_UNMOUNT)) { put_inotify_watch(watch); return; } #ifdef AuDbgHnotify au_debug(1); if (1 || !h_child_name || strcmp(h_child_name, AUFS_XINO_FNAME)) { AuDbg("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s," " hi%lu\n", watch->inode->i_ino, wd, mask, in_name(mask), cookie, h_child_name ? h_child_name : "", h_child_inode ? h_child_inode->i_ino : 0); WARN_ON(1); } au_debug(0); #endif if (h_child_name) h_child_qstr.len = strlen(h_child_name); hnotify = container_of(watch, struct au_hnotify, hn_watch); au_hnotify(watch->inode, hnotify, mask, &h_child_qstr, h_child_inode); }
static int au_conv_oflags(int flags) { int mask = 0; #ifdef CONFIG_IMA fmode_t fmode; /* mask = MAY_OPEN; */ fmode = OPEN_FMODE(flags); if (fmode & FMODE_READ) mask |= MAY_READ; if ((fmode & FMODE_WRITE) || (flags & O_TRUNC)) mask |= MAY_WRITE; /* * if (flags & O_APPEND) * mask |= MAY_APPEND; */ if (flags & vfsub_fmode_to_uint(FMODE_EXEC)) mask |= MAY_EXEC; AuDbg("flags 0x%x, mask 0x%x\n", flags, mask); #endif return mask; }
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; }
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; }
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)) { au_debug_on(); AuDbg("b%d, %s:%d\n", bindex, func, line); AuDbgDentry(dentry); AuDbgInode(inode); au_debug_off(); BUG(); } } }
/* * decide the branch and the parent dir where we will create a new entry. * returns new bindex or an error. * copyup the parent dir if needed. */ int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, struct au_wr_dir_args *args) { int err; aufs_bindex_t bcpup, bstart, src_bstart; const unsigned char add_entry = !!au_ftest_wrdir(args->flags, ADD_ENTRY); struct super_block *sb; struct dentry *parent; struct au_sbinfo *sbinfo; sb = dentry->d_sb; sbinfo = au_sbi(sb); parent = dget_parent(dentry); bstart = au_dbstart(dentry); bcpup = bstart; if (args->force_btgt < 0) { if (src_dentry) { src_bstart = au_dbstart(src_dentry); if (src_bstart < bstart) bcpup = src_bstart; } else if (add_entry) { err = AuWbrCreate(sbinfo, dentry, au_ftest_wrdir(args->flags, ISDIR)); bcpup = err; } if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) { if (add_entry) err = AuWbrCopyup(sbinfo, dentry); else { if (!IS_ROOT(dentry)) { di_read_lock_parent(parent, !AuLock_IR); err = AuWbrCopyup(sbinfo, dentry); di_read_unlock(parent, !AuLock_IR); } else err = AuWbrCopyup(sbinfo, dentry); } bcpup = err; if (unlikely(err < 0)) goto out; } } else { 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); out: dput(parent); return err; }
/* make sure the parent dir is fine */ static int au_mvd_args_parent(const unsigned char dmsg, struct au_mvd_args *a) { int err; aufs_bindex_t bindex; err = 0; if (unlikely(au_alive_dir(a->parent))) { err = -ENOENT; AU_MVD_PR(dmsg, "parent dir is dead\n"); goto out; } a->bopq = au_dbdiropq(a->parent); bindex = au_wbr_nonopq(a->dentry, a->mvd_bdst); AuDbg("b%d\n", bindex); if (unlikely((bindex >= 0 && bindex < a->mvd_bdst) || (a->bopq != -1 && a->bopq < a->mvd_bdst))) { err = -EINVAL; a->mvd_errno = EAU_MVDOWN_OPAQUE; AU_MVD_PR(dmsg, "ancestor is opaque b%d, b%d\n", a->bopq, a->mvd_bdst); } out: AuTraceErr(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; }
static int noinline_for_stack au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[]) { int err; struct path path; char *p; err = -EINVAL; mod->path = args[0].from; p = strchr(mod->path, '='); if (unlikely(!p)) { pr_err("no permssion %s\n", args[0].from); goto out; } *p++ = 0; err = vfsub_kern_path(mod->path, lkup_dirflags, &path); if (unlikely(err)) { pr_err("lookup failed %s (%d)\n", mod->path, err); goto out; } mod->perm = br_perm_val(p); AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p); mod->h_root = dget(path.dentry); path_put(&path); out: 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) { 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 int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags, struct dentry *dentry, aufs_bindex_t bindex) { int err, valid; err = 0; if (!(h_dentry->d_flags & DCACHE_OP_REVALIDATE)) goto out; AuDbg("b%d\n", bindex); /* * gave up supporting LOOKUP_CREATE/OPEN for lower fs, * due to whiteout and branch permission. */ flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_FOLLOW | LOOKUP_EXCL); /* it may return tri-state */ valid = h_dentry->d_op->d_revalidate(h_dentry, flags); if (unlikely(valid < 0)) err = valid; else if (!valid) err = -EINVAL; out: AuTraceErr(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_rdu_verify(struct aufs_rdu *rdu) { AuDbg("rdu{%llu, %p, %u | %u | %llu, %u, %u | " "%llu, b%d, 0x%x, g%u}\n", rdu->sz, rdu->ent.e, rdu->verify[AufsCtlRduV_SZ], rdu->blk, rdu->rent, rdu->shwh, rdu->full, rdu->cookie.h_pos, rdu->cookie.bindex, rdu->cookie.flags, rdu->cookie.generation); if (rdu->verify[AufsCtlRduV_SZ] == sizeof(*rdu)) return 0; AuDbg("%u:%u\n", rdu->verify[AufsCtlRduV_SZ], (unsigned int)sizeof(*rdu)); return -EINVAL; }
/* * 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 int au_hfsn_handle_event(struct fsnotify_group *group, struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event) { int err; struct au_hnotify *hnotify; struct inode *h_dir, *h_inode; __u32 mask; struct qstr h_child_qstr = { .name = event->file_name, .len = event->name_len }; AuDebugOn(event->data_type != FSNOTIFY_EVENT_INODE); err = 0; /* if FS_UNMOUNT happens, there must be another bug */ mask = event->mask; AuDebugOn(mask & FS_UNMOUNT); if (mask & (FS_IN_IGNORED | FS_UNMOUNT)) goto out; h_dir = event->to_tell; h_inode = event->inode; #ifdef AuDbgHnotify au_debug(1); if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", h_dir->i_ino, mask, au_hfsn_name(mask), AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); /* WARN_ON(1); */ } au_debug(0); #endif AuDebugOn(!inode_mark); hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); err = au_hnotify(h_dir, hnotify, mask, &h_child_qstr, h_inode); 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, struct inode *h_inode, struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { mask = (mask & ~FS_EVENT_ON_CHILD); return inode_mark->mask & mask; }
/* 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; }
static void au_hfsn_free_mark(struct fsnotify_mark *mark) { struct au_hnotify *hn = container_of(mark, struct au_hnotify, hn_mark); AuDbg("here\n"); hn->hn_mark_dead = 1; smp_mb(); wake_up_all(&au_hfsn_wq); }
static void au_hfsn_free_mark(struct fsnotify_mark *mark) { struct au_hnotify *hn = container_of(mark, struct au_hnotify, hn_mark); AuDbg("here\n"); au_cache_free_hnotify(hn); smp_mb__before_atomic_dec(); atomic64_dec(&au_hfsn_ifree); wake_up(&au_hfsn_wq); }
/* * copy-down the file and unlink the bsrc file. * - unlink the bdst whout if exist * - copy-down the file (with whtmp name and rename) * - unlink the bsrc file */ static int au_do_mvdown(const unsigned char dmsg, struct au_mvd_args *a) { int err; err = au_do_mkdir(dmsg, a); if (!err) err = au_do_lock(dmsg, a); if (unlikely(err)) goto out; /* * do not revert the activities we made on bdst since they should be * harmless in aufs. */ err = au_do_cpdown(dmsg, a); if (!err) err = au_do_unlink_wh(dmsg, a); if (!err && !(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) err = au_do_unlink(dmsg, a); if (unlikely(err)) goto out_unlock; AuDbg("%pd2, 0x%x, %d --> %d\n", a->dentry, a->mvdown.flags, a->mvd_bsrc, a->mvd_bdst); if (find_lower_writable(a) < 0) a->mvdown.flags |= AUFS_MVDOWN_BOTTOM; if (a->mvdown.flags & AUFS_MVDOWN_STFS) au_do_stfs(dmsg, a); /* maintain internal array */ if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); au_set_dbtop(a->dentry, a->mvd_bdst); au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); au_set_ibtop(a->inode, a->mvd_bdst); } else { /* hide the lower */ au_set_h_dptr(a->dentry, a->mvd_bdst, NULL); au_set_dbbot(a->dentry, a->mvd_bsrc); au_set_h_iptr(a->inode, a->mvd_bdst, NULL, /*flags*/0); au_set_ibbot(a->inode, a->mvd_bsrc); } if (au_dbbot(a->dentry) < a->mvd_bdst) au_set_dbbot(a->dentry, a->mvd_bdst); if (au_ibbot(a->inode) < a->mvd_bdst) au_set_ibbot(a->inode, a->mvd_bdst); out_unlock: au_do_unlock(dmsg, a); out: AuTraceErr(err); 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); }
int au_opts_remount(struct super_block *sb, struct au_opts *opts) { int err, rerr; unsigned char no_dreval; struct inode *dir; struct au_opt_xino *opt_xino; struct au_opt *opt; struct au_sbinfo *sbinfo; SiMustWriteLock(sb); err = 0; dir = d_inode(sb->s_root); sbinfo = au_sbi(sb); opt_xino = NULL; opt = opts->opt; while (err >= 0 && opt->type != Opt_tail) { err = au_opt_simple(sb, opt, opts); if (!err) err = au_opt_br(sb, opt, opts); if (!err) err = au_opt_xino(sb, opt, &opt_xino, opts); opt++; } if (err > 0) err = 0; AuTraceErr(err); /* go on even err */ no_dreval = !!au_ftest_si(sbinfo, NO_DREVAL); rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0); if (unlikely(rerr && !err)) err = rerr; if (no_dreval != !!au_ftest_si(sbinfo, NO_DREVAL)) au_fset_opts(opts->flags, REFRESH_IDOP); if (au_ftest_opts(opts->flags, TRUNC_XIB)) { rerr = au_xib_trunc(sb); if (unlikely(rerr && !err)) err = rerr; } /* will be handled by the caller */ if (!au_ftest_opts(opts->flags, REFRESH) && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO) || au_ftest_opts(opts->flags, REFRESH_IDOP) )) au_fset_opts(opts->flags, REFRESH); AuDbg("status 0x%x\n", opts->flags); return err; }
/* round robin */ static int au_wbr_create_init_rr(struct super_block *sb) { int err; err = au_wbr_bu(sb, au_sbend(sb)); atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ /* smp_mb(); */ AuDbg("b%d\n", err); return err; }
/* * returns a negative dentry whose name is unique and temporary. */ struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, struct qstr *prefix) { struct dentry *dentry; int i; char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN + 1], *name, *p; /* strict atomic_t is unnecessary here */ static unsigned short cnt; struct qstr qs; BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN); name = defname; qs.len = sizeof(defname) - DNAME_INLINE_LEN + prefix->len - 1; if (unlikely(prefix->len > DNAME_INLINE_LEN)) { dentry = ERR_PTR(-ENAMETOOLONG); if (unlikely(qs.len > NAME_MAX)) goto out; dentry = ERR_PTR(-ENOMEM); name = kmalloc(qs.len + 1, GFP_NOFS); if (unlikely(!name)) goto out; } /* doubly whiteout-ed */ memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); p = name + AUFS_WH_PFX_LEN * 2; memcpy(p, prefix->name, prefix->len); p += prefix->len; *p++ = '.'; AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN); qs.name = name; for (i = 0; i < 3; i++) { sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++); dentry = au_sio_lkup_one(&qs, h_parent, br); if (IS_ERR(dentry) || !dentry->d_inode) goto out_name; dput(dentry); } /* pr_warn("could not get random name\n"); */ dentry = ERR_PTR(-EEXIST); AuDbg("%.*s\n", AuLNPair(&qs)); BUG(); out_name: if (name != defname) kfree(name); out: AuTraceErrPtr(dentry); return 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; }
static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, unsigned int sigen, const unsigned int verbose) { int err; unsigned long long max, ull; struct inode *i, **array; aufs_bindex_t bstart, bend; array = au_iarray_alloc(sb, &max); err = PTR_ERR(array); if (IS_ERR(array)) goto out; err = 0; AuDbg("b%d\n", bindex); for (ull = 0; !err && ull < max; ull++) { i = array[ull]; if (i->i_ino == AUFS_ROOT_INO) continue; /* AuDbgInode(i); */ if (au_iigen(i) == sigen) ii_read_lock_child(i); else { ii_write_lock_child(i); err = au_refresh_hinode_self(i); au_iigen_dec(i); if (!err) ii_downgrade_lock(i); else { ii_write_unlock(i); break; } } bstart = au_ibstart(i); bend = au_ibend(i); if (bstart <= bindex && bindex <= bend && au_h_iptr(i, bindex) && au_test_ibusy(i, bstart, bend)) { err = -EBUSY; AuVerbose(verbose, "busy i%lu\n", i->i_ino); AuDbgInode(i); } ii_read_unlock(i); } au_iarray_free(array, max); out: return err; }
/* common function to regular file and dir */ int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), int wlock) { int err; unsigned int sigen, figen; aufs_bindex_t bstart; unsigned char pseudo_link; struct dentry *dentry; struct inode *inode; err = 0; dentry = file->f_dentry; inode = dentry->d_inode; AuDebugOn(au_special_file(inode->i_mode)); sigen = au_sigen(dentry->d_sb); fi_write_lock(file); figen = au_figen(file); di_write_lock_child(dentry); bstart = au_dbstart(dentry); pseudo_link = (bstart != au_ibstart(inode)); if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { if (!wlock) { di_downgrade_lock(dentry, AuLock_IR); fi_downgrade_lock(file); } goto out; /* success */ } AuDbg("sigen %d, figen %d\n", sigen, figen); if (sigen != au_digen(dentry) || sigen != au_iigen(inode)) { err = au_reval_dpath(dentry, sigen); if (unlikely(err < 0)) goto out; AuDebugOn(au_digen(dentry) != sigen || au_iigen(inode) != sigen); } err = refresh_file(file, reopen); if (!err) { if (!wlock) { di_downgrade_lock(dentry, AuLock_IR); fi_downgrade_lock(file); } } else { di_write_unlock(dentry); fi_write_unlock(file); } out: return err; }
static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) { int err; del->pathname = args[0].from; AuDbg("del path %s\n", del->pathname); err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); if (unlikely(err)) pr_err("lookup failed %s (%d)\n", del->pathname, err); return err; }
/* returns found or not */ int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) { struct hlist_head *head; struct au_vdir_wh *pos; struct au_vdir_destr *str; head = au_name_hash(whlist, name, nlen); hlist_for_each_entry(pos, head, wh_hash) { str = &pos->wh_str; AuDbg("%.*s\n", str->len, str->name); if (au_nhash_test_name(str, name, nlen)) return 1; }