Commit 72a970fc authored by J. R. Okajima's avatar J. R. Okajima
Browse files

aufs: writable branch 1/3, white-out



The writable branch prepares a few files and dirs for whiteouts.
For branch filesystems which doesn't support link(2), there is "nolwh"
attribute. On the branch which is specified this attribute, aufs never
try link(2) for whitout and always creat(2) it.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent f5f997d4
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/mount.h> #include <linux/mount.h>
#include "lcnt.h" #include "lcnt.h"
#include "rwsem.h"
#include "super.h" #include "super.h"
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
...@@ -40,6 +41,17 @@ struct au_xino { ...@@ -40,6 +41,17 @@ struct au_xino {
struct kref xi_kref; struct kref xi_kref;
}; };
/* members for writable branch only */
enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last};
struct au_wbr {
struct au_rwsem wbr_wh_rwsem;
struct dentry *wbr_wh[AuBrWh_Last];
atomic_t wbr_wh_running;
#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */
#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */
#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */
};
/* sysfs entries */ /* sysfs entries */
struct au_brsysfs { struct au_brsysfs {
char name[16]; char name[16];
...@@ -62,6 +74,8 @@ struct au_branch { ...@@ -62,6 +74,8 @@ struct au_branch {
struct path br_path; struct path br_path;
au_lcnt_t br_count; /* in-use for other */ au_lcnt_t br_count; /* in-use for other */
struct au_wbr *br_wbr;
#ifdef CONFIG_SYSFS #ifdef CONFIG_SYSFS
/* entries under sysfs per mount-point */ /* entries under sysfs per mount-point */
struct au_brsysfs br_sysfs[AuBrSysfs_Last]; struct au_brsysfs br_sysfs[AuBrSysfs_Last];
...@@ -198,5 +212,31 @@ static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) ...@@ -198,5 +212,31 @@ static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex)
return au_sbr(sb, bindex)->br_perm; return au_sbr(sb, bindex)->br_perm;
} }
static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex)
{
return au_br_whable(au_sbr_perm(sb, bindex));
}
/* ---------------------------------------------------------------------- */
#define wbr_wh_read_lock(wbr) au_rw_read_lock(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_write_lock(wbr) au_rw_write_lock(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_read_trylock(wbr) au_rw_read_trylock(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_write_trylock(wbr) au_rw_write_trylock(&(wbr)->wbr_wh_rwsem)
/*
#define wbr_wh_read_trylock_nested(wbr) \
au_rw_read_trylock_nested(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_write_trylock_nested(wbr) \
au_rw_write_trylock_nested(&(wbr)->wbr_wh_rwsem)
*/
#define wbr_wh_read_unlock(wbr) au_rw_read_unlock(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_write_unlock(wbr) au_rw_write_unlock(&(wbr)->wbr_wh_rwsem)
#define wbr_wh_downgrade_lock(wbr) au_rw_dgrade_lock(&(wbr)->wbr_wh_rwsem)
#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&(wbr)->wbr_wh_rwsem)
#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&(wbr)->wbr_wh_rwsem)
#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&(wbr)->wbr_wh_rwsem)
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* __AUFS_BRANCH_H__ */ #endif /* __AUFS_BRANCH_H__ */
...@@ -68,6 +68,28 @@ out: ...@@ -68,6 +68,28 @@ out:
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl)
{
int err;
struct dentry *d;
IMustLock(dir);
d = path->dentry;
path->dentry = d->d_parent;
err = security_path_mknod(path, d, mode, 0);
path->dentry = d;
if (unlikely(err))
goto out;
lockdep_off();
err = vfs_create(dir, path->dentry, mode, want_excl);
lockdep_on();
out:
return err;
}
static int au_test_nlink(struct inode *inode) static int au_test_nlink(struct inode *inode)
{ {
const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ const unsigned int link_max = UINT_MAX >> 1; /* rough margin */
......
...@@ -51,6 +51,8 @@ static inline struct dentry *vfsub_lkup_one(struct qstr *name, ...@@ -51,6 +51,8 @@ static inline struct dentry *vfsub_lkup_one(struct qstr *name,
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
int vfsub_create(struct inode *dir, struct path *path, int mode,
bool want_excl);
int vfsub_link(struct dentry *src_dentry, struct inode *dir, int vfsub_link(struct dentry *src_dentry, struct inode *dir,
struct path *path, struct inode **delegated_inode); struct path *path, struct inode **delegated_inode);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
* whiteout for logical deletion and opaque directory * whiteout for logical deletion and opaque directory
*/ */
#include <linux/cred.h>
#include "aufs.h" #include "aufs.h"
#define WH_MASK 0444 #define WH_MASK 0444
...@@ -82,6 +83,205 @@ out: ...@@ -82,6 +83,205 @@ out:
return err; return err;
} }
/* ---------------------------------------------------------------------- */
/*
* functions for removing a whiteout
*/
static int do_unlink_wh(struct inode *h_dir, struct path *h_path)
{
int err, force;
struct inode *delegated;
/*
* forces superio when the dir has a sticky bit.
* this may be a violation of unix fs semantics.
*/
force = (h_dir->i_mode & S_ISVTX)
&& !uid_eq(current_fsuid(), d_inode(h_path->dentry)->i_uid);
delegated = NULL;
err = vfsub_unlink(h_dir, h_path, &delegated, force);
if (unlikely(err == -EWOULDBLOCK)) {
pr_warn("cannot retry for NFSv4 delegation"
" for an internal unlink\n");
iput(delegated);
}
return err;
}
int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path,
struct dentry *dentry)
{
int err;
err = do_unlink_wh(h_dir, h_path);
if (!err && dentry)
au_set_dbwh(dentry, -1);
return err;
}
/* ---------------------------------------------------------------------- */
/*
* whiteouts are all hard-linked usually.
* when its link count reaches a ceiling, we create a new whiteout base
* asynchronously.
*/
struct reinit_br_wh {
struct super_block *sb;
struct au_branch *br;
};
static void reinit_br_wh(void *arg)
{
int err;
aufs_bindex_t bindex;
struct path h_path;
struct reinit_br_wh *a = arg;
struct au_wbr *wbr;
struct inode *dir, *delegated;
struct dentry *h_root;
struct au_hinode *hdir;
err = 0;
wbr = a->br->br_wbr;
/* big aufs lock */
si_noflush_write_lock(a->sb);
if (!au_br_writable(a->br->br_perm))
goto out;
bindex = au_br_index(a->sb, a->br->br_id);
if (unlikely(bindex < 0))
goto out;
di_read_lock_parent(a->sb->s_root, AuLock_IR);
dir = d_inode(a->sb->s_root);
hdir = au_hi(dir, bindex);
h_root = au_h_dptr(a->sb->s_root, bindex);
AuDebugOn(h_root != au_br_dentry(a->br));
inode_lock_nested(hdir->hi_inode, AuLsc_I_PARENT);
wbr_wh_write_lock(wbr);
/* re-commit later */
/* err = au_h_verify(wbr->wbr_whbase, hdir->hi_inode, h_root, a->br); */
err = 0;
if (!err) {
h_path.dentry = wbr->wbr_whbase;
h_path.mnt = au_br_mnt(a->br);
delegated = NULL;
err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated,
/*force*/0);
if (unlikely(err == -EWOULDBLOCK)) {
pr_warn("cannot retry for NFSv4 delegation"
" for an internal unlink\n");
iput(delegated);
}
} else {
pr_warn("%pd is moved, ignored\n", wbr->wbr_whbase);
err = 0;
}
dput(wbr->wbr_whbase);
wbr->wbr_whbase = NULL;
#if 0 /* re-commit later */
if (!err)
err = au_wh_init(a->br, a->sb);
#endif
wbr_wh_write_unlock(wbr);
inode_unlock(hdir->hi_inode);
di_read_unlock(a->sb->s_root, AuLock_IR);
out:
if (wbr)
atomic_dec(&wbr->wbr_wh_running);
au_lcnt_dec(&a->br->br_count);
si_write_unlock(a->sb);
au_nwt_done(&au_sbi(a->sb)->si_nowait);
au_kfree_rcu(a);
if (unlikely(err))
AuIOErr("err %d\n", err);
}
static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br)
{
int do_dec, wkq_err;
struct reinit_br_wh *arg;
do_dec = 1;
if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1)
goto out;
/* ignore ENOMEM */
arg = kmalloc(sizeof(*arg), GFP_NOFS);
if (arg) {
/*
* dec(wh_running), kfree(arg) and dec(br_count)
* in reinit function
*/
arg->sb = sb;
arg->br = br;
au_lcnt_inc(&br->br_count);
wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*flags*/0);
if (unlikely(wkq_err)) {
atomic_dec(&br->br_wbr->wbr_wh_running);
au_lcnt_dec(&br->br_count);
au_kfree_rcu(arg);
}
do_dec = 0;
}
out:
if (do_dec)
atomic_dec(&br->br_wbr->wbr_wh_running);
}
/* ---------------------------------------------------------------------- */
/*
* create the whiteout @wh.
*/
static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex,
struct dentry *wh)
{
int err;
struct path h_path = {
.dentry = wh
};
struct au_branch *br;
struct au_wbr *wbr;
struct dentry *h_parent;
struct inode *h_dir, *delegated;
h_parent = wh->d_parent; /* dir inode is locked */
h_dir = d_inode(h_parent);
IMustLock(h_dir);
br = au_sbr(sb, bindex);
h_path.mnt = au_br_mnt(br);
wbr = br->br_wbr;
wbr_wh_read_lock(wbr);
if (wbr->wbr_whbase) {
delegated = NULL;
err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path, &delegated);
if (unlikely(err == -EWOULDBLOCK)) {
pr_warn("cannot retry for NFSv4 delegation"
" for an internal link\n");
iput(delegated);
}
if (!err || err != -EMLINK)
goto out;
/* link count full. re-initialize br_whbase. */
kick_reinit_br_wh(sb, br);
}
/* return this error in this context */
err = vfsub_create(h_dir, &h_path, WH_MASK, /*want_excl*/true);
out:
wbr_wh_read_unlock(wbr);
return err;
}
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* /*
...@@ -119,8 +319,7 @@ struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, ...@@ -119,8 +319,7 @@ struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex,
sb = dentry->d_sb; sb = dentry->d_sb;
wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex)); wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex));
if (!IS_ERR(wh_dentry) && d_is_negative(wh_dentry)) { if (!IS_ERR(wh_dentry) && d_is_negative(wh_dentry)) {
/* err = link_or_create_wh(sb, bindex, wh_dentry); re-commit later */ err = link_or_create_wh(sb, bindex, wh_dentry);
err = 0;
if (!err) if (!err)
au_set_dbwh(dentry, bindex); au_set_dbwh(dentry, bindex);
else { else {
......
...@@ -17,6 +17,10 @@ struct qstr; ...@@ -17,6 +17,10 @@ struct qstr;
int au_wh_name_alloc(struct qstr *wh, const struct qstr *name); int au_wh_name_alloc(struct qstr *wh, const struct qstr *name);
struct dentry; struct dentry;
int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio); int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio);
struct inode;
struct path;
int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path,
struct dentry *dentry);
struct au_branch; struct au_branch;
struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name,
struct au_branch *br); struct au_branch *br);
......
...@@ -77,21 +77,37 @@ typedef int16_t aufs_bindex_t; ...@@ -77,21 +77,37 @@ typedef int16_t aufs_bindex_t;
#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME #define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME
/* branch permissions and attributes */ /* branch permissions and attributes */
#define AUFS_BRPERM_RW "rw"
#define AUFS_BRPERM_RO "ro" #define AUFS_BRPERM_RO "ro"
#define AUFS_BRRATTR_WH "wh" #define AUFS_BRRATTR_WH "wh"
#define AUFS_BRWATTR_NLWH "nolwh"
#define AuBrPerm_RW 1 /* writable, hardlinkable wh */
#define AuBrPerm_RO (1 << 1) /* readonly */ #define AuBrPerm_RO (1 << 1) /* readonly */
#define AuBrPerm_Mask AuBrPerm_RO /* re-commit later */ #define AuBrPerm_Mask (AuBrPerm_RW | AuBrPerm_RO)
#define AuBrRAttr_WH (1 << 7) /* whiteout-able */ #define AuBrRAttr_WH (1 << 7) /* whiteout-able */
#define AuBrRAttr_Mask AuBrRAttr_WH #define AuBrRAttr_Mask AuBrRAttr_WH
#define AuBrWAttr_NoLinkWH (1 << 8) /* un-hardlinkable whiteouts */
#define AuBrWAttr_Mask AuBrWAttr_NoLinkWH
/* the longest combination */ /* the longest combination */
#define AuBrPermStrSz sizeof(AUFS_BRPERM_RO \ #define AuBrPermStrSz sizeof(AUFS_BRPERM_RO \
"+" AUFS_BRRATTR_WH) "+" AUFS_BRWATTR_NLWH)
typedef struct { typedef struct {
char a[AuBrPermStrSz]; char a[AuBrPermStrSz];
} au_br_perm_str_t; } au_br_perm_str_t;
static inline int au_br_writable(int brperm)
{
return brperm & AuBrPerm_RW;
}
static inline int au_br_whable(int brperm)
{
return brperm & (AuBrPerm_RW | AuBrRAttr_WH);
}
#endif /* __AUFS_TYPE_H__ */ #endif /* __AUFS_TYPE_H__ */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment