Add aufs support in allwinner kernel

This commit is contained in:
Nicolas Aguirre 2012-10-17 19:21:01 +02:00
parent 046db95a55
commit a535b80229
75 changed files with 29239 additions and 12 deletions

View File

@ -0,0 +1,37 @@
aufs3.0 kbuild patch
diff --git a/fs/Kconfig b/fs/Kconfig
index 19891aa..b660b64 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -208,5 +208,6 @@ source "fs/pstore/Kconfig"
source "fs/sysv/Kconfig"
source "fs/ufs/Kconfig"
source "fs/exofs/Kconfig"
+source "fs/aufs/Kconfig"
endif # MISC_FILESYSTEMS
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 01f6362..8b3b9f1 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -65,6 +65,7 @@ header-y += atmppp.h
header-y += atmsap.h
header-y += atmsvc.h
header-y += audit.h
+header-y += aufs_type.h
header-y += auto_fs.h
header-y += auto_fs4.h
header-y += auxvec.h
diff --git a/fs/Makefile b/fs/Makefile
index 2999b4d..60af3ce 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -124,6 +124,7 @@ obj-$(CONFIG_GFS2_FS) += gfs2/
obj-$(CONFIG_EXOFS_FS) += exofs/
obj-$(CONFIG_CEPH_FS) += ceph/
obj-$(CONFIG_PSTORE) += pstore/
+obj-$(CONFIG_AUFS_FS) += aufs/
# Patched by YAFFS
obj-$(CONFIG_YAFFS_FS) += yaffs2/

View File

@ -0,0 +1,70 @@
aufs3.0 base patch
diff --git a/fs/namei.c b/fs/namei.c
index 14ab8d3..eb4aef1 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1697,7 +1697,7 @@ static struct dentry *__lookup_hash(struct qstr *name,
* needs parent already locked. Doesn't follow mounts.
* SMP-safe.
*/
-static struct dentry *lookup_hash(struct nameidata *nd)
+struct dentry *lookup_hash(struct nameidata *nd)
{
return __lookup_hash(&nd->last, nd->path.dentry, nd);
}
diff --git a/fs/splice.c b/fs/splice.c
index aa866d3..19afec6 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1085,8 +1085,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
/*
* Attempt to initiate a splice from pipe to file.
*/
-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
- loff_t *ppos, size_t len, unsigned int flags)
+long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
+ loff_t *ppos, size_t len, unsigned int flags)
{
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *,
loff_t *, size_t, unsigned int);
@@ -1113,9 +1113,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
/*
* Attempt to initiate a splice from a file to a pipe.
*/
-static long do_splice_to(struct file *in, loff_t *ppos,
- struct pipe_inode_info *pipe, size_t len,
- unsigned int flags)
+long do_splice_to(struct file *in, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t len,
+ unsigned int flags)
{
ssize_t (*splice_read)(struct file *, loff_t *,
struct pipe_inode_info *, size_t, unsigned int);
diff --git a/include/linux/namei.h b/include/linux/namei.h
index eba45ea..21ed6c9 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -82,6 +82,7 @@ extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
int (*open)(struct inode *, struct file *));
+extern struct dentry *lookup_hash(struct nameidata *nd);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
extern int follow_down_one(struct path *);
diff --git a/include/linux/splice.h b/include/linux/splice.h
index 997c3b4..be9a153 100644
--- a/include/linux/splice.h
+++ b/include/linux/splice.h
@@ -89,4 +89,10 @@ extern int splice_grow_spd(struct pipe_inode_info *, struct splice_pipe_desc *);
extern void splice_shrink_spd(struct pipe_inode_info *,
struct splice_pipe_desc *);
+extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
+ loff_t *ppos, size_t len, unsigned int flags);
+extern long do_splice_to(struct file *in, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t len,
+ unsigned int flags);
+
#endif

View File

@ -0,0 +1,205 @@
aufs3.0 proc_map patch
diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c
index b1822dd..8b29ab7 100644
--- a/fs/proc/nommu.c
+++ b/fs/proc/nommu.c
@@ -46,6 +46,10 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region)
if (file) {
struct inode *inode = region->vm_file->f_path.dentry->d_inode;
+ if (region->vm_prfile) {
+ file = region->vm_prfile;
+ inode = file->f_path.dentry->d_inode;
+ }
dev = inode->i_sb->s_dev;
ino = inode->i_ino;
}
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 25b6a88..47ed5d8 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -220,6 +220,10 @@ static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma)
if (file) {
struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
+ if (vma->vm_prfile) {
+ file = vma->vm_prfile;
+ inode = file->f_path.dentry->d_inode;
+ }
dev = inode->i_sb->s_dev;
ino = inode->i_ino;
pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
@@ -1001,6 +1005,8 @@ static int show_numa_map(struct seq_file *m, void *v)
if (file) {
seq_printf(m, " file=");
+ if (vma->vm_prfile)
+ file = vma->vm_prfile;
seq_path(m, &file->f_path, "\n\t= ");
} else if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk) {
seq_printf(m, " heap");
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
index 980de54..4ee031f 100644
--- a/fs/proc/task_nommu.c
+++ b/fs/proc/task_nommu.c
@@ -148,6 +148,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma)
if (file) {
struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
+ if (vma->vm_prfile) {
+ file = vma->vm_prfile;
+ inode = file->f_path.dentry->d_inode;
+ }
dev = inode->i_sb->s_dev;
ino = inode->i_ino;
pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT;
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 027935c..0ade381 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -117,6 +117,7 @@ struct vm_region {
unsigned long vm_top; /* region allocated to here */
unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */
struct file *vm_file; /* the backing file or NULL */
+ struct file *vm_prfile; /* the virtual backing file or NULL */
int vm_usage; /* region usage count (access under nommu_region_sem) */
bool vm_icache_flushed : 1; /* true if the icache has been flushed for
@@ -176,6 +177,7 @@ struct vm_area_struct {
unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE
units, *not* PAGE_CACHE_SIZE */
struct file * vm_file; /* File we map to (can be NULL). */
+ struct file *vm_prfile; /* shadow of vm_file */
void * vm_private_data; /* was vm_pte (shared mem) */
#ifndef CONFIG_MMU
diff --git a/kernel/fork.c b/kernel/fork.c
index 0276c30..39d8176 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -380,6 +380,8 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
struct address_space *mapping = file->f_mapping;
get_file(file);
+ if (tmp->vm_prfile)
+ get_file(tmp->vm_prfile);
if (tmp->vm_flags & VM_DENYWRITE)
atomic_dec(&inode->i_writecount);
mutex_lock(&mapping->i_mmap_mutex);
diff --git a/mm/memory.c b/mm/memory.c
index 9b8a01d..8902d06 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2577,6 +2577,8 @@ reuse:
/* file_update_time outside page_lock */
if (vma->vm_file)
file_update_time(vma->vm_file);
+ if (vma->vm_prfile)
+ file_update_time(vma->vm_prfile);
return ret;
}
@@ -3257,6 +3259,8 @@ out:
/* file_update_time outside page_lock */
if (vma->vm_file)
file_update_time(vma->vm_file);
+ if (vma->vm_prfile)
+ file_update_time(vma->vm_prfile);
} else {
unlock_page(vmf.page);
if (anon)
diff --git a/mm/mmap.c b/mm/mmap.c
index d49736f..0290c8e 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -240,6 +240,8 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma)
vma->vm_ops->close(vma);
if (vma->vm_file) {
fput(vma->vm_file);
+ if (vma->vm_prfile)
+ fput(vma->vm_prfile);
if (vma->vm_flags & VM_EXECUTABLE)
removed_exe_file_vma(vma->vm_mm);
}
@@ -627,6 +629,8 @@ again: remove_next = 1 + (end > next->vm_end);
if (remove_next) {
if (file) {
fput(file);
+ if (vma->vm_prfile)
+ fput(vma->vm_prfile);
if (next->vm_flags & VM_EXECUTABLE)
removed_exe_file_vma(mm);
}
@@ -1973,6 +1977,8 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
if (new->vm_file) {
get_file(new->vm_file);
+ if (new->vm_prfile)
+ get_file(new->vm_prfile);
if (vma->vm_flags & VM_EXECUTABLE)
added_exe_file_vma(mm);
}
@@ -1997,6 +2003,8 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
if (vma->vm_flags & VM_EXECUTABLE)
removed_exe_file_vma(mm);
fput(new->vm_file);
+ if (new->vm_prfile)
+ fput(new->vm_prfile);
}
unlink_anon_vmas(new);
out_free_mpol:
@@ -2364,6 +2372,8 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
new_vma->vm_pgoff = pgoff;
if (new_vma->vm_file) {
get_file(new_vma->vm_file);
+ if (new_vma->vm_prfile)
+ get_file(new_vma->vm_prfile);
if (vma->vm_flags & VM_EXECUTABLE)
added_exe_file_vma(mm);
}
diff --git a/mm/nommu.c b/mm/nommu.c
index 9edc897..72631ebe 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -634,6 +634,8 @@ static void __put_nommu_region(struct vm_region *region)
if (region->vm_file)
fput(region->vm_file);
+ if (region->vm_prfile)
+ fput(region->vm_prfile);
/* IO memory and memory shared directly out of the pagecache
* from ramfs/tmpfs mustn't be released here */
@@ -790,6 +792,8 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma)
vma->vm_ops->close(vma);
if (vma->vm_file) {
fput(vma->vm_file);
+ if (vma->vm_prfile)
+ fput(vma->vm_prfile);
if (vma->vm_flags & VM_EXECUTABLE)
removed_exe_file_vma(mm);
}
@@ -1363,6 +1367,8 @@ unsigned long do_mmap_pgoff(struct file *file,
}
}
fput(region->vm_file);
+ if (region->vm_prfile)
+ fput(region->vm_prfile);
kmem_cache_free(vm_region_jar, region);
region = pregion;
result = start;
@@ -1439,9 +1445,13 @@ error_just_free:
error:
if (region->vm_file)
fput(region->vm_file);
+ if (region->vm_prfile)
+ fput(region->vm_prfile);
kmem_cache_free(vm_region_jar, region);
if (vma->vm_file)
fput(vma->vm_file);
+ if (vma->vm_prfile)
+ fput(vma->vm_prfile);
if (vma->vm_flags & VM_EXECUTABLE)
removed_exe_file_vma(vma->vm_mm);
kmem_cache_free(vm_area_cachep, vma);

View File

@ -0,0 +1,257 @@
aufs3.0 standalone patch
diff --git a/fs/file_table.c b/fs/file_table.c
index 01e4c1e..0e800e2 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -443,6 +443,8 @@ void file_sb_list_del(struct file *file)
}
}
+EXPORT_SYMBOL(file_sb_list_del);
+
#ifdef CONFIG_SMP
/*
diff --git a/fs/inode.c b/fs/inode.c
index 43566d1..4291eae 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -69,6 +69,7 @@ static DEFINE_SPINLOCK(inode_lru_lock);
__cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_sb_list_lock);
__cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_wb_list_lock);
+EXPORT_SYMBOL(inode_sb_list_lock);
/*
* iprune_sem provides exclusion between the icache shrinking and the
diff --git a/fs/namei.c b/fs/namei.c
index eb4aef1..66d04c6 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -365,6 +365,7 @@ int deny_write_access(struct file * file)
return 0;
}
+EXPORT_SYMBOL(deny_write_access);
/**
* path_get - get a reference to a path
@@ -1701,6 +1702,7 @@ struct dentry *lookup_hash(struct nameidata *nd)
{
return __lookup_hash(&nd->last, nd->path.dentry, nd);
}
+EXPORT_SYMBOL(lookup_hash);
/**
* lookup_one_len - filesystem helper to lookup single pathname component
diff --git a/fs/namespace.c b/fs/namespace.c
index fe59bd1..7d3843f 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1508,6 +1508,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
}
return 0;
}
+EXPORT_SYMBOL(iterate_mounts);
static void cleanup_group_ids(struct vfsmount *mnt, struct vfsmount *end)
{
diff --git a/fs/notify/group.c b/fs/notify/group.c
index d309f38..f0e9568 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -22,6 +22,7 @@
#include <linux/srcu.h>
#include <linux/rculist.h>
#include <linux/wait.h>
+#include <linux/module.h>
#include <linux/fsnotify_backend.h>
#include "fsnotify.h"
@@ -70,6 +71,7 @@ void fsnotify_put_group(struct fsnotify_group *group)
if (atomic_dec_and_test(&group->refcnt))
fsnotify_destroy_group(group);
}
+EXPORT_SYMBOL(fsnotify_put_group);
/*
* Create a new fsnotify_group and hold a reference for the group returned.
@@ -102,3 +104,4 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops)
return group;
}
+EXPORT_SYMBOL(fsnotify_alloc_group);
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index 252ab1f..2199b9b 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -112,6 +112,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
if (atomic_dec_and_test(&mark->refcnt))
mark->free_mark(mark);
}
+EXPORT_SYMBOL(fsnotify_put_mark);
/*
* Any time a mark is getting freed we end up here.
@@ -189,6 +190,7 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)
if (unlikely(atomic_dec_and_test(&group->num_marks)))
fsnotify_final_destroy_group(group);
}
+EXPORT_SYMBOL(fsnotify_destroy_mark);
void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask)
{
@@ -276,6 +278,7 @@ err:
return ret;
}
+EXPORT_SYMBOL(fsnotify_add_mark);
/*
* clear any marks in a group in which mark->flags & flags is true
@@ -331,6 +334,7 @@ void fsnotify_init_mark(struct fsnotify_mark *mark,
atomic_set(&mark->refcnt, 1);
mark->free_mark = free_mark;
}
+EXPORT_SYMBOL(fsnotify_init_mark);
static int fsnotify_mark_destroy(void *ignored)
{
diff --git a/fs/open.c b/fs/open.c
index b52cf01..c1b341c 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -60,6 +60,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
mutex_unlock(&dentry->d_inode->i_mutex);
return ret;
}
+EXPORT_SYMBOL(do_truncate);
static long do_sys_truncate(const char __user *pathname, loff_t length)
{
diff --git a/fs/splice.c b/fs/splice.c
index 19afec6..11f07f86 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1109,6 +1109,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
return splice_write(pipe, out, ppos, len, flags);
}
+EXPORT_SYMBOL(do_splice_from);
/*
* Attempt to initiate a splice from a file to a pipe.
@@ -1135,6 +1136,7 @@ long do_splice_to(struct file *in, loff_t *ppos,
return splice_read(in, ppos, pipe, len, flags);
}
+EXPORT_SYMBOL(do_splice_to);
/**
* splice_direct_to_actor - splices data directly between two non-pipes
diff --git a/security/commoncap.c b/security/commoncap.c
index a93b3b7..024282c 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -971,3 +971,4 @@ int cap_file_mmap(struct file *file, unsigned long reqprot,
}
return ret;
}
+EXPORT_SYMBOL(cap_file_mmap);
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 1be6826..215278c 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -508,6 +508,7 @@ found:
return -EPERM;
}
+EXPORT_SYMBOL(__devcgroup_inode_permission);
int devcgroup_inode_mknod(int mode, dev_t dev)
{
diff --git a/security/security.c b/security/security.c
index 4ba6d4c..9f64bb8 100644
--- a/security/security.c
+++ b/security/security.c
@@ -373,6 +373,7 @@ int security_path_rmdir(struct path *dir, struct dentry *dentry)
return 0;
return security_ops->path_rmdir(dir, dentry);
}
+EXPORT_SYMBOL(security_path_rmdir);
int security_path_unlink(struct path *dir, struct dentry *dentry)
{
@@ -389,6 +390,7 @@ int security_path_symlink(struct path *dir, struct dentry *dentry,
return 0;
return security_ops->path_symlink(dir, dentry, old_name);
}
+EXPORT_SYMBOL(security_path_symlink);
int security_path_link(struct dentry *old_dentry, struct path *new_dir,
struct dentry *new_dentry)
@@ -397,6 +399,7 @@ int security_path_link(struct dentry *old_dentry, struct path *new_dir,
return 0;
return security_ops->path_link(old_dentry, new_dir, new_dentry);
}
+EXPORT_SYMBOL(security_path_link);
int security_path_rename(struct path *old_dir, struct dentry *old_dentry,
struct path *new_dir, struct dentry *new_dentry)
@@ -415,6 +418,7 @@ int security_path_truncate(struct path *path)
return 0;
return security_ops->path_truncate(path);
}
+EXPORT_SYMBOL(security_path_truncate);
int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
mode_t mode)
@@ -423,6 +427,7 @@ int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
return 0;
return security_ops->path_chmod(dentry, mnt, mode);
}
+EXPORT_SYMBOL(security_path_chmod);
int security_path_chown(struct path *path, uid_t uid, gid_t gid)
{
@@ -430,6 +435,7 @@ int security_path_chown(struct path *path, uid_t uid, gid_t gid)
return 0;
return security_ops->path_chown(path, uid, gid);
}
+EXPORT_SYMBOL(security_path_chown);
int security_path_chroot(struct path *path)
{
@@ -506,6 +512,7 @@ int security_inode_readlink(struct dentry *dentry)
return 0;
return security_ops->inode_readlink(dentry);
}
+EXPORT_SYMBOL(security_inode_readlink);
int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd)
{
@@ -520,6 +527,7 @@ int security_inode_permission(struct inode *inode, int mask)
return 0;
return security_ops->inode_permission(inode, mask, 0);
}
+EXPORT_SYMBOL(security_inode_permission);
int security_inode_exec_permission(struct inode *inode, unsigned int flags)
{
@@ -626,6 +634,7 @@ int security_file_permission(struct file *file, int mask)
return fsnotify_perm(file, mask);
}
+EXPORT_SYMBOL(security_file_permission);
int security_file_alloc(struct file *file)
{
@@ -653,6 +662,7 @@ int security_file_mmap(struct file *file, unsigned long reqprot,
return ret;
return ima_file_mmap(file, prot);
}
+EXPORT_SYMBOL(security_file_mmap);
int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot)

View File

@ -0,0 +1,233 @@
/*
* Copyright (C) 2005-2012 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__, current->comm, current->pid
#else
#include <stdint.h>
#include <sys/types.h>
#endif /* __KERNEL__ */
#include <linux/limits.h>
#define AUFS_VERSION "3.0-20120827"
/* 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 */
#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_TRUNC_INIT 64 /* blocks */
#define AUFS_XINO_TRUNC_STEP 4 /* blocks */
#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 100 /* number of plinks */
/* 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"
/* ---------------------------------------------------------------------- */
/* ioctl */
enum {
/* readdir in userspace */
AuCtl_RDU,
AuCtl_RDU_INO,
/* pathconf wrapper */
AuCtl_WBR_FD,
/* busy inode */
AuCtl_IBUSY
};
/* borrowed from linux/include/linux/kernel.h */
#ifndef ALIGN
#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1)
#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask))
#endif
/* borrowed from linux/include/linux/compiler-gcc3.h */
#ifndef __aligned
#define __aligned(x) __attribute__((aligned(x)))
#endif
#ifdef __KERNEL__
#ifndef __packed
#define __packed __attribute__((packed))
#endif
#endif
struct au_rdu_cookie {
uint64_t h_pos;
int16_t bindex;
uint8_t flags;
uint8_t pad;
uint32_t generation;
} __aligned(8);
struct au_rdu_ent {
uint64_t ino;
int16_t bindex;
uint8_t type;
uint8_t nlen;
uint8_t wh;
char name[0];
} __aligned(8);
static inline int au_rdu_len(int nlen)
{
/* include the terminating NULL */
return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1,
sizeof(uint64_t));
}
union au_rdu_ent_ul {
struct au_rdu_ent __user *e;
uint64_t ul;
};
enum {
AufsCtlRduV_SZ,
AufsCtlRduV_End
};
struct aufs_rdu {
/* input */
union {
uint64_t sz; /* AuCtl_RDU */
uint64_t nent; /* AuCtl_RDU_INO */
};
union au_rdu_ent_ul ent;
uint16_t verify[AufsCtlRduV_End];
/* input/output */
uint32_t blk;
/* output */
union au_rdu_ent_ul tail;
/* number of entries which were added in a single call */
uint64_t rent;
uint8_t full;
uint8_t shwh;
struct au_rdu_cookie cookie;
} __aligned(8);
/* ---------------------------------------------------------------------- */
struct aufs_wbr_fd {
uint32_t oflags;
int16_t brid;
} __aligned(8);
/* ---------------------------------------------------------------------- */
struct aufs_ibusy {
uint64_t ino, h_ino;
int16_t bindex;
} __aligned(8);
/* ---------------------------------------------------------------------- */
#define AuCtlType 'A'
#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu)
#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu)
#define AUFS_CTL_WBR_FD _IOW(AuCtlType, AuCtl_WBR_FD, \
struct aufs_wbr_fd)
#define AUFS_CTL_IBUSY _IOWR(AuCtlType, AuCtl_IBUSY, struct aufs_ibusy)
#endif /* __AUFS_TYPE_H__ */

View File

@ -22,6 +22,7 @@ CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_VECTORS_BASE=0xffff0000
# CONFIG_ARM_PATCH_PHYS_VIRT is not set
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
CONFIG_HAVE_IRQ_WORK=y
CONFIG_IRQ_WORK=y
@ -29,7 +30,7 @@ CONFIG_IRQ_WORK=y
#
# General setup
#
# CONFIG_EXPERIMENTAL is not set
CONFIG_EXPERIMENTAL=y
CONFIG_BROKEN_ON_SMP=y
CONFIG_INIT_ENV_ARG_LIMIT=32
CONFIG_CROSS_COMPILE=""
@ -45,6 +46,7 @@ CONFIG_DEFAULT_HOSTNAME="(none)"
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
# CONFIG_POSIX_MQUEUE is not set
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_BSD_PROCESS_ACCT_V3=y
# CONFIG_FHANDLE is not set
@ -85,11 +87,13 @@ CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
# CONFIG_CGROUP_MEM_RES_CTLR is not set
# CONFIG_CGROUP_PERF is not set
# CONFIG_CGROUP_SCHED is not set
CONFIG_BLK_CGROUP=y
# CONFIG_DEBUG_BLK_CGROUP is not set
CONFIG_NAMESPACES=y
CONFIG_UTS_NS=y
CONFIG_IPC_NS=y
CONFIG_USER_NS=y
CONFIG_PID_NS=y
CONFIG_NET_NS=y
# CONFIG_SCHED_AUTOGROUP is not set
@ -160,12 +164,14 @@ CONFIG_BASE_SMALL=0
CONFIG_MODULES=y
CONFIG_MODULE_FORCE_LOAD=y
CONFIG_MODULE_UNLOAD=y
# CONFIG_MODULE_FORCE_UNLOAD is not set
CONFIG_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y
CONFIG_BLOCK=y
CONFIG_LBDAF=y
CONFIG_BLK_DEV_BSG=y
CONFIG_BLK_DEV_INTEGRITY=y
# CONFIG_BLK_DEV_THROTTLING is not set
#
# IO Schedulers
@ -311,7 +317,7 @@ CONFIG_ARM_L1_CACHE_SHIFT_6=y
CONFIG_ARM_L1_CACHE_SHIFT=6
CONFIG_ARM_DMA_MEM_BUFFERABLE=y
CONFIG_CPU_HAS_PMU=y
# CONFIG_ARM_ERRATA_430973 is not set
CONFIG_ARM_ERRATA_430973=y
# CONFIG_ARM_ERRATA_458693 is not set
# CONFIG_ARM_ERRATA_460075 is not set
# CONFIG_ARM_ERRATA_743622 is not set
@ -341,13 +347,17 @@ CONFIG_PAGE_OFFSET=0xC0000000
# CONFIG_PREEMPT_VOLUNTARY is not set
CONFIG_PREEMPT=y
CONFIG_HZ=100
# CONFIG_THUMB2_KERNEL is not set
CONFIG_AEABI=y
# CONFIG_OABI_COMPAT is not set
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
CONFIG_HAVE_ARCH_PFN_VALID=y
CONFIG_HIGHMEM=y
# CONFIG_HIGHPTE is not set
CONFIG_HW_PERF_EVENTS=y
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
CONFIG_HAVE_MEMBLOCK=y
@ -365,7 +375,9 @@ CONFIG_NEED_PER_CPU_KM=y
# CONFIG_CLEANCACHE is not set
CONFIG_FORCE_MAX_ZONEORDER=11
CONFIG_ALIGNMENT_TRAP=y
# CONFIG_UACCESS_WITH_MEMCPY is not set
# CONFIG_SECCOMP is not set
# CONFIG_CC_STACKPROTECTOR is not set
# CONFIG_DEPRECATED_PARAM_STRUCT is not set
# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set
@ -380,6 +392,8 @@ CONFIG_CMDLINE_FROM_BOOTLOADER=y
# CONFIG_CMDLINE_EXTEND is not set
# CONFIG_CMDLINE_FORCE is not set
# CONFIG_XIP_KERNEL is not set
# CONFIG_KEXEC is not set
# CONFIG_CRASH_DUMP is not set
# CONFIG_AUTO_ZRELADDR is not set
#
@ -401,13 +415,13 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_DEFAULT_GOV_FANTASY is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
CONFIG_CPU_FREQ_GOV_USERSPACE=m
CONFIG_CPU_FREQ_GOV_ONDEMAND=m
CONFIG_CPU_FREQ_GOV_INTERACTIVE=m
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
CONFIG_CPU_FREQ_GOV_FANTASY=m
CONFIG_CPU_FREQ_GOV_SMARTASS2=m
# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
# CONFIG_CPU_FREQ_GOV_INTERACTIVE is not set
# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
CONFIG_CPU_FREQ_GOV_FANTASY=y
# CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set
CONFIG_CPU_FREQ_USR_EVNT_NOTIFY=y
CONFIG_CPU_FREQ_DVFS=y
CONFIG_CPU_IDLE=y
@ -460,8 +474,12 @@ CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_XFRM=y
CONFIG_XFRM_USER=y
# CONFIG_XFRM_SUB_POLICY is not set
# CONFIG_XFRM_MIGRATE is not set
# CONFIG_XFRM_STATISTICS is not set
CONFIG_XFRM_IPCOMP=y
CONFIG_NET_KEY=y
# CONFIG_NET_KEY_MIGRATE is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
# CONFIG_IP_ADVANCED_ROUTER is not set
@ -486,30 +504,43 @@ CONFIG_INET_LRO=y
# CONFIG_TCP_CONG_ADVANCED is not set
CONFIG_TCP_CONG_CUBIC=y
CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_TCP_MD5SIG is not set
CONFIG_IPV6=y
# CONFIG_IPV6_PRIVACY is not set
# CONFIG_IPV6_ROUTER_PREF is not set
# CONFIG_IPV6_OPTIMISTIC_DAD is not set
# CONFIG_INET6_AH is not set
# CONFIG_INET6_ESP is not set
# CONFIG_INET6_IPCOMP is not set
# CONFIG_IPV6_MIP6 is not set
# CONFIG_INET6_XFRM_TUNNEL is not set
# CONFIG_INET6_TUNNEL is not set
CONFIG_INET6_XFRM_MODE_TRANSPORT=m
CONFIG_INET6_XFRM_MODE_TUNNEL=m
CONFIG_INET6_XFRM_MODE_BEET=m
# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
CONFIG_IPV6_SIT=m
# CONFIG_IPV6_SIT_6RD is not set
CONFIG_IPV6_NDISC_NODETYPE=y
# CONFIG_IPV6_TUNNEL is not set
# CONFIG_IPV6_MULTIPLE_TABLES is not set
# CONFIG_IPV6_MROUTE is not set
# CONFIG_ANDROID_PARANOID_NETWORK is not set
CONFIG_NET_ACTIVITY_STATS=y
CONFIG_NETWORK_SECMARK=y
# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
# CONFIG_NETFILTER is not set
# CONFIG_IP_DCCP is not set
# CONFIG_IP_SCTP is not set
# CONFIG_RDS is not set
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_L2TP is not set
CONFIG_STP=y
CONFIG_GARP=y
CONFIG_BRIDGE=m
CONFIG_BRIDGE_IGMP_SNOOPING=y
# CONFIG_NET_DSA is not set
CONFIG_VLAN_8021Q=y
CONFIG_VLAN_8021Q_GVRP=y
# CONFIG_DECNET is not set
@ -517,7 +548,12 @@ CONFIG_LLC=y
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_PHONET is not set
# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
# CONFIG_DCB is not set
# CONFIG_BATMAN_ADV is not set
@ -554,6 +590,7 @@ CONFIG_BT_HCIUART_LL=y
# CONFIG_BT_HCIBFUSB is not set
# CONFIG_BT_HCIVHCI is not set
# CONFIG_BT_MRVL is not set
# CONFIG_AF_RXRPC is not set
CONFIG_WIRELESS=y
CONFIG_WIRELESS_EXT=y
CONFIG_WEXT_CORE=y
@ -579,6 +616,7 @@ CONFIG_RFKILL_INPUT=y
CONFIG_SUNXI_RFKILL=y
# CONFIG_NET_9P is not set
# CONFIG_CAIF is not set
# CONFIG_CEPH_LIB is not set
#
# Device Drivers
@ -616,6 +654,7 @@ CONFIG_BLK_DEV_RAM_SIZE=4096
# CONFIG_BLK_DEV_XIP is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
# CONFIG_BLK_DEV_RBD is not set
CONFIG_SUN4I_NANDFLASH=y
CONFIG_SUNXI_NAND=y
# CONFIG_SUNXI_NAND_TEST is not set
@ -625,6 +664,7 @@ CONFIG_MISC_DEVICES=y
# CONFIG_SUN4I_GPIO_UGLY is not set
CONFIG_SUNXI_DBGREG=m
# CONFIG_INTEL_MID_PTI is not set
# CONFIG_ICS932S401 is not set
# CONFIG_ENCLOSURE_SERVICES is not set
# CONFIG_APDS9802ALS is not set
# CONFIG_ISL29003 is not set
@ -635,17 +675,21 @@ CONFIG_SUNXI_DBGREG=m
# CONFIG_SENSORS_APDS990X is not set
# CONFIG_HMC6352 is not set
# CONFIG_SENSORS_AK8975 is not set
# CONFIG_DS1682 is not set
# CONFIG_UID_STAT is not set
# CONFIG_BMP085 is not set
# CONFIG_WL127X_RFKILL is not set
# CONFIG_APANIC is not set
# CONFIG_C2PORT is not set
#
# EEPROM support
#
# CONFIG_EEPROM_AT24 is not set
# CONFIG_EEPROM_LEGACY is not set
# CONFIG_EEPROM_MAX6875 is not set
# CONFIG_EEPROM_93CX6 is not set
# CONFIG_IWMC3200TOP is not set
#
# Texas Instruments shared transport line discipline
@ -661,6 +705,7 @@ CONFIG_SCSI_MOD=y
# CONFIG_RAID_ATTRS is not set
CONFIG_SCSI=y
CONFIG_SCSI_DMA=y
# CONFIG_SCSI_TGT is not set
# CONFIG_SCSI_NETLINK is not set
CONFIG_SCSI_PROC_FS=y
@ -736,6 +781,7 @@ CONFIG_ATA_BMDMA=y
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
# CONFIG_EQUALIZER is not set
CONFIG_TUN=m
# CONFIG_VETH is not set
@ -766,12 +812,14 @@ CONFIG_PHYLIB=y
# CONFIG_NETDEV_10000 is not set
CONFIG_WLAN=y
# CONFIG_USB_ZD1201 is not set
# CONFIG_USB_NET_RNDIS_WLAN is not set
# CONFIG_WIFI_CONTROL_FUNC is not set
# CONFIG_ATH_COMMON is not set
# CONFIG_BCM4329 is not set
CONFIG_BCM4330=m
# CONFIG_BCMDHD is not set
# CONFIG_HOSTAP is not set
# CONFIG_IWM is not set
# CONFIG_LIBERTAS is not set
# CONFIG_MWIFIEX is not set
CONFIG_RTL8192CU_SW=m
@ -783,25 +831,31 @@ CONFIG_RTL8192CU_SW=m
#
# USB Network Adapters
#
# CONFIG_USB_CATC is not set
CONFIG_USB_KAWETH=m
CONFIG_USB_PEGASUS=m
# CONFIG_USB_RTL8150 is not set
CONFIG_USB_USBNET=m
CONFIG_USB_NET_AX8817X=m
CONFIG_USB_NET_QF9700=m
CONFIG_USB_NET_CDCETHER=m
# CONFIG_USB_NET_CDC_EEM is not set
CONFIG_USB_NET_CDC_NCM=m
# CONFIG_USB_NET_DM9601 is not set
# CONFIG_USB_NET_SMSC75XX is not set
# CONFIG_USB_NET_SMSC95XX is not set
# CONFIG_USB_NET_GL620A is not set
CONFIG_USB_NET_NET1080=m
# CONFIG_USB_NET_PLUSB is not set
# CONFIG_USB_NET_MCS7830 is not set
# CONFIG_USB_NET_RNDIS_HOST is not set
CONFIG_USB_NET_CDC_SUBSET=m
# CONFIG_USB_ALI_M5632 is not set
# CONFIG_USB_AN2720 is not set
CONFIG_USB_BELKIN=y
CONFIG_USB_ARMLINUX=y
# CONFIG_USB_EPSON2888 is not set
# CONFIG_USB_KC2190 is not set
CONFIG_USB_NET_ZAURUS=m
# CONFIG_USB_NET_CX82310_ETH is not set
# CONFIG_USB_NET_KALMIA is not set
@ -852,6 +906,7 @@ CONFIG_INPUT_KEYBOARD=y
# CONFIG_KEYBOARD_ADP5589 is not set
CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_QT1070 is not set
# CONFIG_KEYBOARD_QT2160 is not set
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_TCA6416 is not set
# CONFIG_KEYBOARD_MAX7359 is not set
@ -963,6 +1018,7 @@ CONFIG_DEVPTS_MULTIPLE_INSTANCES=y
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_NONSTANDARD=y
# CONFIG_N_HDLC is not set
# CONFIG_N_GSM is not set
# CONFIG_TRACE_SINK is not set
CONFIG_DEVMEM=y
# CONFIG_DEVKMEM is not set
@ -994,6 +1050,7 @@ CONFIG_SERIAL_CORE_CONSOLE=y
# CONFIG_HW_RANDOM is not set
# CONFIG_R3964 is not set
# CONFIG_RAW_DRIVER is not set
# CONFIG_TCG_TPM is not set
# CONFIG_DCC_TTY is not set
# CONFIG_RAMOOPS is not set
CONFIG_SUN4I_G2D=y
@ -1001,6 +1058,7 @@ CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_COMPAT=y
CONFIG_I2C_CHARDEV=y
# CONFIG_I2C_MUX is not set
CONFIG_I2C_HELPER_AUTO=y
CONFIG_I2C_ALGOBIT=m
@ -1012,22 +1070,26 @@ CONFIG_I2C_ALGOBIT=m
# I2C system bus drivers (mostly embedded / system-on-chip)
#
# CONFIG_I2C_DESIGNWARE is not set
# CONFIG_I2C_OCORES is not set
# CONFIG_I2C_PCA_PLATFORM is not set
# CONFIG_I2C_PXA_PCI is not set
# CONFIG_I2C_SIMTEC is not set
CONFIG_I2C_SUN4I=y
# CONFIG_SUNXI_IIC_PRINT_TRANSFER_INFO is not set
# CONFIG_I2C_XILINX is not set
#
# External I2C/SMBus adapter drivers
#
# CONFIG_I2C_DIOLAN_U2C is not set
# CONFIG_I2C_PARPORT_LIGHT is not set
# CONFIG_I2C_TAOS_EVM is not set
# CONFIG_I2C_TINY_USB is not set
#
# Other I2C/SMBus bus drivers
#
# CONFIG_I2C_STUB is not set
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
# CONFIG_I2C_DEBUG_BUS is not set
@ -1036,6 +1098,7 @@ CONFIG_I2C_SUN4I=y
#
# PPS support
#
# CONFIG_PPS is not set
#
# PPS generators support
@ -1104,6 +1167,7 @@ CONFIG_MEDIA_SUPPORT=y
#
# Multimedia core support
#
# CONFIG_MEDIA_CONTROLLER is not set
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_COMMON=y
# CONFIG_DVB_CORE is not set
@ -1139,6 +1203,7 @@ CONFIG_MEDIA_TUNER_CUSTOMISE=y
# CONFIG_MEDIA_TUNER_TDA827X is not set
# CONFIG_MEDIA_TUNER_TDA18271 is not set
# CONFIG_MEDIA_TUNER_TDA9887 is not set
CONFIG_MEDIA_TUNER_TEA5761=m
# CONFIG_MEDIA_TUNER_TEA5767 is not set
# CONFIG_MEDIA_TUNER_MT20XX is not set
# CONFIG_MEDIA_TUNER_MT2060 is not set
@ -1376,6 +1441,7 @@ CONFIG_USB_DEVICE_CLASS=y
# CONFIG_USB_DYNAMIC_MINORS is not set
# CONFIG_USB_SUSPEND is not set
# CONFIG_USB_MON is not set
# CONFIG_USB_WUSB is not set
# CONFIG_USB_WUSB_CBAF is not set
#
@ -1387,6 +1453,7 @@ CONFIG_USB_EHCI_ROOT_HUB_TT=y
CONFIG_USB_EHCI_TT_NEWSCHED=y
# CONFIG_USB_OXU210HP_HCD is not set
# CONFIG_USB_ISP116X_HCD is not set
# CONFIG_USB_ISP1760_HCD is not set
# CONFIG_USB_ISP1362_HCD is not set
CONFIG_USB_OHCI_HCD=y
# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
@ -1394,6 +1461,7 @@ CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_LITTLE_ENDIAN=y
# CONFIG_USB_SL811_HCD is not set
# CONFIG_USB_R8A66597_HCD is not set
# CONFIG_USB_HWA_HCD is not set
CONFIG_USB_SW_SUN4I_HCD=y
CONFIG_USB_SW_SUN4I_HCD0=y
CONFIG_USB_SW_SUN4I_HCI=y
@ -1487,10 +1555,13 @@ CONFIG_USB_GADGET_DUALSPEED=y
# CONFIG_USB_AUDIO is not set
# CONFIG_USB_ETH is not set
# CONFIG_USB_G_NCM is not set
# CONFIG_USB_GADGETFS is not set
# CONFIG_USB_FUNCTIONFS is not set
CONFIG_USB_FILE_STORAGE=m
CONFIG_USB_FILE_STORAGE_TEST=y
# CONFIG_USB_MASS_STORAGE is not set
# CONFIG_USB_G_SERIAL is not set
# CONFIG_USB_MIDI_GADGET is not set
# CONFIG_USB_G_PRINTER is not set
# CONFIG_USB_CDC_COMPOSITE is not set
# CONFIG_USB_G_MULTI is not set
@ -1513,6 +1584,9 @@ CONFIG_USB_SW_SUN4I_USB_DEBUG=y
CONFIG_MMC=y
# CONFIG_MMC_DEBUG is not set
CONFIG_MMC_UNSAFE_RESUME=y
# CONFIG_MMC_CLKGATE is not set
# CONFIG_MMC_EMBEDDED_SDIO is not set
# CONFIG_MMC_PARANOID_SD_INIT is not set
#
# MMC/SD/SDIO Card Drivers
@ -1612,6 +1686,8 @@ CONFIG_RTC_DRV_SUN4I=y
CONFIG_STAGING=y
# CONFIG_RISCOM8 is not set
# CONFIG_SPECIALIX is not set
# CONFIG_VIDEO_TM6000 is not set
# CONFIG_USBIP_CORE is not set
# CONFIG_PRISM2_USB is not set
# CONFIG_ECHO is not set
# CONFIG_BRCMUTIL is not set
@ -1636,6 +1712,7 @@ CONFIG_ZRAM=m
CONFIG_MACH_NO_WESTBRIDGE=y
# CONFIG_ATH6K_LEGACY is not set
# CONFIG_USB_ENESTORAGE is not set
# CONFIG_BCM_WIMAX is not set
# CONFIG_FT1000 is not set
#
@ -1677,6 +1754,8 @@ CONFIG_FS_MBCACHE=y
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_BTRFS_FS is not set
# CONFIG_NILFS2_FS is not set
CONFIG_FS_POSIX_ACL=y
CONFIG_FILE_LOCKING=y
CONFIG_FSNOTIFY=y
@ -1742,7 +1821,14 @@ CONFIG_TMPFS_XATTR=y
# CONFIG_HUGETLB_PAGE is not set
CONFIG_CONFIGFS_FS=y
CONFIG_MISC_FILESYSTEMS=y
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
# CONFIG_LOGFS is not set
# CONFIG_CRAMFS is not set
CONFIG_SQUASHFS=y
CONFIG_SQUASHFS_XATTR=y
@ -1759,6 +1845,21 @@ CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
# CONFIG_PSTORE is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
CONFIG_AUFS_FS=y
CONFIG_AUFS_BRANCH_MAX_127=y
# CONFIG_AUFS_BRANCH_MAX_511 is not set
# CONFIG_AUFS_BRANCH_MAX_1023 is not set
# CONFIG_AUFS_BRANCH_MAX_32767 is not set
CONFIG_AUFS_SBILIST=y
# CONFIG_AUFS_HNOTIFY is not set
# CONFIG_AUFS_RDU is not set
# CONFIG_AUFS_PROC_MAP is not set
# CONFIG_AUFS_SP_IATTR is not set
# CONFIG_AUFS_SHWH is not set
# CONFIG_AUFS_BR_RAMFS is not set
# CONFIG_AUFS_BR_FUSE is not set
CONFIG_AUFS_BDEV_LOOP=y
# CONFIG_AUFS_DEBUG is not set
CONFIG_NETWORK_FILESYSTEMS=y
CONFIG_NFS_FS=y
# CONFIG_NFS_V3 is not set
@ -1768,13 +1869,16 @@ CONFIG_NFS_FS=y
CONFIG_LOCKD=y
CONFIG_NFS_COMMON=y
CONFIG_SUNRPC=y
# CONFIG_CEPH_FS is not set
CONFIG_CIFS=y
# CONFIG_CIFS_STATS is not set
# CONFIG_CIFS_WEAK_PW_HASH is not set
# CONFIG_CIFS_XATTR is not set
# CONFIG_CIFS_DEBUG2 is not set
# CONFIG_CIFS_FSCACHE is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set
#
# Partition Types
@ -1789,7 +1893,7 @@ CONFIG_MSDOS_PARTITION=y
CONFIG_BSD_DISKLABEL=y
CONFIG_MINIX_SUBPARTITION=y
CONFIG_SOLARIS_X86_PARTITION=y
CONFIG_UNIXWARE_DISKLABEL=y
# CONFIG_UNIXWARE_DISKLABEL is not set
# CONFIG_LDM_PARTITION is not set
CONFIG_SGI_PARTITION=y
# CONFIG_ULTRIX_PARTITION is not set
@ -1837,6 +1941,7 @@ CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
CONFIG_NLS_UTF8=y
# CONFIG_DLM is not set
#
# Kernel hacking
@ -1866,6 +1971,7 @@ CONFIG_TIMER_STATS=y
# CONFIG_DEBUG_OBJECTS is not set
# CONFIG_SLUB_DEBUG_ON is not set
# CONFIG_SLUB_STATS is not set
# CONFIG_DEBUG_KMEMLEAK is not set
CONFIG_DEBUG_PREEMPT=y
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_RT_MUTEX_TESTER is not set
@ -1892,7 +1998,6 @@ CONFIG_DEBUG_LIST=y
# CONFIG_DEBUG_SG is not set
# CONFIG_DEBUG_NOTIFIERS is not set
# CONFIG_DEBUG_CREDENTIALS is not set
CONFIG_FRAME_POINTER=y
CONFIG_BOOT_PRINTK_DELAY=y
# CONFIG_RCU_TORTURE_TEST is not set
# CONFIG_BACKTRACE_SELF_TEST is not set
@ -1915,8 +2020,10 @@ CONFIG_DYNAMIC_DEBUG=y
# CONFIG_ATOMIC64_SELFTEST is not set
# CONFIG_SAMPLES is not set
CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_KGDB is not set
# CONFIG_TEST_KSTRTOX is not set
CONFIG_STRICT_DEVMEM=y
CONFIG_ARM_UNWIND=y
# CONFIG_DEBUG_USER is not set
CONFIG_DEBUG_LL=y
CONFIG_EARLY_PRINTK=y
@ -1973,12 +2080,16 @@ CONFIG_CRYPTO_CBC=y
# CONFIG_CRYPTO_CTR is not set
# CONFIG_CRYPTO_CTS is not set
CONFIG_CRYPTO_ECB=y
# CONFIG_CRYPTO_LRW is not set
# CONFIG_CRYPTO_PCBC is not set
# CONFIG_CRYPTO_XTS is not set
#
# Hash modes
#
CONFIG_CRYPTO_HMAC=y
# CONFIG_CRYPTO_XCBC is not set
# CONFIG_CRYPTO_VMAC is not set
#
# Digest
@ -2011,6 +2122,7 @@ CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_DES=y
# CONFIG_CRYPTO_FCRYPT is not set
# CONFIG_CRYPTO_KHAZAD is not set
# CONFIG_CRYPTO_SALSA20 is not set
# CONFIG_CRYPTO_SEED is not set
# CONFIG_CRYPTO_SERPENT is not set
# CONFIG_CRYPTO_TEA is not set

View File

@ -0,0 +1,203 @@
config AUFS_FS
tristate "Aufs (Advanced multi layered unification filesystem) support"
depends on EXPERIMENTAL
help
Aufs is a stackable unification filesystem such as Unionfs,
which unifies several directories and provides a merged single
directory.
In the early days, aufs was entirely re-designed and
re-implemented Unionfs Version 1.x series. Introducing many
original ideas, approaches and improvements, it becomes totally
different from Unionfs while keeping the basic features.
if AUFS_FS
choice
prompt "Maximum number of branches"
default AUFS_BRANCH_MAX_127
help
Specifies the maximum number of branches (or member directories)
in a single aufs. The larger value consumes more system
resources and has a minor impact to performance.
config AUFS_BRANCH_MAX_127
bool "127"
help
Specifies the maximum number of branches (or member directories)
in a single aufs. The larger value consumes more system
resources and has a minor impact to performance.
config AUFS_BRANCH_MAX_511
bool "511"
help
Specifies the maximum number of branches (or member directories)
in a single aufs. The larger value consumes more system
resources and has a minor impact to performance.
config AUFS_BRANCH_MAX_1023
bool "1023"
help
Specifies the maximum number of branches (or member directories)
in a single aufs. The larger value consumes more system
resources and has a minor impact to performance.
config AUFS_BRANCH_MAX_32767
bool "32767"
help
Specifies the maximum number of branches (or member directories)
in a single aufs. The larger value consumes more system
resources and has a minor impact to performance.
endchoice
config AUFS_SBILIST
bool
depends on AUFS_MAGIC_SYSRQ || PROC_FS
default y
help
Automatic configuration for internal use.
When aufs supports Magic SysRq or /proc, enabled automatically.
config AUFS_HNOTIFY
bool "Detect direct branch access (bypassing aufs)"
help
If you want to modify files on branches directly, eg. bypassing aufs,
and want aufs to detect the changes of them fully, then enable this
option and use 'udba=notify' mount option.
Currently there is only one available configuration, "fsnotify".
It will have a negative impact to the performance.
See detail in aufs.5.
choice
prompt "method" if AUFS_HNOTIFY
default AUFS_HFSNOTIFY
config AUFS_HFSNOTIFY
bool "fsnotify"
select FSNOTIFY
endchoice
config AUFS_EXPORT
bool "NFS-exportable aufs"
depends on EXPORTFS
help
If you want to export your mounted aufs via NFS, then enable this
option. There are several requirements for this configuration.
See detail in aufs.5.
config AUFS_INO_T_64
bool
depends on AUFS_EXPORT
depends on 64BIT && !(ALPHA || S390)
default y
help
Automatic configuration for internal use.
/* typedef unsigned long/int __kernel_ino_t */
/* alpha and s390x are int */
config AUFS_RDU
bool "Readdir in userspace"
help
Aufs has two methods to provide a merged view for a directory,
by a user-space library and by kernel-space natively. The latter
is always enabled but sometimes large and slow.
If you enable this option, install the library in aufs2-util
package, and set some environment variables for your readdir(3),
then the work will be handled in user-space which generally
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
When you write something to a special file, some attributes of it
(mtime/ctime mainly) may be updated. Generally such updates are
less important (actually some device drivers and NFS ignore
it). But some applications (such like test program) requires
such updates. If you need these updates, then enable this
configuration which introduces some overhead.
Currently this configuration handles FIFO only.
config AUFS_SHWH
bool "Show whiteouts"
help
If you want to make the whiteouts in aufs visible, then enable
this option and specify 'shwh' mount option. Although it may
sounds like philosophy or something, but in technically it
simply shows the name of whiteout with keeping its behaviour.
config AUFS_BR_RAMFS
bool "Ramfs (initramfs/rootfs) as an aufs branch"
help
If you want to use ramfs as an aufs branch fs, then enable this
option. Generally tmpfs is recommended.
Aufs prohibited them to be a branch fs by default, because
initramfs becomes unusable after switch_root or something
generally. If you sets initramfs as an aufs branch and boot your
system by switch_root, you will meet a problem easily since the
files in initramfs may be inaccessible.
Unless you are going to use ramfs as an aufs branch fs without
switch_root or something, leave it N.
config AUFS_BR_FUSE
bool "Fuse fs as an aufs branch"
depends on FUSE_FS
select AUFS_POLL
help
If you want to use fuse-based userspace filesystem as an aufs
branch fs, then enable this option.
It implements the internal poll(2) operation which is
implemented by fuse only (curretnly).
config AUFS_POLL
bool
help
Automatic configuration for internal use.
config AUFS_BR_HFSPLUS
bool "Hfsplus as an aufs branch"
depends on HFSPLUS_FS
default y
help
If you want to use hfsplus fs as an aufs branch fs, then enable
this option. This option introduces a small overhead at
copying-up a file on hfsplus.
config AUFS_BDEV_LOOP
bool
depends on BLK_DEV_LOOP
default y
help
Automatic configuration for internal use.
Convert =[ym] into =y.
config AUFS_DEBUG
bool "Debug aufs"
help
Enable this to compile aufs internal debug code.
It will have a negative impact to the performance.
config AUFS_MAGIC_SYSRQ
bool
depends on AUFS_DEBUG && MAGIC_SYSRQ
default y
help
Automatic configuration for internal use.
When aufs supports Magic SysRq, enabled automatically.
endif

View File

@ -0,0 +1,42 @@
include ${src}/magic.mk
ifeq (${CONFIG_AUFS_FS},m)
include ${src}/conf.mk
endif
-include ${src}/priv_def.mk
# cf. include/linux/kernel.h
# enable pr_debug
ccflags-y += -DDEBUG
# sparse requires the full pathname
ifdef M
ccflags-y += -include ${M}/../../include/linux/aufs_type.h
else
ccflags-y += -include ${srctree}/include/linux/aufs_type.h
endif
obj-$(CONFIG_AUFS_FS) += aufs.o
aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \
wkq.o vfsub.o dcsub.o \
cpup.o whout.o wbr_policy.o \
dinfo.o dentry.o \
dynop.o \
finfo.o file.o f_op.o \
dir.o vdir.o \
iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \
ioctl.o
# all are boolean
aufs-$(CONFIG_PROC_FS) += procfs.o plink.o
aufs-$(CONFIG_SYSFS) += sysfs.o
aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o
aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o
aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o
aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o
aufs-$(CONFIG_AUFS_EXPORT) += export.o
aufs-$(CONFIG_AUFS_POLL) += poll.o
aufs-$(CONFIG_AUFS_RDU) += rdu.o
aufs-$(CONFIG_AUFS_SP_IATTR) += f_op_sp.o
aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o
aufs-$(CONFIG_AUFS_DEBUG) += debug.o
aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o

View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* all header files
*/
#ifndef __AUFS_H__
#define __AUFS_H__
#ifdef __KERNEL__
#define AuStub(type, name, body, ...) \
static inline type name(__VA_ARGS__) { body; }
#define AuStubVoid(name, ...) \
AuStub(void, name, , __VA_ARGS__)
#define AuStubInt0(name, ...) \
AuStub(int, name, return 0, __VA_ARGS__)
#include "debug.h"
#include "branch.h"
#include "cpup.h"
#include "dcsub.h"
#include "dbgaufs.h"
#include "dentry.h"
#include "dir.h"
#include "dynop.h"
#include "file.h"
#include "fstype.h"
#include "inode.h"
#include "loop.h"
#include "module.h"
#include "opts.h"
#include "rwsem.h"
#include "spl.h"
#include "super.h"
#include "sysaufs.h"
#include "vfsub.h"
#include "whout.h"
#include "wkq.h"
#endif /* __KERNEL__ */
#endif /* __AUFS_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,230 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* branch filesystems and xino for them
*/
#ifndef __AUFS_BRANCH_H__
#define __AUFS_BRANCH_H__
#ifdef __KERNEL__
#include <linux/mount.h>
#include "dynop.h"
#include "rwsem.h"
#include "super.h"
/* ---------------------------------------------------------------------- */
/* a xino file */
struct au_xino_file {
struct file *xi_file;
struct mutex xi_nondir_mtx;
/* todo: make xino files an array to support huge inode number */
#ifdef CONFIG_DEBUG_FS
struct dentry *xi_dbgaufs;
#endif
};
/* 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 */
/* mfs mode */
unsigned long long wbr_bytes;
};
/* ext2 has 3 types of operations at least, ext3 has 4 */
#define AuBrDynOp (AuDyLast * 4)
/* protected by superblock rwsem */
struct au_branch {
struct au_xino_file br_xino;
aufs_bindex_t br_id;
int br_perm;
struct vfsmount *br_mnt;
spinlock_t br_dykey_lock;
struct au_dykey *br_dykey[AuBrDynOp];
atomic_t br_count;
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;
#endif
#ifdef CONFIG_SYSFS
/* an entry under sysfs per mount-point */
char br_name[8];
struct attribute br_attr;
#endif
};
/* ---------------------------------------------------------------------- */
/* branch permissions and attributes */
#define AuBrPerm_RW 1 /* writable, hardlinkable wh */
#define AuBrPerm_RO (1 << 1) /* readonly */
#define AuBrPerm_RR (1 << 2) /* natively readonly */
#define AuBrPerm_Mask (AuBrPerm_RW | AuBrPerm_RO | AuBrPerm_RR)
#define AuBrRAttr_WH (1 << 3) /* whiteout-able */
#define AuBrWAttr_NoLinkWH (1 << 4) /* un-hardlinkable whiteouts */
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);
}
static inline int au_br_wh_linkable(int brperm)
{
return !(brperm & AuBrWAttr_NoLinkWH);
}
static inline int au_br_rdonly(struct au_branch *br)
{
return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY)
|| !au_br_writable(br->br_perm))
? -EROFS : 0;
}
static inline int au_br_hnotifyable(int brperm __maybe_unused)
{
#ifdef CONFIG_AUFS_HNOTIFY
return !(brperm & AuBrPerm_RR);
#else
return 0;
#endif
}
/* ---------------------------------------------------------------------- */
/* 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, int remount);
struct au_opt_del;
int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount);
long au_ibusy_ioctl(struct file *file, unsigned long arg);
#ifdef CONFIG_COMPAT
long au_ibusy_compat_ioctl(struct file *file, unsigned long arg);
#endif
struct au_opt_mod;
int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
int *do_refresh);
/* xino.c */
static const loff_t au_loff_max = LLONG_MAX;
int au_xib_trunc(struct super_block *sb);
ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size,
loff_t *pos);
ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size,
loff_t *pos);
struct file *au_xino_create2(struct file *base_file, struct file *copy_src);
struct file *au_xino_create(struct super_block *sb, char *fname, int silent);
ino_t au_xino_new_ino(struct super_block *sb);
void au_xino_delete_inode(struct inode *inode, const int unlinked);
int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
ino_t ino);
int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
ino_t *ino);
int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino,
struct file *base_file, int do_test);
int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex);
struct au_opt_xino;
int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount);
void au_xino_clr(struct super_block *sb);
struct file *au_xino_def(struct super_block *sb);
int au_xino_path(struct seq_file *seq, struct file *file);
/* ---------------------------------------------------------------------- */
/* 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 vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)
{
return au_sbr(sb, bindex)->br_mnt;
}
static inline
struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
{
return au_sbr_mnt(sb, bindex)->mnt_sb;
}
static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex)
{
atomic_dec(&au_sbr(sb, bindex)->br_count);
}
static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex)
{
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));
}
/* ---------------------------------------------------------------------- */
/*
* wbr_wh_read_lock, wbr_wh_write_lock
* wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock
*/
AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &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 /* __AUFS_BRANCH_H__ */

View File

@ -0,0 +1,38 @@
AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS}
define AuConf
ifdef ${1}
AuConfStr += ${1}=${${1}}
endif
endef
AuConfAll = BRANCH_MAX_127 BRANCH_MAX_511 BRANCH_MAX_1023 BRANCH_MAX_32767 \
SBILIST \
HNOTIFY HFSNOTIFY \
EXPORT INO_T_64 \
RDU \
PROC_MAP \
SP_IATTR \
SHWH \
BR_RAMFS \
BR_FUSE POLL \
BR_HFSPLUS \
BDEV_LOOP \
DEBUG MAGIC_SYSRQ
$(foreach i, ${AuConfAll}, \
$(eval $(call AuConf,CONFIG_AUFS_${i})))
AuConfName = ${obj}/conf.str
${AuConfName}.tmp: FORCE
@echo ${AuConfStr} | tr ' ' '\n' | sed -e 's/^/"/' -e 's/$$/\\n"/' > $@
${AuConfName}: ${AuConfName}.tmp
@diff -q $< $@ > /dev/null 2>&1 || { \
echo ' GEN ' $@; \
cp -p $< $@; \
}
FORCE:
clean-files += ${AuConfName} ${AuConfName}.tmp
${obj}/sysfs.o: ${AuConfName}
-include ${srctree}/${src}/conf_priv.mk

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* copy-up/down functions
*/
#ifndef __AUFS_CPUP_H__
#define __AUFS_CPUP_H__
#ifdef __KERNEL__
#include <linux/path.h>
struct inode;
struct file;
void au_cpup_attr_flags(struct inode *dst, struct inode *src);
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);
void au_cpup_igen(struct inode *inode, struct inode *h_inode);
void au_cpup_attr_all(struct inode *inode, int force);
/* ---------------------------------------------------------------------- */
/* 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 au_ftest_cpup(flags, name) ((flags) & AuCpup_##name)
#define au_fset_cpup(flags, name) \
do { (flags) |= AuCpup_##name; } while (0)
#define au_fclr_cpup(flags, name) \
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_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst,
int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,
struct dentry *h_parent, void *arg),
void *arg);
int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst);
int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst);
/* ---------------------------------------------------------------------- */
/* keep timestamps when copyup */
struct au_dtime {
struct dentry *dt_dentry;
struct path dt_h_path;
struct timespec dt_atime, dt_mtime;
};
void au_dtime_store(struct au_dtime *dt, struct dentry *dentry,
struct path *h_path);
void au_dtime_revert(struct au_dtime *dt);
#endif /* __KERNEL__ */
#endif /* __AUFS_CPUP_H__ */

View File

@ -0,0 +1,334 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* debugfs interface
*/
#include <linux/debugfs.h>
#include "aufs.h"
#ifndef CONFIG_SYSFS
#error DEBUG_FS depends upon SYSFS
#endif
static struct dentry *dbgaufs;
static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH;
/* 20 is max digits length of ulong 64 */
struct dbgaufs_arg {
int n;
char a[20 * 4];
};
/*
* common function for all XINO files
*/
static int dbgaufs_xi_release(struct inode *inode __maybe_unused,
struct file *file)
{
kfree(file->private_data);
return 0;
}
static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt)
{
int err;
struct kstat st;
struct dbgaufs_arg *p;
err = -ENOMEM;
p = kmalloc(sizeof(*p), GFP_NOFS);
if (unlikely(!p))
goto out;
err = 0;
p->n = 0;
file->private_data = p;
if (!xf)
goto out;
err = vfs_getattr(xf->f_vfsmnt, xf->f_dentry, &st);
if (!err) {
if (do_fcnt)
p->n = snprintf
(p->a, sizeof(p->a), "%ld, %llux%lu %lld\n",
(long)file_count(xf), st.blocks, st.blksize,
(long long)st.size);
else
p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n",
st.blocks, st.blksize,
(long long)st.size);
AuDebugOn(p->n >= sizeof(p->a));
} else {
p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err);
err = 0;
}
out:
return err;
}
static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct dbgaufs_arg *p;
p = file->private_data;
return simple_read_from_buffer(buf, count, ppos, p->a, p->n);
}
/* ---------------------------------------------------------------------- */
static int dbgaufs_xib_open(struct inode *inode, struct file *file)
{
int err;
struct au_sbinfo *sbinfo;
struct super_block *sb;
sbinfo = inode->i_private;
sb = sbinfo->si_sb;
si_noflush_read_lock(sb);
err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0);
si_read_unlock(sb);
return err;
}
static const struct file_operations dbgaufs_xib_fop = {
.owner = THIS_MODULE,
.open = dbgaufs_xib_open,
.release = dbgaufs_xi_release,
.read = dbgaufs_xi_read
};
/* ---------------------------------------------------------------------- */
#define DbgaufsXi_PREFIX "xi"
static int dbgaufs_xino_open(struct inode *inode, struct file *file)
{
int err;
long l;
struct au_sbinfo *sbinfo;
struct super_block *sb;
struct file *xf;
struct qstr *name;
err = -ENOENT;
xf = NULL;
name = &file->f_dentry->d_name;
if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX)
|| memcmp(name->name, DbgaufsXi_PREFIX,
sizeof(DbgaufsXi_PREFIX) - 1)))
goto out;
err = strict_strtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l);
if (unlikely(err))
goto out;
sbinfo = inode->i_private;
sb = sbinfo->si_sb;
si_noflush_read_lock(sb);
if (l <= au_sbend(sb)) {
xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file;
err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1);
} else
err = -ENOENT;
si_read_unlock(sb);
out:
return err;
}
static const struct file_operations dbgaufs_xino_fop = {
.owner = THIS_MODULE,
.open = dbgaufs_xino_open,
.release = dbgaufs_xi_release,
.read = dbgaufs_xi_read
};
void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
{
aufs_bindex_t bend;
struct au_branch *br;
struct au_xino_file *xi;
if (!au_sbi(sb)->si_dbgaufs)
return;
bend = au_sbend(sb);
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;
}
}
}
void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)
{
struct au_sbinfo *sbinfo;
struct dentry *parent;
struct au_branch *br;
struct au_xino_file *xi;
aufs_bindex_t bend;
char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */
sbinfo = au_sbi(sb);
parent = sbinfo->si_dbgaufs;
if (!parent)
return;
bend = au_sbend(sb);
for (; bindex <= bend; bindex++) {
snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex);
br = au_sbr(sb, bindex);
xi = &br->br_xino;
AuDebugOn(xi->xi_dbgaufs);
xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent,
sbinfo, &dbgaufs_xino_fop);
/* ignore an error */
if (unlikely(!xi->xi_dbgaufs))
AuWarn1("failed %s under debugfs\n", name);
}
}
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_AUFS_EXPORT
static int dbgaufs_xigen_open(struct inode *inode, struct file *file)
{
int err;
struct au_sbinfo *sbinfo;
struct super_block *sb;
sbinfo = inode->i_private;
sb = sbinfo->si_sb;
si_noflush_read_lock(sb);
err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0);
si_read_unlock(sb);
return err;
}
static const struct file_operations dbgaufs_xigen_fop = {
.owner = THIS_MODULE,
.open = dbgaufs_xigen_open,
.release = dbgaufs_xi_release,
.read = dbgaufs_xi_read
};
static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo)
{
int err;
/*
* This function is a dynamic '__init' fucntion actually,
* so the tiny check for si_rwsem is unnecessary.
*/
/* AuRwMustWriteLock(&sbinfo->si_rwsem); */
err = -EIO;
sbinfo->si_dbgaufs_xigen = debugfs_create_file
("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo,
&dbgaufs_xigen_fop);
if (sbinfo->si_dbgaufs_xigen)
err = 0;
return err;
}
#else
static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo)
{
return 0;
}
#endif /* CONFIG_AUFS_EXPORT */
/* ---------------------------------------------------------------------- */
void dbgaufs_si_fin(struct au_sbinfo *sbinfo)
{
/*
* This function is a dynamic '__init' fucntion actually,
* so the tiny check for si_rwsem is unnecessary.
*/
/* AuRwMustWriteLock(&sbinfo->si_rwsem); */
debugfs_remove_recursive(sbinfo->si_dbgaufs);
sbinfo->si_dbgaufs = NULL;
kobject_put(&sbinfo->si_kobj);
}
int dbgaufs_si_init(struct au_sbinfo *sbinfo)
{
int err;
char name[SysaufsSiNameLen];
/*
* This function is a dynamic '__init' fucntion actually,
* so the tiny check for si_rwsem is unnecessary.
*/
/* AuRwMustWriteLock(&sbinfo->si_rwsem); */
err = -ENOENT;
if (!dbgaufs) {
AuErr1("/debug/aufs is uninitialized\n");
goto out;
}
err = -EIO;
sysaufs_name(sbinfo, name);
sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs);
if (unlikely(!sbinfo->si_dbgaufs))
goto out;
kobject_get(&sbinfo->si_kobj);
sbinfo->si_dbgaufs_xib = debugfs_create_file
("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo,
&dbgaufs_xib_fop);
if (unlikely(!sbinfo->si_dbgaufs_xib))
goto out_dir;
err = dbgaufs_xigen_init(sbinfo);
if (!err)
goto out; /* success */
out_dir:
dbgaufs_si_fin(sbinfo);
out:
return err;
}
/* ---------------------------------------------------------------------- */
void dbgaufs_fin(void)
{
debugfs_remove(dbgaufs);
}
int __init dbgaufs_init(void)
{
int err;
err = -EIO;
dbgaufs = debugfs_create_dir(AUFS_NAME, NULL);
if (dbgaufs)
err = 0;
return err;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* debugfs interface
*/
#ifndef __DBGAUFS_H__
#define __DBGAUFS_H__
#ifdef __KERNEL__
struct super_block;
struct au_sbinfo;
#ifdef CONFIG_DEBUG_FS
/* dbgaufs.c */
void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex);
void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex);
void dbgaufs_si_fin(struct au_sbinfo *sbinfo);
int dbgaufs_si_init(struct au_sbinfo *sbinfo);
void dbgaufs_fin(void);
int __init dbgaufs_init(void);
#else
AuStubVoid(dbgaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex)
AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex)
AuStubVoid(dbgaufs_si_fin, struct au_sbinfo *sbinfo)
AuStubInt0(dbgaufs_si_init, struct au_sbinfo *sbinfo)
AuStubVoid(dbgaufs_fin, void)
AuStubInt0(__init dbgaufs_init, void)
#endif /* CONFIG_DEBUG_FS */
#endif /* __KERNEL__ */
#endif /* __DBGAUFS_H__ */

View File

@ -0,0 +1,243 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* sub-routines for dentry cache
*/
#include "aufs.h"
static void au_dpage_free(struct au_dpage *dpage)
{
int i;
struct dentry **p;
p = dpage->dentries;
for (i = 0; i < dpage->ndentry; i++)
dput(*p++);
free_page((unsigned long)dpage->dentries);
}
int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)
{
int err;
void *p;
err = -ENOMEM;
dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp);
if (unlikely(!dpages->dpages))
goto out;
p = (void *)__get_free_page(gfp);
if (unlikely(!p))
goto out_dpages;
dpages->dpages[0].ndentry = 0;
dpages->dpages[0].dentries = p;
dpages->ndpage = 1;
return 0; /* success */
out_dpages:
kfree(dpages->dpages);
out:
return err;
}
void au_dpages_free(struct au_dcsub_pages *dpages)
{
int i;
struct au_dpage *p;
p = dpages->dpages;
for (i = 0; i < dpages->ndpage; i++)
au_dpage_free(p++);
kfree(dpages->dpages);
}
static int au_dpages_append(struct au_dcsub_pages *dpages,
struct dentry *dentry, gfp_t gfp)
{
int err, sz;
struct au_dpage *dpage;
void *p;
dpage = dpages->dpages + dpages->ndpage - 1;
sz = PAGE_SIZE / sizeof(dentry);
if (unlikely(dpage->ndentry >= sz)) {
AuLabel(new dpage);
err = -ENOMEM;
sz = dpages->ndpage * sizeof(*dpages->dpages);
p = au_kzrealloc(dpages->dpages, sz,
sz + sizeof(*dpages->dpages), gfp);
if (unlikely(!p))
goto out;
dpages->dpages = p;
dpage = dpages->dpages + dpages->ndpage;
p = (void *)__get_free_page(gfp);
if (unlikely(!p))
goto out;
dpage->ndentry = 0;
dpage->dentries = p;
dpages->ndpage++;
}
AuDebugOn(!dentry->d_count);
dpage->dentries[dpage->ndentry++] = dget_dlock(dentry);
return 0; /* success */
out:
return err;
}
int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
au_dpages_test test, void *arg)
{
int err;
struct dentry *this_parent;
struct list_head *next;
struct super_block *sb = root->d_sb;
err = 0;
write_seqlock(&rename_lock);
this_parent = root;
spin_lock(&this_parent->d_lock);
repeat:
next = this_parent->d_subdirs.next;
resume:
if (this_parent->d_sb == sb
&& !IS_ROOT(this_parent)
&& au_di(this_parent)
&& this_parent->d_count
&& (!test || test(this_parent, arg))) {
err = au_dpages_append(dpages, this_parent, GFP_ATOMIC);
if (unlikely(err))
goto out;
}
while (next != &this_parent->d_subdirs) {
struct list_head *tmp = next;
struct dentry *dentry = list_entry(tmp, struct dentry,
d_u.d_child);
next = tmp->next;
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
if (dentry->d_count) {
if (!list_empty(&dentry->d_subdirs)) {
spin_unlock(&this_parent->d_lock);
spin_release(&dentry->d_lock.dep_map, 1,
_RET_IP_);
this_parent = dentry;
spin_acquire(&this_parent->d_lock.dep_map, 0, 1,
_RET_IP_);
goto repeat;
}
if (dentry->d_sb == sb
&& au_di(dentry)
&& (!test || test(dentry, arg)))
err = au_dpages_append(dpages, dentry,
GFP_ATOMIC);
}
spin_unlock(&dentry->d_lock);
if (unlikely(err))
goto out;
}
if (this_parent != root) {
struct dentry *tmp;
struct dentry *child;
tmp = this_parent->d_parent;
rcu_read_lock();
spin_unlock(&this_parent->d_lock);
child = this_parent;
this_parent = tmp;
spin_lock(&this_parent->d_lock);
rcu_read_unlock();
next = child->d_u.d_child.next;
goto resume;
}
out:
spin_unlock(&this_parent->d_lock);
write_sequnlock(&rename_lock);
return err;
}
int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,
int do_include, au_dpages_test test, void *arg)
{
int err;
err = 0;
write_seqlock(&rename_lock);
spin_lock(&dentry->d_lock);
if (do_include
&& dentry->d_count
&& (!test || test(dentry, arg)))
err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
spin_unlock(&dentry->d_lock);
if (unlikely(err))
goto out;
/*
* vfsmount_lock is unnecessary since this is a traverse in a single
* mount
*/
while (!IS_ROOT(dentry)) {
dentry = dentry->d_parent; /* rename_lock is locked */
spin_lock(&dentry->d_lock);
if (dentry->d_count
&& (!test || test(dentry, arg)))
err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
spin_unlock(&dentry->d_lock);
if (unlikely(err))
break;
}
out:
write_sequnlock(&rename_lock);
return err;
}
static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg)
{
return au_di(dentry) && dentry->d_sb == arg;
}
int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages,
struct dentry *dentry, int do_include)
{
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);
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* sub-routines for dentry cache
*/
#ifndef __AUFS_DCSUB_H__
#define __AUFS_DCSUB_H__
#ifdef __KERNEL__
#include <linux/dcache.h>
#include <linux/fs.h>
struct dentry;
struct au_dpage {
int ndentry;
struct dentry **dentries;
};
struct au_dcsub_pages {
int ndpage;
struct au_dpage *dpages;
};
/* ---------------------------------------------------------------------- */
/* dcsub.c */
int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp);
void au_dpages_free(struct au_dcsub_pages *dpages);
typedef int (*au_dpages_test)(struct dentry *dentry, void *arg);
int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
au_dpages_test test, void *arg);
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);
/* ---------------------------------------------------------------------- */
static inline int au_d_hashed_positive(struct dentry *d)
{
int err;
struct inode *inode = d->d_inode;
err = 0;
if (unlikely(d_unhashed(d) || !inode || !inode->i_nlink))
err = -ENOENT;
return err;
}
static inline int au_d_alive(struct dentry *d)
{
int err;
struct inode *inode;
err = 0;
if (!IS_ROOT(d))
err = au_d_hashed_positive(d);
else {
inode = d->d_inode;
if (unlikely(d_unlinked(d) || !inode || !inode->i_nlink))
err = -ENOENT;
}
return err;
}
static inline int au_alive_dir(struct dentry *d)
{
int err;
err = au_d_alive(d);
if (unlikely(err || IS_DEADDIR(d->d_inode)))
err = -ENOENT;
return err;
}
#endif /* __KERNEL__ */
#endif /* __AUFS_DCSUB_H__ */

View File

@ -0,0 +1,489 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* debug print functions
*/
#include <linux/vt_kern.h>
#include "aufs.h"
int aufs_debug;
MODULE_PARM_DESC(debug, "debug print");
module_param_named(debug, aufs_debug, int, S_IRUGO | S_IWUSR | S_IWGRP);
char *au_plevel = KERN_DEBUG;
#define dpri(fmt, ...) do { \
if ((au_plevel \
&& strcmp(au_plevel, KERN_DEBUG)) \
|| au_debug_test()) \
printk("%s" fmt, au_plevel, ##__VA_ARGS__); \
} while (0)
/* ---------------------------------------------------------------------- */
void au_dpri_whlist(struct au_nhash *whlist)
{
unsigned long ul, n;
struct hlist_head *head;
struct au_vdir_wh *tpos;
struct hlist_node *pos;
n = whlist->nh_num;
head = whlist->nh_head;
for (ul = 0; ul < n; ul++) {
hlist_for_each_entry(tpos, pos, head, wh_hash)
dpri("b%d, %.*s, %d\n",
tpos->wh_bindex,
tpos->wh_str.len, tpos->wh_str.name,
tpos->wh_str.len);
head++;
}
}
void au_dpri_vdir(struct au_vdir *vdir)
{
unsigned long ul;
union au_vdir_deblk_p p;
unsigned char *o;
if (!vdir || IS_ERR(vdir)) {
dpri("err %ld\n", PTR_ERR(vdir));
return;
}
dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n",
vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk,
vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version);
for (ul = 0; ul < vdir->vd_nblk; ul++) {
p.deblk = vdir->vd_deblk[ul];
o = p.deblk;
dpri("[%lu]: %p\n", ul, o);
}
}
static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn,
struct dentry *wh)
{
char *n = NULL;
int l = 0;
if (!inode || IS_ERR(inode)) {
dpri("i%d: err %ld\n", bindex, PTR_ERR(inode));
return -1;
}
/* the type of i_blocks depends upon CONFIG_LSF */
BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long)
&& sizeof(inode->i_blocks) != sizeof(u64));
if (wh) {
n = (void *)wh->d_name.name;
l = wh->d_name.len;
}
dpri("i%d: %p, i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu,"
" hn %d, ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, g %x%s%.*s\n",
bindex, inode,
inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??",
atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode,
i_size_read(inode), (unsigned long long)inode->i_blocks,
hn, (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff,
inode->i_mapping ? inode->i_mapping->nrpages : 0,
inode->i_state, inode->i_flags, inode->i_version,
inode->i_generation,
l ? ", wh " : "", l, n);
return 0;
}
void au_dpri_inode(struct inode *inode)
{
struct au_iinfo *iinfo;
aufs_bindex_t bindex;
int err, hn;
err = do_pri_inode(-1, inode, -1, NULL);
if (err || !au_test_aufs(inode->i_sb))
return;
iinfo = au_ii(inode);
if (!iinfo)
return;
dpri("i-1: bstart %d, bend %d, gen %d\n",
iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode));
if (iinfo->ii_bstart < 0)
return;
hn = 0;
for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) {
hn = !!au_hn(iinfo->ii_hinode + bindex);
do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, hn,
iinfo->ii_hinode[0 + bindex].hi_whdentry);
}
}
void au_dpri_dalias(struct inode *inode)
{
struct dentry *d;
spin_lock(&inode->i_lock);
list_for_each_entry(d, &inode->i_dentry, d_alias)
au_dpri_dentry(d);
spin_unlock(&inode->i_lock);
}
static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry)
{
struct dentry *wh = NULL;
int hn;
if (!dentry || IS_ERR(dentry)) {
dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry));
return -1;
}
/* do not call dget_parent() here */
/* note: access d_xxx without d_lock */
dpri("d%d: %.*s?/%.*s, %s, cnt %d, flags 0x%x\n",
bindex,
AuDLNPair(dentry->d_parent), AuDLNPair(dentry),
dentry->d_sb ? au_sbtype(dentry->d_sb) : "??",
dentry->d_count, dentry->d_flags);
hn = -1;
if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) {
struct au_iinfo *iinfo = au_ii(dentry->d_inode);
if (iinfo) {
hn = !!au_hn(iinfo->ii_hinode + bindex);
wh = iinfo->ii_hinode[0 + bindex].hi_whdentry;
}
}
do_pri_inode(bindex, dentry->d_inode, hn, wh);
return 0;
}
void au_dpri_dentry(struct dentry *dentry)
{
struct au_dinfo *dinfo;
aufs_bindex_t bindex;
int err;
struct au_hdentry *hdp;
err = do_pri_dentry(-1, dentry);
if (err || !au_test_aufs(dentry->d_sb))
return;
dinfo = au_di(dentry);
if (!dinfo)
return;
dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d\n",
dinfo->di_bstart, dinfo->di_bend,
dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry));
if (dinfo->di_bstart < 0)
return;
hdp = dinfo->di_hdentry;
for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++)
do_pri_dentry(bindex, hdp[0 + bindex].hd_dentry);
}
static int do_pri_file(aufs_bindex_t bindex, struct file *file)
{
char a[32];
if (!file || IS_ERR(file)) {
dpri("f%d: err %ld\n", bindex, PTR_ERR(file));
return -1;
}
a[0] = 0;
if (bindex < 0
&& file->f_dentry
&& au_test_aufs(file->f_dentry->d_sb)
&& au_fi(file))
snprintf(a, sizeof(a), ", gen %d, mmapped %d",
au_figen(file), atomic_read(&au_fi(file)->fi_mmapped));
dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, v %llu, pos %llu%s\n",
bindex, file->f_mode, file->f_flags, (long)file_count(file),
file->f_version, file->f_pos, a);
if (file->f_dentry)
do_pri_dentry(bindex, file->f_dentry);
return 0;
}
void au_dpri_file(struct file *file)
{
struct au_finfo *finfo;
struct au_fidir *fidir;
struct au_hfile *hfile;
aufs_bindex_t bindex;
int err;
err = do_pri_file(-1, file);
if (err || !file->f_dentry || !au_test_aufs(file->f_dentry->d_sb))
return;
finfo = au_fi(file);
if (!finfo)
return;
if (finfo->fi_btop < 0)
return;
fidir = finfo->fi_hdir;
if (!fidir)
do_pri_file(finfo->fi_btop, finfo->fi_htop.hf_file);
else
for (bindex = finfo->fi_btop;
bindex >= 0 && bindex <= fidir->fd_bbot;
bindex++) {
hfile = fidir->fd_hfile + bindex;
do_pri_file(bindex, hfile ? hfile->hf_file : NULL);
}
}
static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br)
{
struct vfsmount *mnt;
struct super_block *sb;
if (!br || IS_ERR(br))
goto out;
mnt = br->br_mnt;
if (!mnt || IS_ERR(mnt))
goto out;
sb = mnt->mnt_sb;
if (!sb || IS_ERR(sb))
goto out;
dpri("s%d: {perm 0x%x, id %d, cnt %d, wbr %p}, "
"%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, "
"xino %d\n",
bindex, br->br_perm, br->br_id, atomic_read(&br->br_count),
br->br_wbr, au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev),
sb->s_flags, sb->s_count,
atomic_read(&sb->s_active), !!br->br_xino.xi_file);
return 0;
out:
dpri("s%d: err %ld\n", bindex, PTR_ERR(br));
return -1;
}
void au_dpri_sb(struct super_block *sb)
{
struct au_sbinfo *sbinfo;
aufs_bindex_t bindex;
int err;
/* to reuduce stack size */
struct {
struct vfsmount mnt;
struct au_branch fake;
} *a;
/* this function can be called from magic sysrq */
a = kzalloc(sizeof(*a), GFP_ATOMIC);
if (unlikely(!a)) {
dpri("no memory\n");
return;
}
a->mnt.mnt_sb = sb;
a->fake.br_perm = 0;
a->fake.br_mnt = &a->mnt;
a->fake.br_xino.xi_file = NULL;
atomic_set(&a->fake.br_count, 0);
smp_mb(); /* atomic_set */
err = do_pri_br(-1, &a->fake);
kfree(a);
dpri("dev 0x%x\n", sb->s_dev);
if (err || !au_test_aufs(sb))
return;
sbinfo = au_sbi(sb);
if (!sbinfo)
return;
dpri("nw %d, gen %u, kobj %d\n",
atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation,
atomic_read(&sbinfo->si_kobj.kref.refcount));
for (bindex = 0; bindex <= sbinfo->si_bend; bindex++)
do_pri_br(bindex, sbinfo->si_branch[0 + bindex]);
}
/* ---------------------------------------------------------------------- */
void au_dbg_sleep_jiffy(int jiffy)
{
while (jiffy)
jiffy = schedule_timeout_uninterruptible(jiffy);
}
void au_dbg_iattr(struct iattr *ia)
{
#define AuBit(name) if (ia->ia_valid & ATTR_ ## name) \
dpri(#name "\n")
AuBit(MODE);
AuBit(UID);
AuBit(GID);
AuBit(SIZE);
AuBit(ATIME);
AuBit(MTIME);
AuBit(CTIME);
AuBit(ATIME_SET);
AuBit(MTIME_SET);
AuBit(FORCE);
AuBit(ATTR_FLAG);
AuBit(KILL_SUID);
AuBit(KILL_SGID);
AuBit(FILE);
AuBit(KILL_PRIV);
AuBit(OPEN);
AuBit(TIMES_SET);
#undef AuBit
dpri("ia_file %p\n", ia->ia_file);
}
/* ---------------------------------------------------------------------- */
void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line)
{
struct inode *h_inode, *inode = dentry->d_inode;
struct dentry *h_dentry;
aufs_bindex_t bindex, bend, bi;
if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */)
return;
bend = au_dbend(dentry);
bi = au_ibend(inode);
if (bi < bend)
bend = bi;
bindex = au_dbstart(dentry);
bi = au_ibstart(inode);
if (bi > bindex)
bindex = bi;
for (; bindex <= bend; bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry)
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);
AuDbg("b%d, %s:%d\n", bindex, func, line);
AuDbgDentry(dentry);
AuDbgInode(inode);
if (!old)
au_debug(0);
BUG();
}
}
}
void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen)
{
struct dentry *parent;
parent = dget_parent(dentry);
AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode));
AuDebugOn(IS_ROOT(dentry));
AuDebugOn(au_digen_test(parent, sigen));
dput(parent);
}
void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen)
{
struct dentry *parent;
struct inode *inode;
parent = dget_parent(dentry);
inode = dentry->d_inode;
AuDebugOn(inode && S_ISDIR(dentry->d_inode->i_mode));
AuDebugOn(au_digen_test(parent, sigen));
dput(parent);
}
void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen)
{
int err, i, j;
struct au_dcsub_pages dpages;
struct au_dpage *dpage;
struct dentry **dentries;
err = au_dpages_init(&dpages, GFP_NOFS);
AuDebugOn(err);
err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1);
AuDebugOn(err);
for (i = dpages.ndpage - 1; !err && i >= 0; i--) {
dpage = dpages.dpages + i;
dentries = dpage->dentries;
for (j = dpage->ndentry - 1; !err && j >= 0; j--)
AuDebugOn(au_digen_test(dentries[j], sigen));
}
au_dpages_free(&dpages);
}
void au_dbg_verify_kthread(void)
{
if (au_wkq_test()) {
au_dbg_blocked();
/*
* It may be recursive, but udba=notify between two aufs mounts,
* where a single ro branch is shared, is not a problem.
*/
/* WARN_ON(1); */
}
}
/* ---------------------------------------------------------------------- */
void au_debug_sbinfo_init(struct au_sbinfo *sbinfo __maybe_unused)
{
#ifdef AuForceNoPlink
au_opt_clr(sbinfo->si_mntflags, PLINK);
#endif
#ifdef AuForceNoXino
au_opt_clr(sbinfo->si_mntflags, XINO);
#endif
#ifdef AuForceNoRefrof
au_opt_clr(sbinfo->si_mntflags, REFROF);
#endif
#ifdef AuForceHnotify
au_opt_set_udba(sbinfo->si_mntflags, UDBA_HNOTIFY);
#endif
#ifdef AuForceRd0
sbinfo->si_rdblk = 0;
sbinfo->si_rdhash = 0;
#endif
}
int __init au_debug_init(void)
{
aufs_bindex_t bindex;
struct au_vdir_destr destr;
bindex = -1;
AuDebugOn(bindex >= 0);
destr.len = -1;
AuDebugOn(destr.len < NAME_MAX);
#ifdef CONFIG_4KSTACKS
pr_warn("CONFIG_4KSTACKS is defined.\n");
#endif
#ifdef AuForceNoBrs
sysaufs_brs = 0;
#endif
return 0;
}

View File

@ -0,0 +1,243 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* debug print functions
*/
#ifndef __AUFS_DEBUG_H__
#define __AUFS_DEBUG_H__
#ifdef __KERNEL__
#include <asm/system.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/sysrq.h>
#ifdef CONFIG_AUFS_DEBUG
#define AuDebugOn(a) BUG_ON(a)
/* module parameter */
extern int aufs_debug;
static inline void au_debug(int n)
{
aufs_debug = n;
smp_mb();
}
static inline int au_debug_test(void)
{
return aufs_debug;
}
#else
#define AuDebugOn(a) do {} while (0)
AuStubVoid(au_debug, int n)
AuStubInt0(au_debug_test, void)
#endif /* CONFIG_AUFS_DEBUG */
/* ---------------------------------------------------------------------- */
/* debug print */
#define AuDbg(fmt, ...) do { \
if (au_debug_test()) \
pr_debug("DEBUG: " fmt, ##__VA_ARGS__); \
} while (0)
#define AuLabel(l) AuDbg(#l "\n")
#define AuIOErr(fmt, ...) pr_err("I/O Error, " fmt, ##__VA_ARGS__)
#define AuWarn1(fmt, ...) do { \
static unsigned char _c; \
if (!_c++) \
pr_warn(fmt, ##__VA_ARGS__); \
} while (0)
#define AuErr1(fmt, ...) do { \
static unsigned char _c; \
if (!_c++) \
pr_err(fmt, ##__VA_ARGS__); \
} while (0)
#define AuIOErr1(fmt, ...) do { \
static unsigned char _c; \
if (!_c++) \
AuIOErr(fmt, ##__VA_ARGS__); \
} while (0)
#define AuUnsupportMsg "This operation is not supported." \
" Please report this application to aufs-users ML."
#define AuUnsupport(fmt, ...) do { \
pr_err(AuUnsupportMsg "\n" fmt, ##__VA_ARGS__); \
dump_stack(); \
} while (0)
#define AuTraceErr(e) do { \
if (unlikely((e) < 0)) \
AuDbg("err %d\n", (int)(e)); \
} while (0)
#define AuTraceErrPtr(p) do { \
if (IS_ERR(p)) \
AuDbg("err %ld\n", PTR_ERR(p)); \
} while (0)
/* dirty macros for debug print, use with "%.*s" and caution */
#define AuLNPair(qstr) (qstr)->len, (qstr)->name
#define AuDLNPair(d) AuLNPair(&(d)->d_name)
/* ---------------------------------------------------------------------- */
struct au_sbinfo;
struct au_finfo;
struct dentry;
#ifdef CONFIG_AUFS_DEBUG
extern char *au_plevel;
struct au_nhash;
void au_dpri_whlist(struct au_nhash *whlist);
struct au_vdir;
void au_dpri_vdir(struct au_vdir *vdir);
struct inode;
void au_dpri_inode(struct inode *inode);
void au_dpri_dalias(struct inode *inode);
void au_dpri_dentry(struct dentry *dentry);
struct file;
void au_dpri_file(struct file *filp);
struct super_block;
void au_dpri_sb(struct super_block *sb);
void au_dbg_sleep_jiffy(int jiffy);
struct iattr;
void au_dbg_iattr(struct iattr *ia);
#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__)
void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line);
void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen);
void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen);
void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen);
void au_dbg_verify_kthread(void);
int __init au_debug_init(void);
void au_debug_sbinfo_init(struct au_sbinfo *sbinfo);
#define AuDbgWhlist(w) do { \
AuDbg(#w "\n"); \
au_dpri_whlist(w); \
} while (0)
#define AuDbgVdir(v) do { \
AuDbg(#v "\n"); \
au_dpri_vdir(v); \
} while (0)
#define AuDbgInode(i) do { \
AuDbg(#i "\n"); \
au_dpri_inode(i); \
} while (0)
#define AuDbgDAlias(i) do { \
AuDbg(#i "\n"); \
au_dpri_dalias(i); \
} while (0)
#define AuDbgDentry(d) do { \
AuDbg(#d "\n"); \
au_dpri_dentry(d); \
} while (0)
#define AuDbgFile(f) do { \
AuDbg(#f "\n"); \
au_dpri_file(f); \
} while (0)
#define AuDbgSb(sb) do { \
AuDbg(#sb "\n"); \
au_dpri_sb(sb); \
} while (0)
#define AuDbgSleep(sec) do { \
AuDbg("sleep %d sec\n", sec); \
ssleep(sec); \
} while (0)
#define AuDbgSleepJiffy(jiffy) do { \
AuDbg("sleep %d jiffies\n", jiffy); \
au_dbg_sleep_jiffy(jiffy); \
} while (0)
#define AuDbgIAttr(ia) do { \
AuDbg("ia_valid 0x%x\n", (ia)->ia_valid); \
au_dbg_iattr(ia); \
} while (0)
#define AuDbgSym(addr) do { \
char sym[KSYM_SYMBOL_LEN]; \
sprint_symbol(sym, (unsigned long)addr); \
AuDbg("%s\n", sym); \
} while (0)
#define AuInfoSym(addr) do { \
char sym[KSYM_SYMBOL_LEN]; \
sprint_symbol(sym, (unsigned long)addr); \
AuInfo("%s\n", sym); \
} while (0)
#else
AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry)
AuStubVoid(au_dbg_verify_dir_parent, struct dentry *dentry, unsigned int sigen)
AuStubVoid(au_dbg_verify_nondir_parent, struct dentry *dentry,
unsigned int sigen)
AuStubVoid(au_dbg_verify_gen, struct dentry *parent, unsigned int sigen)
AuStubVoid(au_dbg_verify_kthread, void)
AuStubInt0(__init au_debug_init, void)
AuStubVoid(au_debug_sbinfo_init, struct au_sbinfo *sbinfo)
#define AuDbgWhlist(w) do {} while (0)
#define AuDbgVdir(v) do {} while (0)
#define AuDbgInode(i) do {} while (0)
#define AuDbgDAlias(i) do {} while (0)
#define AuDbgDentry(d) do {} while (0)
#define AuDbgFile(f) do {} while (0)
#define AuDbgSb(sb) do {} while (0)
#define AuDbgSleep(sec) do {} while (0)
#define AuDbgSleepJiffy(jiffy) do {} while (0)
#define AuDbgIAttr(ia) do {} while (0)
#define AuDbgSym(addr) do {} while (0)
#define AuInfoSym(addr) do {} while (0)
#endif /* CONFIG_AUFS_DEBUG */
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_AUFS_MAGIC_SYSRQ
int __init au_sysrq_init(void);
void au_sysrq_fin(void);
#ifdef CONFIG_HW_CONSOLE
#define au_dbg_blocked() do { \
WARN_ON(1); \
handle_sysrq('w'); \
} while (0)
#else
AuStubVoid(au_dbg_blocked, void)
#endif
#else
AuStubInt0(__init au_sysrq_init, void)
AuStubVoid(au_sysrq_fin, void)
AuStubVoid(au_dbg_blocked, void)
#endif /* CONFIG_AUFS_MAGIC_SYSRQ */
#endif /* __KERNEL__ */
#endif /* __AUFS_DEBUG_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,237 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* lookup and dentry operations
*/
#ifndef __AUFS_DENTRY_H__
#define __AUFS_DENTRY_H__
#ifdef __KERNEL__
#include <linux/dcache.h>
#include "rwsem.h"
struct au_hdentry {
struct dentry *hd_dentry;
aufs_bindex_t hd_id;
};
struct au_dinfo {
atomic_t di_generation;
struct au_rwsem di_rwsem;
aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq;
struct au_hdentry *di_hdentry;
} ____cacheline_aligned_in_smp;
/* ---------------------------------------------------------------------- */
/* dentry.c */
extern const struct dentry_operations aufs_dop;
struct au_branch;
struct dentry *au_lkup_one(struct qstr *name, struct dentry *h_parent,
struct au_branch *br, struct nameidata *nd);
struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent,
struct au_branch *br);
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,
struct nameidata *nd);
int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex);
int au_refresh_dentry(struct dentry *dentry, struct dentry *parent);
int au_reval_dpath(struct dentry *dentry, unsigned int sigen);
/* dinfo.c */
void au_di_init_once(void *_di);
struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc);
void au_di_free(struct au_dinfo *dinfo);
void au_di_swap(struct au_dinfo *a, struct au_dinfo *b);
void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src);
int au_di_init(struct dentry *dentry);
void au_di_fin(struct dentry *dentry);
int au_di_realloc(struct au_dinfo *dinfo, int nbr);
void di_read_lock(struct dentry *d, int flags, unsigned int lsc);
void di_read_unlock(struct dentry *d, int flags);
void di_downgrade_lock(struct dentry *d, int flags);
void di_write_lock(struct dentry *d, unsigned int lsc);
void di_write_unlock(struct dentry *d);
void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir);
void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir);
void di_write_unlock2(struct dentry *d1, struct dentry *d2);
struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex);
struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex);
aufs_bindex_t au_dbtail(struct dentry *dentry);
aufs_bindex_t au_dbtaildir(struct dentry *dentry);
void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_dentry);
int au_digen_test(struct dentry *dentry, unsigned int sigen);
int au_dbrange_test(struct dentry *dentry);
void au_update_digen(struct dentry *dentry);
void au_update_dbrange(struct dentry *dentry, int do_put_zero);
void au_update_dbstart(struct dentry *dentry);
void au_update_dbend(struct dentry *dentry);
int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry);
/* ---------------------------------------------------------------------- */
static inline struct au_dinfo *au_di(struct dentry *dentry)
{
return dentry->d_fsdata;
}
/* ---------------------------------------------------------------------- */
/* lock subclass for dinfo */
enum {
AuLsc_DI_CHILD, /* child first */
AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hnotify */
AuLsc_DI_CHILD3, /* copyup dirs */
AuLsc_DI_PARENT,
AuLsc_DI_PARENT2,
AuLsc_DI_PARENT3,
AuLsc_DI_TMP /* temp for replacing dinfo */
};
/*
* di_read_lock_child, di_write_lock_child,
* di_read_lock_child2, di_write_lock_child2,
* di_read_lock_child3, di_write_lock_child3,
* di_read_lock_parent, di_write_lock_parent,
* di_read_lock_parent2, di_write_lock_parent2,
* di_read_lock_parent3, di_write_lock_parent3,
*/
#define AuReadLockFunc(name, lsc) \
static inline void di_read_lock_##name(struct dentry *d, int flags) \
{ di_read_lock(d, flags, AuLsc_DI_##lsc); }
#define AuWriteLockFunc(name, lsc) \
static inline void di_write_lock_##name(struct dentry *d) \
{ di_write_lock(d, AuLsc_DI_##lsc); }
#define AuRWLockFuncs(name, lsc) \
AuReadLockFunc(name, lsc) \
AuWriteLockFunc(name, lsc)
AuRWLockFuncs(child, CHILD);
AuRWLockFuncs(child2, CHILD2);
AuRWLockFuncs(child3, CHILD3);
AuRWLockFuncs(parent, PARENT);
AuRWLockFuncs(parent2, PARENT2);
AuRWLockFuncs(parent3, PARENT3);
#undef AuReadLockFunc
#undef AuWriteLockFunc
#undef AuRWLockFuncs
#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem)
#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem)
#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem)
/* ---------------------------------------------------------------------- */
/* todo: memory barrier? */
static inline unsigned int au_digen(struct dentry *d)
{
return atomic_read(&au_di(d)->di_generation);
}
static inline void au_h_dentry_init(struct au_hdentry *hdentry)
{
hdentry->hd_dentry = NULL;
}
static inline void au_hdput(struct au_hdentry *hd)
{
if (hd)
dput(hd->hd_dentry);
}
static inline aufs_bindex_t au_dbstart(struct dentry *dentry)
{
DiMustAnyLock(dentry);
return au_di(dentry)->di_bstart;
}
static inline aufs_bindex_t au_dbend(struct dentry *dentry)
{
DiMustAnyLock(dentry);
return au_di(dentry)->di_bend;
}
static inline aufs_bindex_t au_dbwh(struct dentry *dentry)
{
DiMustAnyLock(dentry);
return au_di(dentry)->di_bwh;
}
static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry)
{
DiMustAnyLock(dentry);
return au_di(dentry)->di_bdiropq;
}
/* todo: hard/soft set? */
static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex)
{
DiMustWriteLock(dentry);
au_di(dentry)->di_bstart = bindex;
}
static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex)
{
DiMustWriteLock(dentry);
au_di(dentry)->di_bend = bindex;
}
static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex)
{
DiMustWriteLock(dentry);
/* dbwh can be outside of bstart - bend range */
au_di(dentry)->di_bwh = bindex;
}
static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex)
{
DiMustWriteLock(dentry);
au_di(dentry)->di_bdiropq = bindex;
}
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_AUFS_HNOTIFY
static inline void au_digen_dec(struct dentry *d)
{
atomic_dec(&au_di(d)->di_generation);
}
static inline void au_hn_di_reinit(struct dentry *dentry)
{
dentry->d_fsdata = NULL;
}
#else
AuStubVoid(au_hn_di_reinit, struct dentry *dentry __maybe_unused)
#endif /* CONFIG_AUFS_HNOTIFY */
#endif /* __KERNEL__ */
#endif /* __AUFS_DENTRY_H__ */

View File

@ -0,0 +1,543 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* dentry private data
*/
#include "aufs.h"
void au_di_init_once(void *_dinfo)
{
struct au_dinfo *dinfo = _dinfo;
static struct lock_class_key aufs_di;
au_rw_init(&dinfo->di_rwsem);
au_rw_class(&dinfo->di_rwsem, &aufs_di);
}
struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc)
{
struct au_dinfo *dinfo;
int nbr, i;
dinfo = au_cache_alloc_dinfo();
if (unlikely(!dinfo))
goto out;
nbr = au_sbend(sb) + 1;
if (nbr <= 0)
nbr = 1;
dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS);
if (dinfo->di_hdentry) {
au_rw_write_lock_nested(&dinfo->di_rwsem, lsc);
dinfo->di_bstart = -1;
dinfo->di_bend = -1;
dinfo->di_bwh = -1;
dinfo->di_bdiropq = -1;
for (i = 0; i < nbr; i++)
dinfo->di_hdentry[i].hd_id = -1;
goto out;
}
au_cache_free_dinfo(dinfo);
dinfo = NULL;
out:
return dinfo;
}
void au_di_free(struct au_dinfo *dinfo)
{
struct au_hdentry *p;
aufs_bindex_t bend, bindex;
/* dentry may not be revalidated */
bindex = dinfo->di_bstart;
if (bindex >= 0) {
bend = dinfo->di_bend;
p = dinfo->di_hdentry + bindex;
while (bindex++ <= bend)
au_hdput(p++);
}
kfree(dinfo->di_hdentry);
au_cache_free_dinfo(dinfo);
}
void au_di_swap(struct au_dinfo *a, struct au_dinfo *b)
{
struct au_hdentry *p;
aufs_bindex_t bi;
AuRwMustWriteLock(&a->di_rwsem);
AuRwMustWriteLock(&b->di_rwsem);
#define DiSwap(v, name) \
do { \
v = a->di_##name; \
a->di_##name = b->di_##name; \
b->di_##name = v; \
} while (0)
DiSwap(p, hdentry);
DiSwap(bi, bstart);
DiSwap(bi, bend);
DiSwap(bi, bwh);
DiSwap(bi, bdiropq);
/* smp_mb(); */
#undef DiSwap
}
void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src)
{
AuRwMustWriteLock(&dst->di_rwsem);
AuRwMustWriteLock(&src->di_rwsem);
dst->di_bstart = src->di_bstart;
dst->di_bend = src->di_bend;
dst->di_bwh = src->di_bwh;
dst->di_bdiropq = src->di_bdiropq;
/* smp_mb(); */
}
int au_di_init(struct dentry *dentry)
{
int err;
struct super_block *sb;
struct au_dinfo *dinfo;
err = 0;
sb = dentry->d_sb;
dinfo = au_di_alloc(sb, AuLsc_DI_CHILD);
if (dinfo) {
atomic_set(&dinfo->di_generation, au_sigen(sb));
/* smp_mb(); */ /* atomic_set */
dentry->d_fsdata = dinfo;
} else
err = -ENOMEM;
return err;
}
void au_di_fin(struct dentry *dentry)
{
struct au_dinfo *dinfo;
dinfo = au_di(dentry);
AuRwDestroy(&dinfo->di_rwsem);
au_di_free(dinfo);
}
int au_di_realloc(struct au_dinfo *dinfo, int nbr)
{
int err, sz;
struct au_hdentry *hdp;
AuRwMustWriteLock(&dinfo->di_rwsem);
err = -ENOMEM;
sz = sizeof(*hdp) * (dinfo->di_bend + 1);
if (!sz)
sz = sizeof(*hdp);
hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS);
if (hdp) {
dinfo->di_hdentry = hdp;
err = 0;
}
return err;
}
/* ---------------------------------------------------------------------- */
static void do_ii_write_lock(struct inode *inode, unsigned int lsc)
{
switch (lsc) {
case AuLsc_DI_CHILD:
ii_write_lock_child(inode);
break;
case AuLsc_DI_CHILD2:
ii_write_lock_child2(inode);
break;
case AuLsc_DI_CHILD3:
ii_write_lock_child3(inode);
break;
case AuLsc_DI_PARENT:
ii_write_lock_parent(inode);
break;
case AuLsc_DI_PARENT2:
ii_write_lock_parent2(inode);
break;
case AuLsc_DI_PARENT3:
ii_write_lock_parent3(inode);
break;
default:
BUG();
}
}
static void do_ii_read_lock(struct inode *inode, unsigned int lsc)
{
switch (lsc) {
case AuLsc_DI_CHILD:
ii_read_lock_child(inode);
break;
case AuLsc_DI_CHILD2:
ii_read_lock_child2(inode);
break;
case AuLsc_DI_CHILD3:
ii_read_lock_child3(inode);
break;
case AuLsc_DI_PARENT:
ii_read_lock_parent(inode);
break;
case AuLsc_DI_PARENT2:
ii_read_lock_parent2(inode);
break;
case AuLsc_DI_PARENT3:
ii_read_lock_parent3(inode);
break;
default:
BUG();
}
}
void di_read_lock(struct dentry *d, int flags, unsigned int lsc)
{
au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc);
if (d->d_inode) {
if (au_ftest_lock(flags, IW))
do_ii_write_lock(d->d_inode, lsc);
else if (au_ftest_lock(flags, IR))
do_ii_read_lock(d->d_inode, lsc);
}
}
void di_read_unlock(struct dentry *d, int flags)
{
if (d->d_inode) {
if (au_ftest_lock(flags, IW)) {
au_dbg_verify_dinode(d);
ii_write_unlock(d->d_inode);
} else if (au_ftest_lock(flags, IR)) {
au_dbg_verify_dinode(d);
ii_read_unlock(d->d_inode);
}
}
au_rw_read_unlock(&au_di(d)->di_rwsem);
}
void di_downgrade_lock(struct dentry *d, int flags)
{
if (d->d_inode && au_ftest_lock(flags, IR))
ii_downgrade_lock(d->d_inode);
au_rw_dgrade_lock(&au_di(d)->di_rwsem);
}
void di_write_lock(struct dentry *d, unsigned int lsc)
{
au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc);
if (d->d_inode)
do_ii_write_lock(d->d_inode, lsc);
}
void di_write_unlock(struct dentry *d)
{
au_dbg_verify_dinode(d);
if (d->d_inode)
ii_write_unlock(d->d_inode);
au_rw_write_unlock(&au_di(d)->di_rwsem);
}
void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir)
{
AuDebugOn(d1 == d2
|| d1->d_inode == d2->d_inode
|| d1->d_sb != d2->d_sb);
if (isdir && au_test_subdir(d1, d2)) {
di_write_lock_child(d1);
di_write_lock_child2(d2);
} else {
/* there should be no races */
di_write_lock_child(d2);
di_write_lock_child2(d1);
}
}
void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir)
{
AuDebugOn(d1 == d2
|| d1->d_inode == d2->d_inode
|| d1->d_sb != d2->d_sb);
if (isdir && au_test_subdir(d1, d2)) {
di_write_lock_parent(d1);
di_write_lock_parent2(d2);
} else {
/* there should be no races */
di_write_lock_parent(d2);
di_write_lock_parent2(d1);
}
}
void di_write_unlock2(struct dentry *d1, struct dentry *d2)
{
di_write_unlock(d1);
if (d1->d_inode == d2->d_inode)
au_rw_write_unlock(&au_di(d2)->di_rwsem);
else
di_write_unlock(d2);
}
/* ---------------------------------------------------------------------- */
struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex)
{
struct dentry *d;
DiMustAnyLock(dentry);
if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry))
return NULL;
AuDebugOn(bindex < 0);
d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry;
AuDebugOn(d && d->d_count <= 0);
return d;
}
/*
* extended version of au_h_dptr().
* returns a hashed and positive h_dentry in bindex, NULL, or error.
*/
struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex)
{
struct dentry *h_dentry;
struct inode *inode, *h_inode;
inode = dentry->d_inode;
AuDebugOn(!inode);
h_dentry = NULL;
if (au_dbstart(dentry) <= bindex
&& bindex <= au_dbend(dentry))
h_dentry = au_h_dptr(dentry, bindex);
if (h_dentry && !au_d_hashed_positive(h_dentry)) {
dget(h_dentry);
goto out; /* success */
}
AuDebugOn(bindex < au_ibstart(inode));
AuDebugOn(au_ibend(inode) < bindex);
h_inode = au_h_iptr(inode, bindex);
h_dentry = d_find_alias(h_inode);
if (h_dentry) {
if (!IS_ERR(h_dentry)) {
if (!au_d_hashed_positive(h_dentry))
goto out; /* success */
dput(h_dentry);
} else
goto out;
}
if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) {
h_dentry = au_plink_lkup(inode, bindex);
AuDebugOn(!h_dentry);
if (!IS_ERR(h_dentry)) {
if (!au_d_hashed_positive(h_dentry))
goto out; /* success */
dput(h_dentry);
h_dentry = NULL;
}
}
out:
AuDbgDentry(h_dentry);
return h_dentry;
}
aufs_bindex_t au_dbtail(struct dentry *dentry)
{
aufs_bindex_t bend, bwh;
bend = au_dbend(dentry);
if (0 <= bend) {
bwh = au_dbwh(dentry);
if (!bwh)
return bwh;
if (0 < bwh && bwh < bend)
return bwh - 1;
}
return bend;
}
aufs_bindex_t au_dbtaildir(struct dentry *dentry)
{
aufs_bindex_t bend, bopq;
bend = au_dbtail(dentry);
if (0 <= bend) {
bopq = au_dbdiropq(dentry);
if (0 <= bopq && bopq < bend)
bend = bopq;
}
return bend;
}
/* ---------------------------------------------------------------------- */
void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_dentry)
{
struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex;
struct au_branch *br;
DiMustWriteLock(dentry);
au_hdput(hd);
hd->hd_dentry = h_dentry;
if (h_dentry) {
br = au_sbr(dentry->d_sb, bindex);
hd->hd_id = br->br_id;
}
}
int au_dbrange_test(struct dentry *dentry)
{
int err;
aufs_bindex_t bstart, bend;
err = 0;
bstart = au_dbstart(dentry);
bend = au_dbend(dentry);
if (bstart >= 0)
AuDebugOn(bend < 0 && bstart > bend);
else {
err = -EIO;
AuDebugOn(bend >= 0);
}
return err;
}
int au_digen_test(struct dentry *dentry, unsigned int sigen)
{
int err;
err = 0;
if (unlikely(au_digen(dentry) != sigen
|| au_iigen_test(dentry->d_inode, sigen)))
err = -EIO;
return err;
}
void au_update_digen(struct dentry *dentry)
{
atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb));
/* smp_mb(); */ /* atomic_set */
}
void au_update_dbrange(struct dentry *dentry, int do_put_zero)
{
struct au_dinfo *dinfo;
struct dentry *h_d;
struct au_hdentry *hdp;
DiMustWriteLock(dentry);
dinfo = au_di(dentry);
if (!dinfo || dinfo->di_bstart < 0)
return;
hdp = dinfo->di_hdentry;
if (do_put_zero) {
aufs_bindex_t bindex, bend;
bend = dinfo->di_bend;
for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) {
h_d = hdp[0 + bindex].hd_dentry;
if (h_d && !h_d->d_inode)
au_set_h_dptr(dentry, bindex, NULL);
}
}
dinfo->di_bstart = -1;
while (++dinfo->di_bstart <= dinfo->di_bend)
if (hdp[0 + dinfo->di_bstart].hd_dentry)
break;
if (dinfo->di_bstart > dinfo->di_bend) {
dinfo->di_bstart = -1;
dinfo->di_bend = -1;
return;
}
dinfo->di_bend++;
while (0 <= --dinfo->di_bend)
if (hdp[0 + dinfo->di_bend].hd_dentry)
break;
AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0);
}
void au_update_dbstart(struct dentry *dentry)
{
aufs_bindex_t bindex, bend;
struct dentry *h_dentry;
bend = au_dbend(dentry);
for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry)
continue;
if (h_dentry->d_inode) {
au_set_dbstart(dentry, bindex);
return;
}
au_set_h_dptr(dentry, bindex, NULL);
}
}
void au_update_dbend(struct dentry *dentry)
{
aufs_bindex_t bindex, bstart;
struct dentry *h_dentry;
bstart = au_dbstart(dentry);
for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) {
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry)
continue;
if (h_dentry->d_inode) {
au_set_dbend(dentry, bindex);
return;
}
au_set_h_dptr(dentry, bindex, NULL);
}
}
int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry)
{
aufs_bindex_t bindex, bend;
bend = au_dbend(dentry);
for (bindex = au_dbstart(dentry); bindex <= bend; bindex++)
if (au_h_dptr(dentry, bindex) == h_dentry)
return bindex;
return -1;
}

View File

@ -0,0 +1,623 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* directory operations
*/
#include <linux/fs_stack.h>
#include "aufs.h"
void au_add_nlink(struct inode *dir, struct inode *h_dir)
{
AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
dir->i_nlink += h_dir->i_nlink - 2;
if (h_dir->i_nlink < 2)
dir->i_nlink += 2;
}
void au_sub_nlink(struct inode *dir, struct inode *h_dir)
{
AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
dir->i_nlink -= h_dir->i_nlink - 2;
if (h_dir->i_nlink < 2)
dir->i_nlink -= 2;
}
loff_t au_dir_size(struct file *file, struct dentry *dentry)
{
loff_t sz;
aufs_bindex_t bindex, bend;
struct file *h_file;
struct dentry *h_dentry;
sz = 0;
if (file) {
AuDebugOn(!file->f_dentry);
AuDebugOn(!file->f_dentry->d_inode);
AuDebugOn(!S_ISDIR(file->f_dentry->d_inode->i_mode));
bend = au_fbend_dir(file);
for (bindex = au_fbstart(file);
bindex <= bend && sz < KMALLOC_MAX_SIZE;
bindex++) {
h_file = au_hf_dir(file, bindex);
if (h_file
&& h_file->f_dentry
&& h_file->f_dentry->d_inode)
sz += i_size_read(h_file->f_dentry->d_inode);
}
} else {
AuDebugOn(!dentry);
AuDebugOn(!dentry->d_inode);
AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode));
bend = au_dbtaildir(dentry);
for (bindex = au_dbstart(dentry);
bindex <= bend && sz < KMALLOC_MAX_SIZE;
bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (h_dentry && h_dentry->d_inode)
sz += i_size_read(h_dentry->d_inode);
}
}
if (sz < KMALLOC_MAX_SIZE)
sz = roundup_pow_of_two(sz);
if (sz > KMALLOC_MAX_SIZE)
sz = KMALLOC_MAX_SIZE;
else if (sz < NAME_MAX) {
BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX);
sz = AUFS_RDBLK_DEF;
}
return sz;
}
/* ---------------------------------------------------------------------- */
static int reopen_dir(struct file *file)
{
int err;
unsigned int flags;
aufs_bindex_t bindex, btail, bstart;
struct dentry *dentry, *h_dentry;
struct file *h_file;
/* open all lower dirs */
dentry = file->f_dentry;
bstart = au_dbstart(dentry);
for (bindex = au_fbstart(file); bindex < bstart; bindex++)
au_set_h_fptr(file, bindex, NULL);
au_set_fbstart(file, bstart);
btail = au_dbtaildir(dentry);
for (bindex = au_fbend_dir(file); btail < bindex; bindex--)
au_set_h_fptr(file, bindex, NULL);
au_set_fbend_dir(file, btail);
flags = vfsub_file_flags(file);
for (bindex = bstart; bindex <= btail; bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry)
continue;
h_file = au_hf_dir(file, bindex);
if (h_file)
continue;
h_file = au_h_open(dentry, bindex, flags, file);
err = PTR_ERR(h_file);
if (IS_ERR(h_file))
goto out; /* close all? */
au_set_h_fptr(file, bindex, h_file);
}
au_update_figen(file);
/* todo: necessary? */
/* file->f_ra = h_file->f_ra; */
err = 0;
out:
return err;
}
static int do_open_dir(struct file *file, int flags)
{
int err;
aufs_bindex_t bindex, btail;
struct dentry *dentry, *h_dentry;
struct file *h_file;
FiMustWriteLock(file);
dentry = file->f_dentry;
err = au_alive_dir(dentry);
if (unlikely(err))
goto out;
file->f_version = dentry->d_inode->i_version;
bindex = au_dbstart(dentry);
au_set_fbstart(file, bindex);
btail = au_dbtaildir(dentry);
au_set_fbend_dir(file, btail);
for (; !err && bindex <= btail; bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry)
continue;
h_file = au_h_open(dentry, bindex, flags, file);
if (IS_ERR(h_file)) {
err = PTR_ERR(h_file);
break;
}
au_set_h_fptr(file, bindex, h_file);
}
au_update_figen(file);
/* todo: necessary? */
/* file->f_ra = h_file->f_ra; */
if (!err)
return 0; /* success */
/* close all */
for (bindex = au_fbstart(file); bindex <= btail; bindex++)
au_set_h_fptr(file, bindex, NULL);
au_set_fbstart(file, -1);
au_set_fbend_dir(file, -1);
out:
return err;
}
static int aufs_open_dir(struct inode *inode __maybe_unused,
struct file *file)
{
int err;
struct super_block *sb;
struct au_fidir *fidir;
err = -ENOMEM;
sb = file->f_dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
fidir = au_fidir_alloc(sb);
if (fidir) {
err = au_do_open(file, do_open_dir, fidir);
if (unlikely(err))
kfree(fidir);
}
si_read_unlock(sb);
return err;
}
static int aufs_release_dir(struct inode *inode __maybe_unused,
struct file *file)
{
struct au_vdir *vdir_cache;
struct au_finfo *finfo;
struct au_fidir *fidir;
aufs_bindex_t bindex, bend;
finfo = au_fi(file);
fidir = finfo->fi_hdir;
if (fidir) {
/* remove me from sb->s_files */
file_sb_list_del(file);
vdir_cache = fidir->fd_vdir_cache; /* lock-free */
if (vdir_cache)
au_vdir_free(vdir_cache);
bindex = finfo->fi_btop;
if (bindex >= 0) {
/*
* calls fput() instead of filp_close(),
* since no dnotify or lock for the lower file.
*/
bend = fidir->fd_bbot;
for (; bindex <= bend; bindex++)
au_set_h_fptr(file, bindex, NULL);
}
kfree(fidir);
finfo->fi_hdir = NULL;
}
au_finfo_fin(file);
return 0;
}
/* ---------------------------------------------------------------------- */
static int au_do_flush_dir(struct file *file, fl_owner_t id)
{
int err;
aufs_bindex_t bindex, bend;
struct file *h_file;
err = 0;
bend = au_fbend_dir(file);
for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) {
h_file = au_hf_dir(file, bindex);
if (h_file)
err = vfsub_flush(h_file, id);
}
return err;
}
static int aufs_flush_dir(struct file *file, fl_owner_t id)
{
return au_do_flush(file, id, au_do_flush_dir);
}
/* ---------------------------------------------------------------------- */
static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync)
{
int err;
aufs_bindex_t bend, bindex;
struct inode *inode;
struct super_block *sb;
err = 0;
sb = dentry->d_sb;
inode = dentry->d_inode;
IMustLock(inode);
bend = au_dbend(dentry);
for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) {
struct path h_path;
if (au_test_ro(sb, bindex, inode))
continue;
h_path.dentry = au_h_dptr(dentry, bindex);
if (!h_path.dentry)
continue;
h_path.mnt = au_sbr_mnt(sb, bindex);
err = vfsub_fsync(NULL, &h_path, datasync);
}
return err;
}
static int au_do_fsync_dir(struct file *file, int datasync)
{
int err;
aufs_bindex_t bend, bindex;
struct file *h_file;
struct super_block *sb;
struct inode *inode;
err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1);
if (unlikely(err))
goto out;
sb = file->f_dentry->d_sb;
inode = file->f_dentry->d_inode;
bend = au_fbend_dir(file);
for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) {
h_file = au_hf_dir(file, bindex);
if (!h_file || au_test_ro(sb, bindex, inode))
continue;
err = vfsub_fsync(h_file, &h_file->f_path, datasync);
}
out:
return err;
}
/*
* @file may be NULL
*/
static int aufs_fsync_dir(struct file *file, int datasync)
{
int err;
struct dentry *dentry;
struct super_block *sb;
dentry = file->f_dentry;
IMustLock(dentry->d_inode);
err = 0;
sb = dentry->d_sb;
si_noflush_read_lock(sb);
if (file)
err = au_do_fsync_dir(file, datasync);
else {
di_write_lock_child(dentry);
err = au_do_fsync_dir_no_file(dentry, datasync);
}
au_cpup_attr_timesizes(dentry->d_inode);
di_write_unlock(dentry);
if (file)
fi_write_unlock(file);
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
static int aufs_readdir(struct file *file, void *dirent, filldir_t filldir)
{
int err;
struct dentry *dentry;
struct inode *inode, *h_inode;
struct super_block *sb;
dentry = file->f_dentry;
inode = dentry->d_inode;
IMustLock(inode);
sb = dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1);
if (unlikely(err))
goto out;
err = au_alive_dir(dentry);
if (!err)
err = au_vdir_init(file);
di_downgrade_lock(dentry, AuLock_IR);
if (unlikely(err))
goto out_unlock;
h_inode = au_h_iptr(inode, au_ibstart(inode));
if (!au_test_nfsd()) {
err = au_vdir_fill_de(file, dirent, filldir);
fsstack_copy_attr_atime(inode, h_inode);
} else {
/*
* nfsd filldir may call lookup_one_len(), vfs_getattr(),
* encode_fh() and others.
*/
atomic_inc(&h_inode->i_count);
di_read_unlock(dentry, AuLock_IR);
si_read_unlock(sb);
err = au_vdir_fill_de(file, dirent, filldir);
fsstack_copy_attr_atime(inode, h_inode);
fi_write_unlock(file);
iput(h_inode);
AuTraceErr(err);
return err;
}
out_unlock:
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
out:
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
#define AuTestEmpty_WHONLY 1
#define AuTestEmpty_CALLED (1 << 1)
#define AuTestEmpty_SHWH (1 << 2)
#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name)
#define au_fset_testempty(flags, name) \
do { (flags) |= AuTestEmpty_##name; } while (0)
#define au_fclr_testempty(flags, name) \
do { (flags) &= ~AuTestEmpty_##name; } while (0)
#ifndef CONFIG_AUFS_SHWH
#undef AuTestEmpty_SHWH
#define AuTestEmpty_SHWH 0
#endif
struct test_empty_arg {
struct au_nhash *whlist;
unsigned int flags;
int err;
aufs_bindex_t bindex;
};
static int test_empty_cb(void *__arg, const char *__name, int namelen,
loff_t offset __maybe_unused, u64 ino,
unsigned int d_type)
{
struct test_empty_arg *arg = __arg;
char *name = (void *)__name;
arg->err = 0;
au_fset_testempty(arg->flags, CALLED);
/* smp_mb(); */
if (name[0] == '.'
&& (namelen == 1 || (name[1] == '.' && namelen == 2)))
goto out; /* success */
if (namelen <= AUFS_WH_PFX_LEN
|| memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
if (au_ftest_testempty(arg->flags, WHONLY)
&& !au_nhash_test_known_wh(arg->whlist, name, namelen))
arg->err = -ENOTEMPTY;
goto out;
}
name += AUFS_WH_PFX_LEN;
namelen -= AUFS_WH_PFX_LEN;
if (!au_nhash_test_known_wh(arg->whlist, name, namelen))
arg->err = au_nhash_append_wh
(arg->whlist, name, namelen, ino, d_type, arg->bindex,
au_ftest_testempty(arg->flags, SHWH));
out:
/* smp_mb(); */
AuTraceErr(arg->err);
return arg->err;
}
static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
{
int err;
struct file *h_file;
h_file = au_h_open(dentry, arg->bindex,
O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE,
/*file*/NULL);
err = PTR_ERR(h_file);
if (IS_ERR(h_file))
goto out;
err = 0;
if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE)
&& !h_file->f_dentry->d_inode->i_nlink)
goto out_put;
do {
arg->err = 0;
au_fclr_testempty(arg->flags, CALLED);
/* smp_mb(); */
err = vfsub_readdir(h_file, test_empty_cb, arg);
if (err >= 0)
err = arg->err;
} while (!err && au_ftest_testempty(arg->flags, CALLED));
out_put:
fput(h_file);
au_sbr_put(dentry->d_sb, arg->bindex);
out:
return err;
}
struct do_test_empty_args {
int *errp;
struct dentry *dentry;
struct test_empty_arg *arg;
};
static void call_do_test_empty(void *args)
{
struct do_test_empty_args *a = args;
*a->errp = do_test_empty(a->dentry, a->arg);
}
static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
{
int err, wkq_err;
struct dentry *h_dentry;
struct inode *h_inode;
h_dentry = au_h_dptr(dentry, arg->bindex);
h_inode = h_dentry->d_inode;
/* todo: i_mode changes anytime? */
mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ);
mutex_unlock(&h_inode->i_mutex);
if (!err)
err = do_test_empty(dentry, arg);
else {
struct do_test_empty_args args = {
.errp = &err,
.dentry = dentry,
.arg = arg
};
unsigned int flags = arg->flags;
wkq_err = au_wkq_wait(call_do_test_empty, &args);
if (unlikely(wkq_err))
err = wkq_err;
arg->flags = flags;
}
return err;
}
int au_test_empty_lower(struct dentry *dentry)
{
int err;
unsigned int rdhash;
aufs_bindex_t bindex, bstart, btail;
struct au_nhash whlist;
struct test_empty_arg arg;
SiMustAnyLock(dentry->d_sb);
rdhash = au_sbi(dentry->d_sb)->si_rdhash;
if (!rdhash)
rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry));
err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS);
if (unlikely(err))
goto out;
arg.flags = 0;
arg.whlist = &whlist;
bstart = au_dbstart(dentry);
if (au_opt_test(au_mntflags(dentry->d_sb), SHWH))
au_fset_testempty(arg.flags, SHWH);
arg.bindex = bstart;
err = do_test_empty(dentry, &arg);
if (unlikely(err))
goto out_whlist;
au_fset_testempty(arg.flags, WHONLY);
btail = au_dbtaildir(dentry);
for (bindex = bstart + 1; !err && bindex <= btail; bindex++) {
struct dentry *h_dentry;
h_dentry = au_h_dptr(dentry, bindex);
if (h_dentry && h_dentry->d_inode) {
arg.bindex = bindex;
err = do_test_empty(dentry, &arg);
}
}
out_whlist:
au_nhash_wh_free(&whlist);
out:
return err;
}
int au_test_empty(struct dentry *dentry, struct au_nhash *whlist)
{
int err;
struct test_empty_arg arg;
aufs_bindex_t bindex, btail;
err = 0;
arg.whlist = whlist;
arg.flags = AuTestEmpty_WHONLY;
if (au_opt_test(au_mntflags(dentry->d_sb), SHWH))
au_fset_testempty(arg.flags, SHWH);
btail = au_dbtaildir(dentry);
for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) {
struct dentry *h_dentry;
h_dentry = au_h_dptr(dentry, bindex);
if (h_dentry && h_dentry->d_inode) {
arg.bindex = bindex;
err = sio_test_empty(dentry, &arg);
}
}
return err;
}
/* ---------------------------------------------------------------------- */
const struct file_operations aufs_dir_fop = {
.owner = THIS_MODULE,
.llseek = default_llseek,
.read = generic_read_dir,
.readdir = aufs_readdir,
.unlocked_ioctl = aufs_ioctl_dir,
#ifdef CONFIG_COMPAT
.compat_ioctl = aufs_compat_ioctl_dir,
#endif
.open = aufs_open_dir,
.release = aufs_release_dir,
.flush = aufs_flush_dir,
.fsync = aufs_fsync_dir
};

View File

@ -0,0 +1,137 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* directory operations
*/
#ifndef __AUFS_DIR_H__
#define __AUFS_DIR_H__
#ifdef __KERNEL__
#include <linux/fs.h>
/* ---------------------------------------------------------------------- */
/* need to be faster and smaller */
struct au_nhash {
unsigned int nh_num;
struct hlist_head *nh_head;
};
struct au_vdir_destr {
unsigned char len;
unsigned char name[0];
} __packed;
struct au_vdir_dehstr {
struct hlist_node hash;
struct au_vdir_destr *str;
} ____cacheline_aligned_in_smp;
struct au_vdir_de {
ino_t de_ino;
unsigned char de_type;
/* caution: packed */
struct au_vdir_destr de_str;
} __packed;
struct au_vdir_wh {
struct hlist_node wh_hash;
#ifdef CONFIG_AUFS_SHWH
ino_t wh_ino;
aufs_bindex_t wh_bindex;
unsigned char wh_type;
#else
aufs_bindex_t wh_bindex;
#endif
/* caution: packed */
struct au_vdir_destr wh_str;
} __packed;
union au_vdir_deblk_p {
unsigned char *deblk;
struct au_vdir_de *de;
};
struct au_vdir {
unsigned char **vd_deblk;
unsigned long vd_nblk;
struct {
unsigned long ul;
union au_vdir_deblk_p p;
} vd_last;
unsigned long vd_version;
unsigned int vd_deblk_sz;
unsigned long vd_jiffy;
} ____cacheline_aligned_in_smp;
/* ---------------------------------------------------------------------- */
/* dir.c */
extern const struct file_operations aufs_dir_fop;
void au_add_nlink(struct inode *dir, struct inode *h_dir);
void au_sub_nlink(struct inode *dir, struct inode *h_dir);
loff_t au_dir_size(struct file *file, struct dentry *dentry);
int au_test_empty_lower(struct dentry *dentry);
int au_test_empty(struct dentry *dentry, struct au_nhash *whlist);
/* vdir.c */
unsigned int au_rdhash_est(loff_t sz);
int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp);
void au_nhash_wh_free(struct au_nhash *whlist);
int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt,
int limit);
int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen);
int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino,
unsigned int d_type, aufs_bindex_t bindex,
unsigned char shwh);
void au_vdir_free(struct au_vdir *vdir);
int au_vdir_init(struct file *file);
int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir);
/* ioctl.c */
long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_AUFS_RDU
/* rdu.c */
long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_COMPAT
long au_rdu_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);
#endif
#else
static inline long au_rdu_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
return -EINVAL;
}
#ifdef CONFIG_COMPAT
static inline long au_rdu_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
return -EINVAL;
}
#endif
#endif
#endif /* __KERNEL__ */
#endif /* __AUFS_DIR_H__ */

View File

@ -0,0 +1,377 @@
/*
* Copyright (C) 2010-2012 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
*/
/*
* dynamically customizable operations for regular files
*/
#include "aufs.h"
#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop)
/*
* How large will these lists be?
* Usually just a few elements, 20-30 at most for each, I guess.
*/
static struct au_splhead dynop[AuDyLast];
static struct au_dykey *dy_gfind_get(struct au_splhead *spl, const void *h_op)
{
struct au_dykey *key, *tmp;
struct list_head *head;
key = NULL;
head = &spl->head;
rcu_read_lock();
list_for_each_entry_rcu(tmp, head, dk_list)
if (tmp->dk_op.dy_hop == h_op) {
key = tmp;
kref_get(&key->dk_kref);
break;
}
rcu_read_unlock();
return key;
}
static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key)
{
struct au_dykey **k, *found;
const void *h_op = key->dk_op.dy_hop;
int i;
found = NULL;
k = br->br_dykey;
for (i = 0; i < AuBrDynOp; i++)
if (k[i]) {
if (k[i]->dk_op.dy_hop == h_op) {
found = k[i];
break;
}
} else
break;
if (!found) {
spin_lock(&br->br_dykey_lock);
for (; i < AuBrDynOp; i++)
if (k[i]) {
if (k[i]->dk_op.dy_hop == h_op) {
found = k[i];
break;
}
} else {
k[i] = key;
break;
}
spin_unlock(&br->br_dykey_lock);
BUG_ON(i == AuBrDynOp); /* expand the array */
}
return found;
}
/* kref_get() if @key is already added */
static struct au_dykey *dy_gadd(struct au_splhead *spl, struct au_dykey *key)
{
struct au_dykey *tmp, *found;
struct list_head *head;
const void *h_op = key->dk_op.dy_hop;
found = NULL;
head = &spl->head;
spin_lock(&spl->spin);
list_for_each_entry(tmp, head, dk_list)
if (tmp->dk_op.dy_hop == h_op) {
kref_get(&tmp->dk_kref);
found = tmp;
break;
}
if (!found)
list_add_rcu(&key->dk_list, head);
spin_unlock(&spl->spin);
if (!found)
DyPrSym(key);
return found;
}
static void dy_free_rcu(struct rcu_head *rcu)
{
struct au_dykey *key;
key = container_of(rcu, struct au_dykey, dk_rcu);
DyPrSym(key);
kfree(key);
}
static void dy_free(struct kref *kref)
{
struct au_dykey *key;
struct au_splhead *spl;
key = container_of(kref, struct au_dykey, dk_kref);
spl = dynop + key->dk_op.dy_type;
au_spl_del_rcu(&key->dk_list, spl);
call_rcu(&key->dk_rcu, dy_free_rcu);
}
void au_dy_put(struct au_dykey *key)
{
kref_put(&key->dk_kref, dy_free);
}
/* ---------------------------------------------------------------------- */
#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *))
#ifdef CONFIG_AUFS_DEBUG
#define DyDbgDeclare(cnt) unsigned int cnt = 0
#define DyDbgInc(cnt) do { cnt++; } while (0)
#else
#define DyDbgDeclare(cnt) do {} while (0)
#define DyDbgInc(cnt) do {} while (0)
#endif
#define DySet(func, dst, src, h_op, h_sb) do { \
DyDbgInc(cnt); \
if (h_op->func) { \
if (src.func) \
dst.func = src.func; \
else \
AuDbg("%s %s\n", au_sbtype(h_sb), #func); \
} \
} while (0)
#define DySetForce(func, dst, src) do { \
AuDebugOn(!src.func); \
DyDbgInc(cnt); \
dst.func = src.func; \
} while (0)
#define DySetAop(func) \
DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb)
#define DySetAopForce(func) \
DySetForce(func, dyaop->da_op, aufs_aop)
static void dy_aop(struct au_dykey *key, const void *h_op,
struct super_block *h_sb __maybe_unused)
{
struct au_dyaop *dyaop = (void *)key;
const struct address_space_operations *h_aop = h_op;
DyDbgDeclare(cnt);
AuDbg("%s\n", au_sbtype(h_sb));
DySetAop(writepage);
DySetAopForce(readpage); /* force */
DySetAop(writepages);
DySetAop(set_page_dirty);
DySetAop(readpages);
DySetAop(write_begin);
DySetAop(write_end);
DySetAop(bmap);
DySetAop(invalidatepage);
DySetAop(releasepage);
DySetAop(freepage);
/* these two will be changed according to an aufs mount option */
DySetAop(direct_IO);
DySetAop(get_xip_mem);
DySetAop(migratepage);
DySetAop(launder_page);
DySetAop(is_partially_uptodate);
DySetAop(error_remove_page);
DyDbgSize(cnt, *h_aop);
dyaop->da_get_xip_mem = h_aop->get_xip_mem;
}
/* ---------------------------------------------------------------------- */
static void dy_bug(struct kref *kref)
{
BUG();
}
static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br)
{
struct au_dykey *key, *old;
struct au_splhead *spl;
struct op {
unsigned int sz;
void (*set)(struct au_dykey *key, const void *h_op,
struct super_block *h_sb __maybe_unused);
};
static const struct op a[] = {
[AuDy_AOP] = {
.sz = sizeof(struct au_dyaop),
.set = dy_aop
}
};
const struct op *p;
spl = dynop + op->dy_type;
key = dy_gfind_get(spl, op->dy_hop);
if (key)
goto out_add; /* success */
p = a + op->dy_type;
key = kzalloc(p->sz, GFP_NOFS);
if (unlikely(!key)) {
key = ERR_PTR(-ENOMEM);
goto out;
}
key->dk_op.dy_hop = op->dy_hop;
kref_init(&key->dk_kref);
p->set(key, op->dy_hop, br->br_mnt->mnt_sb);
old = dy_gadd(spl, key);
if (old) {
kfree(key);
key = old;
}
out_add:
old = dy_bradd(br, key);
if (old)
/* its ref-count should never be zero here */
kref_put(&key->dk_kref, dy_bug);
out:
return key;
}
/* ---------------------------------------------------------------------- */
/*
* Aufs prohibits O_DIRECT by defaut even if the branch supports it.
* This behaviour is neccessary to return an error from open(O_DIRECT) instead
* of the succeeding I/O. The dio mount option enables O_DIRECT and makes
* open(O_DIRECT) always succeed, but the succeeding I/O may return an error.
* See the aufs manual in detail.
*
* To keep this behaviour, aufs has to set NULL to ->get_xip_mem too, and the
* performance of fadvise() and madvise() may be affected.
*/
static void dy_adx(struct au_dyaop *dyaop, int do_dx)
{
if (!do_dx) {
dyaop->da_op.direct_IO = NULL;
dyaop->da_op.get_xip_mem = NULL;
} else {
dyaop->da_op.direct_IO = aufs_aop.direct_IO;
dyaop->da_op.get_xip_mem = aufs_aop.get_xip_mem;
if (!dyaop->da_get_xip_mem)
dyaop->da_op.get_xip_mem = NULL;
}
}
static struct au_dyaop *dy_aget(struct au_branch *br,
const struct address_space_operations *h_aop,
int do_dx)
{
struct au_dyaop *dyaop;
struct au_dynop op;
op.dy_type = AuDy_AOP;
op.dy_haop = h_aop;
dyaop = (void *)dy_get(&op, br);
if (IS_ERR(dyaop))
goto out;
dy_adx(dyaop, do_dx);
out:
return dyaop;
}
int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex,
struct inode *h_inode)
{
int err, do_dx;
struct super_block *sb;
struct au_branch *br;
struct au_dyaop *dyaop;
AuDebugOn(!S_ISREG(h_inode->i_mode));
IiMustWriteLock(inode);
sb = inode->i_sb;
br = au_sbr(sb, bindex);
do_dx = !!au_opt_test(au_mntflags(sb), DIO);
dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx);
err = PTR_ERR(dyaop);
if (IS_ERR(dyaop))
/* unnecessary to call dy_fput() */
goto out;
err = 0;
inode->i_mapping->a_ops = &dyaop->da_op;
out:
return err;
}
/*
* Is it safe to replace a_ops during the inode/file is in operation?
* Yes, I hope so.
*/
int au_dy_irefresh(struct inode *inode)
{
int err;
aufs_bindex_t bstart;
struct inode *h_inode;
err = 0;
if (S_ISREG(inode->i_mode)) {
bstart = au_ibstart(inode);
h_inode = au_h_iptr(inode, bstart);
err = au_dy_iaop(inode, bstart, h_inode);
}
return err;
}
void au_dy_arefresh(int do_dx)
{
struct au_splhead *spl;
struct list_head *head;
struct au_dykey *key;
spl = dynop + AuDy_AOP;
head = &spl->head;
spin_lock(&spl->spin);
list_for_each_entry(key, head, dk_list)
dy_adx((void *)key, do_dx);
spin_unlock(&spl->spin);
}
/* ---------------------------------------------------------------------- */
void __init au_dy_init(void)
{
int i;
/* make sure that 'struct au_dykey *' can be any type */
BUILD_BUG_ON(offsetof(struct au_dyaop, da_key));
for (i = 0; i < AuDyLast; i++)
au_spl_init(dynop + i);
}
void au_dy_fin(void)
{
int i;
for (i = 0; i < AuDyLast; i++)
WARN_ON(!list_empty(&dynop[i].head));
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2010-2012 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
*/
/*
* dynamically customizable operations (for regular files only)
*/
#ifndef __AUFS_DYNOP_H__
#define __AUFS_DYNOP_H__
#ifdef __KERNEL__
#include "inode.h"
enum {AuDy_AOP, AuDyLast};
struct au_dynop {
int dy_type;
union {
const void *dy_hop;
const struct address_space_operations *dy_haop;
};
};
struct au_dykey {
union {
struct list_head dk_list;
struct rcu_head dk_rcu;
};
struct au_dynop dk_op;
/*
* during I am in the branch local array, kref is gotten. when the
* branch is removed, kref is put.
*/
struct kref dk_kref;
};
/* stop unioning since their sizes are very different from each other */
struct au_dyaop {
struct au_dykey da_key;
struct address_space_operations da_op; /* not const */
int (*da_get_xip_mem)(struct address_space *, pgoff_t, int,
void **, unsigned long *);
};
/* ---------------------------------------------------------------------- */
/* dynop.c */
struct au_branch;
void au_dy_put(struct au_dykey *key);
int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex,
struct inode *h_inode);
int au_dy_irefresh(struct inode *inode);
void au_dy_arefresh(int do_dio);
void __init au_dy_init(void);
void au_dy_fin(void);
#endif /* __KERNEL__ */
#endif /* __AUFS_DYNOP_H__ */

View File

@ -0,0 +1,804 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* export via nfs
*/
#include <linux/exportfs.h>
#include <linux/mnt_namespace.h>
#include <linux/namei.h>
#include <linux/nsproxy.h>
#include <linux/random.h>
#include <linux/writeback.h>
#include "aufs.h"
union conv {
#ifdef CONFIG_AUFS_INO_T_64
__u32 a[2];
#else
__u32 a[1];
#endif
ino_t ino;
};
static ino_t decode_ino(__u32 *a)
{
union conv u;
BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a));
u.a[0] = a[0];
#ifdef CONFIG_AUFS_INO_T_64
u.a[1] = a[1];
#endif
return u.ino;
}
static void encode_ino(__u32 *a, ino_t ino)
{
union conv u;
u.ino = ino;
a[0] = u.a[0];
#ifdef CONFIG_AUFS_INO_T_64
a[1] = u.a[1];
#endif
}
/* NFS file handle */
enum {
Fh_br_id,
Fh_sigen,
#ifdef CONFIG_AUFS_INO_T_64
/* support 64bit inode number */
Fh_ino1,
Fh_ino2,
Fh_dir_ino1,
Fh_dir_ino2,
#else
Fh_ino1,
Fh_dir_ino1,
#endif
Fh_igen,
Fh_h_type,
Fh_tail,
Fh_ino = Fh_ino1,
Fh_dir_ino = Fh_dir_ino1
};
static int au_test_anon(struct dentry *dentry)
{
/* note: read d_flags without d_lock */
return !!(dentry->d_flags & DCACHE_DISCONNECTED);
}
/* ---------------------------------------------------------------------- */
/* inode generation external table */
void au_xigen_inc(struct inode *inode)
{
loff_t pos;
ssize_t sz;
__u32 igen;
struct super_block *sb;
struct au_sbinfo *sbinfo;
sb = inode->i_sb;
AuDebugOn(!au_opt_test(au_mntflags(sb), XINO));
sbinfo = au_sbi(sb);
pos = inode->i_ino;
pos *= sizeof(igen);
igen = inode->i_generation + 1;
sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen,
sizeof(igen), &pos);
if (sz == sizeof(igen))
return; /* success */
if (unlikely(sz >= 0))
AuIOErr("xigen error (%zd)\n", sz);
}
int au_xigen_new(struct inode *inode)
{
int err;
loff_t pos;
ssize_t sz;
struct super_block *sb;
struct au_sbinfo *sbinfo;
struct file *file;
err = 0;
/* todo: dirty, at mount time */
if (inode->i_ino == AUFS_ROOT_INO)
goto out;
sb = inode->i_sb;
SiMustAnyLock(sb);
if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
goto out;
err = -EFBIG;
pos = inode->i_ino;
if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) {
AuIOErr1("too large i%lld\n", pos);
goto out;
}
pos *= sizeof(inode->i_generation);
err = 0;
sbinfo = au_sbi(sb);
file = sbinfo->si_xigen;
BUG_ON(!file);
if (i_size_read(file->f_dentry->d_inode)
< pos + sizeof(inode->i_generation)) {
inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next);
sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation,
sizeof(inode->i_generation), &pos);
} else
sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation,
sizeof(inode->i_generation), &pos);
if (sz == sizeof(inode->i_generation))
goto out; /* success */
err = sz;
if (unlikely(sz >= 0)) {
err = -EIO;
AuIOErr("xigen error (%zd)\n", sz);
}
out:
return err;
}
int au_xigen_set(struct super_block *sb, struct file *base)
{
int err;
struct au_sbinfo *sbinfo;
struct file *file;
SiMustWriteLock(sb);
sbinfo = au_sbi(sb);
file = au_xino_create2(base, sbinfo->si_xigen);
err = PTR_ERR(file);
if (IS_ERR(file))
goto out;
err = 0;
if (sbinfo->si_xigen)
fput(sbinfo->si_xigen);
sbinfo->si_xigen = file;
out:
return err;
}
void au_xigen_clr(struct super_block *sb)
{
struct au_sbinfo *sbinfo;
SiMustWriteLock(sb);
sbinfo = au_sbi(sb);
if (sbinfo->si_xigen) {
fput(sbinfo->si_xigen);
sbinfo->si_xigen = NULL;
}
}
/* ---------------------------------------------------------------------- */
static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
ino_t dir_ino)
{
struct dentry *dentry, *d;
struct inode *inode;
unsigned int sigen;
dentry = NULL;
inode = ilookup(sb, ino);
if (!inode)
goto out;
dentry = ERR_PTR(-ESTALE);
sigen = au_sigen(sb);
if (unlikely(is_bad_inode(inode)
|| IS_DEADDIR(inode)
|| sigen != au_iigen(inode)))
goto out_iput;
dentry = NULL;
if (!dir_ino || S_ISDIR(inode->i_mode))
dentry = d_find_alias(inode);
else {
spin_lock(&inode->i_lock);
list_for_each_entry(d, &inode->i_dentry, d_alias) {
spin_lock(&d->d_lock);
if (!au_test_anon(d)
&& d->d_parent->d_inode->i_ino == dir_ino) {
dentry = dget_dlock(d);
spin_unlock(&d->d_lock);
break;
}
spin_unlock(&d->d_lock);
}
spin_unlock(&inode->i_lock);
}
if (unlikely(dentry && au_digen_test(dentry, sigen))) {
/* need to refresh */
dput(dentry);
dentry = NULL;
}
out_iput:
iput(inode);
out:
AuTraceErrPtr(dentry);
return dentry;
}
/* ---------------------------------------------------------------------- */
/* todo: dirty? */
/* if exportfs_decode_fh() passed vfsmount*, we could be happy */
struct au_compare_mnt_args {
/* input */
struct super_block *sb;
/* output */
struct vfsmount *mnt;
};
static int au_compare_mnt(struct vfsmount *mnt, void *arg)
{
struct au_compare_mnt_args *a = arg;
if (mnt->mnt_sb != a->sb)
return 0;
a->mnt = mntget(mnt);
return 1;
}
static struct vfsmount *au_mnt_get(struct super_block *sb)
{
int err;
struct au_compare_mnt_args args = {
.sb = sb
};
struct mnt_namespace *ns;
br_read_lock(vfsmount_lock);
/* no get/put ?? */
AuDebugOn(!current->nsproxy);
ns = current->nsproxy->mnt_ns;
AuDebugOn(!ns);
err = iterate_mounts(au_compare_mnt, &args, ns->root);
br_read_unlock(vfsmount_lock);
AuDebugOn(!err);
AuDebugOn(!args.mnt);
return args.mnt;
}
struct au_nfsd_si_lock {
unsigned int sigen;
aufs_bindex_t bindex, br_id;
unsigned char force_lock;
};
static int si_nfsd_read_lock(struct super_block *sb,
struct au_nfsd_si_lock *nsi_lock)
{
int err;
aufs_bindex_t bindex;
si_read_lock(sb, AuLock_FLUSH);
/* branch id may be wrapped around */
err = 0;
bindex = au_br_index(sb, nsi_lock->br_id);
if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb))
goto out; /* success */
err = -ESTALE;
bindex = -1;
if (!nsi_lock->force_lock)
si_read_unlock(sb);
out:
nsi_lock->bindex = bindex;
return err;
}
struct find_name_by_ino {
int called, found;
ino_t ino;
char *name;
int namelen;
};
static int
find_name_by_ino(void *arg, const char *name, int namelen, loff_t offset,
u64 ino, unsigned int d_type)
{
struct find_name_by_ino *a = arg;
a->called++;
if (a->ino != ino)
return 0;
memcpy(a->name, name, namelen);
a->namelen = namelen;
a->found = 1;
return 1;
}
static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino,
struct au_nfsd_si_lock *nsi_lock)
{
struct dentry *dentry, *parent;
struct file *file;
struct inode *dir;
struct find_name_by_ino arg;
int err;
parent = path->dentry;
if (nsi_lock)
si_read_unlock(parent->d_sb);
file = vfsub_dentry_open(path, au_dir_roflags);
dentry = (void *)file;
if (IS_ERR(file))
goto out;
dentry = ERR_PTR(-ENOMEM);
arg.name = __getname_gfp(GFP_NOFS);
if (unlikely(!arg.name))
goto out_file;
arg.ino = ino;
arg.found = 0;
do {
arg.called = 0;
/* smp_mb(); */
err = vfsub_readdir(file, find_name_by_ino, &arg);
} while (!err && !arg.found && arg.called);
dentry = ERR_PTR(err);
if (unlikely(err))
goto out_name;
dentry = ERR_PTR(-ENOENT);
if (!arg.found)
goto out_name;
/* do not call au_lkup_one() */
dir = parent->d_inode;
mutex_lock(&dir->i_mutex);
dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen);
mutex_unlock(&dir->i_mutex);
AuTraceErrPtr(dentry);
if (IS_ERR(dentry))
goto out_name;
AuDebugOn(au_test_anon(dentry));
if (unlikely(!dentry->d_inode)) {
dput(dentry);
dentry = ERR_PTR(-ENOENT);
}
out_name:
__putname(arg.name);
out_file:
fput(file);
out:
if (unlikely(nsi_lock
&& si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0))
if (!IS_ERR(dentry)) {
dput(dentry);
dentry = ERR_PTR(-ESTALE);
}
AuTraceErrPtr(dentry);
return dentry;
}
static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino,
ino_t dir_ino,
struct au_nfsd_si_lock *nsi_lock)
{
struct dentry *dentry;
struct path path;
if (dir_ino != AUFS_ROOT_INO) {
path.dentry = decode_by_ino(sb, dir_ino, 0);
dentry = path.dentry;
if (!path.dentry || IS_ERR(path.dentry))
goto out;
AuDebugOn(au_test_anon(path.dentry));
} else
path.dentry = dget(sb->s_root);
path.mnt = au_mnt_get(sb);
dentry = au_lkup_by_ino(&path, ino, nsi_lock);
path_put(&path);
out:
AuTraceErrPtr(dentry);
return dentry;
}
/* ---------------------------------------------------------------------- */
static int h_acceptable(void *expv, struct dentry *dentry)
{
return 1;
}
static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath,
char *buf, int len, struct super_block *sb)
{
char *p;
int n;
struct path path;
p = d_path(h_rootpath, buf, len);
if (IS_ERR(p))
goto out;
n = strlen(p);
path.mnt = h_rootpath->mnt;
path.dentry = h_parent;
p = d_path(&path, buf, len);
if (IS_ERR(p))
goto out;
if (n != 1)
p += n;
path.mnt = au_mnt_get(sb);
path.dentry = sb->s_root;
p = d_path(&path, buf, len - strlen(p));
mntput(path.mnt);
if (IS_ERR(p))
goto out;
if (n != 1)
p[strlen(p)] = '/';
out:
AuTraceErrPtr(p);
return p;
}
static
struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh,
int fh_len, struct au_nfsd_si_lock *nsi_lock)
{
struct dentry *dentry, *h_parent, *root;
struct super_block *h_sb;
char *pathname, *p;
struct vfsmount *h_mnt;
struct au_branch *br;
int err;
struct path path;
br = au_sbr(sb, nsi_lock->bindex);
h_mnt = br->br_mnt;
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),
fh_len - Fh_tail, fh[Fh_h_type],
h_acceptable, /*context*/NULL);
dentry = h_parent;
if (unlikely(!h_parent || IS_ERR(h_parent))) {
AuWarn1("%s decode_fh failed, %ld\n",
au_sbtype(h_sb), PTR_ERR(h_parent));
goto out;
}
dentry = NULL;
if (unlikely(au_test_anon(h_parent))) {
AuWarn1("%s decode_fh returned a disconnected dentry\n",
au_sbtype(h_sb));
goto out_h_parent;
}
dentry = ERR_PTR(-ENOMEM);
pathname = (void *)__get_free_page(GFP_NOFS);
if (unlikely(!pathname))
goto out_h_parent;
root = sb->s_root;
path.mnt = h_mnt;
di_read_lock_parent(root, !AuLock_IR);
path.dentry = au_h_dptr(root, nsi_lock->bindex);
di_read_unlock(root, !AuLock_IR);
p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb);
dentry = (void *)p;
if (IS_ERR(p))
goto out_pathname;
si_read_unlock(sb);
err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
dentry = ERR_PTR(err);
if (unlikely(err))
goto out_relock;
dentry = ERR_PTR(-ENOENT);
AuDebugOn(au_test_anon(path.dentry));
if (unlikely(!path.dentry->d_inode))
goto out_path;
if (ino != path.dentry->d_inode->i_ino)
dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL);
else
dentry = dget(path.dentry);
out_path:
path_put(&path);
out_relock:
if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0))
if (!IS_ERR(dentry)) {
dput(dentry);
dentry = ERR_PTR(-ESTALE);
}
out_pathname:
free_page((unsigned long)pathname);
out_h_parent:
dput(h_parent);
out:
AuTraceErrPtr(dentry);
return dentry;
}
/* ---------------------------------------------------------------------- */
static struct dentry *
aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
int fh_type)
{
struct dentry *dentry;
__u32 *fh = fid->raw;
struct au_branch *br;
ino_t ino, dir_ino;
struct au_nfsd_si_lock nsi_lock = {
.force_lock = 0
};
dentry = ERR_PTR(-ESTALE);
/* it should never happen, but the file handle is unreliable */
if (unlikely(fh_len < Fh_tail))
goto out;
nsi_lock.sigen = fh[Fh_sigen];
nsi_lock.br_id = fh[Fh_br_id];
/* branch id may be wrapped around */
br = NULL;
if (unlikely(si_nfsd_read_lock(sb, &nsi_lock)))
goto out;
nsi_lock.force_lock = 1;
/* is this inode still cached? */
ino = decode_ino(fh + Fh_ino);
/* it should never happen */
if (unlikely(ino == AUFS_ROOT_INO))
goto out;
dir_ino = decode_ino(fh + Fh_dir_ino);
dentry = decode_by_ino(sb, ino, dir_ino);
if (IS_ERR(dentry))
goto out_unlock;
if (dentry)
goto accept;
/* is the parent dir cached? */
br = au_sbr(sb, nsi_lock.bindex);
atomic_inc(&br->br_count);
dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock);
if (IS_ERR(dentry))
goto out_unlock;
if (dentry)
goto accept;
/* lookup path */
dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock);
if (IS_ERR(dentry))
goto out_unlock;
if (unlikely(!dentry))
/* todo?: make it ESTALE */
goto out_unlock;
accept:
if (!au_digen_test(dentry, au_sigen(sb))
&& dentry->d_inode->i_generation == fh[Fh_igen])
goto out_unlock; /* success */
dput(dentry);
dentry = ERR_PTR(-ESTALE);
out_unlock:
if (br)
atomic_dec(&br->br_count);
si_read_unlock(sb);
out:
AuTraceErrPtr(dentry);
return dentry;
}
#if 0 /* reserved for future use */
/* support subtreecheck option */
static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid,
int fh_len, int fh_type)
{
struct dentry *parent;
__u32 *fh = fid->raw;
ino_t dir_ino;
dir_ino = decode_ino(fh + Fh_dir_ino);
parent = decode_by_ino(sb, dir_ino, 0);
if (IS_ERR(parent))
goto out;
if (!parent)
parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]),
dir_ino, fh, fh_len);
out:
AuTraceErrPtr(parent);
return parent;
}
#endif
/* ---------------------------------------------------------------------- */
static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,
int connectable)
{
int err;
aufs_bindex_t bindex, bend;
struct super_block *sb, *h_sb;
struct inode *inode;
struct dentry *parent, *h_parent;
struct au_branch *br;
AuDebugOn(au_test_anon(dentry));
parent = NULL;
err = -ENOSPC;
if (unlikely(*max_len <= Fh_tail)) {
AuWarn1("NFSv2 client (max_len %d)?\n", *max_len);
goto out;
}
err = FILEID_ROOT;
if (IS_ROOT(dentry)) {
AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO);
goto out;
}
h_parent = NULL;
err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR | AuLock_GEN);
if (unlikely(err))
goto out;
inode = dentry->d_inode;
AuDebugOn(!inode);
sb = dentry->d_sb;
#ifdef CONFIG_AUFS_DEBUG
if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
AuWarn1("NFS-exporting requires xino\n");
#endif
err = -EIO;
parent = dget_parent(dentry);
di_read_lock_parent(parent, !AuLock_IR);
bend = au_dbtaildir(parent);
for (bindex = au_dbstart(parent); bindex <= bend; bindex++) {
h_parent = au_h_dptr(parent, bindex);
if (h_parent) {
dget(h_parent);
break;
}
}
if (unlikely(!h_parent))
goto out_unlock;
err = -EPERM;
br = au_sbr(sb, bindex);
h_sb = br->br_mnt->mnt_sb;
if (unlikely(!h_sb->s_export_op)) {
AuErr1("%s branch is not exportable\n", au_sbtype(h_sb));
goto out_dput;
}
fh[Fh_br_id] = br->br_id;
fh[Fh_sigen] = au_sigen(sb);
encode_ino(fh + Fh_ino, inode->i_ino);
encode_ino(fh + Fh_dir_ino, parent->d_inode->i_ino);
fh[Fh_igen] = inode->i_generation;
*max_len -= Fh_tail;
fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail),
max_len,
/*connectable or subtreecheck*/0);
err = fh[Fh_h_type];
*max_len += Fh_tail;
/* todo: macros? */
if (err != 255)
err = 99;
else
AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb));
out_dput:
dput(h_parent);
out_unlock:
di_read_unlock(parent, !AuLock_IR);
dput(parent);
aufs_read_unlock(dentry, AuLock_IR);
out:
if (unlikely(err < 0))
err = 255;
return err;
}
/* ---------------------------------------------------------------------- */
static int aufs_commit_metadata(struct inode *inode)
{
int err;
aufs_bindex_t bindex;
struct super_block *sb;
struct inode *h_inode;
int (*f)(struct inode *inode);
sb = inode->i_sb;
si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
ii_write_lock_child(inode);
bindex = au_ibstart(inode);
AuDebugOn(bindex < 0);
h_inode = au_h_iptr(inode, bindex);
f = h_inode->i_sb->s_export_op->commit_metadata;
if (f)
err = f(h_inode);
else {
struct writeback_control wbc = {
.sync_mode = WB_SYNC_ALL,
.nr_to_write = 0 /* metadata only */
};
err = sync_inode(h_inode, &wbc);
}
au_cpup_attr_timesizes(inode);
ii_write_unlock(inode);
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
static struct export_operations aufs_export_op = {
.fh_to_dentry = aufs_fh_to_dentry,
/* .fh_to_parent = aufs_fh_to_parent, */
.encode_fh = aufs_encode_fh,
.commit_metadata = aufs_commit_metadata
};
void au_export_init(struct super_block *sb)
{
struct au_sbinfo *sbinfo;
__u32 u;
sb->s_export_op = &aufs_export_op;
sbinfo = au_sbi(sb);
sbinfo->si_xigen = NULL;
get_random_bytes(&u, sizeof(u));
BUILD_BUG_ON(sizeof(u) != sizeof(int));
atomic_set(&sbinfo->si_xigen_next, u);
}

View File

@ -0,0 +1,737 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* file and vm operations
*/
#include <linux/fs_stack.h>
#include <linux/mman.h>
#include <linux/security.h>
#include "aufs.h"
int au_do_open_nondir(struct file *file, int flags)
{
int err;
aufs_bindex_t bindex;
struct file *h_file;
struct dentry *dentry;
struct au_finfo *finfo;
FiMustWriteLock(file);
dentry = file->f_dentry;
err = au_d_alive(dentry);
if (unlikely(err))
goto out;
finfo = au_fi(file);
memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop));
atomic_set(&finfo->fi_mmapped, 0);
bindex = au_dbstart(dentry);
h_file = au_h_open(dentry, bindex, flags, file);
if (IS_ERR(h_file))
err = PTR_ERR(h_file);
else {
au_set_fbstart(file, bindex);
au_set_h_fptr(file, bindex, h_file);
au_update_figen(file);
/* todo: necessary? */
/* file->f_ra = h_file->f_ra; */
}
out:
return err;
}
static int aufs_open_nondir(struct inode *inode __maybe_unused,
struct file *file)
{
int err;
struct super_block *sb;
AuDbg("%.*s, f_flags 0x%x, f_mode 0x%x\n",
AuDLNPair(file->f_dentry), vfsub_file_flags(file),
file->f_mode);
sb = file->f_dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
err = au_do_open(file, au_do_open_nondir, /*fidir*/NULL);
si_read_unlock(sb);
return err;
}
int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file)
{
struct au_finfo *finfo;
aufs_bindex_t bindex;
finfo = au_fi(file);
bindex = finfo->fi_btop;
if (bindex >= 0) {
/* remove me from sb->s_files */
file_sb_list_del(file);
au_set_h_fptr(file, bindex, NULL);
}
au_finfo_fin(file);
return 0;
}
/* ---------------------------------------------------------------------- */
static int au_do_flush_nondir(struct file *file, fl_owner_t id)
{
int err;
struct file *h_file;
err = 0;
h_file = au_hf_top(file);
if (h_file)
err = vfsub_flush(h_file, id);
return err;
}
static int aufs_flush_nondir(struct file *file, fl_owner_t id)
{
return au_do_flush(file, id, au_do_flush_nondir);
}
/* ---------------------------------------------------------------------- */
/*
* read and write functions acquire [fdi]_rwsem once, but release before
* mmap_sem. This is because to stop a race condition between mmap(2).
* Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping
* si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in
* read functions after [fdi]_rwsem are released, but it should be harmless.
*/
static ssize_t aufs_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
ssize_t err;
struct dentry *dentry;
struct file *h_file;
struct super_block *sb;
dentry = file->f_dentry;
sb = dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
if (unlikely(err))
goto out;
h_file = au_hf_top(file);
get_file(h_file);
di_read_unlock(dentry, AuLock_IR);
fi_read_unlock(file);
/* filedata may be obsoleted by concurrent copyup, but no problem */
err = vfsub_read_u(h_file, buf, count, ppos);
/* todo: necessary? */
/* file->f_ra = h_file->f_ra; */
/* update without lock, I don't think it a problem */
fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);
fput(h_file);
out:
si_read_unlock(sb);
return err;
}
/*
* todo: very ugly
* it locks both of i_mutex and si_rwsem for read in safe.
* if the plink maintenance mode continues forever (that is the problem),
* may loop forever.
*/
static void au_mtx_and_read_lock(struct inode *inode)
{
int err;
struct super_block *sb = inode->i_sb;
while (1) {
mutex_lock(&inode->i_mutex);
err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
if (!err)
break;
mutex_unlock(&inode->i_mutex);
si_read_lock(sb, AuLock_NOPLMW);
si_read_unlock(sb);
}
}
static ssize_t aufs_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
ssize_t err;
struct au_pin pin;
struct dentry *dentry;
struct super_block *sb;
struct inode *inode;
struct file *h_file;
char __user *buf = (char __user *)ubuf;
dentry = file->f_dentry;
sb = dentry->d_sb;
inode = dentry->d_inode;
au_mtx_and_read_lock(inode);
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
if (unlikely(err))
goto out;
err = au_ready_to_write(file, -1, &pin);
di_downgrade_lock(dentry, AuLock_IR);
if (unlikely(err)) {
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
goto out;
}
h_file = au_hf_top(file);
get_file(h_file);
au_unpin(&pin);
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
err = vfsub_write_u(h_file, buf, count, ppos);
ii_write_lock_child(inode);
au_cpup_attr_timesizes(inode);
inode->i_mode = h_file->f_dentry->d_inode->i_mode;
ii_write_unlock(inode);
fput(h_file);
out:
si_read_unlock(sb);
mutex_unlock(&inode->i_mutex);
return err;
}
static ssize_t au_do_aio(struct file *h_file, int rw, struct kiocb *kio,
const struct iovec *iov, unsigned long nv, loff_t pos)
{
ssize_t err;
struct file *file;
ssize_t (*func)(struct kiocb *, const struct iovec *, unsigned long,
loff_t);
err = security_file_permission(h_file, rw);
if (unlikely(err))
goto out;
err = -ENOSYS;
func = NULL;
if (rw == MAY_READ)
func = h_file->f_op->aio_read;
else if (rw == MAY_WRITE)
func = h_file->f_op->aio_write;
if (func) {
file = kio->ki_filp;
kio->ki_filp = h_file;
lockdep_off();
err = func(kio, iov, nv, pos);
lockdep_on();
kio->ki_filp = file;
} else
/* currently there is no such fs */
WARN_ON_ONCE(1);
out:
return err;
}
static ssize_t aufs_aio_read(struct kiocb *kio, const struct iovec *iov,
unsigned long nv, loff_t pos)
{
ssize_t err;
struct file *file, *h_file;
struct dentry *dentry;
struct super_block *sb;
file = kio->ki_filp;
dentry = file->f_dentry;
sb = dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
if (unlikely(err))
goto out;
h_file = au_hf_top(file);
get_file(h_file);
di_read_unlock(dentry, AuLock_IR);
fi_read_unlock(file);
err = au_do_aio(h_file, MAY_READ, kio, iov, nv, pos);
/* todo: necessary? */
/* file->f_ra = h_file->f_ra; */
/* update without lock, I don't think it a problem */
fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);
fput(h_file);
out:
si_read_unlock(sb);
return err;
}
static ssize_t aufs_aio_write(struct kiocb *kio, const struct iovec *iov,
unsigned long nv, loff_t pos)
{
ssize_t err;
struct au_pin pin;
struct dentry *dentry;
struct inode *inode;
struct file *file, *h_file;
struct super_block *sb;
file = kio->ki_filp;
dentry = file->f_dentry;
sb = dentry->d_sb;
inode = dentry->d_inode;
au_mtx_and_read_lock(inode);
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
if (unlikely(err))
goto out;
err = au_ready_to_write(file, -1, &pin);
di_downgrade_lock(dentry, AuLock_IR);
if (unlikely(err)) {
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
goto out;
}
h_file = au_hf_top(file);
get_file(h_file);
au_unpin(&pin);
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
err = au_do_aio(h_file, MAY_WRITE, kio, iov, nv, pos);
ii_write_lock_child(inode);
au_cpup_attr_timesizes(inode);
inode->i_mode = h_file->f_dentry->d_inode->i_mode;
ii_write_unlock(inode);
fput(h_file);
out:
si_read_unlock(sb);
mutex_unlock(&inode->i_mutex);
return err;
}
static ssize_t aufs_splice_read(struct file *file, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags)
{
ssize_t err;
struct file *h_file;
struct dentry *dentry;
struct super_block *sb;
dentry = file->f_dentry;
sb = dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
if (unlikely(err))
goto out;
err = -EINVAL;
h_file = au_hf_top(file);
get_file(h_file);
if (au_test_loopback_kthread()) {
au_warn_loopback(h_file->f_dentry->d_sb);
if (file->f_mapping != h_file->f_mapping) {
file->f_mapping = h_file->f_mapping;
smp_mb(); /* unnecessary? */
}
}
di_read_unlock(dentry, AuLock_IR);
fi_read_unlock(file);
err = vfsub_splice_to(h_file, ppos, pipe, len, flags);
/* todo: necessasry? */
/* file->f_ra = h_file->f_ra; */
/* update without lock, I don't think it a problem */
fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);
fput(h_file);
out:
si_read_unlock(sb);
return err;
}
static ssize_t
aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos,
size_t len, unsigned int flags)
{
ssize_t err;
struct au_pin pin;
struct dentry *dentry;
struct inode *inode;
struct file *h_file;
struct super_block *sb;
dentry = file->f_dentry;
sb = dentry->d_sb;
inode = dentry->d_inode;
au_mtx_and_read_lock(inode);
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
if (unlikely(err))
goto out;
err = au_ready_to_write(file, -1, &pin);
di_downgrade_lock(dentry, AuLock_IR);
if (unlikely(err)) {
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
goto out;
}
h_file = au_hf_top(file);
get_file(h_file);
au_unpin(&pin);
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
err = vfsub_splice_from(pipe, h_file, ppos, len, flags);
ii_write_lock_child(inode);
au_cpup_attr_timesizes(inode);
inode->i_mode = h_file->f_dentry->d_inode->i_mode;
ii_write_unlock(inode);
fput(h_file);
out:
si_read_unlock(sb);
mutex_unlock(&inode->i_mutex);
return err;
}
/* ---------------------------------------------------------------------- */
/*
* The locking order around current->mmap_sem.
* - in most and regular cases
* file I/O syscall -- aufs_read() or something
* -- si_rwsem for read -- mmap_sem
* (Note that [fdi]i_rwsem are released before mmap_sem).
* - in mmap case
* mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem
* This AB-BA order is definitly bad, but is not a problem since "si_rwsem for
* read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in
* file I/O. Aufs needs to stop lockdep in aufs_mmap() though.
* It means that when aufs acquires si_rwsem for write, the process should never
* acquire mmap_sem.
*
* Actually aufs_readdir() holds [fdi]i_rwsem before mmap_sem, but this is not a
* problem either since any directory is not able to be mmap-ed.
* The similar scenario is applied to aufs_readlink() too.
*/
/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */
#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b)
static unsigned long au_arch_prot_conv(unsigned long flags)
{
/* currently ppc64 only */
#ifdef CONFIG_PPC64
/* cf. linux/arch/powerpc/include/asm/mman.h */
AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO);
return AuConv_VM_PROT(flags, SAO);
#else
AuDebugOn(arch_calc_vm_prot_bits(-1));
return 0;
#endif
}
static unsigned long au_prot_conv(unsigned long flags)
{
return AuConv_VM_PROT(flags, READ)
| AuConv_VM_PROT(flags, WRITE)
| AuConv_VM_PROT(flags, EXEC)
| au_arch_prot_conv(flags);
}
/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */
#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b)
static unsigned long au_flag_conv(unsigned long flags)
{
return AuConv_VM_MAP(flags, GROWSDOWN)
| AuConv_VM_MAP(flags, DENYWRITE)
| AuConv_VM_MAP(flags, EXECUTABLE)
| AuConv_VM_MAP(flags, LOCKED);
}
static int aufs_mmap(struct file *file, struct vm_area_struct *vma)
{
int err;
unsigned long prot;
aufs_bindex_t bstart;
const unsigned char wlock
= (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED);
struct dentry *dentry;
struct super_block *sb;
struct file *h_file;
struct au_branch *br;
struct au_pin pin;
AuDbgVmRegion(file, vma);
dentry = file->f_dentry;
sb = dentry->d_sb;
lockdep_off();
si_read_lock(sb, AuLock_NOPLMW);
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
if (unlikely(err))
goto out;
if (wlock) {
err = au_ready_to_write(file, -1, &pin);
di_write_unlock(dentry);
if (unlikely(err)) {
fi_write_unlock(file);
goto out;
}
au_unpin(&pin);
} else
di_write_unlock(dentry);
bstart = au_fbstart(file);
br = au_sbr(sb, bstart);
h_file = au_hf_top(file);
get_file(h_file);
au_set_mmapped(file);
fi_write_unlock(file);
lockdep_on();
au_vm_file_reset(vma, h_file);
prot = au_prot_conv(vma->vm_flags);
err = security_file_mmap(h_file, /*reqprot*/prot, prot,
au_flag_conv(vma->vm_flags), vma->vm_start, 0);
if (!err)
err = h_file->f_op->mmap(h_file, vma);
if (unlikely(err))
goto out_reset;
au_vm_prfile_set(vma, file);
/* update without lock, I don't think it a problem */
fsstack_copy_attr_atime(file->f_dentry->d_inode,
h_file->f_dentry->d_inode);
goto out_fput; /* success */
out_reset:
au_unset_mmapped(file);
au_vm_file_reset(vma, file);
out_fput:
fput(h_file);
lockdep_off();
out:
si_read_unlock(sb);
lockdep_on();
AuTraceErr(err);
return err;
}
/* ---------------------------------------------------------------------- */
static int aufs_fsync_nondir(struct file *file, int datasync)
{
int err;
struct au_pin pin;
struct dentry *dentry;
struct inode *inode;
struct file *h_file;
struct super_block *sb;
dentry = file->f_dentry;
inode = dentry->d_inode;
IMustLock(file->f_mapping->host);
if (inode != file->f_mapping->host) {
mutex_unlock(&file->f_mapping->host->i_mutex);
mutex_lock(&inode->i_mutex);
}
IMustLock(inode);
sb = dentry->d_sb;
err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
if (unlikely(err))
goto out;
err = 0; /* -EBADF; */ /* posix? */
if (unlikely(!(file->f_mode & FMODE_WRITE)))
goto out_si;
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
if (unlikely(err))
goto out_si;
err = au_ready_to_write(file, -1, &pin);
di_downgrade_lock(dentry, AuLock_IR);
if (unlikely(err))
goto out_unlock;
au_unpin(&pin);
err = -EINVAL;
h_file = au_hf_top(file);
err = vfsub_fsync(h_file, &h_file->f_path, datasync);
au_cpup_attr_timesizes(inode);
out_unlock:
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
out_si:
si_read_unlock(sb);
out:
if (inode != file->f_mapping->host) {
mutex_unlock(&inode->i_mutex);
mutex_lock(&file->f_mapping->host->i_mutex);
}
return err;
}
/* no one supports this operation, currently */
#if 0
static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync)
{
int err;
struct au_pin pin;
struct dentry *dentry;
struct inode *inode;
struct file *file, *h_file;
file = kio->ki_filp;
dentry = file->f_dentry;
inode = dentry->d_inode;
au_mtx_and_read_lock(inode);
err = 0; /* -EBADF; */ /* posix? */
if (unlikely(!(file->f_mode & FMODE_WRITE)))
goto out;
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
if (unlikely(err))
goto out;
err = au_ready_to_write(file, -1, &pin);
di_downgrade_lock(dentry, AuLock_IR);
if (unlikely(err))
goto out_unlock;
au_unpin(&pin);
err = -ENOSYS;
h_file = au_hf_top(file);
if (h_file->f_op && h_file->f_op->aio_fsync) {
struct dentry *h_d;
struct mutex *h_mtx;
h_d = h_file->f_dentry;
h_mtx = &h_d->d_inode->i_mutex;
if (!is_sync_kiocb(kio)) {
get_file(h_file);
fput(file);
}
kio->ki_filp = h_file;
err = h_file->f_op->aio_fsync(kio, datasync);
mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
if (!err)
vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);
/*ignore*/
au_cpup_attr_timesizes(inode);
mutex_unlock(h_mtx);
}
out_unlock:
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
out:
si_read_unlock(inode->sb);
mutex_unlock(&inode->i_mutex);
return err;
}
#endif
static int aufs_fasync(int fd, struct file *file, int flag)
{
int err;
struct file *h_file;
struct dentry *dentry;
struct super_block *sb;
dentry = file->f_dentry;
sb = dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
if (unlikely(err))
goto out;
h_file = au_hf_top(file);
if (h_file->f_op && h_file->f_op->fasync)
err = h_file->f_op->fasync(fd, h_file, flag);
di_read_unlock(dentry, AuLock_IR);
fi_read_unlock(file);
out:
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
/* no one supports this operation, currently */
#if 0
static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset,
size_t len, loff_t *pos , int more)
{
}
#endif
/* ---------------------------------------------------------------------- */
const struct file_operations aufs_file_fop = {
.owner = THIS_MODULE,
.llseek = default_llseek,
.read = aufs_read,
.write = aufs_write,
.aio_read = aufs_aio_read,
.aio_write = aufs_aio_write,
#ifdef CONFIG_AUFS_POLL
.poll = aufs_poll,
#endif
.unlocked_ioctl = aufs_ioctl_nondir,
#ifdef CONFIG_COMPAT
.compat_ioctl = aufs_ioctl_nondir, /* same */
#endif
.mmap = aufs_mmap,
.open = aufs_open_nondir,
.flush = aufs_flush_nondir,
.release = aufs_release_nondir,
.fsync = aufs_fsync_nondir,
/* .aio_fsync = aufs_aio_fsync_nondir, */
.fasync = aufs_fasync,
/* .sendpage = aufs_sendpage, */
.splice_write = aufs_splice_write,
.splice_read = aufs_splice_read,
#if 0
.aio_splice_write = aufs_aio_splice_write,
.aio_splice_read = aufs_aio_splice_read
#endif
};

View File

@ -0,0 +1,298 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* file operations for special files.
* while they exist in aufs virtually,
* their file I/O is handled out of aufs.
*/
#include "aufs.h"
static ssize_t aufs_aio_read_sp(struct kiocb *kio, const struct iovec *iov,
unsigned long nv, loff_t pos)
{
ssize_t err;
aufs_bindex_t bstart;
unsigned char wbr;
struct file *file, *h_file;
struct super_block *sb;
file = kio->ki_filp;
sb = file->f_dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
fi_read_lock(file);
bstart = au_fbstart(file);
h_file = au_hf_top(file);
fi_read_unlock(file);
wbr = !!au_br_writable(au_sbr(sb, bstart)->br_perm);
si_read_unlock(sb);
/* do not change the file in kio */
AuDebugOn(!h_file->f_op || !h_file->f_op->aio_read);
err = h_file->f_op->aio_read(kio, iov, nv, pos);
if (err > 0 && wbr)
file_accessed(h_file);
return err;
}
static ssize_t aufs_aio_write_sp(struct kiocb *kio, const struct iovec *iov,
unsigned long nv, loff_t pos)
{
ssize_t err;
aufs_bindex_t bstart;
unsigned char wbr;
struct super_block *sb;
struct file *file, *h_file;
file = kio->ki_filp;
sb = file->f_dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
fi_read_lock(file);
bstart = au_fbstart(file);
h_file = au_hf_top(file);
fi_read_unlock(file);
wbr = !!au_br_writable(au_sbr(sb, bstart)->br_perm);
si_read_unlock(sb);
/* do not change the file in kio */
AuDebugOn(!h_file->f_op || !h_file->f_op->aio_write);
err = h_file->f_op->aio_write(kio, iov, nv, pos);
if (err > 0 && wbr)
file_update_time(h_file);
return err;
}
/* ---------------------------------------------------------------------- */
static int aufs_release_sp(struct inode *inode, struct file *file)
{
int err;
struct file *h_file;
fi_read_lock(file);
h_file = au_hf_top(file);
fi_read_unlock(file);
/* close this fifo in aufs */
err = h_file->f_op->release(inode, file); /* ignore */
aufs_release_nondir(inode, file); /* ignore */
return err;
}
/* ---------------------------------------------------------------------- */
/* currently, support only FIFO */
enum {
AuSp_FIFO, AuSp_FIFO_R, AuSp_FIFO_W, AuSp_FIFO_RW,
/* AuSp_SOCK, AuSp_CHR, AuSp_BLK, */
AuSp_Last
};
static int aufs_open_sp(struct inode *inode, struct file *file);
static struct au_sp_fop {
int done;
struct file_operations fop; /* not 'const' */
spinlock_t spin;
} au_sp_fop[AuSp_Last] = {
[AuSp_FIFO] = {
.fop = {
.owner = THIS_MODULE,
.open = aufs_open_sp
}
}
};
static void au_init_fop_sp(struct file *file)
{
struct au_sp_fop *p;
int i;
struct file *h_file;
p = au_sp_fop;
if (unlikely(!p->done)) {
/* initialize first time only */
static DEFINE_SPINLOCK(spin);
spin_lock(&spin);
if (!p->done) {
BUILD_BUG_ON(sizeof(au_sp_fop)/sizeof(*au_sp_fop)
!= AuSp_Last);
for (i = 0; i < AuSp_Last; i++)
spin_lock_init(&p[i].spin);
p->done = 1;
}
spin_unlock(&spin);
}
switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
case FMODE_READ:
i = AuSp_FIFO_R;
break;
case FMODE_WRITE:
i = AuSp_FIFO_W;
break;
case FMODE_READ | FMODE_WRITE:
i = AuSp_FIFO_RW;
break;
default:
BUG();
}
p += i;
if (unlikely(!p->done)) {
/* initialize first time only */
h_file = au_hf_top(file);
spin_lock(&p->spin);
if (!p->done) {
p->fop = *h_file->f_op;
p->fop.owner = THIS_MODULE;
if (p->fop.aio_read)
p->fop.aio_read = aufs_aio_read_sp;
if (p->fop.aio_write)
p->fop.aio_write = aufs_aio_write_sp;
p->fop.release = aufs_release_sp;
p->done = 1;
}
spin_unlock(&p->spin);
}
file->f_op = &p->fop;
}
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
};
AuDbg("%.*s\n", AuDLNPair(dentry));
di_read_unlock(dentry, AuLock_IR);
di_write_lock_child(dentry);
err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args);
if (unlikely(err < 0))
goto out;
bcpup = err;
err = 0;
if (bcpup == au_dbstart(dentry))
goto out; /* success */
err = au_pin(&pin, dentry, bcpup, au_opt_udba(dentry->d_sb),
AuPin_MNT_WRITE);
if (!err) {
err = au_sio_cpup_simple(dentry, bcpup, -1, AuCpup_DTIME);
au_unpin(&pin);
}
out:
di_downgrade_lock(dentry, AuLock_IR);
return err;
}
static int au_do_open_sp(struct file *file, int flags)
{
int err;
struct dentry *dentry;
struct super_block *sb;
struct file *h_file;
struct inode *h_inode;
dentry = file->f_dentry;
AuDbg("%.*s\n", AuDLNPair(dentry));
/*
* try copying-up.
* operate on the ro branch is not an error.
*/
au_cpup_sp(dentry); /* ignore */
/* prepare h_file */
err = au_do_open_nondir(file, vfsub_file_flags(file));
if (unlikely(err))
goto out;
sb = dentry->d_sb;
h_file = au_hf_top(file);
h_inode = h_file->f_dentry->d_inode;
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
si_read_unlock(sb);
/* open this fifo in aufs */
err = h_inode->i_fop->open(file->f_dentry->d_inode, file);
si_noflush_read_lock(sb);
fi_write_lock(file);
di_read_lock_child(dentry, AuLock_IR);
if (!err)
au_init_fop_sp(file);
out:
return err;
}
static int aufs_open_sp(struct inode *inode, struct file *file)
{
int err;
struct super_block *sb;
sb = file->f_dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
err = au_do_open(file, au_do_open_sp, /*fidir*/NULL);
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
void au_init_special_fop(struct inode *inode, umode_t mode, dev_t rdev)
{
init_special_inode(inode, mode, rdev);
switch (mode & S_IFMT) {
case S_IFIFO:
inode->i_fop = &au_sp_fop[AuSp_FIFO].fop;
/*FALLTHROUGH*/
case S_IFCHR:
case S_IFBLK:
case S_IFSOCK:
break;
default:
AuDebugOn(1);
}
}
int au_special_file(umode_t mode)
{
int ret;
ret = 0;
switch (mode & S_IFMT) {
case S_IFIFO:
#if 0
case S_IFCHR:
case S_IFBLK:
case S_IFSOCK:
#endif
ret = 1;
}
return ret;
}

View File

@ -0,0 +1,673 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* handling file/dir, and address_space operation
*/
#include <linux/pagemap.h>
#include "aufs.h"
/* drop flags for writing */
unsigned int au_file_roflags(unsigned int flags)
{
flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC);
flags |= O_RDONLY | O_NOATIME;
return flags;
}
/* common functions to regular file and dir */
struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
struct file *file)
{
struct file *h_file;
struct dentry *h_dentry;
struct inode *h_inode;
struct super_block *sb;
struct au_branch *br;
struct path h_path;
int err, exec_flag;
/* a race condition can happen between open and unlink/rmdir */
h_file = ERR_PTR(-ENOENT);
h_dentry = au_h_dptr(dentry, bindex);
if (au_test_nfsd() && !h_dentry)
goto out;
h_inode = h_dentry->d_inode;
if (au_test_nfsd() && !h_inode)
goto out;
spin_lock(&h_dentry->d_lock);
err = (!d_unhashed(dentry) && d_unlinked(h_dentry))
|| !h_inode
/* || !dentry->d_inode->i_nlink */
;
spin_unlock(&h_dentry->d_lock);
if (unlikely(err))
goto out;
sb = dentry->d_sb;
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))
goto out;
/* drop flags for writing */
if (au_test_ro(sb, bindex, dentry->d_inode))
flags = au_file_roflags(flags);
flags &= ~O_CREAT;
atomic_inc(&br->br_count);
h_path.dentry = h_dentry;
h_path.mnt = br->br_mnt;
if (!au_special_file(h_inode->i_mode))
h_file = vfsub_dentry_open(&h_path, flags);
else {
/* this block depends upon the configuration */
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
si_read_unlock(sb);
h_file = vfsub_dentry_open(&h_path, flags);
si_noflush_read_lock(sb);
fi_write_lock(file);
di_read_lock_child(dentry, AuLock_IR);
}
if (IS_ERR(h_file))
goto out_br;
if (exec_flag) {
err = deny_write_access(h_file);
if (unlikely(err)) {
fput(h_file);
h_file = ERR_PTR(err);
goto out_br;
}
}
fsnotify_open(h_file);
goto out; /* success */
out_br:
atomic_dec(&br->br_count);
out:
return h_file;
}
int au_do_open(struct file *file, int (*open)(struct file *file, int flags),
struct au_fidir *fidir)
{
int err;
struct dentry *dentry;
err = au_finfo_init(file, fidir);
if (unlikely(err))
goto out;
dentry = file->f_dentry;
di_read_lock_child(dentry, AuLock_IR);
err = open(file, vfsub_file_flags(file));
di_read_unlock(dentry, AuLock_IR);
fi_write_unlock(file);
if (unlikely(err)) {
au_fi(file)->fi_hdir = NULL;
au_finfo_fin(file);
}
out:
return err;
}
int au_reopen_nondir(struct file *file)
{
int err;
aufs_bindex_t bstart;
struct dentry *dentry;
struct file *h_file, *h_file_tmp;
dentry = file->f_dentry;
AuDebugOn(au_special_file(dentry->d_inode->i_mode));
bstart = au_dbstart(dentry);
h_file_tmp = NULL;
if (au_fbstart(file) == bstart) {
h_file = au_hf_top(file);
if (file->f_mode == h_file->f_mode)
return 0; /* success */
h_file_tmp = h_file;
get_file(h_file_tmp);
au_set_h_fptr(file, bstart, NULL);
}
AuDebugOn(au_fi(file)->fi_hdir);
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))
goto out; /* todo: close all? */
err = 0;
au_set_fbstart(file, bstart);
au_set_h_fptr(file, bstart, h_file);
au_update_figen(file);
/* todo: necessary? */
/* file->f_ra = h_file->f_ra; */
out:
if (h_file_tmp)
fput(h_file_tmp);
return err;
}
/* ---------------------------------------------------------------------- */
static int au_reopen_wh(struct file *file, aufs_bindex_t btgt,
struct dentry *hi_wh)
{
int err;
aufs_bindex_t bstart;
struct au_dinfo *dinfo;
struct dentry *h_dentry;
struct au_hdentry *hdp;
dinfo = au_di(file->f_dentry);
AuRwMustWriteLock(&dinfo->di_rwsem);
bstart = dinfo->di_bstart;
dinfo->di_bstart = btgt;
hdp = dinfo->di_hdentry;
h_dentry = hdp[0 + btgt].hd_dentry;
hdp[0 + btgt].hd_dentry = hi_wh;
err = au_reopen_nondir(file);
hdp[0 + btgt].hd_dentry = h_dentry;
dinfo->di_bstart = bstart;
return err;
}
static int au_ready_to_write_wh(struct file *file, loff_t len,
aufs_bindex_t bcpup)
{
int err;
struct inode *inode, *h_inode;
struct dentry *dentry, *h_dentry, *hi_wh;
dentry = file->f_dentry;
au_update_dbstart(dentry);
inode = dentry->d_inode;
h_inode = NULL;
if (au_dbstart(dentry) <= bcpup && au_dbend(dentry) >= bcpup) {
h_dentry = au_h_dptr(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);
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));
return err;
}
/*
* prepare the @file for writing.
*/
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;
struct super_block *sb;
struct file *h_file;
dentry = file->f_dentry;
sb = dentry->d_sb;
inode = dentry->d_inode;
AuDebugOn(au_special_file(inode->i_mode));
bstart = au_fbstart(file);
err = au_test_ro(sb, bstart, inode);
if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) {
err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0);
goto out;
}
/* need to cpup or reopen */
parent = dget_parent(dentry);
di_write_lock_parent(parent);
err = AuWbrCopyup(au_sbi(sb), dentry);
bcpup = 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 (unlikely(err))
goto out_dgrade;
}
err = au_pin(pin, dentry, bcpup, 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);
AuDebugOn(!h_dentry);
h_inode = h_dentry->d_inode;
AuDebugOn(!h_inode);
bstart = bcpup;
}
if (dbstart <= bcpup /* just reopen */
|| !d_unhashed(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)) {
err = PTR_ERR(h_file);
h_file = NULL;
} else {
di_downgrade_lock(parent, AuLock_IR);
if (dbstart > bcpup)
err = au_sio_cpup_simple(dentry, bcpup, len,
AuCpup_DTIME);
if (!err)
err = au_reopen_nondir(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);
di_downgrade_lock(parent, AuLock_IR);
mutex_unlock(&h_inode->i_mutex);
}
if (!err) {
au_pin_set_parent_lflag(pin, /*lflag*/0);
goto out_dput; /* success */
}
au_unpin(pin);
goto out_unlock;
out_dgrade:
di_downgrade_lock(parent, AuLock_IR);
out_unlock:
di_read_unlock(parent, AuLock_IR);
out_dput:
dput(parent);
out:
return err;
}
/* ---------------------------------------------------------------------- */
int au_do_flush(struct file *file, fl_owner_t id,
int (*flush)(struct file *file, fl_owner_t id))
{
int err;
struct dentry *dentry;
struct super_block *sb;
struct inode *inode;
dentry = file->f_dentry;
sb = dentry->d_sb;
inode = dentry->d_inode;
si_noflush_read_lock(sb);
fi_read_lock(file);
ii_read_lock_child(inode);
err = flush(file, id);
au_cpup_attr_timesizes(inode);
ii_read_unlock(inode);
fi_read_unlock(file);
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
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 inode *inode;
struct super_block *sb;
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))
goto out;
parent = dget_parent(dentry);
if (au_test_ro(sb, bstart, inode)) {
di_read_lock_parent(parent, !AuLock_IR);
err = AuWbrCopyup(au_sbi(sb), dentry);
bstart = err;
di_read_unlock(parent, !AuLock_IR);
if (unlikely(err < 0))
goto out_parent;
err = 0;
}
di_read_lock_parent(parent, AuLock_IR);
hi_wh = au_hi_wh(inode, bstart);
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);
if (unlikely(err))
goto out_unlock;
/* always superio. */
err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE,
AuPin_DI_LOCKED | AuPin_MNT_WRITE);
if (!err)
err = au_sio_cpup_simple(dentry, bstart, -1,
AuCpup_DTIME);
au_unpin(&pin);
} else if (hi_wh) {
/* already copied-up after unlink */
err = au_reopen_wh(file, bstart, hi_wh);
*need_reopen = 0;
}
out_unlock:
di_read_unlock(parent, AuLock_IR);
out_parent:
dput(parent);
out:
return err;
}
static void au_do_refresh_dir(struct file *file)
{
aufs_bindex_t bindex, bend, new_bindex, brid;
struct au_hfile *p, tmp, *q;
struct au_finfo *finfo;
struct super_block *sb;
struct au_fidir *fidir;
FiMustWriteLock(file);
sb = file->f_dentry->d_sb;
finfo = au_fi(file);
fidir = finfo->fi_hdir;
AuDebugOn(!fidir);
p = fidir->fd_hfile + finfo->fi_btop;
brid = p->hf_br->br_id;
bend = fidir->fd_bbot;
for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) {
if (!p->hf_file)
continue;
new_bindex = au_br_index(sb, p->hf_br->br_id);
if (new_bindex == bindex)
continue;
if (new_bindex < 0) {
au_set_h_fptr(file, bindex, NULL);
continue;
}
/* swap two lower inode, and loop again */
q = fidir->fd_hfile + new_bindex;
tmp = *q;
*q = *p;
*p = tmp;
if (tmp.hf_file) {
bindex--;
p--;
}
}
p = fidir->fd_hfile;
if (!au_test_mmapped(file) && !d_unlinked(file->f_dentry)) {
bend = au_sbend(sb);
for (finfo->fi_btop = 0; finfo->fi_btop <= bend;
finfo->fi_btop++, p++)
if (p->hf_file) {
if (p->hf_file->f_dentry
&& p->hf_file->f_dentry->d_inode)
break;
else
au_hfput(p, file);
}
} else {
bend = au_br_index(sb, brid);
for (finfo->fi_btop = 0; finfo->fi_btop < bend;
finfo->fi_btop++, p++)
if (p->hf_file)
au_hfput(p, file);
bend = au_sbend(sb);
}
p = fidir->fd_hfile + bend;
for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop;
fidir->fd_bbot--, p--)
if (p->hf_file) {
if (p->hf_file->f_dentry
&& p->hf_file->f_dentry->d_inode)
break;
else
au_hfput(p, file);
}
AuDebugOn(fidir->fd_bbot < finfo->fi_btop);
}
/*
* after branch manipulating, refresh the file.
*/
static int refresh_file(struct file *file, int (*reopen)(struct file *file))
{
int err, need_reopen;
aufs_bindex_t bend, bindex;
struct dentry *dentry;
struct au_finfo *finfo;
struct au_hfile *hfile;
dentry = file->f_dentry;
finfo = au_fi(file);
if (!finfo->fi_hdir) {
hfile = &finfo->fi_htop;
AuDebugOn(!hfile->hf_file);
bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id);
AuDebugOn(bindex < 0);
if (bindex != finfo->fi_btop)
au_set_fbstart(file, bindex);
} else {
err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1);
if (unlikely(err))
goto out;
au_do_refresh_dir(file);
}
err = 0;
need_reopen = 1;
if (!au_test_mmapped(file))
err = au_file_refresh_by_inode(file, &need_reopen);
if (!err && need_reopen && !d_unlinked(dentry))
err = reopen(file);
if (!err) {
au_update_figen(file);
goto out; /* success */
}
/* error, close all lower files */
if (finfo->fi_hdir) {
bend = au_fbend_dir(file);
for (bindex = au_fbstart(file); bindex <= bend; bindex++)
au_set_h_fptr(file, bindex, NULL);
}
out:
return err;
}
/* common function to regular file and dir */
int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),
int wlock)
{
int err;
unsigned int sigen, figen;
aufs_bindex_t bstart;
unsigned char pseudo_link;
struct dentry *dentry;
struct inode *inode;
err = 0;
dentry = file->f_dentry;
inode = dentry->d_inode;
AuDebugOn(au_special_file(inode->i_mode));
sigen = au_sigen(dentry->d_sb);
fi_write_lock(file);
figen = au_figen(file);
di_write_lock_child(dentry);
bstart = au_dbstart(dentry);
pseudo_link = (bstart != au_ibstart(inode));
if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) {
if (!wlock) {
di_downgrade_lock(dentry, AuLock_IR);
fi_downgrade_lock(file);
}
goto out; /* success */
}
AuDbg("sigen %d, figen %d\n", sigen, figen);
if (au_digen_test(dentry, sigen)) {
err = au_reval_dpath(dentry, sigen);
AuDebugOn(!err && au_digen_test(dentry, sigen));
}
if (!err)
err = refresh_file(file, reopen);
if (!err) {
if (!wlock) {
di_downgrade_lock(dentry, AuLock_IR);
fi_downgrade_lock(file);
}
} else {
di_write_unlock(dentry);
fi_write_unlock(file);
}
out:
return err;
}
/* ---------------------------------------------------------------------- */
/* cf. aufs_nopage() */
/* for madvise(2) */
static int aufs_readpage(struct file *file __maybe_unused, struct page *page)
{
unlock_page(page);
return 0;
}
/* it will never be called, but necessary to support O_DIRECT */
static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb,
const struct iovec *iov, loff_t offset,
unsigned long nr_segs)
{ BUG(); return 0; }
/*
* it will never be called, but madvise and fadvise behaves differently
* when get_xip_mem is defined
*/
static int aufs_get_xip_mem(struct address_space *mapping, pgoff_t pgoff,
int create, void **kmem, unsigned long *pfn)
{ BUG(); return 0; }
/* they will never be called. */
#ifdef CONFIG_AUFS_DEBUG
static int aufs_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{ AuUnsupport(); return 0; }
static int aufs_write_end(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata)
{ AuUnsupport(); return 0; }
static int aufs_writepage(struct page *page, struct writeback_control *wbc)
{ AuUnsupport(); return 0; }
static int aufs_set_page_dirty(struct page *page)
{ AuUnsupport(); return 0; }
static void aufs_invalidatepage(struct page *page, unsigned long offset)
{ AuUnsupport(); }
static int aufs_releasepage(struct page *page, gfp_t gfp)
{ AuUnsupport(); return 0; }
static int aufs_migratepage(struct address_space *mapping, struct page *newpage,
struct page *page)
{ AuUnsupport(); return 0; }
static int aufs_launder_page(struct page *page)
{ AuUnsupport(); return 0; }
static int aufs_is_partially_uptodate(struct page *page,
read_descriptor_t *desc,
unsigned long from)
{ AuUnsupport(); return 0; }
static int aufs_error_remove_page(struct address_space *mapping,
struct page *page)
{ AuUnsupport(); return 0; }
#endif /* CONFIG_AUFS_DEBUG */
const struct address_space_operations aufs_aop = {
.readpage = aufs_readpage,
.direct_IO = aufs_direct_IO,
.get_xip_mem = aufs_get_xip_mem,
#ifdef CONFIG_AUFS_DEBUG
.writepage = aufs_writepage,
/* no writepages, because of writepage */
.set_page_dirty = aufs_set_page_dirty,
/* no readpages, because of readpage */
.write_begin = aufs_write_begin,
.write_end = aufs_write_end,
/* no bmap, no block device */
.invalidatepage = aufs_invalidatepage,
.releasepage = aufs_releasepage,
.migratepage = aufs_migratepage,
.launder_page = aufs_launder_page,
.is_partially_uptodate = aufs_is_partially_uptodate,
.error_remove_page = aufs_error_remove_page
#endif /* CONFIG_AUFS_DEBUG */
};

View File

@ -0,0 +1,298 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* file operations
*/
#ifndef __AUFS_FILE_H__
#define __AUFS_FILE_H__
#ifdef __KERNEL__
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include "rwsem.h"
struct au_branch;
struct au_hfile {
struct file *hf_file;
struct au_branch *hf_br;
};
struct au_vdir;
struct au_fidir {
aufs_bindex_t fd_bbot;
aufs_bindex_t fd_nent;
struct au_vdir *fd_vdir_cache;
struct au_hfile fd_hfile[];
};
static inline int au_fidir_sz(int nent)
{
AuDebugOn(nent < 0);
return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent;
}
struct au_finfo {
atomic_t fi_generation;
struct au_rwsem fi_rwsem;
aufs_bindex_t fi_btop;
/* do not union them */
struct { /* for non-dir */
struct au_hfile fi_htop;
atomic_t fi_mmapped;
};
struct au_fidir *fi_hdir; /* for dir only */
} ____cacheline_aligned_in_smp;
/* ---------------------------------------------------------------------- */
/* file.c */
extern const struct address_space_operations aufs_aop;
unsigned int au_file_roflags(unsigned int flags);
struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
struct file *file);
int au_do_open(struct file *file, int (*open)(struct file *file, int flags),
struct au_fidir *fidir);
int au_reopen_nondir(struct file *file);
struct au_pin;
int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin);
int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),
int wlock);
int au_do_flush(struct file *file, fl_owner_t id,
int (*flush)(struct file *file, fl_owner_t id));
/* poll.c */
#ifdef CONFIG_AUFS_POLL
unsigned int aufs_poll(struct file *file, poll_table *wait);
#endif
#ifdef CONFIG_AUFS_BR_HFSPLUS
/* hfsplus.c */
struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex);
void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex,
struct file *h_file);
#else
static inline
struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex)
{
return NULL;
}
AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex,
struct file *h_file);
#endif
/* f_op.c */
extern const struct file_operations aufs_file_fop;
int au_do_open_nondir(struct file *file, int flags);
int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file);
#ifdef CONFIG_AUFS_SP_IATTR
/* f_op_sp.c */
int au_special_file(umode_t mode);
void au_init_special_fop(struct inode *inode, umode_t mode, dev_t rdev);
#else
AuStubInt0(au_special_file, umode_t mode)
static inline void au_init_special_fop(struct inode *inode, umode_t mode,
dev_t rdev)
{
init_special_inode(inode, mode, rdev);
}
#endif
/* finfo.c */
void au_hfput(struct au_hfile *hf, struct file *file);
void au_set_h_fptr(struct file *file, aufs_bindex_t bindex,
struct file *h_file);
void au_update_figen(struct file *file);
struct au_fidir *au_fidir_alloc(struct super_block *sb);
int au_fidir_realloc(struct au_finfo *finfo, int nbr);
void au_fi_init_once(void *_fi);
void au_finfo_fin(struct file *file);
int au_finfo_init(struct file *file, struct au_fidir *fidir);
/* ioctl.c */
long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_COMPAT
long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd,
unsigned long arg);
#endif
/* ---------------------------------------------------------------------- */
static inline struct au_finfo *au_fi(struct file *file)
{
return file->private_data;
}
/* ---------------------------------------------------------------------- */
/*
* fi_read_lock, fi_write_lock,
* fi_read_unlock, fi_write_unlock, fi_downgrade_lock
*/
AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem);
#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem)
#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem)
#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem)
/* ---------------------------------------------------------------------- */
/* todo: hard/soft set? */
static inline aufs_bindex_t au_fbstart(struct file *file)
{
FiMustAnyLock(file);
return au_fi(file)->fi_btop;
}
static inline aufs_bindex_t au_fbend_dir(struct file *file)
{
FiMustAnyLock(file);
AuDebugOn(!au_fi(file)->fi_hdir);
return au_fi(file)->fi_hdir->fd_bbot;
}
static inline struct au_vdir *au_fvdir_cache(struct file *file)
{
FiMustAnyLock(file);
AuDebugOn(!au_fi(file)->fi_hdir);
return au_fi(file)->fi_hdir->fd_vdir_cache;
}
static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex)
{
FiMustWriteLock(file);
au_fi(file)->fi_btop = bindex;
}
static inline void au_set_fbend_dir(struct file *file, aufs_bindex_t bindex)
{
FiMustWriteLock(file);
AuDebugOn(!au_fi(file)->fi_hdir);
au_fi(file)->fi_hdir->fd_bbot = bindex;
}
static inline void au_set_fvdir_cache(struct file *file,
struct au_vdir *vdir_cache)
{
FiMustWriteLock(file);
AuDebugOn(!au_fi(file)->fi_hdir);
au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache;
}
static inline struct file *au_hf_top(struct file *file)
{
FiMustAnyLock(file);
AuDebugOn(au_fi(file)->fi_hdir);
return au_fi(file)->fi_htop.hf_file;
}
static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex)
{
FiMustAnyLock(file);
AuDebugOn(!au_fi(file)->fi_hdir);
return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file;
}
/* todo: memory barrier? */
static inline unsigned int au_figen(struct file *f)
{
return atomic_read(&au_fi(f)->fi_generation);
}
static inline void au_set_mmapped(struct file *f)
{
if (atomic_inc_return(&au_fi(f)->fi_mmapped))
return;
pr_warn("fi_mmapped wrapped around\n");
while (!atomic_inc_return(&au_fi(f)->fi_mmapped))
;
}
static inline void au_unset_mmapped(struct file *f)
{
atomic_dec(&au_fi(f)->fi_mmapped);
}
static inline int au_test_mmapped(struct file *f)
{
return atomic_read(&au_fi(f)->fi_mmapped);
}
/* customize vma->vm_file */
static inline void au_do_vm_file_reset(struct vm_area_struct *vma,
struct file *file)
{
struct file *f;
f = vma->vm_file;
get_file(file);
vma->vm_file = file;
fput(f);
}
#ifdef CONFIG_MMU
#define AuDbgVmRegion(file, vma) do {} while (0)
static inline void au_vm_file_reset(struct vm_area_struct *vma,
struct file *file)
{
au_do_vm_file_reset(vma, file);
}
#else
#define AuDbgVmRegion(file, vma) \
AuDebugOn((vma)->vm_region && (vma)->vm_region->vm_file != (file))
static inline void au_vm_file_reset(struct vm_area_struct *vma,
struct file *file)
{
struct file *f;
au_do_vm_file_reset(vma, file);
f = vma->vm_region->vm_file;
get_file(file);
vma->vm_region->vm_file = file;
fput(f);
}
#endif /* CONFIG_MMU */
/* handle vma->vm_prfile */
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__ */
#endif /* __AUFS_FILE_H__ */

View File

@ -0,0 +1,156 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* file private data
*/
#include "aufs.h"
void au_hfput(struct au_hfile *hf, struct file *file)
{
/* todo: direct access f_flags */
if (vfsub_file_flags(file) & __FMODE_EXEC)
allow_write_access(hf->hf_file);
fput(hf->hf_file);
hf->hf_file = NULL;
atomic_dec(&hf->hf_br->br_count);
hf->hf_br = NULL;
}
void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val)
{
struct au_finfo *finfo = au_fi(file);
struct au_hfile *hf;
struct au_fidir *fidir;
fidir = finfo->fi_hdir;
if (!fidir) {
AuDebugOn(finfo->fi_btop != bindex);
hf = &finfo->fi_htop;
} else
hf = fidir->fd_hfile + bindex;
if (hf && hf->hf_file)
au_hfput(hf, file);
if (val) {
FiMustWriteLock(file);
hf->hf_file = val;
hf->hf_br = au_sbr(file->f_dentry->d_sb, bindex);
}
}
void au_update_figen(struct file *file)
{
atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_dentry));
/* smp_mb(); */ /* atomic_set */
}
/* ---------------------------------------------------------------------- */
struct au_fidir *au_fidir_alloc(struct super_block *sb)
{
struct au_fidir *fidir;
int nbr;
nbr = au_sbend(sb) + 1;
if (nbr < 2)
nbr = 2; /* initial allocate for 2 branches */
fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS);
if (fidir) {
fidir->fd_bbot = -1;
fidir->fd_nent = nbr;
fidir->fd_vdir_cache = NULL;
}
return fidir;
}
int au_fidir_realloc(struct au_finfo *finfo, int nbr)
{
int err;
struct au_fidir *fidir, *p;
AuRwMustWriteLock(&finfo->fi_rwsem);
fidir = finfo->fi_hdir;
AuDebugOn(!fidir);
err = -ENOMEM;
p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr),
GFP_NOFS);
if (p) {
p->fd_nent = nbr;
finfo->fi_hdir = p;
err = 0;
}
return err;
}
/* ---------------------------------------------------------------------- */
void au_finfo_fin(struct file *file)
{
struct au_finfo *finfo;
au_nfiles_dec(file->f_dentry->d_sb);
finfo = au_fi(file);
AuDebugOn(finfo->fi_hdir);
AuRwDestroy(&finfo->fi_rwsem);
au_cache_free_finfo(finfo);
}
void au_fi_init_once(void *_finfo)
{
struct au_finfo *finfo = _finfo;
static struct lock_class_key aufs_fi;
au_rw_init(&finfo->fi_rwsem);
au_rw_class(&finfo->fi_rwsem, &aufs_fi);
}
int au_finfo_init(struct file *file, struct au_fidir *fidir)
{
int err, lc_idx;
struct au_finfo *finfo;
struct dentry *dentry;
err = -ENOMEM;
dentry = file->f_dentry;
finfo = au_cache_alloc_finfo();
if (unlikely(!finfo))
goto out;
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);
au_rw_write_lock(&finfo->fi_rwsem);
finfo->fi_btop = -1;
finfo->fi_hdir = fidir;
atomic_set(&finfo->fi_generation, au_digen(dentry));
/* smp_mb(); */ /* atomic_set */
file->private_data = finfo;
out:
return err;
}

View File

@ -0,0 +1,496 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* judging filesystem type
*/
#ifndef __AUFS_FSTYPE_H__
#define __AUFS_FSTYPE_H__
#ifdef __KERNEL__
#include <linux/fs.h>
#include <linux/magic.h>
#include <linux/romfs_fs.h>
static inline int au_test_aufs(struct super_block *sb)
{
return sb->s_magic == AUFS_SUPER_MAGIC;
}
static inline const char *au_sbtype(struct super_block *sb)
{
return sb->s_type->name;
}
static inline int au_test_iso9660(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE)
return sb->s_magic == ROMFS_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_romfs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE)
return sb->s_magic == ISOFS_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_cramfs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE)
return sb->s_magic == CRAMFS_MAGIC;
#endif
return 0;
}
static inline int au_test_nfs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE)
return sb->s_magic == NFS_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_fuse(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE)
return sb->s_magic == FUSE_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_xfs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE)
return sb->s_magic == XFS_SB_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_tmpfs(struct super_block *sb __maybe_unused)
{
#ifdef CONFIG_TMPFS
return sb->s_magic == TMPFS_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE)
return !strcmp(au_sbtype(sb), "ecryptfs");
#else
return 0;
#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)
return sb->s_magic == OCFS2_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_ocfs2_dlmfs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_OCFS2_FS_O2CB) || defined(CONFIG_OCFS2_FS_O2CB_MODULE)
return sb->s_magic == DLMFS_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_coda(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_CODA_FS) || defined(CONFIG_CODA_FS_MODULE)
return sb->s_magic == CODA_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_v9fs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_9P_FS) || defined(CONFIG_9P_FS_MODULE)
return sb->s_magic == V9FS_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_ext4(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_EXT4DEV_FS) || defined(CONFIG_EXT4DEV_FS_MODULE)
return sb->s_magic == EXT4_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_sysv(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_SYSV_FS) || defined(CONFIG_SYSV_FS_MODULE)
return !strcmp(au_sbtype(sb), "sysv");
#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_ubifs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE)
return sb->s_magic == UBIFS_SUPER_MAGIC;
#else
return 0;
#endif
}
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 defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE)
return sb->s_magic == CONFIGFS_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_minix(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE)
return sb->s_magic == MINIX3_SUPER_MAGIC
|| sb->s_magic == MINIX2_SUPER_MAGIC
|| sb->s_magic == MINIX2_SUPER_MAGIC2
|| sb->s_magic == MINIX_SUPER_MAGIC
|| sb->s_magic == MINIX_SUPER_MAGIC2;
#else
return 0;
#endif
}
static inline int au_test_cifs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_CIFS_FS) || defined(CONFIGCIFS_FS_MODULE)
return sb->s_magic == CIFS_MAGIC_NUMBER;
#else
return 0;
#endif
}
static inline int au_test_fat(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE)
return sb->s_magic == MSDOS_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_msdos(struct super_block *sb)
{
return au_test_fat(sb);
}
static inline int au_test_vfat(struct super_block *sb)
{
return au_test_fat(sb);
}
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_squashfs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE)
return sb->s_magic == SQUASHFS_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_btrfs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE)
return sb->s_magic == BTRFS_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_xenfs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE)
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
}
static inline int au_test_nilfs(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_NILFS) || defined(CONFIG_NILFS_MODULE)
return sb->s_magic == NILFS_SUPER_MAGIC;
#else
return 0;
#endif
}
static inline int au_test_hfsplus(struct super_block *sb __maybe_unused)
{
#if defined(CONFIG_HFSPLUS_FS) || defined(CONFIG_HFSPLUS_FS_MODULE)
return sb->s_magic == HFSPLUS_SUPER_MAGIC;
#else
return 0;
#endif
}
/* ---------------------------------------------------------------------- */
/*
* they can't be an aufs branch.
*/
static inline int au_test_fs_unsuppoted(struct super_block *sb)
{
return
#ifndef CONFIG_AUFS_BR_RAMFS
au_test_ramfs(sb) ||
#endif
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 */
}
/*
* If the filesystem supports NFS-export, then it has to support NULL as
* a nameidata parameter for ->create(), ->lookup() and ->d_revalidate().
* We can apply this principle when we handle a lower filesystem.
*/
static inline int au_test_fs_null_nd(struct super_block *sb)
{
return !!sb->s_export_op;
}
static inline int au_test_fs_remote(struct super_block *sb)
{
return !au_test_tmpfs(sb)
#ifdef CONFIG_AUFS_BR_RAMFS
&& !au_test_ramfs(sb)
#endif
&& !(sb->s_type->fs_flags & FS_REQUIRES_DEV);
}
/* ---------------------------------------------------------------------- */
/*
* Note: these functions (below) are created after reading ->getattr() in all
* filesystems under linux/fs. it means we have to do so in every update...
*/
/*
* some filesystems require getattr to refresh the inode attributes before
* referencing.
* in most cases, we can rely on the inode attribute in NFS (or every remote fs)
* and leave the work for d_revalidate()
*/
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 */
/* || au_test_v9fs(sb) */ /* untested */
;
}
/*
* filesystems which don't maintain i_size or i_blocks.
*/
static inline int au_test_fs_bad_iattr_size(struct super_block *sb)
{
return au_test_xfs(sb)
|| au_test_btrfs(sb)
|| au_test_ubifs(sb)
|| au_test_hfsplus(sb) /* maintained, but incorrect */
/* || au_test_ext4(sb) */ /* untested */
/* || au_test_ocfs2(sb) */ /* untested */
/* || au_test_ocfs2_dlmfs(sb) */ /* untested */
/* || au_test_sysv(sb) */ /* untested */
/* || au_test_minix(sb) */ /* untested */
;
}
/*
* filesystems which don't store the correct value in some of their inode
* attributes.
*/
static inline int au_test_fs_bad_iattr(struct super_block *sb)
{
return au_test_fs_bad_iattr_size(sb)
/* || au_test_cifs(sb) */ /* untested */
|| au_test_fat(sb)
|| au_test_msdos(sb)
|| au_test_vfat(sb);
}
/* they don't check i_nlink in link(2) */
static inline int au_test_fs_no_limit_nlink(struct super_block *sb)
{
return au_test_tmpfs(sb)
#ifdef CONFIG_AUFS_BR_RAMFS
|| au_test_ramfs(sb)
#endif
|| au_test_ubifs(sb)
|| au_test_btrfs(sb)
|| au_test_hfsplus(sb);
}
/*
* filesystems which sets S_NOATIME and S_NOCMTIME.
*/
static inline int au_test_fs_notime(struct super_block *sb)
{
return au_test_nfs(sb)
|| au_test_fuse(sb)
|| au_test_ubifs(sb)
/* || au_test_cifs(sb) */ /* untested */
;
}
/*
* filesystems which requires replacing i_mapping.
*/
static inline int au_test_fs_bad_mapping(struct super_block *sb)
{
return au_test_fuse(sb)
|| au_test_ubifs(sb);
}
/* temporary support for i#1 in cramfs */
static inline int au_test_fs_unique_ino(struct inode *inode)
{
if (au_test_cramfs(inode->i_sb))
return inode->i_ino != 1;
return 1;
}
/* ---------------------------------------------------------------------- */
/*
* the filesystem where the xino files placed must support i/o after unlink and
* maintain i_size and i_blocks.
*/
static inline int au_test_fs_bad_xino(struct super_block *sb)
{
return au_test_fs_remote(sb)
|| au_test_fs_bad_iattr_size(sb)
#ifdef CONFIG_AUFS_BR_RAMFS
|| !(au_test_ramfs(sb) || au_test_fs_null_nd(sb))
#else
|| !au_test_fs_null_nd(sb) /* to keep xino code simple */
#endif
/* don't want unnecessary work for xino */
|| au_test_aufs(sb)
|| au_test_ecryptfs(sb)
|| au_test_nilfs(sb);
}
static inline int au_test_fs_trunc_xino(struct super_block *sb)
{
return au_test_tmpfs(sb)
|| au_test_ramfs(sb);
}
/*
* test if the @sb is real-readonly.
*/
static inline int au_test_fs_rr(struct super_block *sb)
{
return au_test_squashfs(sb)
|| au_test_iso9660(sb)
|| au_test_cramfs(sb)
|| au_test_romfs(sb);
}
#endif /* __KERNEL__ */
#endif /* __AUFS_FSTYPE_H__ */

View File

@ -0,0 +1,260 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* fsnotify for the lower directories
*/
#include "aufs.h"
/* FS_IN_IGNORED is unnecessary */
static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE
| FS_CREATE | FS_EVENT_ON_CHILD);
static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq);
static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0);
static void au_hfsn_free_mark(struct fsnotify_mark *mark)
{
struct au_hnotify *hn = container_of(mark, struct au_hnotify,
hn_mark);
AuDbg("here\n");
au_cache_free_hnotify(hn);
smp_mb__before_atomic_dec();
atomic64_dec(&au_hfsn_ifree);
wake_up(&au_hfsn_wq);
}
static int au_hfsn_alloc(struct au_hinode *hinode)
{
struct au_hnotify *hn;
struct super_block *sb;
struct au_branch *br;
struct fsnotify_mark *mark;
aufs_bindex_t bindex;
hn = hinode->hi_notify;
sb = hn->hn_aufs_inode->i_sb;
bindex = au_br_index(sb, hinode->hi_id);
br = au_sbr(sb, bindex);
mark = &hn->hn_mark;
fsnotify_init_mark(mark, au_hfsn_free_mark);
mark->mask = AuHfsnMask;
/*
* 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,
/*mnt*/NULL, /*allow_dups*/1);
}
static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn)
{
struct fsnotify_mark *mark;
unsigned long long ull;
ull = atomic64_inc_return(&au_hfsn_ifree);
BUG_ON(!ull);
mark = &hn->hn_mark;
fsnotify_destroy_mark(mark);
fsnotify_put_mark(mark);
/* free hn by myself */
return 0;
}
/* ---------------------------------------------------------------------- */
static void au_hfsn_ctl(struct au_hinode *hinode, int do_set)
{
struct fsnotify_mark *mark;
mark = &hinode->hi_notify->hn_mark;
spin_lock(&mark->lock);
if (do_set) {
AuDebugOn(mark->mask & AuHfsnMask);
mark->mask |= AuHfsnMask;
} else {
AuDebugOn(!(mark->mask & AuHfsnMask));
mark->mask &= ~AuHfsnMask;
}
spin_unlock(&mark->lock);
/* fsnotify_recalc_inode_mask(hinode->hi_inode); */
}
/* ---------------------------------------------------------------------- */
/* #define AuDbgHnotify */
#ifdef AuDbgHnotify
static char *au_hfsn_name(u32 mask)
{
#ifdef CONFIG_AUFS_DEBUG
#define test_ret(flag) if (mask & flag) \
return #flag;
test_ret(FS_ACCESS);
test_ret(FS_MODIFY);
test_ret(FS_ATTRIB);
test_ret(FS_CLOSE_WRITE);
test_ret(FS_CLOSE_NOWRITE);
test_ret(FS_OPEN);
test_ret(FS_MOVED_FROM);
test_ret(FS_MOVED_TO);
test_ret(FS_CREATE);
test_ret(FS_DELETE);
test_ret(FS_DELETE_SELF);
test_ret(FS_MOVE_SELF);
test_ret(FS_UNMOUNT);
test_ret(FS_Q_OVERFLOW);
test_ret(FS_IN_IGNORED);
test_ret(FS_IN_ISDIR);
test_ret(FS_IN_ONESHOT);
test_ret(FS_EVENT_ON_CHILD);
return "";
#undef test_ret
#else
return "??";
#endif
}
#endif
/* ---------------------------------------------------------------------- */
static int au_hfsn_handle_event(struct fsnotify_group *group,
struct fsnotify_mark *inode_mark,
struct fsnotify_mark *vfsmount_mark,
struct fsnotify_event *event)
{
int err;
struct au_hnotify *hnotify;
struct inode *h_dir, *h_inode;
__u32 mask;
struct qstr h_child_qstr = {
.name = event->file_name,
.len = event->name_len
};
AuDebugOn(event->data_type != FSNOTIFY_EVENT_INODE);
err = 0;
/* if FS_UNMOUNT happens, there must be another bug */
mask = event->mask;
AuDebugOn(mask & FS_UNMOUNT);
if (mask & (FS_IN_IGNORED | FS_UNMOUNT))
goto out;
h_dir = event->to_tell;
h_inode = event->inode;
#ifdef AuDbgHnotify
au_debug(1);
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",
h_dir->i_ino, mask, au_hfsn_name(mask),
AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0);
/* WARN_ON(1); */
}
au_debug(0);
#endif
AuDebugOn(!inode_mark);
hnotify = container_of(inode_mark, struct au_hnotify, hn_mark);
err = au_hnotify(h_dir, hnotify, mask, &h_child_qstr, h_inode);
out:
return err;
}
/* isn't it waste to ask every registered 'group'? */
/* copied from linux/fs/notify/inotify/inotify_fsnotiry.c */
/* it should be exported to modules */
static bool au_hfsn_should_send_event(struct fsnotify_group *group,
struct inode *h_inode,
struct fsnotify_mark *inode_mark,
struct fsnotify_mark *vfsmount_mark,
__u32 mask, void *data, int data_type)
{
mask = (mask & ~FS_EVENT_ON_CHILD);
return inode_mark->mask & mask;
}
static struct fsnotify_ops au_hfsn_ops = {
.should_send_event = au_hfsn_should_send_event,
.handle_event = au_hfsn_handle_event
};
/* ---------------------------------------------------------------------- */
static void au_hfsn_fin_br(struct au_branch *br)
{
if (br->br_hfsn_group)
fsnotify_put_group(br->br_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;
}
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_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;
}
/* ---------------------------------------------------------------------- */
static void au_hfsn_fin(void)
{
AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree));
wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree));
}
const struct au_hnotify_op au_hnotify_op = {
.ctl = au_hfsn_ctl,
.alloc = au_hfsn_alloc,
.free = au_hfsn_free,
.fin = au_hfsn_fin,
.reset_br = au_hfsn_reset_br,
.fin_br = au_hfsn_fin_br,
.init_br = au_hfsn_init_br
};

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2010-2012 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
*/
/*
* special support for filesystems which aqucires an inode mutex
* at final closing a file, eg, hfsplus.
*
* This trick is very simple and stupid, just to open the file before really
* neceeary open to tell hfsplus that this is not the final closing.
* The caller should call au_h_open_pre() after acquiring the inode mutex,
* and au_h_open_post() after releasing it.
*/
#include "aufs.h"
struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex)
{
struct file *h_file;
struct dentry *h_dentry;
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)
&& S_ISREG(h_dentry->d_inode->i_mode))
h_file = au_h_open(dentry, bindex,
O_RDONLY | O_NOATIME | O_LARGEFILE,
/*file*/NULL);
return h_file;
}
void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex,
struct file *h_file)
{
if (h_file) {
fput(h_file);
au_sbr_put(dentry->d_sb, bindex);
}
}

View File

@ -0,0 +1,712 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* abstraction to notify the direct changes on lower directories
*/
#include "aufs.h"
int au_hn_alloc(struct au_hinode *hinode, struct inode *inode)
{
int err;
struct au_hnotify *hn;
err = -ENOMEM;
hn = au_cache_alloc_hnotify();
if (hn) {
hn->hn_aufs_inode = inode;
hinode->hi_notify = hn;
err = au_hnotify_op.alloc(hinode);
AuTraceErr(err);
if (unlikely(err)) {
hinode->hi_notify = NULL;
au_cache_free_hnotify(hn);
/*
* The upper dir was removed by udba, but the same named
* dir left. In this case, aufs assignes a new inode
* number and set the monitor again.
* For the lower dir, the old monitnor is still left.
*/
if (err == -EEXIST)
err = 0;
}
}
AuTraceErr(err);
return err;
}
void au_hn_free(struct au_hinode *hinode)
{
struct au_hnotify *hn;
hn = hinode->hi_notify;
if (hn) {
hinode->hi_notify = NULL;
if (au_hnotify_op.free(hinode, hn))
au_cache_free_hnotify(hn);
}
}
/* ---------------------------------------------------------------------- */
void au_hn_ctl(struct au_hinode *hinode, int do_set)
{
if (hinode->hi_notify)
au_hnotify_op.ctl(hinode, do_set);
}
void au_hn_reset(struct inode *inode, unsigned int flags)
{
aufs_bindex_t bindex, bend;
struct inode *hi;
struct dentry *iwhdentry;
bend = au_ibend(inode);
for (bindex = au_ibstart(inode); bindex <= bend; bindex++) {
hi = au_h_iptr(inode, bindex);
if (!hi)
continue;
/* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */
iwhdentry = au_hi_wh(inode, bindex);
if (iwhdentry)
dget(iwhdentry);
au_igrab(hi);
au_set_h_iptr(inode, bindex, NULL, 0);
au_set_h_iptr(inode, bindex, au_igrab(hi),
flags & ~AuHi_XINO);
iput(hi);
dput(iwhdentry);
/* mutex_unlock(&hi->i_mutex); */
}
}
/* ---------------------------------------------------------------------- */
static int hn_xino(struct inode *inode, struct inode *h_inode)
{
int err;
aufs_bindex_t bindex, bend, bfound, bstart;
struct inode *h_i;
err = 0;
if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
pr_warn("branch root dir was changed\n");
goto out;
}
bfound = -1;
bend = au_ibend(inode);
bstart = au_ibstart(inode);
#if 0 /* reserved for future use */
if (bindex == bend) {
/* keep this ino in rename case */
goto out;
}
#endif
for (bindex = bstart; bindex <= bend; bindex++)
if (au_h_iptr(inode, bindex) == h_inode) {
bfound = bindex;
break;
}
if (bfound < 0)
goto out;
for (bindex = bstart; bindex <= bend; bindex++) {
h_i = au_h_iptr(inode, bindex);
if (!h_i)
continue;
err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0);
/* ignore this error */
/* bad action? */
}
/* children inode number will be broken */
out:
AuTraceErr(err);
return err;
}
static int hn_gen_tree(struct dentry *dentry)
{
int err, i, j, ndentry;
struct au_dcsub_pages dpages;
struct au_dpage *dpage;
struct dentry **dentries;
err = au_dpages_init(&dpages, GFP_NOFS);
if (unlikely(err))
goto out;
err = au_dcsub_pages(&dpages, dentry, NULL, NULL);
if (unlikely(err))
goto out_dpages;
for (i = 0; i < dpages.ndpage; i++) {
dpage = dpages.dpages + i;
dentries = dpage->dentries;
ndentry = dpage->ndentry;
for (j = 0; j < ndentry; j++) {
struct dentry *d;
d = dentries[j];
if (IS_ROOT(d))
continue;
au_digen_dec(d);
if (d->d_inode)
/* todo: reset children xino?
cached children only? */
au_iigen_dec(d->d_inode);
}
}
out_dpages:
au_dpages_free(&dpages);
#if 0
/* discard children */
dentry_unhash(dentry);
dput(dentry);
#endif
out:
return err;
}
/*
* return 0 if processed.
*/
static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode,
const unsigned int isdir)
{
int err;
struct dentry *d;
struct qstr *dname;
err = 1;
if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
pr_warn("branch root dir was changed\n");
err = 0;
goto out;
}
if (!isdir) {
AuDebugOn(!name);
au_iigen_dec(inode);
spin_lock(&inode->i_lock);
list_for_each_entry(d, &inode->i_dentry, d_alias) {
spin_lock(&d->d_lock);
dname = &d->d_name;
if (dname->len != nlen
&& memcmp(dname->name, name, nlen)) {
spin_unlock(&d->d_lock);
continue;
}
err = 0;
au_digen_dec(d);
spin_unlock(&d->d_lock);
break;
}
spin_unlock(&inode->i_lock);
} else {
au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR);
d = d_find_alias(inode);
if (!d) {
au_iigen_dec(inode);
goto out;
}
spin_lock(&d->d_lock);
dname = &d->d_name;
if (dname->len == nlen && !memcmp(dname->name, name, nlen)) {
spin_unlock(&d->d_lock);
err = hn_gen_tree(d);
spin_lock(&d->d_lock);
}
spin_unlock(&d->d_lock);
dput(d);
}
out:
AuTraceErr(err);
return err;
}
static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir)
{
int err;
struct inode *inode;
inode = dentry->d_inode;
if (IS_ROOT(dentry)
/* || (inode && inode->i_ino == AUFS_ROOT_INO) */
) {
pr_warn("branch root dir was changed\n");
return 0;
}
err = 0;
if (!isdir) {
au_digen_dec(dentry);
if (inode)
au_iigen_dec(inode);
} else {
au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR);
if (inode)
err = hn_gen_tree(dentry);
}
AuTraceErr(err);
return err;
}
/* ---------------------------------------------------------------------- */
/* hnotify job flags */
#define AuHnJob_XINO0 1
#define AuHnJob_GEN (1 << 1)
#define AuHnJob_DIRENT (1 << 2)
#define AuHnJob_ISDIR (1 << 3)
#define AuHnJob_TRYXINO0 (1 << 4)
#define AuHnJob_MNTPNT (1 << 5)
#define au_ftest_hnjob(flags, name) ((flags) & AuHnJob_##name)
#define au_fset_hnjob(flags, name) \
do { (flags) |= AuHnJob_##name; } while (0)
#define au_fclr_hnjob(flags, name) \
do { (flags) &= ~AuHnJob_##name; } while (0)
enum {
AuHn_CHILD,
AuHn_PARENT,
AuHnLast
};
struct au_hnotify_args {
struct inode *h_dir, *dir, *h_child_inode;
u32 mask;
unsigned int flags[AuHnLast];
unsigned int h_child_nlen;
char h_child_name[];
};
struct hn_job_args {
unsigned int flags;
struct inode *inode, *h_inode, *dir, *h_dir;
struct dentry *dentry;
char *h_name;
int h_nlen;
};
static int hn_job(struct hn_job_args *a)
{
const unsigned int isdir = au_ftest_hnjob(a->flags, ISDIR);
/* reset xino */
if (au_ftest_hnjob(a->flags, XINO0) && a->inode)
hn_xino(a->inode, a->h_inode); /* ignore this error */
if (au_ftest_hnjob(a->flags, TRYXINO0)
&& a->inode
&& a->h_inode) {
mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
if (!a->h_inode->i_nlink)
hn_xino(a->inode, a->h_inode); /* ignore this error */
mutex_unlock(&a->h_inode->i_mutex);
}
/* make the generation obsolete */
if (au_ftest_hnjob(a->flags, GEN)) {
int err = -1;
if (a->inode)
err = hn_gen_by_inode(a->h_name, a->h_nlen, a->inode,
isdir);
if (err && a->dentry)
hn_gen_by_name(a->dentry, isdir);
/* ignore this error */
}
/* make dir entries obsolete */
if (au_ftest_hnjob(a->flags, DIRENT) && a->inode) {
struct au_vdir *vdir;
vdir = au_ivdir(a->inode);
if (vdir)
vdir->vd_jiffy = 0;
/* IMustLock(a->inode); */
/* a->inode->i_version++; */
}
/* can do nothing but warn */
if (au_ftest_hnjob(a->flags, MNTPNT)
&& a->dentry
&& d_mountpoint(a->dentry))
pr_warn("mount-point %.*s is removed or renamed\n",
AuDLNPair(a->dentry));
return 0;
}
/* ---------------------------------------------------------------------- */
static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen,
struct inode *dir)
{
struct dentry *dentry, *d, *parent;
struct qstr *dname;
parent = d_find_alias(dir);
if (!parent)
return NULL;
dentry = NULL;
spin_lock(&parent->d_lock);
list_for_each_entry(d, &parent->d_subdirs, d_u.d_child) {
/* AuDbg("%.*s\n", AuDLNPair(d)); */
spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
dname = &d->d_name;
if (dname->len != nlen || memcmp(dname->name, name, nlen))
goto cont_unlock;
if (au_di(d))
au_digen_dec(d);
else
goto cont_unlock;
if (d->d_count) {
dentry = dget_dlock(d);
spin_unlock(&d->d_lock);
break;
}
cont_unlock:
spin_unlock(&d->d_lock);
}
spin_unlock(&parent->d_lock);
dput(parent);
if (dentry)
di_write_lock_child(dentry);
return dentry;
}
static struct inode *lookup_wlock_by_ino(struct super_block *sb,
aufs_bindex_t bindex, ino_t h_ino)
{
struct inode *inode;
ino_t ino;
int err;
inode = NULL;
err = au_xino_read(sb, bindex, h_ino, &ino);
if (!err && ino)
inode = ilookup(sb, ino);
if (!inode)
goto out;
if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
pr_warn("wrong root branch\n");
iput(inode);
inode = NULL;
goto out;
}
ii_write_lock_child(inode);
out:
return inode;
}
static void au_hn_bh(void *_args)
{
struct au_hnotify_args *a = _args;
struct super_block *sb;
aufs_bindex_t bindex, bend, bfound;
unsigned char xino, try_iput;
int err;
struct inode *inode;
ino_t h_ino;
struct hn_job_args args;
struct dentry *dentry;
struct au_sbinfo *sbinfo;
AuDebugOn(!_args);
AuDebugOn(!a->h_dir);
AuDebugOn(!a->dir);
AuDebugOn(!a->mask);
AuDbg("mask 0x%x, i%lu, hi%lu, hci%lu\n",
a->mask, a->dir->i_ino, a->h_dir->i_ino,
a->h_child_inode ? a->h_child_inode->i_ino : 0);
inode = NULL;
dentry = NULL;
/*
* do not lock a->dir->i_mutex here
* because of d_revalidate() may cause a deadlock.
*/
sb = a->dir->i_sb;
AuDebugOn(!sb);
sbinfo = au_sbi(sb);
AuDebugOn(!sbinfo);
si_write_lock(sb, AuLock_NOPLMW);
ii_read_lock_parent(a->dir);
bfound = -1;
bend = au_ibend(a->dir);
for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++)
if (au_h_iptr(a->dir, bindex) == a->h_dir) {
bfound = bindex;
break;
}
ii_read_unlock(a->dir);
if (unlikely(bfound < 0))
goto out;
xino = !!au_opt_test(au_mntflags(sb), XINO);
h_ino = 0;
if (a->h_child_inode)
h_ino = a->h_child_inode->i_ino;
if (a->h_child_nlen
&& (au_ftest_hnjob(a->flags[AuHn_CHILD], GEN)
|| au_ftest_hnjob(a->flags[AuHn_CHILD], MNTPNT)))
dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen,
a->dir);
try_iput = 0;
if (dentry)
inode = dentry->d_inode;
if (xino && !inode && h_ino
&& (au_ftest_hnjob(a->flags[AuHn_CHILD], XINO0)
|| au_ftest_hnjob(a->flags[AuHn_CHILD], TRYXINO0)
|| au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) {
inode = lookup_wlock_by_ino(sb, bfound, h_ino);
try_iput = 1;
}
args.flags = a->flags[AuHn_CHILD];
args.dentry = dentry;
args.inode = inode;
args.h_inode = a->h_child_inode;
args.dir = a->dir;
args.h_dir = a->h_dir;
args.h_name = a->h_child_name;
args.h_nlen = a->h_child_nlen;
err = hn_job(&args);
if (dentry) {
if (au_di(dentry))
di_write_unlock(dentry);
dput(dentry);
}
if (inode && try_iput) {
ii_write_unlock(inode);
iput(inode);
}
ii_write_lock_parent(a->dir);
args.flags = a->flags[AuHn_PARENT];
args.dentry = NULL;
args.inode = a->dir;
args.h_inode = a->h_dir;
args.dir = NULL;
args.h_dir = NULL;
args.h_name = NULL;
args.h_nlen = 0;
err = hn_job(&args);
ii_write_unlock(a->dir);
out:
iput(a->h_child_inode);
iput(a->h_dir);
iput(a->dir);
si_write_unlock(sb);
au_nwt_done(&sbinfo->si_nowait);
kfree(a);
}
/* ---------------------------------------------------------------------- */
int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask,
struct qstr *h_child_qstr, struct inode *h_child_inode)
{
int err, len;
unsigned int flags[AuHnLast], f;
unsigned char isdir, isroot, wh;
struct inode *dir;
struct au_hnotify_args *args;
char *p, *h_child_name;
err = 0;
AuDebugOn(!hnotify || !hnotify->hn_aufs_inode);
dir = igrab(hnotify->hn_aufs_inode);
if (!dir)
goto out;
isroot = (dir->i_ino == AUFS_ROOT_INO);
wh = 0;
h_child_name = (void *)h_child_qstr->name;
len = h_child_qstr->len;
if (h_child_name) {
if (len > AUFS_WH_PFX_LEN
&& !memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
h_child_name += AUFS_WH_PFX_LEN;
len -= AUFS_WH_PFX_LEN;
wh = 1;
}
}
isdir = 0;
if (h_child_inode)
isdir = !!S_ISDIR(h_child_inode->i_mode);
flags[AuHn_PARENT] = AuHnJob_ISDIR;
flags[AuHn_CHILD] = 0;
if (isdir)
flags[AuHn_CHILD] = AuHnJob_ISDIR;
au_fset_hnjob(flags[AuHn_PARENT], DIRENT);
au_fset_hnjob(flags[AuHn_CHILD], GEN);
switch (mask & FS_EVENTS_POSS_ON_CHILD) {
case FS_MOVED_FROM:
case FS_MOVED_TO:
au_fset_hnjob(flags[AuHn_CHILD], XINO0);
au_fset_hnjob(flags[AuHn_CHILD], MNTPNT);
/*FALLTHROUGH*/
case FS_CREATE:
AuDebugOn(!h_child_name || !h_child_inode);
break;
case FS_DELETE:
/*
* aufs never be able to get this child inode.
* revalidation should be in d_revalidate()
* by checking i_nlink, i_generation or d_unhashed().
*/
AuDebugOn(!h_child_name);
au_fset_hnjob(flags[AuHn_CHILD], TRYXINO0);
au_fset_hnjob(flags[AuHn_CHILD], MNTPNT);
break;
default:
AuDebugOn(1);
}
if (wh)
h_child_inode = NULL;
err = -ENOMEM;
/* iput() and kfree() will be called in au_hnotify() */
args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS);
if (unlikely(!args)) {
AuErr1("no memory\n");
iput(dir);
goto out;
}
args->flags[AuHn_PARENT] = flags[AuHn_PARENT];
args->flags[AuHn_CHILD] = flags[AuHn_CHILD];
args->mask = mask;
args->dir = dir;
args->h_dir = igrab(h_dir);
if (h_child_inode)
h_child_inode = igrab(h_child_inode); /* can be NULL */
args->h_child_inode = h_child_inode;
args->h_child_nlen = len;
if (len) {
p = (void *)args;
p += sizeof(*args);
memcpy(p, h_child_name, len);
p[len] = 0;
}
f = 0;
if (!dir->i_nlink)
f = AuWkq_NEST;
err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f);
if (unlikely(err)) {
pr_err("wkq %d\n", err);
iput(args->h_child_inode);
iput(args->h_dir);
iput(args->dir);
kfree(args);
}
out:
return err;
}
/* ---------------------------------------------------------------------- */
int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm)
{
int err;
AuDebugOn(!(udba & AuOptMask_UDBA));
err = 0;
if (au_hnotify_op.reset_br)
err = au_hnotify_op.reset_br(udba, br, perm);
return err;
}
int au_hnotify_init_br(struct au_branch *br, int perm)
{
int err;
err = 0;
if (au_hnotify_op.init_br)
err = au_hnotify_op.init_br(br, perm);
return err;
}
void au_hnotify_fin_br(struct au_branch *br)
{
if (au_hnotify_op.fin_br)
au_hnotify_op.fin_br(br);
}
static void au_hn_destroy_cache(void)
{
kmem_cache_destroy(au_cachep[AuCache_HNOTIFY]);
au_cachep[AuCache_HNOTIFY] = NULL;
}
int __init au_hnotify_init(void)
{
int err;
err = -ENOMEM;
au_cachep[AuCache_HNOTIFY] = AuCache(au_hnotify);
if (au_cachep[AuCache_HNOTIFY]) {
err = 0;
if (au_hnotify_op.init)
err = au_hnotify_op.init();
if (unlikely(err))
au_hn_destroy_cache();
}
AuTraceErr(err);
return err;
}
void au_hnotify_fin(void)
{
if (au_hnotify_op.fin)
au_hnotify_op.fin();
/* cf. au_cache_fin() */
if (au_cachep[AuCache_HNOTIFY])
au_hn_destroy_cache();
}

View File

@ -0,0 +1,985 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* inode operations (except add/del/rename)
*/
#include <linux/device_cgroup.h>
#include <linux/fs_stack.h>
#include <linux/namei.h>
#include <linux/security.h>
#include "aufs.h"
static int h_permission(struct inode *h_inode, int mask, unsigned int flags,
struct vfsmount *h_mnt, int brperm)
{
int err;
const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));
err = -EACCES;
if ((write_mask && IS_IMMUTABLE(h_inode))
|| ((mask & MAY_EXEC)
&& S_ISREG(h_inode->i_mode)
&& ((h_mnt->mnt_flags & MNT_NOEXEC)
|| !(h_inode->i_mode & S_IXUGO))))
goto out;
/*
* - skip the lower fs test in the case of write to ro branch.
* - nfs dir permission write check is optimized, but a policy for
* link/rename requires a real check.
*/
if ((write_mask && !au_br_writable(brperm))
|| (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode)
&& write_mask && !(mask & MAY_READ))
|| !h_inode->i_op->permission) {
/* AuLabel(generic_permission); */
err = generic_permission(h_inode, mask, flags,
h_inode->i_op->check_acl);
} else {
/* AuLabel(h_inode->permission); */
err = h_inode->i_op->permission(h_inode, mask, flags);
AuTraceErr(err);
}
if (!err)
err = devcgroup_inode_permission(h_inode, mask);
if (!err)
err = security_inode_permission(h_inode, mask);
#if 0
if (!err) {
/* todo: do we need to call ima_path_check()? */
struct path h_path = {
.dentry =
.mnt = h_mnt
};
err = ima_path_check(&h_path,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
IMA_COUNT_LEAVE);
}
#endif
out:
return err;
}
static int aufs_permission(struct inode *inode, int mask, unsigned int flags)
{
int err;
aufs_bindex_t bindex, bend;
const unsigned char isdir = !!S_ISDIR(inode->i_mode),
write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));
struct inode *h_inode;
struct super_block *sb;
struct au_branch *br;
/* todo: support rcu-walk? */
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
sb = inode->i_sb;
si_read_lock(sb, AuLock_FLUSH);
ii_read_lock_child(inode);
#if 0
err = au_iigen_test(inode, au_sigen(sb));
if (unlikely(err))
goto out;
#endif
if (!isdir || write_mask) {
err = au_busy_or_stale();
h_inode = au_h_iptr(inode, au_ibstart(inode));
if (unlikely(!h_inode
|| (h_inode->i_mode & S_IFMT)
!= (inode->i_mode & S_IFMT)))
goto out;
err = 0;
bindex = au_ibstart(inode);
br = au_sbr(sb, bindex);
err = h_permission(h_inode, mask, flags, br->br_mnt,
br->br_perm);
if (write_mask
&& !err
&& !special_file(h_inode->i_mode)) {
/* test whether the upper writable branch exists */
err = -EROFS;
for (; bindex >= 0; bindex--)
if (!au_br_rdonly(au_sbr(sb, bindex))) {
err = 0;
break;
}
}
goto out;
}
/* non-write to dir */
err = 0;
bend = au_ibend(inode);
for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) {
h_inode = au_h_iptr(inode, bindex);
if (h_inode) {
err = au_busy_or_stale();
if (unlikely(!S_ISDIR(h_inode->i_mode)))
break;
br = au_sbr(sb, bindex);
err = h_permission(h_inode, mask, flags, br->br_mnt,
br->br_perm);
}
}
out:
ii_read_unlock(inode);
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
struct dentry *ret, *parent;
struct inode *inode;
struct super_block *sb;
int err, npositive, lc_idx;
IMustLock(dir);
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))
goto out_si;
inode = NULL;
npositive = 0; /* suppress a warning */
parent = dentry->d_parent; /* dir inode is locked */
di_read_lock_parent(parent, AuLock_IR);
err = au_alive_dir(parent);
if (!err)
err = au_digen_test(parent, au_sigen(sb));
if (!err) {
npositive = au_lkup_dentry(dentry, au_dbstart(parent),
/*type*/0, nd);
err = npositive;
}
di_read_unlock(parent, AuLock_IR);
ret = ERR_PTR(err);
if (unlikely(err < 0))
goto out_unlock;
if (npositive) {
inode = au_new_inode(dentry, /*must_new*/0);
ret = (void *)inode;
}
if (IS_ERR(inode)) {
inode = NULL;
goto out_unlock;
}
ret = d_splice_alias(inode, dentry);
if (unlikely(IS_ERR(ret) && inode)) {
ii_write_unlock(inode);
iput(inode);
inode = NULL;
}
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);
}
out_si:
si_read_unlock(sb);
out:
return ret;
}
/* ---------------------------------------------------------------------- */
static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent,
const unsigned char add_entry, aufs_bindex_t bcpup,
aufs_bindex_t bstart)
{
int err;
struct dentry *h_parent;
struct inode *h_dir;
if (add_entry)
IMustLock(parent->d_inode);
else
di_write_lock_parent(parent);
err = 0;
if (!au_h_dptr(parent, bcpup)) {
if (bstart < bcpup)
err = au_cpdown_dirs(dentry, bcpup);
else
err = au_cpup_dirs(dentry, bcpup);
}
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);
/* todo: no unlock here */
mutex_unlock(&h_dir->i_mutex);
AuDbg("bcpup %d\n", bcpup);
if (!err) {
if (!dentry->d_inode)
au_set_h_dptr(dentry, bstart, NULL);
au_update_dbrange(dentry, /*do_put_zero*/0);
}
}
if (!add_entry)
di_write_unlock(parent);
if (!err)
err = bcpup; /* success */
AuTraceErr(err);
return err;
}
/*
* decide the branch and the parent dir where we will create a new entry.
* returns new bindex or an error.
* copyup the parent dir if needed.
*/
int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
struct au_wr_dir_args *args)
{
int err;
aufs_bindex_t bcpup, bstart, src_bstart;
const unsigned char add_entry = !!au_ftest_wrdir(args->flags,
ADD_ENTRY);
struct super_block *sb;
struct dentry *parent;
struct au_sbinfo *sbinfo;
sb = dentry->d_sb;
sbinfo = au_sbi(sb);
parent = dget_parent(dentry);
bstart = au_dbstart(dentry);
bcpup = bstart;
if (args->force_btgt < 0) {
if (src_dentry) {
src_bstart = au_dbstart(src_dentry);
if (src_bstart < bstart)
bcpup = src_bstart;
} else if (add_entry) {
err = AuWbrCreate(sbinfo, dentry,
au_ftest_wrdir(args->flags, ISDIR));
bcpup = err;
}
if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) {
if (add_entry)
err = AuWbrCopyup(sbinfo, dentry);
else {
if (!IS_ROOT(dentry)) {
di_read_lock_parent(parent, !AuLock_IR);
err = AuWbrCopyup(sbinfo, dentry);
di_read_unlock(parent, !AuLock_IR);
} else
err = AuWbrCopyup(sbinfo, dentry);
}
bcpup = err;
if (unlikely(err < 0))
goto out;
}
} else {
bcpup = args->force_btgt;
AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode));
}
AuDbg("bstart %d, bcpup %d\n", bstart, bcpup);
err = bcpup;
if (bcpup == bstart)
goto out; /* success */
/* copyup the new parent into the branch we process */
err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart);
if (err >= 0) {
if (!dentry->d_inode) {
au_set_h_dptr(dentry, bstart, NULL);
au_set_dbstart(dentry, bcpup);
au_set_dbend(dentry, bcpup);
}
AuDebugOn(add_entry && !au_h_dptr(dentry, bcpup));
}
out:
dput(parent);
return err;
}
/* ---------------------------------------------------------------------- */
struct dentry *au_pinned_h_parent(struct au_pin *pin)
{
if (pin && pin->parent)
return au_h_dptr(pin->parent, pin->bindex);
return NULL;
}
void au_unpin(struct au_pin *p)
{
if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE))
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);
dput(p->parent);
p->parent = NULL;
p->hdir = NULL;
p->h_mnt = NULL;
}
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);
if (IS_ROOT(p->dentry)) {
if (au_ftest_pin(p->flags, MNT_WRITE)) {
p->h_mnt = br->br_mnt;
err = mnt_want_write(p->h_mnt);
if (unlikely(err)) {
au_fclr_pin(p->flags, MNT_WRITE);
goto out_err;
}
}
goto out;
}
h_dentry = NULL;
if (p->bindex <= au_dbend(p->dentry))
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->hdir = au_hi(p->parent->d_inode, p->bindex);
if (p->hdir)
h_dir = p->hdir->hi_inode;
/*
* udba case, or
* 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)) {
err = -EBUSY;
if (!au_ftest_pin(p->flags, DI_LOCKED))
di_read_unlock(p->parent, AuLock_IR);
dput(p->parent);
p->parent = NULL;
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;
err = mnt_want_write(p->h_mnt);
if (unlikely(err)) {
au_fclr_pin(p->flags, MNT_WRITE);
goto out_unpin;
}
}
goto out; /* success */
out_unpin:
au_unpin(p);
out_err:
pr_err("err %d\n", err);
err = au_busy_or_stale();
out:
return err;
}
void au_pin_init(struct au_pin *p, struct dentry *dentry,
aufs_bindex_t bindex, int lsc_di, int lsc_hi,
unsigned int udba, unsigned char flags)
{
p->dentry = dentry;
p->udba = udba;
p->lsc_di = lsc_di;
p->lsc_hi = lsc_hi;
p->flags = flags;
p->bindex = bindex;
p->parent = NULL;
p->hdir = NULL;
p->h_mnt = NULL;
}
int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
unsigned int udba, unsigned char flags)
{
au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2,
udba, flags);
return au_do_pin(pin);
}
/* ---------------------------------------------------------------------- */
/*
* ->setattr() and ->getattr() are called in various cases.
* chmod, stat: dentry is revalidated.
* fchmod, fstat: file and dentry are not revalidated, additionally they may be
* unhashed.
* for ->setattr(), ia->ia_file is passed from ftruncate only.
*/
/* todo: consolidate with do_refresh() and simple_reval_dpath() */
static int au_reval_for_attr(struct dentry *dentry, unsigned int sigen)
{
int err;
struct inode *inode;
struct dentry *parent;
err = 0;
inode = dentry->d_inode;
if (au_digen_test(dentry, sigen)) {
parent = dget_parent(dentry);
di_read_lock_parent(parent, AuLock_IR);
err = au_refresh_dentry(dentry, parent);
di_read_unlock(parent, AuLock_IR);
dput(parent);
}
AuTraceErr(err);
return err;
}
#define AuIcpup_DID_CPUP 1
#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name)
#define au_fset_icpup(flags, name) \
do { (flags) |= AuIcpup_##name; } while (0)
#define au_fclr_icpup(flags, name) \
do { (flags) &= ~AuIcpup_##name; } while (0)
struct au_icpup_args {
unsigned char flags;
unsigned char pin_flags;
aufs_bindex_t btgt;
unsigned int udba;
struct au_pin pin;
struct path h_path;
struct inode *h_inode;
};
static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
struct au_icpup_args *a)
{
int err;
loff_t sz;
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
};
bstart = au_dbstart(dentry);
inode = dentry->d_inode;
if (S_ISDIR(inode->i_mode))
au_fset_wrdir(wr_dir_args.flags, ISDIR);
/* plink or hi_wh() case */
ibstart = au_ibstart(inode);
if (bstart != ibstart && !au_test_ro(inode->i_sb, ibstart, inode))
wr_dir_args.force_btgt = ibstart;
err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args);
if (unlikely(err < 0))
goto out;
a->btgt = err;
if (err != bstart)
au_fset_icpup(a->flags, DID_CPUP);
err = 0;
a->pin_flags = AuPin_MNT_WRITE;
parent = NULL;
if (!IS_ROOT(dentry)) {
au_fset_pin(a->pin_flags, DI_LOCKED);
parent = dget_parent(dentry);
di_write_lock_parent(parent);
}
err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags);
if (unlikely(err))
goto out_parent;
a->h_path.dentry = au_h_dptr(dentry, bstart);
a->h_inode = a->h_path.dentry->d_inode;
mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
sz = -1;
if ((ia->ia_valid & ATTR_SIZE) && ia->ia_size < i_size_read(a->h_inode))
sz = ia->ia_size;
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);
if (unlikely(err))
goto out_unlock;
hi_wh = au_hi_wh(inode, a->btgt);
/* todo: revalidate hi_wh? */
}
}
if (parent) {
au_pin_set_parent_lflag(&a->pin, /*lflag*/0);
di_downgrade_lock(parent, AuLock_IR);
dput(parent);
parent = NULL;
}
if (!au_ftest_icpup(a->flags, DID_CPUP))
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);
if (!err)
a->h_path.dentry = au_h_dptr(dentry, a->btgt);
} else if (!hi_wh)
a->h_path.dentry = au_h_dptr(dentry, a->btgt);
else
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);
goto out; /* success */
}
au_unpin(&a->pin);
out_parent:
if (parent) {
di_write_unlock(parent);
dput(parent);
}
out:
return err;
}
static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
{
int err;
struct inode *inode;
struct super_block *sb;
struct file *file;
struct au_icpup_args *a;
inode = dentry->d_inode;
IMustLock(inode);
err = -ENOMEM;
a = kzalloc(sizeof(*a), GFP_NOFS);
if (unlikely(!a))
goto out;
if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
ia->ia_valid &= ~ATTR_MODE;
file = NULL;
sb = dentry->d_sb;
err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
if (unlikely(err))
goto out_kfree;
if (ia->ia_valid & ATTR_FILE) {
/* currently ftruncate(2) only */
AuDebugOn(!S_ISREG(inode->i_mode));
file = ia->ia_file;
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
if (unlikely(err))
goto out_si;
ia->ia_file = au_hf_top(file);
a->udba = AuOpt_UDBA_NONE;
} else {
/* fchmod() doesn't pass ia_file */
a->udba = au_opt_udba(sb);
di_write_lock_child(dentry);
/* no d_unlinked(), to set UDBA_NONE for root */
if (d_unhashed(dentry))
a->udba = AuOpt_UDBA_NONE;
if (a->udba != AuOpt_UDBA_NONE) {
AuDebugOn(IS_ROOT(dentry));
err = au_reval_for_attr(dentry, au_sigen(sb));
if (unlikely(err))
goto out_dentry;
}
}
err = au_pin_and_icpup(dentry, ia, a);
if (unlikely(err < 0))
goto out_dentry;
if (au_ftest_icpup(a->flags, DID_CPUP)) {
ia->ia_file = NULL;
ia->ia_valid &= ~ATTR_FILE;
}
a->h_path.mnt = au_sbr_mnt(sb, a->btgt);
if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME))
== (ATTR_MODE | ATTR_CTIME)) {
err = security_path_chmod(a->h_path.dentry, a->h_path.mnt,
ia->ia_mode);
if (unlikely(err))
goto out_unlock;
} else if ((ia->ia_valid & (ATTR_UID | ATTR_GID))
&& (ia->ia_valid & ATTR_CTIME)) {
err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid);
if (unlikely(err))
goto out_unlock;
}
if (ia->ia_valid & ATTR_SIZE) {
struct file *f;
if (ia->ia_size < i_size_read(inode))
/* unmap only */
truncate_setsize(inode, ia->ia_size);
f = NULL;
if (ia->ia_valid & ATTR_FILE)
f = ia->ia_file;
mutex_unlock(&a->h_inode->i_mutex);
err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f);
mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
} else
err = vfsub_notify_change(&a->h_path, ia);
if (!err)
au_cpup_attr_changeable(inode);
out_unlock:
mutex_unlock(&a->h_inode->i_mutex);
au_unpin(&a->pin);
if (unlikely(err))
au_update_dbstart(dentry);
out_dentry:
di_write_unlock(dentry);
if (file) {
fi_write_unlock(file);
ia->ia_file = file;
ia->ia_valid |= ATTR_FILE;
}
out_si:
si_read_unlock(sb);
out_kfree:
kfree(a);
out:
AuTraceErr(err);
return err;
}
static void au_refresh_iattr(struct inode *inode, struct kstat *st,
unsigned int nlink)
{
inode->i_mode = st->mode;
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;
au_cpup_attr_nlink(inode, /*force*/0);
if (S_ISDIR(inode->i_mode)) {
inode->i_nlink -= nlink;
inode->i_nlink += st->nlink;
}
spin_lock(&inode->i_lock);
inode->i_blocks = st->blocks;
i_size_write(inode, st->size);
spin_unlock(&inode->i_lock);
}
static int aufs_getattr(struct vfsmount *mnt __maybe_unused,
struct dentry *dentry, struct kstat *st)
{
int err;
unsigned int mnt_flags;
aufs_bindex_t bindex;
unsigned char udba_none, positive;
struct super_block *sb, *h_sb;
struct inode *inode;
struct vfsmount *h_mnt;
struct dentry *h_dentry;
sb = dentry->d_sb;
inode = dentry->d_inode;
err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
if (unlikely(err))
goto out;
mnt_flags = au_mntflags(sb);
udba_none = !!au_opt_test(mnt_flags, UDBA_NONE);
/* support fstat(2) */
if (!d_unlinked(dentry) && !udba_none) {
unsigned int sigen = au_sigen(sb);
err = au_digen_test(dentry, sigen);
if (!err) {
di_read_lock_child(dentry, AuLock_IR);
err = au_dbrange_test(dentry);
if (unlikely(err))
goto out_unlock;
} else {
AuDebugOn(IS_ROOT(dentry));
di_write_lock_child(dentry);
err = au_dbrange_test(dentry);
if (!err)
err = au_reval_for_attr(dentry, sigen);
di_downgrade_lock(dentry, AuLock_IR);
if (unlikely(err))
goto out_unlock;
}
} else
di_read_lock_child(dentry, AuLock_IR);
bindex = au_ibstart(inode);
h_mnt = au_sbr_mnt(sb, bindex);
h_sb = h_mnt->mnt_sb;
if (!au_test_fs_bad_iattr(h_sb) && udba_none)
goto out_fill; /* success */
h_dentry = NULL;
if (au_dbstart(dentry) == bindex)
h_dentry = dget(au_h_dptr(dentry, bindex));
else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) {
h_dentry = au_plink_lkup(inode, bindex);
if (IS_ERR(h_dentry))
goto out_fill; /* pretending success */
}
/* illegally overlapped or something */
if (unlikely(!h_dentry))
goto out_fill; /* pretending success */
positive = !!h_dentry->d_inode;
if (positive)
err = vfs_getattr(h_mnt, h_dentry, st);
dput(h_dentry);
if (!err) {
if (positive)
au_refresh_iattr(inode, st, h_dentry->d_inode->i_nlink);
goto out_fill; /* success */
}
AuTraceErr(err);
goto out_unlock;
out_fill:
generic_fillattr(inode, st);
out_unlock:
di_read_unlock(dentry, AuLock_IR);
si_read_unlock(sb);
out:
AuTraceErr(err);
return err;
}
/* ---------------------------------------------------------------------- */
static int h_readlink(struct dentry *dentry, int bindex, char __user *buf,
int bufsiz)
{
int err;
struct super_block *sb;
struct dentry *h_dentry;
err = -EINVAL;
h_dentry = au_h_dptr(dentry, bindex);
if (unlikely(!h_dentry->d_inode->i_op->readlink))
goto out;
err = security_inode_readlink(h_dentry);
if (unlikely(err))
goto out;
sb = dentry->d_sb;
if (!au_test_ro(sb, bindex, dentry->d_inode)) {
vfsub_touch_atime(au_sbr_mnt(sb, bindex), h_dentry);
fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode);
}
err = h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz);
out:
return err;
}
static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
{
int err;
err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN);
if (unlikely(err))
goto out;
err = au_d_hashed_positive(dentry);
if (!err)
err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz);
aufs_read_unlock(dentry, AuLock_IR);
out:
return err;
}
static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
int err;
mm_segment_t old_fs;
union {
char *k;
char __user *u;
} buf;
err = -ENOMEM;
buf.k = __getname_gfp(GFP_NOFS);
if (unlikely(!buf.k))
goto out;
err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN);
if (unlikely(err))
goto out_name;
err = au_d_hashed_positive(dentry);
if (!err) {
old_fs = get_fs();
set_fs(KERNEL_DS);
err = h_readlink(dentry, au_dbstart(dentry), buf.u, PATH_MAX);
set_fs(old_fs);
}
aufs_read_unlock(dentry, AuLock_IR);
if (err >= 0) {
buf.k[err] = 0;
/* will be freed by put_link */
nd_set_link(nd, buf.k);
return NULL; /* success */
}
out_name:
__putname(buf.k);
out:
path_put(&nd->path);
AuTraceErr(err);
return ERR_PTR(err);
}
static void aufs_put_link(struct dentry *dentry __maybe_unused,
struct nameidata *nd, void *cookie __maybe_unused)
{
__putname(nd_get_link(nd));
}
/* ---------------------------------------------------------------------- */
static void aufs_truncate_range(struct inode *inode __maybe_unused,
loff_t start __maybe_unused,
loff_t end __maybe_unused)
{
AuUnsupport();
}
/* ---------------------------------------------------------------------- */
struct inode_operations aufs_symlink_iop = {
.permission = aufs_permission,
.setattr = aufs_setattr,
.getattr = aufs_getattr,
.readlink = aufs_readlink,
.follow_link = aufs_follow_link,
.put_link = aufs_put_link
};
struct inode_operations aufs_dir_iop = {
.create = aufs_create,
.lookup = aufs_lookup,
.link = aufs_link,
.unlink = aufs_unlink,
.symlink = aufs_symlink,
.mkdir = aufs_mkdir,
.rmdir = aufs_rmdir,
.mknod = aufs_mknod,
.rename = aufs_rename,
.permission = aufs_permission,
.setattr = aufs_setattr,
.getattr = aufs_getattr
};
struct inode_operations aufs_iop = {
.permission = aufs_permission,
.setattr = aufs_setattr,
.getattr = aufs_getattr,
.truncate_range = aufs_truncate_range
};

View File

@ -0,0 +1,711 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* inode operations (add entry)
*/
#include "aufs.h"
/*
* final procedure of adding a new entry, except link(2).
* remove whiteout, instantiate, copyup the parent dir's times and size
* and update version.
* if it failed, re-create the removed whiteout.
*/
static int epilog(struct inode *dir, aufs_bindex_t bindex,
struct dentry *wh_dentry, struct dentry *dentry)
{
int err, rerr;
aufs_bindex_t bwh;
struct path h_path;
struct inode *inode, *h_dir;
struct dentry *wh;
bwh = -1;
if (wh_dentry) {
h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */
IMustLock(h_dir);
AuDebugOn(au_h_iptr(dir, bindex) != h_dir);
bwh = au_dbwh(dentry);
h_path.dentry = wh_dentry;
h_path.mnt = au_sbr_mnt(dir->i_sb, bindex);
err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path,
dentry);
if (unlikely(err))
goto out;
}
inode = au_new_inode(dentry, /*must_new*/1);
if (!IS_ERR(inode)) {
d_instantiate(dentry, inode);
dir = dentry->d_parent->d_inode; /* dir inode is locked */
IMustLock(dir);
if (au_ibstart(dir) == au_dbstart(dentry))
au_cpup_attr_timesizes(dir);
dir->i_version++;
return 0; /* success */
}
err = PTR_ERR(inode);
if (!wh_dentry)
goto out;
/* revert */
/* dir inode is locked */
wh = au_wh_create(dentry, bwh, wh_dentry->d_parent);
rerr = PTR_ERR(wh);
if (IS_ERR(wh)) {
AuIOErr("%.*s reverting whiteout failed(%d, %d)\n",
AuDLNPair(dentry), err, rerr);
err = -EIO;
} else
dput(wh);
out:
return err;
}
static int au_d_may_add(struct dentry *dentry)
{
int err;
err = 0;
if (unlikely(d_unhashed(dentry)))
err = -ENOENT;
if (unlikely(dentry->d_inode))
err = -EEXIST;
return err;
}
/*
* simple tests for the adding inode operations.
* following the checks in vfs, plus the parent-child relationship.
*/
int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_parent, int isdir)
{
int err;
umode_t h_mode;
struct dentry *h_dentry;
struct inode *h_inode;
err = -ENAMETOOLONG;
if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
goto out;
h_dentry = au_h_dptr(dentry, bindex);
h_inode = h_dentry->d_inode;
if (!dentry->d_inode) {
err = -EEXIST;
if (unlikely(h_inode))
goto out;
} else {
/* rename(2) case */
err = -EIO;
if (unlikely(!h_inode || !h_inode->i_nlink))
goto out;
h_mode = h_inode->i_mode;
if (!isdir) {
err = -EISDIR;
if (unlikely(S_ISDIR(h_mode)))
goto out;
} else if (unlikely(!S_ISDIR(h_mode))) {
err = -ENOTDIR;
goto out;
}
}
err = 0;
/* expected parent dir is locked */
if (unlikely(h_parent != h_dentry->d_parent))
err = -EIO;
out:
AuTraceErr(err);
return err;
}
/*
* initial procedure of adding a new entry.
* prepare writable branch and the parent dir, lock it,
* and lookup whiteout for the new entry.
*/
static struct dentry*
lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt,
struct dentry *src_dentry, struct au_pin *pin,
struct au_wr_dir_args *wr_dir_args)
{
struct dentry *wh_dentry, *h_parent;
struct super_block *sb;
struct au_branch *br;
int err;
unsigned int udba;
aufs_bindex_t bcpup;
AuDbg("%.*s\n", AuDLNPair(dentry));
err = au_wr_dir(dentry, src_dentry, wr_dir_args);
bcpup = err;
wh_dentry = ERR_PTR(err);
if (unlikely(err < 0))
goto out;
sb = dentry->d_sb;
udba = au_opt_udba(sb);
err = au_pin(pin, dentry, bcpup, udba,
AuPin_DI_LOCKED | AuPin_MNT_WRITE);
wh_dentry = ERR_PTR(err);
if (unlikely(err))
goto out;
h_parent = au_pinned_h_parent(pin);
if (udba != AuOpt_UDBA_NONE
&& au_dbstart(dentry) == bcpup)
err = au_may_add(dentry, bcpup, h_parent,
au_ftest_wrdir(wr_dir_args->flags, ISDIR));
else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
err = -ENAMETOOLONG;
wh_dentry = ERR_PTR(err);
if (unlikely(err))
goto out_unpin;
br = au_sbr(sb, bcpup);
if (dt) {
struct path tmp = {
.dentry = h_parent,
.mnt = br->br_mnt
};
au_dtime_store(dt, au_pinned_parent(pin), &tmp);
}
wh_dentry = NULL;
if (bcpup != au_dbwh(dentry))
goto out; /* success */
wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br);
out_unpin:
if (IS_ERR(wh_dentry))
au_unpin(pin);
out:
return wh_dentry;
}
/* ---------------------------------------------------------------------- */
enum { Mknod, Symlink, Creat };
struct simple_arg {
int type;
union {
struct {
int mode;
struct nameidata *nd;
} c;
struct {
const char *symname;
} s;
struct {
int mode;
dev_t dev;
} m;
} u;
};
static int add_simple(struct inode *dir, struct dentry *dentry,
struct simple_arg *arg)
{
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
};
AuDbg("%.*s\n", AuDLNPair(dentry));
IMustLock(dir);
parent = dentry->d_parent; /* dir inode is locked */
err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
if (unlikely(err))
goto out;
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);
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);
switch (arg->type) {
case Creat:
err = vfsub_create(h_dir, &h_path, arg->u.c.mode);
break;
case Symlink:
err = vfsub_symlink(h_dir, &h_path, arg->u.s.symname);
break;
case Mknod:
err = vfsub_mknod(h_dir, &h_path, arg->u.m.mode, arg->u.m.dev);
break;
default:
BUG();
}
created = !err;
if (!err)
err = epilog(dir, bstart, wh_dentry, dentry);
/* revert */
if (unlikely(created && err && h_path.dentry->d_inode)) {
int rerr;
rerr = vfsub_unlink(h_dir, &h_path, /*force*/0);
if (rerr) {
AuIOErr("%.*s revert failure(%d, %d)\n",
AuDLNPair(dentry), err, rerr);
err = -EIO;
}
au_dtime_revert(&dt);
}
au_unpin(&pin);
dput(wh_dentry);
out_parent:
di_write_unlock(parent);
out_unlock:
if (unlikely(err)) {
au_update_dbstart(dentry);
d_drop(dentry);
}
aufs_read_unlock(dentry, AuLock_DW);
out:
return err;
}
int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
struct simple_arg arg = {
.type = Mknod,
.u.m = {
.mode = mode,
.dev = dev
}
};
return add_simple(dir, dentry, &arg);
}
int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
struct simple_arg arg = {
.type = Symlink,
.u.s.symname = symname
};
return add_simple(dir, dentry, &arg);
}
int aufs_create(struct inode *dir, struct dentry *dentry, int mode,
struct nameidata *nd)
{
struct simple_arg arg = {
.type = Creat,
.u.c = {
.mode = mode,
.nd = nd
}
};
return add_simple(dir, dentry, &arg);
}
/* ---------------------------------------------------------------------- */
struct au_link_args {
aufs_bindex_t bdst, bsrc;
struct au_pin pin;
struct path h_path;
struct dentry *src_parent, *parent;
};
static int au_cpup_before_link(struct dentry *src_dentry,
struct au_link_args *a)
{
int err;
struct dentry *h_src_dentry;
struct mutex *h_mtx;
struct file *h_file;
di_read_lock_parent(a->src_parent, AuLock_IR);
err = au_test_and_cpup_dirs(src_dentry, a->bdst);
if (unlikely(err))
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);
au_unpin(&a->pin);
out:
di_read_unlock(a->src_parent, AuLock_IR);
return err;
}
static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a)
{
int err;
unsigned char plink;
struct inode *h_inode, *inode;
struct dentry *h_src_dentry;
struct super_block *sb;
struct file *h_file;
plink = 0;
h_inode = NULL;
sb = src_dentry->d_sb;
inode = src_dentry->d_inode;
if (au_ibstart(inode) <= a->bdst)
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)) {
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 {
/* the inode of src_dentry already exists on a.bdst branch */
h_src_dentry = d_find_alias(h_inode);
if (!h_src_dentry && au_plink_test(inode)) {
plink = 1;
h_src_dentry = au_plink_lkup(inode, a->bdst);
err = PTR_ERR(h_src_dentry);
if (IS_ERR(h_src_dentry))
goto out;
if (unlikely(!h_src_dentry->d_inode)) {
dput(h_src_dentry);
h_src_dentry = NULL;
}
}
if (h_src_dentry) {
err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin),
&a->h_path);
dput(h_src_dentry);
} else {
AuIOErr("no dentry found for hi%lu on b%d\n",
h_inode->i_ino, a->bdst);
err = -EIO;
}
}
if (!err && !plink)
au_plink_append(inode, a->bdst, a->h_path.dentry);
out:
AuTraceErr(err);
return err;
}
int aufs_link(struct dentry *src_dentry, struct inode *dir,
struct dentry *dentry)
{
int err, rerr;
struct au_dtime dt;
struct au_link_args *a;
struct dentry *wh_dentry, *h_src_dentry;
struct inode *inode;
struct super_block *sb;
struct au_wr_dir_args wr_dir_args = {
/* .force_btgt = -1, */
.flags = AuWrDir_ADD_ENTRY
};
IMustLock(dir);
inode = src_dentry->d_inode;
IMustLock(inode);
err = -ENOMEM;
a = kzalloc(sizeof(*a), GFP_NOFS);
if (unlikely(!a))
goto out;
a->parent = dentry->d_parent; /* dir inode is locked */
err = aufs_read_and_write_lock2(dentry, src_dentry,
AuLock_NOPLM | AuLock_GEN);
if (unlikely(err))
goto out_kfree;
err = au_d_hashed_positive(src_dentry);
if (unlikely(err))
goto out_unlock;
err = au_d_may_add(dentry);
if (unlikely(err))
goto out_unlock;
a->src_parent = dget_parent(src_dentry);
wr_dir_args.force_btgt = au_ibstart(inode);
di_write_lock_parent(a->parent);
wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt);
wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin,
&wr_dir_args);
err = PTR_ERR(wh_dentry);
if (IS_ERR(wh_dentry))
goto out_parent;
err = 0;
sb = dentry->d_sb;
a->bdst = au_dbstart(dentry);
a->h_path.dentry = au_h_dptr(dentry, a->bdst);
a->h_path.mnt = au_sbr_mnt(sb, a->bdst);
a->bsrc = au_ibstart(inode);
h_src_dentry = au_h_d_alias(src_dentry, a->bsrc);
if (!h_src_dentry) {
a->bsrc = au_dbstart(src_dentry);
h_src_dentry = au_h_d_alias(src_dentry, a->bsrc);
AuDebugOn(!h_src_dentry);
} else if (IS_ERR(h_src_dentry))
goto out_parent;
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);
else
err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin),
&a->h_path);
dput(h_src_dentry);
} else {
/*
* copyup src_dentry to the branch we process,
* and then link(2) to it.
*/
dput(h_src_dentry);
if (a->bdst < a->bsrc
/* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) {
au_unpin(&a->pin);
di_write_unlock(a->parent);
err = au_cpup_before_link(src_dentry, a);
di_write_lock_parent(a->parent);
if (!err)
err = au_pin(&a->pin, dentry, a->bdst,
au_opt_udba(sb),
AuPin_DI_LOCKED | AuPin_MNT_WRITE);
if (unlikely(err))
goto out_wh;
}
if (!err) {
h_src_dentry = au_h_dptr(src_dentry, a->bdst);
err = -ENOENT;
if (h_src_dentry && h_src_dentry->d_inode)
err = vfsub_link(h_src_dentry,
au_pinned_h_dir(&a->pin),
&a->h_path);
}
}
if (unlikely(err))
goto out_unpin;
if (wh_dentry) {
a->h_path.dentry = wh_dentry;
err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path,
dentry);
if (unlikely(err))
goto out_revert;
}
dir->i_version++;
if (au_ibstart(dir) == au_dbstart(dentry))
au_cpup_attr_timesizes(dir);
inc_nlink(inode);
inode->i_ctime = dir->i_ctime;
d_instantiate(dentry, au_igrab(inode));
if (d_unhashed(a->h_path.dentry))
/* some filesystem calls d_drop() */
d_drop(dentry);
goto out_unpin; /* success */
out_revert:
rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, /*force*/0);
if (unlikely(rerr)) {
AuIOErr("%.*s reverting failed(%d, %d)\n",
AuDLNPair(dentry), err, rerr);
err = -EIO;
}
au_dtime_revert(&dt);
out_unpin:
au_unpin(&a->pin);
out_wh:
dput(wh_dentry);
out_parent:
di_write_unlock(a->parent);
dput(a->src_parent);
out_unlock:
if (unlikely(err)) {
au_update_dbstart(dentry);
d_drop(dentry);
}
aufs_read_and_write_unlock2(dentry, src_dentry);
out_kfree:
kfree(a);
out:
return err;
}
int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
int err, rerr;
aufs_bindex_t bindex;
unsigned char diropq;
struct path h_path;
struct dentry *wh_dentry, *parent, *opq_dentry;
struct mutex *h_mtx;
struct super_block *sb;
struct {
struct au_pin pin;
struct au_dtime dt;
} *a; /* reduce the stack usage */
struct au_wr_dir_args wr_dir_args = {
.force_btgt = -1,
.flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR
};
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_free;
err = au_d_may_add(dentry);
if (unlikely(err))
goto out_unlock;
parent = dentry->d_parent; /* dir inode is locked */
di_write_lock_parent(parent);
wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL,
&a->pin, &wr_dir_args);
err = PTR_ERR(wh_dentry);
if (IS_ERR(wh_dentry))
goto out_parent;
sb = dentry->d_sb;
bindex = au_dbstart(dentry);
h_path.dentry = au_h_dptr(dentry, bindex);
h_path.mnt = au_sbr_mnt(sb, bindex);
err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode);
if (unlikely(err))
goto out_unpin;
/* make the dir opaque */
diropq = 0;
h_mtx = &h_path.dentry->d_inode->i_mutex;
if (wh_dentry
|| au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) {
mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
opq_dentry = au_diropq_create(dentry, bindex);
mutex_unlock(h_mtx);
err = PTR_ERR(opq_dentry);
if (IS_ERR(opq_dentry))
goto out_dir;
dput(opq_dentry);
diropq = 1;
}
err = epilog(dir, bindex, wh_dentry, dentry);
if (!err) {
inc_nlink(dir);
goto out_unpin; /* success */
}
/* revert */
if (diropq) {
AuLabel(revert opq);
mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
rerr = au_diropq_remove(dentry, bindex);
mutex_unlock(h_mtx);
if (rerr) {
AuIOErr("%.*s reverting diropq failed(%d, %d)\n",
AuDLNPair(dentry), err, rerr);
err = -EIO;
}
}
out_dir:
AuLabel(revert dir);
rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path);
if (rerr) {
AuIOErr("%.*s reverting dir failed(%d, %d)\n",
AuDLNPair(dentry), err, rerr);
err = -EIO;
}
au_dtime_revert(&a->dt);
out_unpin:
au_unpin(&a->pin);
dput(wh_dentry);
out_parent:
di_write_unlock(parent);
out_unlock:
if (unlikely(err)) {
au_update_dbstart(dentry);
d_drop(dentry);
}
aufs_read_unlock(dentry, AuLock_DW);
out_free:
kfree(a);
out:
return err;
}

View File

@ -0,0 +1,478 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* inode operations (del entry)
*/
#include "aufs.h"
/*
* decide if a new whiteout for @dentry is necessary or not.
* when it is necessary, prepare the parent dir for the upper branch whose
* branch index is @bcpup for creation. the actual creation of the whiteout will
* be done by caller.
* return value:
* 0: wh is unnecessary
* plus: wh is necessary
* minus: error
*/
int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup)
{
int need_wh, err;
aufs_bindex_t bstart;
struct super_block *sb;
sb = dentry->d_sb;
bstart = au_dbstart(dentry);
if (*bcpup < 0) {
*bcpup = bstart;
if (au_test_ro(sb, bstart, dentry->d_inode)) {
err = AuWbrCopyup(au_sbi(sb), dentry);
*bcpup = err;
if (unlikely(err < 0))
goto out;
}
} else
AuDebugOn(bstart < *bcpup
|| au_test_ro(sb, *bcpup, dentry->d_inode));
AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart);
if (*bcpup != bstart) {
err = au_cpup_dirs(dentry, *bcpup);
if (unlikely(err))
goto out;
need_wh = 1;
} else {
struct au_dinfo *dinfo, *tmp;
need_wh = -ENOMEM;
dinfo = au_di(dentry);
tmp = au_di_alloc(sb, AuLsc_DI_TMP);
if (tmp) {
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,
/*nd*/NULL);
au_di_swap(tmp, dinfo);
au_rw_write_unlock(&tmp->di_rwsem);
au_di_free(tmp);
}
}
AuDbg("need_wh %d\n", need_wh);
err = need_wh;
out:
return err;
}
/*
* simple tests for the del-entry operations.
* following the checks in vfs, plus the parent-child relationship.
*/
int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_parent, int isdir)
{
int err;
umode_t h_mode;
struct dentry *h_dentry, *h_latest;
struct inode *h_inode;
h_dentry = au_h_dptr(dentry, bindex);
h_inode = h_dentry->d_inode;
if (dentry->d_inode) {
err = -ENOENT;
if (unlikely(!h_inode || !h_inode->i_nlink))
goto out;
h_mode = h_inode->i_mode;
if (!isdir) {
err = -EISDIR;
if (unlikely(S_ISDIR(h_mode)))
goto out;
} else if (unlikely(!S_ISDIR(h_mode))) {
err = -ENOTDIR;
goto out;
}
} else {
/* rename(2) case */
err = -EIO;
if (unlikely(h_inode))
goto out;
}
err = -ENOENT;
/* expected parent dir is locked */
if (unlikely(h_parent != h_dentry->d_parent))
goto out;
err = 0;
/*
* rmdir a dir may break the consistency on some filesystem.
* let's try heavy test.
*/
err = -EACCES;
if (unlikely(au_test_h_perm(h_parent->d_inode, MAY_EXEC | MAY_WRITE)))
goto out;
h_latest = au_sio_lkup_one(&dentry->d_name, h_parent,
au_sbr(dentry->d_sb, bindex));
err = -EIO;
if (IS_ERR(h_latest))
goto out;
if (h_latest == h_dentry)
err = 0;
dput(h_latest);
out:
return err;
}
/*
* decide the branch where we operate for @dentry. the branch index will be set
* @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent
* dir for reverting.
* when a new whiteout is necessary, create it.
*/
static struct dentry*
lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup,
struct au_dtime *dt, struct au_pin *pin)
{
struct dentry *wh_dentry;
struct super_block *sb;
struct path h_path;
int err, need_wh;
unsigned int udba;
aufs_bindex_t bcpup;
need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup);
wh_dentry = ERR_PTR(need_wh);
if (unlikely(need_wh < 0))
goto out;
sb = dentry->d_sb;
udba = au_opt_udba(sb);
bcpup = *rbcpup;
err = au_pin(pin, dentry, bcpup, udba,
AuPin_DI_LOCKED | AuPin_MNT_WRITE);
wh_dentry = ERR_PTR(err);
if (unlikely(err))
goto out;
h_path.dentry = au_pinned_h_parent(pin);
if (udba != AuOpt_UDBA_NONE
&& au_dbstart(dentry) == bcpup) {
err = au_may_del(dentry, bcpup, h_path.dentry, isdir);
wh_dentry = ERR_PTR(err);
if (unlikely(err))
goto out_unpin;
}
h_path.mnt = au_sbr_mnt(sb, bcpup);
au_dtime_store(dt, au_pinned_parent(pin), &h_path);
wh_dentry = NULL;
if (!need_wh)
goto out; /* success, no need to create whiteout */
wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry);
if (IS_ERR(wh_dentry))
goto out_unpin;
/* returns with the parent is locked and wh_dentry is dget-ed */
goto out; /* success */
out_unpin:
au_unpin(pin);
out:
return wh_dentry;
}
/*
* when removing a dir, rename it to a unique temporary whiteout-ed name first
* in order to be revertible and save time for removing many child whiteouts
* under the dir.
* returns 1 when there are too many child whiteout and caller should remove
* them asynchronously. returns 0 when the number of children is enough small to
* remove now or the branch fs is a remote fs.
* otherwise return an error.
*/
static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,
struct au_nhash *whlist, struct inode *dir)
{
int rmdir_later, err, dirwh;
struct dentry *h_dentry;
struct super_block *sb;
sb = dentry->d_sb;
SiMustAnyLock(sb);
h_dentry = au_h_dptr(dentry, bindex);
err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex));
if (unlikely(err))
goto out;
/* stop monitoring */
au_hn_free(au_hi(dentry->d_inode, bindex));
if (!au_test_fs_remote(h_dentry->d_sb)) {
dirwh = au_sbi(sb)->si_dirwh;
rmdir_later = (dirwh <= 1);
if (!rmdir_later)
rmdir_later = au_nhash_test_longer_wh(whlist, bindex,
dirwh);
if (rmdir_later)
return rmdir_later;
}
err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist);
if (unlikely(err)) {
AuIOErr("rmdir %.*s, b%d failed, %d. ignored\n",
AuDLNPair(h_dentry), bindex, err);
err = 0;
}
out:
AuTraceErr(err);
return err;
}
/*
* final procedure for deleting a entry.
* maintain dentry and iattr.
*/
static void epilog(struct inode *dir, struct dentry *dentry,
aufs_bindex_t bindex)
{
struct inode *inode;
inode = dentry->d_inode;
d_drop(dentry);
inode->i_ctime = dir->i_ctime;
if (au_ibstart(dir) == bindex)
au_cpup_attr_timesizes(dir);
dir->i_version++;
}
/*
* when an error happened, remove the created whiteout and revert everything.
*/
static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex,
aufs_bindex_t bwh, struct dentry *wh_dentry,
struct dentry *dentry, struct au_dtime *dt)
{
int rerr;
struct path h_path = {
.dentry = wh_dentry,
.mnt = au_sbr_mnt(dir->i_sb, bindex)
};
rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry);
if (!rerr) {
au_set_dbwh(dentry, bwh);
au_dtime_revert(dt);
return 0;
}
AuIOErr("%.*s reverting whiteout failed(%d, %d)\n",
AuDLNPair(dentry), err, rerr);
return -EIO;
}
/* ---------------------------------------------------------------------- */
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;
IMustLock(dir);
err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
if (unlikely(err))
goto out;
err = au_d_hashed_positive(dentry);
if (unlikely(err))
goto out_unlock;
inode = dentry->d_inode;
IMustLock(inode);
err = -EISDIR;
if (unlikely(S_ISDIR(inode->i_mode)))
goto out_unlock; /* possible? */
bstart = au_dbstart(dentry);
bwh = au_dbwh(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);
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);
if (bindex == bstart) {
h_dir = au_pinned_h_dir(&pin);
err = vfsub_unlink(h_dir, &h_path, /*force*/0);
} else {
/* dir inode is locked */
h_dir = wh_dentry->d_parent->d_inode;
IMustLock(h_dir);
err = 0;
}
if (!err) {
vfsub_drop_nlink(inode);
epilog(dir, dentry, bindex);
/* 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;
} else
/* todo: this timestamp may be reverted later */
inode->i_ctime = h_dir->i_ctime;
goto out_unpin; /* success */
}
/* revert */
if (wh_dentry) {
int rerr;
rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt);
if (rerr)
err = rerr;
}
out_unpin:
au_unpin(&pin);
dput(wh_dentry);
dput(h_path.dentry);
out_parent:
di_write_unlock(parent);
out_unlock:
aufs_read_unlock(dentry, AuLock_DW);
out:
return err;
}
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;
IMustLock(dir);
err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN);
if (unlikely(err))
goto out;
err = au_alive_dir(dentry);
if (unlikely(err))
goto out_unlock;
inode = dentry->d_inode;
IMustLock(inode);
err = -ENOTDIR;
if (unlikely(!S_ISDIR(inode->i_mode)))
goto out_unlock; /* possible? */
err = -ENOMEM;
args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS);
if (unlikely(!args))
goto out_unlock;
parent = dentry->d_parent; /* dir inode is locked */
di_write_lock_parent(parent);
err = au_test_empty(dentry, &args->whlist);
if (unlikely(err))
goto out_parent;
bstart = au_dbstart(dentry);
bwh = au_dbwh(dentry);
bindex = -1;
wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt, &pin);
err = PTR_ERR(wh_dentry);
if (IS_ERR(wh_dentry))
goto out_parent;
h_dentry = au_h_dptr(dentry, bstart);
dget(h_dentry);
rmdir_later = 0;
if (bindex == bstart) {
err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir);
if (err > 0) {
rmdir_later = err;
err = 0;
}
} else {
/* stop monitoring */
au_hn_free(au_hi(inode, bstart));
/* dir inode is locked */
IMustLock(wh_dentry->d_parent->d_inode);
err = 0;
}
if (!err) {
vfsub_dead_dir(inode);
au_set_dbdiropq(dentry, -1);
epilog(dir, dentry, bindex);
if (rmdir_later) {
au_whtmp_kick_rmdir(dir, bstart, h_dentry, args);
args = NULL;
}
goto out_unpin; /* success */
}
/* revert */
AuLabel(revert);
if (wh_dentry) {
int rerr;
rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt);
if (rerr)
err = rerr;
}
out_unpin:
au_unpin(&pin);
dput(wh_dentry);
dput(h_dentry);
out_parent:
di_write_unlock(parent);
if (args)
au_whtmp_rmdir_free(args);
out_unlock:
aufs_read_unlock(dentry, AuLock_DW);
out:
AuTraceErr(err);
return err;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,264 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* inode private data
*/
#include "aufs.h"
struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex)
{
struct inode *h_inode;
IiMustAnyLock(inode);
h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode;
AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0);
return h_inode;
}
/* todo: hard/soft set? */
void au_hiput(struct au_hinode *hinode)
{
au_hn_free(hinode);
dput(hinode->hi_whdentry);
iput(hinode->hi_inode);
}
unsigned int au_hi_flags(struct inode *inode, int isdir)
{
unsigned int flags;
const unsigned int mnt_flags = au_mntflags(inode->i_sb);
flags = 0;
if (au_opt_test(mnt_flags, XINO))
au_fset_hi(flags, XINO);
if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY))
au_fset_hi(flags, HNOTIFY);
return flags;
}
void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
struct inode *h_inode, unsigned int flags)
{
struct au_hinode *hinode;
struct inode *hi;
struct au_iinfo *iinfo = au_ii(inode);
IiMustWriteLock(inode);
hinode = iinfo->ii_hinode + bindex;
hi = hinode->hi_inode;
AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0);
if (hi)
au_hiput(hinode);
hinode->hi_inode = h_inode;
if (h_inode) {
int err;
struct super_block *sb = inode->i_sb;
struct au_branch *br;
AuDebugOn(inode->i_mode
&& (h_inode->i_mode & S_IFMT)
!= (inode->i_mode & S_IFMT));
if (bindex == iinfo->ii_bstart)
au_cpup_igen(inode, h_inode);
br = au_sbr(sb, bindex);
hinode->hi_id = br->br_id;
if (au_ftest_hi(flags, XINO)) {
err = au_xino_write(sb, bindex, h_inode->i_ino,
inode->i_ino);
if (unlikely(err))
AuIOErr1("failed au_xino_write() %d\n", err);
}
if (au_ftest_hi(flags, HNOTIFY)
&& au_br_hnotifyable(br->br_perm)) {
err = au_hn_alloc(hinode, inode);
if (unlikely(err))
AuIOErr1("au_hn_alloc() %d\n", err);
}
}
}
void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
struct dentry *h_wh)
{
struct au_hinode *hinode;
IiMustWriteLock(inode);
hinode = au_ii(inode)->ii_hinode + bindex;
AuDebugOn(hinode->hi_whdentry);
hinode->hi_whdentry = h_wh;
}
void au_update_iigen(struct inode *inode)
{
atomic_set(&au_ii(inode)->ii_generation, au_sigen(inode->i_sb));
/* smp_mb(); */ /* atomic_set */
}
/* it may be called at remount time, too */
void au_update_ibrange(struct inode *inode, int do_put_zero)
{
struct au_iinfo *iinfo;
aufs_bindex_t bindex, bend;
iinfo = au_ii(inode);
if (!iinfo)
return;
IiMustWriteLock(inode);
if (do_put_zero && iinfo->ii_bstart >= 0) {
for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
bindex++) {
struct inode *h_i;
h_i = iinfo->ii_hinode[0 + bindex].hi_inode;
if (h_i && !h_i->i_nlink)
au_set_h_iptr(inode, bindex, NULL, 0);
}
}
iinfo->ii_bstart = -1;
iinfo->ii_bend = -1;
bend = au_sbend(inode->i_sb);
for (bindex = 0; bindex <= bend; bindex++)
if (iinfo->ii_hinode[0 + bindex].hi_inode) {
iinfo->ii_bstart = bindex;
break;
}
if (iinfo->ii_bstart >= 0)
for (bindex = bend; bindex >= iinfo->ii_bstart; bindex--)
if (iinfo->ii_hinode[0 + bindex].hi_inode) {
iinfo->ii_bend = bindex;
break;
}
AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend);
}
/* ---------------------------------------------------------------------- */
void au_icntnr_init_once(void *_c)
{
struct au_icntnr *c = _c;
struct au_iinfo *iinfo = &c->iinfo;
static struct lock_class_key aufs_ii;
au_rw_init(&iinfo->ii_rwsem);
au_rw_class(&iinfo->ii_rwsem, &aufs_ii);
inode_init_once(&c->vfs_inode);
}
int au_iinfo_init(struct inode *inode)
{
struct au_iinfo *iinfo;
struct super_block *sb;
int nbr, i;
sb = inode->i_sb;
iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);
nbr = au_sbend(sb) + 1;
if (unlikely(nbr <= 0))
nbr = 1;
iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS);
if (iinfo->ii_hinode) {
au_ninodes_inc(sb);
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_bstart = -1;
iinfo->ii_bend = -1;
iinfo->ii_vdir = NULL;
return 0;
}
return -ENOMEM;
}
int au_ii_realloc(struct au_iinfo *iinfo, int nbr)
{
int err, sz;
struct au_hinode *hip;
AuRwMustWriteLock(&iinfo->ii_rwsem);
err = -ENOMEM;
sz = sizeof(*hip) * (iinfo->ii_bend + 1);
if (!sz)
sz = sizeof(*hip);
hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS);
if (hip) {
iinfo->ii_hinode = hip;
err = 0;
}
return err;
}
void au_iinfo_fin(struct inode *inode)
{
struct au_iinfo *iinfo;
struct au_hinode *hi;
struct super_block *sb;
aufs_bindex_t bindex, bend;
const unsigned char unlinked = !inode->i_nlink;
iinfo = au_ii(inode);
/* bad_inode case */
if (!iinfo)
return;
sb = inode->i_sb;
au_ninodes_dec(sb);
if (si_pid_test(sb))
au_xino_delete_inode(inode, unlinked);
else {
/*
* it is safe to hide the dependency between sbinfo and
* sb->s_umount.
*/
lockdep_off();
si_noflush_read_lock(sb);
au_xino_delete_inode(inode, unlinked);
si_read_unlock(sb);
lockdep_on();
}
if (iinfo->ii_vdir)
au_vdir_free(iinfo->ii_vdir);
bindex = iinfo->ii_bstart;
if (bindex >= 0) {
hi = iinfo->ii_hinode + bindex;
bend = iinfo->ii_bend;
while (bindex++ <= bend) {
if (hi->hi_inode)
au_hiput(hi);
hi++;
}
}
kfree(iinfo->ii_hinode);
iinfo->ii_hinode = NULL;
AuRwDestroy(&iinfo->ii_rwsem);
}

View File

@ -0,0 +1,478 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* inode functions
*/
#include "aufs.h"
struct inode *au_igrab(struct inode *inode)
{
if (inode) {
AuDebugOn(!atomic_read(&inode->i_count));
ihold(inode);
}
return inode;
}
static void au_refresh_hinode_attr(struct inode *inode, int do_version)
{
au_cpup_attr_all(inode, /*force*/0);
au_update_iigen(inode);
if (do_version)
inode->i_version++;
}
static int au_ii_refresh(struct inode *inode, int *update)
{
int err, e;
umode_t type;
aufs_bindex_t bindex, new_bindex;
struct super_block *sb;
struct au_iinfo *iinfo;
struct au_hinode *p, *q, tmp;
IiMustWriteLock(inode);
*update = 0;
sb = inode->i_sb;
type = inode->i_mode & S_IFMT;
iinfo = au_ii(inode);
err = au_ii_realloc(iinfo, au_sbend(sb) + 1);
if (unlikely(err))
goto out;
AuDebugOn(iinfo->ii_bstart < 0);
p = iinfo->ii_hinode + iinfo->ii_bstart;
for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
bindex++, p++) {
if (!p->hi_inode)
continue;
AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT));
new_bindex = au_br_index(sb, p->hi_id);
if (new_bindex == bindex)
continue;
if (new_bindex < 0) {
*update = 1;
au_hiput(p);
p->hi_inode = NULL;
continue;
}
if (new_bindex < iinfo->ii_bstart)
iinfo->ii_bstart = new_bindex;
if (iinfo->ii_bend < new_bindex)
iinfo->ii_bend = new_bindex;
/* swap two lower inode, and loop again */
q = iinfo->ii_hinode + new_bindex;
tmp = *q;
*q = *p;
*p = tmp;
if (tmp.hi_inode) {
bindex--;
p--;
}
}
au_update_ibrange(inode, /*do_put_zero*/0);
e = au_dy_irefresh(inode);
if (unlikely(e && !err))
err = e;
out:
AuTraceErr(err);
return err;
}
int au_refresh_hinode_self(struct inode *inode)
{
int err, update;
err = au_ii_refresh(inode, &update);
if (!err)
au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode));
AuTraceErr(err);
return err;
}
int au_refresh_hinode(struct inode *inode, struct dentry *dentry)
{
int err, e, update;
unsigned int flags;
umode_t mode;
aufs_bindex_t bindex, bend;
unsigned char isdir;
struct au_hinode *p;
struct au_iinfo *iinfo;
err = au_ii_refresh(inode, &update);
if (unlikely(err))
goto out;
update = 0;
iinfo = au_ii(inode);
p = iinfo->ii_hinode + iinfo->ii_bstart;
mode = (inode->i_mode & S_IFMT);
isdir = S_ISDIR(mode);
flags = au_hi_flags(inode, isdir);
bend = au_dbend(dentry);
for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) {
struct inode *h_i;
struct dentry *h_d;
h_d = au_h_dptr(dentry, bindex);
if (!h_d || !h_d->d_inode)
continue;
AuDebugOn(mode != (h_d->d_inode->i_mode & S_IFMT));
if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) {
h_i = au_h_iptr(inode, bindex);
if (h_i) {
if (h_i == h_d->d_inode)
continue;
err = -EIO;
break;
}
}
if (bindex < iinfo->ii_bstart)
iinfo->ii_bstart = bindex;
if (iinfo->ii_bend < bindex)
iinfo->ii_bend = bindex;
au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags);
update = 1;
}
au_update_ibrange(inode, /*do_put_zero*/0);
e = au_dy_irefresh(inode);
if (unlikely(e && !err))
err = e;
if (!err)
au_refresh_hinode_attr(inode, update && isdir);
out:
AuTraceErr(err);
return err;
}
static int set_inode(struct inode *inode, struct dentry *dentry)
{
int err;
unsigned int flags;
umode_t mode;
aufs_bindex_t bindex, bstart, btail;
unsigned char isdir;
struct dentry *h_dentry;
struct inode *h_inode;
struct au_iinfo *iinfo;
IiMustWriteLock(inode);
err = 0;
isdir = 0;
bstart = au_dbstart(dentry);
h_inode = au_h_dptr(dentry, bstart)->d_inode;
mode = h_inode->i_mode;
switch (mode & S_IFMT) {
case S_IFREG:
btail = au_dbtail(dentry);
inode->i_op = &aufs_iop;
inode->i_fop = &aufs_file_fop;
err = au_dy_iaop(inode, bstart, h_inode);
if (unlikely(err))
goto out;
break;
case S_IFDIR:
isdir = 1;
btail = au_dbtaildir(dentry);
inode->i_op = &aufs_dir_iop;
inode->i_fop = &aufs_dir_fop;
break;
case S_IFLNK:
btail = au_dbtail(dentry);
inode->i_op = &aufs_symlink_iop;
break;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
case S_IFSOCK:
btail = au_dbtail(dentry);
inode->i_op = &aufs_iop;
au_init_special_fop(inode, mode, h_inode->i_rdev);
break;
default:
AuIOErr("Unknown file type 0%o\n", mode);
err = -EIO;
goto out;
}
/* do not set hnotify for whiteouted dirs (SHWH mode) */
flags = au_hi_flags(inode, isdir);
if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)
&& au_ftest_hi(flags, HNOTIFY)
&& dentry->d_name.len > AUFS_WH_PFX_LEN
&& !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))
au_fclr_hi(flags, HNOTIFY);
iinfo = au_ii(inode);
iinfo->ii_bstart = bstart;
iinfo->ii_bend = btail;
for (bindex = bstart; bindex <= btail; bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (h_dentry)
au_set_h_iptr(inode, bindex,
au_igrab(h_dentry->d_inode), flags);
}
au_cpup_attr_all(inode, /*force*/1);
out:
return err;
}
/*
* successful returns with iinfo write_locked
* minus: errno
* zero: success, matched
* plus: no error, but unmatched
*/
static int reval_inode(struct inode *inode, struct dentry *dentry)
{
int err;
aufs_bindex_t bindex, bend;
struct inode *h_inode, *h_dinode;
/*
* before this function, if aufs got any iinfo lock, it must be only
* one, the parent dir.
* it can happen by UDBA and the obsoleted inode number.
*/
err = -EIO;
if (unlikely(inode->i_ino == parent_ino(dentry)))
goto out;
err = 1;
ii_write_lock_new_child(inode);
h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode;
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);
break;
}
}
if (unlikely(err))
ii_write_unlock(inode);
out:
return err;
}
int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
unsigned int d_type, ino_t *ino)
{
int err;
struct mutex *mtx;
/* prevent hardlinked inode number from race condition */
mtx = NULL;
if (d_type != DT_DIR) {
mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx;
mutex_lock(mtx);
}
err = au_xino_read(sb, bindex, h_ino, ino);
if (unlikely(err))
goto out;
if (!*ino) {
err = -EIO;
*ino = au_xino_new_ino(sb);
if (unlikely(!*ino))
goto out;
err = au_xino_write(sb, bindex, h_ino, *ino);
if (unlikely(err))
goto out;
}
out:
if (mtx)
mutex_unlock(mtx);
return err;
}
/* successful returns with iinfo write_locked */
/* todo: return with unlocked? */
struct inode *au_new_inode(struct dentry *dentry, int must_new)
{
struct inode *inode, *h_inode;
struct dentry *h_dentry;
struct super_block *sb;
struct mutex *mtx;
ino_t h_ino, ino;
int err, lc_idx;
aufs_bindex_t bstart;
sb = dentry->d_sb;
bstart = au_dbstart(dentry);
h_dentry = au_h_dptr(dentry, bstart);
h_inode = h_dentry->d_inode;
h_ino = h_inode->i_ino;
/*
* stop 'race'-ing between hardlinks under different
* parents.
*/
mtx = NULL;
if (!S_ISDIR(h_inode->i_mode))
mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx;
new_ino:
if (mtx)
mutex_lock(mtx);
err = au_xino_read(sb, bstart, h_ino, &ino);
inode = ERR_PTR(err);
if (unlikely(err))
goto out;
if (!ino) {
ino = au_xino_new_ino(sb);
if (unlikely(!ino)) {
inode = ERR_PTR(-EIO);
goto out;
}
}
AuDbg("i%lu\n", (unsigned long)ino);
inode = au_iget_locked(sb, ino);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out;
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);
ii_write_lock_new_child(inode);
err = set_inode(inode, dentry);
if (!err) {
unlock_new_inode(inode);
goto out; /* success */
}
/*
* iget_failed() calls iput(), but we need to call
* ii_write_unlock() after iget_failed(). so dirty hack for
* i_count.
*/
atomic_inc(&inode->i_count);
iget_failed(inode);
ii_write_unlock(inode);
au_xino_write(sb, bstart, h_ino, /*ino*/0);
/* ignore this error */
goto out_iput;
} else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) {
/*
* horrible race condition between lookup, readdir and copyup
* (or something).
*/
if (mtx)
mutex_unlock(mtx);
err = reval_inode(inode, dentry);
if (unlikely(err < 0)) {
mtx = NULL;
goto out_iput;
}
if (!err) {
mtx = NULL;
goto out; /* success */
} else if (mtx)
mutex_lock(mtx);
}
if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode)))
AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir,"
" b%d, %s, %.*s, hi%lu, i%lu.\n",
bstart, au_sbtype(h_dentry->d_sb), AuDLNPair(dentry),
(unsigned long)h_ino, (unsigned long)ino);
ino = 0;
err = au_xino_write(sb, bstart, h_ino, /*ino*/0);
if (!err) {
iput(inode);
if (mtx)
mutex_unlock(mtx);
goto new_ino;
}
out_iput:
iput(inode);
inode = ERR_PTR(err);
out:
if (mtx)
mutex_unlock(mtx);
return inode;
}
/* ---------------------------------------------------------------------- */
int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
struct inode *inode)
{
int err;
err = au_br_rdonly(au_sbr(sb, bindex));
/* pseudo-link after flushed may happen out of bounds */
if (!err
&& inode
&& au_ibstart(inode) <= bindex
&& bindex <= au_ibend(inode)) {
/*
* permission check is unnecessary since vfsub routine
* will be called later
*/
struct inode *hi = au_h_iptr(inode, bindex);
if (hi)
err = IS_IMMUTABLE(hi) ? -EROFS : 0;
}
return err;
}
int au_test_h_perm(struct inode *h_inode, int mask)
{
if (!current_fsuid())
return 0;
return inode_permission(h_inode, mask);
}
int au_test_h_perm_sio(struct inode *h_inode, int mask)
{
if (au_test_nfs(h_inode->i_sb)
&& (mask & MAY_WRITE)
&& S_ISDIR(h_inode->i_mode))
mask |= MAY_READ; /* force permission check */
return au_test_h_perm(h_inode, mask);
}

View File

@ -0,0 +1,559 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* inode operations
*/
#ifndef __AUFS_INODE_H__
#define __AUFS_INODE_H__
#ifdef __KERNEL__
#include <linux/fsnotify.h>
#include "rwsem.h"
struct vfsmount;
struct au_hnotify {
#ifdef CONFIG_AUFS_HNOTIFY
#ifdef CONFIG_AUFS_HFSNOTIFY
/* never use fsnotify_add_vfsmount_mark() */
struct fsnotify_mark hn_mark;
#endif
struct inode *hn_aufs_inode; /* no get/put */
#endif
} ____cacheline_aligned_in_smp;
struct au_hinode {
struct inode *hi_inode;
aufs_bindex_t hi_id;
#ifdef CONFIG_AUFS_HNOTIFY
struct au_hnotify *hi_notify;
#endif
/* reference to the copied-up whiteout with get/put */
struct dentry *hi_whdentry;
};
struct au_vdir;
struct au_iinfo {
atomic_t ii_generation;
struct super_block *ii_hsb1; /* no get/put */
struct au_rwsem ii_rwsem;
aufs_bindex_t ii_bstart, ii_bend;
__u32 ii_higen;
struct au_hinode *ii_hinode;
struct au_vdir *ii_vdir;
};
struct au_icntnr {
struct au_iinfo iinfo;
struct inode vfs_inode;
} ____cacheline_aligned_in_smp;
/* au_pin flags */
#define AuPin_DI_LOCKED 1
#define AuPin_MNT_WRITE (1 << 1)
#define au_ftest_pin(flags, name) ((flags) & AuPin_##name)
#define au_fset_pin(flags, name) \
do { (flags) |= AuPin_##name; } while (0)
#define au_fclr_pin(flags, name) \
do { (flags) &= ~AuPin_##name; } while (0)
struct au_pin {
/* input */
struct dentry *dentry;
unsigned int udba;
unsigned char lsc_di, lsc_hi, flags;
aufs_bindex_t bindex;
/* output */
struct dentry *parent;
struct au_hinode *hdir;
struct vfsmount *h_mnt;
};
/* ---------------------------------------------------------------------- */
static inline struct au_iinfo *au_ii(struct inode *inode)
{
struct au_iinfo *iinfo;
iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);
if (iinfo->ii_hinode)
return iinfo;
return NULL; /* debugging bad_inode case */
}
/* ---------------------------------------------------------------------- */
/* inode.c */
struct inode *au_igrab(struct inode *inode);
int au_refresh_hinode_self(struct inode *inode);
int au_refresh_hinode(struct inode *inode, struct dentry *dentry);
int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
unsigned int d_type, ino_t *ino);
struct inode *au_new_inode(struct dentry *dentry, int must_new);
int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
struct inode *inode);
int au_test_h_perm(struct inode *h_inode, int mask);
int au_test_h_perm_sio(struct inode *h_inode, int mask);
static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex,
ino_t h_ino, unsigned int d_type, ino_t *ino)
{
#ifdef CONFIG_AUFS_SHWH
return au_ino(sb, bindex, h_ino, d_type, ino);
#else
return 0;
#endif
}
/* i_op.c */
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 au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name)
#define au_fset_wrdir(flags, name) \
do { (flags) |= AuWrDir_##name; } while (0)
#define au_fclr_wrdir(flags, name) \
do { (flags) &= ~AuWrDir_##name; } while (0)
struct au_wr_dir_args {
aufs_bindex_t force_btgt;
unsigned char flags;
};
int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
struct au_wr_dir_args *args);
struct dentry *au_pinned_h_parent(struct au_pin *pin);
void au_pin_init(struct au_pin *pin, struct dentry *dentry,
aufs_bindex_t bindex, int lsc_di, int lsc_hi,
unsigned int udba, unsigned char flags);
int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
unsigned int udba, unsigned char flags) __must_check;
int au_do_pin(struct au_pin *pin) __must_check;
void au_unpin(struct au_pin *pin);
/* i_op_add.c */
int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_parent, int isdir);
int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname);
int aufs_create(struct inode *dir, struct dentry *dentry, int mode,
struct nameidata *nd);
int aufs_link(struct dentry *src_dentry, struct inode *dir,
struct dentry *dentry);
int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
/* i_op_del.c */
int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup);
int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_parent, int isdir);
int aufs_unlink(struct inode *dir, struct dentry *dentry);
int aufs_rmdir(struct inode *dir, struct dentry *dentry);
/* i_op_ren.c */
int au_wbr(struct dentry *dentry, aufs_bindex_t btgt);
int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
struct inode *dir, struct dentry *dentry);
/* iinfo.c */
struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
void au_hiput(struct au_hinode *hinode);
void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
struct dentry *h_wh);
unsigned int au_hi_flags(struct inode *inode, int isdir);
/* hinode flags */
#define AuHi_XINO 1
#define AuHi_HNOTIFY (1 << 1)
#define au_ftest_hi(flags, name) ((flags) & AuHi_##name)
#define au_fset_hi(flags, name) \
do { (flags) |= AuHi_##name; } while (0)
#define au_fclr_hi(flags, name) \
do { (flags) &= ~AuHi_##name; } while (0)
#ifndef CONFIG_AUFS_HNOTIFY
#undef AuHi_HNOTIFY
#define AuHi_HNOTIFY 0
#endif
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_ibrange(struct inode *inode, int do_put_zero);
void au_icntnr_init_once(void *_c);
int au_iinfo_init(struct inode *inode);
void au_iinfo_fin(struct inode *inode);
int au_ii_realloc(struct au_iinfo *iinfo, int nbr);
#ifdef CONFIG_PROC_FS
/* plink.c */
int au_plink_maint(struct super_block *sb, int flags);
void au_plink_maint_leave(struct au_sbinfo *sbinfo);
int au_plink_maint_enter(struct super_block *sb);
#ifdef CONFIG_AUFS_DEBUG
void au_plink_list(struct super_block *sb);
#else
AuStubVoid(au_plink_list, struct super_block *sb)
#endif
int au_plink_test(struct inode *inode);
struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex);
void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
struct dentry *h_dentry);
void au_plink_put(struct super_block *sb, int verbose);
void au_plink_clean(struct super_block *sb, int verbose);
void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id);
#else
AuStubInt0(au_plink_maint, struct super_block *sb, int flags);
AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo);
AuStubInt0(au_plink_maint_enter, struct super_block *sb);
AuStubVoid(au_plink_list, struct super_block *sb);
AuStubInt0(au_plink_test, struct inode *inode);
AuStub(struct dentry *, au_plink_lkup, return NULL,
struct inode *inode, aufs_bindex_t bindex);
AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex,
struct dentry *h_dentry);
AuStubVoid(au_plink_put, struct super_block *sb, int verbose);
AuStubVoid(au_plink_clean, struct super_block *sb, int verbose);
AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id);
#endif /* CONFIG_PROC_FS */
/* ---------------------------------------------------------------------- */
/* lock subclass for iinfo */
enum {
AuLsc_II_CHILD, /* child first */
AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */
AuLsc_II_CHILD3, /* copyup dirs */
AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */
AuLsc_II_PARENT2,
AuLsc_II_PARENT3, /* copyup dirs */
AuLsc_II_NEW_CHILD
};
/*
* ii_read_lock_child, ii_write_lock_child,
* ii_read_lock_child2, ii_write_lock_child2,
* ii_read_lock_child3, ii_write_lock_child3,
* ii_read_lock_parent, ii_write_lock_parent,
* ii_read_lock_parent2, ii_write_lock_parent2,
* ii_read_lock_parent3, ii_write_lock_parent3,
* ii_read_lock_new_child, ii_write_lock_new_child,
*/
#define AuReadLockFunc(name, lsc) \
static inline void ii_read_lock_##name(struct inode *i) \
{ \
au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \
}
#define AuWriteLockFunc(name, lsc) \
static inline void ii_write_lock_##name(struct inode *i) \
{ \
au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \
}
#define AuRWLockFuncs(name, lsc) \
AuReadLockFunc(name, lsc) \
AuWriteLockFunc(name, lsc)
AuRWLockFuncs(child, CHILD);
AuRWLockFuncs(child2, CHILD2);
AuRWLockFuncs(child3, CHILD3);
AuRWLockFuncs(parent, PARENT);
AuRWLockFuncs(parent2, PARENT2);
AuRWLockFuncs(parent3, PARENT3);
AuRWLockFuncs(new_child, NEW_CHILD);
#undef AuReadLockFunc
#undef AuWriteLockFunc
#undef AuRWLockFuncs
/*
* ii_read_unlock, ii_write_unlock, ii_downgrade_lock
*/
AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem);
#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem)
#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem)
#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem)
/* ---------------------------------------------------------------------- */
static inline void au_icntnr_init(struct au_icntnr *c)
{
#ifdef CONFIG_AUFS_DEBUG
c->vfs_inode.i_mode = 0;
#endif
}
static inline unsigned int au_iigen(struct inode *inode)
{
return atomic_read(&au_ii(inode)->ii_generation);
}
/* tiny test for inode number */
/* tmpfs generation is too rough */
static inline int au_test_higen(struct inode *inode, struct inode *h_inode)
{
struct au_iinfo *iinfo;
iinfo = au_ii(inode);
AuRwMustAnyLock(&iinfo->ii_rwsem);
return !(iinfo->ii_hsb1 == h_inode->i_sb
&& iinfo->ii_higen == h_inode->i_generation);
}
static inline void au_iigen_dec(struct inode *inode)
{
atomic_dec(&au_ii(inode)->ii_generation);
}
static inline int au_iigen_test(struct inode *inode, unsigned int sigen)
{
int err;
err = 0;
if (unlikely(inode && au_iigen(inode) != sigen))
err = -EIO;
return err;
}
/* ---------------------------------------------------------------------- */
static inline aufs_bindex_t au_ii_br_id(struct inode *inode,
aufs_bindex_t bindex)
{
IiMustAnyLock(inode);
return au_ii(inode)->ii_hinode[0 + bindex].hi_id;
}
static inline aufs_bindex_t au_ibstart(struct inode *inode)
{
IiMustAnyLock(inode);
return au_ii(inode)->ii_bstart;
}
static inline aufs_bindex_t au_ibend(struct inode *inode)
{
IiMustAnyLock(inode);
return au_ii(inode)->ii_bend;
}
static inline struct au_vdir *au_ivdir(struct inode *inode)
{
IiMustAnyLock(inode);
return au_ii(inode)->ii_vdir;
}
static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex)
{
IiMustAnyLock(inode);
return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry;
}
static inline void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex)
{
IiMustWriteLock(inode);
au_ii(inode)->ii_bstart = bindex;
}
static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex)
{
IiMustWriteLock(inode);
au_ii(inode)->ii_bend = bindex;
}
static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir)
{
IiMustWriteLock(inode);
au_ii(inode)->ii_vdir = vdir;
}
static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex)
{
IiMustAnyLock(inode);
return au_ii(inode)->ii_hinode + bindex;
}
/* ---------------------------------------------------------------------- */
static inline struct dentry *au_pinned_parent(struct au_pin *pin)
{
if (pin)
return pin->parent;
return NULL;
}
static inline struct inode *au_pinned_h_dir(struct au_pin *pin)
{
if (pin && pin->hdir)
return pin->hdir->hi_inode;
return NULL;
}
static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin)
{
if (pin)
return pin->hdir;
return NULL;
}
static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry)
{
if (pin)
pin->dentry = dentry;
}
static inline void au_pin_set_parent_lflag(struct au_pin *pin,
unsigned char lflag)
{
if (pin) {
if (lflag)
au_fset_pin(pin->flags, DI_LOCKED);
else
au_fclr_pin(pin->flags, DI_LOCKED);
}
}
static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent)
{
if (pin) {
dput(pin->parent);
pin->parent = dget(parent);
}
}
/* ---------------------------------------------------------------------- */
struct au_branch;
#ifdef CONFIG_AUFS_HNOTIFY
struct au_hnotify_op {
void (*ctl)(struct au_hinode *hinode, int do_set);
int (*alloc)(struct au_hinode *hinode);
/*
* if it returns true, the the caller should free hinode->hi_notify,
* otherwise ->free() frees it.
*/
int (*free)(struct au_hinode *hinode,
struct au_hnotify *hn) __must_check;
void (*fin)(void);
int (*init)(void);
int (*reset_br)(unsigned int udba, struct au_branch *br, int perm);
void (*fin_br)(struct au_branch *br);
int (*init_br)(struct au_branch *br, int perm);
};
/* hnotify.c */
int au_hn_alloc(struct au_hinode *hinode, struct inode *inode);
void au_hn_free(struct au_hinode *hinode);
void au_hn_ctl(struct au_hinode *hinode, int do_set);
void au_hn_reset(struct inode *inode, unsigned int flags);
int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask,
struct qstr *h_child_qstr, struct inode *h_child_inode);
int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm);
int au_hnotify_init_br(struct au_branch *br, int perm);
void au_hnotify_fin_br(struct au_branch *br);
int __init au_hnotify_init(void);
void au_hnotify_fin(void);
/* hfsnotify.c */
extern const struct au_hnotify_op au_hnotify_op;
static inline
void au_hn_init(struct au_hinode *hinode)
{
hinode->hi_notify = NULL;
}
static inline struct au_hnotify *au_hn(struct au_hinode *hinode)
{
return hinode->hi_notify;
}
#else
static inline
int au_hn_alloc(struct au_hinode *hinode __maybe_unused,
struct inode *inode __maybe_unused)
{
return -EOPNOTSUPP;
}
static inline struct au_hnotify *au_hn(struct au_hinode *hinode)
{
return NULL;
}
AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused)
AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused,
int do_set __maybe_unused)
AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused,
unsigned int flags __maybe_unused)
AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused,
struct au_branch *br __maybe_unused,
int perm __maybe_unused)
AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused,
int perm __maybe_unused)
AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused)
AuStubInt0(__init au_hnotify_init, void)
AuStubVoid(au_hnotify_fin, void)
AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused)
#endif /* CONFIG_AUFS_HNOTIFY */
static inline void au_hn_suspend(struct au_hinode *hdir)
{
au_hn_ctl(hdir, /*do_set*/0);
}
static inline void au_hn_resume(struct au_hinode *hdir)
{
au_hn_ctl(hdir, /*do_set*/1);
}
static inline void au_hn_imtx_lock(struct au_hinode *hdir)
{
mutex_lock(&hdir->hi_inode->i_mutex);
au_hn_suspend(hdir);
}
static inline void au_hn_imtx_lock_nested(struct au_hinode *hdir,
unsigned int sc __maybe_unused)
{
mutex_lock_nested(&hdir->hi_inode->i_mutex, sc);
au_hn_suspend(hdir);
}
static inline void au_hn_imtx_unlock(struct au_hinode *hdir)
{
au_hn_resume(hdir);
mutex_unlock(&hdir->hi_inode->i_mutex);
}
#endif /* __KERNEL__ */
#endif /* __AUFS_INODE_H__ */

View File

@ -0,0 +1,196 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* ioctl
* plink-management and readdir in userspace.
* assist the pathconf(3) wrapper library.
*/
#include "aufs.h"
static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg)
{
int err, fd;
aufs_bindex_t wbi, bindex, bend;
struct file *h_file;
struct super_block *sb;
struct dentry *root;
struct au_branch *br;
struct aufs_wbr_fd wbrfd = {
.oflags = au_dir_roflags,
.brid = -1
};
const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY
| O_NOATIME | O_CLOEXEC;
AuDebugOn(wbrfd.oflags & ~valid);
if (arg) {
err = copy_from_user(&wbrfd, arg, sizeof(wbrfd));
if (unlikely(err)) {
err = -EFAULT;
goto out;
}
err = -EINVAL;
AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid);
wbrfd.oflags |= au_dir_roflags;
AuDbg("0%o\n", wbrfd.oflags);
if (unlikely(wbrfd.oflags & ~valid))
goto out;
}
fd = get_unused_fd();
err = fd;
if (unlikely(fd < 0))
goto out;
h_file = ERR_PTR(-EINVAL);
wbi = 0;
br = NULL;
sb = path->dentry->d_sb;
root = sb->s_root;
aufs_read_lock(root, AuLock_IR);
bend = au_sbend(sb);
if (wbrfd.brid >= 0) {
wbi = au_br_index(sb, wbrfd.brid);
if (unlikely(wbi < 0 || wbi > bend))
goto out_unlock;
}
h_file = ERR_PTR(-ENOENT);
br = au_sbr(sb, wbi);
if (!au_br_writable(br->br_perm)) {
if (arg)
goto out_unlock;
bindex = wbi + 1;
wbi = -1;
for (; bindex <= bend; bindex++) {
br = au_sbr(sb, bindex);
if (au_br_writable(br->br_perm)) {
wbi = bindex;
br = au_sbr(sb, wbi);
break;
}
}
}
AuDbg("wbi %d\n", wbi);
if (wbi >= 0)
h_file = au_h_open(root, wbi, wbrfd.oflags, NULL);
out_unlock:
aufs_read_unlock(root, AuLock_IR);
err = PTR_ERR(h_file);
if (IS_ERR(h_file))
goto out_fd;
atomic_dec(&br->br_count); /* cf. au_h_open() */
fd_install(fd, h_file);
err = fd;
goto out; /* success */
out_fd:
put_unused_fd(fd);
out:
AuTraceErr(err);
return err;
}
/* ---------------------------------------------------------------------- */
long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg)
{
long err;
switch (cmd) {
case AUFS_CTL_RDU:
case AUFS_CTL_RDU_INO:
err = au_rdu_ioctl(file, cmd, arg);
break;
case AUFS_CTL_WBR_FD:
err = au_wbr_fd(&file->f_path, (void __user *)arg);
break;
case AUFS_CTL_IBUSY:
err = au_ibusy_ioctl(file, arg);
break;
default:
/* do not call the lower */
AuDbg("0x%x\n", cmd);
err = -ENOTTY;
}
AuTraceErr(err);
return err;
}
long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg)
{
long err;
switch (cmd) {
case AUFS_CTL_WBR_FD:
err = au_wbr_fd(&file->f_path, (void __user *)arg);
break;
default:
/* do not call the lower */
AuDbg("0x%x\n", cmd);
err = -ENOTTY;
}
AuTraceErr(err);
return err;
}
#ifdef CONFIG_COMPAT
long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd,
unsigned long arg)
{
long err;
switch (cmd) {
case AUFS_CTL_RDU:
case AUFS_CTL_RDU_INO:
err = au_rdu_compat_ioctl(file, cmd, arg);
break;
case AUFS_CTL_IBUSY:
err = au_ibusy_compat_ioctl(file, arg);
break;
default:
err = aufs_ioctl_dir(file, cmd, arg);
}
AuTraceErr(err);
return err;
}
#if 0 /* unused yet */
long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd,
unsigned long arg)
{
return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg));
}
#endif
#endif

View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* support for loopback block device as a branch
*/
#include <linux/loop.h>
#include "aufs.h"
/*
* test if two lower dentries have overlapping branches.
*/
int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding)
{
struct super_block *h_sb;
struct loop_device *l;
h_sb = h_adding->d_sb;
if (MAJOR(h_sb->s_dev) != LOOP_MAJOR)
return 0;
l = h_sb->s_bdev->bd_disk->private_data;
h_adding = l->lo_backing_file->f_dentry;
/*
* h_adding can be local NFS.
* in this case aufs cannot detect the loop.
*/
if (unlikely(h_adding->d_sb == sb))
return 1;
return !!au_test_subdir(h_adding, sb->s_root);
}
/* true if a kernel thread named 'loop[0-9].*' accesses a file */
int au_test_loopback_kthread(void)
{
int ret;
struct task_struct *tsk = current;
ret = 0;
if (tsk->flags & PF_KTHREAD) {
const char c = tsk->comm[4];
ret = ('0' <= c && c <= '9'
&& !strncmp(tsk->comm, "loop", 4));
}
return ret;
}
/* ---------------------------------------------------------------------- */
#define au_warn_loopback_step 16
static int au_warn_loopback_nelem = au_warn_loopback_step;
static unsigned long *au_warn_loopback_array;
void au_warn_loopback(struct super_block *h_sb)
{
int i, new_nelem;
unsigned long *a, magic;
static DEFINE_SPINLOCK(spin);
magic = h_sb->s_magic;
spin_lock(&spin);
a = au_warn_loopback_array;
for (i = 0; i < au_warn_loopback_nelem && *a; i++)
if (a[i] == magic) {
spin_unlock(&spin);
return;
}
/* h_sb is new to us, print it */
if (i < au_warn_loopback_nelem) {
a[i] = magic;
goto pr;
}
/* expand the array */
new_nelem = au_warn_loopback_nelem + au_warn_loopback_step;
a = au_kzrealloc(au_warn_loopback_array,
au_warn_loopback_nelem * sizeof(unsigned long),
new_nelem * sizeof(unsigned long), GFP_ATOMIC);
if (a) {
au_warn_loopback_nelem = new_nelem;
au_warn_loopback_array = a;
a[i] = magic;
goto pr;
}
spin_unlock(&spin);
AuWarn1("realloc failed, ignored\n");
return;
pr:
spin_unlock(&spin);
pr_warn("you may want to try another patch for loopback file "
"on %s(0x%lx) branch\n", au_sbtype(h_sb), magic);
}
int au_loopback_init(void)
{
int err;
struct super_block *sb __maybe_unused;
AuDebugOn(sizeof(sb->s_magic) != sizeof(unsigned long));
err = 0;
au_warn_loopback_array = kcalloc(au_warn_loopback_step,
sizeof(unsigned long), GFP_NOFS);
if (unlikely(!au_warn_loopback_array))
err = -ENOMEM;
return err;
}
void au_loopback_fin(void)
{
kfree(au_warn_loopback_array);
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* support for loopback mount as a branch
*/
#ifndef __AUFS_LOOP_H__
#define __AUFS_LOOP_H__
#ifdef __KERNEL__
struct dentry;
struct super_block;
#ifdef CONFIG_AUFS_BDEV_LOOP
/* loop.c */
int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding);
int au_test_loopback_kthread(void);
void au_warn_loopback(struct super_block *h_sb);
int au_loopback_init(void);
void au_loopback_fin(void);
#else
AuStubInt0(au_test_loopback_overlap, struct super_block *sb,
struct dentry *h_adding)
AuStubInt0(au_test_loopback_kthread, void)
AuStubVoid(au_warn_loopback, struct super_block *h_sb)
AuStubInt0(au_loopback_init, void)
AuStubVoid(au_loopback_fin, void)
#endif /* BLK_DEV_LOOP */
#endif /* __KERNEL__ */
#endif /* __AUFS_LOOP_H__ */

View File

@ -0,0 +1,54 @@
# defined in ${srctree}/fs/fuse/inode.c
# tristate
ifdef CONFIG_FUSE_FS
ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546
endif
# defined in ${srctree}/fs/ocfs2/ocfs2_fs.h
# tristate
ifdef CONFIG_OCFS2_FS
ccflags-y += -DOCFS2_SUPER_MAGIC=0x7461636f
endif
# defined in ${srctree}/fs/ocfs2/dlm/userdlm.h
# tristate
ifdef CONFIG_OCFS2_FS_O2CB
ccflags-y += -DDLMFS_MAGIC=0x76a9f425
endif
# defined in ${srctree}/fs/cifs/cifsfs.c
# tristate
ifdef CONFIG_CIFS_FS
ccflags-y += -DCIFS_MAGIC_NUMBER=0xFF534D42
endif
# defined in ${srctree}/fs/xfs/xfs_sb.h
# tristate
ifdef CONFIG_XFS_FS
ccflags-y += -DXFS_SB_MAGIC=0x58465342
endif
# defined in ${srctree}/fs/configfs/mount.c
# tristate
ifdef CONFIG_CONFIGFS_FS
ccflags-y += -DCONFIGFS_MAGIC=0x62656570
endif
# defined in ${srctree}/fs/9p/v9fs.h
# tristate
ifdef CONFIG_9P_FS
ccflags-y += -DV9FS_MAGIC=0x01021997
endif
# defined in ${srctree}/fs/ubifs/ubifs.h
# tristate
ifdef CONFIG_UBIFS_FS
ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905
endif
# defined in ${srctree}/fs/hfsplus/hfsplus_raw.h
# tristate
ifdef CONFIG_HFSPLUS_FS
ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b
endif

View File

@ -0,0 +1,192 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* module global variables and operations
*/
#include <linux/module.h>
#include <linux/seq_file.h>
#include "aufs.h"
void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp)
{
if (new_sz <= nused)
return p;
p = krealloc(p, new_sz, gfp);
if (p)
memset(p + nused, 0, new_sz - nused);
return p;
}
/* ---------------------------------------------------------------------- */
/*
* aufs caches
*/
struct kmem_cache *au_cachep[AuCache_Last];
static int __init au_cache_init(void)
{
au_cachep[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once);
if (au_cachep[AuCache_DINFO])
/* SLAB_DESTROY_BY_RCU */
au_cachep[AuCache_ICNTNR] = AuCacheCtor(au_icntnr,
au_icntnr_init_once);
if (au_cachep[AuCache_ICNTNR])
au_cachep[AuCache_FINFO] = AuCacheCtor(au_finfo,
au_fi_init_once);
if (au_cachep[AuCache_FINFO])
au_cachep[AuCache_VDIR] = AuCache(au_vdir);
if (au_cachep[AuCache_VDIR])
au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr);
if (au_cachep[AuCache_DEHSTR])
return 0;
return -ENOMEM;
}
static void au_cache_fin(void)
{
int i;
/* excluding AuCache_HNOTIFY */
BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last);
for (i = 0; i < AuCache_HNOTIFY; i++)
if (au_cachep[i]) {
kmem_cache_destroy(au_cachep[i]);
au_cachep[i] = NULL;
}
}
/* ---------------------------------------------------------------------- */
int au_dir_roflags;
#ifdef CONFIG_AUFS_SBILIST
struct au_splhead au_sbilist;
#endif
struct lock_class_key au_lc_key[AuLcKey_Last];
/*
* functions for module interface.
*/
MODULE_LICENSE("GPL");
/* MODULE_LICENSE("GPL v2"); */
MODULE_AUTHOR("Junjiro R. Okajima <aufs-users@lists.sourceforge.net>");
MODULE_DESCRIPTION(AUFS_NAME
" -- Advanced multi layered unification filesystem");
MODULE_VERSION(AUFS_VERSION);
/* this module parameter has no meaning when SYSFS is disabled */
int sysaufs_brs = 1;
MODULE_PARM_DESC(brs, "use <sysfs>/fs/aufs/si_*/brN");
module_param_named(brs, sysaufs_brs, int, S_IRUGO);
/* ---------------------------------------------------------------------- */
static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */
int au_seq_path(struct seq_file *seq, struct path *path)
{
return seq_path(seq, path, au_esc_chars);
}
/* ---------------------------------------------------------------------- */
static int __init aufs_init(void)
{
int err, i;
char *p;
p = au_esc_chars;
for (i = 1; i <= ' '; i++)
*p++ = i;
*p++ = '\\';
*p++ = '\x7f';
*p = 0;
au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE);
au_sbilist_init();
sysaufs_brs_init();
au_debug_init();
au_dy_init();
err = sysaufs_init();
if (unlikely(err))
goto out;
err = au_procfs_init();
if (unlikely(err))
goto out_sysaufs;
err = au_wkq_init();
if (unlikely(err))
goto out_procfs;
err = au_loopback_init();
if (unlikely(err))
goto out_wkq;
err = au_hnotify_init();
if (unlikely(err))
goto out_loopback;
err = au_sysrq_init();
if (unlikely(err))
goto out_hin;
err = au_cache_init();
if (unlikely(err))
goto out_sysrq;
err = register_filesystem(&aufs_fs_type);
if (unlikely(err))
goto out_cache;
/* since we define pr_fmt, call printk directly */
printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n");
goto out; /* success */
out_cache:
au_cache_fin();
out_sysrq:
au_sysrq_fin();
out_hin:
au_hnotify_fin();
out_loopback:
au_loopback_fin();
out_wkq:
au_wkq_fin();
out_procfs:
au_procfs_fin();
out_sysaufs:
sysaufs_fin();
au_dy_fin();
out:
return err;
}
static void __exit aufs_exit(void)
{
unregister_filesystem(&aufs_fs_type);
au_cache_fin();
au_sysrq_fin();
au_hnotify_fin();
au_loopback_fin();
au_wkq_fin();
au_procfs_fin();
sysaufs_fin();
au_dy_fin();
}
module_init(aufs_init);
module_exit(aufs_exit);

View File

@ -0,0 +1,105 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* module initialization and module-global
*/
#ifndef __AUFS_MODULE_H__
#define __AUFS_MODULE_H__
#ifdef __KERNEL__
#include <linux/slab.h>
struct path;
struct seq_file;
/* module parameters */
extern int sysaufs_brs;
/* ---------------------------------------------------------------------- */
extern int au_dir_roflags;
enum {
AuLcNonDir_FIINFO,
AuLcNonDir_DIINFO,
AuLcNonDir_IIINFO,
AuLcDir_FIINFO,
AuLcDir_DIINFO,
AuLcDir_IIINFO,
AuLcSymlink_DIINFO,
AuLcSymlink_IIINFO,
AuLcKey_Last
};
extern struct lock_class_key au_lc_key[AuLcKey_Last];
void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp);
int au_seq_path(struct seq_file *seq, struct path *path);
#ifdef CONFIG_PROC_FS
/* procfs.c */
int __init au_procfs_init(void);
void au_procfs_fin(void);
#else
AuStubInt0(au_procfs_init, void);
AuStubVoid(au_procfs_fin, void);
#endif
/* ---------------------------------------------------------------------- */
/* kmem cache */
enum {
AuCache_DINFO,
AuCache_ICNTNR,
AuCache_FINFO,
AuCache_VDIR,
AuCache_DEHSTR,
AuCache_HNOTIFY, /* must be last */
AuCache_Last
};
#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD)
#define AuCache(type) KMEM_CACHE(type, AuCacheFlags)
#define AuCacheCtor(type, ctor) \
kmem_cache_create(#type, sizeof(struct type), \
__alignof__(struct type), AuCacheFlags, ctor)
extern struct kmem_cache *au_cachep[];
#define AuCacheFuncs(name, index) \
static inline struct au_##name *au_cache_alloc_##name(void) \
{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \
static inline void au_cache_free_##name(struct au_##name *p) \
{ kmem_cache_free(au_cachep[AuCache_##index], p); }
AuCacheFuncs(dinfo, DINFO);
AuCacheFuncs(icntnr, ICNTNR);
AuCacheFuncs(finfo, FINFO);
AuCacheFuncs(vdir, VDIR);
AuCacheFuncs(vdir_dehstr, DEHSTR);
#ifdef CONFIG_AUFS_HNOTIFY
AuCacheFuncs(hnotify, HNOTIFY);
#endif
#endif /* __KERNEL__ */
#endif /* __AUFS_MODULE_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,209 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* mount options/flags
*/
#ifndef __AUFS_OPTS_H__
#define __AUFS_OPTS_H__
#ifdef __KERNEL__
#include <linux/path.h>
struct file;
struct super_block;
/* ---------------------------------------------------------------------- */
/* mount flags */
#define AuOpt_XINO 1 /* external inode number bitmap
and translation table */
#define AuOpt_TRUNC_XINO (1 << 1) /* truncate xino files */
#define AuOpt_UDBA_NONE (1 << 2) /* users direct branch access */
#define AuOpt_UDBA_REVAL (1 << 3)
#define AuOpt_UDBA_HNOTIFY (1 << 4)
#define AuOpt_SHWH (1 << 5) /* show whiteout */
#define AuOpt_PLINK (1 << 6) /* pseudo-link */
#define AuOpt_DIRPERM1 (1 << 7) /* unimplemented */
#define AuOpt_REFROF (1 << 8) /* unimplemented */
#define AuOpt_ALWAYS_DIROPQ (1 << 9) /* policy to creating diropq */
#define AuOpt_SUM (1 << 10) /* summation for statfs(2) */
#define AuOpt_SUM_W (1 << 11) /* unimplemented */
#define AuOpt_WARN_PERM (1 << 12) /* warn when add-branch */
#define AuOpt_VERBOSE (1 << 13) /* busy inode when del-branch */
#define AuOpt_DIO (1 << 14) /* direct io */
#ifndef CONFIG_AUFS_HNOTIFY
#undef AuOpt_UDBA_HNOTIFY
#define AuOpt_UDBA_HNOTIFY 0
#endif
#ifndef CONFIG_AUFS_SHWH
#undef AuOpt_SHWH
#define AuOpt_SHWH 0
#endif
#define AuOpt_Def (AuOpt_XINO \
| AuOpt_UDBA_REVAL \
| AuOpt_PLINK \
/* | AuOpt_DIRPERM1 */ \
| AuOpt_WARN_PERM)
#define AuOptMask_UDBA (AuOpt_UDBA_NONE \
| AuOpt_UDBA_REVAL \
| AuOpt_UDBA_HNOTIFY)
#define au_opt_test(flags, name) (flags & AuOpt_##name)
#define au_opt_set(flags, name) do { \
BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \
((flags) |= AuOpt_##name); \
} while (0)
#define au_opt_set_udba(flags, name) do { \
(flags) &= ~AuOptMask_UDBA; \
((flags) |= AuOpt_##name); \
} while (0)
#define au_opt_clr(flags, name) do { \
((flags) &= ~AuOpt_##name); \
} while (0)
static inline unsigned int au_opts_plink(unsigned int mntflags)
{
#ifdef CONFIG_PROC_FS
return mntflags;
#else
return mntflags & ~AuOpt_PLINK;
#endif
}
/* ---------------------------------------------------------------------- */
/* policies to select one among multiple writable branches */
enum {
AuWbrCreate_TDP, /* top down parent */
AuWbrCreate_RR, /* round robin */
AuWbrCreate_MFS, /* most free space */
AuWbrCreate_MFSV, /* mfs with seconds */
AuWbrCreate_MFSRR, /* mfs then rr */
AuWbrCreate_MFSRRV, /* mfs then rr with seconds */
AuWbrCreate_PMFS, /* parent and mfs */
AuWbrCreate_PMFSV, /* parent and mfs with seconds */
AuWbrCreate_Def = AuWbrCreate_TDP
};
enum {
AuWbrCopyup_TDP, /* top down parent */
AuWbrCopyup_BUP, /* bottom up parent */
AuWbrCopyup_BU, /* bottom up */
AuWbrCopyup_Def = AuWbrCopyup_TDP
};
/* ---------------------------------------------------------------------- */
struct au_opt_add {
aufs_bindex_t bindex;
char *pathname;
int perm;
struct path path;
};
struct au_opt_del {
char *pathname;
struct path h_path;
};
struct au_opt_mod {
char *path;
int perm;
struct dentry *h_root;
};
struct au_opt_xino {
char *path;
struct file *file;
};
struct au_opt_xino_itrunc {
aufs_bindex_t bindex;
};
struct au_opt_wbr_create {
int wbr_create;
int mfs_second;
unsigned long long mfsrr_watermark;
};
struct au_opt {
int type;
union {
struct au_opt_xino xino;
struct au_opt_xino_itrunc xino_itrunc;
struct au_opt_add add;
struct au_opt_del del;
struct au_opt_mod mod;
int dirwh;
int rdcache;
unsigned int rdblk;
unsigned int rdhash;
int udba;
struct au_opt_wbr_create wbr_create;
int wbr_copyup;
};
};
/* opts flags */
#define AuOpts_REMOUNT 1
#define AuOpts_REFRESH (1 << 1)
#define AuOpts_TRUNC_XIB (1 << 2)
#define AuOpts_REFRESH_DYAOP (1 << 3)
#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name)
#define au_fset_opts(flags, name) \
do { (flags) |= AuOpts_##name; } while (0)
#define au_fclr_opts(flags, name) \
do { (flags) &= ~AuOpts_##name; } while (0)
struct au_opts {
struct au_opt *opt;
int max_opt;
unsigned int given_udba;
unsigned int flags;
unsigned long sb_flags;
};
/* ---------------------------------------------------------------------- */
char *au_optstr_br_perm(int brperm);
const char *au_optstr_udba(int udba);
const char *au_optstr_wbr_copyup(int wbr_copyup);
const char *au_optstr_wbr_create(int wbr_create);
void au_opts_free(struct au_opts *opts);
int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts);
int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
unsigned int pending);
int au_opts_mount(struct super_block *sb, struct au_opts *opts);
int au_opts_remount(struct super_block *sb, struct au_opts *opts);
unsigned int au_opt_udba(struct super_block *sb);
/* ---------------------------------------------------------------------- */
#endif /* __KERNEL__ */
#endif /* __AUFS_OPTS_H__ */

View File

@ -0,0 +1,515 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* pseudo-link
*/
#include "aufs.h"
/*
* the pseudo-link maintenance mode.
* during a user process maintains the pseudo-links,
* prohibit adding a new plink and branch manipulation.
*
* Flags
* NOPLM:
* For entry functions which will handle plink, and i_mutex is already held
* in VFS.
* They cannot wait and should return an error at once.
* Callers has to check the error.
* NOPLMW:
* For entry functions which will handle plink, but i_mutex is not held
* in VFS.
* They can wait the plink maintenance mode to finish.
*
* They behave like F_SETLK and F_SETLKW.
* If the caller never handle plink, then both flags are unnecessary.
*/
int au_plink_maint(struct super_block *sb, int flags)
{
int err;
pid_t pid, ppid;
struct au_sbinfo *sbi;
SiMustAnyLock(sb);
err = 0;
if (!au_opt_test(au_mntflags(sb), PLINK))
goto out;
sbi = au_sbi(sb);
pid = sbi->si_plink_maint_pid;
if (!pid || pid == current->pid)
goto out;
/* todo: it highly depends upon /sbin/mount.aufs */
rcu_read_lock();
ppid = task_pid_vnr(rcu_dereference(current->real_parent));
rcu_read_unlock();
if (pid == ppid)
goto out;
if (au_ftest_lock(flags, NOPLMW)) {
/* if there is no i_mutex lock in VFS, we don't need to wait */
/* AuDebugOn(!lockdep_depth(current)); */
while (sbi->si_plink_maint_pid) {
si_read_unlock(sb);
/* gave up wake_up_bit() */
wait_event(sbi->si_plink_wq, !sbi->si_plink_maint_pid);
if (au_ftest_lock(flags, FLUSH))
au_nwt_flush(&sbi->si_nowait);
si_noflush_read_lock(sb);
}
} else if (au_ftest_lock(flags, NOPLM)) {
AuDbg("ppid %d, pid %d\n", ppid, pid);
err = -EAGAIN;
}
out:
return err;
}
void au_plink_maint_leave(struct au_sbinfo *sbinfo)
{
spin_lock(&sbinfo->si_plink_maint_lock);
sbinfo->si_plink_maint_pid = 0;
spin_unlock(&sbinfo->si_plink_maint_lock);
wake_up_all(&sbinfo->si_plink_wq);
}
int au_plink_maint_enter(struct super_block *sb)
{
int err;
struct au_sbinfo *sbinfo;
err = 0;
sbinfo = au_sbi(sb);
/* make sure i am the only one in this fs */
si_write_lock(sb, AuLock_FLUSH);
if (au_opt_test(au_mntflags(sb), PLINK)) {
spin_lock(&sbinfo->si_plink_maint_lock);
if (!sbinfo->si_plink_maint_pid)
sbinfo->si_plink_maint_pid = current->pid;
else
err = -EBUSY;
spin_unlock(&sbinfo->si_plink_maint_lock);
}
si_write_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
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)
{
struct au_sbinfo *sbinfo;
struct list_head *plink_list;
struct pseudo_link *plink;
SiMustAnyLock(sb);
sbinfo = au_sbi(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();
}
#endif
/* is the inode pseudo-linked? */
int au_plink_test(struct inode *inode)
{
int found;
struct au_sbinfo *sbinfo;
struct list_head *plink_list;
struct pseudo_link *plink;
sbinfo = au_sbi(inode->i_sb);
AuRwMustAnyLock(&sbinfo->si_rwsem);
AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK));
AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM));
found = 0;
plink_list = &sbinfo->si_plink.head;
rcu_read_lock();
list_for_each_entry_rcu(plink, plink_list, list)
if (plink->inode == inode) {
found = 1;
break;
}
rcu_read_unlock();
return found;
}
/* ---------------------------------------------------------------------- */
/*
* generate a name for plink.
* the file will be stored under AUFS_WH_PLINKDIR.
*/
/* 20 is max digits length of ulong 64 */
#define PLINK_NAME_LEN ((20 + 1) * 2)
static int plink_name(char *name, int len, struct inode *inode,
aufs_bindex_t bindex)
{
int rlen;
struct inode *h_inode;
h_inode = au_h_iptr(inode, bindex);
rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino);
return rlen;
}
struct au_do_plink_lkup_args {
struct dentry **errp;
struct qstr *tgtname;
struct dentry *h_parent;
struct au_branch *br;
};
static struct dentry *au_do_plink_lkup(struct qstr *tgtname,
struct dentry *h_parent,
struct au_branch *br)
{
struct dentry *h_dentry;
struct mutex *h_mtx;
h_mtx = &h_parent->d_inode->i_mutex;
mutex_lock_nested(h_mtx, AuLsc_I_CHILD2);
h_dentry = au_lkup_one(tgtname, h_parent, br, /*nd*/NULL);
mutex_unlock(h_mtx);
return h_dentry;
}
static void au_call_do_plink_lkup(void *args)
{
struct au_do_plink_lkup_args *a = args;
*a->errp = au_do_plink_lkup(a->tgtname, a->h_parent, a->br);
}
/* lookup the plink-ed @inode under the branch at @bindex */
struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex)
{
struct dentry *h_dentry, *h_parent;
struct au_branch *br;
struct inode *h_dir;
int wkq_err;
char a[PLINK_NAME_LEN];
struct qstr tgtname = {
.name = a
};
AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM));
br = au_sbr(inode->i_sb, bindex);
h_parent = br->br_wbr->wbr_plink;
h_dir = h_parent->d_inode;
tgtname.len = plink_name(a, sizeof(a), inode, bindex);
if (current_fsuid()) {
struct au_do_plink_lkup_args args = {
.errp = &h_dentry,
.tgtname = &tgtname,
.h_parent = h_parent,
.br = br
};
wkq_err = au_wkq_wait(au_call_do_plink_lkup, &args);
if (unlikely(wkq_err))
h_dentry = ERR_PTR(wkq_err);
} else
h_dentry = au_do_plink_lkup(&tgtname, h_parent, br);
return h_dentry;
}
/* create a pseudo-link */
static int do_whplink(struct qstr *tgt, struct dentry *h_parent,
struct dentry *h_dentry, struct au_branch *br)
{
int err;
struct path h_path = {
.mnt = br->br_mnt
};
struct inode *h_dir;
h_dir = h_parent->d_inode;
mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2);
again:
h_path.dentry = au_lkup_one(tgt, h_parent, br, /*nd*/NULL);
err = PTR_ERR(h_path.dentry);
if (IS_ERR(h_path.dentry))
goto out;
err = 0;
/* wh.plink dir is not monitored */
/* todo: is it really safe? */
if (h_path.dentry->d_inode
&& h_path.dentry->d_inode != h_dentry->d_inode) {
err = vfsub_unlink(h_dir, &h_path, /*force*/0);
dput(h_path.dentry);
h_path.dentry = NULL;
if (!err)
goto again;
}
if (!err && !h_path.dentry->d_inode)
err = vfsub_link(h_dentry, h_dir, &h_path);
dput(h_path.dentry);
out:
mutex_unlock(&h_dir->i_mutex);
return err;
}
struct do_whplink_args {
int *errp;
struct qstr *tgt;
struct dentry *h_parent;
struct dentry *h_dentry;
struct au_branch *br;
};
static void call_do_whplink(void *args)
{
struct do_whplink_args *a = args;
*a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br);
}
static int whplink(struct dentry *h_dentry, struct inode *inode,
aufs_bindex_t bindex, struct au_branch *br)
{
int err, wkq_err;
struct au_wbr *wbr;
struct dentry *h_parent;
struct inode *h_dir;
char a[PLINK_NAME_LEN];
struct qstr tgtname = {
.name = a
};
wbr = au_sbr(inode->i_sb, bindex)->br_wbr;
h_parent = wbr->wbr_plink;
h_dir = h_parent->d_inode;
tgtname.len = plink_name(a, sizeof(a), inode, bindex);
/* always superio. */
if (current_fsuid()) {
struct do_whplink_args args = {
.errp = &err,
.tgt = &tgtname,
.h_parent = h_parent,
.h_dentry = h_dentry,
.br = br
};
wkq_err = au_wkq_wait(call_do_whplink, &args);
if (unlikely(wkq_err))
err = wkq_err;
} else
err = do_whplink(&tgtname, h_parent, h_dentry, br);
return err;
}
/* free a single plink */
static void do_put_plink(struct pseudo_link *plink, int do_del)
{
if (do_del)
list_del(&plink->list);
iput(plink->inode);
kfree(plink);
}
static void do_put_plink_rcu(struct rcu_head *rcu)
{
struct pseudo_link *plink;
plink = container_of(rcu, struct pseudo_link, rcu);
iput(plink->inode);
kfree(plink);
}
/*
* create a new pseudo-link for @h_dentry on @bindex.
* the linked inode is held in aufs @inode.
*/
void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
struct dentry *h_dentry)
{
struct super_block *sb;
struct au_sbinfo *sbinfo;
struct list_head *plink_list;
struct pseudo_link *plink, *tmp;
int found, err, cnt;
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();
if (found)
return;
tmp = kmalloc(sizeof(*plink), GFP_NOFS);
if (tmp)
tmp->inode = au_igrab(inode);
else {
err = -ENOMEM;
goto out;
}
spin_lock(&sbinfo->si_plink.spin);
list_for_each_entry(plink, plink_list, list) {
if (plink->inode == inode) {
found = 1;
break;
}
}
if (!found)
list_add_rcu(&tmp->list, plink_list);
spin_unlock(&sbinfo->si_plink.spin);
if (!found) {
cnt++;
WARN_ONCE(cnt > AUFS_PLINK_WARN,
"unexpectedly many pseudo links, %d\n", cnt);
err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex));
} else {
do_put_plink(tmp, 0);
return;
}
out:
if (unlikely(err)) {
pr_warn("err %d, damaged pseudo link.\n", err);
if (tmp) {
au_spl_del_rcu(&tmp->list, &sbinfo->si_plink);
call_rcu(&tmp->rcu, do_put_plink_rcu);
}
}
}
/* free all plinks */
void au_plink_put(struct super_block *sb, int verbose)
{
struct au_sbinfo *sbinfo;
struct list_head *plink_list;
struct pseudo_link *plink, *tmp;
SiMustWriteLock(sb);
sbinfo = au_sbi(sb);
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);
}
void au_plink_clean(struct super_block *sb, int verbose)
{
struct dentry *root;
root = sb->s_root;
aufs_write_lock(root);
if (au_opt_test(au_mntflags(sb), PLINK))
au_plink_put(sb, verbose);
aufs_write_unlock(root);
}
/* 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 inode *inode;
aufs_bindex_t bstart, bend, bindex;
unsigned char do_put;
SiMustWriteLock(sb);
sbinfo = au_sbi(sb);
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;
}
if (do_put)
do_put_plink(plink, 1);
}
ii_write_unlock(inode);
iput(inode);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* poll operation
* There is only one filesystem which implements ->poll operation, currently.
*/
#include "aufs.h"
unsigned int aufs_poll(struct file *file, poll_table *wait)
{
unsigned int mask;
int err;
struct file *h_file;
struct dentry *dentry;
struct super_block *sb;
/* We should pretend an error happened. */
mask = POLLERR /* | POLLIN | POLLOUT */;
dentry = file->f_dentry;
sb = dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
if (unlikely(err))
goto out;
/* it is not an error if h_file has no operation */
mask = DEFAULT_POLLMASK;
h_file = au_hf_top(file);
if (h_file->f_op && h_file->f_op->poll)
mask = h_file->f_op->poll(h_file, wait);
di_read_unlock(dentry, AuLock_IR);
fi_read_unlock(file);
out:
si_read_unlock(sb);
AuTraceErr((int)mask);
return mask;
}

View File

@ -0,0 +1,170 @@
/*
* Copyright (C) 2010-2012 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
*/
/*
* procfs interfaces
*/
#include <linux/proc_fs.h>
#include "aufs.h"
static int au_procfs_plm_release(struct inode *inode, struct file *file)
{
struct au_sbinfo *sbinfo;
sbinfo = file->private_data;
if (sbinfo) {
au_plink_maint_leave(sbinfo);
kobject_put(&sbinfo->si_kobj);
}
return 0;
}
static void au_procfs_plm_write_clean(struct file *file)
{
struct au_sbinfo *sbinfo;
sbinfo = file->private_data;
if (sbinfo)
au_plink_clean(sbinfo->si_sb, /*verbose*/0);
}
static int au_procfs_plm_write_si(struct file *file, unsigned long id)
{
int err;
struct super_block *sb;
struct au_sbinfo *sbinfo;
err = -EBUSY;
if (unlikely(file->private_data))
goto out;
sb = NULL;
/* don't use au_sbilist_lock() here */
spin_lock(&au_sbilist.spin);
list_for_each_entry(sbinfo, &au_sbilist.head, si_list)
if (id == sysaufs_si_id(sbinfo)) {
kobject_get(&sbinfo->si_kobj);
sb = sbinfo->si_sb;
break;
}
spin_unlock(&au_sbilist.spin);
err = -EINVAL;
if (unlikely(!sb))
goto out;
err = au_plink_maint_enter(sb);
if (!err)
/* keep kobject_get() */
file->private_data = sbinfo;
else
kobject_put(&sbinfo->si_kobj);
out:
return err;
}
/*
* Accept a valid "si=xxxx" only.
* Once it is accepted successfully, accept "clean" too.
*/
static ssize_t au_procfs_plm_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
ssize_t err;
unsigned long id;
/* last newline is allowed */
char buf[3 + sizeof(unsigned long) * 2 + 1];
err = -EACCES;
if (unlikely(!capable(CAP_SYS_ADMIN)))
goto out;
err = -EINVAL;
if (unlikely(count > sizeof(buf)))
goto out;
err = copy_from_user(buf, ubuf, count);
if (unlikely(err)) {
err = -EFAULT;
goto out;
}
buf[count] = 0;
err = -EINVAL;
if (!strcmp("clean", buf)) {
au_procfs_plm_write_clean(file);
goto out_success;
} else if (unlikely(strncmp("si=", buf, 3)))
goto out;
err = strict_strtoul(buf + 3, 16, &id);
if (unlikely(err))
goto out;
err = au_procfs_plm_write_si(file, id);
if (unlikely(err))
goto out;
out_success:
err = count; /* success */
out:
return err;
}
static const struct file_operations au_procfs_plm_fop = {
.write = au_procfs_plm_write,
.release = au_procfs_plm_release,
.owner = THIS_MODULE
};
/* ---------------------------------------------------------------------- */
static struct proc_dir_entry *au_procfs_dir;
void au_procfs_fin(void)
{
remove_proc_entry(AUFS_PLINK_MAINT_NAME, au_procfs_dir);
remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL);
}
int __init au_procfs_init(void)
{
int err;
struct proc_dir_entry *entry;
err = -ENOMEM;
au_procfs_dir = proc_mkdir(AUFS_PLINK_MAINT_DIR, NULL);
if (unlikely(!au_procfs_dir))
goto out;
entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | S_IWUSR,
au_procfs_dir, &au_procfs_plm_fop);
if (unlikely(!entry))
goto out_dir;
err = 0;
goto out; /* success */
out_dir:
remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL);
out:
return err;
}

View File

@ -0,0 +1,383 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* readdir in userspace.
*/
#include <linux/compat.h>
#include <linux/fs_stack.h>
#include <linux/security.h>
#include "aufs.h"
/* bits for struct aufs_rdu.flags */
#define AuRdu_CALLED 1
#define AuRdu_CONT (1 << 1)
#define AuRdu_FULL (1 << 2)
#define au_ftest_rdu(flags, name) ((flags) & AuRdu_##name)
#define au_fset_rdu(flags, name) \
do { (flags) |= AuRdu_##name; } while (0)
#define au_fclr_rdu(flags, name) \
do { (flags) &= ~AuRdu_##name; } while (0)
struct au_rdu_arg {
struct aufs_rdu *rdu;
union au_rdu_ent_ul ent;
unsigned long end;
struct super_block *sb;
int err;
};
static int au_rdu_fill(void *__arg, const char *name, int nlen,
loff_t offset, u64 h_ino, unsigned int d_type)
{
int err, len;
struct au_rdu_arg *arg = __arg;
struct aufs_rdu *rdu = arg->rdu;
struct au_rdu_ent ent;
err = 0;
arg->err = 0;
au_fset_rdu(rdu->cookie.flags, CALLED);
len = au_rdu_len(nlen);
if (arg->ent.ul + len < arg->end) {
ent.ino = h_ino;
ent.bindex = rdu->cookie.bindex;
ent.type = d_type;
ent.nlen = nlen;
if (unlikely(nlen > AUFS_MAX_NAMELEN))
ent.type = DT_UNKNOWN;
/* unnecessary to support mmap_sem since this is a dir */
err = -EFAULT;
if (copy_to_user(arg->ent.e, &ent, sizeof(ent)))
goto out;
if (copy_to_user(arg->ent.e->name, name, nlen))
goto out;
/* the terminating NULL */
if (__put_user(0, arg->ent.e->name + nlen))
goto out;
err = 0;
/* AuDbg("%p, %.*s\n", arg->ent.p, nlen, name); */
arg->ent.ul += len;
rdu->rent++;
} else {
err = -EFAULT;
au_fset_rdu(rdu->cookie.flags, FULL);
rdu->full = 1;
rdu->tail = arg->ent;
}
out:
/* AuTraceErr(err); */
return err;
}
static int au_rdu_do(struct file *h_file, struct au_rdu_arg *arg)
{
int err;
loff_t offset;
struct au_rdu_cookie *cookie = &arg->rdu->cookie;
offset = vfsub_llseek(h_file, cookie->h_pos, SEEK_SET);
err = offset;
if (unlikely(offset != cookie->h_pos))
goto out;
err = 0;
do {
arg->err = 0;
au_fclr_rdu(cookie->flags, CALLED);
/* smp_mb(); */
err = vfsub_readdir(h_file, au_rdu_fill, arg);
if (err >= 0)
err = arg->err;
} while (!err
&& au_ftest_rdu(cookie->flags, CALLED)
&& !au_ftest_rdu(cookie->flags, FULL));
cookie->h_pos = h_file->f_pos;
out:
AuTraceErr(err);
return err;
}
static int au_rdu(struct file *file, struct aufs_rdu *rdu)
{
int err;
aufs_bindex_t bend;
struct au_rdu_arg arg;
struct dentry *dentry;
struct inode *inode;
struct file *h_file;
struct au_rdu_cookie *cookie = &rdu->cookie;
err = !access_ok(VERIFY_WRITE, rdu->ent.e, rdu->sz);
if (unlikely(err)) {
err = -EFAULT;
AuTraceErr(err);
goto out;
}
rdu->rent = 0;
rdu->tail = rdu->ent;
rdu->full = 0;
arg.rdu = rdu;
arg.ent = rdu->ent;
arg.end = arg.ent.ul;
arg.end += rdu->sz;
err = -ENOTDIR;
if (unlikely(!file->f_op || !file->f_op->readdir))
goto out;
err = security_file_permission(file, MAY_READ);
AuTraceErr(err);
if (unlikely(err))
goto out;
dentry = file->f_dentry;
inode = dentry->d_inode;
#if 1
mutex_lock(&inode->i_mutex);
#else
err = mutex_lock_killable(&inode->i_mutex);
AuTraceErr(err);
if (unlikely(err))
goto out;
#endif
arg.sb = inode->i_sb;
err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM);
if (unlikely(err))
goto out_mtx;
err = au_alive_dir(dentry);
if (unlikely(err))
goto out_si;
/* todo: reval? */
fi_read_lock(file);
err = -EAGAIN;
if (unlikely(au_ftest_rdu(cookie->flags, CONT)
&& cookie->generation != au_figen(file)))
goto out_unlock;
err = 0;
if (!rdu->blk) {
rdu->blk = au_sbi(arg.sb)->si_rdblk;
if (!rdu->blk)
rdu->blk = au_dir_size(file, /*dentry*/NULL);
}
bend = au_fbstart(file);
if (cookie->bindex < bend)
cookie->bindex = bend;
bend = au_fbend_dir(file);
/* AuDbg("b%d, b%d\n", cookie->bindex, bend); */
for (; !err && cookie->bindex <= bend;
cookie->bindex++, cookie->h_pos = 0) {
h_file = au_hf_dir(file, cookie->bindex);
if (!h_file)
continue;
au_fclr_rdu(cookie->flags, FULL);
err = au_rdu_do(h_file, &arg);
AuTraceErr(err);
if (unlikely(au_ftest_rdu(cookie->flags, FULL) || err))
break;
}
AuDbg("rent %llu\n", rdu->rent);
if (!err && !au_ftest_rdu(cookie->flags, CONT)) {
rdu->shwh = !!au_opt_test(au_sbi(arg.sb)->si_mntflags, SHWH);
au_fset_rdu(cookie->flags, CONT);
cookie->generation = au_figen(file);
}
ii_read_lock_child(inode);
fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode)));
ii_read_unlock(inode);
out_unlock:
fi_read_unlock(file);
out_si:
si_read_unlock(arg.sb);
out_mtx:
mutex_unlock(&inode->i_mutex);
out:
AuTraceErr(err);
return err;
}
static int au_rdu_ino(struct file *file, struct aufs_rdu *rdu)
{
int err;
ino_t ino;
unsigned long long nent;
union au_rdu_ent_ul *u;
struct au_rdu_ent ent;
struct super_block *sb;
err = 0;
nent = rdu->nent;
u = &rdu->ent;
sb = file->f_dentry->d_sb;
si_read_lock(sb, AuLock_FLUSH);
while (nent-- > 0) {
/* unnecessary to support mmap_sem since this is a dir */
err = copy_from_user(&ent, u->e, sizeof(ent));
if (!err)
err = !access_ok(VERIFY_WRITE, &u->e->ino, sizeof(ino));
if (unlikely(err)) {
err = -EFAULT;
AuTraceErr(err);
break;
}
/* AuDbg("b%d, i%llu\n", ent.bindex, ent.ino); */
if (!ent.wh)
err = au_ino(sb, ent.bindex, ent.ino, ent.type, &ino);
else
err = au_wh_ino(sb, ent.bindex, ent.ino, ent.type,
&ino);
if (unlikely(err)) {
AuTraceErr(err);
break;
}
err = __put_user(ino, &u->e->ino);
if (unlikely(err)) {
err = -EFAULT;
AuTraceErr(err);
break;
}
u->ul += au_rdu_len(ent.nlen);
}
si_read_unlock(sb);
return err;
}
/* ---------------------------------------------------------------------- */
static int au_rdu_verify(struct aufs_rdu *rdu)
{
AuDbg("rdu{%llu, %p, %u | %u | %llu, %u, %u | "
"%llu, b%d, 0x%x, g%u}\n",
rdu->sz, rdu->ent.e, rdu->verify[AufsCtlRduV_SZ],
rdu->blk,
rdu->rent, rdu->shwh, rdu->full,
rdu->cookie.h_pos, rdu->cookie.bindex, rdu->cookie.flags,
rdu->cookie.generation);
if (rdu->verify[AufsCtlRduV_SZ] == sizeof(*rdu))
return 0;
AuDbg("%u:%u\n",
rdu->verify[AufsCtlRduV_SZ], (unsigned int)sizeof(*rdu));
return -EINVAL;
}
long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long err, e;
struct aufs_rdu rdu;
void __user *p = (void __user *)arg;
err = copy_from_user(&rdu, p, sizeof(rdu));
if (unlikely(err)) {
err = -EFAULT;
AuTraceErr(err);
goto out;
}
err = au_rdu_verify(&rdu);
if (unlikely(err))
goto out;
switch (cmd) {
case AUFS_CTL_RDU:
err = au_rdu(file, &rdu);
if (unlikely(err))
break;
e = copy_to_user(p, &rdu, sizeof(rdu));
if (unlikely(e)) {
err = -EFAULT;
AuTraceErr(err);
}
break;
case AUFS_CTL_RDU_INO:
err = au_rdu_ino(file, &rdu);
break;
default:
/* err = -ENOTTY; */
err = -EINVAL;
}
out:
AuTraceErr(err);
return err;
}
#ifdef CONFIG_COMPAT
long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long err, e;
struct aufs_rdu rdu;
void __user *p = compat_ptr(arg);
/* todo: get_user()? */
err = copy_from_user(&rdu, p, sizeof(rdu));
if (unlikely(err)) {
err = -EFAULT;
AuTraceErr(err);
goto out;
}
rdu.ent.e = compat_ptr(rdu.ent.ul);
err = au_rdu_verify(&rdu);
if (unlikely(err))
goto out;
switch (cmd) {
case AUFS_CTL_RDU:
err = au_rdu(file, &rdu);
if (unlikely(err))
break;
rdu.ent.ul = ptr_to_compat(rdu.ent.e);
rdu.tail.ul = ptr_to_compat(rdu.tail.e);
e = copy_to_user(p, &rdu, sizeof(rdu));
if (unlikely(e)) {
err = -EFAULT;
AuTraceErr(err);
}
break;
case AUFS_CTL_RDU_INO:
err = au_rdu_ino(file, &rdu);
break;
default:
/* err = -ENOTTY; */
err = -EINVAL;
}
out:
AuTraceErr(err);
return err;
}
#endif

View File

@ -0,0 +1,188 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* simple read-write semaphore wrappers
*/
#ifndef __AUFS_RWSEM_H__
#define __AUFS_RWSEM_H__
#ifdef __KERNEL__
#include "debug.h"
struct au_rwsem {
struct rw_semaphore rwsem;
#ifdef CONFIG_AUFS_DEBUG
/* just for debugging, not almighty counter */
atomic_t rcnt, wcnt;
#endif
};
#ifdef CONFIG_AUFS_DEBUG
#define AuDbgCntInit(rw) do { \
atomic_set(&(rw)->rcnt, 0); \
atomic_set(&(rw)->wcnt, 0); \
smp_mb(); /* atomic set */ \
} while (0)
#define AuDbgRcntInc(rw) atomic_inc(&(rw)->rcnt)
#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0)
#define AuDbgWcntInc(rw) atomic_inc(&(rw)->wcnt)
#define AuDbgWcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->wcnt) < 0)
#else
#define AuDbgCntInit(rw) do {} while (0)
#define AuDbgRcntInc(rw) do {} while (0)
#define AuDbgRcntDec(rw) do {} while (0)
#define AuDbgWcntInc(rw) do {} while (0)
#define AuDbgWcntDec(rw) do {} while (0)
#endif /* CONFIG_AUFS_DEBUG */
/* to debug easier, do not make them inlined functions */
#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list))
/* rwsem_is_locked() is unusable */
#define AuRwMustReadLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0)
#define AuRwMustWriteLock(rw) AuDebugOn(atomic_read(&(rw)->wcnt) <= 0)
#define AuRwMustAnyLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0 \
&& atomic_read(&(rw)->wcnt) <= 0)
#define AuRwDestroy(rw) AuDebugOn(atomic_read(&(rw)->rcnt) \
|| atomic_read(&(rw)->wcnt))
#define au_rw_class(rw, key) lockdep_set_class(&(rw)->rwsem, key)
static inline void au_rw_init(struct au_rwsem *rw)
{
AuDbgCntInit(rw);
init_rwsem(&rw->rwsem);
}
static inline void au_rw_init_wlock(struct au_rwsem *rw)
{
au_rw_init(rw);
down_write(&rw->rwsem);
AuDbgWcntInc(rw);
}
static inline void au_rw_init_wlock_nested(struct au_rwsem *rw,
unsigned int lsc)
{
au_rw_init(rw);
down_write_nested(&rw->rwsem, lsc);
AuDbgWcntInc(rw);
}
static inline void au_rw_read_lock(struct au_rwsem *rw)
{
down_read(&rw->rwsem);
AuDbgRcntInc(rw);
}
static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc)
{
down_read_nested(&rw->rwsem, lsc);
AuDbgRcntInc(rw);
}
static inline void au_rw_read_unlock(struct au_rwsem *rw)
{
AuRwMustReadLock(rw);
AuDbgRcntDec(rw);
up_read(&rw->rwsem);
}
static inline void au_rw_dgrade_lock(struct au_rwsem *rw)
{
AuRwMustWriteLock(rw);
AuDbgRcntInc(rw);
AuDbgWcntDec(rw);
downgrade_write(&rw->rwsem);
}
static inline void au_rw_write_lock(struct au_rwsem *rw)
{
down_write(&rw->rwsem);
AuDbgWcntInc(rw);
}
static inline void au_rw_write_lock_nested(struct au_rwsem *rw,
unsigned int lsc)
{
down_write_nested(&rw->rwsem, lsc);
AuDbgWcntInc(rw);
}
static inline void au_rw_write_unlock(struct au_rwsem *rw)
{
AuRwMustWriteLock(rw);
AuDbgWcntDec(rw);
up_write(&rw->rwsem);
}
/* why is not _nested version defined */
static inline int au_rw_read_trylock(struct au_rwsem *rw)
{
int ret = down_read_trylock(&rw->rwsem);
if (ret)
AuDbgRcntInc(rw);
return ret;
}
static inline int au_rw_write_trylock(struct au_rwsem *rw)
{
int ret = down_write_trylock(&rw->rwsem);
if (ret)
AuDbgWcntInc(rw);
return ret;
}
#undef AuDbgCntInit
#undef AuDbgRcntInc
#undef AuDbgRcntDec
#undef AuDbgWcntInc
#undef AuDbgWcntDec
#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \
static inline void prefix##_read_lock(param) \
{ au_rw_read_lock(rwsem); } \
static inline void prefix##_write_lock(param) \
{ au_rw_write_lock(rwsem); } \
static inline int prefix##_read_trylock(param) \
{ return au_rw_read_trylock(rwsem); } \
static inline int prefix##_write_trylock(param) \
{ return au_rw_write_trylock(rwsem); }
/* why is not _nested version defined */
/* static inline void prefix##_read_trylock_nested(param, lsc)
{ au_rw_read_trylock_nested(rwsem, lsc)); }
static inline void prefix##_write_trylock_nestd(param, lsc)
{ au_rw_write_trylock_nested(rwsem, lsc); } */
#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \
static inline void prefix##_read_unlock(param) \
{ au_rw_read_unlock(rwsem); } \
static inline void prefix##_write_unlock(param) \
{ au_rw_write_unlock(rwsem); } \
static inline void prefix##_downgrade_lock(param) \
{ au_rw_dgrade_lock(rwsem); }
#define AuSimpleRwsemFuncs(prefix, param, rwsem) \
AuSimpleLockRwsemFuncs(prefix, param, rwsem) \
AuSimpleUnlockRwsemFuncs(prefix, param, rwsem)
#endif /* __KERNEL__ */
#endif /* __AUFS_RWSEM_H__ */

View File

@ -0,0 +1,343 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* superblock private data
*/
#include "aufs.h"
/*
* they are necessary regardless sysfs is disabled.
*/
void au_si_free(struct kobject *kobj)
{
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));
AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len));
au_rw_write_lock(&sbinfo->si_rwsem);
au_br_free(sbinfo);
au_rw_write_unlock(&sbinfo->si_rwsem);
AuDebugOn(radix_tree_gang_lookup
(&sbinfo->au_si_pid.tree, (void **)&locked,
/*first_index*/PID_MAX_DEFAULT - 1,
/*max_items*/sizeof(locked)/sizeof(*locked)));
kfree(sbinfo->si_branch);
kfree(sbinfo->au_si_pid.bitmap);
mutex_destroy(&sbinfo->si_xib_mtx);
AuRwDestroy(&sbinfo->si_rwsem);
kfree(sbinfo);
}
int au_si_alloc(struct super_block *sb)
{
int err;
struct au_sbinfo *sbinfo;
static struct lock_class_key aufs_si;
err = -ENOMEM;
sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS);
if (unlikely(!sbinfo))
goto out;
BUILD_BUG_ON(sizeof(unsigned long) !=
sizeof(*sbinfo->au_si_pid.bitmap));
sbinfo->au_si_pid.bitmap = kcalloc(BITS_TO_LONGS(PID_MAX_DEFAULT),
sizeof(*sbinfo->au_si_pid.bitmap),
GFP_NOFS);
if (unlikely(!sbinfo->au_si_pid.bitmap))
goto out_sbinfo;
/* will be reallocated separately */
sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS);
if (unlikely(!sbinfo->si_branch))
goto out_pidmap;
err = sysaufs_si_init(sbinfo);
if (unlikely(err))
goto out_br;
au_nwt_init(&sbinfo->si_nowait);
au_rw_init_wlock(&sbinfo->si_rwsem);
au_rw_class(&sbinfo->si_rwsem, &aufs_si);
spin_lock_init(&sbinfo->au_si_pid.tree_lock);
INIT_RADIX_TREE(&sbinfo->au_si_pid.tree, GFP_ATOMIC | __GFP_NOFAIL);
atomic_long_set(&sbinfo->si_ninodes, 0);
atomic_long_set(&sbinfo->si_nfiles, 0);
sbinfo->si_bend = -1;
sbinfo->si_wbr_copyup = AuWbrCopyup_Def;
sbinfo->si_wbr_create = AuWbrCreate_Def;
sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup;
sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create;
sbinfo->si_mntflags = au_opts_plink(AuOpt_Def);
mutex_init(&sbinfo->si_xib_mtx);
sbinfo->si_xino_brid = -1;
/* leave si_xib_last_pindex and si_xib_next_bit */
sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC);
sbinfo->si_rdblk = AUFS_RDBLK_DEF;
sbinfo->si_rdhash = AUFS_RDHASH_DEF;
sbinfo->si_dirwh = AUFS_DIRWH_DEF;
au_spl_init(&sbinfo->si_plink);
init_waitqueue_head(&sbinfo->si_plink_wq);
spin_lock_init(&sbinfo->si_plink_maint_lock);
/* leave other members for sysaufs and si_mnt. */
sbinfo->si_sb = sb;
sb->s_fs_info = sbinfo;
si_pid_set(sb);
au_debug_sbinfo_init(sbinfo);
return 0; /* success */
out_br:
kfree(sbinfo->si_branch);
out_pidmap:
kfree(sbinfo->au_si_pid.bitmap);
out_sbinfo:
kfree(sbinfo);
out:
return err;
}
int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr)
{
int err, sz;
struct au_branch **brp;
AuRwMustWriteLock(&sbinfo->si_rwsem);
err = -ENOMEM;
sz = sizeof(*brp) * (sbinfo->si_bend + 1);
if (unlikely(!sz))
sz = sizeof(*brp);
brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS);
if (brp) {
sbinfo->si_branch = brp;
err = 0;
}
return err;
}
/* ---------------------------------------------------------------------- */
unsigned int au_sigen_inc(struct super_block *sb)
{
unsigned int gen;
SiMustWriteLock(sb);
gen = ++au_sbi(sb)->si_generation;
au_update_digen(sb->s_root);
au_update_iigen(sb->s_root->d_inode);
sb->s_root->d_inode->i_version++;
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;
}
/* ---------------------------------------------------------------------- */
/* it is ok that new 'nwt' tasks are appended while we are sleeping */
int si_read_lock(struct super_block *sb, int flags)
{
int err;
err = 0;
if (au_ftest_lock(flags, FLUSH))
au_nwt_flush(&au_sbi(sb)->si_nowait);
si_noflush_read_lock(sb);
err = au_plink_maint(sb, flags);
if (unlikely(err))
si_read_unlock(sb);
return err;
}
int si_write_lock(struct super_block *sb, int flags)
{
int err;
if (au_ftest_lock(flags, FLUSH))
au_nwt_flush(&au_sbi(sb)->si_nowait);
si_noflush_write_lock(sb);
err = au_plink_maint(sb, flags);
if (unlikely(err))
si_write_unlock(sb);
return err;
}
/* dentry and super_block lock. call at entry point */
int aufs_read_lock(struct dentry *dentry, int flags)
{
int err;
struct super_block *sb;
sb = dentry->d_sb;
err = si_read_lock(sb, flags);
if (unlikely(err))
goto out;
if (au_ftest_lock(flags, DW))
di_write_lock_child(dentry);
else
di_read_lock_child(dentry, flags);
if (au_ftest_lock(flags, GEN)) {
err = au_digen_test(dentry, au_sigen(sb));
AuDebugOn(!err && au_dbrange_test(dentry));
if (unlikely(err))
aufs_read_unlock(dentry, flags);
}
out:
return err;
}
void aufs_read_unlock(struct dentry *dentry, int flags)
{
if (au_ftest_lock(flags, DW))
di_write_unlock(dentry);
else
di_read_unlock(dentry, flags);
si_read_unlock(dentry->d_sb);
}
void aufs_write_lock(struct dentry *dentry)
{
si_write_lock(dentry->d_sb, AuLock_FLUSH | AuLock_NOPLMW);
di_write_lock_child(dentry);
}
void aufs_write_unlock(struct dentry *dentry)
{
di_write_unlock(dentry);
si_write_unlock(dentry->d_sb);
}
int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags)
{
int err;
unsigned int sigen;
struct super_block *sb;
sb = d1->d_sb;
err = si_read_lock(sb, flags);
if (unlikely(err))
goto out;
di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR));
if (au_ftest_lock(flags, GEN)) {
sigen = au_sigen(sb);
err = au_digen_test(d1, sigen);
AuDebugOn(!err && au_dbrange_test(d1));
if (!err) {
err = au_digen_test(d2, sigen);
AuDebugOn(!err && au_dbrange_test(d2));
}
if (unlikely(err))
aufs_read_and_write_unlock2(d1, d2);
}
out:
return err;
}
void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2)
{
di_write_unlock2(d1, d2);
si_read_unlock(d1->d_sb);
}
/* ---------------------------------------------------------------------- */
int si_pid_test_slow(struct super_block *sb)
{
void *p;
rcu_read_lock();
p = radix_tree_lookup(&au_sbi(sb)->au_si_pid.tree, current->pid);
rcu_read_unlock();
return (long)!!p;
}
void si_pid_set_slow(struct super_block *sb)
{
int err;
struct au_sbinfo *sbinfo;
AuDebugOn(si_pid_test_slow(sb));
sbinfo = au_sbi(sb);
err = radix_tree_preload(GFP_NOFS | __GFP_NOFAIL);
AuDebugOn(err);
spin_lock(&sbinfo->au_si_pid.tree_lock);
err = radix_tree_insert(&sbinfo->au_si_pid.tree, current->pid,
/*any valid ptr*/sb);
spin_unlock(&sbinfo->au_si_pid.tree_lock);
AuDebugOn(err);
radix_tree_preload_end();
}
void si_pid_clr_slow(struct super_block *sb)
{
void *p;
struct au_sbinfo *sbinfo;
AuDebugOn(!si_pid_test_slow(sb));
sbinfo = au_sbi(sb);
spin_lock(&sbinfo->au_si_pid.tree_lock);
p = radix_tree_delete(&sbinfo->au_si_pid.tree, current->pid);
spin_unlock(&sbinfo->au_si_pid.tree_lock);
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* simple list protected by a spinlock
*/
#ifndef __AUFS_SPL_H__
#define __AUFS_SPL_H__
#ifdef __KERNEL__
struct au_splhead {
spinlock_t spin;
struct list_head head;
};
static inline void au_spl_init(struct au_splhead *spl)
{
spin_lock_init(&spl->spin);
INIT_LIST_HEAD(&spl->head);
}
static inline void au_spl_add(struct list_head *list, struct au_splhead *spl)
{
spin_lock(&spl->spin);
list_add(list, &spl->head);
spin_unlock(&spl->spin);
}
static inline void au_spl_del(struct list_head *list, struct au_splhead *spl)
{
spin_lock(&spl->spin);
list_del(list);
spin_unlock(&spl->spin);
}
static inline void au_spl_del_rcu(struct list_head *list,
struct au_splhead *spl)
{
spin_lock(&spl->spin);
list_del_rcu(list);
spin_unlock(&spl->spin);
}
#endif /* __KERNEL__ */
#endif /* __AUFS_SPL_H__ */

View File

@ -0,0 +1,968 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* mount and super_block operations
*/
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/statfs.h>
#include <linux/vmalloc.h>
#include <linux/writeback.h>
#include "aufs.h"
/*
* super_operations
*/
static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused)
{
struct au_icntnr *c;
c = au_cache_alloc_icntnr();
if (c) {
au_icntnr_init(c);
c->vfs_inode.i_version = 1; /* sigen(sb); */
c->iinfo.ii_hinode = NULL;
return &c->vfs_inode;
}
return NULL;
}
static void aufs_destroy_inode_cb(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
INIT_LIST_HEAD(&inode->i_dentry);
au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode));
}
static void aufs_destroy_inode(struct inode *inode)
{
au_iinfo_fin(inode);
call_rcu(&inode->i_rcu, aufs_destroy_inode_cb);
}
struct inode *au_iget_locked(struct super_block *sb, ino_t ino)
{
struct inode *inode;
int err;
inode = iget_locked(sb, ino);
if (unlikely(!inode)) {
inode = ERR_PTR(-ENOMEM);
goto out;
}
if (!(inode->i_state & I_NEW))
goto out;
err = au_xigen_new(inode);
if (!err)
err = au_iinfo_init(inode);
if (!err)
inode->i_version++;
else {
iget_failed(inode);
inode = ERR_PTR(err);
}
out:
/* never return NULL */
AuDebugOn(!inode);
AuTraceErrPtr(inode);
return inode;
}
/* lock free root dinfo */
static int au_show_brs(struct seq_file *seq, struct super_block *sb)
{
int err;
aufs_bindex_t bindex, bend;
struct path path;
struct au_hdentry *hdp;
struct au_branch *br;
char *perm;
err = 0;
bend = au_sbend(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.dentry = hdp[bindex].hd_dentry;
err = au_seq_path(seq, &path);
if (err > 0) {
perm = au_optstr_br_perm(br->br_perm);
if (perm) {
err = seq_printf(seq, "=%s", perm);
kfree(perm);
if (err == -1)
err = -E2BIG;
} else
err = -ENOMEM;
}
if (!err && bindex != bend)
err = seq_putc(seq, ':');
}
return err;
}
static void au_show_wbr_create(struct seq_file *m, int v,
struct au_sbinfo *sbinfo)
{
const char *pat;
AuRwMustAnyLock(&sbinfo->si_rwsem);
seq_printf(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);
break;
case AuWbrCreate_MFSV:
seq_printf(m, /*pat*/"mfs:%lu",
jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
/ MSEC_PER_SEC);
break;
case AuWbrCreate_PMFSV:
seq_printf(m, /*pat*/"pmfs:%lu",
jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
/ MSEC_PER_SEC);
break;
case AuWbrCreate_MFSRR:
seq_printf(m, /*pat*/"mfsrr:%llu",
sbinfo->si_wbr_mfs.mfsrr_watermark);
break;
case AuWbrCreate_MFSRRV:
seq_printf(m, /*pat*/"mfsrr:%llu:%lu",
sbinfo->si_wbr_mfs.mfsrr_watermark,
jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
/ MSEC_PER_SEC);
break;
}
}
static int au_show_xino(struct seq_file *seq, struct vfsmount *mnt)
{
#ifdef CONFIG_SYSFS
return 0;
#else
int err;
const int len = sizeof(AUFS_XINO_FNAME) - 1;
aufs_bindex_t bindex, brid;
struct super_block *sb;
struct qstr *name;
struct file *f;
struct dentry *d, *h_root;
struct au_hdentry *hdp;
AuRwMustAnyLock(&sbinfo->si_rwsem);
err = 0;
sb = mnt->mnt_sb;
f = au_sbi(sb)->si_xib;
if (!f)
goto out;
/* stop printing the default xino path on the first writable branch */
h_root = NULL;
brid = au_xino_brid(sb);
if (brid >= 0) {
bindex = au_br_index(sb, brid);
hdp = au_di(sb->s_root)->di_hdentry;
h_root = hdp[0 + bindex].hd_dentry;
}
d = f->f_dentry;
name = &d->d_name;
/* safe ->d_parent because the file is unlinked */
if (d->d_parent == h_root
&& name->len == len
&& !memcmp(name->name, AUFS_XINO_FNAME, len))
goto out;
seq_puts(seq, ",xino=");
err = au_xino_path(seq, f);
out:
return err;
#endif
}
/* seq_file will re-call me in case of too long string */
static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt)
{
int err;
unsigned int mnt_flags, v;
struct super_block *sb;
struct au_sbinfo *sbinfo;
#define AuBool(name, str) do { \
v = au_opt_test(mnt_flags, name); \
if (v != au_opt_test(AuOpt_Def, name)) \
seq_printf(m, ",%s" #str, v ? "" : "no"); \
} while (0)
#define AuStr(name, str) do { \
v = mnt_flags & AuOptMask_##name; \
if (v != (AuOpt_Def & AuOptMask_##name)) \
seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \
} while (0)
#define AuUInt(name, str, val) do { \
if (val != AUFS_##name##_DEF) \
seq_printf(m, "," #str "=%u", val); \
} while (0)
/* lock free root dinfo */
sb = mnt->mnt_sb;
si_noflush_read_lock(sb);
sbinfo = au_sbi(sb);
seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo));
mnt_flags = au_mntflags(sb);
if (au_opt_test(mnt_flags, XINO)) {
err = au_show_xino(m, mnt);
if (unlikely(err))
goto out;
} else
seq_puts(m, ",noxino");
AuBool(TRUNC_XINO, trunc_xino);
AuStr(UDBA, udba);
AuBool(SHWH, shwh);
AuBool(PLINK, plink);
AuBool(DIO, dio);
/* AuBool(DIRPERM1, dirperm1); */
/* AuBool(REFROF, refrof); */
v = sbinfo->si_wbr_create;
if (v != AuWbrCreate_Def)
au_show_wbr_create(m, v, sbinfo);
v = sbinfo->si_wbr_copyup;
if (v != AuWbrCopyup_Def)
seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v));
v = au_opt_test(mnt_flags, ALWAYS_DIROPQ);
if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ))
seq_printf(m, ",diropq=%c", v ? 'a' : 'w');
AuUInt(DIRWH, dirwh, sbinfo->si_dirwh);
v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC;
AuUInt(RDCACHE, rdcache, v);
AuUInt(RDBLK, rdblk, sbinfo->si_rdblk);
AuUInt(RDHASH, rdhash, sbinfo->si_rdhash);
AuBool(SUM, sum);
/* AuBool(SUM_W, wsum); */
AuBool(WARN_PERM, warn_perm);
AuBool(VERBOSE, verbose);
out:
/* be sure to print "br:" last */
if (!sysaufs_brs) {
seq_puts(m, ",br:");
au_show_brs(m, sb);
}
si_read_unlock(sb);
return 0;
#undef AuBool
#undef AuStr
#undef AuUInt
}
/* ---------------------------------------------------------------------- */
/* sum mode which returns the summation for statfs(2) */
static u64 au_add_till_max(u64 a, u64 b)
{
u64 old;
old = a;
a += b;
if (old <= a)
return a;
return ULLONG_MAX;
}
static u64 au_mul_till_max(u64 a, long mul)
{
u64 old;
old = a;
a *= mul;
if (old <= a)
return a;
return ULLONG_MAX;
}
static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf)
{
int err;
long bsize, factor;
u64 blocks, bfree, bavail, files, ffree;
aufs_bindex_t bend, bindex, i;
unsigned char shared;
struct path h_path;
struct super_block *h_sb;
err = 0;
bsize = LONG_MAX;
files = 0;
ffree = 0;
blocks = 0;
bfree = 0;
bavail = 0;
bend = au_sbend(sb);
for (bindex = 0; bindex <= bend; bindex++) {
h_path.mnt = au_sbr_mnt(sb, bindex);
h_sb = h_path.mnt->mnt_sb;
shared = 0;
for (i = 0; !shared && i < bindex; i++)
shared = (au_sbr_sb(sb, i) == h_sb);
if (shared)
continue;
/* sb->s_root for NFS is unreliable */
h_path.dentry = h_path.mnt->mnt_root;
err = vfs_statfs(&h_path, buf);
if (unlikely(err))
goto out;
if (bsize > buf->f_bsize) {
/*
* we will reduce bsize, so we have to expand blocks
* etc. to match them again
*/
factor = (bsize / buf->f_bsize);
blocks = au_mul_till_max(blocks, factor);
bfree = au_mul_till_max(bfree, factor);
bavail = au_mul_till_max(bavail, factor);
bsize = buf->f_bsize;
}
factor = (buf->f_bsize / bsize);
blocks = au_add_till_max(blocks,
au_mul_till_max(buf->f_blocks, factor));
bfree = au_add_till_max(bfree,
au_mul_till_max(buf->f_bfree, factor));
bavail = au_add_till_max(bavail,
au_mul_till_max(buf->f_bavail, factor));
files = au_add_till_max(files, buf->f_files);
ffree = au_add_till_max(ffree, buf->f_ffree);
}
buf->f_bsize = bsize;
buf->f_blocks = blocks;
buf->f_bfree = bfree;
buf->f_bavail = bavail;
buf->f_files = files;
buf->f_ffree = ffree;
buf->f_frsize = 0;
out:
return err;
}
static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
int err;
struct path h_path;
struct super_block *sb;
/* lock free root dinfo */
sb = dentry->d_sb;
si_noflush_read_lock(sb);
if (!au_opt_test(au_mntflags(sb), SUM)) {
/* sb->s_root for NFS is unreliable */
h_path.mnt = au_sbr_mnt(sb, 0);
h_path.dentry = h_path.mnt->mnt_root;
err = vfs_statfs(&h_path, buf);
} else
err = au_statfs_sum(sb, buf);
si_read_unlock(sb);
if (!err) {
buf->f_type = AUFS_SUPER_MAGIC;
buf->f_namelen = AUFS_MAX_NAMELEN;
memset(&buf->f_fsid, 0, sizeof(buf->f_fsid));
}
/* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */
return err;
}
/* ---------------------------------------------------------------------- */
/* final actions when unmounting a file system */
static void aufs_put_super(struct super_block *sb)
{
struct au_sbinfo *sbinfo;
sbinfo = au_sbi(sb);
if (!sbinfo)
return;
dbgaufs_si_fin(sbinfo);
kobject_put(&sbinfo->si_kobj);
}
/* ---------------------------------------------------------------------- */
void au_array_free(void *array)
{
if (array) {
if (!is_vmalloc_addr(array))
kfree(array);
else
vfree(array);
}
}
void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg)
{
void *array;
unsigned long long n;
array = NULL;
n = 0;
if (!*hint)
goto out;
if (*hint > ULLONG_MAX / sizeof(array)) {
array = ERR_PTR(-EMFILE);
pr_err("hint %llu\n", *hint);
goto out;
}
array = kmalloc(sizeof(array) * *hint, GFP_NOFS);
if (unlikely(!array))
array = vmalloc(sizeof(array) * *hint);
if (unlikely(!array)) {
array = ERR_PTR(-ENOMEM);
goto out;
}
n = cb(array, *hint, arg);
AuDebugOn(n > *hint);
out:
*hint = n;
return array;
}
static unsigned long long au_iarray_cb(void *a,
unsigned long long max __maybe_unused,
void *arg)
{
unsigned long long n;
struct inode **p, *inode;
struct list_head *head;
n = 0;
p = a;
head = arg;
spin_lock(&inode_sb_list_lock);
list_for_each_entry(inode, head, i_sb_list) {
if (!is_bad_inode(inode)
&& au_ii(inode)->ii_bstart >= 0) {
spin_lock(&inode->i_lock);
if (atomic_read(&inode->i_count)) {
au_igrab(inode);
*p++ = inode;
n++;
AuDebugOn(n > max);
}
spin_unlock(&inode->i_lock);
}
}
spin_unlock(&inode_sb_list_lock);
return n;
}
struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max)
{
*max = atomic_long_read(&au_sbi(sb)->si_ninodes);
return au_array_alloc(max, au_iarray_cb, &sb->s_inodes);
}
void au_iarray_free(struct inode **a, unsigned long long max)
{
unsigned long long ull;
for (ull = 0; ull < max; ull++)
iput(a[ull]);
au_array_free(a);
}
/* ---------------------------------------------------------------------- */
/*
* refresh dentry and inode at remount time.
*/
/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */
static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags,
struct dentry *parent)
{
int err;
di_write_lock_child(dentry);
di_read_lock_parent(parent, AuLock_IR);
err = au_refresh_dentry(dentry, parent);
if (!err && dir_flags)
au_hn_reset(dentry->d_inode, dir_flags);
di_read_unlock(parent, AuLock_IR);
di_write_unlock(dentry);
return err;
}
static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen,
struct au_sbinfo *sbinfo,
const unsigned int dir_flags)
{
int err;
struct dentry *parent;
struct inode *inode;
err = 0;
parent = dget_parent(dentry);
if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) {
inode = dentry->d_inode;
if (inode) {
if (!S_ISDIR(inode->i_mode))
err = au_do_refresh(dentry, /*dir_flags*/0,
parent);
else {
err = au_do_refresh(dentry, dir_flags, parent);
if (unlikely(err))
au_fset_si(sbinfo, FAILED_REFRESH_DIR);
}
} else
err = au_do_refresh(dentry, /*dir_flags*/0, parent);
AuDbgDentry(dentry);
}
dput(parent);
AuTraceErr(err);
return err;
}
static int au_refresh_d(struct super_block *sb)
{
int err, i, j, ndentry, e;
unsigned int sigen;
struct au_dcsub_pages dpages;
struct au_dpage *dpage;
struct dentry **dentries, *d;
struct au_sbinfo *sbinfo;
struct dentry *root = sb->s_root;
const unsigned int dir_flags = au_hi_flags(root->d_inode, /*isdir*/1);
err = au_dpages_init(&dpages, GFP_NOFS);
if (unlikely(err))
goto out;
err = au_dcsub_pages(&dpages, root, NULL, NULL);
if (unlikely(err))
goto out_dpages;
sigen = au_sigen(sb);
sbinfo = au_sbi(sb);
for (i = 0; i < dpages.ndpage; i++) {
dpage = dpages.dpages + i;
dentries = dpage->dentries;
ndentry = dpage->ndentry;
for (j = 0; j < ndentry; j++) {
d = dentries[j];
e = au_do_refresh_d(d, sigen, sbinfo, dir_flags);
if (unlikely(e && !err))
err = e;
/* go on even err */
}
}
out_dpages:
au_dpages_free(&dpages);
out:
return err;
}
static int au_refresh_i(struct super_block *sb)
{
int err, e;
unsigned int sigen;
unsigned long long max, ull;
struct inode *inode, **array;
array = au_iarray_alloc(sb, &max);
err = PTR_ERR(array);
if (IS_ERR(array))
goto out;
err = 0;
sigen = au_sigen(sb);
for (ull = 0; ull < max; ull++) {
inode = array[ull];
if (au_iigen(inode) != sigen) {
ii_write_lock_child(inode);
e = au_refresh_hinode_self(inode);
ii_write_unlock(inode);
if (unlikely(e)) {
pr_err("error %d, i%lu\n", e, inode->i_ino);
if (!err)
err = e;
/* go on even if err */
}
}
}
au_iarray_free(array, max);
out:
return err;
}
static void au_remount_refresh(struct super_block *sb)
{
int err, e;
unsigned int udba;
aufs_bindex_t bindex, bend;
struct dentry *root;
struct inode *inode;
struct au_branch *br;
au_sigen_inc(sb);
au_fclr_si(au_sbi(sb), FAILED_REFRESH_DIR);
root = sb->s_root;
DiMustNoWaiters(root);
inode = root->d_inode;
IiMustNoWaiters(inode);
udba = au_opt_udba(sb);
bend = au_sbend(sb);
for (bindex = 0; bindex <= bend; bindex++) {
br = au_sbr(sb, bindex);
err = au_hnotify_reset_br(udba, br, br->br_perm);
if (unlikely(err))
AuIOErr("hnotify failed on br %d, %d, ignored\n",
bindex, err);
/* go on even if err */
}
au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1));
di_write_unlock(root);
err = au_refresh_d(sb);
e = au_refresh_i(sb);
if (unlikely(e && !err))
err = e;
/* aufs_write_lock() calls ..._child() */
di_write_lock_child(root);
au_cpup_attr_all(inode, /*force*/1);
if (unlikely(err))
AuIOErr("refresh failed, ignored, %d\n", err);
}
/* stop extra interpretation of errno in mount(8), and strange error messages */
static int cvt_err(int err)
{
AuTraceErr(err);
switch (err) {
case -ENOENT:
case -ENOTDIR:
case -EEXIST:
case -EIO:
err = -EINVAL;
}
return err;
}
static int aufs_remount_fs(struct super_block *sb, int *flags, char *data)
{
int err, do_dx;
unsigned int mntflags;
struct au_opts opts;
struct dentry *root;
struct inode *inode;
struct au_sbinfo *sbinfo;
err = 0;
root = sb->s_root;
if (!data || !*data) {
err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
if (!err) {
di_write_lock_child(root);
err = au_opts_verify(sb, *flags, /*pending*/0);
aufs_write_unlock(root);
}
goto out;
}
err = -ENOMEM;
memset(&opts, 0, sizeof(opts));
opts.opt = (void *)__get_free_page(GFP_NOFS);
if (unlikely(!opts.opt))
goto out;
opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
opts.flags = AuOpts_REMOUNT;
opts.sb_flags = *flags;
/* parse it before aufs lock */
err = au_opts_parse(sb, data, &opts);
if (unlikely(err))
goto out_opts;
sbinfo = au_sbi(sb);
inode = root->d_inode;
mutex_lock(&inode->i_mutex);
err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
if (unlikely(err))
goto out_mtx;
di_write_lock_child(root);
/* au_opts_remount() may return an error */
err = au_opts_remount(sb, &opts);
au_opts_free(&opts);
if (au_ftest_opts(opts.flags, REFRESH))
au_remount_refresh(sb);
if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) {
mntflags = au_mntflags(sb);
do_dx = !!au_opt_test(mntflags, DIO);
au_dy_arefresh(do_dx);
}
aufs_write_unlock(root);
out_mtx:
mutex_unlock(&inode->i_mutex);
out_opts:
free_page((unsigned long)opts.opt);
out:
err = cvt_err(err);
AuTraceErr(err);
return err;
}
static const struct super_operations aufs_sop = {
.alloc_inode = aufs_alloc_inode,
.destroy_inode = aufs_destroy_inode,
/* always deleting, no clearing */
.drop_inode = generic_delete_inode,
.show_options = aufs_show_options,
.statfs = aufs_statfs,
.put_super = aufs_put_super,
.remount_fs = aufs_remount_fs
};
/* ---------------------------------------------------------------------- */
static int alloc_root(struct super_block *sb)
{
int err;
struct inode *inode;
struct dentry *root;
err = -ENOMEM;
inode = au_iget_locked(sb, AUFS_ROOT_INO);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out;
inode->i_op = &aufs_dir_iop;
inode->i_fop = &aufs_dir_fop;
inode->i_mode = S_IFDIR;
inode->i_nlink = 2;
unlock_new_inode(inode);
root = d_alloc_root(inode);
if (unlikely(!root))
goto out_iput;
err = PTR_ERR(root);
if (IS_ERR(root))
goto out_iput;
err = au_di_init(root);
if (!err) {
sb->s_root = root;
return 0; /* success */
}
dput(root);
goto out; /* do not iput */
out_iput:
iget_failed(inode);
out:
return err;
}
static int aufs_fill_super(struct super_block *sb, void *raw_data,
int silent __maybe_unused)
{
int err;
struct au_opts opts;
struct dentry *root;
struct inode *inode;
char *arg = raw_data;
if (unlikely(!arg || !*arg)) {
err = -EINVAL;
pr_err("no arg\n");
goto out;
}
err = -ENOMEM;
memset(&opts, 0, sizeof(opts));
opts.opt = (void *)__get_free_page(GFP_NOFS);
if (unlikely(!opts.opt))
goto out;
opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
opts.sb_flags = sb->s_flags;
err = au_si_alloc(sb);
if (unlikely(err))
goto out_opts;
/* all timestamps always follow the ones on the branch */
sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
sb->s_op = &aufs_sop;
sb->s_d_op = &aufs_dop;
sb->s_magic = AUFS_SUPER_MAGIC;
sb->s_maxbytes = 0;
au_export_init(sb);
err = alloc_root(sb);
if (unlikely(err)) {
si_write_unlock(sb);
goto out_info;
}
root = sb->s_root;
inode = root->d_inode;
/*
* actually we can parse options regardless aufs lock here.
* but at remount time, parsing must be done before aufs lock.
* so we follow the same rule.
*/
ii_write_lock_parent(inode);
aufs_write_unlock(root);
err = au_opts_parse(sb, arg, &opts);
if (unlikely(err))
goto out_root;
/* lock vfs_inode first, then aufs. */
mutex_lock(&inode->i_mutex);
aufs_write_lock(root);
err = au_opts_mount(sb, &opts);
au_opts_free(&opts);
aufs_write_unlock(root);
mutex_unlock(&inode->i_mutex);
if (!err)
goto out_opts; /* success */
out_root:
dput(root);
sb->s_root = NULL;
out_info:
dbgaufs_si_fin(au_sbi(sb));
kobject_put(&au_sbi(sb)->si_kobj);
sb->s_fs_info = NULL;
out_opts:
free_page((unsigned long)opts.opt);
out:
AuTraceErr(err);
err = cvt_err(err);
AuTraceErr(err);
return err;
}
/* ---------------------------------------------------------------------- */
static struct dentry *aufs_mount(struct file_system_type *fs_type, int flags,
const char *dev_name __maybe_unused,
void *raw_data)
{
struct dentry *root;
struct super_block *sb;
/* all timestamps always follow the ones on the branch */
/* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */
root = mount_nodev(fs_type, flags, raw_data, aufs_fill_super);
if (IS_ERR(root))
goto out;
sb = root->d_sb;
si_write_lock(sb, !AuLock_FLUSH);
sysaufs_brs_add(sb, 0);
si_write_unlock(sb);
au_sbilist_add(sb);
out:
return root;
}
static void aufs_kill_sb(struct super_block *sb)
{
struct au_sbinfo *sbinfo;
sbinfo = au_sbi(sb);
if (sbinfo) {
au_sbilist_del(sb);
aufs_write_lock(sb->s_root);
if (sbinfo->si_wbr_create_ops->fin)
sbinfo->si_wbr_create_ops->fin(sb);
if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) {
au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE);
au_remount_refresh(sb);
}
if (au_opt_test(sbinfo->si_mntflags, PLINK))
au_plink_put(sb, /*verbose*/1);
au_xino_clr(sb);
sbinfo->si_sb = NULL;
aufs_write_unlock(sb->s_root);
au_nwt_flush(&sbinfo->si_nowait);
}
generic_shutdown_super(sb);
}
struct file_system_type aufs_fs_type = {
.name = AUFS_FSTYPE,
.fs_flags =
FS_RENAME_DOES_D_MOVE /* a race between rename and others */
| FS_REVAL_DOT, /* for NFS branch and udba */
.mount = aufs_mount,
.kill_sb = aufs_kill_sb,
/* no need to __module_get() and module_put(). */
.owner = THIS_MODULE,
};

View File

@ -0,0 +1,546 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* super_block operations
*/
#ifndef __AUFS_SUPER_H__
#define __AUFS_SUPER_H__
#ifdef __KERNEL__
#include <linux/fs.h>
#include "rwsem.h"
#include "spl.h"
#include "wkq.h"
typedef ssize_t (*au_readf_t)(struct file *, char __user *, size_t, loff_t *);
typedef ssize_t (*au_writef_t)(struct file *, const char __user *, size_t,
loff_t *);
/* policies to select one among multiple writable branches */
struct au_wbr_copyup_operations {
int (*copyup)(struct dentry *dentry);
};
struct au_wbr_create_operations {
int (*create)(struct dentry *dentry, int isdir);
int (*init)(struct super_block *sb);
int (*fin)(struct super_block *sb);
};
struct au_wbr_mfs {
struct mutex mfs_lock; /* protect this structure */
unsigned long mfs_jiffy;
unsigned long mfs_expire;
aufs_bindex_t mfs_bindex;
unsigned long long mfsrr_bytes;
unsigned long long mfsrr_watermark;
};
struct au_branch;
struct au_sbinfo {
/* nowait tasks in the system-wide workqueue */
struct au_nowait_tasks si_nowait;
/*
* tried sb->s_umount, but failed due to the dependecy between i_mutex.
* rwsem for au_sbinfo is necessary.
*/
struct au_rwsem si_rwsem;
/* prevent recursive locking in deleting inode */
struct {
unsigned long *bitmap;
spinlock_t tree_lock;
struct radix_tree_root tree;
} au_si_pid;
/*
* dirty approach to protect sb->sb_inodes and ->s_files from remount.
*/
atomic_long_t si_ninodes, si_nfiles;
/* branch management */
unsigned int si_generation;
/* see above flags */
unsigned char au_si_status;
aufs_bindex_t si_bend;
/* 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;
/* policy to select a writable branch */
unsigned char si_wbr_copyup;
unsigned char si_wbr_create;
struct au_wbr_copyup_operations *si_wbr_copyup_ops;
struct au_wbr_create_operations *si_wbr_create_ops;
/* round robin */
atomic_t si_wbr_rr_next;
/* most free space */
struct au_wbr_mfs si_wbr_mfs;
/* mount flags */
/* include/asm-ia64/siginfo.h defines a macro named si_flags */
unsigned int si_mntflags;
/* external inode number (bitmap and translation table) */
au_readf_t si_xread;
au_writef_t si_xwrite;
struct file *si_xib;
struct mutex si_xib_mtx; /* protect xib members */
unsigned long *si_xib_buf;
unsigned long si_xib_last_pindex;
int si_xib_next_bit;
aufs_bindex_t si_xino_brid;
/* reserved for future use */
/* unsigned long long si_xib_limit; */ /* Max xib file size */
#ifdef CONFIG_AUFS_EXPORT
/* i_generation */
struct file *si_xigen;
atomic_t si_xigen_next;
#endif
/* vdir parameters */
unsigned long si_rdcache; /* max cache time in jiffies */
unsigned int si_rdblk; /* deblk size */
unsigned int si_rdhash; /* hash size */
/*
* If the number of whiteouts are larger than si_dirwh, leave all of
* them after au_whtmp_ren to reduce the cost of rmdir(2).
* future fsck.aufs or kernel thread will remove them later.
* Otherwise, remove all whiteouts and the dir in rmdir(2).
*/
unsigned int si_dirwh;
/*
* rename(2) a directory with all children.
*/
/* reserved for future use */
/* int si_rendir; */
/* pseudo_link list */
struct au_splhead si_plink;
wait_queue_head_t si_plink_wq;
spinlock_t si_plink_maint_lock;
pid_t si_plink_maint_pid;
/*
* sysfs and lifetime management.
* this is not a small structure and it may be a waste of memory in case
* of sysfs is disabled, particulary when many aufs-es are mounted.
* but using sysfs is majority.
*/
struct kobject si_kobj;
#ifdef CONFIG_DEBUG_FS
struct dentry *si_dbgaufs, *si_dbgaufs_xib;
#ifdef CONFIG_AUFS_EXPORT
struct dentry *si_dbgaufs_xigen;
#endif
#endif
#ifdef CONFIG_AUFS_SBILIST
struct list_head si_list;
#endif
/* dirty, necessary for unmounting, sysfs and sysrq */
struct super_block *si_sb;
};
/* sbinfo status flags */
/*
* set true when refresh_dirs() failed at remount time.
* then try refreshing dirs at access time again.
* if it is false, refreshing dirs at access time is unnecesary
*/
#define AuSi_FAILED_REFRESH_DIR 1
static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi,
unsigned int flag)
{
AuRwMustAnyLock(&sbi->si_rwsem);
return sbi->au_si_status & flag;
}
#define au_ftest_si(sbinfo, name) au_do_ftest_si(sbinfo, AuSi_##name)
#define au_fset_si(sbinfo, name) do { \
AuRwMustWriteLock(&(sbinfo)->si_rwsem); \
(sbinfo)->au_si_status |= AuSi_##name; \
} while (0)
#define au_fclr_si(sbinfo, name) do { \
AuRwMustWriteLock(&(sbinfo)->si_rwsem); \
(sbinfo)->au_si_status &= ~AuSi_##name; \
} while (0)
/* ---------------------------------------------------------------------- */
/* policy to select one among writable branches */
#define AuWbrCopyup(sbinfo, ...) \
((sbinfo)->si_wbr_copyup_ops->copyup(__VA_ARGS__))
#define AuWbrCreate(sbinfo, ...) \
((sbinfo)->si_wbr_create_ops->create(__VA_ARGS__))
/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */
#define AuLock_DW 1 /* write-lock dentry */
#define AuLock_IR (1 << 1) /* read-lock inode */
#define AuLock_IW (1 << 2) /* write-lock inode */
#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */
#define AuLock_DIR (1 << 4) /* target is a dir */
#define AuLock_NOPLM (1 << 5) /* return err in plm mode */
#define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */
#define AuLock_GEN (1 << 7) /* test digen/iigen */
#define au_ftest_lock(flags, name) ((flags) & AuLock_##name)
#define au_fset_lock(flags, name) \
do { (flags) |= AuLock_##name; } while (0)
#define au_fclr_lock(flags, name) \
do { (flags) &= ~AuLock_##name; } while (0)
/* ---------------------------------------------------------------------- */
/* super.c */
extern struct file_system_type aufs_fs_type;
struct inode *au_iget_locked(struct super_block *sb, ino_t ino);
typedef unsigned long long (*au_arraycb_t)(void *array, unsigned long long max,
void *arg);
void au_array_free(void *array);
void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg);
struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max);
void au_iarray_free(struct inode **a, unsigned long long max);
/* sbinfo.c */
void au_si_free(struct kobject *kobj);
int au_si_alloc(struct super_block *sb);
int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr);
unsigned int au_sigen_inc(struct super_block *sb);
aufs_bindex_t au_new_br_id(struct super_block *sb);
int si_read_lock(struct super_block *sb, int flags);
int si_write_lock(struct super_block *sb, int flags);
int aufs_read_lock(struct dentry *dentry, int flags);
void aufs_read_unlock(struct dentry *dentry, int flags);
void aufs_write_lock(struct dentry *dentry);
void aufs_write_unlock(struct dentry *dentry);
int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags);
void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2);
int si_pid_test_slow(struct super_block *sb);
void si_pid_set_slow(struct super_block *sb);
void si_pid_clr_slow(struct super_block *sb);
/* wbr_policy.c */
extern struct au_wbr_copyup_operations au_wbr_copyup_ops[];
extern struct au_wbr_create_operations au_wbr_create_ops[];
int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst);
/* ---------------------------------------------------------------------- */
static inline struct au_sbinfo *au_sbi(struct super_block *sb)
{
return sb->s_fs_info;
}
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_AUFS_EXPORT
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);
void au_xigen_clr(struct super_block *sb);
static inline int au_busy_or_stale(void)
{
if (!au_test_nfsd())
return -EBUSY;
return -ESTALE;
}
#else
AuStubVoid(au_export_init, struct super_block *sb)
AuStubInt0(au_test_nfsd, void)
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)
AuStubVoid(au_xigen_clr, struct super_block *sb)
static inline int au_busy_or_stale(void)
{
return -EBUSY;
}
#endif /* CONFIG_AUFS_EXPORT */
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_AUFS_SBILIST
/* module.c */
extern struct au_splhead au_sbilist;
static inline void au_sbilist_init(void)
{
au_spl_init(&au_sbilist);
}
static inline void au_sbilist_add(struct super_block *sb)
{
au_spl_add(&au_sbi(sb)->si_list, &au_sbilist);
}
static inline void au_sbilist_del(struct super_block *sb)
{
au_spl_del(&au_sbi(sb)->si_list, &au_sbilist);
}
#ifdef CONFIG_AUFS_MAGIC_SYSRQ
static inline void au_sbilist_lock(void)
{
spin_lock(&au_sbilist.spin);
}
static inline void au_sbilist_unlock(void)
{
spin_unlock(&au_sbilist.spin);
}
#define AuGFP_SBILIST GFP_ATOMIC
#else
AuStubVoid(au_sbilist_lock, void)
AuStubVoid(au_sbilist_unlock, void)
#define AuGFP_SBILIST GFP_NOFS
#endif /* CONFIG_AUFS_MAGIC_SYSRQ */
#else
AuStubVoid(au_sbilist_init, void)
AuStubVoid(au_sbilist_add, struct super_block*)
AuStubVoid(au_sbilist_del, struct super_block*)
AuStubVoid(au_sbilist_lock, void)
AuStubVoid(au_sbilist_unlock, void)
#define AuGFP_SBILIST GFP_NOFS
#endif
/* ---------------------------------------------------------------------- */
static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo)
{
/*
* This function is a dynamic '__init' fucntion actually,
* so the tiny check for si_rwsem is unnecessary.
*/
/* AuRwMustWriteLock(&sbinfo->si_rwsem); */
#ifdef CONFIG_DEBUG_FS
sbinfo->si_dbgaufs = NULL;
sbinfo->si_dbgaufs_xib = NULL;
#ifdef CONFIG_AUFS_EXPORT
sbinfo->si_dbgaufs_xigen = NULL;
#endif
#endif
}
/* ---------------------------------------------------------------------- */
static inline pid_t si_pid_bit(void)
{
/* the origin of pid is 1, but the bitmap's is 0 */
return current->pid - 1;
}
static inline int si_pid_test(struct super_block *sb)
{
pid_t bit = si_pid_bit();
if (bit < PID_MAX_DEFAULT)
return test_bit(bit, au_sbi(sb)->au_si_pid.bitmap);
else
return si_pid_test_slow(sb);
}
static inline void si_pid_set(struct super_block *sb)
{
pid_t bit = si_pid_bit();
if (bit < PID_MAX_DEFAULT) {
AuDebugOn(test_bit(bit, au_sbi(sb)->au_si_pid.bitmap));
set_bit(bit, au_sbi(sb)->au_si_pid.bitmap);
/* smp_mb(); */
} else
si_pid_set_slow(sb);
}
static inline void si_pid_clr(struct super_block *sb)
{
pid_t bit = si_pid_bit();
if (bit < PID_MAX_DEFAULT) {
AuDebugOn(!test_bit(bit, au_sbi(sb)->au_si_pid.bitmap));
clear_bit(bit, au_sbi(sb)->au_si_pid.bitmap);
/* smp_mb(); */
} else
si_pid_clr_slow(sb);
}
/* ---------------------------------------------------------------------- */
/* lock superblock. mainly for entry point functions */
/*
* __si_read_lock, __si_write_lock,
* __si_read_unlock, __si_write_unlock, __si_downgrade_lock
*/
AuSimpleRwsemFuncs(__si, struct super_block *sb, &au_sbi(sb)->si_rwsem);
#define SiMustNoWaiters(sb) AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem)
#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem)
#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem)
static inline void si_noflush_read_lock(struct super_block *sb)
{
__si_read_lock(sb);
si_pid_set(sb);
}
static inline int si_noflush_read_trylock(struct super_block *sb)
{
int locked = __si_read_trylock(sb);
if (locked)
si_pid_set(sb);
return locked;
}
static inline void si_noflush_write_lock(struct super_block *sb)
{
__si_write_lock(sb);
si_pid_set(sb);
}
static inline int si_noflush_write_trylock(struct super_block *sb)
{
int locked = __si_write_trylock(sb);
if (locked)
si_pid_set(sb);
return locked;
}
#if 0 /* unused */
static inline int si_read_trylock(struct super_block *sb, int flags)
{
if (au_ftest_lock(flags, FLUSH))
au_nwt_flush(&au_sbi(sb)->si_nowait);
return si_noflush_read_trylock(sb);
}
#endif
static inline void si_read_unlock(struct super_block *sb)
{
si_pid_clr(sb);
__si_read_unlock(sb);
}
#if 0 /* unused */
static inline int si_write_trylock(struct super_block *sb, int flags)
{
if (au_ftest_lock(flags, FLUSH))
au_nwt_flush(&au_sbi(sb)->si_nowait);
return si_noflush_write_trylock(sb);
}
#endif
static inline void si_write_unlock(struct super_block *sb)
{
si_pid_clr(sb);
__si_write_unlock(sb);
}
#if 0 /* unused */
static inline void si_downgrade_lock(struct super_block *sb)
{
__si_downgrade_lock(sb);
}
#endif
/* ---------------------------------------------------------------------- */
static inline aufs_bindex_t au_sbend(struct super_block *sb)
{
SiMustAnyLock(sb);
return au_sbi(sb)->si_bend;
}
static inline unsigned int au_mntflags(struct super_block *sb)
{
SiMustAnyLock(sb);
return au_sbi(sb)->si_mntflags;
}
static inline unsigned int au_sigen(struct super_block *sb)
{
SiMustAnyLock(sb);
return au_sbi(sb)->si_generation;
}
static inline void au_ninodes_inc(struct super_block *sb)
{
atomic_long_inc(&au_sbi(sb)->si_ninodes);
}
static inline void au_ninodes_dec(struct super_block *sb)
{
AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_ninodes));
atomic_long_dec(&au_sbi(sb)->si_ninodes);
}
static inline void au_nfiles_inc(struct super_block *sb)
{
atomic_long_inc(&au_sbi(sb)->si_nfiles);
}
static inline void au_nfiles_dec(struct super_block *sb)
{
AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_nfiles));
atomic_long_dec(&au_sbi(sb)->si_nfiles);
}
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];
}
static inline void au_xino_brid_set(struct super_block *sb, aufs_bindex_t brid)
{
SiMustWriteLock(sb);
au_sbi(sb)->si_xino_brid = brid;
}
static inline aufs_bindex_t au_xino_brid(struct super_block *sb)
{
SiMustAnyLock(sb);
return au_sbi(sb)->si_xino_brid;
}
#endif /* __KERNEL__ */
#endif /* __AUFS_SUPER_H__ */

View File

@ -0,0 +1,105 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* sysfs interface and lifetime management
* they are necessary regardless sysfs is disabled.
*/
#include <linux/random.h>
#include "aufs.h"
unsigned long sysaufs_si_mask;
struct kset *sysaufs_kset;
#define AuSiAttr(_name) { \
.attr = { .name = __stringify(_name), .mode = 0444 }, \
.show = sysaufs_si_##_name, \
}
static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path);
struct attribute *sysaufs_si_attrs[] = {
&sysaufs_si_attr_xi_path.attr,
NULL,
};
static const struct sysfs_ops au_sbi_ops = {
.show = sysaufs_si_show
};
static struct kobj_type au_sbi_ktype = {
.release = au_si_free,
.sysfs_ops = &au_sbi_ops,
.default_attrs = sysaufs_si_attrs
};
/* ---------------------------------------------------------------------- */
int sysaufs_si_init(struct au_sbinfo *sbinfo)
{
int err;
sbinfo->si_kobj.kset = sysaufs_kset;
/* cf. sysaufs_name() */
err = kobject_init_and_add
(&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL,
SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo));
dbgaufs_si_null(sbinfo);
if (!err) {
err = dbgaufs_si_init(sbinfo);
if (unlikely(err))
kobject_put(&sbinfo->si_kobj);
}
return err;
}
void sysaufs_fin(void)
{
dbgaufs_fin();
sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group);
kset_unregister(sysaufs_kset);
}
int __init sysaufs_init(void)
{
int err;
do {
get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask));
} while (!sysaufs_si_mask);
err = -EINVAL;
sysaufs_kset = kset_create_and_add(AUFS_NAME, NULL, fs_kobj);
if (unlikely(!sysaufs_kset))
goto out;
err = PTR_ERR(sysaufs_kset);
if (IS_ERR(sysaufs_kset))
goto out;
err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group);
if (unlikely(err)) {
kset_unregister(sysaufs_kset);
goto out;
}
err = dbgaufs_init();
if (unlikely(err))
sysaufs_fin();
out:
return err;
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* sysfs interface and mount lifetime management
*/
#ifndef __SYSAUFS_H__
#define __SYSAUFS_H__
#ifdef __KERNEL__
#include <linux/sysfs.h>
#include "module.h"
struct super_block;
struct au_sbinfo;
struct sysaufs_si_attr {
struct attribute attr;
int (*show)(struct seq_file *seq, struct super_block *sb);
};
/* ---------------------------------------------------------------------- */
/* sysaufs.c */
extern unsigned long sysaufs_si_mask;
extern struct kset *sysaufs_kset;
extern struct attribute *sysaufs_si_attrs[];
int sysaufs_si_init(struct au_sbinfo *sbinfo);
int __init sysaufs_init(void);
void sysaufs_fin(void);
/* ---------------------------------------------------------------------- */
/* some people doesn't like to show a pointer in kernel */
static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo)
{
return sysaufs_si_mask ^ (unsigned long)sbinfo;
}
#define SysaufsSiNamePrefix "si_"
#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16)
static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name)
{
snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx",
sysaufs_si_id(sbinfo));
}
struct au_branch;
#ifdef CONFIG_SYSFS
/* sysfs.c */
extern struct attribute_group *sysaufs_attr_group;
int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb);
ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,
char *buf);
void sysaufs_br_init(struct au_branch *br);
void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex);
void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex);
#define sysaufs_brs_init() do {} while (0)
#else
#define sysaufs_attr_group NULL
AuStubInt0(sysaufs_si_xi_path, struct seq_file *seq, struct super_block *sb)
static inline
ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
return 0;
}
AuStubVoid(sysaufs_br_init, struct au_branch *br)
AuStubVoid(sysaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex)
AuStubVoid(sysaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex)
static inline void sysaufs_brs_init(void)
{
sysaufs_brs = 0;
}
#endif /* CONFIG_SYSFS */
#endif /* __KERNEL__ */
#endif /* __SYSAUFS_H__ */

View File

@ -0,0 +1,257 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* sysfs interface
*/
#include <linux/seq_file.h>
#include "aufs.h"
#ifdef CONFIG_AUFS_FS_MODULE
/* this entry violates the "one line per file" policy of sysfs */
static ssize_t config_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
ssize_t err;
static char *conf =
/* this file is generated at compiling */
#include "conf.str"
;
err = snprintf(buf, PAGE_SIZE, conf);
if (unlikely(err >= PAGE_SIZE))
err = -EFBIG;
return err;
}
static struct kobj_attribute au_config_attr = __ATTR_RO(config);
#endif
static struct attribute *au_attr[] = {
#ifdef CONFIG_AUFS_FS_MODULE
&au_config_attr.attr,
#endif
NULL, /* need to NULL terminate the list of attributes */
};
static struct attribute_group sysaufs_attr_group_body = {
.attrs = au_attr
};
struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body;
/* ---------------------------------------------------------------------- */
int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb)
{
int err;
SiMustAnyLock(sb);
err = 0;
if (au_opt_test(au_mntflags(sb), XINO)) {
err = au_xino_path(seq, au_sbi(sb)->si_xib);
seq_putc(seq, '\n');
}
return err;
}
/*
* the lifetime of branch is independent from the entry under sysfs.
* sysfs handles the lifetime of the entry, and never call ->show() after it is
* unlinked.
*/
static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb,
aufs_bindex_t bindex)
{
int err;
struct path path;
struct dentry *root;
struct au_branch *br;
char *perm;
AuDbg("b%d\n", bindex);
err = 0;
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);
if (err == -1)
err = -E2BIG;
} else
err = -ENOMEM;
return err;
}
/* ---------------------------------------------------------------------- */
static struct seq_file *au_seq(char *p, ssize_t len)
{
struct seq_file *seq;
seq = kzalloc(sizeof(*seq), GFP_NOFS);
if (seq) {
/* mutex_init(&seq.lock); */
seq->buf = p;
seq->size = len;
return seq; /* success */
}
seq = ERR_PTR(-ENOMEM);
return seq;
}
#define SysaufsBr_PREFIX "br"
/* todo: file size may exceed PAGE_SIZE */
ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
ssize_t err;
long l;
aufs_bindex_t bend;
struct au_sbinfo *sbinfo;
struct super_block *sb;
struct seq_file *seq;
char *name;
struct attribute **cattr;
sbinfo = container_of(kobj, struct au_sbinfo, si_kobj);
sb = sbinfo->si_sb;
/*
* prevent a race condition between sysfs and aufs.
* for instance, sysfs_file_read() calls sysfs_get_active_two() which
* prohibits maintaining the sysfs entries.
* hew we acquire read lock after sysfs_get_active_two().
* on the other hand, the remount process may maintain the sysfs/aufs
* entries after acquiring write lock.
* it can cause a deadlock.
* simply we gave up processing read here.
*/
err = -EBUSY;
if (unlikely(!si_noflush_read_trylock(sb)))
goto out;
seq = au_seq(buf, PAGE_SIZE);
err = PTR_ERR(seq);
if (IS_ERR(seq))
goto out_unlock;
name = (void *)attr->name;
cattr = sysaufs_si_attrs;
while (*cattr) {
if (!strcmp(name, (*cattr)->name)) {
err = container_of(*cattr, struct sysaufs_si_attr, attr)
->show(seq, sb);
goto out_seq;
}
cattr++;
}
bend = au_sbend(sb);
if (!strncmp(name, SysaufsBr_PREFIX, sizeof(SysaufsBr_PREFIX) - 1)) {
name += sizeof(SysaufsBr_PREFIX) - 1;
err = strict_strtol(name, 10, &l);
if (!err) {
if (l <= bend)
err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l);
else
err = -ENOENT;
}
goto out_seq;
}
BUG();
out_seq:
if (!err) {
err = seq->count;
/* sysfs limit */
if (unlikely(err == PAGE_SIZE))
err = -EFBIG;
}
kfree(seq);
out_unlock:
si_read_unlock(sb);
out:
return err;
}
/* ---------------------------------------------------------------------- */
void sysaufs_br_init(struct au_branch *br)
{
struct attribute *attr = &br->br_attr;
sysfs_attr_init(attr);
attr->name = br->br_name;
attr->mode = S_IRUGO;
}
void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
{
struct au_branch *br;
struct kobject *kobj;
aufs_bindex_t bend;
dbgaufs_brs_del(sb, bindex);
if (!sysaufs_brs)
return;
kobj = &au_sbi(sb)->si_kobj;
bend = au_sbend(sb);
for (; bindex <= bend; bindex++) {
br = au_sbr(sb, bindex);
sysfs_remove_file(kobj, &br->br_attr);
}
}
void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)
{
int err;
aufs_bindex_t bend;
struct kobject *kobj;
struct au_branch *br;
dbgaufs_brs_add(sb, bindex);
if (!sysaufs_brs)
return;
kobj = &au_sbi(sb)->si_kobj;
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);
}
}

View File

@ -0,0 +1,148 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* magic sysrq hanlder
*/
/* #include <linux/sysrq.h> */
#include <linux/writeback.h>
#include "aufs.h"
/* ---------------------------------------------------------------------- */
static void sysrq_sb(struct super_block *sb)
{
char *plevel;
struct au_sbinfo *sbinfo;
struct file *file;
plevel = au_plevel;
au_plevel = KERN_WARNING;
sbinfo = au_sbi(sb);
/* since we define pr_fmt, call printk directly */
printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo));
printk(KERN_WARNING AUFS_NAME ": superblock\n");
au_dpri_sb(sb);
#if 0
printk(KERN_WARNING AUFS_NAME ": root dentry\n");
au_dpri_dentry(sb->s_root);
printk(KERN_WARNING AUFS_NAME ": root inode\n");
au_dpri_inode(sb->s_root->d_inode);
#endif
#if 0
do {
int err, i, j, ndentry;
struct au_dcsub_pages dpages;
struct au_dpage *dpage;
err = au_dpages_init(&dpages, GFP_ATOMIC);
if (unlikely(err))
break;
err = au_dcsub_pages(&dpages, sb->s_root, NULL, NULL);
if (!err)
for (i = 0; i < dpages.ndpage; i++) {
dpage = dpages.dpages + i;
ndentry = dpage->ndentry;
for (j = 0; j < ndentry; j++)
au_dpri_dentry(dpage->dentries[j]);
}
au_dpages_free(&dpages);
} while (0);
#endif
#if 1
{
struct inode *i;
printk(KERN_WARNING AUFS_NAME ": 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);
if (1 || list_empty(&i->i_dentry))
au_dpri_inode(i);
spin_unlock(&i->i_lock);
}
spin_unlock(&inode_sb_list_lock);
}
#endif
printk(KERN_WARNING AUFS_NAME ": files\n");
lg_global_lock(files_lglock);
do_file_list_for_each_entry(sb, file) {
umode_t mode;
mode = file->f_dentry->d_inode->i_mode;
if (!special_file(mode) || au_special_file(mode))
au_dpri_file(file);
} while_file_list_for_each_entry;
lg_global_unlock(files_lglock);
printk(KERN_WARNING AUFS_NAME ": done\n");
au_plevel = plevel;
}
/* ---------------------------------------------------------------------- */
/* module parameter */
static char *aufs_sysrq_key = "a";
module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO);
MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME);
static void au_sysrq(int key __maybe_unused)
{
struct au_sbinfo *sbinfo;
lockdep_off();
au_sbilist_lock();
list_for_each_entry(sbinfo, &au_sbilist.head, si_list)
sysrq_sb(sbinfo->si_sb);
au_sbilist_unlock();
lockdep_on();
}
static struct sysrq_key_op au_sysrq_op = {
.handler = au_sysrq,
.help_msg = "Aufs",
.action_msg = "Aufs",
.enable_mask = SYSRQ_ENABLE_DUMP
};
/* ---------------------------------------------------------------------- */
int __init au_sysrq_init(void)
{
int err;
char key;
err = -1;
key = *aufs_sysrq_key;
if ('a' <= key && key <= 'z')
err = register_sysrq_key(key, &au_sysrq_op);
if (unlikely(err))
pr_err("err %d, sysrq=%c\n", err, key);
return err;
}
void au_sysrq_fin(void)
{
int err;
err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op);
if (unlikely(err))
pr_err("err %d (ignored)\n", err);
}

View File

@ -0,0 +1,885 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* virtual or vertical directory
*/
#include "aufs.h"
static unsigned int calc_size(int nlen)
{
return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t));
}
static int set_deblk_end(union au_vdir_deblk_p *p,
union au_vdir_deblk_p *deblk_end)
{
if (calc_size(0) <= deblk_end->deblk - p->deblk) {
p->de->de_str.len = 0;
/* smp_mb(); */
return 0;
}
return -1; /* error */
}
/* returns true or false */
static int is_deblk_end(union au_vdir_deblk_p *p,
union au_vdir_deblk_p *deblk_end)
{
if (calc_size(0) <= deblk_end->deblk - p->deblk)
return !p->de->de_str.len;
return 1;
}
static unsigned char *last_deblk(struct au_vdir *vdir)
{
return vdir->vd_deblk[vdir->vd_nblk - 1];
}
/* ---------------------------------------------------------------------- */
/* estimate the apropriate size for name hash table */
unsigned int au_rdhash_est(loff_t sz)
{
unsigned int n;
n = UINT_MAX;
sz >>= 10;
if (sz < n)
n = sz;
if (sz < AUFS_RDHASH_DEF)
n = AUFS_RDHASH_DEF;
/* pr_info("n %u\n", n); */
return n;
}
/*
* the allocated memory has to be freed by
* au_nhash_wh_free() or au_nhash_de_free().
*/
int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp)
{
struct hlist_head *head;
unsigned int u;
head = kmalloc(sizeof(*nhash->nh_head) * num_hash, gfp);
if (head) {
nhash->nh_num = num_hash;
nhash->nh_head = head;
for (u = 0; u < num_hash; u++)
INIT_HLIST_HEAD(head++);
return 0; /* success */
}
return -ENOMEM;
}
static void nhash_count(struct hlist_head *head)
{
#if 0
unsigned long n;
struct hlist_node *pos;
n = 0;
hlist_for_each(pos, head)
n++;
pr_info("%lu\n", n);
#endif
}
static void au_nhash_wh_do_free(struct hlist_head *head)
{
struct au_vdir_wh *tpos;
struct hlist_node *pos, *node;
hlist_for_each_entry_safe(tpos, pos, node, head, wh_hash) {
/* hlist_del(pos); */
kfree(tpos);
}
}
static void au_nhash_de_do_free(struct hlist_head *head)
{
struct au_vdir_dehstr *tpos;
struct hlist_node *pos, *node;
hlist_for_each_entry_safe(tpos, pos, node, head, hash) {
/* hlist_del(pos); */
au_cache_free_vdir_dehstr(tpos);
}
}
static void au_nhash_do_free(struct au_nhash *nhash,
void (*free)(struct hlist_head *head))
{
unsigned int n;
struct hlist_head *head;
n = nhash->nh_num;
if (!n)
return;
head = nhash->nh_head;
while (n-- > 0) {
nhash_count(head);
free(head++);
}
kfree(nhash->nh_head);
}
void au_nhash_wh_free(struct au_nhash *whlist)
{
au_nhash_do_free(whlist, au_nhash_wh_do_free);
}
static void au_nhash_de_free(struct au_nhash *delist)
{
au_nhash_do_free(delist, au_nhash_de_do_free);
}
/* ---------------------------------------------------------------------- */
int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt,
int limit)
{
int num;
unsigned int u, n;
struct hlist_head *head;
struct au_vdir_wh *tpos;
struct hlist_node *pos;
num = 0;
n = whlist->nh_num;
head = whlist->nh_head;
for (u = 0; u < n; u++, head++)
hlist_for_each_entry(tpos, pos, head, wh_hash)
if (tpos->wh_bindex == btgt && ++num > limit)
return 1;
return 0;
}
static struct hlist_head *au_name_hash(struct au_nhash *nhash,
unsigned char *name,
unsigned int len)
{
unsigned int v;
/* const unsigned int magic_bit = 12; */
AuDebugOn(!nhash->nh_num || !nhash->nh_head);
v = 0;
while (len--)
v += *name++;
/* v = hash_long(v, magic_bit); */
v %= nhash->nh_num;
return nhash->nh_head + v;
}
static int au_nhash_test_name(struct au_vdir_destr *str, const char *name,
int nlen)
{
return str->len == nlen && !memcmp(str->name, name, nlen);
}
/* returns found or not */
int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen)
{
struct hlist_head *head;
struct au_vdir_wh *tpos;
struct hlist_node *pos;
struct au_vdir_destr *str;
head = au_name_hash(whlist, name, nlen);
hlist_for_each_entry(tpos, pos, head, wh_hash) {
str = &tpos->wh_str;
AuDbg("%.*s\n", str->len, str->name);
if (au_nhash_test_name(str, name, nlen))
return 1;
}
return 0;
}
/* returns found(true) or not */
static int test_known(struct au_nhash *delist, char *name, int nlen)
{
struct hlist_head *head;
struct au_vdir_dehstr *tpos;
struct hlist_node *pos;
struct au_vdir_destr *str;
head = au_name_hash(delist, name, nlen);
hlist_for_each_entry(tpos, pos, head, hash) {
str = tpos->str;
AuDbg("%.*s\n", str->len, str->name);
if (au_nhash_test_name(str, name, nlen))
return 1;
}
return 0;
}
static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino,
unsigned char d_type)
{
#ifdef CONFIG_AUFS_SHWH
wh->wh_ino = ino;
wh->wh_type = d_type;
#endif
}
/* ---------------------------------------------------------------------- */
int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino,
unsigned int d_type, aufs_bindex_t bindex,
unsigned char shwh)
{
int err;
struct au_vdir_destr *str;
struct au_vdir_wh *wh;
AuDbg("%.*s\n", nlen, name);
AuDebugOn(!whlist->nh_num || !whlist->nh_head);
err = -ENOMEM;
wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS);
if (unlikely(!wh))
goto out;
err = 0;
wh->wh_bindex = bindex;
if (shwh)
au_shwh_init_wh(wh, ino, d_type);
str = &wh->wh_str;
str->len = nlen;
memcpy(str->name, name, nlen);
hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen));
/* smp_mb(); */
out:
return err;
}
static int append_deblk(struct au_vdir *vdir)
{
int err;
unsigned long ul;
const unsigned int deblk_sz = vdir->vd_deblk_sz;
union au_vdir_deblk_p p, deblk_end;
unsigned char **o;
err = -ENOMEM;
o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1),
GFP_NOFS);
if (unlikely(!o))
goto out;
vdir->vd_deblk = o;
p.deblk = kmalloc(deblk_sz, GFP_NOFS);
if (p.deblk) {
ul = vdir->vd_nblk++;
vdir->vd_deblk[ul] = p.deblk;
vdir->vd_last.ul = ul;
vdir->vd_last.p.deblk = p.deblk;
deblk_end.deblk = p.deblk + deblk_sz;
err = set_deblk_end(&p, &deblk_end);
}
out:
return err;
}
static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino,
unsigned int d_type, struct au_nhash *delist)
{
int err;
unsigned int sz;
const unsigned int deblk_sz = vdir->vd_deblk_sz;
union au_vdir_deblk_p p, *room, deblk_end;
struct au_vdir_dehstr *dehstr;
p.deblk = last_deblk(vdir);
deblk_end.deblk = p.deblk + deblk_sz;
room = &vdir->vd_last.p;
AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk
|| !is_deblk_end(room, &deblk_end));
sz = calc_size(nlen);
if (unlikely(sz > deblk_end.deblk - room->deblk)) {
err = append_deblk(vdir);
if (unlikely(err))
goto out;
p.deblk = last_deblk(vdir);
deblk_end.deblk = p.deblk + deblk_sz;
/* smp_mb(); */
AuDebugOn(room->deblk != p.deblk);
}
err = -ENOMEM;
dehstr = au_cache_alloc_vdir_dehstr();
if (unlikely(!dehstr))
goto out;
dehstr->str = &room->de->de_str;
hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen));
room->de->de_ino = ino;
room->de->de_type = d_type;
room->de->de_str.len = nlen;
memcpy(room->de->de_str.name, name, nlen);
err = 0;
room->deblk += sz;
if (unlikely(set_deblk_end(room, &deblk_end)))
err = append_deblk(vdir);
/* smp_mb(); */
out:
return err;
}
/* ---------------------------------------------------------------------- */
void au_vdir_free(struct au_vdir *vdir)
{
unsigned char **deblk;
deblk = vdir->vd_deblk;
while (vdir->vd_nblk--)
kfree(*deblk++);
kfree(vdir->vd_deblk);
au_cache_free_vdir(vdir);
}
static struct au_vdir *alloc_vdir(struct file *file)
{
struct au_vdir *vdir;
struct super_block *sb;
int err;
sb = file->f_dentry->d_sb;
SiMustAnyLock(sb);
err = -ENOMEM;
vdir = au_cache_alloc_vdir();
if (unlikely(!vdir))
goto out;
vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS);
if (unlikely(!vdir->vd_deblk))
goto out_free;
vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk;
if (!vdir->vd_deblk_sz) {
/* estimate the apropriate size for deblk */
vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL);
/* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */
}
vdir->vd_nblk = 0;
vdir->vd_version = 0;
vdir->vd_jiffy = 0;
err = append_deblk(vdir);
if (!err)
return vdir; /* success */
kfree(vdir->vd_deblk);
out_free:
au_cache_free_vdir(vdir);
out:
vdir = ERR_PTR(err);
return vdir;
}
static int reinit_vdir(struct au_vdir *vdir)
{
int err;
union au_vdir_deblk_p p, deblk_end;
while (vdir->vd_nblk > 1) {
kfree(vdir->vd_deblk[vdir->vd_nblk - 1]);
/* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */
vdir->vd_nblk--;
}
p.deblk = vdir->vd_deblk[0];
deblk_end.deblk = p.deblk + vdir->vd_deblk_sz;
err = set_deblk_end(&p, &deblk_end);
/* keep vd_dblk_sz */
vdir->vd_last.ul = 0;
vdir->vd_last.p.deblk = vdir->vd_deblk[0];
vdir->vd_version = 0;
vdir->vd_jiffy = 0;
/* smp_mb(); */
return err;
}
/* ---------------------------------------------------------------------- */
#define AuFillVdir_CALLED 1
#define AuFillVdir_WHABLE (1 << 1)
#define AuFillVdir_SHWH (1 << 2)
#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name)
#define au_fset_fillvdir(flags, name) \
do { (flags) |= AuFillVdir_##name; } while (0)
#define au_fclr_fillvdir(flags, name) \
do { (flags) &= ~AuFillVdir_##name; } while (0)
#ifndef CONFIG_AUFS_SHWH
#undef AuFillVdir_SHWH
#define AuFillVdir_SHWH 0
#endif
struct fillvdir_arg {
struct file *file;
struct au_vdir *vdir;
struct au_nhash delist;
struct au_nhash whlist;
aufs_bindex_t bindex;
unsigned int flags;
int err;
};
static int fillvdir(void *__arg, const char *__name, int nlen,
loff_t offset __maybe_unused, u64 h_ino,
unsigned int d_type)
{
struct fillvdir_arg *arg = __arg;
char *name = (void *)__name;
struct super_block *sb;
ino_t ino;
const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH);
arg->err = 0;
sb = arg->file->f_dentry->d_sb;
au_fset_fillvdir(arg->flags, CALLED);
/* smp_mb(); */
if (nlen <= AUFS_WH_PFX_LEN
|| memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
if (test_known(&arg->delist, name, nlen)
|| au_nhash_test_known_wh(&arg->whlist, name, nlen))
goto out; /* already exists or whiteouted */
sb = arg->file->f_dentry->d_sb;
arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino);
if (!arg->err) {
if (unlikely(nlen > AUFS_MAX_NAMELEN))
d_type = DT_UNKNOWN;
arg->err = append_de(arg->vdir, name, nlen, ino,
d_type, &arg->delist);
}
} else if (au_ftest_fillvdir(arg->flags, WHABLE)) {
name += AUFS_WH_PFX_LEN;
nlen -= AUFS_WH_PFX_LEN;
if (au_nhash_test_known_wh(&arg->whlist, name, nlen))
goto out; /* already whiteouted */
if (shwh)
arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type,
&ino);
if (!arg->err) {
if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN)
d_type = DT_UNKNOWN;
arg->err = au_nhash_append_wh
(&arg->whlist, name, nlen, ino, d_type,
arg->bindex, shwh);
}
}
out:
if (!arg->err)
arg->vdir->vd_jiffy = jiffies;
/* smp_mb(); */
AuTraceErr(arg->err);
return arg->err;
}
static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir,
struct au_nhash *whlist, struct au_nhash *delist)
{
#ifdef CONFIG_AUFS_SHWH
int err;
unsigned int nh, u;
struct hlist_head *head;
struct au_vdir_wh *tpos;
struct hlist_node *pos, *n;
char *p, *o;
struct au_vdir_destr *destr;
AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH));
err = -ENOMEM;
o = p = __getname_gfp(GFP_NOFS);
if (unlikely(!p))
goto out;
err = 0;
nh = whlist->nh_num;
memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);
p += AUFS_WH_PFX_LEN;
for (u = 0; u < nh; u++) {
head = whlist->nh_head + u;
hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) {
destr = &tpos->wh_str;
memcpy(p, destr->name, destr->len);
err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN,
tpos->wh_ino, tpos->wh_type, delist);
if (unlikely(err))
break;
}
}
__putname(o);
out:
AuTraceErr(err);
return err;
#else
return 0;
#endif
}
static int au_do_read_vdir(struct fillvdir_arg *arg)
{
int err;
unsigned int rdhash;
loff_t offset;
aufs_bindex_t bend, bindex, bstart;
unsigned char shwh;
struct file *hf, *file;
struct super_block *sb;
file = arg->file;
sb = file->f_dentry->d_sb;
SiMustAnyLock(sb);
rdhash = au_sbi(sb)->si_rdhash;
if (!rdhash)
rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL));
err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS);
if (unlikely(err))
goto out;
err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS);
if (unlikely(err))
goto out_delist;
err = 0;
arg->flags = 0;
shwh = 0;
if (au_opt_test(au_mntflags(sb), SHWH)) {
shwh = 1;
au_fset_fillvdir(arg->flags, SHWH);
}
bstart = au_fbstart(file);
bend = au_fbend_dir(file);
for (bindex = bstart; !err && bindex <= bend; bindex++) {
hf = au_hf_dir(file, bindex);
if (!hf)
continue;
offset = vfsub_llseek(hf, 0, SEEK_SET);
err = offset;
if (unlikely(offset))
break;
arg->bindex = bindex;
au_fclr_fillvdir(arg->flags, WHABLE);
if (shwh
|| (bindex != bend
&& au_br_whable(au_sbr_perm(sb, bindex))))
au_fset_fillvdir(arg->flags, WHABLE);
do {
arg->err = 0;
au_fclr_fillvdir(arg->flags, CALLED);
/* smp_mb(); */
err = vfsub_readdir(hf, fillvdir, arg);
if (err >= 0)
err = arg->err;
} while (!err && au_ftest_fillvdir(arg->flags, CALLED));
}
if (!err && shwh)
err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist);
au_nhash_wh_free(&arg->whlist);
out_delist:
au_nhash_de_free(&arg->delist);
out:
return err;
}
static int read_vdir(struct file *file, int may_read)
{
int err;
unsigned long expire;
unsigned char do_read;
struct fillvdir_arg arg;
struct inode *inode;
struct au_vdir *vdir, *allocated;
err = 0;
inode = file->f_dentry->d_inode;
IMustLock(inode);
SiMustAnyLock(inode->i_sb);
allocated = NULL;
do_read = 0;
expire = au_sbi(inode->i_sb)->si_rdcache;
vdir = au_ivdir(inode);
if (!vdir) {
do_read = 1;
vdir = alloc_vdir(file);
err = PTR_ERR(vdir);
if (IS_ERR(vdir))
goto out;
err = 0;
allocated = vdir;
} else if (may_read
&& (inode->i_version != vdir->vd_version
|| time_after(jiffies, vdir->vd_jiffy + expire))) {
do_read = 1;
err = reinit_vdir(vdir);
if (unlikely(err))
goto out;
}
if (!do_read)
return 0; /* success */
arg.file = file;
arg.vdir = vdir;
err = au_do_read_vdir(&arg);
if (!err) {
/* file->f_pos = 0; */
vdir->vd_version = inode->i_version;
vdir->vd_last.ul = 0;
vdir->vd_last.p.deblk = vdir->vd_deblk[0];
if (allocated)
au_set_ivdir(inode, allocated);
} else if (allocated)
au_vdir_free(allocated);
out:
return err;
}
static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src)
{
int err, rerr;
unsigned long ul, n;
const unsigned int deblk_sz = src->vd_deblk_sz;
AuDebugOn(tgt->vd_nblk != 1);
err = -ENOMEM;
if (tgt->vd_nblk < src->vd_nblk) {
unsigned char **p;
p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk,
GFP_NOFS);
if (unlikely(!p))
goto out;
tgt->vd_deblk = p;
}
if (tgt->vd_deblk_sz != deblk_sz) {
unsigned char *p;
tgt->vd_deblk_sz = deblk_sz;
p = krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS);
if (unlikely(!p))
goto out;
tgt->vd_deblk[0] = p;
}
memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz);
tgt->vd_version = src->vd_version;
tgt->vd_jiffy = src->vd_jiffy;
n = src->vd_nblk;
for (ul = 1; ul < n; ul++) {
tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz,
GFP_NOFS);
if (unlikely(!tgt->vd_deblk[ul]))
goto out;
tgt->vd_nblk++;
}
tgt->vd_nblk = n;
tgt->vd_last.ul = tgt->vd_last.ul;
tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul];
tgt->vd_last.p.deblk += src->vd_last.p.deblk
- src->vd_deblk[src->vd_last.ul];
/* smp_mb(); */
return 0; /* success */
out:
rerr = reinit_vdir(tgt);
BUG_ON(rerr);
return err;
}
int au_vdir_init(struct file *file)
{
int err;
struct inode *inode;
struct au_vdir *vdir_cache, *allocated;
err = read_vdir(file, !file->f_pos);
if (unlikely(err))
goto out;
allocated = NULL;
vdir_cache = au_fvdir_cache(file);
if (!vdir_cache) {
vdir_cache = alloc_vdir(file);
err = PTR_ERR(vdir_cache);
if (IS_ERR(vdir_cache))
goto out;
allocated = vdir_cache;
} else if (!file->f_pos && vdir_cache->vd_version != file->f_version) {
err = reinit_vdir(vdir_cache);
if (unlikely(err))
goto out;
} else
return 0; /* success */
inode = file->f_dentry->d_inode;
err = copy_vdir(vdir_cache, au_ivdir(inode));
if (!err) {
file->f_version = inode->i_version;
if (allocated)
au_set_fvdir_cache(file, allocated);
} else if (allocated)
au_vdir_free(allocated);
out:
return err;
}
static loff_t calc_offset(struct au_vdir *vdir)
{
loff_t offset;
union au_vdir_deblk_p p;
p.deblk = vdir->vd_deblk[vdir->vd_last.ul];
offset = vdir->vd_last.p.deblk - p.deblk;
offset += vdir->vd_deblk_sz * vdir->vd_last.ul;
return offset;
}
/* returns true or false */
static int seek_vdir(struct file *file)
{
int valid;
unsigned int deblk_sz;
unsigned long ul, n;
loff_t offset;
union au_vdir_deblk_p p, deblk_end;
struct au_vdir *vdir_cache;
valid = 1;
vdir_cache = au_fvdir_cache(file);
offset = calc_offset(vdir_cache);
AuDbg("offset %lld\n", offset);
if (file->f_pos == offset)
goto out;
vdir_cache->vd_last.ul = 0;
vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0];
if (!file->f_pos)
goto out;
valid = 0;
deblk_sz = vdir_cache->vd_deblk_sz;
ul = div64_u64(file->f_pos, deblk_sz);
AuDbg("ul %lu\n", ul);
if (ul >= vdir_cache->vd_nblk)
goto out;
n = vdir_cache->vd_nblk;
for (; ul < n; ul++) {
p.deblk = vdir_cache->vd_deblk[ul];
deblk_end.deblk = p.deblk + deblk_sz;
offset = ul;
offset *= deblk_sz;
while (!is_deblk_end(&p, &deblk_end) && offset < file->f_pos) {
unsigned int l;
l = calc_size(p.de->de_str.len);
offset += l;
p.deblk += l;
}
if (!is_deblk_end(&p, &deblk_end)) {
valid = 1;
vdir_cache->vd_last.ul = ul;
vdir_cache->vd_last.p = p;
break;
}
}
out:
/* smp_mb(); */
AuTraceErr(!valid);
return valid;
}
int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir)
{
int err;
unsigned int l, deblk_sz;
union au_vdir_deblk_p deblk_end;
struct au_vdir *vdir_cache;
struct au_vdir_de *de;
vdir_cache = au_fvdir_cache(file);
if (!seek_vdir(file))
return 0;
deblk_sz = vdir_cache->vd_deblk_sz;
while (1) {
deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul];
deblk_end.deblk += deblk_sz;
while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) {
de = vdir_cache->vd_last.p.de;
AuDbg("%.*s, off%lld, i%lu, dt%d\n",
de->de_str.len, de->de_str.name, file->f_pos,
(unsigned long)de->de_ino, de->de_type);
err = filldir(dirent, de->de_str.name, de->de_str.len,
file->f_pos, de->de_ino, de->de_type);
if (unlikely(err)) {
AuTraceErr(err);
/* todo: ignore the error caused by udba? */
/* return err; */
return 0;
}
l = calc_size(de->de_str.len);
vdir_cache->vd_last.p.deblk += l;
file->f_pos += l;
}
if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) {
vdir_cache->vd_last.ul++;
vdir_cache->vd_last.p.deblk
= vdir_cache->vd_deblk[vdir_cache->vd_last.ul];
file->f_pos = deblk_sz * vdir_cache->vd_last.ul;
continue;
}
break;
}
/* smp_mb(); */
return 0;
}

View File

@ -0,0 +1,835 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* sub-routines for VFS
*/
#include <linux/ima.h>
#include <linux/namei.h>
#include <linux/security.h>
#include <linux/splice.h>
#include "aufs.h"
int vfsub_update_h_iattr(struct path *h_path, int *did)
{
int err;
struct kstat st;
struct super_block *h_sb;
/* for remote fs, leave work for its getattr or d_revalidate */
/* for bad i_attr fs, handle them in aufs_getattr() */
/* still some fs may acquire i_mutex. we need to skip them */
err = 0;
if (!did)
did = &err;
h_sb = h_path->dentry->d_sb;
*did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb));
if (*did)
err = vfs_getattr(h_path->mnt, h_path->dentry, &st);
return err;
}
/* ---------------------------------------------------------------------- */
struct file *vfsub_dentry_open(struct path *path, int flags)
{
struct file *file;
path_get(path);
file = dentry_open(path->dentry, path->mnt,
flags /* | __FMODE_NONOTIFY */,
current_cred());
if (!IS_ERR_OR_NULL(file)
&& (file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
i_readcount_inc(path->dentry->d_inode);
return file;
}
struct file *vfsub_filp_open(const char *path, int oflags, int mode)
{
struct file *file;
lockdep_off();
file = filp_open(path,
oflags /* | __FMODE_NONOTIFY */,
mode);
lockdep_on();
if (IS_ERR(file))
goto out;
vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/
out:
return file;
}
int vfsub_kern_path(const char *name, unsigned int flags, struct path *path)
{
int err;
err = kern_path(name, flags, path);
if (!err && path->dentry->d_inode)
vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/
return err;
}
struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,
int len)
{
struct path path = {
.mnt = NULL
};
/* VFS checks it too, but by WARN_ON_ONCE() */
IMustLock(parent->d_inode);
path.dentry = lookup_one_len(name, parent, len);
if (IS_ERR(path.dentry))
goto out;
if (path.dentry->d_inode)
vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/
out:
AuTraceErrPtr(path.dentry);
return path.dentry;
}
struct dentry *vfsub_lookup_hash(struct nameidata *nd)
{
struct path path = {
.mnt = nd->path.mnt
};
IMustLock(nd->path.dentry->d_inode);
path.dentry = lookup_hash(nd);
if (IS_ERR(path.dentry))
goto out;
if (path.dentry->d_inode)
vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/
out:
AuTraceErrPtr(path.dentry);
return path.dentry;
}
/*
* this is "VFS:__lookup_one_len()" which was removed and merged into
* VFS:lookup_one_len() by the commit.
* 6a96ba5 2011-03-14 kill __lookup_one_len()
* this function should always be equivalent to the corresponding part in
* VFS:lookup_one_len().
*/
int vfsub_name_hash(const char *name, struct qstr *this, int len)
{
unsigned long hash;
unsigned int c;
this->name = name;
this->len = len;
if (!len)
return -EACCES;
hash = init_name_hash();
while (len--) {
c = *(const unsigned char *)name++;
if (c == '/' || c == '\0')
return -EACCES;
hash = partial_name_hash(c, hash);
}
this->hash = end_name_hash(hash);
return 0;
}
/* ---------------------------------------------------------------------- */
struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1,
struct dentry *d2, struct au_hinode *hdir2)
{
struct dentry *d;
lockdep_off();
d = lock_rename(d1, d2);
lockdep_on();
au_hn_suspend(hdir1);
if (hdir1 != hdir2)
au_hn_suspend(hdir2);
return d;
}
void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1,
struct dentry *d2, struct au_hinode *hdir2)
{
au_hn_resume(hdir1);
if (hdir1 != hdir2)
au_hn_resume(hdir2);
lockdep_off();
unlock_rename(d1, d2);
lockdep_on();
}
/* ---------------------------------------------------------------------- */
int vfsub_create(struct inode *dir, struct path *path, int mode)
{
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;
if (au_test_fs_null_nd(dir->i_sb))
err = vfs_create(dir, path->dentry, mode, NULL);
else {
struct nameidata h_nd;
memset(&h_nd, 0, sizeof(h_nd));
h_nd.flags = LOOKUP_CREATE;
h_nd.intent.open.flags = O_CREAT
| vfsub_fmode_to_uint(FMODE_READ);
h_nd.intent.open.create_mode = mode;
h_nd.path.dentry = path->dentry->d_parent;
h_nd.path.mnt = path->mnt;
path_get(&h_nd.path);
err = vfs_create(dir, path->dentry, mode, &h_nd);
path_put(&h_nd.path);
}
if (!err) {
struct path tmp = *path;
int did;
vfsub_update_h_iattr(&tmp, &did);
if (did) {
tmp.dentry = path->dentry->d_parent;
vfsub_update_h_iattr(&tmp, /*did*/NULL);
}
/*ignore*/
}
out:
return err;
}
int vfsub_symlink(struct inode *dir, struct path *path, const char *symname)
{
int err;
struct dentry *d;
IMustLock(dir);
d = path->dentry;
path->dentry = d->d_parent;
err = security_path_symlink(path, d, symname);
path->dentry = d;
if (unlikely(err))
goto out;
err = vfs_symlink(dir, path->dentry, symname);
if (!err) {
struct path tmp = *path;
int did;
vfsub_update_h_iattr(&tmp, &did);
if (did) {
tmp.dentry = path->dentry->d_parent;
vfsub_update_h_iattr(&tmp, /*did*/NULL);
}
/*ignore*/
}
out:
return err;
}
int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev)
{
int err;
struct dentry *d;
IMustLock(dir);
d = path->dentry;
path->dentry = d->d_parent;
err = security_path_mknod(path, d, mode, new_encode_dev(dev));
path->dentry = d;
if (unlikely(err))
goto out;
err = vfs_mknod(dir, path->dentry, mode, dev);
if (!err) {
struct path tmp = *path;
int did;
vfsub_update_h_iattr(&tmp, &did);
if (did) {
tmp.dentry = path->dentry->d_parent;
vfsub_update_h_iattr(&tmp, /*did*/NULL);
}
/*ignore*/
}
out:
return err;
}
static int au_test_nlink(struct inode *inode)
{
const unsigned int link_max = UINT_MAX >> 1; /* rough margin */
if (!au_test_fs_no_limit_nlink(inode->i_sb)
|| inode->i_nlink < link_max)
return 0;
return -EMLINK;
}
int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path)
{
int err;
struct dentry *d;
IMustLock(dir);
err = au_test_nlink(src_dentry->d_inode);
if (unlikely(err))
return err;
d = path->dentry;
path->dentry = d->d_parent;
err = security_path_link(src_dentry, path, d);
path->dentry = d;
if (unlikely(err))
goto out;
lockdep_off();
err = vfs_link(src_dentry, dir, path->dentry);
lockdep_on();
if (!err) {
struct path tmp = *path;
int did;
/* fuse has different memory inode for the same inumber */
vfsub_update_h_iattr(&tmp, &did);
if (did) {
tmp.dentry = path->dentry->d_parent;
vfsub_update_h_iattr(&tmp, /*did*/NULL);
tmp.dentry = src_dentry;
vfsub_update_h_iattr(&tmp, /*did*/NULL);
}
/*ignore*/
}
out:
return err;
}
int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
struct inode *dir, struct path *path)
{
int err;
struct path tmp = {
.mnt = path->mnt
};
struct dentry *d;
IMustLock(dir);
IMustLock(src_dir);
d = path->dentry;
path->dentry = d->d_parent;
tmp.dentry = src_dentry->d_parent;
err = security_path_rename(&tmp, src_dentry, path, d);
path->dentry = d;
if (unlikely(err))
goto out;
lockdep_off();
err = vfs_rename(src_dir, src_dentry, dir, path->dentry);
lockdep_on();
if (!err) {
int did;
tmp.dentry = d->d_parent;
vfsub_update_h_iattr(&tmp, &did);
if (did) {
tmp.dentry = src_dentry;
vfsub_update_h_iattr(&tmp, /*did*/NULL);
tmp.dentry = src_dentry->d_parent;
vfsub_update_h_iattr(&tmp, /*did*/NULL);
}
/*ignore*/
}
out:
return err;
}
int vfsub_mkdir(struct inode *dir, struct path *path, int mode)
{
int err;
struct dentry *d;
IMustLock(dir);
d = path->dentry;
path->dentry = d->d_parent;
err = security_path_mkdir(path, d, mode);
path->dentry = d;
if (unlikely(err))
goto out;
err = vfs_mkdir(dir, path->dentry, mode);
if (!err) {
struct path tmp = *path;
int did;
vfsub_update_h_iattr(&tmp, &did);
if (did) {
tmp.dentry = path->dentry->d_parent;
vfsub_update_h_iattr(&tmp, /*did*/NULL);
}
/*ignore*/
}
out:
return err;
}
int vfsub_rmdir(struct inode *dir, struct path *path)
{
int err;
struct dentry *d;
IMustLock(dir);
d = path->dentry;
path->dentry = d->d_parent;
err = security_path_rmdir(path, d);
path->dentry = d;
if (unlikely(err))
goto out;
lockdep_off();
err = vfs_rmdir(dir, path->dentry);
lockdep_on();
if (!err) {
struct path tmp = {
.dentry = path->dentry->d_parent,
.mnt = path->mnt
};
vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/
}
out:
return err;
}
/* ---------------------------------------------------------------------- */
/* todo: support mmap_sem? */
ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
loff_t *ppos)
{
ssize_t err;
lockdep_off();
err = vfs_read(file, ubuf, count, ppos);
lockdep_on();
if (err >= 0)
vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/
return err;
}
/* todo: kernel_read()? */
ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count,
loff_t *ppos)
{
ssize_t err;
mm_segment_t oldfs;
union {
void *k;
char __user *u;
} buf;
buf.k = kbuf;
oldfs = get_fs();
set_fs(KERNEL_DS);
err = vfsub_read_u(file, buf.u, count, ppos);
set_fs(oldfs);
return err;
}
ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
loff_t *ppos)
{
ssize_t err;
lockdep_off();
err = vfs_write(file, ubuf, count, ppos);
lockdep_on();
if (err >= 0)
vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/
return err;
}
ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos)
{
ssize_t err;
mm_segment_t oldfs;
union {
void *k;
const char __user *u;
} buf;
buf.k = kbuf;
oldfs = get_fs();
set_fs(KERNEL_DS);
err = vfsub_write_u(file, buf.u, count, ppos);
set_fs(oldfs);
return err;
}
int vfsub_flush(struct file *file, fl_owner_t id)
{
int err;
err = 0;
if (file->f_op && file->f_op->flush) {
if (!au_test_nfs(file->f_dentry->d_sb))
err = file->f_op->flush(file, id);
else {
lockdep_off();
err = file->f_op->flush(file, id);
lockdep_on();
}
if (!err)
vfsub_update_h_iattr(&file->f_path, /*did*/NULL);
/*ignore*/
}
return err;
}
int vfsub_readdir(struct file *file, filldir_t filldir, void *arg)
{
int err;
lockdep_off();
err = vfs_readdir(file, filldir, arg);
lockdep_on();
if (err >= 0)
vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/
return err;
}
long vfsub_splice_to(struct file *in, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags)
{
long err;
lockdep_off();
err = do_splice_to(in, ppos, pipe, len, flags);
lockdep_on();
file_accessed(in);
if (err >= 0)
vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/
return err;
}
long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags)
{
long err;
lockdep_off();
err = do_splice_from(pipe, out, ppos, len, flags);
lockdep_on();
if (err >= 0)
vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/
return err;
}
int vfsub_fsync(struct file *file, struct path *path, int datasync)
{
int err;
/* file can be NULL */
lockdep_off();
err = vfs_fsync(file, datasync);
lockdep_on();
if (!err) {
if (!path) {
AuDebugOn(!file);
path = &file->f_path;
}
vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/
}
return err;
}
/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */
int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr,
struct file *h_file)
{
int err;
struct inode *h_inode;
h_inode = h_path->dentry->d_inode;
if (!h_file) {
err = 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 = locks_verify_truncate(h_inode, h_file, length);
if (!err)
err = security_path_truncate(h_path);
if (!err) {
lockdep_off();
err = do_truncate(h_path->dentry, length, attr, h_file);
lockdep_on();
}
out_inode:
if (!h_file)
put_write_access(h_inode);
out_mnt:
if (!h_file)
mnt_drop_write(h_path->mnt);
out:
return err;
}
/* ---------------------------------------------------------------------- */
struct au_vfsub_mkdir_args {
int *errp;
struct inode *dir;
struct path *path;
int mode;
};
static void au_call_vfsub_mkdir(void *args)
{
struct au_vfsub_mkdir_args *a = args;
*a->errp = vfsub_mkdir(a->dir, a->path, a->mode);
}
int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode)
{
int err, do_sio, wkq_err;
do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE);
if (!do_sio)
err = vfsub_mkdir(dir, path, mode);
else {
struct au_vfsub_mkdir_args args = {
.errp = &err,
.dir = dir,
.path = path,
.mode = mode
};
wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args);
if (unlikely(wkq_err))
err = wkq_err;
}
return err;
}
struct au_vfsub_rmdir_args {
int *errp;
struct inode *dir;
struct path *path;
};
static void au_call_vfsub_rmdir(void *args)
{
struct au_vfsub_rmdir_args *a = args;
*a->errp = vfsub_rmdir(a->dir, a->path);
}
int vfsub_sio_rmdir(struct inode *dir, struct path *path)
{
int err, do_sio, wkq_err;
do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE);
if (!do_sio)
err = vfsub_rmdir(dir, path);
else {
struct au_vfsub_rmdir_args args = {
.errp = &err,
.dir = dir,
.path = path
};
wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args);
if (unlikely(wkq_err))
err = wkq_err;
}
return err;
}
/* ---------------------------------------------------------------------- */
struct notify_change_args {
int *errp;
struct path *path;
struct iattr *ia;
};
static void call_notify_change(void *args)
{
struct notify_change_args *a = args;
struct inode *h_inode;
h_inode = a->path->dentry->d_inode;
IMustLock(h_inode);
*a->errp = -EPERM;
if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) {
*a->errp = notify_change(a->path->dentry, a->ia);
if (!*a->errp)
vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/
}
AuTraceErr(*a->errp);
}
int vfsub_notify_change(struct path *path, struct iattr *ia)
{
int err;
struct notify_change_args args = {
.errp = &err,
.path = path,
.ia = ia
};
call_notify_change(&args);
return err;
}
int vfsub_sio_notify_change(struct path *path, struct iattr *ia)
{
int err, wkq_err;
struct notify_change_args args = {
.errp = &err,
.path = path,
.ia = ia
};
wkq_err = au_wkq_wait(call_notify_change, &args);
if (unlikely(wkq_err))
err = wkq_err;
return err;
}
/* ---------------------------------------------------------------------- */
struct unlink_args {
int *errp;
struct inode *dir;
struct path *path;
};
static void call_unlink(void *args)
{
struct unlink_args *a = args;
struct dentry *d = a->path->dentry;
struct inode *h_inode;
const int stop_sillyrename = (au_test_nfs(d->d_sb)
&& d->d_count == 1);
IMustLock(a->dir);
a->path->dentry = d->d_parent;
*a->errp = security_path_unlink(a->path, d);
a->path->dentry = d;
if (unlikely(*a->errp))
return;
if (!stop_sillyrename)
dget(d);
h_inode = d->d_inode;
if (h_inode)
ihold(h_inode);
lockdep_off();
*a->errp = vfs_unlink(a->dir, d);
lockdep_on();
if (!*a->errp) {
struct path tmp = {
.dentry = d->d_parent,
.mnt = a->path->mnt
};
vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/
}
if (!stop_sillyrename)
dput(d);
if (h_inode)
iput(h_inode);
AuTraceErr(*a->errp);
}
/*
* @dir: must be locked.
* @dentry: target dentry.
*/
int vfsub_unlink(struct inode *dir, struct path *path, int force)
{
int err;
struct unlink_args args = {
.errp = &err,
.dir = dir,
.path = path
};
if (!force)
call_unlink(&args);
else {
int wkq_err;
wkq_err = au_wkq_wait(call_unlink, &args);
if (unlikely(wkq_err))
err = wkq_err;
}
return err;
}

View File

@ -0,0 +1,232 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* sub-routines for VFS
*/
#ifndef __AUFS_VFSUB_H__
#define __AUFS_VFSUB_H__
#ifdef __KERNEL__
#include <linux/fs.h>
#include <linux/lglock.h>
#include "debug.h"
/* copied from linux/fs/internal.h */
/* todo: BAD approach!! */
DECLARE_BRLOCK(vfsmount_lock);
extern void file_sb_list_del(struct file *f);
extern spinlock_t inode_sb_list_lock;
/* copied from linux/fs/file_table.c */
DECLARE_LGLOCK(files_lglock);
#ifdef CONFIG_SMP
/*
* These macros iterate all files on all CPUs for a given superblock.
* files_lglock must be held globally.
*/
#define do_file_list_for_each_entry(__sb, __file) \
{ \
int i; \
for_each_possible_cpu(i) { \
struct list_head *list; \
list = per_cpu_ptr((__sb)->s_files, i); \
list_for_each_entry((__file), list, f_u.fu_list)
#define while_file_list_for_each_entry \
} \
}
#else
#define do_file_list_for_each_entry(__sb, __file) \
{ \
struct list_head *list; \
list = &(sb)->s_files; \
list_for_each_entry((__file), list, f_u.fu_list)
#define while_file_list_for_each_entry \
}
#endif
/* ---------------------------------------------------------------------- */
/* lock subclass for lower inode */
/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */
/* reduce? gave up. */
enum {
AuLsc_I_Begin = I_MUTEX_QUOTA, /* 4 */
AuLsc_I_PARENT, /* lower inode, parent first */
AuLsc_I_PARENT2, /* copyup dirs */
AuLsc_I_PARENT3, /* copyup wh */
AuLsc_I_CHILD,
AuLsc_I_CHILD2,
AuLsc_I_End
};
/* to debug easier, do not make them inlined functions */
#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx))
#define IMustLock(i) MtxMustLock(&(i)->i_mutex)
/* ---------------------------------------------------------------------- */
static inline void vfsub_drop_nlink(struct inode *inode)
{
AuDebugOn(!inode->i_nlink);
drop_nlink(inode);
}
static inline void vfsub_dead_dir(struct inode *inode)
{
AuDebugOn(!S_ISDIR(inode->i_mode));
inode->i_flags |= S_DEAD;
clear_nlink(inode);
}
/* ---------------------------------------------------------------------- */
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);
int vfsub_kern_path(const char *name, unsigned int flags, struct path *path);
struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,
int len);
struct dentry *vfsub_lookup_hash(struct nameidata *nd);
int vfsub_name_hash(const char *name, struct qstr *this, int len);
/* ---------------------------------------------------------------------- */
struct au_hinode;
struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1,
struct dentry *d2, struct au_hinode *hdir2);
void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1,
struct dentry *d2, struct au_hinode *hdir2);
int vfsub_create(struct inode *dir, struct path *path, int mode);
int vfsub_symlink(struct inode *dir, struct path *path,
const char *symname);
int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev);
int vfsub_link(struct dentry *src_dentry, struct inode *dir,
struct path *path);
int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry,
struct inode *hdir, struct path *path);
int vfsub_mkdir(struct inode *dir, struct path *path, int mode);
int vfsub_rmdir(struct inode *dir, struct path *path);
/* ---------------------------------------------------------------------- */
ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
loff_t *ppos);
ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count,
loff_t *ppos);
ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
loff_t *ppos);
ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count,
loff_t *ppos);
int vfsub_flush(struct file *file, fl_owner_t id);
int vfsub_readdir(struct file *file, filldir_t filldir, void *arg);
static inline unsigned int vfsub_file_flags(struct file *file)
{
unsigned int flags;
spin_lock(&file->f_lock);
flags = file->f_flags;
spin_unlock(&file->f_lock);
return flags;
}
static inline void vfsub_file_accessed(struct file *h_file)
{
file_accessed(h_file);
vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/
}
static inline void vfsub_touch_atime(struct vfsmount *h_mnt,
struct dentry *h_dentry)
{
struct path h_path = {
.dentry = h_dentry,
.mnt = h_mnt
};
touch_atime(h_mnt, h_dentry);
vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/
}
long vfsub_splice_to(struct file *in, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags);
long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags);
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);
/* ---------------------------------------------------------------------- */
static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin)
{
loff_t err;
lockdep_off();
err = vfs_llseek(file, offset, origin);
lockdep_on();
return err;
}
/* ---------------------------------------------------------------------- */
/* dirty workaround for strict type of fmode_t */
union vfsub_fmu {
fmode_t fm;
unsigned int ui;
};
static inline unsigned int vfsub_fmode_to_uint(fmode_t fm)
{
union vfsub_fmu u = {
.fm = fm
};
BUILD_BUG_ON(sizeof(u.fm) != sizeof(u.ui));
return u.ui;
}
static inline fmode_t vfsub_uint_to_fmode(unsigned int ui)
{
union vfsub_fmu u = {
.ui = ui
};
return u.fm;
}
/* ---------------------------------------------------------------------- */
int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode);
int vfsub_sio_rmdir(struct inode *dir, struct path *path);
int vfsub_sio_notify_change(struct path *path, struct iattr *ia);
int vfsub_notify_change(struct path *path, struct iattr *ia);
int vfsub_unlink(struct inode *dir, struct path *path, int force);
#endif /* __KERNEL__ */
#endif /* __AUFS_VFSUB_H__ */

View File

@ -0,0 +1,700 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* policies for selecting one among multiple writable branches
*/
#include <linux/statfs.h>
#include "aufs.h"
/* subset of cpup_attr() */
static noinline_for_stack
int au_cpdown_attr(struct path *h_path, struct dentry *h_src)
{
int err, sbits;
struct iattr ia;
struct inode *h_isrc;
h_isrc = h_src->d_inode;
ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID;
ia.ia_mode = h_isrc->i_mode;
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);
err = vfsub_sio_notify_change(h_path, &ia);
/* is this nfs only? */
if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) {
ia.ia_valid = ATTR_FORCE | ATTR_MODE;
ia.ia_mode = h_isrc->i_mode;
err = vfsub_sio_notify_change(h_path, &ia);
}
return err;
}
#define AuCpdown_PARENT_OPQ 1
#define AuCpdown_WHED (1 << 1)
#define AuCpdown_MADE_DIR (1 << 2)
#define AuCpdown_DIROPQ (1 << 3)
#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name)
#define au_fset_cpdown(flags, name) \
do { (flags) |= AuCpdown_##name; } while (0)
#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)
{
int err;
struct dentry *opq_dentry;
opq_dentry = au_diropq_create(dentry, bdst);
err = PTR_ERR(opq_dentry);
if (IS_ERR(opq_dentry))
goto out;
dput(opq_dentry);
au_fset_cpdown(a->flags, DIROPQ);
out:
return err;
}
static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent,
struct inode *dir, aufs_bindex_t bdst)
{
int err;
struct path h_path;
struct au_branch *br;
br = au_sbr(dentry->d_sb, bdst);
h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br);
err = PTR_ERR(h_path.dentry);
if (IS_ERR(h_path.dentry))
goto out;
err = 0;
if (h_path.dentry->d_inode) {
h_path.mnt = br->br_mnt;
err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path,
dentry);
}
dput(h_path.dentry);
out:
return err;
}
static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
struct dentry *h_parent, void *arg)
{
int err, rerr;
aufs_bindex_t bopq, bstart;
struct path h_path;
struct dentry *parent;
struct inode *h_dir, *h_inode, *inode, *dir;
struct au_cpdown_dir_args *args = arg;
bstart = au_dbstart(dentry);
/* dentry is di-locked */
parent = dget_parent(dentry);
dir = parent->d_inode;
h_dir = h_parent->d_inode;
AuDebugOn(h_dir != au_h_iptr(dir, bdst));
IMustLock(h_dir);
err = au_lkup_neg(dentry, bdst);
if (unlikely(err < 0))
goto out;
h_path.dentry = au_h_dptr(dentry, bdst);
h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst);
err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path,
S_IRWXU | S_IRUGO | S_IXUGO);
if (unlikely(err))
goto out_put;
au_fset_cpdown(args->flags, MADE_DIR);
bopq = au_dbdiropq(dentry);
au_fclr_cpdown(args->flags, WHED);
au_fclr_cpdown(args->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);
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 (unlikely(err)) {
mutex_unlock(&h_inode->i_mutex);
goto out_dir;
}
}
err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart));
mutex_unlock(&h_inode->i_mutex);
if (unlikely(err))
goto out_opq;
if (au_ftest_cpdown(args->flags, WHED)) {
err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst);
if (unlikely(err))
goto out_opq;
}
inode = dentry->d_inode;
if (au_ibend(inode) < bdst)
au_set_ibend(inode, bdst);
au_set_h_iptr(inode, bdst, au_igrab(h_inode),
au_hi_flags(inode, /*isdir*/1));
goto out; /* success */
/* revert */
out_opq:
if (au_ftest_cpdown(args->flags, DIROPQ)) {
mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
rerr = au_diropq_remove(dentry, bdst);
mutex_unlock(&h_inode->i_mutex);
if (unlikely(rerr)) {
AuIOErr("failed removing diropq for %.*s b%d (%d)\n",
AuDLNPair(dentry), bdst, rerr);
err = -EIO;
goto out;
}
}
out_dir:
if (au_ftest_cpdown(args->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",
AuDLNPair(dentry), bdst, rerr);
err = -EIO;
}
}
out_put:
au_set_h_dptr(dentry, bdst, NULL);
if (au_dbend(dentry) == bdst)
au_update_dbend(dentry);
out:
dput(parent);
return err;
}
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
};
err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &args);
dput(args.parent);
return err;
}
/* ---------------------------------------------------------------------- */
/* policies for create */
static int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex)
{
int err, i, j, ndentry;
aufs_bindex_t bopq;
struct au_dcsub_pages dpages;
struct au_dpage *dpage;
struct dentry **dentries, *parent, *d;
err = au_dpages_init(&dpages, GFP_NOFS);
if (unlikely(err))
goto out;
parent = dget_parent(dentry);
err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0);
if (unlikely(err))
goto out_free;
err = bindex;
for (i = 0; i < dpages.ndpage; i++) {
dpage = dpages.dpages + i;
dentries = dpage->dentries;
ndentry = dpage->ndentry;
for (j = 0; j < ndentry; j++) {
d = dentries[j];
di_read_lock_parent2(d, !AuLock_IR);
bopq = au_dbdiropq(d);
di_read_unlock(d, !AuLock_IR);
if (bopq >= 0 && bopq < err)
err = bopq;
}
}
out_free:
dput(parent);
au_dpages_free(&dpages);
out:
return err;
}
static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex)
{
for (; bindex >= 0; bindex--)
if (!au_br_rdonly(au_sbr(sb, bindex)))
return bindex;
return -EROFS;
}
/* top down parent */
static int au_wbr_create_tdp(struct dentry *dentry, int isdir __maybe_unused)
{
int err;
aufs_bindex_t bstart, bindex;
struct super_block *sb;
struct dentry *parent, *h_parent;
sb = dentry->d_sb;
bstart = au_dbstart(dentry);
err = bstart;
if (!au_br_rdonly(au_sbr(sb, bstart)))
goto out;
err = -EROFS;
parent = dget_parent(dentry);
for (bindex = au_dbstart(parent); bindex < bstart; bindex++) {
h_parent = au_h_dptr(parent, bindex);
if (!h_parent || !h_parent->d_inode)
continue;
if (!au_br_rdonly(au_sbr(sb, bindex))) {
err = bindex;
break;
}
}
dput(parent);
/* bottom up here */
if (unlikely(err < 0)) {
err = au_wbr_bu(sb, bstart - 1);
if (err >= 0)
err = au_wbr_nonopq(dentry, err);
}
out:
AuDbg("b%d\n", err);
return err;
}
/* ---------------------------------------------------------------------- */
/* an exception for the policy other than tdp */
static int au_wbr_create_exp(struct dentry *dentry)
{
int err;
aufs_bindex_t bwh, bdiropq;
struct dentry *parent;
err = -1;
bwh = au_dbwh(dentry);
parent = dget_parent(dentry);
bdiropq = au_dbdiropq(parent);
if (bwh >= 0) {
if (bdiropq >= 0)
err = min(bdiropq, bwh);
else
err = bwh;
AuDbg("%d\n", err);
} else if (bdiropq >= 0) {
err = bdiropq;
AuDbg("%d\n", err);
}
dput(parent);
if (err >= 0)
err = au_wbr_nonopq(dentry, err);
if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err)))
err = -1;
AuDbg("%d\n", err);
return err;
}
/* ---------------------------------------------------------------------- */
/* round robin */
static int au_wbr_create_init_rr(struct super_block *sb)
{
int err;
err = au_wbr_bu(sb, au_sbend(sb));
atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */
/* smp_mb(); */
AuDbg("b%d\n", err);
return err;
}
static int au_wbr_create_rr(struct dentry *dentry, int isdir)
{
int err, nbr;
unsigned int u;
aufs_bindex_t bindex, bend;
struct super_block *sb;
atomic_t *next;
err = au_wbr_create_exp(dentry);
if (err >= 0)
goto out;
sb = dentry->d_sb;
next = &au_sbi(sb)->si_wbr_rr_next;
bend = au_sbend(sb);
nbr = bend + 1;
for (bindex = 0; bindex <= bend; bindex++) {
if (!isdir) {
err = atomic_dec_return(next) + 1;
/* modulo for 0 is meaningless */
if (unlikely(!err))
err = atomic_dec_return(next) + 1;
} else
err = atomic_read(next);
AuDbg("%d\n", err);
u = err;
err = u % nbr;
AuDbg("%d\n", err);
if (!au_br_rdonly(au_sbr(sb, err)))
break;
err = -EROFS;
}
if (err >= 0)
err = au_wbr_nonopq(dentry, err);
out:
AuDbg("%d\n", err);
return err;
}
/* ---------------------------------------------------------------------- */
/* most free space */
static void au_mfs(struct dentry *dentry)
{
struct super_block *sb;
struct au_branch *br;
struct au_wbr_mfs *mfs;
aufs_bindex_t bindex, bend;
int err;
unsigned long long b, bavail;
struct path h_path;
/* reduce the stack usage */
struct kstatfs *st;
st = kmalloc(sizeof(*st), GFP_NOFS);
if (unlikely(!st)) {
AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM);
return;
}
bavail = 0;
sb = dentry->d_sb;
mfs = &au_sbi(sb)->si_wbr_mfs;
MtxMustLock(&mfs->mfs_lock);
mfs->mfs_bindex = -EROFS;
mfs->mfsrr_bytes = 0;
bend = au_sbend(sb);
for (bindex = 0; bindex <= bend; bindex++) {
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.dentry = h_path.mnt->mnt_root;
err = vfs_statfs(&h_path, st);
if (unlikely(err)) {
AuWarn1("failed statfs, b%d, %d\n", bindex, err);
continue;
}
/* when the available size is equal, select the lower one */
BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail)
|| sizeof(b) < sizeof(st->f_bsize));
b = st->f_bavail * st->f_bsize;
br->br_wbr->wbr_bytes = b;
if (b >= bavail) {
bavail = b;
mfs->mfs_bindex = bindex;
mfs->mfs_jiffy = jiffies;
}
}
mfs->mfsrr_bytes = bavail;
AuDbg("b%d\n", mfs->mfs_bindex);
kfree(st);
}
static int au_wbr_create_mfs(struct dentry *dentry, int isdir __maybe_unused)
{
int err;
struct super_block *sb;
struct au_wbr_mfs *mfs;
err = au_wbr_create_exp(dentry);
if (err >= 0)
goto out;
sb = dentry->d_sb;
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);
mutex_unlock(&mfs->mfs_lock);
err = mfs->mfs_bindex;
if (err >= 0)
err = au_wbr_nonopq(dentry, err);
out:
AuDbg("b%d\n", err);
return err;
}
static int au_wbr_create_init_mfs(struct super_block *sb)
{
struct au_wbr_mfs *mfs;
mfs = &au_sbi(sb)->si_wbr_mfs;
mutex_init(&mfs->mfs_lock);
mfs->mfs_jiffy = 0;
mfs->mfs_bindex = -EROFS;
return 0;
}
static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused)
{
mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock);
return 0;
}
/* ---------------------------------------------------------------------- */
/* most free space and then round robin */
static int au_wbr_create_mfsrr(struct dentry *dentry, int isdir)
{
int err;
struct au_wbr_mfs *mfs;
err = au_wbr_create_mfs(dentry, isdir);
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);
mutex_unlock(&mfs->mfs_lock);
}
AuDbg("b%d\n", err);
return err;
}
static int au_wbr_create_init_mfsrr(struct super_block *sb)
{
int err;
au_wbr_create_init_mfs(sb); /* ignore */
err = au_wbr_create_init_rr(sb);
return err;
}
/* ---------------------------------------------------------------------- */
/* top down parent and most free space */
static int au_wbr_create_pmfs(struct dentry *dentry, int isdir)
{
int err, e2;
unsigned long long b;
aufs_bindex_t bindex, bstart, bend;
struct super_block *sb;
struct dentry *parent, *h_parent;
struct au_branch *br;
err = au_wbr_create_tdp(dentry, isdir);
if (unlikely(err < 0))
goto out;
parent = dget_parent(dentry);
bstart = au_dbstart(parent);
bend = au_dbtaildir(parent);
if (bstart == bend)
goto out_parent; /* success */
e2 = au_wbr_create_mfs(dentry, isdir);
if (e2 < 0)
goto out_parent; /* success */
/* when the available size is equal, select upper one */
sb = dentry->d_sb;
br = au_sbr(sb, err);
b = br->br_wbr->wbr_bytes;
AuDbg("b%d, %llu\n", err, b);
for (bindex = bstart; bindex <= bend; bindex++) {
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) && br->br_wbr->wbr_bytes > b) {
b = br->br_wbr->wbr_bytes;
err = bindex;
AuDbg("b%d, %llu\n", err, b);
}
}
if (err >= 0)
err = au_wbr_nonopq(dentry, err);
out_parent:
dput(parent);
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);
}
/* bottom up parent */
static int au_wbr_copyup_bup(struct dentry *dentry)
{
int err;
aufs_bindex_t bindex, bstart;
struct dentry *parent, *h_parent;
struct super_block *sb;
err = -EROFS;
sb = dentry->d_sb;
parent = dget_parent(dentry);
bstart = au_dbstart(parent);
for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) {
h_parent = au_h_dptr(parent, bindex);
if (!h_parent || !h_parent->d_inode)
continue;
if (!au_br_rdonly(au_sbr(sb, bindex))) {
err = bindex;
break;
}
}
dput(parent);
/* bottom up here */
if (unlikely(err < 0))
err = au_wbr_bu(sb, bstart - 1);
AuDbg("b%d\n", err);
return err;
}
/* bottom up */
static int au_wbr_copyup_bu(struct dentry *dentry)
{
int err;
aufs_bindex_t bstart;
bstart = au_dbstart(dentry);
err = au_wbr_bu(dentry->d_sb, bstart);
AuDbg("b%d\n", err);
if (err > bstart)
err = au_wbr_nonopq(dentry, err);
AuDbg("b%d\n", err);
return err;
}
/* ---------------------------------------------------------------------- */
struct au_wbr_copyup_operations au_wbr_copyup_ops[] = {
[AuWbrCopyup_TDP] = {
.copyup = au_wbr_copyup_tdp
},
[AuWbrCopyup_BUP] = {
.copyup = au_wbr_copyup_bup
},
[AuWbrCopyup_BU] = {
.copyup = au_wbr_copyup_bu
}
};
struct au_wbr_create_operations au_wbr_create_ops[] = {
[AuWbrCreate_TDP] = {
.create = au_wbr_create_tdp
},
[AuWbrCreate_RR] = {
.create = au_wbr_create_rr,
.init = au_wbr_create_init_rr
},
[AuWbrCreate_MFS] = {
.create = au_wbr_create_mfs,
.init = au_wbr_create_init_mfs,
.fin = au_wbr_create_fin_mfs
},
[AuWbrCreate_MFSV] = {
.create = au_wbr_create_mfs,
.init = au_wbr_create_init_mfs,
.fin = au_wbr_create_fin_mfs
},
[AuWbrCreate_MFSRR] = {
.create = au_wbr_create_mfsrr,
.init = au_wbr_create_init_mfsrr,
.fin = au_wbr_create_fin_mfs
},
[AuWbrCreate_MFSRRV] = {
.create = au_wbr_create_mfsrr,
.init = au_wbr_create_init_mfsrr,
.fin = au_wbr_create_fin_mfs
},
[AuWbrCreate_PMFS] = {
.create = au_wbr_create_pmfs,
.init = au_wbr_create_init_mfs,
.fin = au_wbr_create_fin_mfs
},
[AuWbrCreate_PMFSV] = {
.create = au_wbr_create_pmfs,
.init = au_wbr_create_init_mfs,
.fin = au_wbr_create_fin_mfs
}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* whiteout for logical deletion and opaque directory
*/
#ifndef __AUFS_WHOUT_H__
#define __AUFS_WHOUT_H__
#ifdef __KERNEL__
#include "dir.h"
/* whout.c */
int au_wh_name_alloc(struct qstr *wh, const struct qstr *name);
struct au_branch;
int au_wh_test(struct dentry *h_parent, struct qstr *wh_name,
struct au_branch *br, int try_sio);
int au_diropq_test(struct dentry *h_dentry, struct au_branch *br);
struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br,
struct qstr *prefix);
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);
/* diropq flags */
#define AuDiropq_CREATE 1
#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name)
#define au_fset_diropq(flags, name) \
do { (flags) |= AuDiropq_##name; } while (0)
#define au_fclr_diropq(flags, name) \
do { (flags) &= ~AuDiropq_##name; } while (0)
struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex,
unsigned int flags);
struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name,
struct au_branch *br);
struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_parent);
/* real rmdir for the whiteout-ed dir */
struct au_whtmp_rmdir {
struct inode *dir;
struct au_branch *br;
struct dentry *wh_dentry;
struct au_nhash whlist;
};
struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp);
void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp);
int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex,
struct dentry *wh_dentry, struct au_nhash *whlist);
void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex,
struct dentry *wh_dentry, struct au_whtmp_rmdir *args);
/* ---------------------------------------------------------------------- */
static inline struct dentry *au_diropq_create(struct dentry *dentry,
aufs_bindex_t bindex)
{
return au_diropq_sio(dentry, bindex, AuDiropq_CREATE);
}
static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex)
{
return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE));
}
#endif /* __KERNEL__ */
#endif /* __AUFS_WHOUT_H__ */

View File

@ -0,0 +1,214 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* workqueue for asynchronous/super-io operations
* todo: try new dredential scheme
*/
#include <linux/module.h>
#include "aufs.h"
/* internal workqueue named AUFS_WKQ_NAME */
static struct workqueue_struct *au_wkq;
struct au_wkinfo {
struct work_struct wk;
struct kobject *kobj;
unsigned int flags; /* see wkq.h */
au_wkq_func_t func;
void *args;
struct completion *comp;
};
/* ---------------------------------------------------------------------- */
static void wkq_func(struct work_struct *wk)
{
struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk);
AuDebugOn(current_fsuid());
AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY);
wkinfo->func(wkinfo->args);
if (au_ftest_wkq(wkinfo->flags, WAIT))
complete(wkinfo->comp);
else {
kobject_put(wkinfo->kobj);
module_put(THIS_MODULE); /* todo: ?? */
kfree(wkinfo);
}
}
/*
* Since struct completion is large, try allocating it dynamically.
*/
#if 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)
{
*comp = kmalloc(sizeof(**comp), GFP_NOFS);
if (*comp) {
init_completion(*comp);
wkinfo->comp = *comp;
return 0;
}
return -ENOMEM;
}
static void au_wkq_comp_free(struct completion *comp)
{
kfree(comp);
}
#else
/* no braces */
#define AuWkqCompDeclare(name) \
DECLARE_COMPLETION_ONSTACK(_ ## name); \
struct completion *comp = &_ ## name
static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp)
{
wkinfo->comp = *comp;
return 0;
}
static void au_wkq_comp_free(struct completion *comp __maybe_unused)
{
/* empty */
}
#endif /* 4KSTACKS */
static void au_wkq_run(struct au_wkinfo *wkinfo)
{
if (au_ftest_wkq(wkinfo->flags, NEST)) {
if (au_wkq_test()) {
AuWarn1("wkq from wkq, due to a dead dir by UDBA?\n");
AuDebugOn(au_ftest_wkq(wkinfo->flags, WAIT));
}
} else
au_dbg_verify_kthread();
if (au_ftest_wkq(wkinfo->flags, WAIT)) {
INIT_WORK_ONSTACK(&wkinfo->wk, wkq_func);
queue_work(au_wkq, &wkinfo->wk);
} else {
INIT_WORK(&wkinfo->wk, wkq_func);
schedule_work(&wkinfo->wk);
}
}
/*
* Be careful. It is easy to make deadlock happen.
* processA: lock, wkq and wait
* processB: wkq and wait, lock in wkq
* --> deadlock
*/
int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args)
{
int err;
AuWkqCompDeclare(comp);
struct au_wkinfo wkinfo = {
.flags = flags,
.func = func,
.args = args
};
err = au_wkq_comp_alloc(&wkinfo, &comp);
if (!err) {
au_wkq_run(&wkinfo);
/* no timeout, no interrupt */
wait_for_completion(wkinfo.comp);
au_wkq_comp_free(comp);
destroy_work_on_stack(&wkinfo.wk);
}
return err;
}
/*
* Note: dget/dput() in func for aufs dentries are not supported. It will be a
* problem in a concurrent umounting.
*/
int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb,
unsigned int flags)
{
int err;
struct au_wkinfo *wkinfo;
atomic_inc(&au_sbi(sb)->si_nowait.nw_len);
/*
* wkq_func() must free this wkinfo.
* it highly depends upon the implementation of workqueue.
*/
err = 0;
wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS);
if (wkinfo) {
wkinfo->kobj = &au_sbi(sb)->si_kobj;
wkinfo->flags = flags & ~AuWkq_WAIT;
wkinfo->func = func;
wkinfo->args = args;
wkinfo->comp = NULL;
kobject_get(wkinfo->kobj);
__module_get(THIS_MODULE); /* todo: ?? */
au_wkq_run(wkinfo);
} else {
err = -ENOMEM;
au_nwt_done(&au_sbi(sb)->si_nowait);
}
return err;
}
/* ---------------------------------------------------------------------- */
void au_nwt_init(struct au_nowait_tasks *nwt)
{
atomic_set(&nwt->nw_len, 0);
/* smp_mb(); */ /* atomic_set */
init_waitqueue_head(&nwt->nw_wq);
}
void au_wkq_fin(void)
{
destroy_workqueue(au_wkq);
}
int __init au_wkq_init(void)
{
int err;
err = 0;
BUILD_BUG_ON(!WQ_RESCUER);
au_wkq = alloc_workqueue(AUFS_WKQ_NAME, !WQ_RESCUER, WQ_DFL_ACTIVE);
if (IS_ERR(au_wkq))
err = PTR_ERR(au_wkq);
else if (!au_wkq)
err = -ENOMEM;
return err;
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2005-2012 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
*/
/*
* workqueue for asynchronous/super-io operations
* todo: try new credentials management scheme
*/
#ifndef __AUFS_WKQ_H__
#define __AUFS_WKQ_H__
#ifdef __KERNEL__
struct super_block;
/* ---------------------------------------------------------------------- */
/*
* in the next operation, wait for the 'nowait' tasks in system-wide workqueue
*/
struct au_nowait_tasks {
atomic_t nw_len;
wait_queue_head_t nw_wq;
};
/* ---------------------------------------------------------------------- */
typedef void (*au_wkq_func_t)(void *args);
/* wkq flags */
#define AuWkq_WAIT 1
#define AuWkq_NEST (1 << 1)
#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name)
#define au_fset_wkq(flags, name) \
do { (flags) |= AuWkq_##name; } while (0)
#define au_fclr_wkq(flags, name) \
do { (flags) &= ~AuWkq_##name; } while (0)
#ifndef CONFIG_AUFS_HNOTIFY
#undef AuWkq_NEST
#define AuWkq_NEST 0
#endif
/* wkq.c */
int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args);
int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb,
unsigned int flags);
void au_nwt_init(struct au_nowait_tasks *nwt);
int __init au_wkq_init(void);
void au_wkq_fin(void);
/* ---------------------------------------------------------------------- */
static inline int au_wkq_test(void)
{
return current->flags & PF_WQ_WORKER;
}
static inline int au_wkq_wait(au_wkq_func_t func, void *args)
{
return au_wkq_do_wait(AuWkq_WAIT, func, args);
}
static inline void au_nwt_done(struct au_nowait_tasks *nwt)
{
if (atomic_dec_and_test(&nwt->nw_len))
wake_up_all(&nwt->nw_wq);
}
static inline int au_nwt_flush(struct au_nowait_tasks *nwt)
{
wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len));
return 0;
}
#endif /* __KERNEL__ */
#endif /* __AUFS_WKQ_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ KERNEL_IMAGETYPE = "uImage"
COMPATIBLE_MACHINE = "(allwinner-a10)"
PR = "1"
PR = "2"
PV = "3.0.42"
# Last tested version by myself"
@ -15,7 +15,21 @@ SRCREV_pn-${PN} = "7003c80c6dbea815c4a78e745565ec03a039ba3a"
MACHINE_KERNEL_PR_append = "a"
SRC_URI += "git://github.com/amery/linux-allwinner.git;branch=allwinner-v3.0-android-v2;protocol=git \
file://0001-aufs3-kbuild.patch \
file://0002-aufs3-base.patch \
file://0003-aufs3-proc_map.patch \
file://0004-aufs3-standalone.patch \
file://fs \
file://aufs_type.h \
file://defconfig \
"
S = "${WORKDIR}/git"
#add AUFS stuff
do_copy_aufs () {
cp ${WORKDIR}/aufs_type.h ${S}/include/linux
cp -a ${WORKDIR}/fs ${S}/
}
addtask copy_aufs after do_unpack before do_patch