diff options
Diffstat (limited to 'fs/aufs/xino.c')
-rw-r--r-- | fs/aufs/xino.c | 1297 |
1 files changed, 0 insertions, 1297 deletions
diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c deleted file mode 100644 index 053594410..000000000 --- a/fs/aufs/xino.c +++ /dev/null @@ -1,1297 +0,0 @@ -/* - * Copyright (C) 2005-2015 Junjiro R. Okajima - * - * This program, aufs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * external inode number translation table and bitmap - */ - -#include <linux/seq_file.h> -#include <linux/statfs.h> -#include "aufs.h" - -/* todo: unnecessary to support mmap_sem since kernel-space? */ -ssize_t xino_fread(vfs_readf_t func, struct file *file, void *kbuf, size_t size, - loff_t *pos) -{ - ssize_t err; - mm_segment_t oldfs; - union { - void *k; - char __user *u; - } buf; - - buf.k = kbuf; - oldfs = get_fs(); - set_fs(KERNEL_DS); - do { - /* todo: signal_pending? */ - err = func(file, buf.u, size, pos); - } while (err == -EAGAIN || err == -EINTR); - set_fs(oldfs); - -#if 0 /* reserved for future use */ - if (err > 0) - fsnotify_access(file->f_path.dentry); -#endif - - return err; -} - -/* ---------------------------------------------------------------------- */ - -static ssize_t do_xino_fwrite(vfs_writef_t func, struct file *file, void *kbuf, - size_t size, loff_t *pos) -{ - ssize_t err; - mm_segment_t oldfs; - union { - void *k; - const char __user *u; - } buf; - - buf.k = kbuf; - oldfs = get_fs(); - set_fs(KERNEL_DS); - do { - /* todo: signal_pending? */ - err = func(file, buf.u, size, pos); - } while (err == -EAGAIN || err == -EINTR); - set_fs(oldfs); - -#if 0 /* reserved for future use */ - if (err > 0) - fsnotify_modify(file->f_path.dentry); -#endif - - return err; -} - -struct do_xino_fwrite_args { - ssize_t *errp; - vfs_writef_t func; - struct file *file; - void *buf; - size_t size; - loff_t *pos; -}; - -static void call_do_xino_fwrite(void *args) -{ - struct do_xino_fwrite_args *a = args; - *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); -} - -ssize_t xino_fwrite(vfs_writef_t func, struct file *file, void *buf, - size_t size, loff_t *pos) -{ - ssize_t err; - - /* todo: signal block and no wkq? */ - if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { - lockdep_off(); - err = do_xino_fwrite(func, file, buf, size, pos); - lockdep_on(); - } else { - /* - * it breaks RLIMIT_FSIZE and normal user's limit, - * users should care about quota and real 'filesystem full.' - */ - int wkq_err; - struct do_xino_fwrite_args args = { - .errp = &err, - .func = func, - .file = file, - .buf = buf, - .size = size, - .pos = pos - }; - - wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); - if (unlikely(wkq_err)) - err = wkq_err; - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * create a new xinofile at the same place/path as @base_file. - */ -struct file *au_xino_create2(struct file *base_file, struct file *copy_src) -{ - struct file *file; - struct dentry *base, *parent; - struct inode *dir, *delegated; - struct qstr *name; - struct path path; - int err; - - base = base_file->f_path.dentry; - parent = base->d_parent; /* dir inode is locked */ - dir = d_inode(parent); - IMustLock(dir); - - file = ERR_PTR(-EINVAL); - name = &base->d_name; - path.dentry = vfsub_lookup_one_len(name->name, parent, name->len); - if (IS_ERR(path.dentry)) { - file = (void *)path.dentry; - pr_err("%pd lookup err %ld\n", - base, PTR_ERR(path.dentry)); - goto out; - } - - /* no need to mnt_want_write() since we call dentry_open() later */ - err = vfs_create(dir, path.dentry, S_IRUGO | S_IWUGO, NULL); - if (unlikely(err)) { - file = ERR_PTR(err); - pr_err("%pd create err %d\n", base, err); - goto out_dput; - } - - path.mnt = base_file->f_path.mnt; - file = vfsub_dentry_open(&path, - O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE - /* | __FMODE_NONOTIFY */); - if (IS_ERR(file)) { - pr_err("%pd open err %ld\n", base, PTR_ERR(file)); - goto out_dput; - } - - delegated = NULL; - err = vfsub_unlink(dir, &file->f_path, &delegated, /*force*/0); - if (unlikely(err == -EWOULDBLOCK)) { - pr_warn("cannot retry for NFSv4 delegation" - " for an internal unlink\n"); - iput(delegated); - } - if (unlikely(err)) { - pr_err("%pd unlink err %d\n", base, err); - goto out_fput; - } - - if (copy_src) { - /* no one can touch copy_src xino */ - err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src)); - if (unlikely(err)) { - pr_err("%pd copy err %d\n", base, err); - goto out_fput; - } - } - goto out_dput; /* success */ - -out_fput: - fput(file); - file = ERR_PTR(err); -out_dput: - dput(path.dentry); -out: - return file; -} - -struct au_xino_lock_dir { - struct au_hinode *hdir; - struct dentry *parent; - struct mutex *mtx; -}; - -static void au_xino_lock_dir(struct super_block *sb, struct file *xino, - struct au_xino_lock_dir *ldir) -{ - aufs_bindex_t brid, bindex; - - ldir->hdir = NULL; - bindex = -1; - brid = au_xino_brid(sb); - if (brid >= 0) - bindex = au_br_index(sb, brid); - if (bindex >= 0) { - ldir->hdir = au_hi(d_inode(sb->s_root), bindex); - au_hn_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT); - } else { - ldir->parent = dget_parent(xino->f_path.dentry); - ldir->mtx = &d_inode(ldir->parent)->i_mutex; - mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT); - } -} - -static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) -{ - if (ldir->hdir) - au_hn_imtx_unlock(ldir->hdir); - else { - mutex_unlock(ldir->mtx); - dput(ldir->parent); - } -} - -/* ---------------------------------------------------------------------- */ - -/* trucate xino files asynchronously */ - -int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) -{ - int err; - unsigned long jiffy; - blkcnt_t blocks; - aufs_bindex_t bi, bend; - struct kstatfs *st; - struct au_branch *br; - struct file *new_xino, *file; - struct super_block *h_sb; - struct au_xino_lock_dir ldir; - - err = -ENOMEM; - st = kzalloc(sizeof(*st), GFP_NOFS); - if (unlikely(!st)) - goto out; - - err = -EINVAL; - bend = au_sbend(sb); - if (unlikely(bindex < 0 || bend < bindex)) - goto out_st; - br = au_sbr(sb, bindex); - file = br->br_xino.xi_file; - if (!file) - goto out_st; - - err = vfs_statfs(&file->f_path, st); - if (unlikely(err)) - AuErr1("statfs err %d, ignored\n", err); - jiffy = jiffies; - blocks = file_inode(file)->i_blocks; - pr_info("begin truncating xino(b%d), ib%llu, %llu/%llu free blks\n", - bindex, (u64)blocks, st->f_bfree, st->f_blocks); - - au_xino_lock_dir(sb, file, &ldir); - /* mnt_want_write() is unnecessary here */ - new_xino = au_xino_create2(file, file); - au_xino_unlock_dir(&ldir); - err = PTR_ERR(new_xino); - if (IS_ERR(new_xino)) { - pr_err("err %d, ignored\n", err); - goto out_st; - } - err = 0; - fput(file); - br->br_xino.xi_file = new_xino; - - h_sb = au_br_sb(br); - for (bi = 0; bi <= bend; bi++) { - if (unlikely(bi == bindex)) - continue; - br = au_sbr(sb, bi); - if (au_br_sb(br) != h_sb) - continue; - - fput(br->br_xino.xi_file); - br->br_xino.xi_file = new_xino; - get_file(new_xino); - } - - err = vfs_statfs(&new_xino->f_path, st); - if (!err) { - pr_info("end truncating xino(b%d), ib%llu, %llu/%llu free blks\n", - bindex, (u64)file_inode(new_xino)->i_blocks, - st->f_bfree, st->f_blocks); - if (file_inode(new_xino)->i_blocks < blocks) - au_sbi(sb)->si_xino_jiffy = jiffy; - } else - AuErr1("statfs err %d, ignored\n", err); - -out_st: - kfree(st); -out: - return err; -} - -struct xino_do_trunc_args { - struct super_block *sb; - struct au_branch *br; -}; - -static void xino_do_trunc(void *_args) -{ - struct xino_do_trunc_args *args = _args; - struct super_block *sb; - struct au_branch *br; - struct inode *dir; - int err; - aufs_bindex_t bindex; - - err = 0; - sb = args->sb; - dir = d_inode(sb->s_root); - br = args->br; - - si_noflush_write_lock(sb); - ii_read_lock_parent(dir); - bindex = au_br_index(sb, br->br_id); - err = au_xino_trunc(sb, bindex); - ii_read_unlock(dir); - if (unlikely(err)) - pr_warn("err b%d, (%d)\n", bindex, err); - atomic_dec(&br->br_xino_running); - atomic_dec(&br->br_count); - si_write_unlock(sb); - au_nwt_done(&au_sbi(sb)->si_nowait); - kfree(args); -} - -static int xino_trunc_test(struct super_block *sb, struct au_branch *br) -{ - int err; - struct kstatfs st; - struct au_sbinfo *sbinfo; - - /* todo: si_xino_expire and the ratio should be customizable */ - sbinfo = au_sbi(sb); - if (time_before(jiffies, - sbinfo->si_xino_jiffy + sbinfo->si_xino_expire)) - return 0; - - /* truncation border */ - err = vfs_statfs(&br->br_xino.xi_file->f_path, &st); - if (unlikely(err)) { - AuErr1("statfs err %d, ignored\n", err); - return 0; - } - if (div64_u64(st.f_bfree * 100, st.f_blocks) >= AUFS_XINO_DEF_TRUNC) - return 0; - - return 1; -} - -static void xino_try_trunc(struct super_block *sb, struct au_branch *br) -{ - struct xino_do_trunc_args *args; - int wkq_err; - - if (!xino_trunc_test(sb, br)) - return; - - if (atomic_inc_return(&br->br_xino_running) > 1) - goto out; - - /* lock and kfree() will be called in trunc_xino() */ - args = kmalloc(sizeof(*args), GFP_NOFS); - if (unlikely(!args)) { - AuErr1("no memory\n"); - goto out_args; - } - - atomic_inc(&br->br_count); - args->sb = sb; - args->br = br; - wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); - if (!wkq_err) - return; /* success */ - - pr_err("wkq %d\n", wkq_err); - atomic_dec(&br->br_count); - -out_args: - kfree(args); -out: - atomic_dec(&br->br_xino_running); -} - -/* ---------------------------------------------------------------------- */ - -static int au_xino_do_write(vfs_writef_t write, struct file *file, - ino_t h_ino, ino_t ino) -{ - loff_t pos; - ssize_t sz; - - pos = h_ino; - if (unlikely(au_loff_max / sizeof(ino) - 1 < pos)) { - AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); - return -EFBIG; - } - pos *= sizeof(ino); - sz = xino_fwrite(write, file, &ino, sizeof(ino), &pos); - if (sz == sizeof(ino)) - return 0; /* success */ - - AuIOErr("write failed (%zd)\n", sz); - return -EIO; -} - -/* - * write @ino to the xinofile for the specified branch{@sb, @bindex} - * at the position of @h_ino. - * even if @ino is zero, it is written to the xinofile and means no entry. - * if the size of the xino file on a specific filesystem exceeds the watermark, - * try truncating it. - */ -int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, - ino_t ino) -{ - int err; - unsigned int mnt_flags; - struct au_branch *br; - - BUILD_BUG_ON(sizeof(long long) != sizeof(au_loff_max) - || ((loff_t)-1) > 0); - SiMustAnyLock(sb); - - mnt_flags = au_mntflags(sb); - if (!au_opt_test(mnt_flags, XINO)) - return 0; - - br = au_sbr(sb, bindex); - err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, - h_ino, ino); - if (!err) { - if (au_opt_test(mnt_flags, TRUNC_XINO) - && au_test_fs_trunc_xino(au_br_sb(br))) - xino_try_trunc(sb, br); - return 0; /* success */ - } - - AuIOErr("write failed (%d)\n", err); - return -EIO; -} - -/* ---------------------------------------------------------------------- */ - -/* aufs inode number bitmap */ - -static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; -static ino_t xib_calc_ino(unsigned long pindex, int bit) -{ - ino_t ino; - - AuDebugOn(bit < 0 || page_bits <= bit); - ino = AUFS_FIRST_INO + pindex * page_bits + bit; - return ino; -} - -static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit) -{ - AuDebugOn(ino < AUFS_FIRST_INO); - ino -= AUFS_FIRST_INO; - *pindex = ino / page_bits; - *bit = ino % page_bits; -} - -static int xib_pindex(struct super_block *sb, unsigned long pindex) -{ - int err; - loff_t pos; - ssize_t sz; - struct au_sbinfo *sbinfo; - struct file *xib; - unsigned long *p; - - sbinfo = au_sbi(sb); - MtxMustLock(&sbinfo->si_xib_mtx); - AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE - || !au_opt_test(sbinfo->si_mntflags, XINO)); - - if (pindex == sbinfo->si_xib_last_pindex) - return 0; - - xib = sbinfo->si_xib; - p = sbinfo->si_xib_buf; - pos = sbinfo->si_xib_last_pindex; - pos *= PAGE_SIZE; - sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); - if (unlikely(sz != PAGE_SIZE)) - goto out; - - pos = pindex; - pos *= PAGE_SIZE; - if (vfsub_f_size_read(xib) >= pos + PAGE_SIZE) - sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos); - else { - memset(p, 0, PAGE_SIZE); - sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); - } - if (sz == PAGE_SIZE) { - sbinfo->si_xib_last_pindex = pindex; - return 0; /* success */ - } - -out: - AuIOErr1("write failed (%zd)\n", sz); - err = sz; - if (sz >= 0) - err = -EIO; - return err; -} - -/* ---------------------------------------------------------------------- */ - -static void au_xib_clear_bit(struct inode *inode) -{ - int err, bit; - unsigned long pindex; - struct super_block *sb; - struct au_sbinfo *sbinfo; - - AuDebugOn(inode->i_nlink); - - sb = inode->i_sb; - xib_calc_bit(inode->i_ino, &pindex, &bit); - AuDebugOn(page_bits <= bit); - sbinfo = au_sbi(sb); - mutex_lock(&sbinfo->si_xib_mtx); - err = xib_pindex(sb, pindex); - if (!err) { - clear_bit(bit, sbinfo->si_xib_buf); - sbinfo->si_xib_next_bit = bit; - } - mutex_unlock(&sbinfo->si_xib_mtx); -} - -/* for s_op->delete_inode() */ -void au_xino_delete_inode(struct inode *inode, const int unlinked) -{ - int err; - unsigned int mnt_flags; - aufs_bindex_t bindex, bend, bi; - unsigned char try_trunc; - struct au_iinfo *iinfo; - struct super_block *sb; - struct au_hinode *hi; - struct inode *h_inode; - struct au_branch *br; - vfs_writef_t xwrite; - - sb = inode->i_sb; - mnt_flags = au_mntflags(sb); - if (!au_opt_test(mnt_flags, XINO) - || inode->i_ino == AUFS_ROOT_INO) - return; - - if (unlinked) { - au_xigen_inc(inode); - au_xib_clear_bit(inode); - } - - iinfo = au_ii(inode); - if (!iinfo) - return; - - bindex = iinfo->ii_bstart; - if (bindex < 0) - return; - - xwrite = au_sbi(sb)->si_xwrite; - try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); - hi = iinfo->ii_hinode + bindex; - bend = iinfo->ii_bend; - for (; bindex <= bend; bindex++, hi++) { - h_inode = hi->hi_inode; - if (!h_inode - || (!unlinked && h_inode->i_nlink)) - continue; - - /* inode may not be revalidated */ - bi = au_br_index(sb, hi->hi_id); - if (bi < 0) - continue; - - br = au_sbr(sb, bi); - err = au_xino_do_write(xwrite, br->br_xino.xi_file, - h_inode->i_ino, /*ino*/0); - if (!err && try_trunc - && au_test_fs_trunc_xino(au_br_sb(br))) - xino_try_trunc(sb, br); - } -} - -/* get an unused inode number from bitmap */ -ino_t au_xino_new_ino(struct super_block *sb) -{ - ino_t ino; - unsigned long *p, pindex, ul, pend; - struct au_sbinfo *sbinfo; - struct file *file; - int free_bit, err; - - if (!au_opt_test(au_mntflags(sb), XINO)) - return iunique(sb, AUFS_FIRST_INO); - - sbinfo = au_sbi(sb); - mutex_lock(&sbinfo->si_xib_mtx); - p = sbinfo->si_xib_buf; - free_bit = sbinfo->si_xib_next_bit; - if (free_bit < page_bits && !test_bit(free_bit, p)) - goto out; /* success */ - free_bit = find_first_zero_bit(p, page_bits); - if (free_bit < page_bits) - goto out; /* success */ - - pindex = sbinfo->si_xib_last_pindex; - for (ul = pindex - 1; ul < ULONG_MAX; ul--) { - err = xib_pindex(sb, ul); - if (unlikely(err)) - goto out_err; - free_bit = find_first_zero_bit(p, page_bits); - if (free_bit < page_bits) - goto out; /* success */ - } - - file = sbinfo->si_xib; - pend = vfsub_f_size_read(file) / PAGE_SIZE; - for (ul = pindex + 1; ul <= pend; ul++) { - err = xib_pindex(sb, ul); - if (unlikely(err)) - goto out_err; - free_bit = find_first_zero_bit(p, page_bits); - if (free_bit < page_bits) - goto out; /* success */ - } - BUG(); - -out: - set_bit(free_bit, p); - sbinfo->si_xib_next_bit = free_bit + 1; - pindex = sbinfo->si_xib_last_pindex; - mutex_unlock(&sbinfo->si_xib_mtx); - ino = xib_calc_ino(pindex, free_bit); - AuDbg("i%lu\n", (unsigned long)ino); - return ino; -out_err: - mutex_unlock(&sbinfo->si_xib_mtx); - AuDbg("i0\n"); - return 0; -} - -/* - * read @ino from xinofile for the specified branch{@sb, @bindex} - * at the position of @h_ino. - * if @ino does not exist and @do_new is true, get new one. - */ -int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, - ino_t *ino) -{ - int err; - ssize_t sz; - loff_t pos; - struct file *file; - struct au_sbinfo *sbinfo; - - *ino = 0; - if (!au_opt_test(au_mntflags(sb), XINO)) - return 0; /* no xino */ - - err = 0; - sbinfo = au_sbi(sb); - pos = h_ino; - if (unlikely(au_loff_max / sizeof(*ino) - 1 < pos)) { - AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); - return -EFBIG; - } - pos *= sizeof(*ino); - - file = au_sbr(sb, bindex)->br_xino.xi_file; - if (vfsub_f_size_read(file) < pos + sizeof(*ino)) - return 0; /* no ino */ - - sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos); - if (sz == sizeof(*ino)) - return 0; /* success */ - - err = sz; - if (unlikely(sz >= 0)) { - err = -EIO; - AuIOErr("xino read error (%zd)\n", sz); - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* create and set a new xino file */ - -struct file *au_xino_create(struct super_block *sb, char *fname, int silent) -{ - struct file *file; - struct dentry *h_parent, *d; - struct inode *h_dir, *inode; - int err; - - /* - * at mount-time, and the xino file is the default path, - * hnotify is disabled so we have no notify events to ignore. - * when a user specified the xino, we cannot get au_hdir to be ignored. - */ - file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE - /* | __FMODE_NONOTIFY */, - S_IRUGO | S_IWUGO); - if (IS_ERR(file)) { - if (!silent) - pr_err("open %s(%ld)\n", fname, PTR_ERR(file)); - return file; - } - - /* keep file count */ - err = 0; - inode = file_inode(file); - h_parent = dget_parent(file->f_path.dentry); - h_dir = d_inode(h_parent); - mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); - /* mnt_want_write() is unnecessary here */ - /* no delegation since it is just created */ - if (inode->i_nlink) - err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, - /*force*/0); - mutex_unlock(&h_dir->i_mutex); - dput(h_parent); - if (unlikely(err)) { - if (!silent) - pr_err("unlink %s(%d)\n", fname, err); - goto out; - } - - err = -EINVAL; - d = file->f_path.dentry; - if (unlikely(sb == d->d_sb)) { - if (!silent) - pr_err("%s must be outside\n", fname); - goto out; - } - if (unlikely(au_test_fs_bad_xino(d->d_sb))) { - if (!silent) - pr_err("xino doesn't support %s(%s)\n", - fname, au_sbtype(d->d_sb)); - goto out; - } - return file; /* success */ - -out: - fput(file); - file = ERR_PTR(err); - return file; -} - -/* - * find another branch who is on the same filesystem of the specified - * branch{@btgt}. search until @bend. - */ -static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, - aufs_bindex_t bend) -{ - aufs_bindex_t bindex; - struct super_block *tgt_sb = au_sbr_sb(sb, btgt); - - for (bindex = 0; bindex < btgt; bindex++) - if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) - return bindex; - for (bindex++; bindex <= bend; bindex++) - if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) - return bindex; - return -1; -} - -/* ---------------------------------------------------------------------- */ - -/* - * initialize the xinofile for the specified branch @br - * at the place/path where @base_file indicates. - * test whether another branch is on the same filesystem or not, - * if @do_test is true. - */ -int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, - struct file *base_file, int do_test) -{ - int err; - ino_t ino; - aufs_bindex_t bend, bindex; - struct au_branch *shared_br, *b; - struct file *file; - struct super_block *tgt_sb; - - shared_br = NULL; - bend = au_sbend(sb); - if (do_test) { - tgt_sb = au_br_sb(br); - for (bindex = 0; bindex <= bend; bindex++) { - b = au_sbr(sb, bindex); - if (tgt_sb == au_br_sb(b)) { - shared_br = b; - break; - } - } - } - - if (!shared_br || !shared_br->br_xino.xi_file) { - struct au_xino_lock_dir ldir; - - au_xino_lock_dir(sb, base_file, &ldir); - /* mnt_want_write() is unnecessary here */ - file = au_xino_create2(base_file, NULL); - au_xino_unlock_dir(&ldir); - err = PTR_ERR(file); - if (IS_ERR(file)) - goto out; - br->br_xino.xi_file = file; - } else { - br->br_xino.xi_file = shared_br->br_xino.xi_file; - get_file(br->br_xino.xi_file); - } - - ino = AUFS_ROOT_INO; - err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, - h_ino, ino); - if (unlikely(err)) { - fput(br->br_xino.xi_file); - br->br_xino.xi_file = NULL; - } - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* trucate a xino bitmap file */ - -/* todo: slow */ -static int do_xib_restore(struct super_block *sb, struct file *file, void *page) -{ - int err, bit; - ssize_t sz; - unsigned long pindex; - loff_t pos, pend; - struct au_sbinfo *sbinfo; - vfs_readf_t func; - ino_t *ino; - unsigned long *p; - - err = 0; - sbinfo = au_sbi(sb); - MtxMustLock(&sbinfo->si_xib_mtx); - p = sbinfo->si_xib_buf; - func = sbinfo->si_xread; - pend = vfsub_f_size_read(file); - pos = 0; - while (pos < pend) { - sz = xino_fread(func, file, page, PAGE_SIZE, &pos); - err = sz; - if (unlikely(sz <= 0)) - goto out; - - err = 0; - for (ino = page; sz > 0; ino++, sz -= sizeof(ino)) { - if (unlikely(*ino < AUFS_FIRST_INO)) - continue; - - xib_calc_bit(*ino, &pindex, &bit); - AuDebugOn(page_bits <= bit); - err = xib_pindex(sb, pindex); - if (!err) - set_bit(bit, p); - else - goto out; - } - } - -out: - return err; -} - -static int xib_restore(struct super_block *sb) -{ - int err; - aufs_bindex_t bindex, bend; - void *page; - - err = -ENOMEM; - page = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!page)) - goto out; - - err = 0; - bend = au_sbend(sb); - for (bindex = 0; !err && bindex <= bend; bindex++) - if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) - err = do_xib_restore - (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); - else - AuDbg("b%d\n", bindex); - free_page((unsigned long)page); - -out: - return err; -} - -int au_xib_trunc(struct super_block *sb) -{ - int err; - ssize_t sz; - loff_t pos; - struct au_xino_lock_dir ldir; - struct au_sbinfo *sbinfo; - unsigned long *p; - struct file *file; - - SiMustWriteLock(sb); - - err = 0; - sbinfo = au_sbi(sb); - if (!au_opt_test(sbinfo->si_mntflags, XINO)) - goto out; - - file = sbinfo->si_xib; - if (vfsub_f_size_read(file) <= PAGE_SIZE) - goto out; - - au_xino_lock_dir(sb, file, &ldir); - /* mnt_want_write() is unnecessary here */ - file = au_xino_create2(sbinfo->si_xib, NULL); - au_xino_unlock_dir(&ldir); - err = PTR_ERR(file); - if (IS_ERR(file)) - goto out; - fput(sbinfo->si_xib); - sbinfo->si_xib = file; - - p = sbinfo->si_xib_buf; - memset(p, 0, PAGE_SIZE); - pos = 0; - sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xib, p, PAGE_SIZE, &pos); - if (unlikely(sz != PAGE_SIZE)) { - err = sz; - AuIOErr("err %d\n", err); - if (sz >= 0) - err = -EIO; - goto out; - } - - mutex_lock(&sbinfo->si_xib_mtx); - /* mnt_want_write() is unnecessary here */ - err = xib_restore(sb); - mutex_unlock(&sbinfo->si_xib_mtx); - -out: - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * xino mount option handlers - */ - -/* xino bitmap */ -static void xino_clear_xib(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - sbinfo->si_xread = NULL; - sbinfo->si_xwrite = NULL; - if (sbinfo->si_xib) - fput(sbinfo->si_xib); - sbinfo->si_xib = NULL; - free_page((unsigned long)sbinfo->si_xib_buf); - sbinfo->si_xib_buf = NULL; -} - -static int au_xino_set_xib(struct super_block *sb, struct file *base) -{ - int err; - loff_t pos; - struct au_sbinfo *sbinfo; - struct file *file; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - file = au_xino_create2(base, sbinfo->si_xib); - err = PTR_ERR(file); - if (IS_ERR(file)) - goto out; - if (sbinfo->si_xib) - fput(sbinfo->si_xib); - sbinfo->si_xib = file; - sbinfo->si_xread = vfs_readf(file); - sbinfo->si_xwrite = vfs_writef(file); - - err = -ENOMEM; - if (!sbinfo->si_xib_buf) - sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS); - if (unlikely(!sbinfo->si_xib_buf)) - goto out_unset; - - sbinfo->si_xib_last_pindex = 0; - sbinfo->si_xib_next_bit = 0; - if (vfsub_f_size_read(file) < PAGE_SIZE) { - pos = 0; - err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf, - PAGE_SIZE, &pos); - if (unlikely(err != PAGE_SIZE)) - goto out_free; - } - err = 0; - goto out; /* success */ - -out_free: - free_page((unsigned long)sbinfo->si_xib_buf); - sbinfo->si_xib_buf = NULL; - if (err >= 0) - err = -EIO; -out_unset: - fput(sbinfo->si_xib); - sbinfo->si_xib = NULL; - sbinfo->si_xread = NULL; - sbinfo->si_xwrite = NULL; -out: - return err; -} - -/* xino for each branch */ -static void xino_clear_br(struct super_block *sb) -{ - aufs_bindex_t bindex, bend; - struct au_branch *br; - - bend = au_sbend(sb); - for (bindex = 0; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - if (!br || !br->br_xino.xi_file) - continue; - - fput(br->br_xino.xi_file); - br->br_xino.xi_file = NULL; - } -} - -static int au_xino_set_br(struct super_block *sb, struct file *base) -{ - int err; - ino_t ino; - aufs_bindex_t bindex, bend, bshared; - struct { - struct file *old, *new; - } *fpair, *p; - struct au_branch *br; - struct inode *inode; - vfs_writef_t writef; - - SiMustWriteLock(sb); - - err = -ENOMEM; - bend = au_sbend(sb); - fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS); - if (unlikely(!fpair)) - goto out; - - inode = d_inode(sb->s_root); - ino = AUFS_ROOT_INO; - writef = au_sbi(sb)->si_xwrite; - for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { - br = au_sbr(sb, bindex); - bshared = is_sb_shared(sb, bindex, bindex - 1); - if (bshared >= 0) { - /* shared xino */ - *p = fpair[bshared]; - get_file(p->new); - } - - if (!p->new) { - /* new xino */ - p->old = br->br_xino.xi_file; - p->new = au_xino_create2(base, br->br_xino.xi_file); - err = PTR_ERR(p->new); - if (IS_ERR(p->new)) { - p->new = NULL; - goto out_pair; - } - } - - err = au_xino_do_write(writef, p->new, - au_h_iptr(inode, bindex)->i_ino, ino); - if (unlikely(err)) - goto out_pair; - } - - for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { - br = au_sbr(sb, bindex); - if (br->br_xino.xi_file) - fput(br->br_xino.xi_file); - get_file(p->new); - br->br_xino.xi_file = p->new; - } - -out_pair: - for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) - if (p->new) - fput(p->new); - else - break; - kfree(fpair); -out: - return err; -} - -void au_xino_clr(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - - au_xigen_clr(sb); - xino_clear_xib(sb); - xino_clear_br(sb); - sbinfo = au_sbi(sb); - /* lvalue, do not call au_mntflags() */ - au_opt_clr(sbinfo->si_mntflags, XINO); -} - -int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount) -{ - int err, skip; - struct dentry *parent, *cur_parent; - struct qstr *dname, *cur_name; - struct file *cur_xino; - struct inode *dir; - struct au_sbinfo *sbinfo; - - SiMustWriteLock(sb); - - err = 0; - sbinfo = au_sbi(sb); - parent = dget_parent(xino->file->f_path.dentry); - if (remount) { - skip = 0; - dname = &xino->file->f_path.dentry->d_name; - cur_xino = sbinfo->si_xib; - if (cur_xino) { - cur_parent = dget_parent(cur_xino->f_path.dentry); - cur_name = &cur_xino->f_path.dentry->d_name; - skip = (cur_parent == parent - && au_qstreq(dname, cur_name)); - dput(cur_parent); - } - if (skip) - goto out; - } - - au_opt_set(sbinfo->si_mntflags, XINO); - dir = d_inode(parent); - mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); - /* mnt_want_write() is unnecessary here */ - err = au_xino_set_xib(sb, xino->file); - if (!err) - err = au_xigen_set(sb, xino->file); - if (!err) - err = au_xino_set_br(sb, xino->file); - mutex_unlock(&dir->i_mutex); - if (!err) - goto out; /* success */ - - /* reset all */ - AuIOErr("failed creating xino(%d).\n", err); - au_xigen_clr(sb); - xino_clear_xib(sb); - -out: - dput(parent); - return err; -} - -/* ---------------------------------------------------------------------- */ - -/* - * create a xinofile at the default place/path. - */ -struct file *au_xino_def(struct super_block *sb) -{ - struct file *file; - char *page, *p; - struct au_branch *br; - struct super_block *h_sb; - struct path path; - aufs_bindex_t bend, bindex, bwr; - - br = NULL; - bend = au_sbend(sb); - bwr = -1; - for (bindex = 0; bindex <= bend; bindex++) { - br = au_sbr(sb, bindex); - if (au_br_writable(br->br_perm) - && !au_test_fs_bad_xino(au_br_sb(br))) { - bwr = bindex; - break; - } - } - - if (bwr >= 0) { - file = ERR_PTR(-ENOMEM); - page = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!page)) - goto out; - path.mnt = au_br_mnt(br); - path.dentry = au_h_dptr(sb->s_root, bwr); - p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); - file = (void *)p; - if (!IS_ERR(p)) { - strcat(p, "/" AUFS_XINO_FNAME); - AuDbg("%s\n", p); - file = au_xino_create(sb, p, /*silent*/0); - if (!IS_ERR(file)) - au_xino_brid_set(sb, br->br_id); - } - free_page((unsigned long)page); - } else { - file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0); - if (IS_ERR(file)) - goto out; - h_sb = file->f_path.dentry->d_sb; - if (unlikely(au_test_fs_bad_xino(h_sb))) { - pr_err("xino doesn't support %s(%s)\n", - AUFS_XINO_DEFPATH, au_sbtype(h_sb)); - fput(file); - file = ERR_PTR(-EINVAL); - } - if (!IS_ERR(file)) - au_xino_brid_set(sb, -1); - } - -out: - return file; -} - -/* ---------------------------------------------------------------------- */ - -int au_xino_path(struct seq_file *seq, struct file *file) -{ - int err; - - err = au_seq_path(seq, &file->f_path); - if (unlikely(err < 0)) - goto out; - - err = 0; -#define Deleted "\\040(deleted)" - seq->count -= sizeof(Deleted) - 1; - AuDebugOn(memcmp(seq->buf + seq->count, Deleted, - sizeof(Deleted) - 1)); -#undef Deleted - -out: - return err; -} |