ubuntu/aufs: this should bring us up-to-date with the current aufs.

Patch here's was taken from AUFS git on SF.
git://git.code.sf.net/p/aufs/aufs3-standalone

Again, If this breaks something... leming is the one to be blamed
This commit is contained in:
Mauro Ribeiro
2014-04-21 22:24:57 -03:00
parent e97d091568
commit 84a7f42ace
72 changed files with 2230 additions and 1047 deletions

View File

@@ -426,6 +426,7 @@ void __mnt_drop_write(struct vfsmount *mnt)
mnt_dec_writers(real_mount(mnt));
preempt_enable();
}
EXPORT_SYMBOL_GPL(__mnt_drop_write);
/**
* mnt_drop_write - give up write access to a mount
@@ -446,6 +447,7 @@ void __mnt_drop_write_file(struct file *file)
{
__mnt_drop_write(file->f_path.mnt);
}
EXPORT_SYMBOL_GPL(__mnt_drop_write_file);
void mnt_drop_write_file(struct file *file)
{

View File

@@ -283,6 +283,7 @@ err:
return ret;
}
EXPORT_SYMBOL(fsnotify_add_mark);
int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group,
struct inode *inode, struct vfsmount *mnt, int allow_dups)
@@ -293,7 +294,6 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group,
mutex_unlock(&group->mark_mutex);
return ret;
}
EXPORT_SYMBOL(fsnotify_add_mark);
/*
* clear any marks in a group in which mark->flags & flags is true

View File

@@ -100,29 +100,6 @@ config AUFS_RDU
shows better performance in most cases.
See detail in aufs.5.
config AUFS_PROC_MAP
bool "support for /proc/maps and lsof(1)"
depends on PROC_FS
help
When you issue mmap(2) in aufs, it is actually a direct mmap(2)
call to the file on the branch fs since the file in aufs is
purely virtual. And the file path printed in /proc/maps (and
others) will be the path on the branch fs. In most cases, it
does no harm. But some utilities like lsof(1) may confuse since
the utility or user may expect the file path in aufs to be
printed.
To address this issue, aufs provides a patch which introduces a
new member called vm_prfile into struct vm_are_struct. The patch
is meaningless without enabling this configuration since nobody
sets the new vm_prfile member.
If you don't apply the patch, then enabling this configuration
will cause a compile error.
This approach is fragile since if someone else make some changes
around vm_file, then vm_prfile may not work anymore. As a
workaround such case, aufs provides this configuration. If you
disable it, then lsof(1) may produce incorrect result but the
problem will be gone even if the aufs patch is applied (I hope).
config AUFS_SP_IATTR
bool "Respect the attributes (mtime/ctime mainly) of special files"
help

View File

@@ -10,9 +10,9 @@ endif
ccflags-y += -DDEBUG
# sparse requires the full pathname
ifdef M
ccflags-y += -include ${M}/../../ubuntu/include/linux/aufs_type.h
ccflags-y += -include ${M}/../../include/uapi/linux/aufs_type.h
else
ccflags-y += -include ${srctree}/ubuntu/include/linux/aufs_type.h
ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h
endif
obj-$(CONFIG_AUFS_FS) += aufs.o

View File

@@ -1,5 +1,5 @@
/*
* 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

235
ubuntu/aufs/aufs_type.h Normal file
View File

@@ -0,0 +1,235 @@
/*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __AUFS_TYPE_H__
#define __AUFS_TYPE_H__
#define AUFS_NAME "aufs"
#ifdef __KERNEL__
/*
* define it before including all other headers.
* sched.h may use pr_* macros before defining "current", so define the
* no-current version first, and re-define later.
*/
#define pr_fmt(fmt) AUFS_NAME " %s:%d: " fmt, __func__, __LINE__
#include <linux/sched.h>
#undef pr_fmt
#define pr_fmt(fmt) \
AUFS_NAME " %s:%d:%.*s[%d]: " fmt, __func__, __LINE__, \
(int)sizeof(current->comm), current->comm, current->pid
#else
#include <stdint.h>
#include <sys/types.h>
#endif /* __KERNEL__ */
#include <linux/limits.h>
#define AUFS_VERSION "3.8-20131104"
/* todo? move this to linux-2.6.19/include/magic.h */
#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's')
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_AUFS_BRANCH_MAX_127
typedef int8_t aufs_bindex_t;
#define AUFS_BRANCH_MAX 127
#else
typedef int16_t aufs_bindex_t;
#ifdef CONFIG_AUFS_BRANCH_MAX_511
#define AUFS_BRANCH_MAX 511
#elif defined(CONFIG_AUFS_BRANCH_MAX_1023)
#define AUFS_BRANCH_MAX 1023
#elif defined(CONFIG_AUFS_BRANCH_MAX_32767)
#define AUFS_BRANCH_MAX 32767
#endif
#endif
#ifdef __KERNEL__
#ifndef AUFS_BRANCH_MAX
#error unknown CONFIG_AUFS_BRANCH_MAX value
#endif
#endif /* __KERNEL__ */
/* ---------------------------------------------------------------------- */
#define AUFS_FSTYPE AUFS_NAME
#define AUFS_ROOT_INO 2
#define AUFS_FIRST_INO 11
#define AUFS_WH_PFX ".wh."
#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1)
#define AUFS_WH_TMP_LEN 4
/* a limit for rmdir/rename a dir and copyup */
#define AUFS_MAX_NAMELEN (NAME_MAX \
- AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\
- 1 /* dot */\
- AUFS_WH_TMP_LEN) /* hex */
#define AUFS_XINO_FNAME "." AUFS_NAME ".xino"
#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME
#define AUFS_XINO_DEF_SEC 30 /* seconds */
#define AUFS_XINO_DEF_TRUNC 45 /* percentage */
#define AUFS_DIRWH_DEF 3
#define AUFS_RDCACHE_DEF 10 /* seconds */
#define AUFS_RDCACHE_MAX 3600 /* seconds */
#define AUFS_RDBLK_DEF 512 /* bytes */
#define AUFS_RDHASH_DEF 32
#define AUFS_WKQ_NAME AUFS_NAME "d"
#define AUFS_MFS_DEF_SEC 30 /* seconds */
#define AUFS_MFS_MAX_SEC 3600 /* seconds */
#define AUFS_PLINK_WARN 50 /* number of plinks in a single bucket */
/* pseudo-link maintenace under /proc */
#define AUFS_PLINK_MAINT_NAME "plink_maint"
#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME
#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME
#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */
#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME
#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME
#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk"
#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph"
/* doubly whiteouted */
#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME
#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME
#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME
/* branch permissions and attributes */
#define AUFS_BRPERM_RW "rw"
#define AUFS_BRPERM_RO "ro"
#define AUFS_BRPERM_RR "rr"
#define AUFS_BRRATTR_WH "wh"
#define AUFS_BRWATTR_NLWH "nolwh"
#define AUFS_BRATTR_UNPIN "unpin"
/* ---------------------------------------------------------------------- */
/* ioctl */
enum {
/* readdir in userspace */
AuCtl_RDU,
AuCtl_RDU_INO,
/* pathconf wrapper */
AuCtl_WBR_FD,
/* busy inode */
AuCtl_IBUSY
};
/* borrowed from linux/include/linux/kernel.h */
#ifndef ALIGN
#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1)
#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask))
#endif
/* borrowed from linux/include/linux/compiler-gcc3.h */
#ifndef __aligned
#define __aligned(x) __attribute__((aligned(x)))
#endif
#ifdef __KERNEL__
#ifndef __packed
#define __packed __attribute__((packed))
#endif
#endif
struct au_rdu_cookie {
uint64_t h_pos;
int16_t bindex;
uint8_t flags;
uint8_t pad;
uint32_t generation;
} __aligned(8);
struct au_rdu_ent {
uint64_t ino;
int16_t bindex;
uint8_t type;
uint8_t nlen;
uint8_t wh;
char name[0];
} __aligned(8);
static inline int au_rdu_len(int nlen)
{
/* include the terminating NULL */
return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1,
sizeof(uint64_t));
}
union au_rdu_ent_ul {
struct au_rdu_ent __user *e;
uint64_t ul;
};
enum {
AufsCtlRduV_SZ,
AufsCtlRduV_End
};
struct aufs_rdu {
/* input */
union {
uint64_t sz; /* AuCtl_RDU */
uint64_t nent; /* AuCtl_RDU_INO */
};
union au_rdu_ent_ul ent;
uint16_t verify[AufsCtlRduV_End];
/* input/output */
uint32_t blk;
/* output */
union au_rdu_ent_ul tail;
/* number of entries which were added in a single call */
uint64_t rent;
uint8_t full;
uint8_t shwh;
struct au_rdu_cookie cookie;
} __aligned(8);
/* ---------------------------------------------------------------------- */
struct aufs_wbr_fd {
uint32_t oflags;
int16_t brid;
} __aligned(8);
/* ---------------------------------------------------------------------- */
struct aufs_ibusy {
uint64_t ino, h_ino;
int16_t bindex;
} __aligned(8);
/* ---------------------------------------------------------------------- */
#define AuCtlType 'A'
#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu)
#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu)
#define AUFS_CTL_WBR_FD _IOW(AuCtlType, AuCtl_WBR_FD, \
struct aufs_wbr_fd)
#define AUFS_CTL_IBUSY _IOWR(AuCtlType, AuCtl_IBUSY, struct aufs_ibusy)
#endif /* __AUFS_TYPE_H__ */

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -27,6 +27,34 @@
/*
* 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;
@@ -56,7 +84,12 @@ static void au_br_do_free(struct au_branch *br)
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);
}
@@ -269,7 +302,7 @@ 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;
@@ -277,6 +310,10 @@ static int au_br_init_wh(struct super_block *sb, struct au_branch *br,
struct au_wbr *wbr;
struct au_hinode *hdir;
err = vfsub_mnt_want_write(au_br_mnt(br));
if (unlikely(err))
goto out;
wbr = br->br_wbr;
old_perm = br->br_perm;
br->br_perm = new_perm;
@@ -287,20 +324,21 @@ static int au_br_init_wh(struct super_block *sb, struct au_branch *br,
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)
au_hn_imtx_unlock(hdir);
else
mutex_unlock(h_mtx);
vfsub_mnt_drop_write(au_br_mnt(br));
br->br_perm = old_perm;
if (!err && wbr && !au_br_writable(new_perm)) {
@@ -308,16 +346,16 @@ static int au_br_init_wh(struct super_block *sb, struct au_branch *br,
br->br_wbr = NULL;
}
out:
return err;
}
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);
@@ -327,19 +365,18 @@ static int au_wbr_init(struct au_branch *br, struct super_block *sb,
/*
* a limit for rmdir/rename a dir
* cf. AUFS_MAX_NAMELEN in include/linux/aufs_type.h
* cf. AUFS_MAX_NAMELEN in include/uapi/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;
@@ -355,17 +392,19 @@ static int au_br_init(struct au_branch *br, struct super_block *sb,
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;
}
@@ -380,11 +419,11 @@ static int au_br_init(struct au_branch *br, struct super_block *sb,
}
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;
}
@@ -436,17 +475,20 @@ static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex,
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);
@@ -489,15 +531,15 @@ int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount)
}
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;
@@ -632,7 +674,7 @@ static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex,
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);
@@ -803,6 +845,7 @@ int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount)
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);
@@ -851,7 +894,7 @@ int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount)
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_warn("failed re-creating base whiteout, %s. (%d)\n",
del->pathname, rerr);
@@ -1071,8 +1114,8 @@ static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex)
hf->f_mode &= ~FMODE_WRITE;
spin_unlock(&hf->f_lock);
if (!file_check_writeable(hf)) {
__mnt_drop_write(hf->f_path.mnt);
file_release_write(hf);
vfsub_mnt_drop_write(hf->f_vfsmnt);
}
}
@@ -1088,7 +1131,6 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
{
int err, rerr;
aufs_bindex_t bindex;
struct path path;
struct dentry *root;
struct au_branch *br;
@@ -1108,12 +1150,13 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
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;
@@ -1130,12 +1173,8 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
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);
@@ -1148,9 +1187,7 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
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;
@@ -1159,6 +1196,12 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
}
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;
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -61,6 +61,25 @@ struct au_wbr {
/* ext2 has 3 types of operations at least, ext3 has 4 */
#define AuBrDynOp (AuDyLast * 4)
#ifdef CONFIG_AUFS_HFSNOTIFY
/* support for asynchronous destruction */
struct au_br_hfsnotify {
struct fsnotify_group *hfsn_group;
};
#endif
/* 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;
@@ -68,7 +87,8 @@ struct au_branch {
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;
@@ -76,23 +96,35 @@ struct au_branch {
struct au_wbr *br_wbr;
/* xino truncation */
blkcnt_t br_xino_upper; /* watermark in blocks */
atomic_t br_xino_running;
#ifdef CONFIG_AUFS_HFSNOTIFY
struct fsnotify_group *br_hfsn_group;
struct fsnotify_ops br_hfsn_ops;
struct au_br_hfsnotify *br_hfsn;
#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 */
@@ -103,6 +135,9 @@ struct au_branch {
#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;
@@ -120,7 +155,7 @@ static inline int au_br_wh_linkable(int brperm)
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;
}
@@ -190,13 +225,13 @@ aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex)
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)

View File

@@ -12,7 +12,6 @@ AuConfAll = BRANCH_MAX_127 BRANCH_MAX_511 BRANCH_MAX_1023 BRANCH_MAX_32767 \
HNOTIFY HFSNOTIFY \
EXPORT INO_T_64 \
RDU \
PROC_MAP \
SP_IATTR \
SHWH \
BR_RAMFS \

14
ubuntu/aufs/conf.str Normal file
View File

@@ -0,0 +1,14 @@
"CONFIG_AUFS_FS=m\n"
"CONFIG_AUFS_BRANCH_MAX_127=y\n"
"CONFIG_AUFS_SBILIST=y\n"
"CONFIG_AUFS_HNOTIFY=y\n"
"CONFIG_AUFS_HFSNOTIFY=y\n"
"CONFIG_AUFS_EXPORT=y\n"
"CONFIG_AUFS_RDU=y\n"
"CONFIG_AUFS_SP_IATTR=y\n"
"CONFIG_AUFS_SHWH=y\n"
"CONFIG_AUFS_BR_RAMFS=y\n"
"CONFIG_AUFS_BR_FUSE=y\n"
"CONFIG_AUFS_POLL=y\n"
"CONFIG_AUFS_BR_HFSPLUS=y\n"
"CONFIG_AUFS_BDEV_LOOP=y\n"

14
ubuntu/aufs/conf.str.tmp Normal file
View File

@@ -0,0 +1,14 @@
"CONFIG_AUFS_FS=m\n"
"CONFIG_AUFS_BRANCH_MAX_127=y\n"
"CONFIG_AUFS_SBILIST=y\n"
"CONFIG_AUFS_HNOTIFY=y\n"
"CONFIG_AUFS_HFSNOTIFY=y\n"
"CONFIG_AUFS_EXPORT=y\n"
"CONFIG_AUFS_RDU=y\n"
"CONFIG_AUFS_SP_IATTR=y\n"
"CONFIG_AUFS_SHWH=y\n"
"CONFIG_AUFS_BR_RAMFS=y\n"
"CONFIG_AUFS_BR_FUSE=y\n"
"CONFIG_AUFS_POLL=y\n"
"CONFIG_AUFS_BR_HFSPLUS=y\n"
"CONFIG_AUFS_BDEV_LOOP=y\n"

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -24,12 +24,14 @@
#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;
}
@@ -88,7 +90,7 @@ void au_cpup_attr_changeable(struct inode *inode)
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)
@@ -149,13 +151,22 @@ void au_dtime_revert(struct au_dtime *dt)
/* ---------------------------------------------------------------------- */
/* 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;
@@ -164,17 +175,32 @@ int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src)
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? */
@@ -240,6 +266,8 @@ static int au_do_copy_file(struct file *dst, struct file *src, loff_t len,
wbytes -= b;
p += b;
}
if (unlikely(err < 0))
break;
} else {
loff_t res;
@@ -323,8 +351,7 @@ out:
* 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 };
@@ -336,14 +363,14 @@ static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst,
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,
@@ -353,11 +380,12 @@ static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst,
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;
@@ -368,7 +396,7 @@ static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst,
/* 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);
@@ -380,27 +408,39 @@ out:
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 */
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);
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_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:
@@ -443,17 +483,15 @@ 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;
@@ -461,19 +499,22 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
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);
@@ -484,14 +525,10 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
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,
/*want_excl*/true);
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;
@@ -501,10 +538,10 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
* 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:
@@ -529,10 +566,10 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
&& 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)
@@ -540,160 +577,232 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
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;
}
au_set_ibstart(inode, bdst);
}
au_set_h_iptr(inode, bdst, au_igrab(dst_inode),
au_hi_flags(inode, isdir));
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);
if (!isdir
&& h_src->d_inode->i_nlink > 1
&& plink)
au_plink_append(inode, bdst, h_dst);
goto out; /* success */
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, 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))) {
@@ -710,30 +819,29 @@ static int au_cpup_sio_test(struct super_block *sb, umode_t mode)
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);
@@ -743,31 +851,47 @@ int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
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;
@@ -775,37 +899,44 @@ static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
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))
@@ -813,6 +944,10 @@ int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
}
dput(parent);
if (h_file)
au_h_open_post(dentry, cpg->bsrc, h_file);
out:
return err;
}
@@ -821,50 +956,57 @@ int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
/*
* 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);
@@ -874,9 +1016,9 @@ static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
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;
@@ -903,32 +1045,37 @@ out:
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;
@@ -938,28 +1085,26 @@ int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
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);
@@ -972,6 +1117,8 @@ int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
/* 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);
@@ -987,6 +1134,7 @@ int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
/* 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)
{
@@ -1032,7 +1180,7 @@ int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst,
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);
}
}
@@ -1049,10 +1197,19 @@ out:
}
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)

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -29,8 +29,9 @@
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);
@@ -39,10 +40,21 @@ void au_cpup_attr_all(struct inode *inode, int force);
/* ---------------------------------------------------------------------- */
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)
@@ -50,16 +62,12 @@ void au_cpup_attr_all(struct inode *inode, int force);
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);

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -96,6 +96,101 @@ static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf,
/* ---------------------------------------------------------------------- */
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;
@@ -175,10 +270,8 @@ void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
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;
}
}
@@ -305,6 +398,12 @@ int dbgaufs_si_init(struct au_sbinfo *sbinfo)
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 */

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -23,9 +23,39 @@
#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 { \
@@ -88,7 +118,7 @@ static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn,
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) {
@@ -124,7 +154,7 @@ void au_dpri_inode(struct inode *inode)
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;
@@ -257,7 +287,7 @@ static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br)
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;
@@ -298,7 +328,7 @@ void au_dpri_sb(struct super_block *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 */
@@ -328,8 +358,11 @@ void au_dbg_sleep_jiffy(int jiffy)
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);
@@ -377,14 +410,11 @@ void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line)
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();
}
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -25,6 +25,7 @@
#ifdef __KERNEL__
#include <linux/atomic.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/sysrq.h>
@@ -33,23 +34,29 @@
#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 */

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -33,7 +33,6 @@
struct au_do_lookup_args {
unsigned int flags;
mode_t type;
unsigned int nd_flags;
};
/*
@@ -124,8 +123,7 @@ static int au_test_shwh(struct super_block *sb, const struct qstr *name)
* otherwise an error.
* can be called at unlinking with @type is zero.
*/
int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,
unsigned int flags)
int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type)
{
int npositive, err;
aufs_bindex_t bindex, btail, bdiropq;
@@ -133,8 +131,7 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,
struct qstr whname;
struct au_do_lookup_args args = {
.flags = 0,
.type = type,
.nd_flags = flags
.type = type
};
const struct qstr *name = &dentry->d_name;
struct dentry *parent;
@@ -249,15 +246,19 @@ struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent,
/*
* 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;
@@ -732,7 +733,7 @@ int au_refresh_dentry(struct dentry *dentry, struct dentry *parent)
* if current working dir is removed, it returns an error.
* but the dentry is legal.
*/
err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0, /*flags*/0);
err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0);
AuDbgDentry(dentry);
au_di_swap(tmp, dinfo);
if (err == -ENOENT)

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -51,9 +51,8 @@ struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent,
int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir,
struct dentry *h_parent, struct au_branch *br);
int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,
unsigned int flags);
int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex);
int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type);
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);

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -33,6 +33,7 @@ void au_add_nlink(struct inode *dir, struct inode *h_dir)
nlink += h_dir->i_nlink - 2;
if (h_dir->i_nlink < 2)
nlink += 2;
smp_mb();
/* 0 can happen in revaliding */
set_nlink(dir, nlink);
}
@@ -47,6 +48,7 @@ void au_sub_nlink(struct inode *dir, struct inode *h_dir)
nlink -= h_dir->i_nlink - 2;
if (h_dir->i_nlink < 2)
nlink -= 2;
smp_mb();
/* nlink == 0 means the branch-fs is broken */
set_nlink(dir, nlink);
}

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -239,7 +239,7 @@ static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br)
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);

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -89,6 +89,21 @@ static int au_test_anon(struct dentry *dentry)
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 */
@@ -222,7 +237,7 @@ static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
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;
@@ -380,7 +395,8 @@ static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino,
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;
@@ -492,7 +508,7 @@ struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh,
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),
@@ -716,7 +732,7 @@ static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len,
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_hparent;
@@ -735,7 +751,7 @@ static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len,
err = fh[Fh_h_type];
*max_len += Fh_tail;
/* todo: macros? */
if (err != 255)
if (err != FILEID_INVALID)
err = 99;
else
AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb));
@@ -749,7 +765,7 @@ out_unlock:
si_read_unlock(sb);
out:
if (unlikely(err < 0))
err = 255;
err = FILEID_INVALID;
return err;
}

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -174,12 +174,19 @@ static void au_init_fop_sp(struct file *file)
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));
@@ -188,15 +195,15 @@ static int au_cpup_sp(struct dentry *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);
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -67,7 +67,7 @@ struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
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 */
@@ -76,7 +76,7 @@ struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
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 {
@@ -154,13 +154,29 @@ int au_reopen_nondir(struct file *file)
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);
@@ -202,32 +218,39 @@ static int au_reopen_wh(struct file *file, aufs_bindex_t btgt,
}
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;
struct dentry *h_dentry, *hi_wh;
struct au_cp_generic cpg = {
.dentry = file->f_dentry,
.bdst = bcpup,
.bsrc = -1,
.len = len,
.pin = pin
};
dentry = file->f_dentry;
au_update_dbstart(dentry);
inode = dentry->d_inode;
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;
}
@@ -238,81 +261,80 @@ static int au_ready_to_write_wh(struct file *file, loff_t len,
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;
struct au_cp_generic cpg = {
.dentry = file->f_dentry,
.bdst = -1,
.bsrc = -1,
.len = len,
.pin = pin,
.flags = AuCpup_DTIME
};
dentry = file->f_dentry;
sb = dentry->d_sb;
inode = dentry->d_inode;
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) {
@@ -363,29 +385,35 @@ int au_do_flush(struct file *file, fl_owner_t id,
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;
@@ -393,25 +421,26 @@ static int au_file_refresh_by_inode(struct file *file, int *need_reopen)
}
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;
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -284,14 +284,12 @@ static inline void au_vm_file_reset(struct vm_area_struct *vma,
static inline void au_vm_prfile_set(struct vm_area_struct *vma,
struct file *file)
{
#ifdef CONFIG_AUFS_PROC_MAP
get_file(file);
vma->vm_prfile = file;
#ifndef CONFIG_MMU
get_file(file);
vma->vm_region->vm_prfile = file;
#endif
#endif
}
#endif /* __KERNEL__ */

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -127,7 +127,7 @@ void au_fi_init_once(void *_finfo)
int au_finfo_init(struct file *file, struct au_fidir *fidir)
{
int err, lc_idx;
int err;
struct au_finfo *finfo;
struct dentry *dentry;
@@ -139,10 +139,11 @@ int au_finfo_init(struct file *file, struct au_fidir *fidir)
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;

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -110,15 +110,6 @@ static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused)
#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)
@@ -157,7 +148,7 @@ static inline int au_test_v9fs(struct super_block *sb __maybe_unused)
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;
@@ -365,7 +356,6 @@ static inline int au_test_fs_refresh_iattr(struct super_block *sb)
{
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 */
@@ -411,7 +401,6 @@ static inline int au_test_fs_no_limit_nlink(struct super_block *sb)
|| au_test_ramfs(sb)
#endif
|| au_test_ubifs(sb)
|| au_test_btrfs(sb)
|| au_test_hfsplus(sb);
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -35,12 +35,13 @@ static void au_hfsn_free_mark(struct fsnotify_mark *mark)
AuDbg("here\n");
au_cache_free_hnotify(hn);
smp_mb__before_atomic_dec();
atomic64_dec(&au_hfsn_ifree);
wake_up(&au_hfsn_wq);
if (atomic64_dec_and_test(&au_hfsn_ifree))
wake_up(&au_hfsn_wq);
}
static int au_hfsn_alloc(struct au_hinode *hinode)
{
int err;
struct au_hnotify *hn;
struct super_block *sb;
struct au_branch *br;
@@ -51,6 +52,8 @@ 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);
AuDebugOn(!br->br_hfsn);
mark = &hn->hn_mark;
fsnotify_init_mark(mark, au_hfsn_free_mark);
mark->mask = AuHfsnMask;
@@ -58,8 +61,12 @@ static int au_hfsn_alloc(struct au_hinode *hinode)
* by udba rename or rmdir, aufs assign a new inode to the known
* h_inode, so specify 1 to allow dups.
*/
return fsnotify_add_mark(mark, br->br_hfsn_group, hinode->hi_inode,
err = fsnotify_add_mark(mark, br->br_hfsn->hfsn_group, hinode->hi_inode,
/*mnt*/NULL, /*allow_dups*/1);
/* even if err */
fsnotify_put_mark(mark);
return err;
}
static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn)
@@ -109,8 +116,11 @@ static void au_hfsn_ctl(struct au_hinode *hinode, int do_set)
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);
@@ -139,6 +149,14 @@ static char *au_hfsn_name(u32 mask)
/* ---------------------------------------------------------------------- */
static void au_hfsn_free_group(struct fsnotify_group *group)
{
struct au_br_hfsnotify *hfsn = group->private;
AuDbg("here\n");
kfree(hfsn);
}
static int au_hfsn_handle_event(struct fsnotify_group *group,
struct fsnotify_mark *inode_mark,
struct fsnotify_mark *vfsmount_mark,
@@ -162,7 +180,7 @@ static int au_hfsn_handle_event(struct fsnotify_group *group,
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",
@@ -170,7 +188,7 @@ static int au_hfsn_handle_event(struct fsnotify_group *group,
AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0);
/* WARN_ON(1); */
}
au_debug(0);
au_debug_off();
#endif
AuDebugOn(!inode_mark);
@@ -196,22 +214,54 @@ static bool au_hfsn_should_send_event(struct fsnotify_group *group,
static struct fsnotify_ops au_hfsn_ops = {
.should_send_event = au_hfsn_should_send_event,
.handle_event = au_hfsn_handle_event
.handle_event = au_hfsn_handle_event,
.free_group_priv = au_hfsn_free_group
};
/* ---------------------------------------------------------------------- */
static void au_hfsn_fin_br(struct au_branch *br)
{
if (br->br_hfsn_group)
fsnotify_put_group(br->br_hfsn_group);
struct au_br_hfsnotify *hfsn;
hfsn = br->br_hfsn;
if (hfsn)
fsnotify_put_group(hfsn->hfsn_group);
}
static int au_hfsn_init_br(struct au_branch *br, int perm)
{
br->br_hfsn_group = NULL;
br->br_hfsn_ops = au_hfsn_ops;
return 0;
int err;
struct fsnotify_group *group;
struct au_br_hfsnotify *hfsn;
err = 0;
br->br_hfsn = NULL;
if (!au_br_hnotifyable(perm))
goto out;
err = -ENOMEM;
hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS);
if (unlikely(!hfsn))
goto out;
err = 0;
group = fsnotify_alloc_group(&au_hfsn_ops);
if (IS_ERR(group)) {
err = PTR_ERR(group);
pr_err("fsnotify_alloc_group() failed, %d\n", err);
goto out_hfsn;
}
group->private = hfsn;
hfsn->hfsn_group = group;
br->br_hfsn = hfsn;
goto out; /* success */
out_hfsn:
kfree(hfsn);
out:
return err;
}
static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm)
@@ -219,25 +269,9 @@ static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm)
int err;
err = 0;
if (udba != AuOpt_UDBA_HNOTIFY
|| !au_br_hnotifyable(perm)) {
au_hfsn_fin_br(br);
br->br_hfsn_group = NULL;
goto out;
}
if (!br->br_hfsn)
err = au_hfsn_init_br(br, perm);
if (br->br_hfsn_group)
goto out;
br->br_hfsn_group = fsnotify_alloc_group(&br->br_hfsn_ops);
if (IS_ERR(br->br_hfsn_group)) {
err = PTR_ERR(br->br_hfsn_group);
pr_err("fsnotify_alloc_group() failed, %d\n", err);
br->br_hfsn_group = NULL;
}
out:
AuTraceErr(err);
return err;
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -36,7 +36,6 @@ struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex)
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)

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -114,7 +114,7 @@ static int aufs_permission(struct inode *inode, int mask)
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)) {
@@ -140,7 +140,7 @@ static int aufs_permission(struct inode *inode, int mask)
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);
}
}
@@ -159,19 +159,25 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
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 (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))
@@ -186,7 +192,7 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
err = au_digen_test(parent, au_sigen(sb));
if (!err) {
npositive = au_lkup_dentry(dentry, au_dbstart(parent),
/*type*/0, flags);
/*type*/0);
err = npositive;
}
di_read_unlock(parent, AuLock_IR);
@@ -204,6 +210,13 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
}
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);
iput(inode);
@@ -213,12 +226,16 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
out_unlock:
di_write_unlock(dentry);
if (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);
/* 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);
@@ -243,16 +260,19 @@ static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent,
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);
@@ -282,9 +302,11 @@ int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
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;
@@ -300,8 +322,10 @@ int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
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;
}
@@ -348,6 +372,85 @@ out:
/* ---------------------------------------------------------------------- */
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)
@@ -357,12 +460,13 @@ struct dentry *au_pinned_h_parent(struct au_pin *pin)
void au_unpin(struct au_pin *p)
{
if (p->hdir)
au_pin_hdir_unlock(p);
if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE))
vfsub_mnt_drop_write(p->h_mnt);
if (!p->hdir)
return;
au_hn_imtx_unlock(p->hdir);
if (!au_ftest_pin(p->flags, DI_LOCKED))
di_read_unlock(p->parent, AuLock_IR);
iput(p->hdir->hi_inode);
@@ -370,22 +474,21 @@ void au_unpin(struct au_pin *p)
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 = vfsub_mnt_want_write(p->h_mnt);
if (unlikely(err)) {
au_fclr_pin(p->flags, MNT_WRITE);
@@ -395,16 +498,16 @@ int au_do_pin(struct au_pin *p)
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;
@@ -414,7 +517,7 @@ int au_do_pin(struct au_pin *p)
* 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);
@@ -423,33 +526,24 @@ int au_do_pin(struct au_pin *p)
goto out_err;
}
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;
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 = vfsub_mnt_want_write(p->h_mnt);
if (unlikely(err)) {
au_fclr_pin(p->flags, MNT_WRITE);
goto out_unpin;
if (!au_ftest_pin(p->flags, DI_LOCKED))
di_read_unlock(p->parent, AuLock_IR);
dput(p->parent);
p->parent = NULL;
goto out_err;
}
}
goto out; /* success */
out_unpin:
au_unpin(p);
au_igrab(h_dir);
err = au_pin_hdir_lock(p);
if (!err)
goto out; /* success */
out_err:
pr_err("err %d\n", err);
err = au_busy_or_stale();
@@ -471,6 +565,11 @@ void au_pin_init(struct au_pin *p, struct dentry *dentry,
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,
@@ -536,7 +635,6 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
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
@@ -576,13 +674,20 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
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);
@@ -600,13 +705,15 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
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)
@@ -615,14 +722,9 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
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) {
@@ -630,6 +732,8 @@ out_parent:
dput(parent);
}
out:
if (!err)
mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
return err;
}
@@ -698,8 +802,7 @@ static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
goto out_unlock;
} else if ((ia->ia_valid & (ATTR_UID | ATTR_GID))
&& (ia->ia_valid & ATTR_CTIME)) {
err = security_path_chown(&a->h_path, vfsub_ia_uid(ia),
vfsub_ia_gid(ia));
err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid);
if (unlikely(err))
goto out_unlock;
}
@@ -749,8 +852,9 @@ static void au_refresh_iattr(struct inode *inode, struct kstat *st,
unsigned int n;
inode->i_mode = st->mode;
i_uid_write(inode, st->uid);
i_gid_write(inode, st->gid);
/* don't i_[ug]id_write() here */
inode->i_uid = st->uid;
inode->i_gid = st->gid;
inode->i_atime = st->atime;
inode->i_mtime = st->mtime;
inode->i_ctime = st->ctime;
@@ -760,6 +864,7 @@ static void au_refresh_iattr(struct inode *inode, struct kstat *st,
n = inode->i_nlink;
n -= nlink;
n += st->nlink;
smp_mb();
/* 0 can happen */
set_nlink(inode, n);
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -190,7 +190,7 @@ lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt,
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);
}
@@ -234,47 +234,55 @@ static int add_simple(struct inode *dir, struct dentry *dentry,
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,
arg->u.c.want_excl);
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();
@@ -284,18 +292,18 @@ static int add_simple(struct inode *dir, struct dentry *dentry,
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:
@@ -306,6 +314,8 @@ out_unlock:
d_drop(dentry);
}
aufs_read_unlock(dentry, AuLock_DW);
out_free:
kfree(a);
out:
return err;
}
@@ -359,8 +369,14 @@ static int au_cpup_before_link(struct dentry *src_dentry,
{
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);
@@ -368,22 +384,13 @@ static int au_cpup_before_link(struct dentry *src_dentry,
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:
@@ -391,12 +398,14 @@ 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;
@@ -408,22 +417,38 @@ static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a)
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);
@@ -522,7 +547,7 @@ int aufs_link(struct dentry *src_dentry, struct inode *dir,
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);
@@ -601,6 +626,7 @@ out_unlock:
out_kfree:
kfree(a);
out:
AuTraceErr(err);
return err;
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -68,8 +68,7 @@ int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup)
au_di_cp(tmp, dinfo);
au_di_swap(tmp, dinfo);
/* returns the number of positive dentries */
need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0,
/*flags*/0);
need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0);
au_di_swap(tmp, dinfo);
au_rw_write_unlock(&tmp->di_rwsem);
au_di_free(tmp);
@@ -300,17 +299,25 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry)
{
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;
@@ -325,17 +332,18 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry)
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;
@@ -349,8 +357,9 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry)
/* 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;
@@ -361,19 +370,22 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry)
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;
}
@@ -382,17 +394,25 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
{
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;
@@ -416,7 +436,8 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
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;
@@ -457,13 +478,14 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
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:
@@ -472,6 +494,8 @@ out_parent:
au_whtmp_rmdir_free(args);
out_unlock:
aufs_read_unlock(dentry, AuLock_DW);
out_free:
kfree(a);
out:
AuTraceErr(err);
return err;

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -208,32 +208,9 @@ static int au_ren_or_cpup(struct au_ren_args *a)
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);
@@ -334,31 +311,13 @@ static int do_rename(struct au_ren_args *a)
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;
@@ -610,13 +569,10 @@ out:
*/
static void au_ren_unlock(struct au_ren_args *a)
{
struct super_block *sb;
sb = a->dst_dentry->d_sb;
if (au_ftest_ren(a->flags, MNT_WRITE))
vfsub_mnt_drop_write(a->br->br_mnt);
vfsub_unlock_rename(a->src_h_parent, a->src_hdir,
a->dst_h_parent, a->dst_hdir);
if (au_ftest_ren(a->flags, MNT_WRITE))
vfsub_mnt_drop_write(au_br_mnt(a->br));
}
static int au_ren_lock(struct au_ren_args *a)
@@ -629,6 +585,11 @@ static int au_ren_lock(struct au_ren_args *a)
a->src_hdir = au_hi(a->src_dir, a->btgt);
a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt);
a->dst_hdir = au_hi(a->dst_dir, a->btgt);
err = vfsub_mnt_want_write(au_br_mnt(a->br));
if (unlikely(err))
goto out;
au_fset_ren(a->flags, MNT_WRITE);
a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir,
a->dst_h_parent, a->dst_hdir);
udba = au_opt_udba(a->src_dentry->d_sb);
@@ -643,18 +604,12 @@ static int au_ren_lock(struct au_ren_args *a)
err = au_h_verify(a->dst_h_dentry, udba,
a->dst_h_parent->d_inode, a->dst_h_parent,
a->br);
if (!err) {
err = vfsub_mnt_want_write(a->br->br_mnt);
if (unlikely(err))
goto out_unlock;
au_fset_ren(a->flags, MNT_WRITE);
if (!err)
goto out; /* success */
}
err = au_busy_or_stale();
out_unlock:
au_ren_unlock(a);
out:
return err;
}
@@ -924,7 +879,7 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
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);
@@ -962,9 +917,37 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
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))

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -110,10 +110,22 @@ void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
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 */
@@ -164,6 +176,7 @@ void au_icntnr_init_once(void *_c)
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);
@@ -186,8 +199,7 @@ int au_iinfo_init(struct inode *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;

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -34,7 +34,7 @@ struct inode *au_igrab(struct inode *inode)
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++;
}
@@ -253,6 +253,8 @@ out:
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;
@@ -271,12 +273,20 @@ static int reval_inode(struct inode *inode, struct dentry *dentry)
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))
@@ -326,7 +336,7 @@ struct inode *au_new_inode(struct dentry *dentry, int must_new)
struct super_block *sb;
struct mutex *mtx;
ino_t h_ino, ino;
int err, lc_idx;
int err;
aufs_bindex_t bstart;
sb = dentry->d_sb;
@@ -367,12 +377,16 @@ new_ino:
AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
if (inode->i_state & I_NEW) {
lc_idx = AuLcNonDir_IIINFO;
if (S_ISLNK(h_inode->i_mode))
lc_idx = AuLcSymlink_IIINFO;
else if (S_ISDIR(h_inode->i_mode))
lc_idx = AuLcDir_IIINFO;
au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + lc_idx);
/* 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);

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -51,9 +51,22 @@ struct au_hinode {
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;
@@ -88,8 +101,19 @@ struct au_pin {
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)
@@ -131,7 +155,8 @@ extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop;
/* 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)
@@ -202,7 +227,7 @@ unsigned int au_hi_flags(struct inode *inode, int isdir);
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);
@@ -310,9 +335,19 @@ static inline void au_icntnr_init(struct au_icntnr *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 */
@@ -329,7 +364,12 @@ static inline int au_test_higen(struct inode *inode, struct inode *h_inode)
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)
@@ -337,7 +377,7 @@ 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;

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -51,12 +51,14 @@ int au_test_loopback_kthread(void)
{
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;

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -186,17 +186,16 @@ static match_table_t brperm = {
{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[])
{
@@ -227,7 +226,7 @@ static int br_attr_val(char *str, match_table_t table, substring_t args[])
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, '+');
@@ -244,13 +243,33 @@ static int noinline_for_stack br_perm_val(char *perm)
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;
}
@@ -293,6 +312,7 @@ char *au_optstr_br_perm(int brperm)
AuDebugOn(1);
}
AppendAttr(AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN);
AppendAttr(AuBrRAttr_WH, AUFS_BRRATTR_WH);
AppendAttr(AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH);
@@ -344,6 +364,8 @@ static match_table_t au_wbr_create_policy = {
{AuWbrCreate_MFSRRV, "mfsrr:%d:%d"},
{AuWbrCreate_PMFS, "pmfs"},
{AuWbrCreate_PMFSV, "pmfs:%d"},
{AuWbrCreate_PMFSRR, "pmfsrr:%d"},
{AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"},
{-1, NULL}
};
@@ -413,6 +435,7 @@ au_wbr_create_val(char *str, struct au_opt_wbr_create *create)
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);
@@ -420,6 +443,7 @@ au_wbr_create_val(char *str, struct au_opt_wbr_create *create)
err = e;
break;
case AuWbrCreate_MFSRR:
case AuWbrCreate_PMFSRR:
e = au_wbr_mfs_wmark(&args[0], str, create);
if (unlikely(e)) {
err = e;
@@ -636,6 +660,7 @@ static void dump_opts(struct au_opts *opts)
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);
@@ -1175,6 +1200,8 @@ static int au_opt_wbr_create(struct super_block *sb,
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:
@@ -1518,7 +1545,7 @@ int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
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);

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -102,6 +102,8 @@ enum {
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
};

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -119,19 +119,13 @@ int au_plink_maint_enter(struct super_block *sb)
/* ---------------------------------------------------------------------- */
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);
@@ -140,20 +134,23 @@ void au_plink_list(struct super_block *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);
@@ -162,9 +159,10 @@ int au_plink_test(struct inode *inode)
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;
@@ -260,7 +258,7 @@ static int do_whplink(struct qstr *tgt, struct dentry *h_parent,
{
int err;
struct path h_path = {
.mnt = br->br_mnt
.mnt = au_br_mnt(br)
};
struct inode *h_dir;
@@ -343,7 +341,7 @@ static int whplink(struct dentry *h_dentry, struct inode *inode,
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);
}
@@ -366,30 +364,24 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
{
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);
@@ -398,20 +390,22 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
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);
@@ -422,7 +416,7 @@ out:
if (unlikely(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);
}
}
@@ -431,9 +425,11 @@ out:
/* 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);
@@ -441,12 +437,18 @@ void au_plink_put(struct super_block *sb, int verbose)
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)
@@ -460,15 +462,44 @@ 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);
@@ -476,36 +507,17 @@ void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id)
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);
}
}

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -27,11 +27,13 @@
*/
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);
@@ -53,7 +55,7 @@ void au_si_free(struct kobject *kobj)
int au_si_alloc(struct super_block *sb)
{
int err;
int err, i;
struct au_sbinfo *sbinfo;
static struct lock_class_key aufs_si;
@@ -89,6 +91,7 @@ int au_si_alloc(struct super_block *sb)
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;
@@ -97,6 +100,9 @@ int au_si_alloc(struct super_block *sb)
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 */
@@ -106,7 +112,8 @@ int au_si_alloc(struct super_block *sb)
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);
@@ -157,7 +164,7 @@ unsigned int au_sigen_inc(struct super_block *sb)
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;
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -58,5 +58,55 @@ static inline void au_spl_del_rcu(struct list_head *list,
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__ */

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -104,7 +104,7 @@ static int au_show_brs(struct seq_file *seq, struct super_block *sb)
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) {
@@ -131,14 +131,14 @@ static void au_show_wbr_create(struct seq_file *m, int v,
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",
@@ -160,6 +160,16 @@ static void au_show_wbr_create(struct seq_file *m, int v,
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;
}
}
@@ -417,6 +427,36 @@ static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf)
/* ---------------------------------------------------------------------- */
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)
{
@@ -627,7 +667,7 @@ static int au_refresh_i(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);
@@ -780,6 +820,7 @@ static const struct super_operations aufs_sop = {
.show_options = aufs_show_options,
.statfs = aufs_statfs,
.put_super = aufs_put_super,
.sync_fs = aufs_sync_fs,
.remount_fs = aufs_remount_fs
};

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -39,8 +39,15 @@ struct au_wbr_copyup_operations {
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);
};
@@ -55,6 +62,20 @@ struct au_wbr_mfs {
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 */
@@ -116,6 +137,8 @@ struct au_sbinfo {
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 */
@@ -145,7 +168,7 @@ struct au_sbinfo {
/* 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;
@@ -158,7 +181,9 @@ struct au_sbinfo {
*/
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
@@ -266,16 +291,8 @@ static inline struct au_sbinfo *au_sbi(struct super_block *sb)
/* ---------------------------------------------------------------------- */
#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);
@@ -288,8 +305,8 @@ static inline int au_busy_or_stale(void)
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)
@@ -357,6 +374,7 @@ static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo)
/* 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;

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -78,7 +78,7 @@ int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb)
* 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;
@@ -92,18 +92,30 @@ static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb,
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;
}
@@ -125,13 +137,15 @@ static struct seq_file *au_seq(char *p, ssize_t len)
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;
@@ -173,19 +187,25 @@ ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,
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) {
@@ -205,17 +225,26 @@ out:
void sysaufs_br_init(struct au_branch *br)
{
struct attribute *attr = &br->br_attr;
int i;
struct au_brsysfs *br_sysfs;
struct attribute *attr;
sysfs_attr_init(attr);
attr->name = br->br_name;
attr->mode = S_IRUGO;
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);
@@ -227,16 +256,21 @@ void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t 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);
@@ -247,11 +281,17 @@ void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t 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_warn("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++;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -35,16 +35,18 @@ static void sysrq_sb(struct super_block *sb)
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
@@ -72,7 +74,7 @@ static void sysrq_sb(struct super_block *sb)
#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);
@@ -83,7 +85,7 @@ static void sysrq_sb(struct super_block *sb)
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;
@@ -92,8 +94,9 @@ static void sysrq_sb(struct super_block *sb)
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;
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -522,7 +522,7 @@ static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir,
AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH));
err = -ENOMEM;
o = p = __getname_gfp(GFP_NOFS);
o = p = (void *)__get_free_page(GFP_NOFS);
if (unlikely(!p))
goto out;
@@ -542,7 +542,7 @@ static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir,
}
}
__putname(o);
free_page((unsigned long)o);
out:
AuTraceErr(err);

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -540,23 +540,18 @@ int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr,
{
int err;
struct inode *h_inode;
struct super_block *h_sb;
h_inode = h_path->dentry->d_inode;
if (!h_file) {
err = vfsub_mnt_want_write(h_path->mnt);
if (err)
goto out;
err = inode_permission(h_inode, MAY_WRITE);
if (err)
goto out_mnt;
err = get_write_access(h_inode);
if (err)
goto out_mnt;
err = break_lease(h_inode, O_WRONLY);
if (err)
goto out_inode;
err = vfsub_truncate(h_path, length);
goto out;
}
h_inode = h_path->dentry->d_inode;
h_sb = h_inode->i_sb;
lockdep_off();
sb_start_write(h_sb);
lockdep_on();
err = locks_verify_truncate(h_inode, h_file, length);
if (!err)
err = security_path_truncate(h_path);
@@ -565,13 +560,10 @@ int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr,
err = do_truncate(h_path->dentry, length, attr, h_file);
lockdep_on();
}
lockdep_off();
sb_end_write(h_sb);
lockdep_on();
out_inode:
if (!h_file)
put_write_access(h_inode);
out_mnt:
if (!h_file)
vfsub_mnt_drop_write(h_path->mnt);
out:
return err;
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -33,6 +33,7 @@
/* copied from linux/fs/internal.h */
/* todo: BAD approach!! */
extern struct lglock vfsmount_lock;
extern void __mnt_drop_write(struct vfsmount *);
extern spinlock_t inode_sb_list_lock;
/* copied from linux/fs/file_table.c */
@@ -102,19 +103,6 @@ static inline void vfsub_dead_dir(struct inode *inode)
/* ---------------------------------------------------------------------- */
/* cf. i_[ug]id_read() in linux/include/fs.h */
static inline uid_t vfsub_ia_uid(struct iattr *ia)
{
return from_kuid(&init_user_ns, ia->ia_uid);
}
static inline gid_t vfsub_ia_gid(struct iattr *ia)
{
return from_kgid(&init_user_ns, ia->ia_gid);
}
/* ---------------------------------------------------------------------- */
int vfsub_update_h_iattr(struct path *h_path, int *did);
struct file *vfsub_dentry_open(struct path *path, int flags);
struct file *vfsub_filp_open(const char *path, int oflags, int mode);
@@ -155,6 +143,13 @@ static inline void vfsub_mnt_drop_write(struct vfsmount *mnt)
lockdep_on();
}
static inline void vfsub_mnt_drop_write_file(struct file *file)
{
lockdep_off();
mnt_drop_write_file(file);
lockdep_on();
}
/* ---------------------------------------------------------------------- */
struct au_hinode;
@@ -228,6 +223,16 @@ long vfsub_splice_to(struct file *in, loff_t *ppos,
unsigned int flags);
long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags);
static inline long vfsub_truncate(struct path *path, loff_t length)
{
long err;
lockdep_off();
err = vfs_truncate(path, length);
lockdep_on();
return err;
}
int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr,
struct file *h_file);
int vfsub_fsync(struct file *file, struct path *path, int datasync);

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -37,7 +37,7 @@ int au_cpdown_attr(struct path *h_path, struct dentry *h_src)
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? */
@@ -60,13 +60,8 @@ int au_cpdown_attr(struct path *h_path, struct dentry *h_src)
#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;
@@ -76,7 +71,7 @@ static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst,
if (IS_ERR(opq_dentry))
goto out;
dput(opq_dentry);
au_fset_cpdown(a->flags, DIROPQ);
au_fset_cpdown(*flags, DIROPQ);
out:
return err;
@@ -97,7 +92,7 @@ static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent,
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);
}
@@ -108,6 +103,7 @@ out:
}
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;
@@ -115,7 +111,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
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 */
@@ -125,7 +121,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
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);
@@ -134,19 +130,19 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t 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;
@@ -158,7 +154,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
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;
@@ -173,7 +169,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
/* 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);
@@ -185,7 +181,7 @@ out_opq:
}
}
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",
@@ -205,13 +201,10 @@ out:
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;
}
@@ -267,7 +260,8 @@ static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex)
}
/* 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;
@@ -356,7 +350,7 @@ static int au_wbr_create_init_rr(struct super_block *sb)
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;
@@ -373,7 +367,7 @@ static int au_wbr_create_rr(struct dentry *dentry, int isdir)
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))
@@ -400,11 +394,12 @@ out:
/* ---------------------------------------------------------------------- */
/* 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;
@@ -424,14 +419,26 @@ static void au_mfs(struct dentry *dentry)
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)) {
@@ -456,9 +463,10 @@ static void au_mfs(struct dentry *dentry)
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;
@@ -467,14 +475,18 @@ static int au_wbr_create_mfs(struct dentry *dentry, int isdir __maybe_unused)
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);
@@ -505,17 +517,17 @@ static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused)
/* ---------------------------------------------------------------------- */
/* 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);
}
@@ -536,7 +548,7 @@ static int au_wbr_create_init_mfsrr(struct super_block *sb)
/* ---------------------------------------------------------------------- */
/* 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;
@@ -545,7 +557,7 @@ static int au_wbr_create_pmfs(struct dentry *dentry, int isdir)
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);
@@ -554,7 +566,7 @@ static int au_wbr_create_pmfs(struct dentry *dentry, int isdir)
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 */
@@ -589,12 +601,46 @@ out:
/* ---------------------------------------------------------------------- */
/*
* - 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 */
@@ -696,5 +742,15 @@ struct au_wbr_create_operations au_wbr_create_ops[] = {
.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
}
};

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -174,7 +174,7 @@ int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br)
{
int err;
struct path h_path = {
.mnt = br->br_mnt
.mnt = au_br_mnt(br)
};
struct inode *h_dir;
struct dentry *h_parent;
@@ -233,7 +233,7 @@ static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh,
{
int err;
struct path h_path = {
.mnt = br->br_mnt
.mnt = au_br_mnt(br)
};
err = 0;
@@ -263,14 +263,10 @@ static void au_wh_clean(struct inode *h_dir, struct path *whpath,
if (!whpath->dentry->d_inode)
return;
err = vfsub_mnt_want_write(whpath->mnt);
if (!err) {
if (isdir)
err = vfsub_rmdir(h_dir, whpath);
else
err = vfsub_unlink(h_dir, whpath, /*force*/0);
vfsub_mnt_drop_write(whpath->mnt);
}
if (isdir)
err = vfsub_rmdir(h_dir, whpath);
else
err = vfsub_unlink(h_dir, whpath, /*force*/0);
if (unlikely(err))
pr_warn("failed removing %.*s (%d), ignored.\n",
AuDLNPair(whpath->dentry), err);
@@ -299,11 +295,7 @@ static int au_whdir(struct inode *h_dir, struct path *path)
if (au_test_nfs(path->dentry->d_sb))
mode |= S_IXUGO;
err = vfsub_mnt_want_write(path->mnt);
if (!err) {
err = vfsub_mkdir(h_dir, path, mode);
vfsub_mnt_drop_write(path->mnt);
}
err = vfsub_mkdir(h_dir, path, mode);
} else if (S_ISDIR(path->dentry->d_inode->i_mode))
err = 0;
else
@@ -397,13 +389,8 @@ static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr,
err = -EEXIST;
h_dir = h_root->d_inode;
if (!base[AuBrWh_BASE].dentry->d_inode) {
err = vfsub_mnt_want_write(h_path->mnt);
if (!err) {
h_path->dentry = base[AuBrWh_BASE].dentry;
err = vfsub_create(h_dir, h_path, WH_MASK,
/*want_excl*/true);
vfsub_mnt_drop_write(h_path->mnt);
}
h_path->dentry = base[AuBrWh_BASE].dentry;
err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true);
} else if (S_ISREG(base[AuBrWh_BASE].dentry->d_inode->i_mode))
err = 0;
else
@@ -435,16 +422,14 @@ out:
/*
* 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] = QSTR_INIT(AUFS_BASE_NAME,
@@ -558,19 +543,16 @@ static void reinit_br_wh(void *arg)
dir = a->sb->s_root->d_inode;
hdir = au_hi(dir, bindex);
h_root = au_h_dptr(a->sb->s_root, bindex);
AuDebugOn(h_root != au_br_dentry(a->br));
au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT);
wbr_wh_write_lock(wbr);
err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode,
h_root, a->br);
if (!err) {
err = vfsub_mnt_want_write(a->br->br_mnt);
if (!err) {
h_path.dentry = wbr->wbr_whbase;
h_path.mnt = a->br->br_mnt;
err = vfsub_unlink(hdir->hi_inode, &h_path, /*force*/0);
vfsub_mnt_drop_write(a->br->br_mnt);
}
h_path.dentry = wbr->wbr_whbase;
h_path.mnt = au_br_mnt(a->br);
err = vfsub_unlink(hdir->hi_inode, &h_path, /*force*/0);
} else {
pr_warn("%.*s is moved, ignored\n",
AuDLNPair(wbr->wbr_whbase));
@@ -579,7 +561,7 @@ static void reinit_br_wh(void *arg)
dput(wbr->wbr_whbase);
wbr->wbr_whbase = NULL;
if (!err)
err = au_wh_init(h_root, a->br, a->sb);
err = au_wh_init(a->br, a->sb);
wbr_wh_write_unlock(wbr);
au_hn_imtx_unlock(hdir);
di_read_unlock(a->sb->s_root, AuLock_IR);
@@ -650,7 +632,7 @@ static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex,
IMustLock(h_dir);
br = au_sbr(sb, bindex);
h_path.mnt = br->br_mnt;
h_path.mnt = au_br_mnt(br);
wbr = br->br_wbr;
wbr_wh_read_lock(wbr);
if (wbr->wbr_whbase) {
@@ -699,7 +681,7 @@ static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex,
} else {
struct path tmp = {
.dentry = opq_dentry,
.mnt = br->br_mnt
.mnt = au_br_mnt(br)
};
err = do_unlink_wh(au_h_iptr(dentry->d_inode, bindex), &tmp);
if (!err)
@@ -951,7 +933,7 @@ int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex,
if (!err) {
h_tmp.dentry = wh_dentry;
h_tmp.mnt = br->br_mnt;
h_tmp.mnt = au_br_mnt(br);
err = vfsub_rmdir(h_dir, &h_tmp);
}
@@ -995,21 +977,20 @@ static void call_rmdir_whtmp(void *args)
h_parent = dget_parent(a->wh_dentry);
h_dir = h_parent->d_inode;
hdir = au_hi(a->dir, bindex);
err = vfsub_mnt_want_write(au_br_mnt(a->br));
if (unlikely(err))
goto out_mnt;
au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT);
err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent,
a->br);
if (!err) {
err = vfsub_mnt_want_write(a->br->br_mnt);
if (!err) {
err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry,
&a->whlist);
vfsub_mnt_drop_write(a->br->br_mnt);
}
}
if (!err)
err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist);
au_hn_imtx_unlock(hdir);
vfsub_mnt_drop_write(au_br_mnt(a->br));
out_mnt:
dput(h_parent);
ii_write_unlock(a->dir);
out:
/* mutex_unlock(&a->dir->i_mutex); */
au_whtmp_rmdir_free(a);

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -38,8 +38,7 @@ struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br,
int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br);
int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path,
struct dentry *dentry);
int au_wh_init(struct dentry *h_parent, struct au_branch *br,
struct super_block *sb);
int au_wh_init(struct au_branch *br, struct super_block *sb);
/* diropq flags */
#define AuDiropq_CREATE 1

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -62,7 +62,7 @@ static void wkq_func(struct work_struct *wk)
/*
* Since struct completion is large, try allocating it dynamically.
*/
#if defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS)
#if 1 /* defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) */
#define AuWkqCompDeclare(name) struct completion *comp = NULL
static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp)

View File

@@ -1,5 +1,5 @@
/*
* 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

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -21,6 +21,7 @@
*/
#include <linux/seq_file.h>
#include <linux/statfs.h>
#include "aufs.h"
/* todo: unnecessary to support mmap_sem since kernel-space? */
@@ -243,38 +244,56 @@ static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir)
int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex)
{
int err;
unsigned long jiffy;
blkcnt_t blocks;
aufs_bindex_t bi, bend;
struct kstatfs *st;
struct au_branch *br;
struct file *new_xino, *file;
struct super_block *h_sb;
struct au_xino_lock_dir ldir;
err = -ENOMEM;
st = kzalloc(sizeof(*st), GFP_NOFS);
if (unlikely(!st))
goto out;
err = -EINVAL;
bend = au_sbend(sb);
if (unlikely(bindex < 0 || bend < bindex))
goto out;
goto out_st;
br = au_sbr(sb, bindex);
file = br->br_xino.xi_file;
if (!file)
goto out;
goto out_st;
err = vfs_statfs(&file->f_path, st);
if (unlikely(err))
AuErr1("statfs err %d, ignored\n", err);
jiffy = jiffies;
blocks = file->f_dentry->d_inode->i_blocks;
pr_info("begin truncating xino(b%d), ib%llu, %llu/%llu free blks\n",
bindex, (u64)blocks, st->f_bfree, st->f_blocks);
au_xino_lock_dir(sb, file, &ldir);
/* mnt_want_write() is unnecessary here */
new_xino = au_xino_create2(file, file);
au_xino_unlock_dir(&ldir);
err = PTR_ERR(new_xino);
if (IS_ERR(new_xino))
goto out;
if (IS_ERR(new_xino)) {
pr_err("err %d, ignored\n", err);
goto out_st;
}
err = 0;
fput(file);
br->br_xino.xi_file = new_xino;
h_sb = br->br_mnt->mnt_sb;
h_sb = au_br_sb(br);
for (bi = 0; bi <= bend; bi++) {
if (unlikely(bi == bindex))
continue;
br = au_sbr(sb, bi);
if (br->br_mnt->mnt_sb != h_sb)
if (au_br_sb(br) != h_sb)
continue;
fput(br->br_xino.xi_file);
@@ -282,6 +301,18 @@ int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex)
get_file(new_xino);
}
err = vfs_statfs(&new_xino->f_path, st);
if (!err) {
pr_info("end truncating xino(b%d), ib%llu, %llu/%llu free blks\n",
bindex, (u64)new_xino->f_dentry->d_inode->i_blocks,
st->f_bfree, st->f_blocks);
if (new_xino->f_dentry->d_inode->i_blocks < blocks)
au_sbi(sb)->si_xino_jiffy = jiffy;
} else
AuErr1("statfs err %d, ignored\n", err);
out_st:
kfree(st);
out:
return err;
}
@@ -309,11 +340,6 @@ static void xino_do_trunc(void *_args)
ii_read_lock_parent(dir);
bindex = au_br_index(sb, br->br_id);
err = au_xino_trunc(sb, bindex);
if (!err
&& br->br_xino.xi_file->f_dentry->d_inode->i_blocks
>= br->br_xino_upper)
br->br_xino_upper += AUFS_XINO_TRUNC_STEP;
ii_read_unlock(dir);
if (unlikely(err))
pr_warn("err b%d, (%d)\n", bindex, err);
@@ -324,13 +350,36 @@ static void xino_do_trunc(void *_args)
kfree(args);
}
static int xino_trunc_test(struct super_block *sb, struct au_branch *br)
{
int err;
struct kstatfs st;
struct au_sbinfo *sbinfo;
/* todo: si_xino_expire and the ratio should be customizable */
sbinfo = au_sbi(sb);
if (time_before(jiffies,
sbinfo->si_xino_jiffy + sbinfo->si_xino_expire))
return 0;
/* truncation border */
err = vfs_statfs(&br->br_xino.xi_file->f_path, &st);
if (unlikely(err)) {
AuErr1("statfs err %d, ignored\n", err);
return 0;
}
if (div64_u64(st.f_bfree * 100, st.f_blocks) >= AUFS_XINO_DEF_TRUNC)
return 0;
return 1;
}
static void xino_try_trunc(struct super_block *sb, struct au_branch *br)
{
struct xino_do_trunc_args *args;
int wkq_err;
if (br->br_xino.xi_file->f_dentry->d_inode->i_blocks
< br->br_xino_upper)
if (!xino_trunc_test(sb, br))
return;
if (atomic_inc_return(&br->br_xino_running) > 1)
@@ -408,7 +457,7 @@ int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
h_ino, ino);
if (!err) {
if (au_opt_test(mnt_flags, TRUNC_XINO)
&& au_test_fs_trunc_xino(br->br_mnt->mnt_sb))
&& au_test_fs_trunc_xino(au_br_sb(br)))
xino_try_trunc(sb, br);
return 0; /* success */
}
@@ -561,7 +610,7 @@ void au_xino_delete_inode(struct inode *inode, const int unlinked)
err = au_xino_do_write(xwrite, br->br_xino.xi_file,
h_inode->i_ino, /*ino*/0);
if (!err && try_trunc
&& au_test_fs_trunc_xino(br->br_mnt->mnt_sb))
&& au_test_fs_trunc_xino(au_br_sb(br)))
xino_try_trunc(sb, br);
}
}
@@ -768,10 +817,10 @@ int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino,
shared_br = NULL;
bend = au_sbend(sb);
if (do_test) {
tgt_sb = br->br_mnt->mnt_sb;
tgt_sb = au_br_sb(br);
for (bindex = 0; bindex <= bend; bindex++) {
b = au_sbr(sb, bindex);
if (tgt_sb == b->br_mnt->mnt_sb) {
if (tgt_sb == au_br_sb(b)) {
shared_br = b;
break;
}
@@ -1200,7 +1249,7 @@ struct file *au_xino_def(struct super_block *sb)
for (bindex = 0; bindex <= bend; bindex++) {
br = au_sbr(sb, bindex);
if (au_br_writable(br->br_perm)
&& !au_test_fs_bad_xino(br->br_mnt->mnt_sb)) {
&& !au_test_fs_bad_xino(au_br_sb(br))) {
bwr = bindex;
break;
}
@@ -1211,7 +1260,7 @@ struct file *au_xino_def(struct super_block *sb)
page = (void *)__get_free_page(GFP_NOFS);
if (unlikely(!page))
goto out;
path.mnt = br->br_mnt;
path.mnt = au_br_mnt(br);
path.dentry = au_h_dptr(sb->s_root, bwr);
p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME));
file = (void *)p;