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

aufs: readonly branch 1/2, definition



The branch object is managed by the sbinfo object as an element of its
internal array. The iinfo and dinfo objects contain the branch id, and
it will be used to implement the correct order in branch management
(add/del).

See also the documents in this commit.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent f2ade007
# Copyright (C) 2005-2019 Junjiro R. Okajima
Branch Manipulation
Since aufs supports dynamic branch manipulation, ie. add/remove a branch
and changing its permission/attribute, there are a lot of works to do.
Add a Branch
----------------------------------------------------------------------
o Confirm the adding dir exists outside of aufs, including loopback
mount, and its various attributes.
o Initialize the xino file and whiteout bases if necessary.
See struct.txt.
o Check the owner/group/mode of the directory
When the owner/group/mode of the adding directory differs from the
existing branch, aufs issues a warning because it may impose a
security risk.
For example, when a upper writable branch has a world writable empty
top directory, a malicious user can create any files on the writable
branch directly, like copy-up and modify manually. If something like
/etc/{passwd,shadow} exists on the lower readonly branch but the upper
writable branch, and the writable branch is world-writable, then a
malicious guy may create /etc/passwd on the writable branch directly
and the infected file will be valid in aufs.
I am afraid it can be a security issue, but aufs can do nothing except
producing a warning.
# SPDX-License-Identifier: GPL-2.0
include ${srctree}/${src}/magic.mk
-include ${srctree}/${src}/priv_def.mk
# cf. include/linux/kernel.h
......@@ -9,7 +10,7 @@ ccflags-y += -DDEBUG
ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h
obj-$(CONFIG_AUFS_FS) += aufs.o
aufs-y := module.o sbinfo.o super.o \
aufs-y := module.o sbinfo.o super.o branch.o \
dcsub.o \
cpup.o \
dinfo.o \
......
......@@ -22,14 +22,18 @@
#include "debug.h"
#include "branch.h"
#include "cpup.h"
#include "dcsub.h"
#include "dentry.h"
#include "fstype.h"
#include "inode.h"
#include "lcnt.h"
#include "module.h"
#include "opts.h"
#include "rwsem.h"
#include "super.h"
#include "vfsub.h"
#endif /* __KERNEL__ */
#endif /* __AUFS_H__ */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* branch management
*/
#include "aufs.h"
/*
* free a single branch
*/
static void au_br_do_free(struct au_branch *br)
{
AuLCntZero(au_lcnt_read(&br->br_count, /*do_rev*/0));
au_lcnt_fin(&br->br_count, /*do_sync*/0);
/* recursive lock, s_umount of branch's */
/* synchronize_rcu(); */ /* why? */
lockdep_off();
path_put(&br->br_path);
lockdep_on();
au_lcnt_wait_for_fin(&br->br_count);
/* I don't know why, but percpu_refcount requires this */
/* synchronize_rcu(); */
au_kfree_rcu(br);
}
/*
* frees all branches
*/
void au_br_free(struct au_sbinfo *sbinfo)
{
aufs_bindex_t bmax;
struct au_branch **br;
AuRwMustWriteLock(&sbinfo->si_rwsem);
bmax = sbinfo->si_bbot + 1;
br = sbinfo->si_branch;
while (bmax--)
au_br_do_free(*br++);
}
/*
* find the index of a branch which is specified by @br_id.
*/
int au_br_index(struct super_block *sb, aufs_bindex_t br_id)
{
aufs_bindex_t bindex, bbot;
bbot = au_sbbot(sb);
for (bindex = 0; bindex <= bbot; bindex++)
if (au_sbr_id(sb, bindex) == br_id)
return bindex;
return -1;
}
/* ---------------------------------------------------------------------- */
/*
* add a branch
*/
static int test_overlap(struct super_block *sb, struct dentry *h_adding,
struct dentry *h_root)
{
if (unlikely(h_adding == h_root))
return 1;
if (h_adding->d_sb != h_root->d_sb)
return 0;
return au_test_subdir(h_adding, h_root)
|| au_test_subdir(h_root, h_adding);
}
/*
* returns a newly allocated branch. @new_nbranch is a number of branches
* after adding a branch.
*/
static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch,
int perm)
{
struct au_branch *add_branch;
int err;
err = -ENOMEM;
add_branch = kzalloc(sizeof(*add_branch), GFP_NOFS);
if (unlikely(!add_branch))
goto out;
#if 0 /* re-commit later */
struct dentry *root;
struct inode *inode;
root = sb->s_root;
err = au_sbr_realloc(au_sbi(sb), new_nbranch, /*may_shrink*/0);
if (!err)
err = au_di_realloc(au_di(root), new_nbranch, /*may_shrink*/0);
if (!err) {
inode = d_inode(root);
err = au_hinode_realloc(au_ii(inode), new_nbranch,
/*may_shrink*/0);
}
#endif
if (!err)
return add_branch; /* success */
au_kfree_rcu(add_branch);
out:
return ERR_PTR(err);
}
/*
* returns:
* 0: success, the caller will add it
* plus: success, it is already unified, the caller should ignore it
* minus: error
*/
static int test_add(struct super_block *sb, struct au_opt_add *add)
{
int err;
aufs_bindex_t bbot, bindex;
struct dentry *root;
struct inode *inode;
root = sb->s_root;
bbot = au_sbbot(sb);
if (unlikely(bbot >= 0
/* re-commit later */
/* && au_find_dbindex(root, add->path.dentry) >= 0 */)) {
err = -EINVAL;
pr_err("%s duplicated\n", add->pathname);
goto out;
}
err = -ENOSPC; /* -E2BIG; */
if (unlikely(AUFS_BRANCH_MAX <= add->bindex
|| AUFS_BRANCH_MAX - 1 <= bbot)) {
pr_err("number of branches exceeded %s\n", add->pathname);
goto out;
}
err = -EDOM;
if (unlikely(add->bindex < 0 || bbot + 1 < add->bindex)) {
pr_err("bad index %d\n", add->bindex);
goto out;
}
inode = d_inode(add->path.dentry);
err = -ENOENT;
if (unlikely(!inode->i_nlink)) {
pr_err("no existence %s\n", add->pathname);
goto out;
}
err = -EINVAL;
if (unlikely(inode->i_sb == sb)) {
pr_err("%s must be outside\n", add->pathname);
goto out;
}
if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) {
pr_err("unsupported filesystem, %s (%s)\n",
add->pathname, au_sbtype(inode->i_sb));
goto out;
}
if (unlikely(inode->i_sb->s_stack_depth)) {
pr_err("already stacked, %s (%s)\n",
add->pathname, au_sbtype(inode->i_sb));
goto out;
}
if (bbot < 0)
return 0; /* success */
err = -EINVAL;
for (bindex = 0; bindex <= bbot; bindex++)
if (unlikely(test_overlap(sb, add->path.dentry,
au_h_dptr(root, bindex)))) {
pr_err("%s is overlapped\n", add->pathname);
goto out;
}
err = 0;
out:
return err;
}
/* initialize a new branch */
static int au_br_init(struct au_branch *br, struct super_block *sb,
struct au_opt_add *add)
{
int err;
err = 0;
br->br_perm = add->perm;
br->br_path = add->path; /* set first, path_get() later */
au_lcnt_init(&br->br_count, /*release*/NULL);
br->br_id = au_new_br_id(sb);
AuDebugOn(br->br_id < 0);
path_get(&br->br_path);
goto out; /* success */
memset(&br->br_path, 0, sizeof(br->br_path));
out:
return err;
}
static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex,
struct au_branch *br, aufs_bindex_t bbot,
aufs_bindex_t amount)
{
struct au_branch **brp;
AuRwMustWriteLock(&sbinfo->si_rwsem);
brp = sbinfo->si_branch + bindex;
memmove(brp + 1, brp, sizeof(*brp) * amount);
*brp = br;
sbinfo->si_bbot++;
if (unlikely(bbot < 0))
sbinfo->si_bbot = 0;
}
static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex,
aufs_bindex_t bbot, aufs_bindex_t amount)
{
struct au_hdentry *hdp;
AuRwMustWriteLock(&dinfo->di_rwsem);
hdp = au_hdentry(dinfo, bindex);
memmove(hdp + 1, hdp, sizeof(*hdp) * amount);
au_h_dentry_init(hdp);
dinfo->di_bbot++;
if (unlikely(bbot < 0))
dinfo->di_btop = 0;
}
static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex,
aufs_bindex_t bbot, aufs_bindex_t amount)
{
struct au_hinode *hip;
AuRwMustWriteLock(&iinfo->ii_rwsem);
hip = au_hinode(iinfo, bindex);
memmove(hip + 1, hip, sizeof(*hip) * amount);
au_hinode_init(hip);
iinfo->ii_bbot++;
if (unlikely(bbot < 0))
iinfo->ii_btop = 0;
}
static void au_br_do_add(struct super_block *sb, struct au_branch *br,
aufs_bindex_t bindex)
{
struct dentry *root, *h_dentry;
struct inode *root_inode, *h_inode;
aufs_bindex_t bbot, amount;
root = sb->s_root;
root_inode = d_inode(root);
bbot = au_sbbot(sb);
amount = bbot + 1 - bindex;
h_dentry = au_br_dentry(br);
au_br_do_add_brp(au_sbi(sb), bindex, br, bbot, amount);
au_br_do_add_hdp(au_di(root), bindex, bbot, amount);
au_br_do_add_hip(au_ii(root_inode), bindex, bbot, amount);
au_set_h_dptr(root, bindex, dget(h_dentry));
h_inode = d_inode(h_dentry);
/* re-commit later */
/* au_set_h_iptr(root_inode, bindex, au_igrab(h_inode), /\*flags*\/0); */
}
int au_br_add(struct super_block *sb, struct au_opt_add *add)
{
int err;
aufs_bindex_t bbot, add_bindex;
struct dentry *root, *h_dentry;
struct inode *root_inode;
struct au_branch *add_branch;
root = sb->s_root;
root_inode = d_inode(root);
IMustLock(root_inode);
IiMustWriteLock(root_inode);
err = test_add(sb, add);
if (unlikely(err < 0))
goto out;
if (err) {
err = 0;
goto out; /* success */
}
bbot = au_sbbot(sb);
add_branch = au_br_alloc(sb, bbot + 2, add->perm);
err = PTR_ERR(add_branch);
if (IS_ERR(add_branch))
goto out;
err = au_br_init(add_branch, sb, add);
if (unlikely(err)) {
au_br_do_free(add_branch);
goto out;
}
add_bindex = add->bindex;
au_br_do_add(sb, add_branch, add_bindex);
h_dentry = add->path.dentry;
if (!add_bindex)
sb->s_maxbytes = h_dentry->d_sb->s_maxbytes;
out:
return err;
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* branch filesystems and xino for them
*/
#ifndef __AUFS_BRANCH_H__
#define __AUFS_BRANCH_H__
#ifdef __KERNEL__
#include <linux/mount.h>
#include "lcnt.h"
#include "super.h"
/* ---------------------------------------------------------------------- */
/* protected by superblock rwsem */
struct au_branch {
aufs_bindex_t br_id;
int br_perm;
struct path br_path;
au_lcnt_t br_count; /* in-use for other */
};
/* ---------------------------------------------------------------------- */
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.c */
struct au_sbinfo;
void au_br_free(struct au_sbinfo *sinfo);
int au_br_index(struct super_block *sb, aufs_bindex_t br_id);
struct au_opt_add;
int au_br_add(struct super_block *sb, struct au_opt_add *add);
/* ---------------------------------------------------------------------- */
/* Superblock to branch */
static inline
aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex)
{
return au_sbr(sb, bindex)->br_id;
}
static inline
struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
{
return au_br_sb(au_sbr(sb, bindex));
}
static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex)
{
return au_sbr(sb, bindex)->br_perm;
}
#endif /* __KERNEL__ */
#endif /* __AUFS_BRANCH_H__ */
......@@ -141,3 +141,17 @@ int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages,
return au_dcsub_pages_rev(dpages, dentry, do_include,
au_dcsub_dpages_aufs, dentry->d_sb);
}
int au_test_subdir(struct dentry *d1, struct dentry *d2)
{
struct path path[2] = {
{
.dentry = d1
},
{
.dentry = d2
}
};
return path_is_under(path + 0, path + 1);
}
......@@ -34,6 +34,7 @@ int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,
int do_include, au_dpages_test test, void *arg);
int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages,
struct dentry *dentry, int do_include);
int au_test_subdir(struct dentry *d1, struct dentry *d2);
/* ---------------------------------------------------------------------- */
......
......@@ -51,6 +51,11 @@ AuStubInt0(au_debug_test, void)
pr_debug("DEBUG: " fmt, ##__VA_ARGS__); \
} while (0)
#define AuLabel(l) AuDbg(#l "\n")
#define AuWarn1(fmt, ...) do { \
static unsigned char _c; \
if (!_c++) \
pr_warn(fmt, ##__VA_ARGS__); \
} while (0)
#define AuTraceErr(e) do { \
if (unlikely((e) < 0)) \
......
......@@ -25,5 +25,92 @@ static inline const char *au_sbtype(struct super_block *sb)
return sb->s_type->name;
}
static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused)
{
#if IS_ENABLED(CONFIG_ECRYPT_FS)
return !strcmp(au_sbtype(sb), "ecryptfs");
#else
return 0;
#endif
}
static inline int au_test_ramfs(struct super_block *sb)
{
return sb->s_magic == RAMFS_MAGIC;
}
static inline int au_test_procfs(struct super_block *sb __maybe_unused)
{
#ifdef CONFIG_PROC_FS
return sb->s_magic == PROC_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_sysfs(struct super_block *sb __maybe_unused)
{
#ifdef CONFIG_SYSFS
return sb->s_magic == SYSFS_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_configfs(struct super_block *sb __maybe_unused)
{
#if IS_ENABLED(CONFIG_CONFIGFS_FS)
return sb->s_magic == CONFIGFS_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_securityfs(struct super_block *sb __maybe_unused)
{
#ifdef CONFIG_SECURITYFS
return sb->s_magic == SECURITYFS_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_xenfs(struct super_block *sb __maybe_unused)
{
#if IS_ENABLED(CONFIG_XENFS)
return sb->s_magic == XENFS_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_debugfs(struct super_block *sb __maybe_unused)
{
#ifdef CONFIG_DEBUG_FS
return sb->s_magic == DEBUGFS_MAGIC;
#else
return 0;
#endif
}
/* ---------------------------------------------------------------------- */
/*
* they can't be an aufs branch.
*/
static inline int au_test_fs_unsuppoted(struct super_block *sb)
{
return
au_test_ramfs(sb) ||
au_test_procfs(sb)
|| au_test_sysfs(sb)
|| au_test_configfs(sb)
|| au_test_debugfs(sb)
|| au_test_securityfs(sb)
|| au_test_xenfs(sb)
|| au_test_ecryptfs(sb)
/* || !strcmp(au_sbtype(sb), "unionfs") */
|| au_test_aufs(sb); /* will be supported in next version */
}
#endif /* __KERNEL__ */
#endif /* __AUFS_FSTYPE_H__ */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2018-2019 Junjiro R. Okajima
*/
/*
* simple long counter wrapper
*/
#ifndef __AUFS_LCNT_H__
#define __AUFS_LCNT_H__
#ifdef __KERNEL__
#include "debug.h"
#define AuLCntATOMIC 1
#define AuLCntPCPUCNT 2
/*
* why does percpu_refcount require extra synchronize_rcu()s in
* au_br_do_free()
*/
#define AuLCntPCPUREF 3
/* #define AuLCntChosen AuLCntATOMIC */
#define AuLCntChosen AuLCntPCPUCNT
/* #define AuLCntChosen AuLCntPCPUREF */
#if AuLCntChosen == AuLCntATOMIC
#include <linux/atomic.h>
typedef atomic_long_t au_lcnt_t;
static inline int au_lcnt_init(au_lcnt_t *cnt, void *release __maybe_unused)
{
atomic_long_set(cnt, 0);
return 0;
}
static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused)
{
/* empty */
}
static inline void au_lcnt_fin(au_lcnt_t *cnt __maybe_unused,
int do_sync __maybe_unused)
{
/* empty */
}
static inline void au_lcnt_inc(au_lcnt_t *cnt)
{
atomic_long_inc(cnt);
}
static inline void au_lcnt_dec(au_lcnt_t *cnt)
{
atomic_long_dec(cnt);
}
static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev __maybe_unused)
{
return atomic_long_read(cnt);
}
#endif
#if AuLCntChosen == AuLCntPCPUCNT
#include <linux/percpu_counter.h>
typedef struct percpu_counter au_lcnt_t;
static inline int au_lcnt_init(au_lcnt_t *cnt, void *release __maybe_unused)
{
return percpu_counter_init(cnt, 0, GFP_NOFS);
}
static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused)
{
/* empty */
}
static inline void au_lcnt_fin(au_lcnt_t *cnt, int do_sync __maybe_unused)
{
percpu_counter_destroy(cnt);
}
static inline void au_lcnt_inc(au_lcnt_t *cnt)
{
percpu_counter_inc(cnt);
}
static inline void au_lcnt_dec(au_lcnt_t *cnt)
{
percpu_counter_dec(cnt);
}
static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev __maybe_unused)
{
s64 n;
n = percpu_counter_sum(cnt);
BUG_ON(n < 0);
if (LONG_MAX != LLONG_MAX
&& n > LONG_MAX)
AuWarn1("%s\n", "wrap-around");
return n;
}
#endif
#if AuLCntChosen == AuLCntPCPUREF
#include <linux/percpu-refcount.h>
typedef struct percpu_ref au_lcnt_t;
static inline int au_lcnt_init(au_lcnt_t *cnt, percpu_ref_func_t *release)
{
if (!release)
release = percpu_ref_exit;
return percpu_ref_init(cnt, release, /*percpu mode*/0, GFP_NOFS);
}
static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused)
{
synchronize_rcu();
}
static inline void au_lcnt_fin(au_lcnt_t *cnt, int do_sync)
{
percpu_ref_kill(cnt);
if (do_sync)
au_lcnt_wait_for_fin(cnt);
}
static inline void au_lcnt_inc(au_lcnt_t *cnt)
{
percpu_ref_get(cnt);
}
static inline void au_lcnt_dec(au_lcnt_t *cnt)
{
percpu_ref_put(cnt);
}
/*
* avoid calling this func as possible.
*/
static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev)
{
long l;
percpu_ref_switch_to_atomic_sync(cnt);
l = atomic_long_read(&cnt->count);
if (do_rev)
percpu_ref_switch_to_percpu(cnt);
/* percpu_ref is initialized by 1 instead of 0 */
return l - 1;
}
#endif
#ifdef CONFIG_AUFS_DEBUG
#define AuLCntZero(val) do { \
long l = val; \
if (l) \
AuDbg("%s = %ld\n", #val, l); \
} while (0)
#else
#define AuLCntZero(val) do {} while (0)
#endif
#endif /* __KERNEL__ */
#endif /* __AUFS_LCNT_H__ */
# SPDX-License-Identifier: GPL-2.0
# defined in ${srctree}/fs/configfs/mount.c
# tristate
ifdef CONFIG_CONFIGFS_FS
ccflags-y += -DCONFIGFS_MAGIC=0x62656570
endif
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* mount options/flags
*/
#ifndef __AUFS_OPTS_H__
#define __AUFS_OPTS_H__
#ifdef __KERNEL__
#include <linux/path.h>
struct au_opt_add {
aufs_bindex_t bindex;
char *pathname;
int perm;
struct path path;
};
struct au_opt {
int type;
union {
struct au_opt_add add;
/* add more later */
};
};
struct au_opts {
struct au_opt *opt;
int max_opt;
};
#endif /* __KERNEL__ */
#endif /* __AUFS_OPTS_H__ */
......@@ -42,6 +42,7 @@ int au_si_alloc(struct super_block *sb)
au_rw_init_wlock(&sbinfo->si_rwsem);
sbinfo->si_bbot = -1;
sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2;
/* leave other members for sysaufs and si_mnt. */
sb->s_fs_info = sbinfo;
......@@ -70,3 +71,22 @@ unsigned int au_sigen_inc(struct super_block *sb)
inode_inc_iversion(inode);
return gen;
}
aufs_bindex_t au_new_br_id(struct super_block *sb)
{
aufs_bindex_t br_id;
int i;
struct au_sbinfo *sbinfo;
SiMustWriteLock(sb);
sbinfo = au_sbi(sb);
for (i = 0; i <= AUFS_BRANCH_MAX; i++) {
br_id = ++sbinfo->si_last_br_id;
AuDebugOn(br_id < 0);
if (br_id && au_br_index(sb, br_id) < 0)
return br_id;
}
return -1;
}
......@@ -28,6 +28,10 @@ struct au_sbinfo {
unsigned int si_generation;
aufs_bindex_t si_bbot;
/* dirty trick to keep br_id plus */
unsigned int si_last_br_id :
sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1;
struct au_branch **si_branch;
/*
......@@ -61,6 +65,7 @@ void au_si_free(struct kobject *kobj);
int au_si_alloc(struct super_block *sb);
unsigned int au_sigen_inc(struct super_block *sb);
aufs_bindex_t au_new_br_id(struct super_block *sb);
/* ---------------------------------------------------------------------- */
......@@ -105,5 +110,12 @@ static inline unsigned int au_sigen(struct super_block *sb)
return au_sbi(sb)->si_generation;
}
static inline struct au_branch *au_sbr(struct super_block *sb,
aufs_bindex_t bindex)
{
SiMustAnyLock(sb);
return au_sbi(sb)->si_branch[0 + bindex];
}
#endif /* __KERNEL__ */
#endif /* __AUFS_SUPER_H__ */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* sub-routines for VFS
*/
#ifndef __AUFS_VFSUB_H__
#define __AUFS_VFSUB_H__
#ifdef __KERNEL__
#include <linux/fs.h>
#include "debug.h"
/* to debug easier, do not make them inlined functions */
#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx))
#define IMustLock(i) AuDebugOn(!inode_is_locked(i))
#endif /* __KERNEL__ */
#endif /* __AUFS_VFSUB_H__ */
......@@ -54,4 +54,17 @@ typedef int16_t aufs_bindex_t;
#endif
#endif /* __KERNEL__ */
/* branch permissions and attributes */
#define AUFS_BRPERM_RO "ro"
#define AuBrPerm_RO (1 << 1) /* readonly */
#define AuBrPerm_Mask AuBrPerm_RO /* re-commit later */
/* the longest combination */
#define AuBrPermStrSz sizeof(AUFS_BRPERM_RO)
typedef struct {
char a[AuBrPermStrSz];
} au_br_perm_str_t;
#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