per aufs mount, where <id> is a unique id generated
internally.
+What: /debug/aufs/si_<id>/plink
+Date: Apr 2013
+Contact: J. R. Okajima <hooanon05@yahoo.co.jp>
+Description:
+ It has three lines and shows the information about the
+ pseudo-link. The first line is a single number
+ representing a number of buckets. The second line is a
+ number of pseudo-links per buckets (separated by a
+ blank). The last line is a single number representing a
+ total number of psedo-links.
+ When the aufs mount option 'noplink' is specified, it
+ will show "1\n0\n0\n".
+
What: /debug/aufs/si_<id>/xib
Date: March 2009
Contact: J. R. Okajima <hooanon05@yahoo.co.jp>
It shows the abolute path of a member directory (which
is called branch) in aufs, and its permission.
+What: /sys/fs/aufs/si_<id>/brid0, brid1 ... bridN
+Date: July 2013
+Contact: J. R. Okajima <hooanon05@yahoo.co.jp>
+Description:
+ It shows the id of a member directory (which is called
+ branch) in aufs.
+
What: /sys/fs/aufs/si_<id>/xi_path
Date: March 2009
Contact: J. R. Okajima <hooanon05@yahoo.co.jp>
The aufs3-linux tree includes the whole linux mainline GIT tree,
git://git.kernel.org/.../torvalds/linux.git.
And you cannot select CONFIG_AUFS_FS=m for this version, eg. you cannot
-build aufs3 as an externel kernel module.
+build aufs3 as an external kernel module.
On the other hand, the aufs3-standalone tree has only aufs source files
and necessary patches, and you can select CONFIG_AUFS_FS=m.
o aufs3-linux tree
$ git clone --reference /your/linux/git/tree \
- git://aufs.git.sourceforge.net/gitroot/aufs/aufs3-linux.git \
+ git://git.code.sf.net/p/aufs/aufs3-linux aufs-aufs3-linux \
aufs3-linux.git
- if you don't have linux GIT tree, then remove "--reference ..."
$ cd aufs3-linux.git
$ git checkout origin/aufs3.0
o aufs3-standalone tree
-$ git clone git://aufs.git.sourceforge.net/gitroot/aufs/aufs3-standalone.git \
+$ git clone git://git.code.sf.net/p/aufs/aufs3-standalone \
aufs3-standalone.git
$ cd aufs3-standalone.git
$ git checkout origin/aufs3.0
o aufs-util tree
-$ git clone git://aufs.git.sourceforge.net/gitroot/aufs/aufs-util.git \
+$ git clone git://git.code.sf.net/p/aufs/aufs-util \
aufs-util.git
$ cd aufs-util.git
$ git checkout origin/aufs3.0
For (an unreleased) example:
If you are using "linux-3.10" and the "aufs3.10" branch
-does not exit in aufs-util repository, then "aufs3.9", "aufs3.8"
+does not exist in aufs-util repository, then "aufs3.9", "aufs3.8"
or something numerically smaller is the branch for your kernel.
Also you can view all branches by
1.
- apply ./aufs3-kbuild.patch to your kernel source files.
- apply ./aufs3-base.patch too.
-- apply ./aufs3-proc_map.patch too, if you want to make /proc/PID/maps (and
- others including lsof(1)) show the file path on aufs instead of the
- path on the branch fs.
+- apply ./aufs3-mmap.patch too.
- apply ./aufs3-standalone.patch too, if you have a plan to set
CONFIG_AUFS_FS=m. otherwise you don't need ./aufs3-standalone.patch.
- copy ./{Documentation,fs,include/linux/aufs_type.h} files to your
- kernel source tree. Never copy ./include/linux/Kbuild.
+ kernel source tree. Never copy $PWD/include/linux/Kbuild.
- enable CONFIG_EXPERIMENTAL and CONFIG_AUFS_FS, you can select either
=m or =y.
- and build your kernel as usual.
- install the built kernel.
-- install the header files too by "make headers_install".
+- install the header files too by "make headers_install" to the
+ directory where you specify. By default, it is $PWD/usr.
+ "make help" shows a brief note for headers_install.
- and reboot your system.
2.
- module only (CONFIG_AUFS_FS=m).
- apply ./aufs3-base.patch to your kernel source files.
-- apply ./aufs3-proc_map.patch too to your kernel source files,
- if you want to make /proc/PID/maps (and others including lsof(1)) show
- the file path on aufs instead of the path on the branch fs.
+- apply ./aufs3-mmap.patch too.
- apply ./aufs3-standalone.patch too.
- build your kernel, don't forget "make headers_install", and reboot.
- edit ./config.mk and set other aufs configurations if necessary.
- Note: You should read ./fs/aufs/Kconfig carefully which describes
+ Note: You should read $PWD/fs/aufs/Kconfig carefully which describes
every aufs configurations.
- build the module by simple "make".
- you can specify ${KDIR} make variable which points to your kernel
source tree.
- install the files
+ run "make install" to install the aufs module, or copy the built
- ./aufs.ko to /lib/modules/... and run depmod -a (or reboot simply).
- + run "make headers_install" to install the aufs header file (you can
- specify DESTDIR), or copty ./usr/include/linux/aufs_type.h to
- /usr/include/linux or wherever you like.
+ $PWD/aufs.ko to /lib/modules/... and run depmod -a (or reboot simply).
+ + run "make install_headers" (instead of headers_install) to install
+ the modified aufs header file (you can specify DESTDIR which is
+ available in aufs standalone version's Makefile only), or copy
+ $PWD/usr/include/linux/aufs_type.h to /usr/include/linux or wherever
+ you like manually. By default, the target directory is $PWD/usr.
- no need to apply aufs3-kbuild.patch, nor copying source files to your
kernel source tree.
-Note: The haeder file aufs_type.h is necessary to build aufs-util
+Note: The header file aufs_type.h is necessary to build aufs-util
as well as "make headers_install" in the kernel source tree.
headers_install is subject to be forgotten, but it is essentially
necessary, not only for building aufs-util.
Since Apr 2010, Tomas M (the author of Slax and Linux Live
scripts) is making "doubling" donations.
Unfortunately I cannot list all of the donators, but I really
- appriciate.
+ appreciate.
It ends Aug 2010, but the ordinary donation URL is still available.
<http://sourceforge.net/donate/index.php?group_id=167503>
Dai Itasaka made a donation (2007/8).
Pavel Pronskiy made a donation (2011/2).
Iridium and Inmarsat satellite phone retailer (www.mailasail.com), Nippy
Networks (Ed Wildgoose) made a donation for hardware (2011/3).
-Max Lekomcev (DOM-TV project) made a donation (2011/7 and 12).
+Max Lekomcev (DOM-TV project) made a donation (2011/7, 12, 2012/3, 6 and
+11).
Sam Liddicott made a donation (2011/9).
+Era Scarecrow made a donation (2013/4).
+Bor Ratajc made a donation (2013/4).
+Alessandro Gorreta made a donation (2013/4).
+POIRETTE Marc made a donation (2013/4).
+Alessandro Gorreta made a donation (2013/4).
+lauri kasvandik made a donation (2013/5).
+"pemasu from Finland" made a donation (2013/7).
+The Parted Magic Project made a donation (2013/9).
+Pavel Barta made a donation (2013/10).
Thank you very much.
Donations are always, including future donations, very important and
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# Copyright (C) 2010-2011 Junjiro R. Okajima
+# Copyright (C) 2010-2013 Junjiro R. Okajima
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# Copyright (C) 2005-2011 Junjiro R. Okajima
+# Copyright (C) 2005-2013 Junjiro R. Okajima
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
* free a single branch
*/
+
+/* prohibit rmdir to the root of the branch */
+/* todo: another new flag? */
+static void au_br_dflags_force(struct au_branch *br)
+{
+ struct dentry *h_dentry;
+
+ h_dentry = au_br_dentry(br);
+ spin_lock(&h_dentry->d_lock);
+ br->br_dflags = h_dentry->d_flags & DCACHE_MOUNTED;
+ h_dentry->d_flags |= DCACHE_MOUNTED;
+ spin_unlock(&h_dentry->d_lock);
+}
+
+/* restore its d_flags */
+static void au_br_dflags_restore(struct au_branch *br)
+{
+ struct dentry *h_dentry;
+
+ if (br->br_dflags)
+ return;
+
+ h_dentry = au_br_dentry(br);
+ spin_lock(&h_dentry->d_lock);
+ h_dentry->d_flags &= ~DCACHE_MOUNTED;
+ spin_unlock(&h_dentry->d_lock);
+}
+
static void au_br_do_free(struct au_branch *br)
{
int i;
else
break;
- mntput(br->br_mnt);
+ au_br_dflags_restore(br);
+
+ /* recursive lock, s_umount of branch's */
+ lockdep_off();
+ path_put(&br->br_path);
+ lockdep_on();
kfree(wbr);
kfree(br);
}
if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO)
|| h_inode->i_uid != inode->i_uid
|| h_inode->i_gid != inode->i_gid)
- pr_warning("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n",
- add->pathname,
- inode->i_uid, inode->i_gid,
- (inode->i_mode & S_IALLUGO),
- h_inode->i_uid, h_inode->i_gid,
- (h_inode->i_mode & S_IALLUGO));
+ pr_warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n",
+ add->pathname,
+ inode->i_uid, inode->i_gid,
+ (inode->i_mode & S_IALLUGO),
+ h_inode->i_uid, h_inode->i_gid,
+ (h_inode->i_mode & S_IALLUGO));
}
out:
* initialize or clean the whiteouts for an adding branch
*/
static int au_br_init_wh(struct super_block *sb, struct au_branch *br,
- int new_perm, struct dentry *h_root)
+ int new_perm)
{
int err, old_perm;
aufs_bindex_t bindex;
hdir = au_hi(sb->s_root->d_inode, bindex);
au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT);
} else {
- h_mtx = &h_root->d_inode->i_mutex;
+ h_mtx = &au_br_dentry(br)->d_inode->i_mutex;
mutex_lock_nested(h_mtx, AuLsc_I_PARENT);
}
if (!wbr)
- err = au_wh_init(h_root, br, sb);
+ err = au_wh_init(br, sb);
else {
wbr_wh_write_lock(wbr);
- err = au_wh_init(h_root, br, sb);
+ err = au_wh_init(br, sb);
wbr_wh_write_unlock(wbr);
}
if (hdir)
}
static int au_wbr_init(struct au_branch *br, struct super_block *sb,
- int perm, struct path *path)
+ int perm)
{
int err;
struct kstatfs kst;
struct au_wbr *wbr;
- struct dentry *h_dentry;
wbr = br->br_wbr;
au_rw_init(&wbr->wbr_wh_rwsem);
* a limit for rmdir/rename a dir
* cf. AUFS_MAX_NAMELEN in include/linux/aufs_type.h
*/
- err = vfs_statfs(path, &kst);
+ err = vfs_statfs(&br->br_path, &kst);
if (unlikely(err))
goto out;
err = -EINVAL;
- h_dentry = path->dentry;
if (kst.f_namelen >= NAME_MAX)
- err = au_br_init_wh(sb, br, perm, h_dentry);
+ err = au_br_init_wh(sb, br, perm);
else
pr_err("%.*s(%s), unsupported namelen %ld\n",
- AuDLNPair(h_dentry), au_sbtype(h_dentry->d_sb),
- kst.f_namelen);
+ AuDLNPair(au_br_dentry(br)),
+ au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen);
out:
return err;
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 */
+ BUILD_BUG_ON(sizeof(br->br_dflags)
+ != sizeof(br->br_path.dentry->d_flags));
+ br->br_dflags = DCACHE_MOUNTED;
+ br->br_path = add->path; /* set first, path_get() 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);
+ err = au_wbr_init(br, sb, add->perm);
if (unlikely(err))
goto out_err;
}
}
sysaufs_br_init(br);
- mntget(add->path.mnt);
+ path_get(&br->br_path);
goto out; /* success */
out_err:
- br->br_mnt = NULL;
+ memset(&br->br_path, 0, sizeof(br->br_path));
out:
return err;
}
iinfo->ii_bstart = 0;
}
-static void au_br_do_add(struct super_block *sb, struct dentry *h_dentry,
- struct au_branch *br, aufs_bindex_t bindex)
+static void au_br_do_add(struct super_block *sb, struct au_branch *br,
+ aufs_bindex_t bindex)
{
- struct dentry *root;
+ struct dentry *root, *h_dentry;
struct inode *root_inode;
aufs_bindex_t bend, amount;
+ au_br_dflags_force(br);
+
root = sb->s_root;
root_inode = root->d_inode;
bend = au_sbend(sb);
amount = bend + 1 - bindex;
+ h_dentry = au_br_dentry(br);
au_sbilist_lock();
au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount);
au_br_do_add_hdp(au_di(root), bindex, bend, amount);
}
add_bindex = add->bindex;
- h_dentry = add->path.dentry;
if (!remount)
- au_br_do_add(sb, h_dentry, add_branch, add_bindex);
+ au_br_do_add(sb, add_branch, add_bindex);
else {
sysaufs_brs_del(sb, add_bindex);
- au_br_do_add(sb, h_dentry, add_branch, add_bindex);
+ au_br_do_add(sb, add_branch, add_bindex);
sysaufs_brs_add(sb, add_bindex);
}
+ h_dentry = add->path.dentry;
if (!add_bindex) {
au_cpup_attr_all(root_inode, /*force*/1);
sb->s_maxbytes = h_dentry->d_sb->s_maxbytes;
continue;
/* AuDbgInode(i); */
- if (au_iigen(i) == sigen)
+ if (au_iigen(i, NULL) == sigen)
ii_read_lock_child(i);
else {
ii_write_lock_child(i);
goto out;
}
br = au_sbr(sb, bindex);
+ AuDebugOn(!path_equal(&br->br_path, &del->h_path));
i = atomic_read(&br->br_count);
if (unlikely(i)) {
AuVerbose(verbose, "%d file(s) opened\n", i);
out_wh:
/* revert */
- rerr = au_br_init_wh(sb, br, br->br_perm, del->h_path.dentry);
+ rerr = au_br_init_wh(sb, br, br->br_perm);
if (rerr)
- pr_warning("failed re-creating base whiteout, %s. (%d)\n",
- del->pathname, rerr);
+ pr_warn("failed re-creating base whiteout, %s. (%d)\n",
+ del->pathname, rerr);
out:
return err;
}
{
int err, rerr;
aufs_bindex_t bindex;
- struct path path;
struct dentry *root;
struct au_branch *br;
goto out;
br = au_sbr(sb, bindex);
+ AuDebugOn(mod->h_root != au_br_dentry(br));
if (br->br_perm == mod->perm)
return 0; /* success */
if (au_br_writable(br->br_perm)) {
/* remove whiteout base */
- err = au_br_init_wh(sb, br, mod->perm, mod->h_root);
+ err = au_br_init_wh(sb, br, mod->perm);
if (unlikely(err))
goto out;
rerr = -ENOMEM;
br->br_wbr = kmalloc(sizeof(*br->br_wbr),
GFP_NOFS);
- if (br->br_wbr) {
- path.mnt = br->br_mnt;
- path.dentry = mod->h_root;
- rerr = au_wbr_init(br, sb, br->br_perm,
- &path);
- }
+ if (br->br_wbr)
+ rerr = au_wbr_init(br, sb, br->br_perm);
if (unlikely(rerr)) {
AuIOErr("nested error %d (%d)\n",
rerr, err);
err = -ENOMEM;
br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS);
if (br->br_wbr) {
- path.mnt = br->br_mnt;
- path.dentry = mod->h_root;
- err = au_wbr_init(br, sb, mod->perm, &path);
+ err = au_wbr_init(br, sb, mod->perm);
if (unlikely(err)) {
kfree(br->br_wbr);
br->br_wbr = NULL;
}
if (!err) {
+ if ((br->br_perm & AuBrAttr_UNPIN)
+ && !(mod->perm & AuBrAttr_UNPIN))
+ au_br_dflags_force(br);
+ else if (!(br->br_perm & AuBrAttr_UNPIN)
+ && (mod->perm & AuBrAttr_UNPIN))
+ au_br_dflags_restore(br);
*do_refresh |= need_sigen_inc(br->br_perm, mod->perm);
br->br_perm = mod->perm;
}
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/* ext2 has 3 types of operations at least, ext3 has 4 */
#define AuBrDynOp (AuDyLast * 4)
+/* sysfs entries */
+struct au_brsysfs {
+ char name[16];
+ struct attribute attr;
+};
+
+enum {
+ AuBrSysfs_BR,
+ AuBrSysfs_BRID,
+ AuBrSysfs_Last
+};
+
/* protected by superblock rwsem */
struct au_branch {
struct au_xino_file br_xino;
aufs_bindex_t br_id;
int br_perm;
- struct vfsmount *br_mnt;
+ unsigned int br_dflags;
+ struct path br_path;
spinlock_t br_dykey_lock;
struct au_dykey *br_dykey[AuBrDynOp];
atomic_t br_count;
struct au_wbr *br_wbr;
/* xino truncation */
- blkcnt_t br_xino_upper; /* watermark in blocks */
atomic_t br_xino_running;
#ifdef CONFIG_AUFS_HFSNOTIFY
#endif
#ifdef CONFIG_SYSFS
- /* an entry under sysfs per mount-point */
- char br_name[8];
- struct attribute br_attr;
+ /* entries under sysfs per mount-point */
+ struct au_brsysfs br_sysfs[AuBrSysfs_Last];
#endif
};
/* ---------------------------------------------------------------------- */
+static inline struct vfsmount *au_br_mnt(struct au_branch *br)
+{
+ return br->br_path.mnt;
+}
+
+static inline struct dentry *au_br_dentry(struct au_branch *br)
+{
+ return br->br_path.dentry;
+}
+
+static inline struct super_block *au_br_sb(struct au_branch *br)
+{
+ return au_br_mnt(br)->mnt_sb;
+}
+
/* branch permissions and attributes */
#define AuBrPerm_RW 1 /* writable, hardlinkable wh */
#define AuBrPerm_RO (1 << 1) /* readonly */
#define AuBrWAttr_NoLinkWH (1 << 4) /* un-hardlinkable whiteouts */
+#define AuBrAttr_UNPIN (1 << 5) /* rename-able top dir of
+ branch */
+
static inline int au_br_writable(int brperm)
{
return brperm & AuBrPerm_RW;
static inline int au_br_rdonly(struct au_branch *br)
{
- return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY)
+ return ((au_br_sb(br)->s_flags & MS_RDONLY)
|| !au_br_writable(br->br_perm))
? -EROFS : 0;
}
static inline
struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)
{
- return au_sbr(sb, bindex)->br_mnt;
+ return au_br_mnt(au_sbr(sb, bindex));
}
static inline
struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
{
- return au_sbr_mnt(sb, bindex)->mnt_sb;
+ return au_br_sb(au_sbr(sb, bindex));
}
static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex)
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
#include <linux/mm.h>
#include "aufs.h"
-void au_cpup_attr_flags(struct inode *dst, struct inode *src)
+void au_cpup_attr_flags(struct inode *dst, unsigned int iflags)
{
const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE
- | S_NOATIME | S_NOCMTIME;
+ | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT;
- dst->i_flags |= src->i_flags & ~mask;
+ BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags));
+
+ dst->i_flags |= iflags & ~mask;
if (au_test_fs_notime(dst->i_sb))
dst->i_flags |= S_NOATIME | S_NOCMTIME;
}
inode->i_uid = h_inode->i_uid;
inode->i_gid = h_inode->i_gid;
au_cpup_attr_timesizes(inode);
- au_cpup_attr_flags(inode, h_inode);
+ au_cpup_attr_flags(inode, h_inode->i_flags);
}
void au_cpup_igen(struct inode *inode, struct inode *h_inode)
err = vfsub_notify_change(&dt->dt_h_path, &attr);
if (unlikely(err))
- pr_warning("restoring timestamps failed(%d). ignored\n", err);
+ pr_warn("restoring timestamps failed(%d). ignored\n", err);
}
/* ---------------------------------------------------------------------- */
+/* internal use only */
+struct au_cpup_reg_attr {
+ int valid;
+ struct kstat st;
+ unsigned int iflags; /* inode->i_flags */
+};
+
static noinline_for_stack
-int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src)
+int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src,
+ struct au_cpup_reg_attr *h_src_attr)
{
int err, sbits;
struct iattr ia;
struct path h_path;
struct inode *h_isrc, *h_idst;
+ struct kstat *h_st;
h_path.dentry = au_h_dptr(dst, bindex);
h_idst = h_path.dentry->d_inode;
ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID
| ATTR_ATIME | ATTR_MTIME
| ATTR_ATIME_SET | ATTR_MTIME_SET;
- ia.ia_uid = h_isrc->i_uid;
- ia.ia_gid = h_isrc->i_gid;
- ia.ia_atime = h_isrc->i_atime;
- ia.ia_mtime = h_isrc->i_mtime;
- if (h_idst->i_mode != h_isrc->i_mode
- && !S_ISLNK(h_idst->i_mode)) {
- ia.ia_valid |= ATTR_MODE;
- ia.ia_mode = h_isrc->i_mode;
+ if (h_src_attr && h_src_attr->valid) {
+ h_st = &h_src_attr->st;
+ ia.ia_uid = h_st->uid;
+ ia.ia_gid = h_st->gid;
+ ia.ia_atime = h_st->atime;
+ ia.ia_mtime = h_st->mtime;
+ if (h_idst->i_mode != h_st->mode
+ && !S_ISLNK(h_idst->i_mode)) {
+ ia.ia_valid |= ATTR_MODE;
+ ia.ia_mode = h_st->mode;
+ }
+ sbits = !!(h_st->mode & (S_ISUID | S_ISGID));
+ au_cpup_attr_flags(h_idst, h_src_attr->iflags);
+ } else {
+ ia.ia_uid = h_isrc->i_uid;
+ ia.ia_gid = h_isrc->i_gid;
+ ia.ia_atime = h_isrc->i_atime;
+ ia.ia_mtime = h_isrc->i_mtime;
+ if (h_idst->i_mode != h_isrc->i_mode
+ && !S_ISLNK(h_idst->i_mode)) {
+ ia.ia_valid |= ATTR_MODE;
+ ia.ia_mode = h_isrc->i_mode;
+ }
+ sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID));
+ au_cpup_attr_flags(h_idst, h_isrc->i_flags);
}
- sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID));
- au_cpup_attr_flags(h_idst, h_isrc);
err = vfsub_notify_change(&h_path, &ia);
/* is this nfs only? */
wbytes -= b;
p += b;
}
+ if (unlikely(err < 0))
+ break;
} else {
loff_t res;
* to support a sparse file which is opened with O_APPEND,
* we need to close the file.
*/
-static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst,
- aufs_bindex_t bsrc, loff_t len)
+static int au_cp_regular(struct au_cp_generic *cpg)
{
int err, i;
enum { SRC, DST };
void *label, *label_file;
} *f, file[] = {
{
- .bindex = bsrc,
+ .bindex = cpg->bsrc,
.flags = O_RDONLY | O_NOATIME | O_LARGEFILE,
.file = NULL,
.label = &&out,
.label_file = &&out_src
},
{
- .bindex = bdst,
+ .bindex = cpg->bdst,
.flags = O_WRONLY | O_NOATIME | O_LARGEFILE,
.file = NULL,
.label = &&out_src,
struct super_block *sb;
/* bsrc branch can be ro/rw. */
- sb = dentry->d_sb;
+ sb = cpg->dentry->d_sb;
f = file;
for (i = 0; i < 2; i++, f++) {
- f->dentry = au_h_dptr(dentry, f->bindex);
- f->file = au_h_open(dentry, f->bindex, f->flags, /*file*/NULL);
+ f->dentry = au_h_dptr(cpg->dentry, f->bindex);
+ f->file = au_h_open(cpg->dentry, f->bindex, f->flags,
+ /*file*/NULL);
err = PTR_ERR(f->file);
if (IS_ERR(f->file))
goto *f->label;
/* try stopping to update while we copyup */
IMustLock(file[SRC].dentry->d_inode);
- err = au_copy_file(file[DST].file, file[SRC].file, len);
+ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len);
out_dst:
fput(file[DST].file);
return err;
}
-static int au_do_cpup_regular(struct dentry *dentry, aufs_bindex_t bdst,
- aufs_bindex_t bsrc, loff_t len,
- struct inode *h_dir, struct path *h_path)
+static int au_do_cpup_regular(struct au_cp_generic *cpg,
+ struct au_cpup_reg_attr *h_src_attr)
{
int err, rerr;
loff_t l;
+ struct dentry *h_src_dentry;
+ struct inode *h_src_inode;
+ struct vfsmount *h_src_mnt;
err = 0;
- l = i_size_read(au_h_iptr(dentry->d_inode, bsrc));
- if (len == -1 || l < len)
- len = l;
- if (len)
- err = au_cp_regular(dentry, bdst, bsrc, len);
- if (!err)
- goto out; /* success */
-
- rerr = vfsub_unlink(h_dir, h_path, /*force*/0);
- if (rerr) {
- AuIOErr("failed unlinking cpup-ed %.*s(%d, %d)\n",
- AuDLNPair(h_path->dentry), err, rerr);
- err = -EIO;
+ h_src_inode = au_h_iptr(cpg->dentry->d_inode, cpg->bsrc);
+ l = i_size_read(h_src_inode);
+ if (cpg->len == -1 || l < cpg->len)
+ cpg->len = l;
+ if (cpg->len) {
+ /* try stopping to update while we are referencing */
+ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD);
+ au_pin_hdir_unlock(cpg->pin);
+
+ h_src_dentry = au_h_dptr(cpg->dentry, cpg->bsrc);
+ h_src_mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc);
+ h_src_attr->iflags = h_src_inode->i_flags;
+ err = vfs_getattr(h_src_mnt, h_src_dentry, &h_src_attr->st);
+ if (unlikely(err)) {
+ mutex_unlock(&h_src_inode->i_mutex);
+ goto out;
+ }
+ h_src_attr->valid = 1;
+ err = au_cp_regular(cpg);
+ mutex_unlock(&h_src_inode->i_mutex);
+ rerr = au_pin_hdir_relock(cpg->pin);
+ if (!err && rerr)
+ err = rerr;
}
out:
return err;
}
-/* return with the lower dst inode is locked */
static noinline_for_stack
-int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
- aufs_bindex_t bsrc, loff_t len, unsigned int flags,
- struct dentry *dst_parent)
+int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent,
+ struct au_cpup_reg_attr *h_src_attr)
{
int err;
umode_t mode;
unsigned int mnt_flags;
unsigned char isdir;
- const unsigned char do_dt = !!au_ftest_cpup(flags, DTIME);
+ const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME);
struct au_dtime dt;
struct path h_path;
struct dentry *h_src, *h_dst, *h_parent;
struct super_block *sb;
/* bsrc branch can be ro/rw. */
- h_src = au_h_dptr(dentry, bsrc);
+ h_src = au_h_dptr(cpg->dentry, cpg->bsrc);
h_inode = h_src->d_inode;
- AuDebugOn(h_inode != au_h_iptr(dentry->d_inode, bsrc));
+ AuDebugOn(h_inode != au_h_iptr(cpg->dentry->d_inode, cpg->bsrc));
/* try stopping to be referenced while we are creating */
- h_dst = au_h_dptr(dentry, bdst);
+ h_dst = au_h_dptr(cpg->dentry, cpg->bdst);
+ if (au_ftest_cpup(cpg->flags, RENAME))
+ AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX,
+ AUFS_WH_PFX_LEN));
h_parent = h_dst->d_parent; /* dir inode is locked */
h_dir = h_parent->d_inode;
IMustLock(h_dir);
AuDebugOn(h_parent != h_dst->d_parent);
- sb = dentry->d_sb;
- h_path.mnt = au_sbr_mnt(sb, bdst);
+ sb = cpg->dentry->d_sb;
+ h_path.mnt = au_sbr_mnt(sb, cpg->bdst);
if (do_dt) {
h_path.dentry = h_parent;
au_dtime_store(&dt, dst_parent, &h_path);
mode = h_inode->i_mode;
switch (mode & S_IFMT) {
case S_IFREG:
- /* try stopping to update while we are referencing */
- IMustLock(h_inode);
err = vfsub_create(h_dir, &h_path, mode | S_IWUSR);
if (!err)
- err = au_do_cpup_regular
- (dentry, bdst, bsrc, len,
- au_h_iptr(dst_parent->d_inode, bdst), &h_path);
+ err = au_do_cpup_regular(cpg, h_src_attr);
break;
case S_IFDIR:
isdir = 1;
* strange behaviour from the users view,
* particularry setattr case
*/
- if (au_ibstart(dst_parent->d_inode) == bdst)
+ if (au_ibstart(dst_parent->d_inode) == cpg->bdst)
au_cpup_attr_nlink(dst_parent->d_inode,
/*force*/1);
- au_cpup_attr_nlink(dentry->d_inode, /*force*/1);
+ au_cpup_attr_nlink(cpg->dentry->d_inode, /*force*/1);
}
break;
case S_IFLNK:
&& au_opt_test(mnt_flags, XINO)
&& h_inode->i_nlink == 1
/* todo: unnecessary? */
- /* && dentry->d_inode->i_nlink == 1 */
- && bdst < bsrc
- && !au_ftest_cpup(flags, KEEPLINO))
- au_xino_write(sb, bsrc, h_inode->i_ino, /*ino*/0);
+ /* && cpg->dentry->d_inode->i_nlink == 1 */
+ && cpg->bdst < cpg->bsrc
+ && !au_ftest_cpup(cpg->flags, KEEPLINO))
+ au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0);
/* ignore this error */
if (do_dt)
return err;
}
+static int au_do_ren_after_cpup(struct dentry *dentry, aufs_bindex_t bdst,
+ struct path *h_path)
+{
+ int err;
+ struct dentry *h_dentry, *h_parent;
+ struct inode *h_dir;
+
+ h_dentry = dget(au_h_dptr(dentry, bdst));
+ au_set_h_dptr(dentry, bdst, NULL);
+ err = au_lkup_neg(dentry, bdst, /*wh*/0);
+ if (unlikely(err)) {
+ au_set_h_dptr(dentry, bdst, h_dentry);
+ goto out;
+ }
+
+ h_path->dentry = dget(au_h_dptr(dentry, bdst));
+ au_set_h_dptr(dentry, bdst, h_dentry);
+ h_parent = h_dentry->d_parent; /* dir inode is locked */
+ h_dir = h_parent->d_inode;
+ IMustLock(h_dir);
+ AuDbg("%.*s %.*s\n", AuDLNPair(h_dentry), AuDLNPair(h_path->dentry));
+ err = vfsub_rename(h_dir, h_dentry, h_dir, h_path);
+ dput(h_path->dentry);
+
+out:
+ return err;
+}
+
/*
* copyup the @dentry from @bsrc to @bdst.
* the caller must set the both of lower dentries.
* @len is for truncating when it is -1 copyup the entire file.
* in link/rename cases, @dst_parent may be different from the real one.
*/
-static int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
- aufs_bindex_t bsrc, loff_t len, unsigned int flags,
- struct dentry *dst_parent)
+static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent)
{
int err, rerr;
aufs_bindex_t old_ibstart;
unsigned char isdir, plink;
- struct au_dtime dt;
- struct path h_path;
struct dentry *h_src, *h_dst, *h_parent;
struct inode *dst_inode, *h_dir, *inode;
struct super_block *sb;
+ struct au_branch *br;
+ /* to reuduce stack size */
+ struct {
+ struct au_dtime dt;
+ struct path h_path;
+ struct au_cpup_reg_attr h_src_attr;
+ } *a;
- AuDebugOn(bsrc <= bdst);
+ AuDebugOn(cpg->bsrc <= cpg->bdst);
- sb = dentry->d_sb;
- h_path.mnt = au_sbr_mnt(sb, bdst);
- h_dst = au_h_dptr(dentry, bdst);
+ err = -ENOMEM;
+ a = kmalloc(sizeof(*a), GFP_NOFS);
+ if (unlikely(!a))
+ goto out;
+ a->h_src_attr.valid = 0;
+
+ sb = cpg->dentry->d_sb;
+ br = au_sbr(sb, cpg->bdst);
+ a->h_path.mnt = au_br_mnt(br);
+ h_dst = au_h_dptr(cpg->dentry, cpg->bdst);
h_parent = h_dst->d_parent; /* dir inode is locked */
h_dir = h_parent->d_inode;
IMustLock(h_dir);
- h_src = au_h_dptr(dentry, bsrc);
- inode = dentry->d_inode;
+ h_src = au_h_dptr(cpg->dentry, cpg->bsrc);
+ inode = cpg->dentry->d_inode;
if (!dst_parent)
- dst_parent = dget_parent(dentry);
+ dst_parent = dget_parent(cpg->dentry);
else
dget(dst_parent);
plink = !!au_opt_test(au_mntflags(sb), PLINK);
- dst_inode = au_h_iptr(inode, bdst);
+ dst_inode = au_h_iptr(inode, cpg->bdst);
if (dst_inode) {
if (unlikely(!plink)) {
err = -EIO;
AuIOErr("hi%lu(i%lu) exists on b%d "
"but plink is disabled\n",
- dst_inode->i_ino, inode->i_ino, bdst);
- goto out;
+ dst_inode->i_ino, inode->i_ino, cpg->bdst);
+ goto out_parent;
}
if (dst_inode->i_nlink) {
- const int do_dt = au_ftest_cpup(flags, DTIME);
+ const int do_dt = au_ftest_cpup(cpg->flags, DTIME);
- h_src = au_plink_lkup(inode, bdst);
+ h_src = au_plink_lkup(inode, cpg->bdst);
err = PTR_ERR(h_src);
if (IS_ERR(h_src))
- goto out;
+ goto out_parent;
if (unlikely(!h_src->d_inode)) {
err = -EIO;
AuIOErr("i%lu exists on a upper branch "
"but not pseudo-linked\n",
inode->i_ino);
dput(h_src);
- goto out;
+ goto out_parent;
}
if (do_dt) {
- h_path.dentry = h_parent;
- au_dtime_store(&dt, dst_parent, &h_path);
+ a->h_path.dentry = h_parent;
+ au_dtime_store(&a->dt, dst_parent, &a->h_path);
}
- h_path.dentry = h_dst;
- err = vfsub_link(h_src, h_dir, &h_path);
+
+ a->h_path.dentry = h_dst;
+ err = vfsub_link(h_src, h_dir, &a->h_path);
+ if (!err && au_ftest_cpup(cpg->flags, RENAME))
+ err = au_do_ren_after_cpup
+ (cpg->dentry, cpg->bdst, &a->h_path);
if (do_dt)
- au_dtime_revert(&dt);
+ au_dtime_revert(&a->dt);
dput(h_src);
- goto out;
+ goto out_parent;
} else
/* todo: cpup_wh_file? */
/* udba work */
au_update_ibrange(inode, /*do_put_zero*/1);
}
+ isdir = S_ISDIR(inode->i_mode);
old_ibstart = au_ibstart(inode);
- err = cpup_entry(dentry, bdst, bsrc, len, flags, dst_parent);
+ err = cpup_entry(cpg, dst_parent, &a->h_src_attr);
if (unlikely(err))
- goto out;
+ goto out_rev;
dst_inode = h_dst->d_inode;
mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2);
+ /* todo: necessary? */
+ /* au_pin_hdir_unlock(cpg->pin); */
- err = cpup_iattr(dentry, bdst, h_src);
- isdir = S_ISDIR(dst_inode->i_mode);
- if (!err) {
- if (bdst < old_ibstart) {
- if (S_ISREG(inode->i_mode)) {
- err = au_dy_iaop(inode, bdst, dst_inode);
- if (unlikely(err))
- goto out_rev;
+ err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr);
+ if (unlikely(err)) {
+ /* todo: necessary? */
+ /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */
+ mutex_unlock(&dst_inode->i_mutex);
+ goto out_rev;
+ }
+
+ if (cpg->bdst < old_ibstart) {
+ if (S_ISREG(inode->i_mode)) {
+ err = au_dy_iaop(inode, cpg->bdst, dst_inode);
+ if (unlikely(err)) {
+ /* ignore an error */
+ /* au_pin_hdir_relock(cpg->pin); */
+ mutex_unlock(&dst_inode->i_mutex);
+ goto out_rev;
}
- au_set_ibstart(inode, bdst);
}
- au_set_h_iptr(inode, bdst, au_igrab(dst_inode),
- au_hi_flags(inode, isdir));
- mutex_unlock(&dst_inode->i_mutex);
- if (!isdir
- && h_src->d_inode->i_nlink > 1
- && plink)
- au_plink_append(inode, bdst, h_dst);
- goto out; /* success */
+ au_set_ibstart(inode, cpg->bdst);
+ }
+ au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode),
+ au_hi_flags(inode, isdir));
+
+ /* todo: necessary? */
+ /* err = au_pin_hdir_relock(cpg->pin); */
+ mutex_unlock(&dst_inode->i_mutex);
+ if (unlikely(err))
+ goto out_rev;
+
+ if (!isdir
+ && h_src->d_inode->i_nlink > 1
+ && plink)
+ au_plink_append(inode, cpg->bdst, h_dst);
+
+ if (au_ftest_cpup(cpg->flags, RENAME)) {
+ a->h_path.dentry = h_dst;
+ err = au_do_ren_after_cpup(cpg->dentry, cpg->bdst, &a->h_path);
}
+ if (!err)
+ goto out_parent; /* success */
/* revert */
out_rev:
- h_path.dentry = h_parent;
- mutex_unlock(&dst_inode->i_mutex);
- au_dtime_store(&dt, dst_parent, &h_path);
- h_path.dentry = h_dst;
- if (!isdir)
- rerr = vfsub_unlink(h_dir, &h_path, /*force*/0);
- else
- rerr = vfsub_rmdir(h_dir, &h_path);
- au_dtime_revert(&dt);
+ a->h_path.dentry = h_parent;
+ au_dtime_store(&a->dt, dst_parent, &a->h_path);
+ a->h_path.dentry = h_dst;
+ rerr = 0;
+ if (h_dst->d_inode) {
+ if (!isdir)
+ rerr = vfsub_unlink(h_dir, &a->h_path, /*force*/0);
+ else
+ rerr = vfsub_rmdir(h_dir, &a->h_path);
+ }
+ au_dtime_revert(&a->dt);
if (rerr) {
AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr);
err = -EIO;
}
-
-out:
+out_parent:
dput(dst_parent);
+ kfree(a);
+out:
return err;
}
+#if 0 /* unused */
struct au_cpup_single_args {
int *errp;
- struct dentry *dentry;
- aufs_bindex_t bdst, bsrc;
- loff_t len;
- unsigned int flags;
+ struct au_cp_generic *cpg;
struct dentry *dst_parent;
};
static void au_call_cpup_single(void *args)
{
struct au_cpup_single_args *a = args;
- *a->errp = au_cpup_single(a->dentry, a->bdst, a->bsrc, a->len,
- a->flags, a->dst_parent);
+
+ au_pin_hdir_acquire_nest(a->cpg->pin);
+ *a->errp = au_cpup_single(a->cpg, a->dst_parent);
+ au_pin_hdir_release(a->cpg->pin);
}
+#endif
/*
* prevent SIGXFSZ in copy-up.
* testing CAP_MKNOD is for generic fs,
* but CAP_FSETID is for xfs only, currently.
*/
-static int au_cpup_sio_test(struct super_block *sb, umode_t mode)
+static int au_cpup_sio_test(struct au_pin *pin, umode_t mode)
{
int do_sio;
+ struct super_block *sb;
+ struct inode *h_dir;
do_sio = 0;
+ sb = au_pinned_parent(pin)->d_sb;
if (!au_wkq_test()
&& (!au_sbi(sb)->si_plink_maint_pid
|| au_plink_maint(sb, AuLock_NOPLM))) {
if (!do_sio)
do_sio = ((mode & (S_ISUID | S_ISGID))
&& !capable(CAP_FSETID));
+ /* this workaround may be removed in the future */
+ if (!do_sio) {
+ h_dir = au_pinned_h_dir(pin);
+ do_sio = h_dir->i_mode & S_ISVTX;
+ }
}
return do_sio;
}
-int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
- aufs_bindex_t bsrc, loff_t len, unsigned int flags,
- struct dentry *dst_parent)
+#if 0 /* unused */
+int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent)
{
int err, wkq_err;
struct dentry *h_dentry;
- h_dentry = au_h_dptr(dentry, bsrc);
- if (!au_cpup_sio_test(dentry->d_sb, h_dentry->d_inode->i_mode))
- err = au_cpup_single(dentry, bdst, bsrc, len, flags,
- dst_parent);
+ h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc);
+ if (!au_cpup_sio_test(pin, h_dentry->d_inode->i_mode))
+ err = au_cpup_single(cpg, dst_parent);
else {
struct au_cpup_single_args args = {
.errp = &err,
- .dentry = dentry,
- .bdst = bdst,
- .bsrc = bsrc,
- .len = len,
- .flags = flags,
+ .cpg = cpg,
.dst_parent = dst_parent
};
wkq_err = au_wkq_wait(au_call_cpup_single, &args);
return err;
}
+#endif
/*
* copyup the @dentry from the first active lower branch to @bdst,
* using au_cpup_single().
*/
-static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
- unsigned int flags)
+static int au_cpup_simple(struct au_cp_generic *cpg)
{
int err;
+ unsigned int flags_orig;
aufs_bindex_t bsrc, bend;
+ struct dentry *dentry, *h_dentry;
+
+ dentry = cpg->dentry;
+ DiMustWriteLock(dentry);
bend = au_dbend(dentry);
- for (bsrc = bdst + 1; bsrc <= bend; bsrc++)
- if (au_h_dptr(dentry, bsrc))
- break;
+ if (cpg->bsrc < 0) {
+ for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) {
+ h_dentry = au_h_dptr(dentry, bsrc);
+ if (h_dentry) {
+ AuDebugOn(!h_dentry->d_inode);
+ break;
+ }
+ }
+ AuDebugOn(bsrc > bend);
+ cpg->bsrc = bsrc;
+ }
- err = au_lkup_neg(dentry, bdst);
+ err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1);
if (!err) {
- err = au_cpup_single(dentry, bdst, bsrc, len, flags, NULL);
+ flags_orig = cpg->flags;
+ au_fset_cpup(cpg->flags, RENAME);
+ err = au_cpup_single(cpg, NULL);
+ cpg->flags = flags_orig;
if (!err)
return 0; /* success */
/* revert */
- au_set_h_dptr(dentry, bdst, NULL);
- au_set_dbstart(dentry, bsrc);
+ au_set_h_dptr(dentry, cpg->bdst, NULL);
+ au_set_dbstart(dentry, cpg->bsrc);
}
return err;
struct au_cpup_simple_args {
int *errp;
- struct dentry *dentry;
- aufs_bindex_t bdst;
- loff_t len;
- unsigned int flags;
+ struct au_cp_generic *cpg;
};
static void au_call_cpup_simple(void *args)
{
struct au_cpup_simple_args *a = args;
- *a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags);
+
+ au_pin_hdir_acquire_nest(a->cpg->pin);
+ *a->errp = au_cpup_simple(a->cpg);
+ au_pin_hdir_release(a->cpg->pin);
}
-int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
- unsigned int flags)
+int au_sio_cpup_simple(struct au_cp_generic *cpg)
{
int err, wkq_err;
- struct dentry *parent;
+ struct dentry *dentry, *parent;
+ struct file *h_file;
struct inode *h_dir;
+ dentry = cpg->dentry;
+ h_file = NULL;
+ if (au_ftest_cpup(cpg->flags, HOPEN)) {
+ AuDebugOn(cpg->bsrc < 0);
+ h_file = au_h_open_pre(dentry, cpg->bsrc);
+ err = PTR_ERR(h_file);
+ if (IS_ERR(h_file))
+ goto out;
+ }
+
parent = dget_parent(dentry);
- h_dir = au_h_iptr(parent->d_inode, bdst);
+ h_dir = au_h_iptr(parent->d_inode, cpg->bdst);
if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE)
- && !au_cpup_sio_test(dentry->d_sb, dentry->d_inode->i_mode))
- err = au_cpup_simple(dentry, bdst, len, flags);
+ && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode))
+ err = au_cpup_simple(cpg);
else {
struct au_cpup_simple_args args = {
.errp = &err,
- .dentry = dentry,
- .bdst = bdst,
- .len = len,
- .flags = flags
+ .cpg = cpg
};
wkq_err = au_wkq_wait(au_call_cpup_simple, &args);
if (unlikely(wkq_err))
}
dput(parent);
+ if (h_file)
+ au_h_open_post(dentry, cpg->bsrc, h_file);
+
+out:
return err;
}
/*
* copyup the deleted file for writing.
*/
-static int au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst,
- struct dentry *wh_dentry, struct file *file,
- loff_t len)
+static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry,
+ struct file *file)
{
int err;
- aufs_bindex_t bstart;
- struct au_dinfo *dinfo;
+ unsigned int flags_orig;
+ aufs_bindex_t bsrc_orig;
struct dentry *h_d_dst, *h_d_start;
+ struct au_dinfo *dinfo;
struct au_hdentry *hdp;
- dinfo = au_di(dentry);
+ dinfo = au_di(cpg->dentry);
AuRwMustWriteLock(&dinfo->di_rwsem);
- bstart = dinfo->di_bstart;
+ bsrc_orig = cpg->bsrc;
+ cpg->bsrc = dinfo->di_bstart;
hdp = dinfo->di_hdentry;
- h_d_dst = hdp[0 + bdst].hd_dentry;
- dinfo->di_bstart = bdst;
- hdp[0 + bdst].hd_dentry = wh_dentry;
+ h_d_dst = hdp[0 + cpg->bdst].hd_dentry;
+ dinfo->di_bstart = cpg->bdst;
+ hdp[0 + cpg->bdst].hd_dentry = wh_dentry;
+ h_d_start = NULL;
if (file) {
- h_d_start = hdp[0 + bstart].hd_dentry;
- hdp[0 + bstart].hd_dentry = au_hf_top(file)->f_dentry;
+ h_d_start = hdp[0 + cpg->bsrc].hd_dentry;
+ hdp[0 + cpg->bsrc].hd_dentry = au_hf_top(file)->f_dentry;
}
- err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME,
- /*h_parent*/NULL);
+ flags_orig = cpg->flags;
+ cpg->flags = !AuCpup_DTIME;
+ err = au_cpup_single(cpg, /*h_parent*/NULL);
+ cpg->flags = flags_orig;
if (file) {
if (!err)
err = au_reopen_nondir(file);
- hdp[0 + bstart].hd_dentry = h_d_start;
+ hdp[0 + cpg->bsrc].hd_dentry = h_d_start;
}
- hdp[0 + bdst].hd_dentry = h_d_dst;
- dinfo->di_bstart = bstart;
+ hdp[0 + cpg->bdst].hd_dentry = h_d_dst;
+ dinfo->di_bstart = cpg->bsrc;
+ cpg->bsrc = bsrc_orig;
return err;
}
-static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
- struct file *file)
+static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file)
{
int err;
+ aufs_bindex_t bdst;
struct au_dtime dt;
- struct dentry *parent, *h_parent, *wh_dentry;
+ struct dentry *dentry, *parent, *h_parent, *wh_dentry;
struct au_branch *br;
struct path h_path;
+ dentry = cpg->dentry;
+ bdst = cpg->bdst;
br = au_sbr(dentry->d_sb, bdst);
parent = dget_parent(dentry);
h_parent = au_h_dptr(parent, bdst);
goto out;
h_path.dentry = h_parent;
- h_path.mnt = br->br_mnt;
+ h_path.mnt = au_br_mnt(br);
au_dtime_store(&dt, parent, &h_path);
- err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len);
+ err = au_do_cpup_wh(cpg, wh_dentry, file);
if (unlikely(err))
goto out_wh;
struct au_cpup_wh_args {
int *errp;
- struct dentry *dentry;
- aufs_bindex_t bdst;
- loff_t len;
+ struct au_cp_generic *cpg;
struct file *file;
};
static void au_call_cpup_wh(void *args)
{
struct au_cpup_wh_args *a = args;
- *a->errp = au_cpup_wh(a->dentry, a->bdst, a->len, a->file);
+
+ au_pin_hdir_acquire_nest(a->cpg->pin);
+ *a->errp = au_cpup_wh(a->cpg, a->file);
+ au_pin_hdir_release(a->cpg->pin);
}
-int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
- struct file *file)
+int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file)
{
int err, wkq_err;
- struct dentry *parent, *h_orph, *h_parent, *h_dentry;
- struct inode *dir, *h_dir, *h_tmpdir, *h_inode;
+ aufs_bindex_t bdst;
+ struct dentry *dentry, *parent, *h_orph, *h_parent, *h_dentry;
+ struct inode *dir, *h_dir, *h_tmpdir;
struct au_wbr *wbr;
+ struct au_pin wh_pin, *pin_orig;
+ dentry = cpg->dentry;
+ bdst = cpg->bdst;
parent = dget_parent(dentry);
dir = parent->d_inode;
h_orph = NULL;
h_parent = NULL;
h_dir = au_igrab(au_h_iptr(dir, bdst));
h_tmpdir = h_dir;
+ pin_orig = NULL;
if (!h_dir->i_nlink) {
wbr = au_sbr(dentry->d_sb, bdst)->br_wbr;
h_orph = wbr->wbr_orph;
h_tmpdir = h_orph->d_inode;
au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0);
- /* this temporary unlock is safe */
if (file)
h_dentry = au_hf_top(file)->f_dentry;
else
h_dentry = au_h_dptr(dentry, au_dbstart(dentry));
- h_inode = h_dentry->d_inode;
- IMustLock(h_inode);
- mutex_unlock(&h_inode->i_mutex);
mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3);
- mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
/* todo: au_h_open_pre()? */
+
+ pin_orig = cpg->pin;
+ au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT,
+ AuLsc_I_PARENT3, cpg->pin->udba, AuPin_DI_LOCKED);
+ cpg->pin = &wh_pin;
}
if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE)
- && !au_cpup_sio_test(dentry->d_sb, dentry->d_inode->i_mode))
- err = au_cpup_wh(dentry, bdst, len, file);
+ && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode))
+ err = au_cpup_wh(cpg, file);
else {
struct au_cpup_wh_args args = {
.errp = &err,
- .dentry = dentry,
- .bdst = bdst,
- .len = len,
+ .cpg = cpg,
.file = file
};
wkq_err = au_wkq_wait(au_call_cpup_wh, &args);
/* todo: au_h_open_post()? */
au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0);
au_set_h_dptr(parent, bdst, h_parent);
+ AuDebugOn(!pin_orig);
+ cpg->pin = pin_orig;
}
iput(h_dir);
dput(parent);
/* cf. revalidate function in file.c */
int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst,
int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,
+ struct au_pin *pin,
struct dentry *h_parent, void *arg),
void *arg)
{
au_pin_set_dentry(&pin, d);
err = au_do_pin(&pin);
if (!err) {
- err = cp(d, bdst, h_parent, arg);
+ err = cp(d, bdst, &pin, h_parent, arg);
au_unpin(&pin);
}
}
}
static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst,
+ struct au_pin *pin,
struct dentry *h_parent __maybe_unused ,
void *arg __maybe_unused)
{
- return au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME);
+ struct au_cp_generic cpg = {
+ .dentry = dentry,
+ .bdst = bdst,
+ .bsrc = -1,
+ .len = 0,
+ .pin = pin,
+ .flags = AuCpup_DTIME
+ };
+ return au_sio_cpup_simple(&cpg);
}
int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst)
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
struct inode;
struct file;
+struct au_pin;
-void au_cpup_attr_flags(struct inode *dst, struct inode *src);
+void au_cpup_attr_flags(struct inode *dst, unsigned int iflags);
void au_cpup_attr_timesizes(struct inode *inode);
void au_cpup_attr_nlink(struct inode *inode, int force);
void au_cpup_attr_changeable(struct inode *inode);
/* ---------------------------------------------------------------------- */
+struct au_cp_generic {
+ struct dentry *dentry;
+ aufs_bindex_t bdst, bsrc;
+ loff_t len;
+ struct au_pin *pin;
+ unsigned int flags;
+};
+
/* cpup flags */
#define AuCpup_DTIME 1 /* do dtime_store/revert */
#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino,
for link(2) */
+#define AuCpup_RENAME (1 << 2) /* rename after cpup */
+#define AuCpup_HOPEN (1 << 3) /* call h_open_pre/post() in cpup */
+
#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name)
#define au_fset_cpup(flags, name) \
do { (flags) |= AuCpup_##name; } while (0)
do { (flags) &= ~AuCpup_##name; } while (0)
int au_copy_file(struct file *dst, struct file *src, loff_t len);
-int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
- aufs_bindex_t bsrc, loff_t len, unsigned int flags,
- struct dentry *dst_parent);
-int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
- unsigned int flags);
-int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
- struct file *file);
+int au_sio_cpup_simple(struct au_cp_generic *cpg);
+int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file);
int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst,
int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,
+ struct au_pin *pin,
struct dentry *h_parent, void *arg),
void *arg);
int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst);
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/* ---------------------------------------------------------------------- */
+struct dbgaufs_plink_arg {
+ int n;
+ char a[];
+};
+
+static int dbgaufs_plink_release(struct inode *inode __maybe_unused,
+ struct file *file)
+{
+ free_page((unsigned long)file->private_data);
+ return 0;
+}
+
+static int dbgaufs_plink_open(struct inode *inode, struct file *file)
+{
+ int err, i, limit;
+ unsigned long n, sum;
+ struct dbgaufs_plink_arg *p;
+ struct au_sbinfo *sbinfo;
+ struct super_block *sb;
+ struct au_sphlhead *sphl;
+
+ err = -ENOMEM;
+ p = (void *)get_zeroed_page(GFP_NOFS);
+ if (unlikely(!p))
+ goto out;
+
+ err = -EFBIG;
+ sbinfo = inode->i_private;
+ sb = sbinfo->si_sb;
+ si_noflush_read_lock(sb);
+ if (au_opt_test(au_mntflags(sb), PLINK)) {
+ limit = PAGE_SIZE - sizeof(p->n);
+
+ /* the number of buckets */
+ n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH);
+ p->n += n;
+ limit -= n;
+
+ sum = 0;
+ for (i = 0, sphl = sbinfo->si_plink;
+ i < AuPlink_NHASH;
+ i++, sphl++) {
+ n = au_sphl_count(sphl);
+ sum += n;
+
+ n = snprintf(p->a + p->n, limit, "%lu ", n);
+ p->n += n;
+ limit -= n;
+ if (unlikely(limit <= 0))
+ goto out_free;
+ }
+ p->a[p->n - 1] = '\n';
+
+ /* the sum of plinks */
+ n = snprintf(p->a + p->n, limit, "%lu\n", sum);
+ p->n += n;
+ limit -= n;
+ if (unlikely(limit <= 0))
+ goto out_free;
+ } else {
+#define str "1\n0\n0\n"
+ p->n = sizeof(str) - 1;
+ strcpy(p->a, str);
+#undef str
+ }
+ si_read_unlock(sb);
+
+ err = 0;
+ file->private_data = p;
+ goto out; /* success */
+
+out_free:
+ free_page((unsigned long)p);
+out:
+ return err;
+}
+
+static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dbgaufs_plink_arg *p;
+
+ p = file->private_data;
+ return simple_read_from_buffer(buf, count, ppos, p->a, p->n);
+}
+
+static const struct file_operations dbgaufs_plink_fop = {
+ .owner = THIS_MODULE,
+ .open = dbgaufs_plink_open,
+ .release = dbgaufs_plink_release,
+ .read = dbgaufs_plink_read
+};
+
+/* ---------------------------------------------------------------------- */
+
static int dbgaufs_xib_open(struct inode *inode, struct file *file)
{
int err;
for (; bindex <= bend; bindex++) {
br = au_sbr(sb, bindex);
xi = &br->br_xino;
- if (xi->xi_dbgaufs) {
- debugfs_remove(xi->xi_dbgaufs);
- xi->xi_dbgaufs = NULL;
- }
+ debugfs_remove(xi->xi_dbgaufs);
+ xi->xi_dbgaufs = NULL;
}
}
if (unlikely(!sbinfo->si_dbgaufs_xib))
goto out_dir;
+ sbinfo->si_dbgaufs_plink = debugfs_create_file
+ ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo,
+ &dbgaufs_plink_fop);
+ if (unlikely(!sbinfo->si_dbgaufs_plink))
+ goto out_dir;
+
err = dbgaufs_xigen_init(sbinfo);
if (!err)
goto out; /* success */
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
#include <linux/vt_kern.h>
#include "aufs.h"
-int aufs_debug;
+/* Returns 0, or -errno. arg is in kp->arg. */
+static int param_atomic_t_set(const char *val, const struct kernel_param *kp)
+{
+ int err, n;
+
+ err = kstrtoint(val, 0, &n);
+ if (!err) {
+ if (n > 0)
+ au_debug_on();
+ else
+ au_debug_off();
+ }
+ return err;
+}
+
+/* Returns length written or -errno. Buffer is 4k (ie. be short!) */
+static int param_atomic_t_get(char *buffer, const struct kernel_param *kp)
+{
+ atomic_t *a;
+
+ a = kp->arg;
+ return sprintf(buffer, "%d", atomic_read(a));
+}
+
+static struct kernel_param_ops param_ops_atomic_t = {
+ .set = param_atomic_t_set,
+ .get = param_atomic_t_get
+ /* void (*free)(void *arg) */
+};
+
+atomic_t aufs_debug = ATOMIC_INIT(0);
MODULE_PARM_DESC(debug, "debug print");
-module_param_named(debug, aufs_debug, int, S_IRUGO | S_IWUSR | S_IWGRP);
+module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP);
char *au_plevel = KERN_DEBUG;
#define dpri(fmt, ...) do { \
return -1;
}
- /* the type of i_blocks depends upon CONFIG_LSF */
+ /* the type of i_blocks depends upon CONFIG_LBDAF */
BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long)
&& sizeof(inode->i_blocks) != sizeof(u64));
if (wh) {
if (!iinfo)
return;
dpri("i-1: bstart %d, bend %d, gen %d\n",
- iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode));
+ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode, NULL));
if (iinfo->ii_bstart < 0)
return;
hn = 0;
if (!br || IS_ERR(br))
goto out;
- mnt = br->br_mnt;
+ mnt = au_br_mnt(br);
if (!mnt || IS_ERR(mnt))
goto out;
sb = mnt->mnt_sb;
a->mnt.mnt_sb = sb;
a->fake.br_perm = 0;
- a->fake.br_mnt = &a->mnt;
+ a->fake.br_path.mnt = &a->mnt;
a->fake.br_xino.xi_file = NULL;
atomic_set(&a->fake.br_count, 0);
smp_mb(); /* atomic_set */
void au_dbg_iattr(struct iattr *ia)
{
-#define AuBit(name) if (ia->ia_valid & ATTR_ ## name) \
- dpri(#name "\n")
+#define AuBit(name) \
+ do { \
+ if (ia->ia_valid & ATTR_ ## name) \
+ dpri(#name "\n"); \
+ } while (0)
AuBit(MODE);
AuBit(UID);
AuBit(GID);
continue;
h_inode = au_h_iptr(inode, bindex);
if (unlikely(h_inode != h_dentry->d_inode)) {
- int old = au_debug_test();
- if (!old)
- au_debug(1);
+ au_debug_on();
AuDbg("b%d, %s:%d\n", bindex, func, line);
AuDbgDentry(dentry);
AuDbgInode(inode);
- if (!old)
- au_debug(0);
+ au_debug_off();
BUG();
}
}
AuDebugOn(destr.len < NAME_MAX);
#ifdef CONFIG_4KSTACKS
- pr_warning("CONFIG_4KSTACKS is defined.\n");
+ pr_warn("CONFIG_4KSTACKS is defined.\n");
#endif
#ifdef AuForceNoBrs
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
#ifdef __KERNEL__
#include <asm/system.h>
+#include <linux/atomic.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/sysrq.h>
#define AuDebugOn(a) BUG_ON(a)
/* module parameter */
-extern int aufs_debug;
-static inline void au_debug(int n)
+extern atomic_t aufs_debug;
+static inline void au_debug_on(void)
{
- aufs_debug = n;
- smp_mb();
+ atomic_inc(&aufs_debug);
+}
+static inline void au_debug_off(void)
+{
+ atomic_dec_if_positive(&aufs_debug);
}
static inline int au_debug_test(void)
{
- return aufs_debug;
+ return atomic_read(&aufs_debug) > 0;
}
#else
#define AuDebugOn(a) do {} while (0)
-AuStubVoid(au_debug, int n)
+AuStubVoid(au_debug_on, void)
+AuStubVoid(au_debug_off, void)
AuStubInt0(au_debug_test, void)
#endif /* CONFIG_AUFS_DEBUG */
+#define param_check_atomic_t(name, p) __param_check(name, p, atomic_t)
+
/* ---------------------------------------------------------------------- */
/* debug print */
#define AuWarn1(fmt, ...) do { \
static unsigned char _c; \
if (!_c++) \
- pr_warning(fmt, ##__VA_ARGS__); \
+ pr_warn(fmt, ##__VA_ARGS__); \
} while (0)
#define AuErr1(fmt, ...) do { \
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
au_h_nd(&h_nd, nd);
h_nd.path.dentry = h_parent;
- h_nd.path.mnt = br->br_mnt;
+ h_nd.path.mnt = au_br_mnt(br);
err = vfsub_name_hash(name->name, &h_nd.last, name->len);
h_dentry = ERR_PTR(err);
/*
* lookup @dentry on @bindex which should be negative.
*/
-int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex)
+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh)
{
int err;
struct dentry *parent, *h_parent, *h_dentry;
+ struct au_branch *br;
parent = dget_parent(dentry);
h_parent = au_h_dptr(parent, bindex);
- h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent,
- au_sbr(dentry->d_sb, bindex));
+ br = au_sbr(dentry->d_sb, bindex);
+ if (wh)
+ h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name);
+ else
+ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent, br);
err = PTR_ERR(h_dentry);
if (IS_ERR(h_dentry))
goto out;
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;
+ h_nd.path.mnt = au_sbr_mnt(parent->d_sb, bindex);
path_get(&h_nd.path);
valid = reval(h_dentry, &h_nd);
path_put(&h_nd.path);
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,
struct nameidata *nd);
-int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex);
+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh);
int au_refresh_dentry(struct dentry *dentry, struct dentry *parent);
int au_reval_dpath(struct dentry *dentry, unsigned int sigen);
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
nlink += h_dir->i_nlink - 2;
if (h_dir->i_nlink < 2)
nlink += 2;
+ smp_mb();
set_nlink(dir, nlink);
}
nlink -= h_dir->i_nlink - 2;
if (h_dir->i_nlink < 2)
nlink -= 2;
+ smp_mb();
set_nlink(dir, nlink);
}
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2010-2012 Junjiro R. Okajima
+ * Copyright (C) 2010-2013 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
key->dk_op.dy_hop = op->dy_hop;
kref_init(&key->dk_kref);
- p->set(key, op->dy_hop, br->br_mnt->mnt_sb);
+ p->set(key, op->dy_hop, au_br_sb(br));
old = dy_gadd(spl, key);
if (old) {
kfree(key);
/*
- * Copyright (C) 2010-2012 Junjiro R. Okajima
+ * Copyright (C) 2010-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
return !!(dentry->d_flags & DCACHE_DISCONNECTED);
}
+int au_test_nfsd(void)
+{
+ int ret;
+ struct task_struct *tsk = current;
+ char comm[sizeof(tsk->comm)];
+
+ ret = 0;
+ if (tsk->flags & PF_KTHREAD) {
+ get_task_comm(comm, tsk);
+ ret = !strcmp(comm, "nfsd");
+ }
+
+ return ret;
+}
+
/* ---------------------------------------------------------------------- */
/* inode generation external table */
sigen = au_sigen(sb);
if (unlikely(is_bad_inode(inode)
|| IS_DEADDIR(inode)
- || sigen != au_iigen(inode)))
+ || sigen != au_iigen(inode, NULL)))
goto out_iput;
dentry = NULL;
dentry = ERR_PTR(err);
if (unlikely(err))
goto out_name;
- dentry = ERR_PTR(-ENOENT);
+ /* instead of ENOENT */
+ dentry = ERR_PTR(-ESTALE);
if (!arg.found)
goto out_name;
struct path path;
br = au_sbr(sb, nsi_lock->bindex);
- h_mnt = br->br_mnt;
+ h_mnt = au_br_mnt(br);
h_sb = h_mnt->mnt_sb;
/* todo: call lower fh_to_dentry()? fh_to_parent()? */
h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail),
err = -EPERM;
br = au_sbr(sb, bindex);
- h_sb = br->br_mnt->mnt_sb;
+ h_sb = au_br_sb(br);
if (unlikely(!h_sb->s_export_op)) {
AuErr1("%s branch is not exportable\n", au_sbtype(h_sb));
goto out_dput;
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
static int au_cpup_sp(struct dentry *dentry)
{
int err;
- aufs_bindex_t bcpup;
struct au_pin pin;
struct au_wr_dir_args wr_dir_args = {
.force_btgt = -1,
.flags = 0
};
+ struct au_cp_generic cpg = {
+ .dentry = dentry,
+ .bdst = -1,
+ .bsrc = -1,
+ .len = -1,
+ .pin = &pin,
+ .flags = AuCpup_DTIME
+ };
AuDbg("%.*s\n", AuDLNPair(dentry));
err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args);
if (unlikely(err < 0))
goto out;
- bcpup = err;
+ cpg.bdst = err;
err = 0;
- if (bcpup == au_dbstart(dentry))
+ if (cpg.bdst == au_dbstart(dentry))
goto out; /* success */
- err = au_pin(&pin, dentry, bcpup, au_opt_udba(dentry->d_sb),
+ err = au_pin(&pin, dentry, cpg.bdst, au_opt_udba(dentry->d_sb),
AuPin_MNT_WRITE);
if (!err) {
- err = au_sio_cpup_simple(dentry, bcpup, -1, AuCpup_DTIME);
+ err = au_sio_cpup_simple(&cpg);
au_unpin(&pin);
}
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
* handling file/dir, and address_space operation
*/
+#ifdef CONFIG_AUFS_DEBUG
+#include <linux/migrate.h>
+#endif
#include <linux/pagemap.h>
#include "aufs.h"
br = au_sbr(sb, bindex);
h_file = ERR_PTR(-EACCES);
exec_flag = flags & __FMODE_EXEC;
- if (exec_flag && (br->br_mnt->mnt_flags & MNT_NOEXEC))
+ if (exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC))
goto out;
/* drop flags for writing */
flags &= ~O_CREAT;
atomic_inc(&br->br_count);
h_path.dentry = h_dentry;
- h_path.mnt = br->br_mnt;
+ h_path.mnt = au_br_mnt(br);
if (!au_special_file(h_inode->i_mode))
h_file = vfsub_dentry_open(&h_path, flags);
else {
au_set_h_fptr(file, bstart, NULL);
}
AuDebugOn(au_fi(file)->fi_hdir);
- AuDebugOn(au_fbstart(file) < bstart);
+ /*
+ * 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);
err = PTR_ERR(h_file);
- if (IS_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);
}
static int au_ready_to_write_wh(struct file *file, loff_t len,
- aufs_bindex_t bcpup)
+ aufs_bindex_t bcpup, struct au_pin *pin)
{
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;
+ struct dentry *h_dentry, *hi_wh;
+ struct au_cp_generic cpg = {
+ .dentry = file->f_dentry,
+ .bdst = bcpup,
+ .bsrc = -1,
+ .len = len,
+ .pin = pin
+ };
+
+ au_update_dbstart(cpg.dentry);
+ inode = cpg.dentry->d_inode;
h_inode = NULL;
- if (au_dbstart(dentry) <= bcpup && au_dbend(dentry) >= bcpup) {
- h_dentry = au_h_dptr(dentry, bcpup);
+ if (au_dbstart(cpg.dentry) <= bcpup
+ && au_dbend(cpg.dentry) >= bcpup) {
+ h_dentry = au_h_dptr(cpg.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);
+ err = au_sio_cpup_wh(&cpg, 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));
+ && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK))
+ au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup));
return err;
}
int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin)
{
int err;
- aufs_bindex_t bstart, bcpup, dbstart;
- struct dentry *dentry, *parent, *h_dentry;
- struct inode *h_inode, *inode;
+ aufs_bindex_t dbstart;
+ struct dentry *parent, *h_dentry;
+ struct inode *inode;
struct super_block *sb;
struct file *h_file;
-
- dentry = file->f_dentry;
- sb = dentry->d_sb;
- inode = dentry->d_inode;
+ struct au_cp_generic cpg = {
+ .dentry = file->f_dentry,
+ .bdst = -1,
+ .bsrc = -1,
+ .len = len,
+ .pin = pin,
+ .flags = AuCpup_DTIME
+ };
+
+ sb = cpg.dentry->d_sb;
+ inode = cpg.dentry->d_inode;
AuDebugOn(au_special_file(inode->i_mode));
- bstart = au_fbstart(file);
- err = au_test_ro(sb, bstart, inode);
+ cpg.bsrc = au_fbstart(file);
+ err = au_test_ro(sb, cpg.bsrc, inode);
if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) {
- err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0);
+ err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE,
+ /*flags*/0);
goto out;
}
/* need to cpup or reopen */
- parent = dget_parent(dentry);
+ parent = dget_parent(cpg.dentry);
di_write_lock_parent(parent);
- err = AuWbrCopyup(au_sbi(sb), dentry);
- bcpup = err;
+ err = AuWbrCopyup(au_sbi(sb), cpg.dentry);
+ cpg.bdst = err;
if (unlikely(err < 0))
goto out_dgrade;
err = 0;
- if (!d_unhashed(dentry) && !au_h_dptr(parent, bcpup)) {
- err = au_cpup_dirs(dentry, bcpup);
+ if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) {
+ err = au_cpup_dirs(cpg.dentry, cpg.bdst);
if (unlikely(err))
goto out_dgrade;
}
- err = au_pin(pin, dentry, bcpup, AuOpt_UDBA_NONE,
+ err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE,
AuPin_DI_LOCKED | AuPin_MNT_WRITE);
if (unlikely(err))
goto out_dgrade;
h_dentry = au_hf_top(file)->f_dentry;
- h_inode = h_dentry->d_inode;
- dbstart = au_dbstart(dentry);
- if (dbstart <= bcpup) {
- h_dentry = au_h_dptr(dentry, bcpup);
+ dbstart = au_dbstart(cpg.dentry);
+ if (dbstart <= cpg.bdst) {
+ h_dentry = au_h_dptr(cpg.dentry, cpg.bdst);
AuDebugOn(!h_dentry);
- h_inode = h_dentry->d_inode;
- AuDebugOn(!h_inode);
- bstart = bcpup;
+ cpg.bsrc = cpg.bdst;
}
- if (dbstart <= bcpup /* just reopen */
- || !d_unhashed(dentry) /* copyup and reopen */
+ if (dbstart <= cpg.bdst /* just reopen */
+ || !d_unhashed(cpg.dentry) /* copyup and reopen */
) {
- mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
- h_file = au_h_open_pre(dentry, bstart);
- if (IS_ERR(h_file)) {
+ h_file = au_h_open_pre(cpg.dentry, cpg.bsrc);
+ if (IS_ERR(h_file))
err = PTR_ERR(h_file);
- h_file = NULL;
- } else {
+ else {
di_downgrade_lock(parent, AuLock_IR);
- if (dbstart > bcpup)
- err = au_sio_cpup_simple(dentry, bcpup, len,
- AuCpup_DTIME);
+ if (dbstart > cpg.bdst)
+ err = au_sio_cpup_simple(&cpg);
if (!err)
err = au_reopen_nondir(file);
+ au_h_open_post(cpg.dentry, cpg.bsrc, h_file);
}
- mutex_unlock(&h_inode->i_mutex);
- au_h_open_post(dentry, bstart, h_file);
} else { /* copyup as wh and reopen */
/*
* since writable hfsplus branch is not supported,
* h_open_pre/post() are unnecessary.
*/
- mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
- err = au_ready_to_write_wh(file, len, bcpup);
+ err = au_ready_to_write_wh(file, len, cpg.bdst, pin);
di_downgrade_lock(parent, AuLock_IR);
- mutex_unlock(&h_inode->i_mutex);
}
if (!err) {
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 dentry *parent, *hi_wh;
struct inode *inode;
struct super_block *sb;
+ struct au_cp_generic cpg = {
+ .dentry = file->f_dentry,
+ .bdst = -1,
+ .bsrc = -1,
+ .len = -1,
+ .pin = &pin,
+ .flags = AuCpup_DTIME
+ };
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))
+ sb = cpg.dentry->d_sb;
+ inode = cpg.dentry->d_inode;
+ cpg.bdst = au_ibstart(inode);
+ if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry))
goto out;
- parent = dget_parent(dentry);
- if (au_test_ro(sb, bstart, inode)) {
+ parent = dget_parent(cpg.dentry);
+ if (au_test_ro(sb, cpg.bdst, inode)) {
di_read_lock_parent(parent, !AuLock_IR);
- err = AuWbrCopyup(au_sbi(sb), dentry);
- bstart = err;
+ err = AuWbrCopyup(au_sbi(sb), cpg.dentry);
+ cpg.bdst = err;
di_read_unlock(parent, !AuLock_IR);
if (unlikely(err < 0))
goto out_parent;
}
di_read_lock_parent(parent, AuLock_IR);
- hi_wh = au_hi_wh(inode, bstart);
+ hi_wh = au_hi_wh(inode, cpg.bdst);
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);
+ && !d_unhashed(cpg.dentry)
+ && cpg.bdst < au_dbstart(cpg.dentry)) {
+ err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst);
if (unlikely(err))
goto out_unlock;
/* always superio. */
- err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE,
+ err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE,
AuPin_DI_LOCKED | AuPin_MNT_WRITE);
- if (!err)
- err = au_sio_cpup_simple(dentry, bstart, -1,
- AuCpup_DTIME);
- au_unpin(&pin);
+ if (!err) {
+ err = au_sio_cpup_simple(&cpg);
+ au_unpin(&pin);
+ }
} else if (hi_wh) {
/* already copied-up after unlink */
- err = au_reopen_wh(file, bstart, hi_wh);
+ err = au_reopen_wh(file, cpg.bdst, hi_wh);
*need_reopen = 0;
}
static int aufs_releasepage(struct page *page, gfp_t gfp)
{ AuUnsupport(); return 0; }
static int aufs_migratepage(struct address_space *mapping, struct page *newpage,
- struct page *page)
+ struct page *page, enum migrate_mode mode)
{ AuUnsupport(); return 0; }
static int aufs_launder_page(struct page *page)
{ AuUnsupport(); return 0; }
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
{
if (atomic_inc_return(&au_fi(f)->fi_mmapped))
return;
- pr_warning("fi_mmapped wrapped around\n");
+ pr_warn("fi_mmapped wrapped around\n");
while (!atomic_inc_return(&au_fi(f)->fi_mmapped))
;
}
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
int au_finfo_init(struct file *file, struct au_fidir *fidir)
{
- int err, lc_idx;
+ int err;
struct au_finfo *finfo;
struct dentry *dentry;
err = 0;
au_nfiles_inc(dentry->d_sb);
- lc_idx = AuLcNonDir_FIINFO;
- if (fidir)
- lc_idx = AuLcDir_FIINFO;
- au_rw_class(&finfo->fi_rwsem, au_lc_key + lc_idx);
+ /* verbose coding for lock class name */
+ if (!fidir)
+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcNonDir_FIINFO);
+ else
+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcDir_FIINFO);
au_rw_write_lock(&finfo->fi_rwsem);
finfo->fi_btop = -1;
finfo->fi_hdir = fidir;
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
#endif
}
-static inline int au_test_smbfs(struct super_block *sb __maybe_unused)
-{
-#if defined(CONFIG_SMB_FS) || defined(CONFIG_SMB_FS_MODULE)
- return sb->s_magic == SMB_SUPER_MAGIC;
-#else
- return 0;
-#endif
-}
-
static inline int au_test_ocfs2(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_OCFS2_FS) || defined(CONFIG_OCFS2_FS_MODULE)
static inline int au_test_ext4(struct super_block *sb __maybe_unused)
{
-#if defined(CONFIG_EXT4DEV_FS) || defined(CONFIG_EXT4DEV_FS_MODULE)
+#if defined(CONFIG_EXT4_FS) || defined(CONFIG_EXT4_FS_MODULE)
return sb->s_magic == EXT4_SUPER_MAGIC;
#else
return 0;
{
return au_test_nfs(sb)
|| au_test_fuse(sb)
- /* || au_test_smbfs(sb) */ /* untested */
/* || au_test_ocfs2(sb) */ /* untested */
/* || au_test_btrfs(sb) */ /* untested */
/* || au_test_coda(sb) */ /* untested */
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE
| FS_CREATE | FS_EVENT_ON_CHILD);
static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq);
+static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0);
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);
+ au_cache_free_hnotify(hn);
+ smp_mb__before_atomic_dec();
+ atomic64_dec(&au_hfsn_ifree);
+ wake_up(&au_hfsn_wq);
}
static int au_hfsn_alloc(struct au_hinode *hinode)
sb = hn->hn_aufs_inode->i_sb;
bindex = au_br_index(sb, hinode->hi_id);
br = au_sbr(sb, bindex);
- hn->hn_mark_dead = 0;
mark = &hn->hn_mark;
fsnotify_init_mark(mark, au_hfsn_free_mark);
mark->mask = AuHfsnMask;
/*mnt*/NULL, /*allow_dups*/1);
}
-static void au_hfsn_free(struct au_hinode *hinode)
+static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn)
{
- struct au_hnotify *hn;
struct fsnotify_mark *mark;
+ unsigned long long ull;
+
+ ull = atomic64_inc_return(&au_hfsn_ifree);
+ BUG_ON(!ull);
- hn = hinode->hi_notify;
mark = &hn->hn_mark;
fsnotify_destroy_mark(mark);
fsnotify_put_mark(mark);
- /* TODO: bad approach */
- wait_event(au_hfsn_wq, hn->hn_mark_dead);
+ /* free hn by myself */
+ return 0;
}
/* ---------------------------------------------------------------------- */
static char *au_hfsn_name(u32 mask)
{
#ifdef CONFIG_AUFS_DEBUG
-#define test_ret(flag) if (mask & flag) \
- return #flag;
+#define test_ret(flag) \
+ do { \
+ if (mask & flag) \
+ return #flag; \
+ } while (0)
test_ret(FS_ACCESS);
test_ret(FS_MODIFY);
test_ret(FS_ATTRIB);
h_dir = event->to_tell;
h_inode = event->inode;
#ifdef AuDbgHnotify
- au_debug(1);
+ au_debug_on();
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",
AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0);
/* WARN_ON(1); */
}
- au_debug(0);
+ au_debug_off();
#endif
AuDebugOn(!inode_mark);
return err;
}
+/* ---------------------------------------------------------------------- */
+
+static void au_hfsn_fin(void)
+{
+ AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree));
+ wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree));
+}
+
const struct au_hnotify_op au_hnotify_op = {
.ctl = au_hfsn_ctl,
.alloc = au_hfsn_alloc,
.free = au_hfsn_free,
+ .fin = au_hfsn_fin,
+
.reset_br = au_hfsn_reset_br,
.fin_br = au_hfsn_fin_br,
.init_br = au_hfsn_init_br
/*
- * Copyright (C) 2010-2012 Junjiro R. Okajima
+ * Copyright (C) 2010-2013 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
h_dentry = au_h_dptr(dentry, bindex);
AuDebugOn(!h_dentry);
AuDebugOn(!h_dentry->d_inode);
- IMustLock(h_dentry->d_inode);
h_file = NULL;
if (au_test_hfsplus(h_dentry->d_sb)
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
hn = hinode->hi_notify;
if (hn) {
- au_hnotify_op.free(hinode);
- au_cache_free_hnotify(hn);
hinode->hi_notify = NULL;
+ if (au_hnotify_op.free(hinode, hn))
+ au_cache_free_hnotify(hn);
}
}
err = 0;
if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
- pr_warning("branch root dir was changed\n");
+ pr_warn("branch root dir was changed\n");
goto out;
}
err = 1;
if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
- pr_warning("branch root dir was changed\n");
+ pr_warn("branch root dir was changed\n");
err = 0;
goto out;
}
if (IS_ROOT(dentry)
/* || (inode && inode->i_ino == AUFS_ROOT_INO) */
) {
- pr_warning("branch root dir was changed\n");
+ pr_warn("branch root dir was changed\n");
return 0;
}
if (au_ftest_hnjob(a->flags, MNTPNT)
&& a->dentry
&& d_mountpoint(a->dentry))
- pr_warning("mount-point %.*s is removed or renamed\n",
- AuDLNPair(a->dentry));
+ pr_warn("mount-point %.*s is removed or renamed\n",
+ AuDLNPair(a->dentry));
return 0;
}
goto out;
if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
- pr_warning("wrong root branch\n");
+ pr_warn("wrong root branch\n");
iput(inode);
inode = NULL;
goto out;
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
err = 0;
bindex = au_ibstart(inode);
br = au_sbr(sb, bindex);
- err = h_permission(h_inode, mask, br->br_mnt, br->br_perm);
+ err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm);
if (write_mask
&& !err
&& !special_file(h_inode->i_mode)) {
break;
br = au_sbr(sb, bindex);
- err = h_permission(h_inode, mask, br->br_mnt,
+ err = h_permission(h_inode, mask, au_br_mnt(br),
br->br_perm);
}
}
struct dentry *ret, *parent;
struct inode *inode;
struct super_block *sb;
- int err, npositive, lc_idx;
+ int err, npositive;
IMustLock(dir);
+ /* todo: support rcu-walk? */
+ ret = ERR_PTR(-ECHILD);
+ if (nd && (nd->flags & LOOKUP_RCU))
+ goto out;
+
+ ret = ERR_PTR(-ENAMETOOLONG);
+ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
+ goto out;
+
sb = dir->i_sb;
err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
ret = ERR_PTR(err);
if (unlikely(err))
goto out;
- ret = ERR_PTR(-ENAMETOOLONG);
- if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
- goto out_si;
err = au_di_init(dentry);
ret = ERR_PTR(err);
if (unlikely(err))
}
ret = d_splice_alias(inode, dentry);
+#if 0
+ if (unlikely(d_need_lookup(dentry))) {
+ spin_lock(&dentry->d_lock);
+ dentry->d_flags &= ~DCACHE_NEED_LOOKUP;
+ spin_unlock(&dentry->d_lock);
+ } else
+#endif
if (unlikely(IS_ERR(ret) && inode)) {
ii_write_unlock(inode);
- lc_idx = AuLcNonDir_IIINFO;
- if (S_ISLNK(inode->i_mode))
- lc_idx = AuLcSymlink_IIINFO;
- else if (S_ISDIR(inode->i_mode))
- lc_idx = AuLcDir_IIINFO;
- au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + lc_idx);
iput(inode);
+ inode = NULL;
}
out_unlock:
di_write_unlock(dentry);
- if (unlikely(IS_ERR(ret) && inode)) {
- lc_idx = AuLcNonDir_DIINFO;
- if (S_ISLNK(inode->i_mode))
- lc_idx = AuLcSymlink_DIINFO;
- else if (S_ISDIR(inode->i_mode))
- lc_idx = AuLcDir_DIINFO;
- au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + lc_idx);
+ if (inode) {
+ /* verbose coding for lock class name */
+ if (unlikely(S_ISLNK(inode->i_mode)))
+ au_rw_class(&au_di(dentry)->di_rwsem,
+ au_lc_key + AuLcSymlink_DIINFO);
+ else if (unlikely(S_ISDIR(inode->i_mode)))
+ au_rw_class(&au_di(dentry)->di_rwsem,
+ au_lc_key + AuLcDir_DIINFO);
+ else /* likely */
+ au_rw_class(&au_di(dentry)->di_rwsem,
+ au_lc_key + AuLcNonDir_DIINFO);
}
out_si:
si_read_unlock(sb);
err = 0;
if (!au_h_dptr(parent, bcpup)) {
- if (bstart < bcpup)
+ if (bstart > bcpup)
+ err = au_cpup_dirs(dentry, bcpup);
+ else if (bstart < bcpup)
err = au_cpdown_dirs(dentry, bcpup);
else
- err = au_cpup_dirs(dentry, bcpup);
+ BUG();
}
if (!err && add_entry) {
h_parent = au_h_dptr(parent, bcpup);
h_dir = h_parent->d_inode;
mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);
- err = au_lkup_neg(dentry, bcpup);
+ err = au_lkup_neg(dentry, bcpup,
+ au_ftest_wrdir(add_entry, TMP_WHENTRY));
/* todo: no unlock here */
mutex_unlock(&h_dir->i_mutex);
struct au_wr_dir_args *args)
{
int err;
+ unsigned int flags;
aufs_bindex_t bcpup, bstart, src_bstart;
- const unsigned char add_entry = !!au_ftest_wrdir(args->flags,
- ADD_ENTRY);
+ const unsigned char add_entry
+ = au_ftest_wrdir(args->flags, ADD_ENTRY)
+ | au_ftest_wrdir(args->flags, TMP_WHENTRY);
struct super_block *sb;
struct dentry *parent;
struct au_sbinfo *sbinfo;
if (src_bstart < bstart)
bcpup = src_bstart;
} else if (add_entry) {
- err = AuWbrCreate(sbinfo, dentry,
- au_ftest_wrdir(args->flags, ISDIR));
+ flags = 0;
+ if (au_ftest_wrdir(args->flags, ISDIR))
+ au_fset_wbr(flags, DIR);
+ err = AuWbrCreate(sbinfo, dentry, flags);
bcpup = err;
}
/* ---------------------------------------------------------------------- */
+void au_pin_hdir_unlock(struct au_pin *p)
+{
+ if (p->hdir)
+ au_hn_imtx_unlock(p->hdir);
+}
+
+static int au_pin_hdir_lock(struct au_pin *p)
+{
+ int err;
+
+ err = 0;
+ if (!p->hdir)
+ goto out;
+
+ /* even if an error happens later, keep this lock */
+ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi);
+
+ err = -EBUSY;
+ if (unlikely(p->hdir->hi_inode != p->h_parent->d_inode))
+ goto out;
+
+ err = 0;
+ if (p->h_dentry)
+ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode,
+ p->h_parent, p->br);
+
+out:
+ return err;
+}
+
+int au_pin_hdir_relock(struct au_pin *p)
+{
+ int err, i;
+ struct inode *h_i;
+ struct dentry *h_d[] = {
+ p->h_dentry,
+ p->h_parent
+ };
+
+ err = au_pin_hdir_lock(p);
+ if (unlikely(err))
+ goto out;
+
+ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) {
+ if (!h_d[i])
+ continue;
+ h_i = h_d[i]->d_inode;
+ if (h_i)
+ err = !h_i->i_nlink;
+ }
+
+out:
+ return err;
+}
+
+void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task)
+{
+#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
+ p->hdir->hi_inode->i_mutex.owner = task;
+#endif
+}
+
+void au_pin_hdir_acquire_nest(struct au_pin *p)
+{
+ if (p->hdir) {
+ mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map,
+ p->lsc_hi, 0, NULL, _RET_IP_);
+ au_pin_hdir_set_owner(p, current);
+ }
+}
+
+void au_pin_hdir_release(struct au_pin *p)
+{
+ if (p->hdir) {
+ au_pin_hdir_set_owner(p, p->task);
+ mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_);
+ }
+}
+
struct dentry *au_pinned_h_parent(struct au_pin *pin)
{
if (pin && pin->parent)
if (!p->hdir)
return;
- au_hn_imtx_unlock(p->hdir);
+ au_pin_hdir_unlock(p);
if (!au_ftest_pin(p->flags, DI_LOCKED))
di_read_unlock(p->parent, AuLock_IR);
iput(p->hdir->hi_inode);
p->parent = NULL;
p->hdir = NULL;
p->h_mnt = NULL;
+ /* do not clear p->task */
}
int au_do_pin(struct au_pin *p)
{
int err;
struct super_block *sb;
- struct dentry *h_dentry, *h_parent;
- struct au_branch *br;
struct inode *h_dir;
err = 0;
sb = p->dentry->d_sb;
- br = au_sbr(sb, p->bindex);
+ p->br = au_sbr(sb, p->bindex);
if (IS_ROOT(p->dentry)) {
if (au_ftest_pin(p->flags, MNT_WRITE)) {
- p->h_mnt = br->br_mnt;
+ p->h_mnt = au_br_mnt(p->br);
err = mnt_want_write(p->h_mnt);
if (unlikely(err)) {
au_fclr_pin(p->flags, MNT_WRITE);
goto out;
}
- h_dentry = NULL;
+ p->h_dentry = NULL;
if (p->bindex <= au_dbend(p->dentry))
- h_dentry = au_h_dptr(p->dentry, p->bindex);
+ p->h_dentry = au_h_dptr(p->dentry, p->bindex);
p->parent = dget_parent(p->dentry);
if (!au_ftest_pin(p->flags, DI_LOCKED))
di_read_lock(p->parent, AuLock_IR, p->lsc_di);
h_dir = NULL;
- h_parent = au_h_dptr(p->parent, p->bindex);
+ p->h_parent = au_h_dptr(p->parent, p->bindex);
p->hdir = au_hi(p->parent->d_inode, p->bindex);
if (p->hdir)
h_dir = p->hdir->hi_inode;
* if DI_LOCKED is not set, then p->parent may be different
* and h_parent can be NULL.
*/
- if (unlikely(!p->hdir || !h_dir || !h_parent)) {
+ if (unlikely(!p->hdir || !h_dir || !p->h_parent)) {
err = -EBUSY;
if (!au_ftest_pin(p->flags, DI_LOCKED))
di_read_unlock(p->parent, AuLock_IR);
}
au_igrab(h_dir);
- au_hn_imtx_lock_nested(p->hdir, p->lsc_hi);
-
- if (unlikely(p->hdir->hi_inode != h_parent->d_inode)) {
- err = -EBUSY;
+ err = au_pin_hdir_lock(p);
+ if (unlikely(err))
goto out_unpin;
- }
- if (h_dentry) {
- err = au_h_verify(h_dentry, p->udba, h_dir, h_parent, br);
- if (unlikely(err)) {
- au_fclr_pin(p->flags, MNT_WRITE);
- goto out_unpin;
- }
- }
if (au_ftest_pin(p->flags, MNT_WRITE)) {
- p->h_mnt = br->br_mnt;
+ p->h_mnt = au_br_mnt(p->br);
err = mnt_want_write(p->h_mnt);
if (unlikely(err)) {
au_fclr_pin(p->flags, MNT_WRITE);
p->parent = NULL;
p->hdir = NULL;
p->h_mnt = NULL;
+
+ p->h_dentry = NULL;
+ p->h_parent = NULL;
+ p->br = NULL;
+ p->task = current;
}
int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
aufs_bindex_t bstart, ibstart;
struct dentry *hi_wh, *parent;
struct inode *inode;
- struct file *h_file;
struct au_wr_dir_args wr_dir_args = {
.force_btgt = -1,
.flags = 0
sz = -1;
if ((ia->ia_valid & ATTR_SIZE) && ia->ia_size < i_size_read(a->h_inode))
sz = ia->ia_size;
+ mutex_unlock(&a->h_inode->i_mutex);
- h_file = NULL;
hi_wh = NULL;
if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) {
hi_wh = au_hi_wh(inode, a->btgt);
if (!hi_wh) {
- err = au_sio_cpup_wh(dentry, a->btgt, sz, /*file*/NULL);
+ struct au_cp_generic cpg = {
+ .dentry = dentry,
+ .bdst = a->btgt,
+ .bsrc = -1,
+ .len = sz,
+ .pin = &a->pin
+ };
+ err = au_sio_cpup_wh(&cpg, /*file*/NULL);
if (unlikely(err))
goto out_unlock;
hi_wh = au_hi_wh(inode, a->btgt);
goto out; /* success */
if (!d_unhashed(dentry)) {
- h_file = au_h_open_pre(dentry, bstart);
- if (IS_ERR(h_file)) {
- err = PTR_ERR(h_file);
- h_file = NULL;
- } else
- err = au_sio_cpup_simple(dentry, a->btgt, sz,
- AuCpup_DTIME);
+ struct au_cp_generic cpg = {
+ .dentry = dentry,
+ .bdst = a->btgt,
+ .bsrc = bstart,
+ .len = sz,
+ .pin = &a->pin,
+ .flags = AuCpup_DTIME | AuCpup_HOPEN
+ };
+ err = au_sio_cpup_simple(&cpg);
if (!err)
a->h_path.dentry = au_h_dptr(dentry, a->btgt);
} else if (!hi_wh)
a->h_path.dentry = hi_wh; /* do not dget here */
out_unlock:
- mutex_unlock(&a->h_inode->i_mutex);
- au_h_open_post(dentry, bstart, h_file);
a->h_inode = a->h_path.dentry->d_inode;
- if (!err) {
- mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
+ if (!err)
goto out; /* success */
- }
-
au_unpin(&a->pin);
out_parent:
if (parent) {
dput(parent);
}
out:
+ if (!err)
+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
return err;
}
n = inode->i_nlink;
n -= nlink;
n += st->nlink;
+ smp_mb();
set_nlink(inode, n);
}
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
if (dt) {
struct path tmp = {
.dentry = h_parent,
- .mnt = br->br_mnt
+ .mnt = au_br_mnt(br)
};
au_dtime_store(dt, au_pinned_parent(pin), &tmp);
}
int err;
aufs_bindex_t bstart;
unsigned char created;
- struct au_dtime dt;
- struct au_pin pin;
- struct path h_path;
struct dentry *wh_dentry, *parent;
struct inode *h_dir;
- struct au_wr_dir_args wr_dir_args = {
- .force_btgt = -1,
- .flags = AuWrDir_ADD_ENTRY
- };
+ /* to reuduce stack size */
+ struct {
+ struct au_dtime dt;
+ struct au_pin pin;
+ struct path h_path;
+ struct au_wr_dir_args wr_dir_args;
+ } *a;
AuDbg("%.*s\n", AuDLNPair(dentry));
IMustLock(dir);
+ err = -ENOMEM;
+ a = kmalloc(sizeof(*a), GFP_NOFS);
+ if (unlikely(!a))
+ goto out;
+ a->wr_dir_args.force_btgt = -1;
+ a->wr_dir_args.flags = AuWrDir_ADD_ENTRY;
+
parent = dentry->d_parent; /* dir inode is locked */
err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
if (unlikely(err))
- goto out;
+ goto out_free;
err = au_d_may_add(dentry);
if (unlikely(err))
goto out_unlock;
di_write_lock_parent(parent);
- wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin,
- &wr_dir_args);
+ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL,
+ &a->pin, &a->wr_dir_args);
err = PTR_ERR(wh_dentry);
if (IS_ERR(wh_dentry))
goto out_parent;
bstart = au_dbstart(dentry);
- h_path.dentry = au_h_dptr(dentry, bstart);
- h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
- h_dir = au_pinned_h_dir(&pin);
+ a->h_path.dentry = au_h_dptr(dentry, bstart);
+ a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
+ h_dir = au_pinned_h_dir(&a->pin);
switch (arg->type) {
case Creat:
- err = vfsub_create(h_dir, &h_path, arg->u.c.mode);
+ err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode);
break;
case Symlink:
- err = vfsub_symlink(h_dir, &h_path, arg->u.s.symname);
+ err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname);
break;
case Mknod:
- err = vfsub_mknod(h_dir, &h_path, arg->u.m.mode, arg->u.m.dev);
+ err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode,
+ arg->u.m.dev);
break;
default:
BUG();
err = epilog(dir, bstart, wh_dentry, dentry);
/* revert */
- if (unlikely(created && err && h_path.dentry->d_inode)) {
+ if (unlikely(created && err && a->h_path.dentry->d_inode)) {
int rerr;
- rerr = vfsub_unlink(h_dir, &h_path, /*force*/0);
+ rerr = vfsub_unlink(h_dir, &a->h_path, /*force*/0);
if (rerr) {
AuIOErr("%.*s revert failure(%d, %d)\n",
AuDLNPair(dentry), err, rerr);
err = -EIO;
}
- au_dtime_revert(&dt);
+ au_dtime_revert(&a->dt);
}
- au_unpin(&pin);
+ au_unpin(&a->pin);
dput(wh_dentry);
out_parent:
d_drop(dentry);
}
aufs_read_unlock(dentry, AuLock_DW);
+out_free:
+ kfree(a);
out:
return err;
}
{
int err;
struct dentry *h_src_dentry;
- struct mutex *h_mtx;
- struct file *h_file;
+ struct au_cp_generic cpg = {
+ .dentry = src_dentry,
+ .bdst = a->bdst,
+ .bsrc = a->bsrc,
+ .len = -1,
+ .pin = &a->pin,
+ .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */
+ };
di_read_lock_parent(a->src_parent, AuLock_IR);
err = au_test_and_cpup_dirs(src_dentry, a->bdst);
goto out;
h_src_dentry = au_h_dptr(src_dentry, a->bsrc);
- h_mtx = &h_src_dentry->d_inode->i_mutex;
err = au_pin(&a->pin, src_dentry, a->bdst,
au_opt_udba(src_dentry->d_sb),
AuPin_DI_LOCKED | AuPin_MNT_WRITE);
if (unlikely(err))
goto out;
- mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
- h_file = au_h_open_pre(src_dentry, a->bsrc);
- if (IS_ERR(h_file)) {
- err = PTR_ERR(h_file);
- h_file = NULL;
- } else
- err = au_sio_cpup_simple(src_dentry, a->bdst, -1,
- AuCpup_DTIME /* | AuCpup_KEEPLINO */);
- mutex_unlock(h_mtx);
- au_h_open_post(src_dentry, a->bsrc, h_file);
+
+ err = au_sio_cpup_simple(&cpg);
au_unpin(&a->pin);
out:
return err;
}
-static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a)
+static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry,
+ struct au_link_args *a)
{
int err;
unsigned char plink;
- struct inode *h_inode, *inode;
+ aufs_bindex_t bend;
struct dentry *h_src_dentry;
+ struct inode *h_inode, *inode;
struct super_block *sb;
struct file *h_file;
h_inode = au_h_iptr(inode, a->bdst);
if (!h_inode || !h_inode->i_nlink) {
/* copyup src_dentry as the name of dentry. */
- au_set_dbstart(src_dentry, a->bdst);
- au_set_h_dptr(src_dentry, a->bdst, dget(a->h_path.dentry));
- h_inode = au_h_dptr(src_dentry, a->bsrc)->d_inode;
- mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
- h_file = au_h_open_pre(src_dentry, a->bsrc);
- if (IS_ERR(h_file)) {
+ bend = au_dbend(dentry);
+ if (bend < a->bsrc)
+ au_set_dbend(dentry, a->bsrc);
+ au_set_h_dptr(dentry, a->bsrc,
+ dget(au_h_dptr(src_dentry, a->bsrc)));
+ dget(a->h_path.dentry);
+ au_set_h_dptr(dentry, a->bdst, NULL);
+ dentry->d_inode = src_dentry->d_inode; /* tmp */
+ h_file = au_h_open_pre(dentry, a->bsrc);
+ if (IS_ERR(h_file))
err = PTR_ERR(h_file);
- h_file = NULL;
- } else
- err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc,
- -1, AuCpup_KEEPLINO,
- a->parent);
- mutex_unlock(&h_inode->i_mutex);
- au_h_open_post(src_dentry, a->bsrc, h_file);
- au_set_h_dptr(src_dentry, a->bdst, NULL);
- au_set_dbstart(src_dentry, a->bsrc);
+ else {
+ struct au_cp_generic cpg = {
+ .dentry = dentry,
+ .bdst = a->bdst,
+ .bsrc = -1,
+ .len = -1,
+ .pin = &a->pin,
+ .flags = AuCpup_KEEPLINO
+ };
+ err = au_sio_cpup_simple(&cpg);
+ au_h_open_post(dentry, a->bsrc, h_file);
+ if (!err) {
+ dput(a->h_path.dentry);
+ a->h_path.dentry = au_h_dptr(dentry, a->bdst);
+ } else
+ au_set_h_dptr(dentry, a->bdst,
+ a->h_path.dentry);
+ }
+ dentry->d_inode = NULL; /* restore */
+ au_set_h_dptr(dentry, a->bsrc, NULL);
+ au_set_dbend(dentry, bend);
} else {
/* the inode of src_dentry already exists on a.bdst branch */
h_src_dentry = d_find_alias(h_inode);
if (au_opt_test(au_mntflags(sb), PLINK)) {
if (a->bdst < a->bsrc
/* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */)
- err = au_cpup_or_link(src_dentry, a);
+ err = au_cpup_or_link(src_dentry, dentry, a);
else
err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin),
&a->h_path);
out_kfree:
kfree(a);
out:
+ AuTraceErr(err);
return err;
}
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
{
int err;
aufs_bindex_t bwh, bindex, bstart;
- struct au_dtime dt;
- struct au_pin pin;
- struct path h_path;
struct inode *inode, *h_dir;
struct dentry *parent, *wh_dentry;
+ /* to reuduce stack size */
+ struct {
+ struct au_dtime dt;
+ struct au_pin pin;
+ struct path h_path;
+ } *a;
IMustLock(dir);
+ err = -ENOMEM;
+ a = kmalloc(sizeof(*a), GFP_NOFS);
+ if (unlikely(!a))
+ goto out;
+
err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
if (unlikely(err))
- goto out;
+ goto out_free;
err = au_d_hashed_positive(dentry);
if (unlikely(err))
goto out_unlock;
bindex = -1;
parent = dentry->d_parent; /* dir inode is locked */
di_write_lock_parent(parent);
- wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt, &pin);
+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt,
+ &a->pin);
err = PTR_ERR(wh_dentry);
if (IS_ERR(wh_dentry))
goto out_parent;
- h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
- h_path.dentry = au_h_dptr(dentry, bstart);
- dget(h_path.dentry);
+ a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
+ a->h_path.dentry = au_h_dptr(dentry, bstart);
+ dget(a->h_path.dentry);
if (bindex == bstart) {
- h_dir = au_pinned_h_dir(&pin);
- err = vfsub_unlink(h_dir, &h_path, /*force*/0);
+ h_dir = au_pinned_h_dir(&a->pin);
+ err = vfsub_unlink(h_dir, &a->h_path, /*force*/0);
} else {
/* dir inode is locked */
h_dir = wh_dentry->d_parent->d_inode;
/* update target timestamps */
if (bindex == bstart) {
- vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/
- inode->i_ctime = h_path.dentry->d_inode->i_ctime;
+ vfsub_update_h_iattr(&a->h_path, /*did*/NULL);
+ /*ignore*/
+ inode->i_ctime = a->h_path.dentry->d_inode->i_ctime;
} else
/* todo: this timestamp may be reverted later */
inode->i_ctime = h_dir->i_ctime;
if (wh_dentry) {
int rerr;
- rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt);
+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry,
+ &a->dt);
if (rerr)
err = rerr;
}
out_unpin:
- au_unpin(&pin);
+ au_unpin(&a->pin);
dput(wh_dentry);
- dput(h_path.dentry);
+ dput(a->h_path.dentry);
out_parent:
di_write_unlock(parent);
out_unlock:
aufs_read_unlock(dentry, AuLock_DW);
+out_free:
+ kfree(a);
out:
return err;
}
{
int err, rmdir_later;
aufs_bindex_t bwh, bindex, bstart;
- struct au_dtime dt;
- struct au_pin pin;
struct inode *inode;
struct dentry *parent, *wh_dentry, *h_dentry;
struct au_whtmp_rmdir *args;
+ /* to reuduce stack size */
+ struct {
+ struct au_dtime dt;
+ struct au_pin pin;
+ } *a;
IMustLock(dir);
+ err = -ENOMEM;
+ a = kmalloc(sizeof(*a), GFP_NOFS);
+ if (unlikely(!a))
+ goto out;
+
err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN);
if (unlikely(err))
- goto out;
+ goto out_free;
err = au_alive_dir(dentry);
if (unlikely(err))
goto out_unlock;
bstart = au_dbstart(dentry);
bwh = au_dbwh(dentry);
bindex = -1;
- wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt, &pin);
+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt,
+ &a->pin);
err = PTR_ERR(wh_dentry);
if (IS_ERR(wh_dentry))
goto out_parent;
if (wh_dentry) {
int rerr;
- rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt);
+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry,
+ &a->dt);
if (rerr)
err = rerr;
}
out_unpin:
- au_unpin(&pin);
+ au_unpin(&a->pin);
dput(wh_dentry);
dput(h_dentry);
out_parent:
au_whtmp_rmdir_free(args);
out_unlock:
aufs_read_unlock(dentry, AuLock_DW);
+out_free:
+ kfree(a);
out:
AuTraceErr(err);
return err;
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
AuDebugOn(au_dbstart(d) != a->btgt);
err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt),
a->dst_h_dir, &a->h_path);
- } else {
- struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex;
- struct file *h_file;
+ } else
+ BUG();
- au_fset_ren(a->flags, CPUP);
- mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
- au_set_dbstart(d, a->btgt);
- au_set_h_dptr(d, a->btgt, dget(a->dst_h_dentry));
- h_file = au_h_open_pre(d, a->src_bstart);
- if (IS_ERR(h_file)) {
- err = PTR_ERR(h_file);
- h_file = NULL;
- } else
- err = au_sio_cpup_single(d, a->btgt, a->src_bstart, -1,
- !AuCpup_DTIME, a->dst_parent);
- mutex_unlock(h_mtx);
- au_h_open_post(d, a->src_bstart, h_file);
- if (!err) {
- d = a->dst_dentry;
- au_set_h_dptr(d, a->btgt, NULL);
- au_update_dbstart(d);
- } else {
- au_set_h_dptr(d, a->btgt, NULL);
- au_set_dbstart(d, a->src_bstart);
- }
- }
if (!err && a->h_dst)
/* it will be set to dinfo later */
dget(a->h_dst);
|| au_test_fs_remote(a->h_dst->d_sb)) {
err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist);
if (unlikely(err))
- pr_warning("failed removing whtmp dir %.*s (%d), "
- "ignored.\n", AuDLNPair(a->h_dst), err);
+ pr_warn("failed removing whtmp dir %.*s (%d), "
+ "ignored.\n", AuDLNPair(a->h_dst), err);
} else {
au_nhash_wh_free(&a->thargs->whlist);
a->thargs->whlist = a->whlist;
d = a->dst_dentry;
au_set_h_dptr(d, a->btgt, NULL);
- err = au_lkup_neg(d, a->btgt);
+ err = au_lkup_neg(d, a->btgt, /*wh*/0);
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;
- }
+ BUG_ON(a->dst_h_dentry->d_inode && a->src_bstart != a->btgt);
/* rename by vfs_rename or cpup */
d = a->dst_dentry;
sb = a->dst_dentry->d_sb;
if (au_ftest_ren(a->flags, MNT_WRITE))
- mnt_drop_write(a->br->br_mnt);
+ mnt_drop_write(au_br_mnt(a->br));
vfsub_unlock_rename(a->src_h_parent, a->src_hdir,
a->dst_h_parent, a->dst_hdir);
}
a->dst_h_parent->d_inode, a->dst_h_parent,
a->br);
if (!err) {
- err = mnt_want_write(a->br->br_mnt);
+ err = mnt_want_write(au_br_mnt(a->br));
if (unlikely(err))
goto out_unlock;
au_fset_ren(a->flags, MNT_WRITE);
} else if (unlikely(d_unhashed(a->dst_dentry)))
goto out_unlock;
+ /*
+ * is it possible?
+ * yes, it happend (in linux-3.3-rcN) but I don't know why.
+ * there may exist a problem somewhere else.
+ */
+ err = -EINVAL;
+ if (unlikely(a->dst_parent->d_inode == a->src_dentry->d_inode))
+ goto out_unlock;
+
au_fset_ren(a->flags, ISSAMEDIR); /* temporary */
di_write_lock_parent(a->dst_parent);
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;
+ a->h_path.mnt = au_br_mnt(a->br);
/* are they available to be renamed */
err = au_ren_may_dir(a);
if (err)
au_fset_ren(a->flags, WHSRC);
+ /* cpup src */
+ if (a->src_bstart != a->btgt) {
+ struct au_pin pin;
+
+ err = au_pin(&pin, a->src_dentry, a->btgt,
+ au_opt_udba(a->src_dentry->d_sb),
+ AuPin_DI_LOCKED | AuPin_MNT_WRITE);
+ if (!err) {
+ struct au_cp_generic cpg = {
+ .dentry = a->src_dentry,
+ .bdst = a->btgt,
+ .bsrc = a->src_bstart,
+ .len = -1,
+ .pin = &pin,
+ .flags = AuCpup_DTIME | AuCpup_HOPEN
+ };
+ AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart);
+ err = au_sio_cpup_simple(&cpg);
+ au_unpin(&pin);
+ }
+ if (unlikely(err))
+ goto out_children;
+ a->src_bstart = a->btgt;
+ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt);
+ au_fset_ren(a->flags, WHSRC);
+ }
+
/* lock them all */
err = au_ren_lock(a);
if (unlikely(err))
+ /* leave the copied-up one */
goto out_children;
if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE))
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
hinode->hi_whdentry = h_wh;
}
-void au_update_iigen(struct inode *inode)
+void au_update_iigen(struct inode *inode, int half)
{
- atomic_set(&au_ii(inode)->ii_generation, au_sigen(inode->i_sb));
- /* smp_mb(); */ /* atomic_set */
+ struct au_iinfo *iinfo;
+ struct au_iigen *iigen;
+ unsigned int sigen;
+
+ sigen = au_sigen(inode->i_sb);
+ iinfo = au_ii(inode);
+ iigen = &iinfo->ii_generation;
+ spin_lock(&iinfo->ii_genspin);
+ iigen->ig_generation = sigen;
+ if (half)
+ au_ig_fset(iigen->ig_flags, HALF_REFRESHED);
+ else
+ au_ig_fclr(iigen->ig_flags, HALF_REFRESHED);
+ spin_unlock(&iinfo->ii_genspin);
}
/* it may be called at remount time, too */
struct au_iinfo *iinfo = &c->iinfo;
static struct lock_class_key aufs_ii;
+ spin_lock_init(&iinfo->ii_genspin);
au_rw_init(&iinfo->ii_rwsem);
au_rw_class(&iinfo->ii_rwsem, &aufs_ii);
inode_init_once(&c->vfs_inode);
for (i = 0; i < nbr; i++)
iinfo->ii_hinode[i].hi_id = -1;
- atomic_set(&iinfo->ii_generation, au_sigen(sb));
- /* smp_mb(); */ /* atomic_set */
+ iinfo->ii_generation.ig_generation = au_sigen(sb);
iinfo->ii_bstart = -1;
iinfo->ii_bend = -1;
iinfo->ii_vdir = NULL;
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
static void au_refresh_hinode_attr(struct inode *inode, int do_version)
{
au_cpup_attr_all(inode, /*force*/0);
- au_update_iigen(inode);
+ au_update_iigen(inode, /*half*/1);
if (do_version)
inode->i_version++;
}
static int reval_inode(struct inode *inode, struct dentry *dentry)
{
int err;
+ unsigned int gen;
+ struct au_iigen iigen;
aufs_bindex_t bindex, bend;
struct inode *h_inode, *h_dinode;
bend = au_ibend(inode);
for (bindex = au_ibstart(inode); bindex <= bend; bindex++) {
h_inode = au_h_iptr(inode, bindex);
- if (h_inode && h_inode == h_dinode) {
- err = 0;
- if (au_iigen_test(inode, au_digen(dentry)))
- err = au_refresh_hinode(inode, dentry);
+ if (!h_inode || h_inode != h_dinode)
+ continue;
+
+ err = 0;
+ gen = au_iigen(inode, &iigen);
+ if (gen == au_digen(dentry)
+ && !au_ig_ftest(iigen.ig_flags, HALF_REFRESHED))
break;
- }
+
+ /* fully refresh inode using dentry */
+ err = au_refresh_hinode(inode, dentry);
+ if (!err)
+ au_update_iigen(inode, /*half*/0);
+ break;
}
if (unlikely(err))
AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
if (inode->i_state & I_NEW) {
+ /* verbose coding for lock class name */
+ if (unlikely(S_ISLNK(h_inode->i_mode)))
+ au_rw_class(&au_ii(inode)->ii_rwsem,
+ au_lc_key + AuLcSymlink_IIINFO);
+ else if (unlikely(S_ISDIR(h_inode->i_mode)))
+ au_rw_class(&au_ii(inode)->ii_rwsem,
+ au_lc_key + AuLcDir_IIINFO);
+ else /* likely */
+ au_rw_class(&au_ii(inode)->ii_rwsem,
+ au_lc_key + AuLcNonDir_IIINFO);
+
ii_write_lock_new_child(inode);
err = set_inode(inode, dentry);
if (!err) {
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
#ifdef CONFIG_AUFS_HFSNOTIFY
/* never use fsnotify_add_vfsmount_mark() */
struct fsnotify_mark hn_mark;
- int hn_mark_dead;
#endif
struct inode *hn_aufs_inode; /* no get/put */
#endif
struct dentry *hi_whdentry;
};
+/* ig_flags */
+#define AuIG_HALF_REFRESHED 1
+#define au_ig_ftest(flags, name) ((flags) & AuIG_##name)
+#define au_ig_fset(flags, name) \
+ do { (flags) |= AuIG_##name; } while (0)
+#define au_ig_fclr(flags, name) \
+ do { (flags) &= ~AuIG_##name; } while (0)
+
+struct au_iigen {
+ __u32 ig_generation, ig_flags;
+};
+
struct au_vdir;
struct au_iinfo {
- atomic_t ii_generation;
+ spinlock_t ii_genspin;
+ struct au_iigen ii_generation;
struct super_block *ii_hsb1; /* no get/put */
struct au_rwsem ii_rwsem;
struct dentry *parent;
struct au_hinode *hdir;
struct vfsmount *h_mnt;
+
+ /* temporary unlock/relock for copyup */
+ struct dentry *h_dentry, *h_parent;
+ struct au_branch *br;
+ struct task_struct *task;
};
+void au_pin_hdir_unlock(struct au_pin *p);
+int au_pin_hdir_relock(struct au_pin *p);
+void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task);
+void au_pin_hdir_acquire_nest(struct au_pin *p);
+void au_pin_hdir_release(struct au_pin *p);
+
/* ---------------------------------------------------------------------- */
static inline struct au_iinfo *au_ii(struct inode *inode)
/* au_wr_dir flags */
#define AuWrDir_ADD_ENTRY 1
-#define AuWrDir_ISDIR (1 << 1)
+#define AuWrDir_TMP_WHENTRY (1 << 1)
+#define AuWrDir_ISDIR (1 << 2)
#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name)
#define au_fset_wrdir(flags, name) \
do { (flags) |= AuWrDir_##name; } while (0)
void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
struct inode *h_inode, unsigned int flags);
-void au_update_iigen(struct inode *inode);
+void au_update_iigen(struct inode *inode, int half);
void au_update_ibrange(struct inode *inode, int do_put_zero);
void au_icntnr_init_once(void *_c);
#endif
}
-static inline unsigned int au_iigen(struct inode *inode)
+static inline unsigned int au_iigen(struct inode *inode, struct au_iigen *iigen)
{
- return atomic_read(&au_ii(inode)->ii_generation);
+ unsigned int gen;
+ struct au_iinfo *iinfo;
+
+ iinfo = au_ii(inode);
+ spin_lock(&iinfo->ii_genspin);
+ if (iigen)
+ *iigen = iinfo->ii_generation;
+ gen = iinfo->ii_generation.ig_generation;
+ spin_unlock(&iinfo->ii_genspin);
+
+ return gen;
}
/* tiny test for inode number */
static inline void au_iigen_dec(struct inode *inode)
{
- atomic_dec(&au_ii(inode)->ii_generation);
+ struct au_iinfo *iinfo;
+
+ iinfo = au_ii(inode);
+ spin_lock(&iinfo->ii_genspin);
+ iinfo->ii_generation.ig_generation--;
+ spin_unlock(&iinfo->ii_genspin);
}
static inline int au_iigen_test(struct inode *inode, unsigned int sigen)
int err;
err = 0;
- if (unlikely(inode && au_iigen(inode) != sigen))
+ if (unlikely(inode && au_iigen(inode, NULL) != sigen))
err = -EIO;
return err;
struct au_hnotify_op {
void (*ctl)(struct au_hinode *hinode, int do_set);
int (*alloc)(struct au_hinode *hinode);
- void (*free)(struct au_hinode *hinode);
+
+ /*
+ * if it returns true, the the caller should free hinode->hi_notify,
+ * otherwise ->free() frees it.
+ */
+ int (*free)(struct au_hinode *hinode,
+ struct au_hnotify *hn) __must_check;
void (*fin)(void);
int (*init)(void);
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
{
int ret;
struct task_struct *tsk = current;
+ char c, comm[sizeof(tsk->comm)];
ret = 0;
if (tsk->flags & PF_KTHREAD) {
- const char c = tsk->comm[4];
+ get_task_comm(comm, tsk);
+ c = comm[4];
ret = ('0' <= c && c <= '9'
- && !strncmp(tsk->comm, "loop", 4));
+ && !strncmp(comm, "loop", 4));
}
return ret;
pr:
spin_unlock(&spin);
- pr_warning("you may want to try another patch for loopback file "
- "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic);
+ pr_warn("you may want to try another patch for loopback file "
+ "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic);
}
int au_loopback_init(void)
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
{
int i;
- /* including AuCache_HNOTIFY */
- for (i = 0; i < AuCache_Last; i++)
+ /* excluding AuCache_HNOTIFY */
+ BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last);
+ for (i = 0; i < AuCache_HNOTIFY; i++)
if (au_cachep[i]) {
kmem_cache_destroy(au_cachep[i]);
au_cachep[i] = NULL;
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
AuCache_FINFO,
AuCache_VDIR,
AuCache_DEHSTR,
-#ifdef CONFIG_AUFS_HNOTIFY
- AuCache_HNOTIFY,
-#endif
+ AuCache_HNOTIFY, /* must be last */
AuCache_Last
};
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
{0, NULL}
};
-static match_table_t brrattr = {
+static match_table_t brattr = {
+ {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN},
{AuBrRAttr_WH, AUFS_BRRATTR_WH},
- {0, NULL}
-};
-
-static match_table_t brwattr = {
{AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH},
{0, NULL}
};
-#define AuBrStr_LONGEST AUFS_BRPERM_RW "+" AUFS_BRWATTR_NLWH
+#define AuBrStr_LONGEST AUFS_BRPERM_RW \
+ "+" AUFS_BRATTR_UNPIN \
+ "+" AUFS_BRWATTR_NLWH
static int br_attr_val(char *str, match_table_t table, substring_t args[])
{
else {
if (p)
*p = '+';
- pr_warning("ignored branch attribute %s\n", str);
+ pr_warn("ignored branch attribute %s\n", str);
break;
}
if (p)
static int noinline_for_stack br_perm_val(char *perm)
{
int val;
- char *p;
+ char *p, *q;
substring_t args[MAX_OPT_ARGS];
p = strchr(perm, '+');
if (!val) {
if (p)
*p = '+';
- pr_warning("ignored branch permission %s\n", perm);
+ pr_warn("ignored branch permission %s\n", perm);
val = AuBrPerm_RO;
goto out;
}
if (!p)
goto out;
- switch (val) {
+ p++;
+ while (1) {
+ q = strchr(p, '+');
+ if (q)
+ *q = 0;
+ val |= br_attr_val(p, brattr, args);
+ if (q) {
+ *q = '+';
+ p = q + 1;
+ } else
+ break;
+ }
+ switch (val & AuBrPerm_Mask) {
case AuBrPerm_RO:
case AuBrPerm_RR:
- val |= br_attr_val(p + 1, brrattr, args);
+ if (unlikely(val & AuBrWAttr_NoLinkWH)) {
+ pr_warn("ignored branch attribute %s\n",
+ AUFS_BRWATTR_NLWH);
+ val &= ~AuBrWAttr_NoLinkWH;
+ }
break;
case AuBrPerm_RW:
- val |= br_attr_val(p + 1, brwattr, args);
+ if (unlikely(val & AuBrRAttr_WH)) {
+ pr_warn("ignored branch attribute %s\n",
+ AUFS_BRRATTR_WH);
+ val &= ~AuBrRAttr_WH;
+ }
break;
}
AuDebugOn(1);
}
+ AppendAttr(AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN);
AppendAttr(AuBrRAttr_WH, AUFS_BRRATTR_WH);
AppendAttr(AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH);
{AuWbrCreate_MFSRRV, "mfsrr:%d:%d"},
{AuWbrCreate_PMFS, "pmfs"},
{AuWbrCreate_PMFSV, "pmfs:%d"},
+ {AuWbrCreate_PMFSRR, "pmfsrr:%d"},
+ {AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"},
{-1, NULL}
};
create->wbr_create = err;
switch (err) {
case AuWbrCreate_MFSRRV:
+ case AuWbrCreate_PMFSRRV:
e = au_wbr_mfs_wmark(&args[0], str, create);
if (!e)
e = au_wbr_mfs_sec(&args[1], str, create);
err = e;
break;
case AuWbrCreate_MFSRR:
+ case AuWbrCreate_PMFSRR:
e = au_wbr_mfs_wmark(&args[0], str, create);
if (unlikely(e)) {
err = e;
u.create->mfsrr_watermark);
break;
case AuWbrCreate_MFSRRV:
+ case AuWbrCreate_PMFSRRV:
AuDbg("%llu watermark, %d sec\n",
u.create->mfsrr_watermark,
u.create->mfs_second);
break;
case Opt_ignore:
- pr_warning("ignored %s\n", opt_str);
+ pr_warn("ignored %s\n", opt_str);
/*FALLTHROUGH*/
case Opt_ignore_silent:
skipped = 1;
switch (create->wbr_create) {
case AuWbrCreate_MFSRRV:
case AuWbrCreate_MFSRR:
+ case AuWbrCreate_PMFSRR:
+ case AuWbrCreate_PMFSRRV:
sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark;
/*FALLTHROUGH*/
case AuWbrCreate_MFS:
if (!(sb_flags & MS_RDONLY)) {
if (unlikely(!au_br_writable(au_sbr_perm(sb, 0))))
- pr_warning("first branch should be rw\n");
+ pr_warn("first branch should be rw\n");
if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH)))
- pr_warning("shwh should be used with ro\n");
+ pr_warn("shwh should be used with ro\n");
}
if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY)
&& !au_opt_test(sbinfo->si_mntflags, XINO))
- pr_warning("udba=*notify requires xino\n");
+ pr_warn("udba=*notify requires xino\n");
err = 0;
root = sb->s_root;
au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT);
if (wbr)
wbr_wh_write_lock(wbr);
- err = au_wh_init(au_h_dptr(root, bindex), br, sb);
+ err = au_wh_init(br, sb);
if (wbr)
wbr_wh_write_unlock(wbr);
au_hn_imtx_unlock(hdir);
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
AuWbrCreate_MFSRRV, /* mfs then rr with seconds */
AuWbrCreate_PMFS, /* parent and mfs */
AuWbrCreate_PMFSV, /* parent and mfs with seconds */
+ AuWbrCreate_PMFSRR, /* parent, mfs and round-robin */
+ AuWbrCreate_PMFSRRV, /* plus seconds */
AuWbrCreate_Def = AuWbrCreate_TDP
};
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/* ---------------------------------------------------------------------- */
-struct pseudo_link {
- union {
- struct list_head list;
- struct rcu_head rcu;
- };
- struct inode *inode;
-};
-
#ifdef CONFIG_AUFS_DEBUG
void au_plink_list(struct super_block *sb)
{
+ int i;
struct au_sbinfo *sbinfo;
- struct list_head *plink_list;
+ struct hlist_head *plink_hlist;
+ struct hlist_node *pos;
struct pseudo_link *plink;
SiMustAnyLock(sb);
AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));
AuDebugOn(au_plink_maint(sb, AuLock_NOPLM));
- 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();
+ for (i = 0; i < AuPlink_NHASH; i++) {
+ plink_hlist = &sbinfo->si_plink[i].head;
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(plink, pos, plink_hlist, hlist)
+ AuDbg("%lu\n", plink->inode->i_ino);
+ rcu_read_unlock();
+ }
}
#endif
/* is the inode pseudo-linked? */
int au_plink_test(struct inode *inode)
{
- int found;
+ int found, i;
struct au_sbinfo *sbinfo;
- struct list_head *plink_list;
+ struct hlist_head *plink_hlist;
+ struct hlist_node *pos;
struct pseudo_link *plink;
sbinfo = au_sbi(inode->i_sb);
AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM));
found = 0;
- plink_list = &sbinfo->si_plink.head;
+ i = au_plink_hash(inode->i_ino);
+ plink_hlist = &sbinfo->si_plink[i].head;
rcu_read_lock();
- list_for_each_entry_rcu(plink, plink_list, list)
+ hlist_for_each_entry_rcu(plink, pos, plink_hlist, hlist)
if (plink->inode == inode) {
found = 1;
break;
{
int err;
struct path h_path = {
- .mnt = br->br_mnt
+ .mnt = au_br_mnt(br)
};
struct inode *h_dir;
static void do_put_plink(struct pseudo_link *plink, int do_del)
{
if (do_del)
- list_del(&plink->list);
+ hlist_del(&plink->hlist);
iput(plink->inode);
kfree(plink);
}
{
struct super_block *sb;
struct au_sbinfo *sbinfo;
- struct list_head *plink_list;
+ struct hlist_head *plink_hlist;
+ struct hlist_node *pos;
struct pseudo_link *plink, *tmp;
- int found, err, cnt;
+ struct au_sphlhead *sphl;
+ int found, err, cnt, i;
sb = inode->i_sb;
sbinfo = au_sbi(sb);
AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));
AuDebugOn(au_plink_maint(sb, AuLock_NOPLM));
- cnt = 0;
- found = 0;
- plink_list = &sbinfo->si_plink.head;
- rcu_read_lock();
- list_for_each_entry_rcu(plink, plink_list, list) {
- cnt++;
- if (plink->inode == inode) {
- found = 1;
- break;
- }
- }
- rcu_read_unlock();
+ found = au_plink_test(inode);
if (found)
return;
+ i = au_plink_hash(inode->i_ino);
+ sphl = sbinfo->si_plink + i;
+ plink_hlist = &sphl->head;
tmp = kmalloc(sizeof(*plink), GFP_NOFS);
if (tmp)
tmp->inode = au_igrab(inode);
goto out;
}
- spin_lock(&sbinfo->si_plink.spin);
- list_for_each_entry(plink, plink_list, list) {
+ spin_lock(&sphl->spin);
+ hlist_for_each_entry(plink, pos, plink_hlist, hlist) {
if (plink->inode == inode) {
found = 1;
break;
}
}
if (!found)
- list_add_rcu(&tmp->list, plink_list);
- spin_unlock(&sbinfo->si_plink.spin);
+ hlist_add_head_rcu(&tmp->hlist, plink_hlist);
+ spin_unlock(&sphl->spin);
if (!found) {
- cnt++;
- WARN_ONCE(cnt > AUFS_PLINK_WARN,
- "unexpectedly many pseudo links, %d\n", cnt);
+ cnt = au_sphl_count(sphl);
+#define msg "unexpectedly unblanced or too many pseudo-links"
+ if (cnt > AUFS_PLINK_WARN)
+ AuWarn1(msg ", %d\n", cnt);
+#undef msg
err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex));
} else {
do_put_plink(tmp, 0);
out:
if (unlikely(err)) {
- pr_warning("err %d, damaged pseudo link.\n", err);
+ pr_warn("err %d, damaged pseudo link.\n", err);
if (tmp) {
- au_spl_del_rcu(&tmp->list, &sbinfo->si_plink);
+ au_sphl_del_rcu(&tmp->hlist, sphl);
call_rcu(&tmp->rcu, do_put_plink_rcu);
}
}
/* free all plinks */
void au_plink_put(struct super_block *sb, int verbose)
{
+ int i, warned;
struct au_sbinfo *sbinfo;
- struct list_head *plink_list;
- struct pseudo_link *plink, *tmp;
+ struct hlist_head *plink_hlist;
+ struct hlist_node *pos, *tmp;
+ struct pseudo_link *plink;
SiMustWriteLock(sb);
AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));
AuDebugOn(au_plink_maint(sb, AuLock_NOPLM));
- plink_list = &sbinfo->si_plink.head;
/* no spin_lock since sbinfo is write-locked */
- WARN(verbose && !list_empty(plink_list), "pseudo-link is not flushed");
- list_for_each_entry_safe(plink, tmp, plink_list, list)
- do_put_plink(plink, 0);
- INIT_LIST_HEAD(plink_list);
+ warned = 0;
+ for (i = 0; i < AuPlink_NHASH; i++) {
+ plink_hlist = &sbinfo->si_plink[i].head;
+ if (!warned && verbose && !hlist_empty(plink_hlist)) {
+ pr_warn("pseudo-link is not flushed");
+ warned = 1;
+ }
+ hlist_for_each_entry_safe(plink, pos, tmp, plink_hlist, hlist)
+ do_put_plink(plink, 0);
+ INIT_HLIST_HEAD(plink_hlist);
+ }
}
void au_plink_clean(struct super_block *sb, int verbose)
aufs_write_unlock(root);
}
+static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id)
+{
+ int do_put;
+ aufs_bindex_t bstart, bend, bindex;
+
+ do_put = 0;
+ bstart = au_ibstart(inode);
+ bend = au_ibend(inode);
+ if (bstart >= 0) {
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ if (!au_h_iptr(inode, bindex)
+ || au_ii_br_id(inode, bindex) != br_id)
+ continue;
+ au_set_h_iptr(inode, bindex, NULL, 0);
+ do_put = 1;
+ break;
+ }
+ if (do_put)
+ for (bindex = bstart; bindex <= bend; bindex++)
+ if (au_h_iptr(inode, bindex)) {
+ do_put = 0;
+ break;
+ }
+ } else
+ do_put = 1;
+
+ return do_put;
+}
+
/* free the plinks on a branch specified by @br_id */
void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id)
{
struct au_sbinfo *sbinfo;
- struct list_head *plink_list;
- struct pseudo_link *plink, *tmp;
+ struct hlist_head *plink_hlist;
+ struct hlist_node *pos, *tmp;
+ struct pseudo_link *plink;
struct inode *inode;
- aufs_bindex_t bstart, bend, bindex;
- unsigned char do_put;
+ int i, do_put;
SiMustWriteLock(sb);
AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));
AuDebugOn(au_plink_maint(sb, AuLock_NOPLM));
- plink_list = &sbinfo->si_plink.head;
/* no spin_lock since sbinfo is write-locked */
- list_for_each_entry_safe(plink, tmp, plink_list, list) {
- do_put = 0;
- inode = au_igrab(plink->inode);
- ii_write_lock_child(inode);
- bstart = au_ibstart(inode);
- bend = au_ibend(inode);
- if (bstart >= 0) {
- for (bindex = bstart; bindex <= bend; bindex++) {
- if (!au_h_iptr(inode, bindex)
- || au_ii_br_id(inode, bindex) != br_id)
- continue;
- au_set_h_iptr(inode, bindex, NULL, 0);
- do_put = 1;
- break;
- }
- } else
- do_put_plink(plink, 1);
-
- if (do_put) {
- for (bindex = bstart; bindex <= bend; bindex++)
- if (au_h_iptr(inode, bindex)) {
- do_put = 0;
- break;
- }
+ for (i = 0; i < AuPlink_NHASH; i++) {
+ plink_hlist = &sbinfo->si_plink[i].head;
+ hlist_for_each_entry_safe(plink, pos, tmp, plink_hlist, hlist) {
+ inode = au_igrab(plink->inode);
+ ii_write_lock_child(inode);
+ do_put = au_plink_do_half_refresh(inode, br_id);
if (do_put)
do_put_plink(plink, 1);
+ ii_write_unlock(inode);
+ iput(inode);
}
- ii_write_unlock(inode);
- iput(inode);
}
}
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2010-2012 Junjiro R. Okajima
+ * Copyright (C) 2010-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
*/
void au_si_free(struct kobject *kobj)
{
+ int i;
struct au_sbinfo *sbinfo;
char *locked __maybe_unused; /* debug only */
sbinfo = container_of(kobj, struct au_sbinfo, si_kobj);
- AuDebugOn(!list_empty(&sbinfo->si_plink.head));
+ for (i = 0; i < AuPlink_NHASH; i++)
+ AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head));
AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len));
au_rw_write_lock(&sbinfo->si_rwsem);
int au_si_alloc(struct super_block *sb)
{
- int err;
+ int err, i;
struct au_sbinfo *sbinfo;
static struct lock_class_key aufs_si;
atomic_long_set(&sbinfo->si_nfiles, 0);
sbinfo->si_bend = -1;
+ sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2;
sbinfo->si_wbr_copyup = AuWbrCopyup_Def;
sbinfo->si_wbr_create = AuWbrCreate_Def;
sbinfo->si_mntflags = au_opts_plink(AuOpt_Def);
+ sbinfo->si_xino_jiffy = jiffies;
+ sbinfo->si_xino_expire
+ = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC);
mutex_init(&sbinfo->si_xib_mtx);
sbinfo->si_xino_brid = -1;
/* leave si_xib_last_pindex and si_xib_next_bit */
sbinfo->si_rdhash = AUFS_RDHASH_DEF;
sbinfo->si_dirwh = AUFS_DIRWH_DEF;
- au_spl_init(&sbinfo->si_plink);
+ for (i = 0; i < AuPlink_NHASH; i++)
+ au_sphl_init(sbinfo->si_plink + i);
init_waitqueue_head(&sbinfo->si_plink_wq);
spin_lock_init(&sbinfo->si_plink_maint_lock);
gen = ++au_sbi(sb)->si_generation;
au_update_digen(sb->s_root);
- au_update_iigen(sb->s_root->d_inode);
+ au_update_iigen(sb->s_root->d_inode, /*half*/0);
sb->s_root->d_inode->i_version++;
return gen;
}
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
spin_unlock(&spl->spin);
}
+/* ---------------------------------------------------------------------- */
+
+struct au_sphlhead {
+ spinlock_t spin;
+ struct hlist_head head;
+};
+
+static inline void au_sphl_init(struct au_sphlhead *sphl)
+{
+ spin_lock_init(&sphl->spin);
+ INIT_HLIST_HEAD(&sphl->head);
+}
+
+static inline void au_sphl_add(struct hlist_node *hlist,
+ struct au_sphlhead *sphl)
+{
+ spin_lock(&sphl->spin);
+ hlist_add_head(hlist, &sphl->head);
+ spin_unlock(&sphl->spin);
+}
+
+static inline void au_sphl_del(struct hlist_node *hlist,
+ struct au_sphlhead *sphl)
+{
+ spin_lock(&sphl->spin);
+ hlist_del(hlist);
+ spin_unlock(&sphl->spin);
+}
+
+static inline void au_sphl_del_rcu(struct hlist_node *hlist,
+ struct au_sphlhead *sphl)
+{
+ spin_lock(&sphl->spin);
+ hlist_del_rcu(hlist);
+ spin_unlock(&sphl->spin);
+}
+
+static inline unsigned long au_sphl_count(struct au_sphlhead *sphl)
+{
+ unsigned long cnt;
+ struct hlist_node *pos;
+
+ cnt = 0;
+ spin_lock(&sphl->spin);
+ hlist_for_each(pos, &sphl->head)
+ cnt++;
+ spin_unlock(&sphl->spin);
+ return cnt;
+}
+
#endif /* __KERNEL__ */
#endif /* __AUFS_SPL_H__ */
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
hdp = au_di(sb->s_root)->di_hdentry;
for (bindex = 0; !err && bindex <= bend; bindex++) {
br = au_sbr(sb, bindex);
- path.mnt = br->br_mnt;
+ path.mnt = au_br_mnt(br);
path.dentry = hdp[bindex].hd_dentry;
err = au_seq_path(seq, &path);
if (err > 0) {
AuRwMustAnyLock(&sbinfo->si_rwsem);
- seq_printf(m, ",create=");
+ seq_puts(m, ",create=");
pat = au_optstr_wbr_create(v);
switch (v) {
case AuWbrCreate_TDP:
case AuWbrCreate_RR:
case AuWbrCreate_MFS:
case AuWbrCreate_PMFS:
- seq_printf(m, pat);
+ seq_puts(m, pat);
break;
case AuWbrCreate_MFSV:
seq_printf(m, /*pat*/"mfs:%lu",
jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
/ MSEC_PER_SEC);
break;
+ case AuWbrCreate_PMFSRR:
+ seq_printf(m, /*pat*/"pmfsrr:%llu",
+ sbinfo->si_wbr_mfs.mfsrr_watermark);
+ break;
+ case AuWbrCreate_PMFSRRV:
+ seq_printf(m, /*pat*/"pmfsrr:%llu:%lu",
+ sbinfo->si_wbr_mfs.mfsrr_watermark,
+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
+ / MSEC_PER_SEC);
+ break;
}
}
old = a;
a += b;
- if (old < a)
+ if (old <= a)
+ return a;
+ return ULLONG_MAX;
+}
+
+static u64 au_mul_till_max(u64 a, long mul)
+{
+ u64 old;
+
+ old = a;
+ a *= mul;
+ if (old <= a)
return a;
return ULLONG_MAX;
}
static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf)
{
int err;
+ long bsize, factor;
u64 blocks, bfree, bavail, files, ffree;
aufs_bindex_t bend, bindex, i;
unsigned char shared;
struct path h_path;
struct super_block *h_sb;
+ err = 0;
+ bsize = LONG_MAX;
+ files = 0;
+ ffree = 0;
blocks = 0;
bfree = 0;
bavail = 0;
- files = 0;
- ffree = 0;
-
- err = 0;
bend = au_sbend(sb);
- for (bindex = bend; bindex >= 0; bindex--) {
+ for (bindex = 0; bindex <= bend; bindex++) {
h_path.mnt = au_sbr_mnt(sb, bindex);
h_sb = h_path.mnt->mnt_sb;
shared = 0;
- for (i = bindex + 1; !shared && i <= bend; i++)
+ for (i = 0; !shared && i < bindex; i++)
shared = (au_sbr_sb(sb, i) == h_sb);
if (shared)
continue;
if (unlikely(err))
goto out;
- blocks = au_add_till_max(blocks, buf->f_blocks);
- bfree = au_add_till_max(bfree, buf->f_bfree);
- bavail = au_add_till_max(bavail, buf->f_bavail);
+ if (bsize > buf->f_bsize) {
+ /*
+ * we will reduce bsize, so we have to expand blocks
+ * etc. to match them again
+ */
+ factor = (bsize / buf->f_bsize);
+ blocks = au_mul_till_max(blocks, factor);
+ bfree = au_mul_till_max(bfree, factor);
+ bavail = au_mul_till_max(bavail, factor);
+ bsize = buf->f_bsize;
+ }
+
+ factor = (buf->f_bsize / bsize);
+ blocks = au_add_till_max(blocks,
+ au_mul_till_max(buf->f_blocks, factor));
+ bfree = au_add_till_max(bfree,
+ au_mul_till_max(buf->f_bfree, factor));
+ bavail = au_add_till_max(bavail,
+ au_mul_till_max(buf->f_bavail, factor));
files = au_add_till_max(files, buf->f_files);
ffree = au_add_till_max(ffree, buf->f_ffree);
}
+ buf->f_bsize = bsize;
buf->f_blocks = blocks;
buf->f_bfree = bfree;
buf->f_bavail = bavail;
buf->f_files = files;
buf->f_ffree = ffree;
+ buf->f_frsize = 0;
out:
return err;
/* ---------------------------------------------------------------------- */
+static int aufs_sync_fs(struct super_block *sb, int wait)
+{
+ int err, e;
+ aufs_bindex_t bend, bindex;
+ struct au_branch *br;
+ struct super_block *h_sb;
+
+ err = 0;
+ si_noflush_read_lock(sb);
+ bend = au_sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++) {
+ br = au_sbr(sb, bindex);
+ if (!au_br_writable(br->br_perm))
+ continue;
+
+ h_sb = au_sbr_sb(sb, bindex);
+ if (h_sb->s_op->sync_fs) {
+ e = h_sb->s_op->sync_fs(h_sb, wait);
+ if (unlikely(e && !err))
+ err = e;
+ /* go on even if an error happens */
+ }
+ }
+ si_read_unlock(sb);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
/* final actions when unmounting a file system */
static void aufs_put_super(struct super_block *sb)
{
sigen = au_sigen(sb);
for (ull = 0; ull < max; ull++) {
inode = array[ull];
- if (au_iigen(inode) != sigen) {
+ if (au_iigen(inode, NULL) != sigen) {
ii_write_lock_child(inode);
e = au_refresh_hinode_self(inode);
ii_write_unlock(inode);
.show_options = aufs_show_options,
.statfs = aufs_statfs,
.put_super = aufs_put_super,
+ .sync_fs = aufs_sync_fs,
.remount_fs = aufs_remount_fs
};
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
int (*copyup)(struct dentry *dentry);
};
+#define AuWbr_DIR 1 /* target is a dir */
+#define AuWbr_PARENT (1 << 1) /* always require a parent */
+
+#define au_ftest_wbr(flags, name) ((flags) & AuWbr_##name)
+#define au_fset_wbr(flags, name) { (flags) |= AuWbr_##name; }
+#define au_fclr_wbr(flags, name) { (flags) &= ~AuWbr_##name; }
+
struct au_wbr_create_operations {
- int (*create)(struct dentry *dentry, int isdir);
+ int (*create)(struct dentry *dentry, unsigned int flags);
int (*init)(struct super_block *sb);
int (*fin)(struct super_block *sb);
};
unsigned long long mfsrr_watermark;
};
+struct pseudo_link {
+ union {
+ struct hlist_node hlist;
+ struct rcu_head rcu;
+ };
+ struct inode *inode;
+};
+
+#define AuPlink_NHASH 100
+static inline int au_plink_hash(ino_t ino)
+{
+ return ino % AuPlink_NHASH;
+}
+
struct au_branch;
struct au_sbinfo {
/* nowait tasks in the system-wide workqueue */
unsigned long si_xib_last_pindex;
int si_xib_next_bit;
aufs_bindex_t si_xino_brid;
+ unsigned long si_xino_jiffy;
+ unsigned long si_xino_expire;
/* reserved for future use */
/* unsigned long long si_xib_limit; */ /* Max xib file size */
/* int si_rendir; */
/* pseudo_link list */
- struct au_splhead si_plink;
+ struct au_sphlhead si_plink[AuPlink_NHASH];
wait_queue_head_t si_plink_wq;
spinlock_t si_plink_maint_lock;
pid_t si_plink_maint_pid;
*/
struct kobject si_kobj;
#ifdef CONFIG_DEBUG_FS
- struct dentry *si_dbgaufs, *si_dbgaufs_xib;
+ struct dentry *si_dbgaufs;
+ struct dentry *si_dbgaufs_plink;
+ struct dentry *si_dbgaufs_xib;
#ifdef CONFIG_AUFS_EXPORT
struct dentry *si_dbgaufs_xigen;
#endif
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_AUFS_EXPORT
+int au_test_nfsd(void);
void au_export_init(struct super_block *sb);
-
-static inline int au_test_nfsd(void)
-{
- struct task_struct *tsk = current;
-
- return (tsk->flags & PF_KTHREAD)
- && !strcmp(tsk->comm, "nfsd");
-}
-
void au_xigen_inc(struct inode *inode);
int au_xigen_new(struct inode *inode);
int au_xigen_set(struct super_block *sb, struct file *base);
return -ESTALE;
}
#else
-AuStubVoid(au_export_init, struct super_block *sb)
AuStubInt0(au_test_nfsd, void)
+AuStubVoid(au_export_init, struct super_block *sb)
AuStubVoid(au_xigen_inc, struct inode *inode)
AuStubInt0(au_xigen_new, struct inode *inode)
AuStubInt0(au_xigen_set, struct super_block *sb, struct file *base)
/* AuRwMustWriteLock(&sbinfo->si_rwsem); */
#ifdef CONFIG_DEBUG_FS
sbinfo->si_dbgaufs = NULL;
+ sbinfo->si_dbgaufs_plink = NULL;
sbinfo->si_dbgaufs_xib = NULL;
#ifdef CONFIG_AUFS_EXPORT
sbinfo->si_dbgaufs_xigen = NULL;
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
* unlinked.
*/
static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb,
- aufs_bindex_t bindex)
+ aufs_bindex_t bindex, int idx)
{
int err;
struct path path;
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);
+
+ switch (idx) {
+ case AuBrSysfs_BR:
+ path.mnt = au_br_mnt(br);
+ 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;
+ break;
+ case AuBrSysfs_BRID:
+ err = seq_printf(seq, "%d\n", br->br_id);
+ di_read_unlock(root, !AuLock_IR);
if (err == -1)
err = -E2BIG;
- } else
- err = -ENOMEM;
+ break;
+ }
+
return err;
}
return seq;
}
-#define SysaufsBr_PREFIX "br"
+#define SysaufsBr_PREFIX "br"
+#define SysaufsBrid_PREFIX "brid"
/* todo: file size may exceed PAGE_SIZE */
ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
ssize_t err;
+ int idx;
long l;
aufs_bindex_t bend;
struct au_sbinfo *sbinfo;
cattr++;
}
- bend = au_sbend(sb);
- if (!strncmp(name, SysaufsBr_PREFIX, sizeof(SysaufsBr_PREFIX) - 1)) {
+ if (!strncmp(name, SysaufsBrid_PREFIX,
+ sizeof(SysaufsBrid_PREFIX) - 1)) {
+ idx = AuBrSysfs_BRID;
+ name += sizeof(SysaufsBrid_PREFIX) - 1;
+ } else if (!strncmp(name, SysaufsBr_PREFIX,
+ sizeof(SysaufsBr_PREFIX) - 1)) {
+ idx = AuBrSysfs_BR;
name += sizeof(SysaufsBr_PREFIX) - 1;
- err = kstrtol(name, 10, &l);
- if (!err) {
- if (l <= bend)
- err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l);
- else
- err = -ENOENT;
- }
- goto out_seq;
+ } else
+ BUG();
+
+ err = kstrtol(name, 10, &l);
+ if (!err) {
+ bend = au_sbend(sb);
+ if (l <= bend)
+ err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx);
+ else
+ err = -ENOENT;
}
- BUG();
out_seq:
if (!err) {
void sysaufs_br_init(struct au_branch *br)
{
- struct attribute *attr = &br->br_attr;
-
- sysfs_attr_init(attr);
- attr->name = br->br_name;
- attr->mode = S_IRUGO;
+ int i;
+ struct au_brsysfs *br_sysfs;
+ struct attribute *attr;
+
+ br_sysfs = br->br_sysfs;
+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) {
+ attr = &br_sysfs->attr;
+ sysfs_attr_init(attr);
+ attr->name = br_sysfs->name;
+ attr->mode = S_IRUGO;
+ br_sysfs++;
+ }
}
void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
{
struct au_branch *br;
struct kobject *kobj;
+ struct au_brsysfs *br_sysfs;
+ int i;
aufs_bindex_t bend;
dbgaufs_brs_del(sb, bindex);
bend = au_sbend(sb);
for (; bindex <= bend; bindex++) {
br = au_sbr(sb, bindex);
- sysfs_remove_file(kobj, &br->br_attr);
+ br_sysfs = br->br_sysfs;
+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) {
+ sysfs_remove_file(kobj, &br_sysfs->attr);
+ br_sysfs++;
+ }
}
}
void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)
{
- int err;
+ int err, i;
aufs_bindex_t bend;
struct kobject *kobj;
struct au_branch *br;
+ struct au_brsysfs *br_sysfs;
dbgaufs_brs_add(sb, bindex);
bend = au_sbend(sb);
for (; bindex <= bend; bindex++) {
br = au_sbr(sb, bindex);
- snprintf(br->br_name, sizeof(br->br_name), SysaufsBr_PREFIX
- "%d", bindex);
- err = sysfs_create_file(kobj, &br->br_attr);
- if (unlikely(err))
- pr_warning("failed %s under sysfs(%d)\n",
- br->br_name, err);
+ br_sysfs = br->br_sysfs;
+ snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name),
+ SysaufsBr_PREFIX "%d", bindex);
+ snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name),
+ SysaufsBrid_PREFIX "%d", bindex);
+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) {
+ err = sysfs_create_file(kobj, &br_sysfs->attr);
+ if (unlikely(err))
+ pr_warn("failed %s under sysfs(%d)\n",
+ br_sysfs->name, err);
+ br_sysfs++;
+ }
}
}
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
plevel = au_plevel;
au_plevel = KERN_WARNING;
- sbinfo = au_sbi(sb);
/* since we define pr_fmt, call printk directly */
+#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str)
+
+ sbinfo = au_sbi(sb);
printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo));
- printk(KERN_WARNING AUFS_NAME ": superblock\n");
+ pr("superblock\n");
au_dpri_sb(sb);
#if 0
- printk(KERN_WARNING AUFS_NAME ": root dentry\n");
+ pr("root dentry\n");
au_dpri_dentry(sb->s_root);
- printk(KERN_WARNING AUFS_NAME ": root inode\n");
+ pr("root inode\n");
au_dpri_inode(sb->s_root->d_inode);
#endif
#if 1
{
struct inode *i;
- printk(KERN_WARNING AUFS_NAME ": isolated inode\n");
+ pr("isolated inode\n");
spin_lock(&inode_sb_list_lock);
list_for_each_entry(i, &sb->s_inodes, i_sb_list) {
spin_lock(&i->i_lock);
spin_unlock(&inode_sb_list_lock);
}
#endif
- printk(KERN_WARNING AUFS_NAME ": files\n");
+ pr("files\n");
lg_global_lock(files_lglock);
do_file_list_for_each_entry(sb, file) {
umode_t mode;
au_dpri_file(file);
} while_file_list_for_each_entry;
lg_global_unlock(files_lglock);
- printk(KERN_WARNING AUFS_NAME ": done\n");
+ pr("done\n");
+#undef pr
au_plevel = plevel;
}
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
ia.ia_uid = h_isrc->i_uid;
ia.ia_gid = h_isrc->i_gid;
sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID));
- au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc);
+ au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc->i_flags);
err = vfsub_sio_notify_change(h_path, &ia);
/* is this nfs only? */
#define au_fclr_cpdown(flags, name) \
do { (flags) &= ~AuCpdown_##name; } while (0)
-struct au_cpdown_dir_args {
- struct dentry *parent;
- unsigned int flags;
-};
-
static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst,
- struct au_cpdown_dir_args *a)
+ unsigned int *flags)
{
int err;
struct dentry *opq_dentry;
if (IS_ERR(opq_dentry))
goto out;
dput(opq_dentry);
- au_fset_cpdown(a->flags, DIROPQ);
+ au_fset_cpdown(*flags, DIROPQ);
out:
return err;
err = 0;
if (h_path.dentry->d_inode) {
- h_path.mnt = br->br_mnt;
+ h_path.mnt = au_br_mnt(br);
err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path,
dentry);
}
}
static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
+ struct au_pin *pin,
struct dentry *h_parent, void *arg)
{
int err, rerr;
struct path h_path;
struct dentry *parent;
struct inode *h_dir, *h_inode, *inode, *dir;
- struct au_cpdown_dir_args *args = arg;
+ unsigned int *flags = arg;
bstart = au_dbstart(dentry);
/* dentry is di-locked */
AuDebugOn(h_dir != au_h_iptr(dir, bdst));
IMustLock(h_dir);
- err = au_lkup_neg(dentry, bdst);
+ err = au_lkup_neg(dentry, bdst, /*wh*/0);
if (unlikely(err < 0))
goto out;
h_path.dentry = au_h_dptr(dentry, bdst);
S_IRWXU | S_IRUGO | S_IXUGO);
if (unlikely(err))
goto out_put;
- au_fset_cpdown(args->flags, MADE_DIR);
+ au_fset_cpdown(*flags, MADE_DIR);
bopq = au_dbdiropq(dentry);
- au_fclr_cpdown(args->flags, WHED);
- au_fclr_cpdown(args->flags, DIROPQ);
+ au_fclr_cpdown(*flags, WHED);
+ au_fclr_cpdown(*flags, DIROPQ);
if (au_dbwh(dentry) == bdst)
- au_fset_cpdown(args->flags, WHED);
- if (!au_ftest_cpdown(args->flags, PARENT_OPQ) && bopq <= bdst)
- au_fset_cpdown(args->flags, PARENT_OPQ);
+ au_fset_cpdown(*flags, WHED);
+ if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst)
+ au_fset_cpdown(*flags, PARENT_OPQ);
h_inode = h_path.dentry->d_inode;
mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
- if (au_ftest_cpdown(args->flags, WHED)) {
- err = au_cpdown_dir_opq(dentry, bdst, args);
+ if (au_ftest_cpdown(*flags, WHED)) {
+ err = au_cpdown_dir_opq(dentry, bdst, flags);
if (unlikely(err)) {
mutex_unlock(&h_inode->i_mutex);
goto out_dir;
if (unlikely(err))
goto out_opq;
- if (au_ftest_cpdown(args->flags, WHED)) {
+ if (au_ftest_cpdown(*flags, WHED)) {
err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst);
if (unlikely(err))
goto out_opq;
/* revert */
out_opq:
- if (au_ftest_cpdown(args->flags, DIROPQ)) {
+ if (au_ftest_cpdown(*flags, DIROPQ)) {
mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
rerr = au_diropq_remove(dentry, bdst);
mutex_unlock(&h_inode->i_mutex);
}
}
out_dir:
- if (au_ftest_cpdown(args->flags, MADE_DIR)) {
+ if (au_ftest_cpdown(*flags, MADE_DIR)) {
rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path);
if (unlikely(rerr)) {
AuIOErr("failed removing %.*s b%d (%d)\n",
int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst)
{
int err;
- struct au_cpdown_dir_args args = {
- .parent = dget_parent(dentry),
- .flags = 0
- };
+ unsigned int flags;
- err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &args);
- dput(args.parent);
+ flags = 0;
+ err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags);
return err;
}
}
/* top down parent */
-static int au_wbr_create_tdp(struct dentry *dentry, int isdir __maybe_unused)
+static int au_wbr_create_tdp(struct dentry *dentry,
+ unsigned int flags __maybe_unused)
{
int err;
aufs_bindex_t bstart, bindex;
return err;
}
-static int au_wbr_create_rr(struct dentry *dentry, int isdir)
+static int au_wbr_create_rr(struct dentry *dentry, unsigned int flags)
{
int err, nbr;
unsigned int u;
bend = au_sbend(sb);
nbr = bend + 1;
for (bindex = 0; bindex <= bend; bindex++) {
- if (!isdir) {
+ if (!au_ftest_wbr(flags, DIR)) {
err = atomic_dec_return(next) + 1;
/* modulo for 0 is meaningless */
if (unlikely(!err))
/* ---------------------------------------------------------------------- */
/* most free space */
-static void au_mfs(struct dentry *dentry)
+static void au_mfs(struct dentry *dentry, struct dentry *parent)
{
struct super_block *sb;
struct au_branch *br;
struct au_wbr_mfs *mfs;
+ struct dentry *h_parent;
aufs_bindex_t bindex, bend;
int err;
unsigned long long b, bavail;
MtxMustLock(&mfs->mfs_lock);
mfs->mfs_bindex = -EROFS;
mfs->mfsrr_bytes = 0;
- bend = au_sbend(sb);
- for (bindex = 0; bindex <= bend; bindex++) {
+ if (!parent) {
+ bindex = 0;
+ bend = au_sbend(sb);
+ } else {
+ bindex = au_dbstart(parent);
+ bend = au_dbtaildir(parent);
+ }
+
+ for (; bindex <= bend; bindex++) {
+ if (parent) {
+ h_parent = au_h_dptr(parent, bindex);
+ if (!h_parent || !h_parent->d_inode)
+ continue;
+ }
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.mnt = au_br_mnt(br);
h_path.dentry = h_path.mnt->mnt_root;
err = vfs_statfs(&h_path, st);
if (unlikely(err)) {
kfree(st);
}
-static int au_wbr_create_mfs(struct dentry *dentry, int isdir __maybe_unused)
+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;
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);
+ au_mfs(dentry, parent);
mutex_unlock(&mfs->mfs_lock);
err = mfs->mfs_bindex;
+ dput(parent);
if (err >= 0)
err = au_wbr_nonopq(dentry, err);
/* ---------------------------------------------------------------------- */
/* most free space and then round robin */
-static int au_wbr_create_mfsrr(struct dentry *dentry, int isdir)
+static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags)
{
int err;
struct au_wbr_mfs *mfs;
- err = au_wbr_create_mfs(dentry, isdir);
+ err = au_wbr_create_mfs(dentry, flags);
if (err >= 0) {
mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs;
mutex_lock(&mfs->mfs_lock);
if (mfs->mfsrr_bytes < mfs->mfsrr_watermark)
- err = au_wbr_create_rr(dentry, isdir);
+ err = au_wbr_create_rr(dentry, flags);
mutex_unlock(&mfs->mfs_lock);
}
/* ---------------------------------------------------------------------- */
/* top down parent and most free space */
-static int au_wbr_create_pmfs(struct dentry *dentry, int isdir)
+static int au_wbr_create_pmfs(struct dentry *dentry, unsigned int flags)
{
int err, e2;
unsigned long long b;
struct dentry *parent, *h_parent;
struct au_branch *br;
- err = au_wbr_create_tdp(dentry, isdir);
+ err = au_wbr_create_tdp(dentry, flags);
if (unlikely(err < 0))
goto out;
parent = dget_parent(dentry);
if (bstart == bend)
goto out_parent; /* success */
- e2 = au_wbr_create_mfs(dentry, isdir);
+ e2 = au_wbr_create_mfs(dentry, flags);
if (e2 < 0)
goto out_parent; /* success */
/* ---------------------------------------------------------------------- */
+/*
+ * - top down parent
+ * - most free space with parent
+ * - most free space round-robin regardless parent
+ */
+static int au_wbr_create_pmfsrr(struct dentry *dentry, unsigned int flags)
+{
+ int err;
+ unsigned long long watermark;
+ struct super_block *sb;
+ struct au_branch *br;
+ struct au_wbr_mfs *mfs;
+
+ err = au_wbr_create_pmfs(dentry, flags | AuWbr_PARENT);
+ if (unlikely(err < 0))
+ goto out;
+
+ sb = dentry->d_sb;
+ br = au_sbr(sb, err);
+ mfs = &au_sbi(sb)->si_wbr_mfs;
+ mutex_lock(&mfs->mfs_lock);
+ watermark = mfs->mfsrr_watermark;
+ mutex_unlock(&mfs->mfs_lock);
+ if (br->br_wbr->wbr_bytes < watermark)
+ /* regardless the parent dir */
+ err = au_wbr_create_mfsrr(dentry, flags);
+
+out:
+ AuDbg("b%d\n", err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
/* policies for copyup */
/* top down parent */
static int au_wbr_copyup_tdp(struct dentry *dentry)
{
- return au_wbr_create_tdp(dentry, /*isdir, anything is ok*/0);
+ return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0);
}
/* bottom up parent */
.create = au_wbr_create_pmfs,
.init = au_wbr_create_init_mfs,
.fin = au_wbr_create_fin_mfs
+ },
+ [AuWbrCreate_PMFSRR] = {
+ .create = au_wbr_create_pmfsrr,
+ .init = au_wbr_create_init_mfsrr,
+ .fin = au_wbr_create_fin_mfs
+ },
+ [AuWbrCreate_PMFSRRV] = {
+ .create = au_wbr_create_pmfsrr,
+ .init = au_wbr_create_init_mfsrr,
+ .fin = au_wbr_create_fin_mfs
}
};
/*
- * Copyright (C) 2005-2012 Junjiro R. Okajima
+ * Copyright (C) 2005-2013 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
goto out_name;
dput(dentry);
}
- /* pr_warning("could not get random name\n"); */
+ /* pr_warn("could not get random name\n"); */
dentry = ERR_PTR(-EEXIST);
AuDbg("%.*s\n", AuLNPair(&qs));
BUG();
{
int err;
struct path h_path = {
- .mnt = br->br_mnt
+ .mnt = au_br_mnt(br)
};
struct inode *h_dir;
struct dentry *h_parent;
{
int err;
struct path h_path = {
- .mnt = br->br_mnt
+ .mnt = au_br_mnt(br)
};
err = 0;
mnt_drop_write(whpath->mnt);
}
if (unlikely(err))
- pr_warning("failed removing %.*s (%d), ignored.\n",
- AuDLNPair(whpath->dentry), err);
+ pr_warn("failed removing %.*s (%d), ignored.\n",
+ AuDLNPair(whpath->dentry), err);
}
static int test_linkable(struct dentry *h_root)
/*
* initialize the whiteout base file/dir for @br.
*/
-int au_wh_init(struct dentry *h_root, struct au_branch *br,
- struct super_block *sb)
+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 path path = {
- .mnt = br->br_mnt
- };
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] = {
dir = a->sb->s_root->d_inode;
hdir = au_hi(dir, bindex);
h_root = au_h_dptr(a->sb->s_root, bindex);
+&nb