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; }
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; }
/* 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); }
/* 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; }
/* 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; }
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 au_fhsm_stfs(struct super_block *sb, aufs_bindex_t bindex, struct aufs_stfs *rstfs, int do_lock, int do_notify) { int err; struct au_branch *br; struct au_br_fhsm *bf; br = au_sbr(sb, bindex); AuDebugOn(au_br_rdonly(br)); bf = br->br_fhsm; AuDebugOn(!bf); if (do_lock) mutex_lock(&bf->bf_lock); else MtxMustLock(&bf->bf_lock); /* sb->s_root for NFS is unreliable */ err = au_br_stfs(br, &bf->bf_stfs); if (unlikely(err)) { AuErr1("FHSM failed (%d), b%d, ignored.\n", bindex, err); goto out; } bf->bf_jiffy = jiffies; bf->bf_readable = 1; if (do_notify) au_fhsm_notify(sb, /*val*/1); if (rstfs) *rstfs = bf->bf_stfs; out: if (do_lock) mutex_unlock(&bf->bf_lock); au_fhsm_notify(sb, /*val*/1); return err; }
int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, struct inode *inode) { int err; err = au_br_rdonly(au_sbr(sb, bindex)); /* pseudo-link after flushed may happen out of bounds */ if (!err && inode && au_ibstart(inode) <= bindex && bindex <= au_ibend(inode)) { /* * permission check is unnecessary since vfsub routine * will be called later */ struct inode *hi = au_h_iptr(inode, bindex); if (hi) err = IS_IMMUTABLE(hi) ? -EROFS : 0; } return err; }
static int aufs_permission(struct inode *inode, int mask) { int err; aufs_bindex_t bindex, bend; const unsigned char isdir = !!S_ISDIR(inode->i_mode), write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); struct inode *h_inode; struct super_block *sb; struct au_branch *br; /* todo: support rcu-walk? */ if (mask & MAY_NOT_BLOCK) return -ECHILD; 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(); h_inode = au_h_iptr(inode, au_ibstart(inode)); if (unlikely(!h_inode || (h_inode->i_mode & S_IFMT) != (inode->i_mode & S_IFMT))) goto out; err = 0; bindex = au_ibstart(inode); br = au_sbr(sb, bindex); err = h_permission(h_inode, mask, br->br_mnt, br->br_perm); if (write_mask && !err && !special_file(h_inode->i_mode)) { /* test whether the upper writable branch exists */ err = -EROFS; for (; bindex >= 0; bindex--) if (!au_br_rdonly(au_sbr(sb, bindex))) { err = 0; break; } } goto out; } /* non-write to dir */ err = 0; bend = au_ibend(inode); for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); if (h_inode) { err = au_busy_or_stale(); if (unlikely(!S_ISDIR(h_inode->i_mode))) break; br = au_sbr(sb, bindex); err = h_permission(h_inode, mask, br->br_mnt, br->br_perm); } } out: ii_read_unlock(inode); si_read_unlock(sb); return err; }
static int au_mvd_args(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct au_branch *br; err = -EISDIR; if (unlikely(S_ISDIR(a->inode->i_mode))) goto out; err = -EINVAL; if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) a->mvd_bsrc = au_ibstart(a->inode); else { a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); if (unlikely(a->mvd_bsrc < 0 || (a->mvd_bsrc < au_dbstart(a->dentry) || au_dbend(a->dentry) < a->mvd_bsrc || !au_h_dptr(a->dentry, a->mvd_bsrc)) || (a->mvd_bsrc < au_ibstart(a->inode) || au_ibend(a->inode) < a->mvd_bsrc || !au_h_iptr(a->inode, a->mvd_bsrc)))) { a->mvd_errno = EAU_MVDOWN_NOUPPER; AU_MVD_PR(dmsg, "no upper\n"); goto out; } } if (unlikely(a->mvd_bsrc == au_sbend(a->sb))) { a->mvd_errno = EAU_MVDOWN_BOTTOM; AU_MVD_PR(dmsg, "on the bottom\n"); goto out; } a->mvd_h_src_inode = au_h_iptr(a->inode, a->mvd_bsrc); br = au_sbr(a->sb, a->mvd_bsrc); err = au_br_rdonly(br); if (!(a->mvdown.flags & AUFS_MVDOWN_ROUPPER)) { if (unlikely(err)) goto out; } else if (!(vfsub_native_ro(a->mvd_h_src_inode) || IS_APPEND(a->mvd_h_src_inode))) { if (err) a->mvdown.flags |= AUFS_MVDOWN_ROUPPER_R; /* go on */ } else goto out; err = -EINVAL; if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_LOWER)) { a->mvd_bdst = find_lower_writable(a); if (unlikely(a->mvd_bdst < 0)) { a->mvd_errno = EAU_MVDOWN_BOTTOM; AU_MVD_PR(dmsg, "no writable lower branch\n"); goto out; } } else { a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); if (unlikely(a->mvd_bdst < 0 || au_sbend(a->sb) < a->mvd_bdst)) { a->mvd_errno = EAU_MVDOWN_NOLOWERBR; AU_MVD_PR(dmsg, "no lower brid\n"); goto out; } } err = au_mvd_args_busy(dmsg, a); if (!err) err = au_mvd_args_parent(dmsg, a); if (!err) err = au_mvd_args_intermediate(dmsg, a); if (!err) err = au_mvd_args_exist(dmsg, a); if (!err) AuDbg("b%d, b%d\n", a->mvd_bsrc, a->mvd_bdst); out: AuTraceErr(err); return err; }