mirror of
https://github.com/hardkernel/linux.git
synced 2026-03-24 19:40:21 +09:00
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:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
235
ubuntu/aufs/aufs_type.h
Normal 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__ */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
14
ubuntu/aufs/conf.str
Normal 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
14
ubuntu/aufs/conf.str.tmp
Normal 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"
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user