Commit 53c63b41 authored by J. R. Okajima's avatar J. R. Okajima
Browse files

aufs: dirren 1/6, inum-list of the renamed dir in a branch



This commits brings a list of the inode numbers which indicates the
logically renamed dir into a branch. The list will be referred in
lookup, and its lifetime is equivalent to the branch's, ie. the list is
loaded/created in adding a branch, and stored/deleted in deleting a
branch. The simple storing happens in remounting and unmounting aufs
too.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent cd66ec8c
......@@ -96,6 +96,19 @@ config AUFS_XATTR
branch attributes for EA.
See detail in aufs.5.
config AUFS_DIRREN
bool "Workaround for rename(2)-ing a directory"
help
By default, aufs returns EXDEV error in renameing a dir who has
his child on the lower branch, since it is a bad idea to issue
rename(2) internally for every lower branch. But user may not
accept this behaviour. So here is a workaround to allow such
rename(2) and store some extra infromation on the writable
branch. Obviously this costs high (and I don't like it).
To use this feature, you need to enable this configuration AND
to specify the mount option `dirren.'
See details in aufs.5 and the design documents.
config AUFS_DEBUG
bool "Debug aufs"
help
......
......@@ -27,4 +27,5 @@ aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o
aufs-$(CONFIG_AUFS_EXPORT) += export.o
aufs-$(CONFIG_AUFS_XATTR) += xattr.o
aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o
aufs-$(CONFIG_AUFS_DIRREN) += dirren.o
aufs-$(CONFIG_AUFS_DEBUG) += debug.o
......@@ -27,6 +27,7 @@
#include "dcsub.h"
#include "dentry.h"
#include "dir.h"
#include "dirren.h"
#include "dynop.h"
#include "file.h"
#include "fstype.h"
......
......@@ -20,6 +20,8 @@ static void au_br_do_free(struct au_branch *br)
struct au_dykey **key;
au_hnotify_fin_br(br);
/* always, regardless the mount option */
au_dr_hino_free(&br->br_dirren);
au_xino_put(br);
AuLCntZero(au_lcnt_read(&br->br_nfiles, /*do_rev*/0));
......@@ -361,6 +363,11 @@ static int au_br_init(struct au_branch *br, struct super_block *sb,
br->br_id = au_new_br_id(sb);
AuDebugOn(br->br_id < 0);
/* always, regardless the given option */
err = au_dr_br_init(sb, br, &add->path);
if (unlikely(err))
goto out_err;
if (au_br_writable(add->perm)) {
err = au_wbr_init(br, sb, add->perm);
if (unlikely(err))
......@@ -933,6 +940,9 @@ static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex,
au_br_do_del_hdp(au_di(root), bindex, bbot);
au_br_do_del_hip(au_ii(inode), bindex, bbot);
/* ignore an error */
au_dr_br_fin(sb, br); /* always, regardless the mount option */
dput(h_root);
iput(h_inode);
au_br_do_free(br);
......
......@@ -13,6 +13,7 @@
#ifdef __KERNEL__
#include <linux/mount.h>
#include "dirren.h"
#include "dynop.h"
#include "lcnt.h"
#include "rwsem.h"
......@@ -101,6 +102,8 @@ struct au_branch {
/* entries under sysfs per mount-point */
struct au_brsysfs br_sysfs[AuBrSysfs_Last];
#endif
struct au_dr_br br_dirren;
};
/* ---------------------------------------------------------------------- */
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017-2019 Junjiro R. Okajima
*/
/*
* special handling in renaming a directory
* in order to support looking-up the before-renamed name on the lower readonly
* branches
*/
#include <linux/byteorder/generic.h>
#include "aufs.h"
/* re-commit later */ __maybe_unused
static void au_dr_hino_del(struct au_dr_br *dr, struct au_dr_hino *ent)
{
int idx;
idx = au_dr_ihash(ent->dr_h_ino);
au_hbl_del(&ent->dr_hnode, dr->dr_h_ino + idx);
}
static int au_dr_hino_test_empty(struct au_dr_br *dr)
{
int ret, i;
struct hlist_bl_head *hbl;
ret = 1;
for (i = 0; ret && i < AuDirren_NHASH; i++) {
hbl = dr->dr_h_ino + i;
hlist_bl_lock(hbl);
ret &= hlist_bl_empty(hbl);
hlist_bl_unlock(hbl);
}
return ret;
}
/* re-commit later */ __maybe_unused
static struct au_dr_hino *au_dr_hino_find(struct au_dr_br *dr, ino_t ino)
{
struct au_dr_hino *found, *ent;
struct hlist_bl_head *hbl;
struct hlist_bl_node *pos;
int idx;
found = NULL;
idx = au_dr_ihash(ino);
hbl = dr->dr_h_ino + idx;
hlist_bl_lock(hbl);
hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode)
if (ent->dr_h_ino == ino) {
found = ent;
break;
}
hlist_bl_unlock(hbl);
return found;
}
int au_dr_hino_test_add(struct au_dr_br *dr, ino_t ino,
struct au_dr_hino *add_ent)
{
int found, idx;
struct hlist_bl_head *hbl;
struct hlist_bl_node *pos;
struct au_dr_hino *ent;
found = 0;
idx = au_dr_ihash(ino);
hbl = dr->dr_h_ino + idx;
#if 0
{
struct hlist_bl_node *tmp;
hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode)
AuDbg("hi%llu\n", (unsigned long long)ent->dr_h_ino);
}
#endif
hlist_bl_lock(hbl);
hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode)
if (ent->dr_h_ino == ino) {
found = 1;
break;
}
if (!found && add_ent)
hlist_bl_add_head(&add_ent->dr_hnode, hbl);
hlist_bl_unlock(hbl);
if (!found && add_ent)
AuDbg("i%llu added\n", (unsigned long long)add_ent->dr_h_ino);
return found;
}
void au_dr_hino_free(struct au_dr_br *dr)
{
int i;
struct hlist_bl_head *hbl;
struct hlist_bl_node *pos, *tmp;
struct au_dr_hino *ent;
/* SiMustWriteLock(sb); */
for (i = 0; i < AuDirren_NHASH; i++) {
hbl = dr->dr_h_ino + i;
/* no spinlock since sbinfo must be write-locked */
hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode)
au_kfree_rcu(ent);
INIT_HLIST_BL_HEAD(hbl);
}
}
/* returns the number of inodes or an error */
static int au_dr_hino_store(struct super_block *sb, struct au_branch *br,
struct file *hinofile)
{
int err, i;
ssize_t ssz;
loff_t pos, oldsize;
__be64 u64;
struct inode *hinoinode;
struct hlist_bl_head *hbl;
struct hlist_bl_node *n1, *n2;
struct au_dr_hino *ent;
SiMustWriteLock(sb);
AuDebugOn(!au_br_writable(br->br_perm));
hinoinode = file_inode(hinofile);
oldsize = i_size_read(hinoinode);
err = 0;
pos = 0;
hbl = br->br_dirren.dr_h_ino;
for (i = 0; !err && i < AuDirren_NHASH; i++, hbl++) {
/* no bit-lock since sbinfo must be write-locked */
hlist_bl_for_each_entry_safe(ent, n1, n2, hbl, dr_hnode) {
AuDbg("hi%llu, %pD2\n",
(unsigned long long)ent->dr_h_ino, hinofile);
u64 = cpu_to_be64(ent->dr_h_ino);
ssz = vfsub_write_k(hinofile, &u64, sizeof(u64), &pos);
if (ssz == sizeof(u64))
continue;
/* write error */
pr_err("ssz %zd, %pD2\n", ssz, hinofile);
err = -ENOSPC;
if (ssz < 0)
err = ssz;
break;
}
}
/* regardless the error */
if (pos < oldsize) {
err = vfsub_trunc(&hinofile->f_path, pos, /*attr*/0, hinofile);
AuTraceErr(err);
}
AuTraceErr(err);
return err;
}
static int au_dr_hino_load(struct au_dr_br *dr, struct file *hinofile)
{
int err, hidx;
ssize_t ssz;
size_t sz, n;
loff_t pos;
uint64_t u64;
struct au_dr_hino *ent;
struct inode *hinoinode;
struct hlist_bl_head *hbl;
err = 0;
pos = 0;
hbl = dr->dr_h_ino;
hinoinode = file_inode(hinofile);
sz = i_size_read(hinoinode);
AuDebugOn(sz % sizeof(u64));
n = sz / sizeof(u64);
while (n--) {
ssz = vfsub_read_k(hinofile, &u64, sizeof(u64), &pos);
if (unlikely(ssz != sizeof(u64))) {
pr_err("ssz %zd, %pD2\n", ssz, hinofile);
err = -EINVAL;
if (ssz < 0)
err = ssz;
goto out_free;
}
ent = kmalloc(sizeof(*ent), GFP_NOFS);
if (!ent) {
err = -ENOMEM;
AuTraceErr(err);
goto out_free;
}
ent->dr_h_ino = be64_to_cpu((__force __be64)u64);
AuDbg("hi%llu, %pD2\n",
(unsigned long long)ent->dr_h_ino, hinofile);
hidx = au_dr_ihash(ent->dr_h_ino);
au_hbl_add(&ent->dr_hnode, hbl + hidx);
}
goto out; /* success */
out_free:
au_dr_hino_free(dr);
out:
AuTraceErr(err);
return err;
}
/*
* @bindex/@br is a switch to distinguish whether suspending hnotify or not.
* @path is a switch to distinguish load and store.
*/
static int au_dr_hino(struct super_block *sb, aufs_bindex_t bindex,
struct au_branch *br, const struct path *path)
{
int err, flags;
unsigned char load, suspend;
struct file *hinofile;
struct au_hinode *hdir;
struct inode *dir, *delegated;
struct path hinopath;
struct qstr hinoname = QSTR_INIT(AUFS_WH_DR_BRHINO,
sizeof(AUFS_WH_DR_BRHINO) - 1);
AuDebugOn(bindex < 0 && !br);
AuDebugOn(bindex >= 0 && br);
err = -EINVAL;
suspend = !br;
if (suspend)
br = au_sbr(sb, bindex);
load = !!path;
if (!load) {
path = &br->br_path;
AuDebugOn(!au_br_writable(br->br_perm));
if (unlikely(!au_br_writable(br->br_perm)))
goto out;
}
hdir = NULL;
if (suspend) {
dir = d_inode(sb->s_root);
hdir = au_hinode(au_ii(dir), bindex);
dir = hdir->hi_inode;
au_hn_inode_lock_nested(hdir, AuLsc_I_CHILD);
} else {
dir = d_inode(path->dentry);
inode_lock_nested(dir, AuLsc_I_CHILD);
}
hinopath.dentry = vfsub_lkup_one(&hinoname, path->dentry);
err = PTR_ERR(hinopath.dentry);
if (IS_ERR(hinopath.dentry))
goto out_unlock;
err = 0;
flags = O_RDONLY;
if (load) {
if (d_is_negative(hinopath.dentry))
goto out_dput; /* success */
} else {
if (au_dr_hino_test_empty(&br->br_dirren)) {
if (d_is_positive(hinopath.dentry)) {
delegated = NULL;
err = vfsub_unlink(dir, &hinopath, &delegated,
/*force*/0);
AuTraceErr(err);
if (unlikely(err))
pr_err("ignored err %d, %pd2\n",
err, hinopath.dentry);
if (unlikely(err == -EWOULDBLOCK))
iput(delegated);
err = 0;
}
goto out_dput;
} else if (!d_is_positive(hinopath.dentry)) {
err = vfsub_create(dir, &hinopath, 0600,
/*want_excl*/false);
AuTraceErr(err);
if (unlikely(err))
goto out_dput;
}
flags = O_WRONLY;
}
hinopath.mnt = path->mnt;
hinofile = vfsub_dentry_open(&hinopath, flags);
if (suspend)
au_hn_inode_unlock(hdir);
else
inode_unlock(dir);
dput(hinopath.dentry);
AuTraceErrPtr(hinofile);
if (IS_ERR(hinofile)) {
err = PTR_ERR(hinofile);
goto out;
}
if (load)
err = au_dr_hino_load(&br->br_dirren, hinofile);
else
err = au_dr_hino_store(sb, br, hinofile);
fput(hinofile);
goto out;
out_dput:
dput(hinopath.dentry);
out_unlock:
if (suspend)
au_hn_inode_unlock(hdir);
else
inode_unlock(dir);
out:
AuTraceErr(err);
return err;
}
int au_dr_br_init(struct super_block *sb, struct au_branch *br,
const struct path *path)
{
int err, i;
struct au_dr_br *dr;
struct hlist_bl_head *hbl;
dr = &br->br_dirren;
hbl = dr->dr_h_ino;
for (i = 0; i < AuDirren_NHASH; i++, hbl++)
INIT_HLIST_BL_HEAD(hbl);
err = 0;
if (au_opt_test(au_mntflags(sb), DIRREN))
err = au_dr_hino(sb, /*bindex*/-1, br, path);
AuTraceErr(err);
return err;
}
int au_dr_br_fin(struct super_block *sb, struct au_branch *br)
{
int err;
err = 0;
if (au_br_writable(br->br_perm))
err = au_dr_hino(sb, /*bindex*/-1, br, /*path*/NULL);
if (!err)
au_dr_hino_free(&br->br_dirren);
return err;
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2017-2019 Junjiro R. Okajima
*/
/*
* renamed dir info
*/
#ifndef __AUFS_DIRREN_H__
#define __AUFS_DIRREN_H__
#ifdef __KERNEL__
#include "hbl.h"
#define AuDirren_NHASH 100
#ifdef CONFIG_AUFS_DIRREN
struct au_dr_hino {
struct hlist_bl_node dr_hnode;
ino_t dr_h_ino;
};
struct au_dr_br {
struct hlist_bl_head dr_h_ino[AuDirren_NHASH];
};
#else
struct au_dr_hino;
/* empty */
struct au_dr_br { };
#endif
/* ---------------------------------------------------------------------- */
struct au_branch;
struct au_hinode;
struct path;
struct super_block;
#ifdef CONFIG_AUFS_DIRREN
int au_dr_hino_test_add(struct au_dr_br *dr, ino_t h_ino,
struct au_dr_hino *add_ent);
void au_dr_hino_free(struct au_dr_br *dr);
int au_dr_br_init(struct super_block *sb, struct au_branch *br,
const struct path *path);
int au_dr_br_fin(struct super_block *sb, struct au_branch *br);
#else
AuStubInt0(au_dr_hino_test_add, struct au_dr_br *dr, ino_t h_ino,
struct au_dr_hino *add_ent);
AuStubVoid(au_dr_hino_free, struct au_dr_br *dr);
AuStubInt0(au_dr_br_init, struct super_block *sb, struct au_branch *br,
const struct path *path);
AuStubInt0(au_dr_br_fin, struct super_block *sb, struct au_branch *br);
#endif
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_AUFS_DIRREN
static inline int au_dr_ihash(ino_t h_ino)
{
return h_ino % AuDirren_NHASH;
}
#else
AuStubInt0(au_dr_ihash, ino_t h_ino);
#endif
#endif /* __KERNEL__ */
#endif /* __AUFS_DIRREN_H__ */
......@@ -32,11 +32,16 @@ struct file;
#define AuOpt_SUM_W (1 << 11) /* unimplemented */
#define AuOpt_VERBOSE (1 << 13) /* print the cause of error */
#define AuOpt_DIO (1 << 14) /* direct io */
#define AuOpt_DIRREN (1 << 15) /* directory rename */
#ifndef CONFIG_AUFS_HNOTIFY
#undef AuOpt_UDBA_HNOTIFY
#define AuOpt_UDBA_HNOTIFY 0
#endif
#ifndef CONFIG_AUFS_DIRREN
#undef AuOpt_DIRREN
#define AuOpt_DIRREN 0
#endif
#define AuOpt_Def (AuOpt_XINO \
| AuOpt_UDBA_REVAL \
......
......@@ -88,6 +88,10 @@ typedef int16_t aufs_bindex_t;
#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME
#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME
/* whiteouted doubly */
#define AUFS_DR_BRHINO_NAME AUFS_WH_PFX "hino"
#define AUFS_WH_DR_BRHINO AUFS_WH_PFX AUFS_DR_BRHINO_NAME
#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */
#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME
......
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