X-Git-Url: http://git.pld-linux.org/?a=blobdiff_plain;f=kernel-aufs4.patch;h=f409e97405564c299faa85cb27437fd46b7194f7;hb=4e1a4c84e6398a77c78048ea1fa3fe653dce7712;hp=cce529e5d2291f2bd42e47c34055e3a62ca3ac7a;hpb=e2f27e51ceca2d312847b5db0436f7004fe489b7;p=packages%2Fkernel.git diff --git a/kernel-aufs4.patch b/kernel-aufs4.patch index cce529e5..f409e974 100644 --- a/kernel-aufs4.patch +++ b/kernel-aufs4.patch @@ -1,10 +1,10 @@ -aufs4.x-rcN kbuild patch +aufs4.9 kbuild patch diff --git a/fs/Kconfig b/fs/Kconfig -index 2bc7ad7..3049386 100644 +index 4bd03a2..620e01b 100644 --- a/fs/Kconfig +++ b/fs/Kconfig -@@ -245,6 +245,7 @@ source "fs/pstore/Kconfig" +@@ -249,6 +249,7 @@ source "fs/pstore/Kconfig" source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" @@ -22,7 +22,7 @@ index ed2b632..aa6d14b 100644 obj-$(CONFIG_EFIVAR_FS) += efivarfs/ +obj-$(CONFIG_AUFS_FS) += aufs/ diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild -index 185f8ea..5deb0d1 100644 +index cd2be1c..78f3c68 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -59,6 +59,7 @@ header-y += atmsvc.h @@ -33,13 +33,13 @@ index 185f8ea..5deb0d1 100644 header-y += auto_fs4.h header-y += auto_fs.h header-y += auxvec.h -aufs4.x-rcN base patch +aufs4.9 base patch diff --git a/MAINTAINERS b/MAINTAINERS -index 01bff8e..ac77455 100644 +index 63cefa6..d78b954 100644 --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -2256,6 +2256,19 @@ F: include/linux/audit.h +@@ -2293,6 +2293,19 @@ F: include/linux/audit.h F: include/uapi/linux/audit.h F: kernel/audit* @@ -60,7 +60,7 @@ index 01bff8e..ac77455 100644 M: Miguel Ojeda Sandonis W: http://miguelojeda.es/auxdisplay.htm diff --git a/drivers/block/loop.c b/drivers/block/loop.c -index c9f2107..005e292 100644 +index fa1b7a9..6ee9235 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -701,6 +701,24 @@ static inline int is_loop_device(struct file *file) @@ -124,10 +124,10 @@ index 350a2c8..6f42279 100644 return error; diff --git a/fs/inode.c b/fs/inode.c -index 7e3ef3a..675fe84 100644 +index 88110fd..9a9ba3a 100644 --- a/fs/inode.c +++ b/fs/inode.c -@@ -1593,7 +1593,7 @@ EXPORT_SYMBOL(generic_update_time); +@@ -1642,7 +1642,7 @@ EXPORT_SYMBOL(generic_update_time); * This does the actual work of updating an inodes time or version. Must have * had called mnt_want_write() before calling this. */ @@ -136,8 +136,25 @@ index 7e3ef3a..675fe84 100644 { int (*update_time)(struct inode *, struct timespec *, int); +diff --git a/fs/namespace.c b/fs/namespace.c +index e6c234b..db0b1ac 100644 +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -787,6 +787,12 @@ static inline int check_mnt(struct mount *mnt) + return mnt->mnt_ns == current->nsproxy->mnt_ns; + } + ++/* for aufs, CONFIG_AUFS_BR_FUSE */ ++int is_current_mnt_ns(struct vfsmount *mnt) ++{ ++ return check_mnt(real_mount(mnt)); ++} ++ + /* + * vfsmount lock must be held for write + */ diff --git a/fs/read_write.c b/fs/read_write.c -index 66215a7..a1da117 100644 +index 190e0d36..4052813 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -515,6 +515,28 @@ ssize_t __vfs_write(struct file *file, const char __user *p, size_t count, @@ -170,10 +187,10 @@ index 66215a7..a1da117 100644 { mm_segment_t old_fs; diff --git a/fs/splice.c b/fs/splice.c -index dd9bf7e..9326c2a 100644 +index 5a7750b..28160a7 100644 --- a/fs/splice.c +++ b/fs/splice.c -@@ -1111,8 +1111,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); +@@ -855,8 +855,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); /* * Attempt to initiate a splice from pipe to file. */ @@ -184,7 +201,7 @@ index dd9bf7e..9326c2a 100644 { ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); -@@ -1128,9 +1128,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +@@ -872,9 +872,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. */ @@ -197,6 +214,19 @@ index dd9bf7e..9326c2a 100644 { ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); +diff --git a/fs/sync.c b/fs/sync.c +index 2a54c1f..7a5fa3f 100644 +--- a/fs/sync.c ++++ b/fs/sync.c +@@ -27,7 +27,7 @@ + * wait == 1 case since in that case write_inode() functions do + * sync_dirty_buffer() and thus effectively write one block at a time. + */ +-static int __sync_filesystem(struct super_block *sb, int wait) ++int __sync_filesystem(struct super_block *sb, int wait) + { + if (wait) + sync_inodes_sb(sb); diff --git a/include/linux/file.h b/include/linux/file.h index 7444f5f..bdac0be 100644 --- a/include/linux/file.h @@ -210,10 +240,10 @@ index 7444f5f..bdac0be 100644 static inline void fput_light(struct file *file, int fput_needed) { diff --git a/include/linux/fs.h b/include/linux/fs.h -index 901e25d..a71aa9e 100644 +index dc0478c..a02be40d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h -@@ -1275,6 +1275,7 @@ extern void fasync_free(struct fasync_struct *); +@@ -1291,6 +1291,7 @@ extern void fasync_free(struct fasync_struct *); /* can be called from interrupts */ extern void kill_fasync(struct fasync_struct **, int, int); @@ -221,7 +251,7 @@ index 901e25d..a71aa9e 100644 extern void __f_setown(struct file *filp, struct pid *, enum pid_type, int force); extern void f_setown(struct file *filp, unsigned long arg, int force); extern void f_delown(struct file *filp); -@@ -1699,6 +1700,7 @@ struct file_operations { +@@ -1715,6 +1716,7 @@ struct file_operations { ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); @@ -229,7 +259,7 @@ index 901e25d..a71aa9e 100644 int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); -@@ -1759,6 +1761,12 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, +@@ -1768,6 +1770,12 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, struct iovec *fast_pointer, struct iovec **ret_pointer); @@ -242,7 +272,7 @@ index 901e25d..a71aa9e 100644 extern ssize_t __vfs_read(struct file *, char __user *, size_t, loff_t *); extern ssize_t __vfs_write(struct file *, const char __user *, size_t, loff_t *); extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *); -@@ -2123,6 +2131,7 @@ extern int current_umask(void); +@@ -2140,6 +2148,7 @@ extern int current_umask(void); extern void ihold(struct inode * inode); extern void iput(struct inode *); extern int generic_update_time(struct inode *, struct timespec *, int); @@ -250,14 +280,41 @@ index 901e25d..a71aa9e 100644 /* /sys/fs */ extern struct kobject *fs_kobj; +@@ -2419,6 +2428,7 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb) + return false; + } + #endif ++extern int __sync_filesystem(struct super_block *, int); + extern int sync_filesystem(struct super_block *); + extern const struct file_operations def_blk_fops; + extern const struct file_operations def_chr_fops; +diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h +index 12b2ab5..8b810d1 100644 +--- a/include/linux/mnt_namespace.h ++++ b/include/linux/mnt_namespace.h +@@ -5,11 +5,14 @@ + struct mnt_namespace; + struct fs_struct; + struct user_namespace; ++struct vfsmount; + + extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *, + struct user_namespace *, struct fs_struct *); + extern void put_mnt_ns(struct mnt_namespace *ns); + ++extern int is_current_mnt_ns(struct vfsmount *mnt); ++ + extern const struct file_operations proc_mounts_operations; + extern const struct file_operations proc_mountinfo_operations; + extern const struct file_operations proc_mountstats_operations; diff --git a/include/linux/splice.h b/include/linux/splice.h -index da2751d..2e0fca6 100644 +index 00a2116..1f0a4a2 100644 --- a/include/linux/splice.h +++ b/include/linux/splice.h -@@ -83,4 +83,10 @@ extern void splice_shrink_spd(struct splice_pipe_desc *); - extern void spd_release_page(struct splice_pipe_desc *, unsigned int); +@@ -86,4 +86,10 @@ extern void spd_release_page(struct splice_pipe_desc *, unsigned int); extern const struct pipe_buf_operations page_cache_pipe_buf_ops; + extern const struct pipe_buf_operations default_pipe_buf_ops; + +extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags); @@ -265,13 +322,13 @@ index da2751d..2e0fca6 100644 + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); #endif -aufs4.x-rcN mmap patch +aufs4.9 mmap patch diff --git a/fs/proc/base.c b/fs/proc/base.c -index ac0df4d..42255e5 100644 +index ca651ac..0e8551a 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c -@@ -1938,7 +1938,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path) +@@ -1953,7 +1953,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path) down_read(&mm->mmap_sem); vma = find_exact_vma(mm, vm_start, vm_end); if (vma && vma->vm_file) { @@ -297,10 +354,10 @@ index f8595e8..cb8eda0 100644 ino = inode->i_ino; } diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c -index f6fa99e..2750949 100644 +index 35b92d8..5b981db 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c -@@ -298,7 +298,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) +@@ -291,7 +291,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) const char *name = NULL; if (file) { @@ -312,7 +369,7 @@ index f6fa99e..2750949 100644 dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; -@@ -1634,7 +1637,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) +@@ -1627,7 +1630,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) struct proc_maps_private *proc_priv = &numa_priv->proc_maps; struct vm_area_struct *vma = v; struct numa_maps *md = &numa_priv->md; @@ -322,10 +379,10 @@ index f6fa99e..2750949 100644 struct mm_walk walk = { .hugetlb_entry = gather_hugetlb_stats, diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c -index faacb0c..17b43be 100644 +index 3717562..6a328f1 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c -@@ -163,7 +163,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, +@@ -155,7 +155,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, file = vma->vm_file; if (file) { @@ -338,10 +395,10 @@ index faacb0c..17b43be 100644 ino = inode->i_ino; pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; diff --git a/include/linux/mm.h b/include/linux/mm.h -index ef815b9..a772481 100644 +index a92c8d7..1d83a2a 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h -@@ -1278,6 +1278,28 @@ static inline int fixup_user_fault(struct task_struct *tsk, +@@ -1266,6 +1266,28 @@ static inline int fixup_user_fault(struct task_struct *tsk, } #endif @@ -367,11 +424,11 @@ index ef815b9..a772481 100644 +#define vmr_fput(region) vmr_do_fput(region, __func__, __LINE__) +#endif /* !CONFIG_MMU */ + - extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); + extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, + unsigned int gup_flags); extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, - void *buf, int len, int write); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h -index 903200f..55fc528 100644 +index 4a8aced..badd16b 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -275,6 +275,7 @@ struct vm_region { @@ -391,10 +448,10 @@ index 903200f..55fc528 100644 #ifndef CONFIG_MMU diff --git a/kernel/fork.c b/kernel/fork.c -index beb3172..ad4cfa8 100644 +index 997ac1d..4d0131b 100644 --- a/kernel/fork.c +++ b/kernel/fork.c -@@ -477,7 +477,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) +@@ -624,7 +624,7 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm, struct inode *inode = file_inode(file); struct address_space *mapping = file->f_mapping; @@ -404,10 +461,10 @@ index beb3172..ad4cfa8 100644 atomic_dec(&inode->i_writecount); i_mmap_lock_write(mapping); diff --git a/mm/Makefile b/mm/Makefile -index 2ca1faf..6b9da3f 100644 +index 295bd7a..14fa1c8 100644 --- a/mm/Makefile +++ b/mm/Makefile -@@ -40,7 +40,7 @@ obj-y := filemap.o mempool.o oom_kill.o \ +@@ -37,7 +37,7 @@ obj-y := filemap.o mempool.o oom_kill.o \ mm_init.o mmu_context.o percpu.o slab_common.o \ compaction.o vmacache.o \ interval_tree.o list_lru.o workingset.o \ @@ -417,10 +474,10 @@ index 2ca1faf..6b9da3f 100644 obj-y += init-mm.o diff --git a/mm/filemap.c b/mm/filemap.c -index 8a287df..7583d66 100644 +index 50b52fe..9e607f9 100644 --- a/mm/filemap.c +++ b/mm/filemap.c -@@ -2284,7 +2284,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) +@@ -2304,7 +2304,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) int ret = VM_FAULT_LOCKED; sb_start_pagefault(inode->i_sb); @@ -430,10 +487,10 @@ index 8a287df..7583d66 100644 if (page->mapping != inode->i_mapping) { unlock_page(page); diff --git a/mm/memory.c b/mm/memory.c -index 793fe0f..45f39f3 100644 +index e18c57b..7be4a39 100644 --- a/mm/memory.c +++ b/mm/memory.c -@@ -2113,7 +2113,7 @@ static inline int wp_page_reuse(struct fault_env *fe, pte_t orig_pte, +@@ -2117,7 +2117,7 @@ static inline int wp_page_reuse(struct fault_env *fe, pte_t orig_pte, } if (!page_mkwrite) @@ -443,10 +500,10 @@ index 793fe0f..45f39f3 100644 return VM_FAULT_WRITE; diff --git a/mm/mmap.c b/mm/mmap.c -index ca9d91b..f3ebc5a 100644 +index 1af87c1..95b0ff4 100644 --- a/mm/mmap.c +++ b/mm/mmap.c -@@ -163,7 +163,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) +@@ -170,7 +170,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close(vma); if (vma->vm_file) @@ -455,7 +512,7 @@ index ca9d91b..f3ebc5a 100644 mpol_put(vma_policy(vma)); kmem_cache_free(vm_area_cachep, vma); return next; -@@ -790,7 +790,7 @@ again: +@@ -879,7 +879,7 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start, if (remove_next) { if (file) { uprobe_munmap(next, next->vm_start, next->vm_end); @@ -464,7 +521,7 @@ index ca9d91b..f3ebc5a 100644 } if (next->anon_vma) anon_vma_merge(vma, next); -@@ -1574,8 +1574,8 @@ out: +@@ -1727,8 +1727,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr, return addr; unmap_and_free_vma: @@ -474,7 +531,7 @@ index ca9d91b..f3ebc5a 100644 /* Undo any partial mapping done by a device driver. */ unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); -@@ -2380,7 +2380,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, +@@ -2533,7 +2533,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, goto out_free_mpol; if (new->vm_file) @@ -483,7 +540,7 @@ index ca9d91b..f3ebc5a 100644 if (new->vm_ops && new->vm_ops->open) new->vm_ops->open(new); -@@ -2399,7 +2399,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, +@@ -2552,7 +2552,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, if (new->vm_ops && new->vm_ops->close) new->vm_ops->close(new); if (new->vm_file) @@ -492,7 +549,7 @@ index ca9d91b..f3ebc5a 100644 unlink_anon_vmas(new); out_free_mpol: mpol_put(vma_policy(new)); -@@ -2550,7 +2550,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, +@@ -2703,7 +2703,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, struct vm_area_struct *vma; unsigned long populate = 0; unsigned long ret = -EINVAL; @@ -501,7 +558,7 @@ index ca9d91b..f3ebc5a 100644 pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/vm/remap_file_pages.txt.\n", current->comm, current->pid); -@@ -2625,10 +2625,27 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, +@@ -2778,10 +2778,27 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, } } @@ -530,7 +587,7 @@ index ca9d91b..f3ebc5a 100644 out: up_write(&mm->mmap_sem); if (populate) -@@ -2903,7 +2920,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, +@@ -3056,7 +3073,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, if (anon_vma_clone(new_vma, vma)) goto out_free_mempol; if (new_vma->vm_file) @@ -540,10 +597,10 @@ index ca9d91b..f3ebc5a 100644 new_vma->vm_ops->open(new_vma); vma_link(mm, new_vma, prev, rb_link, rb_parent); diff --git a/mm/nommu.c b/mm/nommu.c -index 95daf81..5086a29 100644 +index 8b8faaf..5d26ed94 100644 --- a/mm/nommu.c +++ b/mm/nommu.c -@@ -644,7 +644,7 @@ static void __put_nommu_region(struct vm_region *region) +@@ -636,7 +636,7 @@ static void __put_nommu_region(struct vm_region *region) up_write(&nommu_region_sem); if (region->vm_file) @@ -552,7 +609,7 @@ index 95daf81..5086a29 100644 /* IO memory and memory shared directly out of the pagecache * from ramfs/tmpfs mustn't be released here */ -@@ -802,7 +802,7 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma) +@@ -794,7 +794,7 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma) if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close(vma); if (vma->vm_file) @@ -561,7 +618,7 @@ index 95daf81..5086a29 100644 put_nommu_region(vma->vm_region); kmem_cache_free(vm_area_cachep, vma); } -@@ -1328,7 +1328,7 @@ unsigned long do_mmap(struct file *file, +@@ -1320,7 +1320,7 @@ unsigned long do_mmap(struct file *file, goto error_just_free; } } @@ -570,7 +627,7 @@ index 95daf81..5086a29 100644 kmem_cache_free(vm_region_jar, region); region = pregion; result = start; -@@ -1403,10 +1403,10 @@ error_just_free: +@@ -1395,10 +1395,10 @@ unsigned long do_mmap(struct file *file, up_write(&nommu_region_sem); error: if (region->vm_file) @@ -585,18 +642,18 @@ index 95daf81..5086a29 100644 diff --git a/mm/prfile.c b/mm/prfile.c new file mode 100644 -index 0000000..b323b8a +index 0000000..86e01bd --- /dev/null +++ b/mm/prfile.c -@@ -0,0 +1,86 @@ +@@ -0,0 +1,85 @@ +/* -+ * Mainly for aufs which mmap(2) diffrent file and wants to print different path -+ * in /proc/PID/maps. ++ * Mainly for aufs which mmap(2) different file and wants to print different ++ * path in /proc/PID/maps. + * Call these functions via macros defined in linux/mm.h. + * + * See Documentation/filesystems/aufs/design/06mmap.txt + * -+ * Copyright (c) 2014 Junjro R. Okajima ++ * Copyright (c) 2014-2018 Junjro R. Okajima + * Copyright (c) 2014 Ian Campbell + */ + @@ -610,8 +667,7 @@ index 0000000..b323b8a +{ +#ifdef PRFILE_TRACE + if (pr) -+ pr_info("%s:%d: %s, %s\n", func, line, func2, -+ f ? (char *)f->f_path.dentry->d_name.name : "(null)"); ++ pr_info("%s:%d: %s, %pD2\n", func, line, func2, f); +#endif +} + @@ -675,13 +731,13 @@ index 0000000..b323b8a + fput(pr); +} +#endif /* !CONFIG_MMU */ -aufs4.x-rcN standalone patch +aufs4.9 standalone patch diff --git a/fs/dcache.c b/fs/dcache.c -index df0268c..76280ee 100644 +index df0268c..755fea1 100644 --- a/fs/dcache.c +++ b/fs/dcache.c -@@ -1272,6 +1272,7 @@ rename_retry: +@@ -1272,6 +1272,7 @@ void d_walk(struct dentry *parent, void *data, seq = 1; goto again; } @@ -689,8 +745,16 @@ index df0268c..76280ee 100644 /* * Search for at least 1 mount point in the dentry's subdirs. +@@ -2855,6 +2856,7 @@ void d_exchange(struct dentry *dentry1, struct dentry *dentry2) + + write_sequnlock(&rename_lock); + } ++EXPORT_SYMBOL_GPL(d_exchange); + + /** + * d_ancestor - search for an ancestor diff --git a/fs/exec.c b/fs/exec.c -index 6fcfb3f..ed9d646 100644 +index 4e497b9..e27d323 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -104,6 +104,7 @@ bool path_noexec(const struct path *path) @@ -717,7 +781,7 @@ diff --git a/fs/file_table.c b/fs/file_table.c index ad17e05..ae9f267 100644 --- a/fs/file_table.c +++ b/fs/file_table.c -@@ -147,6 +147,7 @@ over: +@@ -147,6 +147,7 @@ struct file *get_empty_filp(void) } return ERR_PTR(-ENFILE); } @@ -750,10 +814,10 @@ index ad17e05..ae9f267 100644 void __init files_init(void) { diff --git a/fs/inode.c b/fs/inode.c -index 675fe84..da063d3 100644 +index 9a9ba3a..a3a18d83 100644 --- a/fs/inode.c +++ b/fs/inode.c -@@ -1602,6 +1602,7 @@ int update_time(struct inode *inode, struct timespec *time, int flags) +@@ -1651,6 +1651,7 @@ int update_time(struct inode *inode, struct timespec *time, int flags) return update_time(inode, time, flags); } @@ -762,10 +826,10 @@ index 675fe84..da063d3 100644 /** * touch_atime - update the access time diff --git a/fs/namespace.c b/fs/namespace.c -index 7bb2cda..88ec098 100644 +index db0b1ac..2d1e8ff 100644 --- a/fs/namespace.c +++ b/fs/namespace.c -@@ -463,6 +463,7 @@ void __mnt_drop_write(struct vfsmount *mnt) +@@ -466,6 +466,7 @@ void __mnt_drop_write(struct vfsmount *mnt) mnt_dec_writers(real_mount(mnt)); preempt_enable(); } @@ -773,7 +837,15 @@ index 7bb2cda..88ec098 100644 /** * mnt_drop_write - give up write access to a mount -@@ -1812,6 +1813,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, +@@ -792,6 +793,7 @@ int is_current_mnt_ns(struct vfsmount *mnt) + { + return check_mnt(real_mount(mnt)); + } ++EXPORT_SYMBOL_GPL(is_current_mnt_ns); + + /* + * vfsmount lock must be held for write +@@ -1829,6 +1831,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, } return 0; } @@ -782,7 +854,7 @@ index 7bb2cda..88ec098 100644 static void cleanup_group_ids(struct mount *mnt, struct mount *end) { diff --git a/fs/notify/group.c b/fs/notify/group.c -index b47f7cf..618bc9e 100644 +index fbe3cbe..bdfc61e 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -22,6 +22,7 @@ @@ -837,7 +909,7 @@ index d3fea0b..5fc06ad 100644 void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock) { -@@ -415,6 +417,7 @@ err: +@@ -415,6 +417,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark, return ret; } @@ -854,7 +926,7 @@ index d3fea0b..5fc06ad 100644 /* * Destroy all marks in destroy_list, waits for SRCU period to finish before diff --git a/fs/open.c b/fs/open.c -index 4fd6e25..ec6f532 100644 +index d3ed817..20d2494 100644 --- a/fs/open.c +++ b/fs/open.c @@ -64,6 +64,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, @@ -865,7 +937,7 @@ index 4fd6e25..ec6f532 100644 long vfs_truncate(const struct path *path, loff_t length) { -@@ -678,6 +679,7 @@ int open_check_o_direct(struct file *f) +@@ -695,6 +696,7 @@ int open_check_o_direct(struct file *f) } return 0; } @@ -874,7 +946,7 @@ index 4fd6e25..ec6f532 100644 static int do_dentry_open(struct file *f, struct inode *inode, diff --git a/fs/read_write.c b/fs/read_write.c -index a1da117..c643215 100644 +index 4052813..7dfd732 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -525,6 +525,7 @@ vfs_readf_t vfs_readf(struct file *file) @@ -894,10 +966,10 @@ index a1da117..c643215 100644 ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) { diff --git a/fs/splice.c b/fs/splice.c -index 9326c2a..0606690 100644 +index 28160a7..98c1902 100644 --- a/fs/splice.c +++ b/fs/splice.c -@@ -1124,6 +1124,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +@@ -868,6 +868,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out, return splice_write(pipe, out, ppos, len, flags); } @@ -905,7 +977,7 @@ index 9326c2a..0606690 100644 /* * Attempt to initiate a splice from a file to a pipe. -@@ -1153,6 +1154,7 @@ long do_splice_to(struct file *in, loff_t *ppos, +@@ -897,6 +898,7 @@ long do_splice_to(struct file *in, loff_t *ppos, return splice_read(in, ppos, pipe, len, flags); } @@ -913,18 +985,30 @@ index 9326c2a..0606690 100644 /** * splice_direct_to_actor - splices data directly between two non-pipes +diff --git a/fs/sync.c b/fs/sync.c +index 7a5fa3f..c9b9d46 100644 +--- a/fs/sync.c ++++ b/fs/sync.c +@@ -38,6 +38,7 @@ int __sync_filesystem(struct super_block *sb, int wait) + sb->s_op->sync_fs(sb, wait); + return __sync_blockdev(sb->s_bdev, wait); + } ++EXPORT_SYMBOL_GPL(__sync_filesystem); + + /* + * Write out and wait upon all dirty data associated with this diff --git a/fs/xattr.c b/fs/xattr.c -index c243905..b60dc60 100644 +index 2d13b4e..41c2bcd 100644 --- a/fs/xattr.c +++ b/fs/xattr.c -@@ -214,6 +214,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, +@@ -296,6 +296,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, *xattr_value = value; return error; } +EXPORT_SYMBOL_GPL(vfs_getxattr_alloc); ssize_t - vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) + __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name, diff --git a/kernel/task_work.c b/kernel/task_work.c index d513051..e056d54 100644 --- a/kernel/task_work.c @@ -935,10 +1019,10 @@ index d513051..e056d54 100644 } +EXPORT_SYMBOL_GPL(task_work_run); diff --git a/security/commoncap.c b/security/commoncap.c -index 14540bd..4e3b242 100644 +index 8df676f..6b5cc07 100644 --- a/security/commoncap.c +++ b/security/commoncap.c -@@ -1066,12 +1066,14 @@ int cap_mmap_addr(unsigned long addr) +@@ -1061,12 +1061,14 @@ int cap_mmap_addr(unsigned long addr) } return ret; } @@ -974,10 +1058,10 @@ index 03c1652..f88c84b 100644 int devcgroup_inode_mknod(int mode, dev_t dev) { diff --git a/security/security.c b/security/security.c -index 4838e7f..36c741e 100644 +index f825304..8dd441d 100644 --- a/security/security.c +++ b/security/security.c -@@ -434,6 +434,7 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry) +@@ -443,6 +443,7 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry) return 0; return call_int_hook(path_rmdir, 0, dir, dentry); } @@ -985,7 +1069,7 @@ index 4838e7f..36c741e 100644 int security_path_unlink(const struct path *dir, struct dentry *dentry) { -@@ -450,6 +451,7 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry, +@@ -459,6 +460,7 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry, return 0; return call_int_hook(path_symlink, 0, dir, dentry, old_name); } @@ -993,7 +1077,7 @@ index 4838e7f..36c741e 100644 int security_path_link(struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) -@@ -458,6 +460,7 @@ int security_path_link(struct dentry *old_dentry, const struct path *new_dir, +@@ -467,6 +469,7 @@ int security_path_link(struct dentry *old_dentry, const struct path *new_dir, return 0; return call_int_hook(path_link, 0, old_dentry, new_dir, new_dentry); } @@ -1001,7 +1085,7 @@ index 4838e7f..36c741e 100644 int security_path_rename(const struct path *old_dir, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry, -@@ -485,6 +488,7 @@ int security_path_truncate(const struct path *path) +@@ -494,6 +497,7 @@ int security_path_truncate(const struct path *path) return 0; return call_int_hook(path_truncate, 0, path); } @@ -1009,7 +1093,7 @@ index 4838e7f..36c741e 100644 int security_path_chmod(const struct path *path, umode_t mode) { -@@ -492,6 +496,7 @@ int security_path_chmod(const struct path *path, umode_t mode) +@@ -501,6 +505,7 @@ int security_path_chmod(const struct path *path, umode_t mode) return 0; return call_int_hook(path_chmod, 0, path, mode); } @@ -1017,7 +1101,7 @@ index 4838e7f..36c741e 100644 int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) { -@@ -499,6 +504,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) +@@ -508,6 +513,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) return 0; return call_int_hook(path_chown, 0, path, uid, gid); } @@ -1025,7 +1109,7 @@ index 4838e7f..36c741e 100644 int security_path_chroot(const struct path *path) { -@@ -584,6 +590,7 @@ int security_inode_readlink(struct dentry *dentry) +@@ -593,6 +599,7 @@ int security_inode_readlink(struct dentry *dentry) return 0; return call_int_hook(inode_readlink, 0, dentry); } @@ -1033,7 +1117,7 @@ index 4838e7f..36c741e 100644 int security_inode_follow_link(struct dentry *dentry, struct inode *inode, bool rcu) -@@ -599,6 +606,7 @@ int security_inode_permission(struct inode *inode, int mask) +@@ -608,6 +615,7 @@ int security_inode_permission(struct inode *inode, int mask) return 0; return call_int_hook(inode_permission, 0, inode, mask); } @@ -1041,7 +1125,7 @@ index 4838e7f..36c741e 100644 int security_inode_setattr(struct dentry *dentry, struct iattr *attr) { -@@ -758,6 +766,7 @@ int security_file_permission(struct file *file, int mask) +@@ -779,6 +787,7 @@ int security_file_permission(struct file *file, int mask) return fsnotify_perm(file, mask); } @@ -1049,7 +1133,7 @@ index 4838e7f..36c741e 100644 int security_file_alloc(struct file *file) { -@@ -817,6 +826,7 @@ int security_mmap_file(struct file *file, unsigned long prot, +@@ -838,6 +847,7 @@ int security_mmap_file(struct file *file, unsigned long prot, return ret; return ima_file_mmap(file, prot); } @@ -1059,7 +1143,7 @@ index 4838e7f..36c741e 100644 { diff -urN /usr/share/empty/Documentation/ABI/testing/debugfs-aufs linux/Documentation/ABI/testing/debugfs-aufs --- /usr/share/empty/Documentation/ABI/testing/debugfs-aufs 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/ABI/testing/debugfs-aufs 2016-10-09 16:55:36.476034536 +0200 ++++ linux/Documentation/ABI/testing/debugfs-aufs 2017-07-29 12:14:25.893041746 +0200 @@ -0,0 +1,50 @@ +What: /debug/aufs/si_/ +Date: March 2009 @@ -1113,7 +1197,7 @@ diff -urN /usr/share/empty/Documentation/ABI/testing/debugfs-aufs linux/Document + will be empty. About XINO files, see the aufs manual. diff -urN /usr/share/empty/Documentation/ABI/testing/sysfs-aufs linux/Documentation/ABI/testing/sysfs-aufs --- /usr/share/empty/Documentation/ABI/testing/sysfs-aufs 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/ABI/testing/sysfs-aufs 2016-10-09 16:55:36.476034536 +0200 ++++ linux/Documentation/ABI/testing/sysfs-aufs 2017-07-29 12:14:25.893041746 +0200 @@ -0,0 +1,31 @@ +What: /sys/fs/aufs/si_/ +Date: March 2009 @@ -1148,10 +1232,10 @@ diff -urN /usr/share/empty/Documentation/ABI/testing/sysfs-aufs linux/Documentat + will be empty. About XINO files, see the aufs manual. diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt linux/Documentation/filesystems/aufs/design/01intro.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/01intro.txt 2016-10-09 16:55:36.479367956 +0200 -@@ -0,0 +1,170 @@ ++++ linux/Documentation/filesystems/aufs/design/01intro.txt 2018-04-15 08:49:13.394483860 +0200 +@@ -0,0 +1,171 @@ + -+# Copyright (C) 2005-2016 Junjiro R. Okajima ++# Copyright (C) 2005-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -1169,12 +1253,13 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt lin +Introduction +---------------------------------------- + -+aufs [ei ju: ef es] | [a u f s] ++aufs [ei ju: ef es] | /ey-yoo-ef-es/ | [a u f s] +1. abbrev. for "advanced multi-layered unification filesystem". +2. abbrev. for "another unionfs". +3. abbrev. for "auf das" in German which means "on the" in English. + Ex. "Butter aufs Brot"(G) means "butter onto bread"(E). + But "Filesystem aufs Filesystem" is hard to understand. ++4. abbrev. for "African Urban Fashion Show". + +AUFS is a filesystem with features: +- multi layered stackable unification filesystem, the member directory @@ -1322,10 +1407,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt lin +about it. But currently I have implemented it in kernel space. diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt linux/Documentation/filesystems/aufs/design/02struct.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/02struct.txt 2016-10-09 16:55:36.479367956 +0200 ++++ linux/Documentation/filesystems/aufs/design/02struct.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,258 @@ + -+# Copyright (C) 2005-2016 Junjiro R. Okajima ++# Copyright (C) 2005-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -1584,10 +1669,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li +For this purpose, use "aumvdown" command in aufs-util.git. diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03atomic_open.txt linux/Documentation/filesystems/aufs/design/03atomic_open.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/03atomic_open.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/03atomic_open.txt 2016-10-09 16:55:36.479367956 +0200 ++++ linux/Documentation/filesystems/aufs/design/03atomic_open.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,85 @@ + -+# Copyright (C) 2015-2016 Junjiro R. Okajima ++# Copyright (C) 2015-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -1673,10 +1758,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03atomic_open.t + be implemented in aufs, but not all I am afraid. diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03lookup.txt linux/Documentation/filesystems/aufs/design/03lookup.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/03lookup.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/03lookup.txt 2016-10-09 16:55:36.479367956 +0200 ++++ linux/Documentation/filesystems/aufs/design/03lookup.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,113 @@ + -+# Copyright (C) 2005-2016 Junjiro R. Okajima ++# Copyright (C) 2005-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -1790,10 +1875,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03lookup.txt li + by over-mounting something (or another method). diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/04branch.txt linux/Documentation/filesystems/aufs/design/04branch.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/04branch.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/04branch.txt 2016-10-09 16:55:36.482701377 +0200 ++++ linux/Documentation/filesystems/aufs/design/04branch.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,74 @@ + -+# Copyright (C) 2005-2016 Junjiro R. Okajima ++# Copyright (C) 2005-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -1868,10 +1953,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/04branch.txt li + same named entry on the upper branch. diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/05wbr_policy.txt linux/Documentation/filesystems/aufs/design/05wbr_policy.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/05wbr_policy.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/05wbr_policy.txt 2016-10-09 16:55:36.482701377 +0200 ++++ linux/Documentation/filesystems/aufs/design/05wbr_policy.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,64 @@ + -+# Copyright (C) 2005-2016 Junjiro R. Okajima ++# Copyright (C) 2005-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -1934,12 +2019,153 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/05wbr_policy.tx + where the source and the target exists and selects the higher + one. If the selected branch is readonly, then aufs follows the + copyup policy. +diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06dirren.dot linux/Documentation/filesystems/aufs/design/06dirren.dot +--- /usr/share/empty/Documentation/filesystems/aufs/design/06dirren.dot 1970-01-01 01:00:00.000000000 +0100 ++++ linux/Documentation/filesystems/aufs/design/06dirren.dot 2018-04-15 08:49:13.394483860 +0200 +@@ -0,0 +1,31 @@ ++ ++// to view this graph, run dot(1) command in GRAPHVIZ. ++ ++digraph G { ++node [shape=box]; ++whinfo [label="detailed info file\n(lower_brid_root-hinum, h_inum, namelen, old name)"]; ++ ++node [shape=oval]; ++ ++aufs_rename -> whinfo [label="store/remove"]; ++ ++node [shape=oval]; ++inode_list [label="h_inum list in branch\ncache"]; ++ ++node [shape=box]; ++whinode [label="h_inum list file"]; ++ ++node [shape=oval]; ++brmgmt [label="br_add/del/mod/umount"]; ++ ++brmgmt -> inode_list [label="create/remove"]; ++brmgmt -> whinode [label="load/store"]; ++ ++inode_list -> whinode [style=dashed,dir=both]; ++ ++aufs_rename -> inode_list [label="add/del"]; ++ ++aufs_lookup -> inode_list [label="search"]; ++ ++aufs_lookup -> whinfo [label="load/remove"]; ++} +diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06dirren.txt linux/Documentation/filesystems/aufs/design/06dirren.txt +--- /usr/share/empty/Documentation/filesystems/aufs/design/06dirren.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux/Documentation/filesystems/aufs/design/06dirren.txt 2018-04-15 08:49:13.394483860 +0200 +@@ -0,0 +1,102 @@ ++ ++# Copyright (C) 2017-2018 Junjiro R. Okajima ++# ++# This program 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, see . ++ ++Special handling for renaming a directory (DIRREN) ++---------------------------------------------------------------------- ++First, let's assume we have a simple usecase. ++ ++- /u = /rw + /ro ++- /rw/dirA exists ++- /ro/dirA and /ro/dirA/file exist too ++- there is no dirB on both branches ++- a user issues rename("dirA", "dirB") ++ ++Now, what should aufs behave against this rename(2)? ++There are a few possible cases. ++ ++A. returns EROFS. ++ since dirA exists on a readonly branch which cannot be renamed. ++B. returns EXDEV. ++ it is possible to copy-up dirA (only the dir itself), but the child ++ entries ("file" in this case) should not be. it must be a bad ++ approach to copy-up recursively. ++C. returns a success. ++ even the branch /ro is readonly, aufs tries renaming it. Obviously it ++ is a violation of aufs' policy. ++D. construct an extra information which indicates that /ro/dirA should ++ be handled as the name of dirB. ++ overlayfs has a similar feature called REDIRECT. ++ ++Until now, aufs implements the case B only which returns EXDEV, and ++expects the userspace application behaves like mv(1) which tries ++issueing rename(2) recursively. ++ ++A new aufs feature called DIRREN is introduced which implements the case ++D. There are several "extra information" added. ++ ++1. detailed info per renamed directory ++ path: /rw/dirB/$AUFS_WH_DR_INFO_PFX. ++2. the inode-number list of directories on a branch ++ path: /rw/dirB/$AUFS_WH_DR_BRHINO ++ ++The filename of "detailed info per directory" represents the lower ++branch, and its format is ++- a type of the branch id ++ one of these. ++ + uuid (not implemented yet) ++ + fsid ++ + dev ++- the inode-number of the branch root dir ++ ++And it contains these info in a single regular file. ++- magic number ++- branch's inode-number of the logically renamed dir ++- the name of the before-renamed dir ++ ++The "detailed info per directory" file is created in aufs rename(2), and ++loaded in any lookup. ++The info is considered in lookup for the matching case only. Here ++"matching" means that the root of branch (in the info filename) is same ++to the current looking-up branch. After looking-up the before-renamed ++name, the inode-number is compared. And the matched dentry is used. ++ ++The "inode-number list of directories" is a regular file which contains ++simply the inode-numbers on the branch. The file is created or updated ++in removing the branch, and loaded in adding the branch. Its lifetime is ++equal to the branch. ++The list is refered in lookup, and when the current target inode is ++found in the list, the aufs tries loading the "detailed info per ++directory" and get the changed and valid name of the dir. ++ ++Theoretically these "extra informaiton" may be able to be put into XATTR ++in the dir inode. But aufs doesn't choose this way because ++1. XATTR may not be supported by the branch (or its configuration) ++2. XATTR may have its size limit. ++3. XATTR may be less easy to convert than a regular file, when the ++ format of the info is changed in the future. ++At the same time, I agree that the regular file approach is much slower ++than XATTR approach. So, in the future, aufs may take the XATTR or other ++better approach. ++ ++This DIRREN feature is enabled by aufs configuration, and is activated ++by a new mount option. ++ ++For the more complicated case, there is a work with UDBA option, which ++is to dected the direct access to the branches (by-passing aufs) and to ++maintain the cashes in aufs. Since a single cached aufs dentry may ++contains two names, before- and after-rename, the name comparision in ++UDBA handler may not work correctly. In this case, the behaviour will be ++equivalen to udba=reval case. diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06fhsm.txt linux/Documentation/filesystems/aufs/design/06fhsm.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/06fhsm.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/06fhsm.txt 2016-10-09 16:55:36.482701377 +0200 ++++ linux/Documentation/filesystems/aufs/design/06fhsm.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,120 @@ + -+# Copyright (C) 2011-2016 Junjiro R. Okajima ++# Copyright (C) 2011-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -2060,10 +2286,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06fhsm.txt linu +should restore the original file state after an error happens. diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06mmap.txt linux/Documentation/filesystems/aufs/design/06mmap.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/06mmap.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/06mmap.txt 2016-10-09 16:55:36.482701377 +0200 ++++ linux/Documentation/filesystems/aufs/design/06mmap.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,72 @@ + -+# Copyright (C) 2005-2016 Junjiro R. Okajima ++# Copyright (C) 2005-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -2136,10 +2362,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06mmap.txt linu +I have to give up this "looks-smater" approach. diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06xattr.txt linux/Documentation/filesystems/aufs/design/06xattr.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/06xattr.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/06xattr.txt 2016-10-09 16:55:36.482701377 +0200 ++++ linux/Documentation/filesystems/aufs/design/06xattr.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,96 @@ + -+# Copyright (C) 2014-2016 Junjiro R. Okajima ++# Copyright (C) 2014-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -2236,10 +2462,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06xattr.txt lin +now, aufs implements the branch attributes to ignore the error. diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/07export.txt linux/Documentation/filesystems/aufs/design/07export.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/07export.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/07export.txt 2016-10-09 16:55:36.482701377 +0200 ++++ linux/Documentation/filesystems/aufs/design/07export.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,58 @@ + -+# Copyright (C) 2005-2016 Junjiro R. Okajima ++# Copyright (C) 2005-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -2298,10 +2524,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/07export.txt li + lookup_one_len(), vfs_getattr(), encode_fh() and others. diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/08shwh.txt linux/Documentation/filesystems/aufs/design/08shwh.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/08shwh.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/08shwh.txt 2016-10-09 16:55:36.482701377 +0200 ++++ linux/Documentation/filesystems/aufs/design/08shwh.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,52 @@ + -+# Copyright (C) 2005-2016 Junjiro R. Okajima ++# Copyright (C) 2005-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -2354,10 +2580,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/08shwh.txt linu +initramfs will use it to replace the old one at the next boot. diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/10dynop.txt linux/Documentation/filesystems/aufs/design/10dynop.txt --- /usr/share/empty/Documentation/filesystems/aufs/design/10dynop.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/design/10dynop.txt 2016-10-09 16:55:36.482701377 +0200 ++++ linux/Documentation/filesystems/aufs/design/10dynop.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,47 @@ + -+# Copyright (C) 2010-2016 Junjiro R. Okajima ++# Copyright (C) 2010-2018 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by @@ -2405,8 +2631,8 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/10dynop.txt lin +regular files only. diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documentation/filesystems/aufs/README --- /usr/share/empty/Documentation/filesystems/aufs/README 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/filesystems/aufs/README 2016-10-09 16:55:36.479367956 +0200 -@@ -0,0 +1,392 @@ ++++ linux/Documentation/filesystems/aufs/README 2017-07-29 12:14:25.893041746 +0200 +@@ -0,0 +1,393 @@ + +Aufs4 -- advanced multi layered unification filesystem version 4.x +http://aufs.sf.net @@ -2782,6 +3008,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta +Daniel Epellei made a donation (2015/1). +OmegaPhil made a donation (2016/1). +Tomasz Szewczyk made a donation (2016/4). ++James Burry made a donation (2016/12). + +Thank you very much. +Donations are always, including future donations, very important and @@ -2801,10 +3028,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta +# End: ; diff -urN /usr/share/empty/fs/aufs/aufs.h linux/fs/aufs/aufs.h --- /usr/share/empty/fs/aufs/aufs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/aufs.h 2016-10-09 16:55:36.486034798 +0200 -@@ -0,0 +1,59 @@ ++++ linux/fs/aufs/aufs.h 2018-04-15 08:49:13.394483860 +0200 +@@ -0,0 +1,60 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -2845,15 +3072,16 @@ diff -urN /usr/share/empty/fs/aufs/aufs.h linux/fs/aufs/aufs.h +#include "dbgaufs.h" +#include "dentry.h" +#include "dir.h" ++#include "dirren.h" +#include "dynop.h" +#include "file.h" +#include "fstype.h" ++#include "hbl.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" @@ -2864,10 +3092,10 @@ diff -urN /usr/share/empty/fs/aufs/aufs.h linux/fs/aufs/aufs.h +#endif /* __AUFS_H__ */ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c --- /usr/share/empty/fs/aufs/branch.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/branch.c 2016-10-09 16:55:38.886097714 +0200 -@@ -0,0 +1,1412 @@ ++++ linux/fs/aufs/branch.c 2018-04-15 08:49:13.394483860 +0200 +@@ -0,0 +1,1432 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -2901,10 +3129,14 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + struct au_dykey **key; + + au_hnotify_fin_br(br); ++ /* always, regardless the mount option */ ++ au_dr_hino_free(&br->br_dirren); + + if (br->br_xino.xi_file) + fput(br->br_xino.xi_file); -+ mutex_destroy(&br->br_xino.xi_nondir_mtx); ++ for (i = br->br_xino.xi_nondir.total - 1; i >= 0; i--) ++ AuDebugOn(br->br_xino.xi_nondir.array[i]); ++ kfree(br->br_xino.xi_nondir.array); + + AuDebugOn(au_br_count(br)); + au_br_count_fin(br); @@ -2919,7 +3151,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + + if (br->br_fhsm) { + au_br_fhsm_fin(br->br_fhsm); -+ au_delayed_kfree(br->br_fhsm); ++ kfree(br->br_fhsm); + } + + key = br->br_dykey; @@ -2933,9 +3165,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + lockdep_off(); + path_put(&br->br_path); + lockdep_on(); -+ if (wbr) -+ au_delayed_kfree(wbr); -+ au_delayed_kfree(br); ++ kfree(wbr); ++ kfree(br); +} + +/* @@ -2999,14 +3230,19 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + int err; + + err = -ENOMEM; -+ root = sb->s_root; + add_branch = kzalloc(sizeof(*add_branch), GFP_NOFS); + if (unlikely(!add_branch)) + goto out; ++ add_branch->br_xino.xi_nondir.total = 8; /* initial size */ ++ add_branch->br_xino.xi_nondir.array ++ = kcalloc(add_branch->br_xino.xi_nondir.total, sizeof(ino_t), ++ GFP_NOFS); ++ if (unlikely(!add_branch->br_xino.xi_nondir.array)) ++ goto out_br; + + err = au_hnotify_init_br(add_branch, perm); + if (unlikely(err)) -+ goto out_br; ++ goto out_xinondir; + + if (au_br_writable(perm)) { + /* may be freed separately at changing the branch permission */ @@ -3022,23 +3258,26 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + goto out_wbr; + } + ++ root = sb->s_root; + err = au_sbr_realloc(au_sbi(sb), new_nbranch, /*may_shrink*/0); + if (!err) + err = au_di_realloc(au_di(root), new_nbranch, /*may_shrink*/0); + if (!err) { + inode = d_inode(root); -+ err = au_hinode_realloc(au_ii(inode), new_nbranch, /*may_shrink*/0); ++ err = au_hinode_realloc(au_ii(inode), new_nbranch, ++ /*may_shrink*/0); + } + if (!err) + return add_branch; /* success */ + +out_wbr: -+ if (add_branch->br_wbr) -+ au_delayed_kfree(add_branch->br_wbr); ++ kfree(add_branch->br_wbr); +out_hnotify: + au_hnotify_fin_br(add_branch); ++out_xinondir: ++ kfree(add_branch->br_xino.xi_nondir.array); +out_br: -+ au_delayed_kfree(add_branch); ++ kfree(add_branch); +out: + return ERR_PTR(err); +} @@ -3204,7 +3443,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + br->br_perm = old_perm; + + if (!err && wbr && !au_br_writable(new_perm)) { -+ au_delayed_kfree(wbr); ++ kfree(wbr); + br->br_wbr = NULL; + } + @@ -3250,7 +3489,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + struct inode *h_inode; + + err = 0; -+ mutex_init(&br->br_xino.xi_nondir_mtx); ++ spin_lock_init(&br->br_xino.xi_nondir.spin); ++ init_waitqueue_head(&br->br_xino.xi_nondir.wqh); + br->br_perm = add->perm; + br->br_path = add->path; /* set first, path_get() later */ + spin_lock_init(&br->br_dykey_lock); @@ -3259,6 +3499,11 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + br->br_id = au_new_br_id(sb); + AuDebugOn(br->br_id < 0); + ++ /* always, regardless the given option */ ++ err = au_dr_br_init(sb, br, &add->path); ++ if (unlikely(err)) ++ goto out_err; ++ + if (au_br_writable(add->perm)) { + err = au_wbr_init(br, sb, add->perm); + if (unlikely(err)) @@ -3425,14 +3670,15 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +{ + unsigned long long n; + struct file **p, *f; -+ struct au_sphlhead *files; ++ struct hlist_bl_head *files; ++ struct hlist_bl_node *pos; + struct au_finfo *finfo; + + n = 0; + p = a; + files = &au_sbi(sb)->si_files; -+ spin_lock(&files->spin); -+ hlist_for_each_entry(finfo, &files->head, fi_hlist) { ++ hlist_bl_lock(files); ++ hlist_bl_for_each_entry(finfo, pos, files, fi_hlist) { + f = finfo->fi_file; + if (file_count(f) + && !special_file(file_inode(f)->i_mode)) { @@ -3442,7 +3688,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + AuDebugOn(n > max); + } + } -+ spin_unlock(&files->spin); ++ hlist_bl_unlock(files); + + return n; +} @@ -3845,6 +4091,9 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + au_br_do_del_hip(au_ii(inode), bindex, bbot); + au_sbilist_unlock(); + ++ /* ignore an error */ ++ au_dr_br_fin(sb, br); /* always, regardless the mount option */ ++ + dput(h_root); + iput(h_inode); + au_br_do_free(br); @@ -4230,7 +4479,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + if (br->br_wbr) { + err = au_wbr_init(br, sb, mod->perm); + if (unlikely(err)) { -+ au_delayed_kfree(br->br_wbr); ++ kfree(br->br_wbr); + br->br_wbr = NULL; + } + } @@ -4242,7 +4491,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + if (!au_br_fhsm(mod->perm)) { + /* fhsm --> non-fhsm */ + au_br_fhsm_fin(br->br_fhsm); -+ au_delayed_kfree(br->br_fhsm); ++ kfree(br->br_fhsm); + br->br_fhsm = NULL; + } + } else if (au_br_fhsm(mod->perm)) @@ -4254,8 +4503,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + goto out; /* success */ + +out_bf: -+ if (bf) -+ au_delayed_kfree(bf); ++ kfree(bf); +out: + AuTraceErr(err); + return err; @@ -4280,10 +4528,10 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +} diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h --- /usr/share/empty/fs/aufs/branch.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/branch.h 2016-10-09 16:55:36.486034798 +0200 -@@ -0,0 +1,309 @@ ++++ linux/fs/aufs/branch.h 2018-04-15 08:49:13.394483860 +0200 +@@ -0,0 +1,324 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -4309,6 +4557,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h +#ifdef __KERNEL__ + +#include ++#include "dirren.h" +#include "dynop.h" +#include "rwsem.h" +#include "super.h" @@ -4318,7 +4567,14 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h +/* a xino file */ +struct au_xino_file { + struct file *xi_file; -+ struct mutex xi_nondir_mtx; ++ struct { ++ spinlock_t spin; ++ ino_t *array; ++ int total; ++ /* reserved for future use */ ++ /* unsigned long *bitmap; */ ++ wait_queue_head_t wqh; ++ } xi_nondir; + + /* todo: make xino files an array to support huge inode number */ + @@ -4399,6 +4655,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h + /* entries under sysfs per mount-point */ + struct au_brsysfs br_sysfs[AuBrSysfs_Last]; +#endif ++ ++ struct au_dr_br br_dirren; +}; + +/* ---------------------------------------------------------------------- */ @@ -4517,6 +4775,11 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h +struct file *au_xino_def(struct super_block *sb); +int au_xino_path(struct seq_file *seq, struct file *file); + ++void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex, ++ ino_t h_ino, int idx); ++int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ int *idx); ++ +/* ---------------------------------------------------------------------- */ + +/* Superblock to branch */ @@ -4593,8 +4856,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h +#endif /* __AUFS_BRANCH_H__ */ diff -urN /usr/share/empty/fs/aufs/conf.mk linux/fs/aufs/conf.mk --- /usr/share/empty/fs/aufs/conf.mk 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/conf.mk 2016-10-09 16:55:36.486034798 +0200 -@@ -0,0 +1,38 @@ ++++ linux/fs/aufs/conf.mk 2018-04-15 08:49:13.394483860 +0200 +@@ -0,0 +1,39 @@ + +AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS} + @@ -4611,6 +4874,7 @@ diff -urN /usr/share/empty/fs/aufs/conf.mk linux/fs/aufs/conf.mk + XATTR \ + FHSM \ + RDU \ ++ DIRREN \ + SHWH \ + BR_RAMFS \ + BR_FUSE POLL \ @@ -4635,10 +4899,10 @@ diff -urN /usr/share/empty/fs/aufs/conf.mk linux/fs/aufs/conf.mk +-include ${srctree}/${src}/conf_priv.mk diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c --- /usr/share/empty/fs/aufs/cpup.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/cpup.c 2016-10-09 16:55:38.886097714 +0200 -@@ -0,0 +1,1391 @@ ++++ linux/fs/aufs/cpup.c 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,1414 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -4996,9 +5260,9 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + dst->f_pos = 0; + err = au_do_copy_file(dst, src, len, buf, blksize); + if (do_kfree) -+ au_delayed_kfree(buf); ++ kfree(buf); + else -+ au_delayed_free_page((unsigned long)buf); ++ free_page((unsigned long)buf); + +out: + return err; @@ -5032,7 +5296,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + .label = &&out_src + } + }; -+ struct super_block *sb; ++ struct super_block *sb, *h_src_sb; + struct inode *h_src_inode; + struct task_struct *tsk = current; + @@ -5050,16 +5314,35 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + + /* try stopping to update while we copyup */ + h_src_inode = d_inode(file[SRC].dentry); -+ if (!au_test_nfs(h_src_inode->i_sb)) ++ h_src_sb = h_src_inode->i_sb; ++ if (!au_test_nfs(h_src_sb)) + IMustLock(h_src_inode); -+ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len); ++ ++ if (h_src_sb != file_inode(file[DST].file)->i_sb ++ || !file[DST].file->f_op->clone_file_range) ++ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len); ++ else { ++ if (!au_test_nfs(h_src_sb)) { ++ inode_unlock_shared(h_src_inode); ++ err = vfsub_clone_file_range(file[SRC].file, ++ file[DST].file, cpg->len); ++ vfsub_inode_lock_shared_nested(h_src_inode, ++ AuLsc_I_CHILD); ++ } else ++ err = vfsub_clone_file_range(file[SRC].file, ++ file[DST].file, cpg->len); ++ if (unlikely(err == -EOPNOTSUPP && au_test_nfs(h_src_sb))) ++ /* the backend fs on NFS may not support cloning */ ++ err = au_copy_file(file[DST].file, file[SRC].file, ++ cpg->len); ++ AuTraceErr(err); ++ } + + /* i wonder if we had O_NO_DELAY_FPUT flag */ + if (tsk->flags & PF_KTHREAD) + __fput_sync(file[DST].file); + else { -+ WARN(1, "%pD\nPlease report this warning to aufs-users ML", -+ file[DST].file); ++ /* it happend actually */ + fput(file[DST].file); + /* + * too bad. @@ -5093,7 +5376,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + cpg->len = l; + if (cpg->len) { + /* try stopping to update while we are referencing */ -+ inode_lock_nested(h_src_inode, AuLsc_I_CHILD); ++ vfsub_inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); + au_pin_hdir_unlock(cpg->pin); + + h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc); @@ -5102,20 +5385,21 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + if (!au_test_nfs(h_src_inode->i_sb)) + err = vfs_getattr(&h_path, &h_src_attr->st); + else { -+ inode_unlock(h_src_inode); ++ inode_unlock_shared(h_src_inode); + err = vfs_getattr(&h_path, &h_src_attr->st); -+ inode_lock_nested(h_src_inode, AuLsc_I_CHILD); ++ vfsub_inode_lock_shared_nested(h_src_inode, ++ AuLsc_I_CHILD); + } + if (unlikely(err)) { -+ inode_unlock(h_src_inode); ++ inode_unlock_shared(h_src_inode); + goto out; + } + h_src_attr->valid = 1; + if (!au_test_nfs(h_src_inode->i_sb)) { + err = au_cp_regular(cpg); -+ inode_unlock(h_src_inode); ++ inode_unlock_shared(h_src_inode); + } else { -+ inode_unlock(h_src_inode); ++ inode_unlock_shared(h_src_inode); + err = au_cp_regular(cpg); + } + rerr = au_pin_hdir_relock(cpg->pin); @@ -5166,7 +5450,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + sym.k[symlen] = 0; + err = vfsub_symlink(h_dir, h_path, sym.k); + } -+ au_delayed_free_page((unsigned long)sym.k); ++ free_page((unsigned long)sym.k); + +out: + return err; @@ -5358,7 +5642,8 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + IMustLock(h_dir); + AuDbg("%pd %pd\n", h_dentry, h_path->dentry); + /* no delegation since it is just created */ -+ err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL); ++ err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL, ++ /*flags*/0); + dput(h_path->dentry); + +out: @@ -5371,6 +5656,8 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + * @len is for truncating when it is -1 copyup the entire file. + * in link/rename cases, @dst_parent may be different from the real one. + * basic->bsrc can be larger than basic->bdst. ++ * aufs doesn't touch the credential so ++ * security_inode_copy_up{,_xattr}() are unnecrssary. + */ +static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) +{ @@ -5537,7 +5824,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + } +out_parent: + dput(dst_parent); -+ au_delayed_kfree(a); ++ kfree(a); +out: + return err; +} @@ -6030,10 +6317,10 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c +} diff -urN /usr/share/empty/fs/aufs/cpup.h linux/fs/aufs/cpup.h --- /usr/share/empty/fs/aufs/cpup.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/cpup.h 2016-10-09 16:55:36.486034798 +0200 -@@ -0,0 +1,94 @@ ++++ linux/fs/aufs/cpup.h 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,99 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -6093,6 +6380,11 @@ diff -urN /usr/share/empty/fs/aufs/cpup.h linux/fs/aufs/cpup.h +#define AuCpup_RWDST (1 << 5) /* force write target even if + the branch is marked as RO */ + ++#ifndef CONFIG_AUFS_BR_HFSPLUS ++#undef AuCpup_HOPEN ++#define AuCpup_HOPEN 0 ++#endif ++ +#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) +#define au_fset_cpup(flags, name) \ + do { (flags) |= AuCpup_##name; } while (0) @@ -6128,10 +6420,10 @@ diff -urN /usr/share/empty/fs/aufs/cpup.h linux/fs/aufs/cpup.h +#endif /* __AUFS_CPUP_H__ */ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c --- /usr/share/empty/fs/aufs/dbgaufs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/dbgaufs.c 2016-10-09 16:55:38.886097714 +0200 -@@ -0,0 +1,438 @@ ++++ linux/fs/aufs/dbgaufs.c 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,437 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -6173,7 +6465,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c +static int dbgaufs_xi_release(struct inode *inode __maybe_unused, + struct file *file) +{ -+ au_delayed_kfree(file->private_data); ++ kfree(file->private_data); + return 0; +} + @@ -6235,7 +6527,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c +static int dbgaufs_plink_release(struct inode *inode __maybe_unused, + struct file *file) +{ -+ au_delayed_free_page((unsigned long)file->private_data); ++ free_page((unsigned long)file->private_data); + return 0; +} + @@ -6246,7 +6538,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c + struct dbgaufs_plink_arg *p; + struct au_sbinfo *sbinfo; + struct super_block *sb; -+ struct au_sphlhead *sphl; ++ struct hlist_bl_head *hbl; + + err = -ENOMEM; + p = (void *)get_zeroed_page(GFP_NOFS); @@ -6266,10 +6558,9 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c + limit -= n; + + sum = 0; -+ for (i = 0, sphl = sbinfo->si_plink; -+ i < AuPlink_NHASH; -+ i++, sphl++) { -+ n = au_sphl_count(sphl); ++ for (i = 0, hbl = sbinfo->si_plink; i < AuPlink_NHASH; ++ i++, hbl++) { ++ n = au_hbl_count(hbl); + sum += n; + + n = snprintf(p->a + p->n, limit, "%lu ", n); @@ -6299,7 +6590,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c + goto out; /* success */ + +out_free: -+ au_delayed_free_page((unsigned long)p); ++ free_page((unsigned long)p); +out: + return err; +} @@ -6570,10 +6861,10 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c +} diff -urN /usr/share/empty/fs/aufs/dbgaufs.h linux/fs/aufs/dbgaufs.h --- /usr/share/empty/fs/aufs/dbgaufs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/dbgaufs.h 2016-10-09 16:55:36.486034798 +0200 ++++ linux/fs/aufs/dbgaufs.h 2018-04-15 08:49:13.397817296 +0200 @@ -0,0 +1,48 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -6622,10 +6913,10 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.h linux/fs/aufs/dbgaufs.h +#endif /* __DBGAUFS_H__ */ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c --- /usr/share/empty/fs/aufs/dcsub.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/dcsub.c 2016-10-09 16:55:38.886097714 +0200 ++++ linux/fs/aufs/dcsub.c 2018-04-15 08:49:13.397817296 +0200 @@ -0,0 +1,225 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -6655,7 +6946,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c + p = dpage->dentries; + for (i = 0; i < dpage->ndentry; i++) + dput(*p++); -+ au_delayed_free_page((unsigned long)dpage->dentries); ++ free_page((unsigned long)dpage->dentries); +} + +int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) @@ -6678,7 +6969,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c + return 0; /* success */ + +out_dpages: -+ au_delayed_kfree(dpages->dpages); ++ kfree(dpages->dpages); +out: + return err; +} @@ -6691,7 +6982,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c + p = dpages->dpages; + for (i = 0; i < dpages->ndpage; i++) + au_dpage_free(p++); -+ au_delayed_kfree(dpages->dpages); ++ kfree(dpages->dpages); +} + +static int au_dpages_append(struct au_dcsub_pages *dpages, @@ -6851,10 +7142,10 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c +} diff -urN /usr/share/empty/fs/aufs/dcsub.h linux/fs/aufs/dcsub.h --- /usr/share/empty/fs/aufs/dcsub.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/dcsub.h 2016-10-09 16:55:36.486034798 +0200 ++++ linux/fs/aufs/dcsub.h 2018-04-15 08:49:13.397817296 +0200 @@ -0,0 +1,136 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -6991,10 +7282,10 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.h linux/fs/aufs/dcsub.h +#endif /* __AUFS_DCSUB_H__ */ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c --- /usr/share/empty/fs/aufs/debug.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/debug.c 2016-10-09 16:55:36.486034798 +0200 ++++ linux/fs/aufs/debug.c 2018-04-15 08:49:13.397817296 +0200 @@ -0,0 +1,440 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -7331,7 +7622,7 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c + au_br_count_init(&a->fake); + err = do_pri_br(-1, &a->fake); + au_br_count_fin(&a->fake); -+ au_delayed_kfree(a); ++ kfree(a); + dpri("dev 0x%x\n", sb->s_dev); + if (err || !au_test_aufs(sb)) + return; @@ -7435,10 +7726,10 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c +} diff -urN /usr/share/empty/fs/aufs/debug.h linux/fs/aufs/debug.h --- /usr/share/empty/fs/aufs/debug.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/debug.h 2016-10-09 16:55:36.486034798 +0200 ++++ linux/fs/aufs/debug.h 2018-04-15 08:49:13.397817296 +0200 @@ -0,0 +1,225 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -7664,10 +7955,10 @@ diff -urN /usr/share/empty/fs/aufs/debug.h linux/fs/aufs/debug.h +#endif /* __AUFS_DEBUG_H__ */ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c --- /usr/share/empty/fs/aufs/dentry.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/dentry.c 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,1130 @@ ++++ linux/fs/aufs/dentry.c 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,1152 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -7690,19 +7981,13 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c +#include +#include "aufs.h" + -+struct au_do_lookup_args { -+ unsigned int flags; -+ mode_t type; -+}; -+ +/* + * returns positive/negative dentry, NULL or an error. + * NULL means whiteout-ed or not-found. + */ +static struct dentry* +au_do_lookup(struct dentry *h_parent, struct dentry *dentry, -+ aufs_bindex_t bindex, struct qstr *wh_name, -+ struct au_do_lookup_args *args) ++ aufs_bindex_t bindex, struct au_do_lookup_args *args) +{ + struct dentry *h_dentry; + struct inode *h_inode; @@ -7717,7 +8002,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + br = au_sbr(dentry->d_sb, bindex); + wh_able = !!au_br_whable(br->br_perm); + if (wh_able) -+ wh_found = au_wh_test(h_parent, wh_name, ignore_perm); ++ wh_found = au_wh_test(h_parent, &args->whname, ignore_perm); + h_dentry = ERR_PTR(wh_found); + if (!wh_found) + goto real_lookup; @@ -7732,9 +8017,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + +real_lookup: + if (!ignore_perm) -+ h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent); ++ h_dentry = vfsub_lkup_one(args->name, h_parent); + else -+ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); ++ h_dentry = au_sio_lkup_one(args->name, h_parent); + if (IS_ERR(h_dentry)) { + if (PTR_ERR(h_dentry) == -ENAMETOOLONG + && !allow_neg) @@ -7749,6 +8034,13 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + } else if (wh_found + || (args->type && args->type != (h_inode->i_mode & S_IFMT))) + goto out_neg; ++ else if (au_ftest_lkup(args->flags, DIRREN) ++ /* && h_inode */ ++ && !au_dr_lkup_h_ino(args, bindex, h_inode->i_ino)) { ++ AuDbg("b%d %pd ignored hi%llu\n", bindex, h_dentry, ++ (unsigned long long)h_inode->i_ino); ++ goto out_neg; ++ } + + if (au_dbbot(dentry) <= bindex) + au_set_dbbot(dentry, bindex); @@ -7761,9 +8053,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + || (d_really_is_positive(dentry) && !d_is_dir(dentry))) + goto out; /* success */ + -+ inode_lock_nested(h_inode, AuLsc_I_CHILD); ++ vfsub_inode_lock_shared_nested(h_inode, AuLsc_I_CHILD); + opq = au_diropq_test(h_dentry); -+ inode_unlock(h_inode); ++ inode_unlock_shared(h_inode); + if (opq > 0) + au_set_dbdiropq(dentry, bindex); + else if (unlikely(opq < 0)) { @@ -7797,26 +8089,28 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c +{ + int npositive, err; + aufs_bindex_t bindex, btail, bdiropq; -+ unsigned char isdir, dirperm1; -+ struct qstr whname; ++ unsigned char isdir, dirperm1, dirren; + struct au_do_lookup_args args = { -+ .flags = flags ++ .flags = flags, ++ .name = &dentry->d_name + }; -+ const struct qstr *name = &dentry->d_name; + struct dentry *parent; + struct super_block *sb; + + sb = dentry->d_sb; -+ err = au_test_shwh(sb, name); ++ err = au_test_shwh(sb, args.name); + if (unlikely(err)) + goto out; + -+ err = au_wh_name_alloc(&whname, name); ++ err = au_wh_name_alloc(&args.whname, args.name); + if (unlikely(err)) + goto out; + + isdir = !!d_is_dir(dentry); + dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); ++ dirren = !!au_opt_test(au_mntflags(sb), DIRREN); ++ if (dirren) ++ au_fset_lkup(args.flags, DIRREN); + + npositive = 0; + parent = dget_parent(dentry); @@ -7824,6 +8118,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + for (bindex = btop; bindex <= btail; bindex++) { + struct dentry *h_parent, *h_dentry; + struct inode *h_inode, *h_dir; ++ struct au_branch *br; + + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry) { @@ -7835,11 +8130,17 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + if (!h_parent || !d_is_dir(h_parent)) + continue; + ++ if (dirren) { ++ /* if the inum matches, then use the prepared name */ ++ err = au_dr_lkup_name(&args, bindex); ++ if (unlikely(err)) ++ goto out_parent; ++ } ++ + h_dir = d_inode(h_parent); -+ inode_lock_nested(h_dir, AuLsc_I_PARENT); -+ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, -+ &args); -+ inode_unlock(h_dir); ++ vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT); ++ h_dentry = au_do_lookup(h_parent, dentry, bindex, &args); ++ inode_unlock_shared(h_dir); + err = PTR_ERR(h_dentry); + if (IS_ERR(h_dentry)) + goto out_parent; @@ -7866,6 +8167,15 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + if (bdiropq >= 0 && bdiropq <= bindex) + break; + } ++ br = au_sbr(sb, bindex); ++ if (dirren ++ && au_dr_hino_test_add(&br->br_dirren, h_inode->i_ino, ++ /*add_ent*/NULL)) { ++ /* prepare next name to lookup */ ++ err = au_dr_lkup(&args, dentry, bindex); ++ if (unlikely(err)) ++ goto out_parent; ++ } + } + + if (npositive) { @@ -7882,7 +8192,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + +out_parent: + dput(parent); -+ au_delayed_kfree(whname.name); ++ kfree(args.whname.name); ++ if (dirren) ++ au_dr_lkup_fin(&args); +out: + return err; +} @@ -8491,7 +8803,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + +/* todo: remove this */ +static int h_d_revalidate(struct dentry *dentry, struct inode *inode, -+ unsigned int flags, int do_udba) ++ unsigned int flags, int do_udba, int dirren) +{ + int err; + umode_t mode, h_mode; @@ -8542,7 +8854,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + && !is_root + && ((!h_nfs + && (unhashed != !!d_unhashed(h_dentry) -+ || (!tmpfile ++ || (!tmpfile && !dirren + && !au_qstreq(name, h_name)) + )) + || (h_nfs @@ -8683,7 +8995,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c +{ + int valid, err; + unsigned int sigen; -+ unsigned char do_udba; ++ unsigned char do_udba, dirren; + struct super_block *sb; + struct inode *inode; + @@ -8756,7 +9068,8 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + } + } + -+ err = h_d_revalidate(dentry, inode, flags, do_udba); ++ dirren = !!au_opt_test(au_mntflags(sb), DIRREN); ++ err = h_d_revalidate(dentry, inode, flags, do_udba, dirren); + if (unlikely(!err && do_udba && au_dbtop(dentry) < 0)) { + err = -EIO; + AuDbg("both of real entry and whiteout found, %p, err %d\n", @@ -8798,10 +9111,10 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c +}; diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h --- /usr/share/empty/fs/aufs/dentry.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/dentry.h 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,255 @@ ++++ linux/fs/aufs/dentry.h 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,266 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -8827,6 +9140,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h +#ifdef __KERNEL__ + +#include ++#include "dirren.h" +#include "rwsem.h" + +struct au_hdentry { @@ -8840,10 +9154,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h + struct au_rwsem di_rwsem; + aufs_bindex_t di_btop, di_bbot, di_bwh, di_bdiropq; + unsigned char di_tmpfile; /* to allow the different name */ -+ union { -+ struct au_hdentry *di_hdentry; -+ struct llist_node di_lnode; /* delayed free */ -+ }; ++ struct au_hdentry *di_hdentry; +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ @@ -8851,12 +9162,25 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h +/* flags for au_lkup_dentry() */ +#define AuLkup_ALLOW_NEG 1 +#define AuLkup_IGNORE_PERM (1 << 1) ++#define AuLkup_DIRREN (1 << 2) +#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) +#define au_fset_lkup(flags, name) \ + do { (flags) |= AuLkup_##name; } while (0) +#define au_fclr_lkup(flags, name) \ + do { (flags) &= ~AuLkup_##name; } while (0) + ++#ifndef CONFIG_AUFS_DIRREN ++#undef AuLkup_DIRREN ++#define AuLkup_DIRREN 0 ++#endif ++ ++struct au_do_lookup_args { ++ unsigned int flags; ++ mode_t type; ++ struct qstr whname, *name; ++ struct au_dr_lookup dirren; ++}; ++ +/* ---------------------------------------------------------------------- */ + +/* dentry.c */ @@ -9057,10 +9381,10 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h +#endif /* __AUFS_DENTRY_H__ */ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c --- /usr/share/empty/fs/aufs/dinfo.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/dinfo.c 2016-10-09 16:55:38.889431135 +0200 ++++ linux/fs/aufs/dinfo.c 2018-04-15 08:49:13.397817296 +0200 @@ -0,0 +1,553 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -9114,7 +9438,7 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + goto out; + } + -+ au_cache_dfree_dinfo(dinfo); ++ au_cache_free_dinfo(dinfo); + dinfo = NULL; + +out: @@ -9134,8 +9458,8 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + while (bindex++ <= bbot) + au_hdput(p++); + } -+ au_delayed_kfree(dinfo->di_hdentry); -+ au_cache_dfree_dinfo(dinfo); ++ kfree(dinfo->di_hdentry); ++ au_cache_free_dinfo(dinfo); +} + +void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) @@ -9337,11 +9661,11 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + || d_inode(d1) == d_inode(d2) + || d1->d_sb != d2->d_sb); + -+ if (isdir && au_test_subdir(d1, d2)) { ++ if ((isdir && au_test_subdir(d1, d2)) ++ || 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); + } @@ -9353,11 +9677,11 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + || d_inode(d1) == d_inode(d2) + || d1->d_sb != d2->d_sb); + -+ if (isdir && au_test_subdir(d1, d2)) { ++ if ((isdir && au_test_subdir(d1, d2)) ++ || 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); + } @@ -9614,10 +9938,10 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c +} diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c --- /usr/share/empty/fs/aufs/dir.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/dir.c 2016-10-09 16:55:36.489368218 +0200 -@@ -0,0 +1,762 @@ ++++ linux/fs/aufs/dir.c 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,759 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -9774,7 +10098,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c +out: + dput(a->dentry); + au_nwt_done(&au_sbi(sb)->si_nowait); -+ au_delayed_kfree(arg); ++ kfree(arg); +} + +void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) @@ -9810,7 +10134,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + if (unlikely(wkq_err)) { + pr_err("wkq %d\n", wkq_err); + dput(dentry); -+ au_delayed_kfree(arg); ++ kfree(arg); + } + +out: @@ -9929,7 +10253,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + }; + err = au_do_open(file, &args); + if (unlikely(err)) -+ au_delayed_kfree(fidir); ++ kfree(fidir); + } + si_read_unlock(sb); + return err; @@ -9943,21 +10267,18 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + struct au_fidir *fidir; + struct au_hfile *hf; + aufs_bindex_t bindex, bbot; -+ int execed, delayed; + -+ delayed = (current->flags & PF_KTHREAD) || in_interrupt(); + finfo = au_fi(file); + fidir = finfo->fi_hdir; + if (fidir) { -+ au_sphl_del(&finfo->fi_hlist, -+ &au_sbi(file->f_path.dentry->d_sb)->si_files); ++ au_hbl_del(&finfo->fi_hlist, ++ &au_sbi(file->f_path.dentry->d_sb)->si_files); + vdir_cache = fidir->fd_vdir_cache; /* lock-free */ + if (vdir_cache) -+ au_vdir_free(vdir_cache, delayed); ++ au_vdir_free(vdir_cache); + + bindex = finfo->fi_btop; + if (bindex >= 0) { -+ execed = vfsub_file_execed(file); + hf = fidir->fd_hfile + bindex; + /* + * calls fput() instead of filp_close(), @@ -9966,12 +10287,12 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + bbot = fidir->fd_bbot; + for (; bindex <= bbot; bindex++, hf++) + if (hf->hf_file) -+ au_hfput(hf, execed); ++ au_hfput(hf, /*execed*/0); + } -+ au_delayed_kfree(fidir); ++ kfree(fidir); + finfo->fi_hdir = NULL; + } -+ au_finfo_fin(file, delayed); ++ au_finfo_fin(file); + return 0; +} + @@ -10036,7 +10357,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + struct super_block *sb; + struct inode *inode; + -+ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); ++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0); + if (unlikely(err)) + goto out; + @@ -10105,7 +10426,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); -+ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); ++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0); + if (unlikely(err)) + goto out; + err = au_alive_dir(dentry); @@ -10258,9 +10579,9 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + h_dentry = au_h_dptr(dentry, arg->bindex); + h_inode = d_inode(h_dentry); + /* todo: i_mode changes anytime? */ -+ inode_lock_nested(h_inode, AuLsc_I_CHILD); ++ vfsub_inode_lock_shared_nested(h_inode, AuLsc_I_CHILD); + err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); -+ inode_unlock(h_inode); ++ inode_unlock_shared(h_inode); + if (!err) + err = do_test_empty(dentry, arg); + else { @@ -10380,10 +10701,10 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c +}; diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h --- /usr/share/empty/fs/aufs/dir.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/dir.h 2016-10-09 16:55:36.489368218 +0200 -@@ -0,0 +1,137 @@ ++++ linux/fs/aufs/dir.h 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,131 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -10426,10 +10747,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h + +struct au_vdir_dehstr { + struct hlist_node hash; -+ union { -+ struct au_vdir_destr *str; -+ struct llist_node lnode; /* delayed free */ -+ }; ++ struct au_vdir_destr *str; +} ____cacheline_aligned_in_smp; + +struct au_vdir_de { @@ -10467,10 +10785,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h + + unsigned long vd_version; + unsigned int vd_deblk_sz; -+ union { -+ unsigned long vd_jiffy; -+ struct llist_node vd_lnode; /* delayed free */ -+ }; ++ unsigned long vd_jiffy; +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ @@ -10494,7 +10809,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h +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 atonce); ++void au_vdir_free(struct au_vdir *vdir); +int au_vdir_init(struct file *file); +int au_vdir_fill_de(struct file *file, struct dir_context *ctx); + @@ -10519,12 +10834,1479 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DIR_H__ */ +diff -urN /usr/share/empty/fs/aufs/dirren.c linux/fs/aufs/dirren.c +--- /usr/share/empty/fs/aufs/dirren.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/aufs/dirren.c 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,1314 @@ ++/* ++ * Copyright (C) 2017-2018 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, see . ++ */ ++ ++/* ++ * special handling in renaming a directoy ++ * in order to support looking-up the before-renamed name on the lower readonly ++ * branches ++ */ ++ ++#include ++#include "aufs.h" ++ ++static void au_dr_hino_del(struct au_dr_br *dr, struct au_dr_hino *ent) ++{ ++ int idx; ++ ++ idx = au_dr_ihash(ent->dr_h_ino); ++ au_hbl_del(&ent->dr_hnode, dr->dr_h_ino + idx); ++} ++ ++static int au_dr_hino_test_empty(struct au_dr_br *dr) ++{ ++ int ret, i; ++ struct hlist_bl_head *hbl; ++ ++ ret = 1; ++ for (i = 0; ret && i < AuDirren_NHASH; i++) { ++ hbl = dr->dr_h_ino + i; ++ hlist_bl_lock(hbl); ++ ret &= hlist_bl_empty(hbl); ++ hlist_bl_unlock(hbl); ++ } ++ ++ return ret; ++} ++ ++static struct au_dr_hino *au_dr_hino_find(struct au_dr_br *dr, ino_t ino) ++{ ++ struct au_dr_hino *found, *ent; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; ++ int idx; ++ ++ found = NULL; ++ idx = au_dr_ihash(ino); ++ hbl = dr->dr_h_ino + idx; ++ hlist_bl_lock(hbl); ++ hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode) ++ if (ent->dr_h_ino == ino) { ++ found = ent; ++ break; ++ } ++ hlist_bl_unlock(hbl); ++ ++ return found; ++} ++ ++int au_dr_hino_test_add(struct au_dr_br *dr, ino_t ino, ++ struct au_dr_hino *add_ent) ++{ ++ int found, idx; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; ++ struct au_dr_hino *ent; ++ ++ found = 0; ++ idx = au_dr_ihash(ino); ++ hbl = dr->dr_h_ino + idx; ++#if 0 ++ { ++ struct hlist_bl_node *tmp; ++ ++ hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode) ++ AuDbg("hi%llu\n", (unsigned long long)ent->dr_h_ino); ++ } ++#endif ++ hlist_bl_lock(hbl); ++ hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode) ++ if (ent->dr_h_ino == ino) { ++ found = 1; ++ break; ++ } ++ if (!found && add_ent) ++ hlist_bl_add_head(&add_ent->dr_hnode, hbl); ++ hlist_bl_unlock(hbl); ++ ++ if (!found && add_ent) ++ AuDbg("i%llu added\n", (unsigned long long)add_ent->dr_h_ino); ++ ++ return found; ++} ++ ++void au_dr_hino_free(struct au_dr_br *dr) ++{ ++ int i; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos, *tmp; ++ struct au_dr_hino *ent; ++ ++ /* SiMustWriteLock(sb); */ ++ ++ for (i = 0; i < AuDirren_NHASH; i++) { ++ hbl = dr->dr_h_ino + i; ++ /* no spinlock since sbinfo must be write-locked */ ++ hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode) ++ kfree(ent); ++ INIT_HLIST_BL_HEAD(hbl); ++ } ++} ++ ++/* returns the number of inodes or an error */ ++static int au_dr_hino_store(struct super_block *sb, struct au_branch *br, ++ struct file *hinofile) ++{ ++ int err, i; ++ ssize_t ssz; ++ loff_t pos, oldsize; ++ uint64_t u64; ++ struct inode *hinoinode; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *n1, *n2; ++ struct au_dr_hino *ent; ++ ++ SiMustWriteLock(sb); ++ AuDebugOn(!au_br_writable(br->br_perm)); ++ ++ hinoinode = file_inode(hinofile); ++ oldsize = i_size_read(hinoinode); ++ ++ err = 0; ++ pos = 0; ++ hbl = br->br_dirren.dr_h_ino; ++ for (i = 0; !err && i < AuDirren_NHASH; i++, hbl++) { ++ /* no bit-lock since sbinfo must be write-locked */ ++ hlist_bl_for_each_entry_safe(ent, n1, n2, hbl, dr_hnode) { ++ AuDbg("hi%llu, %pD2\n", ++ (unsigned long long)ent->dr_h_ino, hinofile); ++ u64 = cpu_to_be64(ent->dr_h_ino); ++ ssz = vfsub_write_k(hinofile, &u64, sizeof(u64), &pos); ++ if (ssz == sizeof(u64)) ++ continue; ++ ++ /* write error */ ++ pr_err("ssz %zd, %pD2\n", ssz, hinofile); ++ err = -ENOSPC; ++ if (ssz < 0) ++ err = ssz; ++ break; ++ } ++ } ++ /* regardless the error */ ++ if (pos < oldsize) { ++ err = vfsub_trunc(&hinofile->f_path, pos, /*attr*/0, hinofile); ++ AuTraceErr(err); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_dr_hino_load(struct au_dr_br *dr, struct file *hinofile) ++{ ++ int err, hidx; ++ ssize_t ssz; ++ size_t sz, n; ++ loff_t pos; ++ uint64_t u64; ++ struct au_dr_hino *ent; ++ struct inode *hinoinode; ++ struct hlist_bl_head *hbl; ++ ++ err = 0; ++ pos = 0; ++ hbl = dr->dr_h_ino; ++ hinoinode = file_inode(hinofile); ++ sz = i_size_read(hinoinode); ++ AuDebugOn(sz % sizeof(u64)); ++ n = sz / sizeof(u64); ++ while (n--) { ++ ssz = vfsub_read_k(hinofile, &u64, sizeof(u64), &pos); ++ if (unlikely(ssz != sizeof(u64))) { ++ pr_err("ssz %zd, %pD2\n", ssz, hinofile); ++ err = -EINVAL; ++ if (ssz < 0) ++ err = ssz; ++ goto out_free; ++ } ++ ++ ent = kmalloc(sizeof(*ent), GFP_NOFS); ++ if (!ent) { ++ err = -ENOMEM; ++ AuTraceErr(err); ++ goto out_free; ++ } ++ ent->dr_h_ino = be64_to_cpu(u64); ++ AuDbg("hi%llu, %pD2\n", ++ (unsigned long long)ent->dr_h_ino, hinofile); ++ hidx = au_dr_ihash(ent->dr_h_ino); ++ au_hbl_add(&ent->dr_hnode, hbl + hidx); ++ } ++ goto out; /* success */ ++ ++out_free: ++ au_dr_hino_free(dr); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * @bindex/@br is a switch to distinguish whether suspending hnotify or not. ++ * @path is a switch to distinguish load and store. ++ */ ++static int au_dr_hino(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_branch *br, const struct path *path) ++{ ++ int err, flags; ++ unsigned char load, suspend; ++ struct file *hinofile; ++ struct au_hinode *hdir; ++ struct inode *dir, *delegated; ++ struct path hinopath; ++ struct qstr hinoname = QSTR_INIT(AUFS_WH_DR_BRHINO, ++ sizeof(AUFS_WH_DR_BRHINO) - 1); ++ ++ AuDebugOn(bindex < 0 && !br); ++ AuDebugOn(bindex >= 0 && br); ++ ++ err = -EINVAL; ++ suspend = !br; ++ if (suspend) ++ br = au_sbr(sb, bindex); ++ load = !!path; ++ if (!load) { ++ path = &br->br_path; ++ AuDebugOn(!au_br_writable(br->br_perm)); ++ if (unlikely(!au_br_writable(br->br_perm))) ++ goto out; ++ } ++ ++ hdir = NULL; ++ if (suspend) { ++ dir = d_inode(sb->s_root); ++ hdir = au_hinode(au_ii(dir), bindex); ++ dir = hdir->hi_inode; ++ au_hn_inode_lock_nested(hdir, AuLsc_I_CHILD); ++ } else { ++ dir = d_inode(path->dentry); ++ inode_lock_nested(dir, AuLsc_I_CHILD); ++ } ++ hinopath.dentry = vfsub_lkup_one(&hinoname, path->dentry); ++ err = PTR_ERR(hinopath.dentry); ++ if (IS_ERR(hinopath.dentry)) ++ goto out_unlock; ++ ++ err = 0; ++ flags = O_RDONLY; ++ if (load) { ++ if (d_is_negative(hinopath.dentry)) ++ goto out_dput; /* success */ ++ } else { ++ if (au_dr_hino_test_empty(&br->br_dirren)) { ++ if (d_is_positive(hinopath.dentry)) { ++ delegated = NULL; ++ err = vfsub_unlink(dir, &hinopath, &delegated, ++ /*force*/0); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ pr_err("ignored err %d, %pd2\n", ++ err, hinopath.dentry); ++ if (unlikely(err == -EWOULDBLOCK)) ++ iput(delegated); ++ err = 0; ++ } ++ goto out_dput; ++ } else if (!d_is_positive(hinopath.dentry)) { ++ err = vfsub_create(dir, &hinopath, 0600, ++ /*want_excl*/false); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ goto out_dput; ++ } ++ flags = O_WRONLY; ++ } ++ hinopath.mnt = path->mnt; ++ hinofile = vfsub_dentry_open(&hinopath, flags); ++ if (suspend) ++ au_hn_inode_unlock(hdir); ++ else ++ inode_unlock(dir); ++ dput(hinopath.dentry); ++ AuTraceErrPtr(hinofile); ++ if (IS_ERR(hinofile)) { ++ err = PTR_ERR(hinofile); ++ goto out; ++ } ++ ++ if (load) ++ err = au_dr_hino_load(&br->br_dirren, hinofile); ++ else ++ err = au_dr_hino_store(sb, br, hinofile); ++ fput(hinofile); ++ goto out; ++ ++out_dput: ++ dput(hinopath.dentry); ++out_unlock: ++ if (suspend) ++ au_hn_inode_unlock(hdir); ++ else ++ inode_unlock(dir); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_dr_brid_init(struct au_dr_brid *brid, const struct path *path) ++{ ++ int err; ++ struct kstatfs kstfs; ++ dev_t dev; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ err = vfs_statfs((void *)path, &kstfs); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ goto out; ++ ++ /* todo: support for UUID */ ++ ++ if (kstfs.f_fsid.val[0] || kstfs.f_fsid.val[1]) { ++ brid->type = AuBrid_FSID; ++ brid->fsid = kstfs.f_fsid; ++ } else { ++ dentry = path->dentry; ++ sb = dentry->d_sb; ++ dev = sb->s_dev; ++ if (dev) { ++ brid->type = AuBrid_DEV; ++ brid->dev = dev; ++ } ++ } ++ ++out: ++ return err; ++} ++ ++int au_dr_br_init(struct super_block *sb, struct au_branch *br, ++ const struct path *path) ++{ ++ int err, i; ++ struct au_dr_br *dr; ++ struct hlist_bl_head *hbl; ++ ++ dr = &br->br_dirren; ++ hbl = dr->dr_h_ino; ++ for (i = 0; i < AuDirren_NHASH; i++, hbl++) ++ INIT_HLIST_BL_HEAD(hbl); ++ ++ err = au_dr_brid_init(&dr->dr_brid, path); ++ if (unlikely(err)) ++ goto out; ++ ++ if (au_opt_test(au_mntflags(sb), DIRREN)) ++ err = au_dr_hino(sb, /*bindex*/-1, br, path); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_dr_br_fin(struct super_block *sb, struct au_branch *br) ++{ ++ int err; ++ ++ err = 0; ++ if (au_br_writable(br->br_perm)) ++ err = au_dr_hino(sb, /*bindex*/-1, br, /*path*/NULL); ++ if (!err) ++ au_dr_hino_free(&br->br_dirren); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_brid_str(struct au_dr_brid *brid, struct inode *h_inode, ++ char *buf, size_t sz) ++{ ++ int err; ++ unsigned int major, minor; ++ char *p; ++ ++ p = buf; ++ err = snprintf(p, sz, "%d_", brid->type); ++ AuDebugOn(err > sz); ++ p += err; ++ sz -= err; ++ switch (brid->type) { ++ case AuBrid_Unset: ++ return -EINVAL; ++ case AuBrid_UUID: ++ err = snprintf(p, sz, "%pU", brid->uuid.__u_bits); ++ break; ++ case AuBrid_FSID: ++ err = snprintf(p, sz, "%08x-%08x", ++ brid->fsid.val[0], brid->fsid.val[1]); ++ break; ++ case AuBrid_DEV: ++ major = MAJOR(brid->dev); ++ minor = MINOR(brid->dev); ++ if (major <= 0xff && minor <= 0xff) ++ err = snprintf(p, sz, "%02x%02x", major, minor); ++ else ++ err = snprintf(p, sz, "%03x:%05x", major, minor); ++ break; ++ } ++ AuDebugOn(err > sz); ++ p += err; ++ sz -= err; ++ err = snprintf(p, sz, "_%llu", (unsigned long long)h_inode->i_ino); ++ AuDebugOn(err > sz); ++ p += err; ++ sz -= err; ++ ++ return p - buf; ++} ++ ++static int au_drinfo_name(struct au_branch *br, char *name, int len) ++{ ++ int rlen; ++ struct dentry *br_dentry; ++ struct inode *br_inode; ++ ++ br_dentry = au_br_dentry(br); ++ br_inode = d_inode(br_dentry); ++ rlen = au_brid_str(&br->br_dirren.dr_brid, br_inode, name, len); ++ AuDebugOn(rlen >= AUFS_DIRREN_ENV_VAL_SZ); ++ AuDebugOn(rlen > len); ++ ++ return rlen; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * from the given @h_dentry, construct drinfo at @*fdata. ++ * when the size of @*fdata is not enough, reallocate and return new @fdata and ++ * @allocated. ++ */ ++static int au_drinfo_construct(struct au_drinfo_fdata **fdata, ++ struct dentry *h_dentry, ++ unsigned char *allocated) ++{ ++ int err, v; ++ struct au_drinfo_fdata *f, *p; ++ struct au_drinfo *drinfo; ++ struct inode *h_inode; ++ struct qstr *qname; ++ ++ err = 0; ++ f = *fdata; ++ h_inode = d_inode(h_dentry); ++ qname = &h_dentry->d_name; ++ drinfo = &f->drinfo; ++ drinfo->ino = cpu_to_be64(h_inode->i_ino); ++ drinfo->oldnamelen = qname->len; ++ if (*allocated < sizeof(*f) + qname->len) { ++ v = roundup_pow_of_two(*allocated + qname->len); ++ p = au_krealloc(f, v, GFP_NOFS, /*may_shrink*/0); ++ if (unlikely(!p)) { ++ err = -ENOMEM; ++ AuTraceErr(err); ++ goto out; ++ } ++ f = p; ++ *fdata = f; ++ *allocated = v; ++ drinfo = &f->drinfo; ++ } ++ memcpy(drinfo->oldname, qname->name, qname->len); ++ AuDbg("i%llu, %.*s\n", ++ be64_to_cpu(drinfo->ino), drinfo->oldnamelen, drinfo->oldname); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* callers have to free the return value */ ++static struct au_drinfo *au_drinfo_read_k(struct file *file, ino_t h_ino) ++{ ++ struct au_drinfo *ret, *drinfo; ++ struct au_drinfo_fdata fdata; ++ int len; ++ loff_t pos; ++ ssize_t ssz; ++ ++ ret = ERR_PTR(-EIO); ++ pos = 0; ++ ssz = vfsub_read_k(file, &fdata, sizeof(fdata), &pos); ++ if (unlikely(ssz != sizeof(fdata))) { ++ AuIOErr("ssz %zd, %u, %pD2\n", ++ ssz, (unsigned int)sizeof(fdata), file); ++ goto out; ++ } ++ ++ fdata.magic = ntohl(fdata.magic); ++ switch (fdata.magic) { ++ case AUFS_DRINFO_MAGIC_V1: ++ break; ++ default: ++ AuIOErr("magic-num 0x%x, 0x%x, %pD2\n", ++ fdata.magic, AUFS_DRINFO_MAGIC_V1, file); ++ goto out; ++ } ++ ++ drinfo = &fdata.drinfo; ++ len = drinfo->oldnamelen; ++ if (!len) { ++ AuIOErr("broken drinfo %pD2\n", file); ++ goto out; ++ } ++ ++ ret = NULL; ++ drinfo->ino = be64_to_cpu(drinfo->ino); ++ if (unlikely(h_ino && drinfo->ino != h_ino)) { ++ AuDbg("ignored i%llu, i%llu, %pD2\n", ++ (unsigned long long)drinfo->ino, ++ (unsigned long long)h_ino, file); ++ goto out; /* success */ ++ } ++ ++ ret = kmalloc(sizeof(*ret) + len, GFP_NOFS); ++ if (unlikely(!ret)) { ++ ret = ERR_PTR(-ENOMEM); ++ AuTraceErrPtr(ret); ++ goto out; ++ } ++ ++ *ret = *drinfo; ++ ssz = vfsub_read_k(file, (void *)ret->oldname, len, &pos); ++ if (unlikely(ssz != len)) { ++ kfree(ret); ++ ret = ERR_PTR(-EIO); ++ AuIOErr("ssz %zd, %u, %pD2\n", ssz, len, file); ++ goto out; ++ } ++ ++ AuDbg("oldname %.*s\n", ret->oldnamelen, ret->oldname); ++ ++out: ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* in order to be revertible */ ++struct au_drinfo_rev_elm { ++ int created; ++ struct dentry *info_dentry; ++ struct au_drinfo *info_last; ++}; ++ ++struct au_drinfo_rev { ++ unsigned char already; ++ aufs_bindex_t nelm; ++ struct au_drinfo_rev_elm elm[0]; ++}; ++ ++/* todo: isn't it too large? */ ++struct au_drinfo_store { ++ struct path h_ppath; ++ struct dentry *h_dentry; ++ struct au_drinfo_fdata *fdata; ++ char *infoname; /* inside of whname, just after PFX */ ++ char whname[sizeof(AUFS_WH_DR_INFO_PFX) + AUFS_DIRREN_ENV_VAL_SZ]; ++ aufs_bindex_t btgt, btail; ++ unsigned char no_sio, ++ allocated, /* current size of *fdata */ ++ infonamelen, /* room size for p */ ++ whnamelen, /* length of the genarated name */ ++ renameback; /* renamed back */ ++}; ++ ++/* on rename(2) error, the caller should revert it using @elm */ ++static int au_drinfo_do_store(struct au_drinfo_store *w, ++ struct au_drinfo_rev_elm *elm) ++{ ++ int err, len; ++ ssize_t ssz; ++ loff_t pos; ++ struct path infopath = { ++ .mnt = w->h_ppath.mnt ++ }; ++ struct inode *h_dir, *h_inode, *delegated; ++ struct file *infofile; ++ struct qstr *qname; ++ ++ AuDebugOn(elm ++ && memcmp(elm, page_address(ZERO_PAGE(0)), sizeof(*elm))); ++ ++ infopath.dentry = vfsub_lookup_one_len(w->whname, w->h_ppath.dentry, ++ w->whnamelen); ++ AuTraceErrPtr(infopath.dentry); ++ if (IS_ERR(infopath.dentry)) { ++ err = PTR_ERR(infopath.dentry); ++ goto out; ++ } ++ ++ err = 0; ++ h_dir = d_inode(w->h_ppath.dentry); ++ if (elm && d_is_negative(infopath.dentry)) { ++ err = vfsub_create(h_dir, &infopath, 0600, /*want_excl*/true); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ goto out_dput; ++ elm->created = 1; ++ elm->info_dentry = dget(infopath.dentry); ++ } ++ ++ infofile = vfsub_dentry_open(&infopath, O_RDWR); ++ AuTraceErrPtr(infofile); ++ if (IS_ERR(infofile)) { ++ err = PTR_ERR(infofile); ++ goto out_dput; ++ } ++ ++ h_inode = d_inode(infopath.dentry); ++ if (elm && i_size_read(h_inode)) { ++ h_inode = d_inode(w->h_dentry); ++ elm->info_last = au_drinfo_read_k(infofile, h_inode->i_ino); ++ AuTraceErrPtr(elm->info_last); ++ if (IS_ERR(elm->info_last)) { ++ err = PTR_ERR(elm->info_last); ++ elm->info_last = NULL; ++ AuDebugOn(elm->info_dentry); ++ goto out_fput; ++ } ++ } ++ ++ if (elm && w->renameback) { ++ delegated = NULL; ++ err = vfsub_unlink(h_dir, &infopath, &delegated, /*force*/0); ++ AuTraceErr(err); ++ if (unlikely(err == -EWOULDBLOCK)) ++ iput(delegated); ++ goto out_fput; ++ } ++ ++ pos = 0; ++ qname = &w->h_dentry->d_name; ++ len = sizeof(*w->fdata) + qname->len; ++ if (!elm) ++ len = sizeof(*w->fdata) + w->fdata->drinfo.oldnamelen; ++ ssz = vfsub_write_k(infofile, w->fdata, len, &pos); ++ if (ssz == len) { ++ AuDbg("hi%llu, %.*s\n", w->fdata->drinfo.ino, ++ w->fdata->drinfo.oldnamelen, w->fdata->drinfo.oldname); ++ goto out_fput; /* success */ ++ } else { ++ err = -EIO; ++ if (ssz < 0) ++ err = ssz; ++ /* the caller should revert it using @elm */ ++ } ++ ++out_fput: ++ fput(infofile); ++out_dput: ++ dput(infopath.dentry); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++struct au_call_drinfo_do_store_args { ++ int *errp; ++ struct au_drinfo_store *w; ++ struct au_drinfo_rev_elm *elm; ++}; ++ ++static void au_call_drinfo_do_store(void *args) ++{ ++ struct au_call_drinfo_do_store_args *a = args; ++ ++ *a->errp = au_drinfo_do_store(a->w, a->elm); ++} ++ ++static int au_drinfo_store_sio(struct au_drinfo_store *w, ++ struct au_drinfo_rev_elm *elm) ++{ ++ int err, wkq_err; ++ ++ if (w->no_sio) ++ err = au_drinfo_do_store(w, elm); ++ else { ++ struct au_call_drinfo_do_store_args a = { ++ .errp = &err, ++ .w = w, ++ .elm = elm ++ }; ++ wkq_err = au_wkq_wait(au_call_drinfo_do_store, &a); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ AuTraceErr(err); ++ ++ return err; ++} ++ ++static int au_drinfo_store_work_init(struct au_drinfo_store *w, ++ aufs_bindex_t btgt) ++{ ++ int err; ++ ++ memset(w, 0, sizeof(*w)); ++ w->allocated = roundup_pow_of_two(sizeof(*w->fdata) + 40); ++ strcpy(w->whname, AUFS_WH_DR_INFO_PFX); ++ w->infoname = w->whname + sizeof(AUFS_WH_DR_INFO_PFX) - 1; ++ w->infonamelen = sizeof(w->whname) - sizeof(AUFS_WH_DR_INFO_PFX); ++ w->btgt = btgt; ++ w->no_sio = !!uid_eq(current_fsuid(), GLOBAL_ROOT_UID); ++ ++ err = -ENOMEM; ++ w->fdata = kcalloc(1, w->allocated, GFP_NOFS); ++ if (unlikely(!w->fdata)) { ++ AuTraceErr(err); ++ goto out; ++ } ++ w->fdata->magic = htonl(AUFS_DRINFO_MAGIC_V1); ++ err = 0; ++ ++out: ++ return err; ++} ++ ++static void au_drinfo_store_work_fin(struct au_drinfo_store *w) ++{ ++ kfree(w->fdata); ++} ++ ++static void au_drinfo_store_rev(struct au_drinfo_rev *rev, ++ struct au_drinfo_store *w) ++{ ++ struct au_drinfo_rev_elm *elm; ++ struct inode *h_dir, *delegated; ++ int err, nelm; ++ struct path infopath = { ++ .mnt = w->h_ppath.mnt ++ }; ++ ++ h_dir = d_inode(w->h_ppath.dentry); ++ IMustLock(h_dir); ++ ++ err = 0; ++ elm = rev->elm; ++ for (nelm = rev->nelm; nelm > 0; nelm--, elm++) { ++ AuDebugOn(elm->created && elm->info_last); ++ if (elm->created) { ++ AuDbg("here\n"); ++ delegated = NULL; ++ infopath.dentry = elm->info_dentry; ++ err = vfsub_unlink(h_dir, &infopath, &delegated, ++ !w->no_sio); ++ AuTraceErr(err); ++ if (unlikely(err == -EWOULDBLOCK)) ++ iput(delegated); ++ dput(elm->info_dentry); ++ } else if (elm->info_last) { ++ AuDbg("here\n"); ++ w->fdata->drinfo = *elm->info_last; ++ memcpy(w->fdata->drinfo.oldname, ++ elm->info_last->oldname, ++ elm->info_last->oldnamelen); ++ err = au_drinfo_store_sio(w, /*elm*/NULL); ++ kfree(elm->info_last); ++ } ++ if (unlikely(err)) ++ AuIOErr("%d, %s\n", err, w->whname); ++ /* go on even if err */ ++ } ++} ++ ++/* caller has to call au_dr_rename_fin() later */ ++static int au_drinfo_store(struct dentry *dentry, aufs_bindex_t btgt, ++ struct qstr *dst_name, void *_rev) ++{ ++ int err, sz, nelm; ++ aufs_bindex_t bindex, btail; ++ struct au_drinfo_store work; ++ struct au_drinfo_rev *rev, **p; ++ struct au_drinfo_rev_elm *elm; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_hinode *hdir; ++ ++ err = au_drinfo_store_work_init(&work, btgt); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ goto out; ++ ++ err = -ENOMEM; ++ btail = au_dbtaildir(dentry); ++ nelm = btail - btgt; ++ sz = sizeof(*rev) + sizeof(*elm) * nelm; ++ rev = kcalloc(1, sz, GFP_NOFS); ++ if (unlikely(!rev)) { ++ AuTraceErr(err); ++ goto out_args; ++ } ++ rev->nelm = nelm; ++ elm = rev->elm; ++ p = _rev; ++ *p = rev; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ work.h_ppath.dentry = au_h_dptr(dentry, btgt); ++ work.h_ppath.mnt = au_sbr_mnt(sb, btgt); ++ hdir = au_hi(d_inode(dentry), btgt); ++ au_hn_inode_lock_nested(hdir, AuLsc_I_CHILD); ++ for (bindex = btgt + 1; bindex <= btail; bindex++, elm++) { ++ work.h_dentry = au_h_dptr(dentry, bindex); ++ if (!work.h_dentry) ++ continue; ++ ++ err = au_drinfo_construct(&work.fdata, work.h_dentry, ++ &work.allocated); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ break; ++ ++ work.renameback = au_qstreq(&work.h_dentry->d_name, dst_name); ++ br = au_sbr(sb, bindex); ++ work.whnamelen = sizeof(AUFS_WH_DR_INFO_PFX) - 1; ++ work.whnamelen += au_drinfo_name(br, work.infoname, ++ work.infonamelen); ++ AuDbg("whname %.*s, i%llu, %.*s\n", ++ work.whnamelen, work.whname, ++ be64_to_cpu(work.fdata->drinfo.ino), ++ work.fdata->drinfo.oldnamelen, ++ work.fdata->drinfo.oldname); ++ ++ err = au_drinfo_store_sio(&work, elm); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ break; ++ } ++ if (unlikely(err)) { ++ /* revert all drinfo */ ++ au_drinfo_store_rev(rev, &work); ++ kfree(rev); ++ *p = NULL; ++ } ++ au_hn_inode_unlock(hdir); ++ ++out_args: ++ au_drinfo_store_work_fin(&work); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_dr_rename(struct dentry *src, aufs_bindex_t bindex, ++ struct qstr *dst_name, void *_rev) ++{ ++ int err, already; ++ ino_t ino; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_dr_br *dr; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ struct au_dr_hino *ent; ++ struct au_drinfo_rev *rev, **p; ++ ++ AuDbg("bindex %d\n", bindex); ++ ++ err = -ENOMEM; ++ ent = kmalloc(sizeof(*ent), GFP_NOFS); ++ if (unlikely(!ent)) ++ goto out; ++ ++ sb = src->d_sb; ++ br = au_sbr(sb, bindex); ++ dr = &br->br_dirren; ++ h_dentry = au_h_dptr(src, bindex); ++ h_inode = d_inode(h_dentry); ++ ino = h_inode->i_ino; ++ ent->dr_h_ino = ino; ++ already = au_dr_hino_test_add(dr, ino, ent); ++ AuDbg("b%d, hi%llu, already %d\n", ++ bindex, (unsigned long long)ino, already); ++ ++ err = au_drinfo_store(src, bindex, dst_name, _rev); ++ AuTraceErr(err); ++ if (!err) { ++ p = _rev; ++ rev = *p; ++ rev->already = already; ++ goto out; /* success */ ++ } ++ ++ /* revert */ ++ if (!already) ++ au_dr_hino_del(dr, ent); ++ kfree(ent); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *_rev) ++{ ++ struct au_drinfo_rev *rev; ++ struct au_drinfo_rev_elm *elm; ++ int nelm; ++ ++ rev = _rev; ++ elm = rev->elm; ++ for (nelm = rev->nelm; nelm > 0; nelm--, elm++) { ++ dput(elm->info_dentry); ++ kfree(elm->info_last); ++ } ++ kfree(rev); ++} ++ ++void au_dr_rename_rev(struct dentry *src, aufs_bindex_t btgt, void *_rev) ++{ ++ int err; ++ struct au_drinfo_store work; ++ struct au_drinfo_rev *rev = _rev; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct inode *h_inode; ++ struct au_dr_br *dr; ++ struct au_dr_hino *ent; ++ ++ err = au_drinfo_store_work_init(&work, btgt); ++ if (unlikely(err)) ++ goto out; ++ ++ sb = src->d_sb; ++ br = au_sbr(sb, btgt); ++ work.h_ppath.dentry = au_h_dptr(src, btgt); ++ work.h_ppath.mnt = au_br_mnt(br); ++ au_drinfo_store_rev(rev, &work); ++ au_drinfo_store_work_fin(&work); ++ if (rev->already) ++ goto out; ++ ++ dr = &br->br_dirren; ++ h_inode = d_inode(work.h_ppath.dentry); ++ ent = au_dr_hino_find(dr, h_inode->i_ino); ++ BUG_ON(!ent); ++ au_dr_hino_del(dr, ent); ++ kfree(ent); ++ ++out: ++ kfree(rev); ++ if (unlikely(err)) ++ pr_err("failed to remove dirren info\n"); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct au_drinfo *au_drinfo_do_load(struct path *h_ppath, ++ char *whname, int whnamelen, ++ struct dentry **info_dentry) ++{ ++ struct au_drinfo *drinfo; ++ struct file *f; ++ struct inode *h_dir; ++ struct path infopath; ++ int unlocked; ++ ++ AuDbg("%pd/%.*s\n", h_ppath->dentry, whnamelen, whname); ++ ++ *info_dentry = NULL; ++ drinfo = NULL; ++ unlocked = 0; ++ h_dir = d_inode(h_ppath->dentry); ++ vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT); ++ infopath.dentry = vfsub_lookup_one_len(whname, h_ppath->dentry, ++ whnamelen); ++ if (IS_ERR(infopath.dentry)) { ++ drinfo = (void *)infopath.dentry; ++ goto out; ++ } ++ ++ if (d_is_negative(infopath.dentry)) ++ goto out_dput; /* success */ ++ ++ infopath.mnt = h_ppath->mnt; ++ f = vfsub_dentry_open(&infopath, O_RDONLY); ++ inode_unlock_shared(h_dir); ++ unlocked = 1; ++ if (IS_ERR(f)) { ++ drinfo = (void *)f; ++ goto out_dput; ++ } ++ ++ drinfo = au_drinfo_read_k(f, /*h_ino*/0); ++ if (IS_ERR_OR_NULL(drinfo)) ++ goto out_fput; ++ ++ AuDbg("oldname %.*s\n", drinfo->oldnamelen, drinfo->oldname); ++ *info_dentry = dget(infopath.dentry); /* keep it alive */ ++ ++out_fput: ++ fput(f); ++out_dput: ++ dput(infopath.dentry); ++out: ++ if (!unlocked) ++ inode_unlock_shared(h_dir); ++ AuTraceErrPtr(drinfo); ++ return drinfo; ++} ++ ++struct au_drinfo_do_load_args { ++ struct au_drinfo **drinfop; ++ struct path *h_ppath; ++ char *whname; ++ int whnamelen; ++ struct dentry **info_dentry; ++}; ++ ++static void au_call_drinfo_do_load(void *args) ++{ ++ struct au_drinfo_do_load_args *a = args; ++ ++ *a->drinfop = au_drinfo_do_load(a->h_ppath, a->whname, a->whnamelen, ++ a->info_dentry); ++} ++ ++struct au_drinfo_load { ++ struct path h_ppath; ++ struct qstr *qname; ++ unsigned char no_sio; ++ ++ aufs_bindex_t ninfo; ++ struct au_drinfo **drinfo; ++}; ++ ++static int au_drinfo_load(struct au_drinfo_load *w, aufs_bindex_t bindex, ++ struct au_branch *br) ++{ ++ int err, wkq_err, whnamelen, e; ++ char whname[sizeof(AUFS_WH_DR_INFO_PFX) + AUFS_DIRREN_ENV_VAL_SZ] ++ = AUFS_WH_DR_INFO_PFX; ++ struct au_drinfo *drinfo; ++ struct qstr oldname; ++ struct inode *h_dir, *delegated; ++ struct dentry *info_dentry; ++ struct path infopath; ++ ++ whnamelen = sizeof(AUFS_WH_DR_INFO_PFX) - 1; ++ whnamelen += au_drinfo_name(br, whname + whnamelen, ++ sizeof(whname) - whnamelen); ++ if (w->no_sio) ++ drinfo = au_drinfo_do_load(&w->h_ppath, whname, whnamelen, ++ &info_dentry); ++ else { ++ struct au_drinfo_do_load_args args = { ++ .drinfop = &drinfo, ++ .h_ppath = &w->h_ppath, ++ .whname = whname, ++ .whnamelen = whnamelen, ++ .info_dentry = &info_dentry ++ }; ++ wkq_err = au_wkq_wait(au_call_drinfo_do_load, &args); ++ if (unlikely(wkq_err)) ++ drinfo = ERR_PTR(wkq_err); ++ } ++ err = PTR_ERR(drinfo); ++ if (IS_ERR_OR_NULL(drinfo)) ++ goto out; ++ ++ err = 0; ++ oldname.len = drinfo->oldnamelen; ++ oldname.name = drinfo->oldname; ++ if (au_qstreq(w->qname, &oldname)) { ++ /* the name is renamed back */ ++ kfree(drinfo); ++ drinfo = NULL; ++ ++ infopath.dentry = info_dentry; ++ infopath.mnt = w->h_ppath.mnt; ++ h_dir = d_inode(w->h_ppath.dentry); ++ delegated = NULL; ++ inode_lock_nested(h_dir, AuLsc_I_PARENT); ++ e = vfsub_unlink(h_dir, &infopath, &delegated, !w->no_sio); ++ inode_unlock(h_dir); ++ if (unlikely(e)) ++ AuIOErr("ignored %d, %pd2\n", e, &infopath.dentry); ++ if (unlikely(e == -EWOULDBLOCK)) ++ iput(delegated); ++ } ++ kfree(w->drinfo[bindex]); ++ w->drinfo[bindex] = drinfo; ++ dput(info_dentry); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_dr_lkup_free(struct au_drinfo **drinfo, int n) ++{ ++ struct au_drinfo **p = drinfo; ++ ++ while (n-- > 0) ++ kfree(*drinfo++); ++ kfree(p); ++} ++ ++int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry, ++ aufs_bindex_t btgt) ++{ ++ int err, ninfo; ++ struct au_drinfo_load w; ++ aufs_bindex_t bindex, bbot; ++ struct au_branch *br; ++ struct inode *h_dir; ++ struct au_dr_hino *ent; ++ struct super_block *sb; ++ ++ AuDbg("%.*s, name %.*s, whname %.*s, b%d\n", ++ AuLNPair(&dentry->d_name), AuLNPair(&lkup->dirren.dr_name), ++ AuLNPair(&lkup->whname), btgt); ++ ++ sb = dentry->d_sb; ++ bbot = au_sbbot(sb); ++ w.ninfo = bbot + 1; ++ if (!lkup->dirren.drinfo) { ++ lkup->dirren.drinfo = kcalloc(w.ninfo, ++ sizeof(*lkup->dirren.drinfo), ++ GFP_NOFS); ++ if (unlikely(!lkup->dirren.drinfo)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ lkup->dirren.ninfo = w.ninfo; ++ } ++ w.drinfo = lkup->dirren.drinfo; ++ w.no_sio = !!uid_eq(current_fsuid(), GLOBAL_ROOT_UID); ++ w.h_ppath.dentry = au_h_dptr(dentry, btgt); ++ AuDebugOn(!w.h_ppath.dentry); ++ w.h_ppath.mnt = au_sbr_mnt(sb, btgt); ++ w.qname = &dentry->d_name; ++ ++ ninfo = 0; ++ for (bindex = btgt + 1; bindex <= bbot; bindex++) { ++ br = au_sbr(sb, bindex); ++ err = au_drinfo_load(&w, bindex, br); ++ if (unlikely(err)) ++ goto out_free; ++ if (w.drinfo[bindex]) ++ ninfo++; ++ } ++ if (!ninfo) { ++ br = au_sbr(sb, btgt); ++ h_dir = d_inode(w.h_ppath.dentry); ++ ent = au_dr_hino_find(&br->br_dirren, h_dir->i_ino); ++ AuDebugOn(!ent); ++ au_dr_hino_del(&br->br_dirren, ent); ++ kfree(ent); ++ } ++ goto out; /* success */ ++ ++out_free: ++ au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo); ++ lkup->dirren.ninfo = 0; ++ lkup->dirren.drinfo = NULL; ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++void au_dr_lkup_fin(struct au_do_lookup_args *lkup) ++{ ++ au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo); ++} ++ ++int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt) ++{ ++ int err; ++ struct au_drinfo *drinfo; ++ ++ err = 0; ++ if (!lkup->dirren.drinfo) ++ goto out; ++ AuDebugOn(lkup->dirren.ninfo < btgt + 1); ++ drinfo = lkup->dirren.drinfo[btgt + 1]; ++ if (!drinfo) ++ goto out; ++ ++ kfree(lkup->whname.name); ++ lkup->whname.name = NULL; ++ lkup->dirren.dr_name.len = drinfo->oldnamelen; ++ lkup->dirren.dr_name.name = drinfo->oldname; ++ lkup->name = &lkup->dirren.dr_name; ++ err = au_wh_name_alloc(&lkup->whname, lkup->name); ++ if (!err) ++ AuDbg("name %.*s, whname %.*s, b%d\n", ++ AuLNPair(lkup->name), AuLNPair(&lkup->whname), ++ btgt); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex, ++ ino_t h_ino) ++{ ++ int match; ++ struct au_drinfo *drinfo; ++ ++ match = 1; ++ if (!lkup->dirren.drinfo) ++ goto out; ++ AuDebugOn(lkup->dirren.ninfo < bindex + 1); ++ drinfo = lkup->dirren.drinfo[bindex + 1]; ++ if (!drinfo) ++ goto out; ++ ++ match = (drinfo->ino == h_ino); ++ AuDbg("match %d\n", match); ++ ++out: ++ return match; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_dr_opt_set(struct super_block *sb) ++{ ++ int err; ++ aufs_bindex_t bindex, bbot; ++ struct au_branch *br; ++ ++ err = 0; ++ bbot = au_sbbot(sb); ++ for (bindex = 0; !err && bindex <= bbot; bindex++) { ++ br = au_sbr(sb, bindex); ++ err = au_dr_hino(sb, bindex, /*br*/NULL, &br->br_path); ++ } ++ ++ return err; ++} ++ ++int au_dr_opt_flush(struct super_block *sb) ++{ ++ int err; ++ aufs_bindex_t bindex, bbot; ++ struct au_branch *br; ++ ++ err = 0; ++ bbot = au_sbbot(sb); ++ for (bindex = 0; !err && bindex <= bbot; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_writable(br->br_perm)) ++ err = au_dr_hino(sb, bindex, /*br*/NULL, /*path*/NULL); ++ } ++ ++ return err; ++} ++ ++int au_dr_opt_clr(struct super_block *sb, int no_flush) ++{ ++ int err; ++ aufs_bindex_t bindex, bbot; ++ struct au_branch *br; ++ ++ err = 0; ++ if (!no_flush) { ++ err = au_dr_opt_flush(sb); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { ++ br = au_sbr(sb, bindex); ++ au_dr_hino_free(&br->br_dirren); ++ } ++ ++out: ++ return err; ++} +diff -urN /usr/share/empty/fs/aufs/dirren.h linux/fs/aufs/dirren.h +--- /usr/share/empty/fs/aufs/dirren.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/aufs/dirren.h 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,145 @@ ++/* ++ * Copyright (C) 2017-2018 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, see . ++ */ ++ ++/* ++ * renamed dir info ++ */ ++ ++#ifndef __AUFS_DIRREN_H__ ++#define __AUFS_DIRREN_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include "hbl.h" ++ ++#define AuDirren_NHASH 100 ++ ++#ifdef CONFIG_AUFS_DIRREN ++/* copied from linux/fs/xfs/uuid.h */ ++typedef struct { ++ unsigned char __u_bits[16]; ++} uuid_t; ++ ++#define __UUID_TMPLT "01234567-0123-4567-0123-456701234567" ++ ++enum au_brid_type { ++ AuBrid_Unset, ++ AuBrid_UUID, ++ AuBrid_FSID, ++ AuBrid_DEV ++}; ++ ++struct au_dr_brid { ++ enum au_brid_type type; ++ union { ++ uuid_t uuid; /* unimplemented yet */ ++ fsid_t fsid; ++ dev_t dev; ++ }; ++}; ++ ++/* 20 is the max digits length of ulong 64 */ ++/* brid-type "_" uuid "_" inum */ ++#define AUFS_DIRREN_FNAME_SZ (1 + 1 + sizeof(__UUID_TMPLT) + 20) ++#define AUFS_DIRREN_ENV_VAL_SZ (AUFS_DIRREN_FNAME_SZ + 1 + 20) ++ ++struct au_dr_hino { ++ struct hlist_bl_node dr_hnode; ++ ino_t dr_h_ino; ++}; ++ ++struct au_dr_br { ++ struct hlist_bl_head dr_h_ino[AuDirren_NHASH]; ++ struct au_dr_brid dr_brid; ++}; ++ ++struct au_dr_lookup { ++ /* dr_name is pointed by struct au_do_lookup_args.name */ ++ struct qstr dr_name; /* subset of dr_info */ ++ aufs_bindex_t ninfo; ++ struct au_drinfo **drinfo; ++}; ++#else ++struct au_dr_hino; ++/* empty */ ++struct au_dr_br { }; ++struct au_dr_lookup { }; ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_branch; ++struct au_do_lookup_args; ++struct au_hinode; ++#ifdef CONFIG_AUFS_DIRREN ++int au_dr_hino_test_add(struct au_dr_br *dr, ino_t h_ino, ++ struct au_dr_hino *add_ent); ++void au_dr_hino_free(struct au_dr_br *dr); ++int au_dr_br_init(struct super_block *sb, struct au_branch *br, ++ const struct path *path); ++int au_dr_br_fin(struct super_block *sb, struct au_branch *br); ++int au_dr_rename(struct dentry *src, aufs_bindex_t bindex, ++ struct qstr *dst_name, void *_rev); ++void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *rev); ++void au_dr_rename_rev(struct dentry *src, aufs_bindex_t bindex, void *rev); ++int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry, ++ aufs_bindex_t bindex); ++int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt); ++int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex, ++ ino_t h_ino); ++void au_dr_lkup_fin(struct au_do_lookup_args *lkup); ++int au_dr_opt_set(struct super_block *sb); ++int au_dr_opt_flush(struct super_block *sb); ++int au_dr_opt_clr(struct super_block *sb, int no_flush); ++#else ++AuStubInt0(au_dr_hino_test_add, struct au_dr_br *dr, ino_t h_ino, ++ struct au_dr_hino *add_ent); ++AuStubVoid(au_dr_hino_free, struct au_dr_br *dr); ++AuStubInt0(au_dr_br_init, struct super_block *sb, struct au_branch *br, ++ const struct path *path); ++AuStubInt0(au_dr_br_fin, struct super_block *sb, struct au_branch *br); ++AuStubInt0(au_dr_rename, struct dentry *src, aufs_bindex_t bindex, ++ struct qstr *dst_name, void *_rev); ++AuStubVoid(au_dr_rename_fin, struct dentry *src, aufs_bindex_t btgt, void *rev); ++AuStubVoid(au_dr_rename_rev, struct dentry *src, aufs_bindex_t bindex, ++ void *rev); ++AuStubInt0(au_dr_lkup, struct au_do_lookup_args *lkup, struct dentry *dentry, ++ aufs_bindex_t bindex); ++AuStubInt0(au_dr_lkup_name, struct au_do_lookup_args *lkup, aufs_bindex_t btgt); ++AuStubInt0(au_dr_lkup_h_ino, struct au_do_lookup_args *lkup, ++ aufs_bindex_t bindex, ino_t h_ino); ++AuStubVoid(au_dr_lkup_fin, struct au_do_lookup_args *lkup); ++AuStubInt0(au_dr_opt_set, struct super_block *sb); ++AuStubInt0(au_dr_opt_flush, struct super_block *sb); ++AuStubInt0(au_dr_opt_clr, struct super_block *sb, int no_flush); ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_DIRREN ++static inline int au_dr_ihash(ino_t h_ino) ++{ ++ return h_ino % AuDirren_NHASH; ++} ++#else ++AuStubInt0(au_dr_ihash, ino_t h_ino); ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DIRREN_H__ */ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c --- /usr/share/empty/fs/aufs/dynop.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/dynop.c 2016-10-09 16:55:36.489368218 +0200 -@@ -0,0 +1,371 @@ ++++ linux/fs/aufs/dynop.c 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,369 @@ +/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima ++ * Copyright (C) 2010-2018 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 @@ -10552,23 +12334,23 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c + * How large will these lists be? + * Usually just a few elements, 20-30 at most for each, I guess. + */ -+static struct au_sphlhead dynop[AuDyLast]; ++static struct hlist_bl_head dynop[AuDyLast]; + -+static struct au_dykey *dy_gfind_get(struct au_sphlhead *sphl, const void *h_op) ++static struct au_dykey *dy_gfind_get(struct hlist_bl_head *hbl, ++ const void *h_op) +{ + struct au_dykey *key, *tmp; -+ struct hlist_head *head; ++ struct hlist_bl_node *pos; + + key = NULL; -+ head = &sphl->head; -+ rcu_read_lock(); -+ hlist_for_each_entry_rcu(tmp, head, dk_hnode) ++ hlist_bl_lock(hbl); ++ hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode) + if (tmp->dk_op.dy_hop == h_op) { + key = tmp; + kref_get(&key->dk_kref); + break; + } -+ rcu_read_unlock(); ++ hlist_bl_unlock(hbl); + + return key; +} @@ -10609,24 +12391,23 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c +} + +/* kref_get() if @key is already added */ -+static struct au_dykey *dy_gadd(struct au_sphlhead *sphl, struct au_dykey *key) ++static struct au_dykey *dy_gadd(struct hlist_bl_head *hbl, struct au_dykey *key) +{ + struct au_dykey *tmp, *found; -+ struct hlist_head *head; ++ struct hlist_bl_node *pos; + const void *h_op = key->dk_op.dy_hop; + + found = NULL; -+ head = &sphl->head; -+ spin_lock(&sphl->spin); -+ hlist_for_each_entry(tmp, head, dk_hnode) ++ hlist_bl_lock(hbl); ++ hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode) + if (tmp->dk_op.dy_hop == h_op) { + kref_get(&tmp->dk_kref); + found = tmp; + break; + } + if (!found) -+ hlist_add_head_rcu(&key->dk_hnode, head); -+ spin_unlock(&sphl->spin); ++ hlist_bl_add_head(&key->dk_hnode, hbl); ++ hlist_bl_unlock(hbl); + + if (!found) + DyPrSym(key); @@ -10639,17 +12420,17 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c + + key = container_of(rcu, struct au_dykey, dk_rcu); + DyPrSym(key); -+ kfree(key); /* not delayed */ ++ kfree(key); +} + +static void dy_free(struct kref *kref) +{ + struct au_dykey *key; -+ struct au_sphlhead *sphl; ++ struct hlist_bl_head *hbl; + + key = container_of(kref, struct au_dykey, dk_kref); -+ sphl = dynop + key->dk_op.dy_type; -+ au_sphl_del_rcu(&key->dk_hnode, sphl); ++ hbl = dynop + key->dk_op.dy_type; ++ au_hbl_del(&key->dk_hnode, hbl); + call_rcu(&key->dk_rcu, dy_free_rcu); +} + @@ -10736,7 +12517,7 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c +static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) +{ + struct au_dykey *key, *old; -+ struct au_sphlhead *sphl; ++ struct hlist_bl_head *hbl; + struct op { + unsigned int sz; + void (*set)(struct au_dykey *key, const void *h_op, @@ -10750,8 +12531,8 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c + }; + const struct op *p; + -+ sphl = dynop + op->dy_type; -+ key = dy_gfind_get(sphl, op->dy_hop); ++ hbl = dynop + op->dy_type; ++ key = dy_gfind_get(hbl, op->dy_hop); + if (key) + goto out_add; /* success */ + @@ -10765,9 +12546,9 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c + key->dk_op.dy_hop = op->dy_hop; + kref_init(&key->dk_kref); + p->set(key, op->dy_hop, au_br_sb(br)); -+ old = dy_gadd(sphl, key); ++ old = dy_gadd(hbl, key); + if (old) { -+ au_delayed_kfree(key); ++ kfree(key); + key = old; + } + @@ -10862,16 +12643,15 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c + +void au_dy_arefresh(int do_dx) +{ -+ struct au_sphlhead *sphl; -+ struct hlist_head *head; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; + struct au_dykey *key; + -+ sphl = dynop + AuDy_AOP; -+ head = &sphl->head; -+ spin_lock(&sphl->spin); -+ hlist_for_each_entry(key, head, dk_hnode) ++ hbl = dynop + AuDy_AOP; ++ hlist_bl_lock(hbl); ++ hlist_bl_for_each_entry(key, pos, hbl, dk_hnode) + dy_adx((void *)key, do_dx); -+ spin_unlock(&sphl->spin); ++ hlist_bl_unlock(hbl); +} + +/* ---------------------------------------------------------------------- */ @@ -10884,7 +12664,7 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c + BUILD_BUG_ON(offsetof(struct au_dyaop, da_key)); + + for (i = 0; i < AuDyLast; i++) -+ au_sphl_init(dynop + i); ++ INIT_HLIST_BL_HEAD(dynop + i); +} + +void au_dy_fin(void) @@ -10892,14 +12672,14 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c + int i; + + for (i = 0; i < AuDyLast; i++) -+ WARN_ON(!hlist_empty(&dynop[i].head)); ++ WARN_ON(!hlist_bl_empty(dynop + i)); +} diff -urN /usr/share/empty/fs/aufs/dynop.h linux/fs/aufs/dynop.h --- /usr/share/empty/fs/aufs/dynop.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/dynop.h 2016-10-09 16:55:36.489368218 +0200 ++++ linux/fs/aufs/dynop.h 2018-04-15 08:49:13.397817296 +0200 @@ -0,0 +1,74 @@ +/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima ++ * Copyright (C) 2010-2018 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 @@ -10939,7 +12719,7 @@ diff -urN /usr/share/empty/fs/aufs/dynop.h linux/fs/aufs/dynop.h + +struct au_dykey { + union { -+ struct hlist_node dk_hnode; ++ struct hlist_bl_node dk_hnode; + struct rcu_head dk_rcu; + }; + struct au_dynop dk_op; @@ -10974,10 +12754,10 @@ diff -urN /usr/share/empty/fs/aufs/dynop.h linux/fs/aufs/dynop.h +#endif /* __AUFS_DYNOP_H__ */ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c --- /usr/share/empty/fs/aufs/export.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/export.c 2016-10-09 16:55:36.489368218 +0200 -@@ -0,0 +1,837 @@ ++++ linux/fs/aufs/export.c 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,836 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -11003,7 +12783,6 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c +#include +#include +#include -+#include "../fs/mount.h" +#include "aufs.h" + +union conv { @@ -11395,7 +13174,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + } + +out_name: -+ au_delayed_free_page((unsigned long)arg.name); ++ free_page((unsigned long)arg.name); +out_file: + fput(file); +out: @@ -11549,7 +13328,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + dentry = ERR_PTR(-ESTALE); + } +out_pathname: -+ au_delayed_free_page((unsigned long)pathname); ++ free_page((unsigned long)pathname); +out_h_parent: + dput(h_parent); +out: @@ -11815,10 +13594,10 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c +} diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c --- /usr/share/empty/fs/aufs/fhsm.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/fhsm.c 2016-10-09 16:55:36.489368218 +0200 -@@ -0,0 +1,426 @@ ++++ linux/fs/aufs/fhsm.c 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,427 @@ +/* -+ * Copyright (C) 2011-2016 Junjiro R. Okajima ++ * Copyright (C) 2011-2018 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 @@ -12002,7 +13781,8 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c + if (atomic_read(&fhsm->fhsm_readable)) + mask = POLLIN /* | POLLRDNORM */; + -+ AuTraceErr((int)mask); ++ if (!mask) ++ AuDbg("mask 0x%x\n", mask); + return mask; +} + @@ -12245,10 +14025,10 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c +} diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c --- /usr/share/empty/fs/aufs/file.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/file.c 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,857 @@ ++++ linux/fs/aufs/file.c 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,848 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -12357,7 +14137,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + +static int au_cmoo(struct dentry *dentry) +{ -+ int err, cmoo; ++ int err, cmoo, matched; + unsigned int udba; + struct path h_path; + struct au_pin pin; @@ -12392,9 +14172,12 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + pid = au_fhsm_pid(fhsm); -+ if (pid -+ && (current->pid == pid -+ || current->real_parent->pid == pid)) ++ rcu_read_lock(); ++ matched = (pid ++ && (current->pid == pid ++ || rcu_dereference(current->real_parent)->pid == pid)); ++ rcu_read_unlock(); ++ if (matched) + goto out; + + br = au_sbr(sb, cpg.bsrc); @@ -12471,11 +14254,11 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + +int au_do_open(struct file *file, struct au_do_open_args *args) +{ -+ int err, no_lock = args->no_lock; ++ int err, aopen = args->aopen; + struct dentry *dentry; + struct au_finfo *finfo; + -+ if (!no_lock) ++ if (!aopen) + err = au_finfo_init(file, args->fidir); + else { + lockdep_off(); @@ -12487,33 +14270,20 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + + dentry = file->f_path.dentry; + AuDebugOn(IS_ERR_OR_NULL(dentry)); -+ if (!no_lock) { -+ di_write_lock_child(dentry); -+ err = au_cmoo(dentry); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (!err) -+ err = args->open(file, vfsub_file_flags(file), NULL); -+ di_read_unlock(dentry, AuLock_IR); -+ } else { -+ err = au_cmoo(dentry); -+ if (!err) -+ err = args->open(file, vfsub_file_flags(file), -+ args->h_file); -+ if (!err && au_fbtop(file) != au_dbtop(dentry)) -+ /* -+ * cmoo happens after h_file was opened. -+ * need to refresh file later. -+ */ -+ atomic_dec(&au_fi(file)->fi_generation); -+ } ++ di_write_lock_child(dentry); ++ err = au_cmoo(dentry); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (!err) ++ err = args->open(file, vfsub_file_flags(file), NULL); ++ di_read_unlock(dentry, AuLock_IR); + + finfo = au_fi(file); + if (!err) { + finfo->fi_file = file; -+ au_sphl_add(&finfo->fi_hlist, -+ &au_sbi(file->f_path.dentry->d_sb)->si_files); ++ au_hbl_add(&finfo->fi_hlist, ++ &au_sbi(file->f_path.dentry->d_sb)->si_files); + } -+ if (!no_lock) ++ if (!aopen) + fi_write_unlock(file); + else { + lockdep_off(); @@ -12522,7 +14292,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + } + if (unlikely(err)) { + finfo->fi_hdir = NULL; -+ au_finfo_fin(file, /*atonce*/0); ++ au_finfo_fin(file); + } + +out: @@ -12842,7 +14612,6 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + +static void au_do_refresh_dir(struct file *file) +{ -+ int execed; + aufs_bindex_t bindex, bbot, new_bindex, brid; + struct au_hfile *p, tmp, *q; + struct au_finfo *finfo; @@ -12881,7 +14650,6 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + } + } + -+ execed = vfsub_file_execed(file); + p = fidir->fd_hfile; + if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) { + bbot = au_sbbot(sb); @@ -12890,14 +14658,14 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + if (p->hf_file) { + if (file_inode(p->hf_file)) + break; -+ au_hfput(p, execed); ++ au_hfput(p, /*execed*/0); + } + } else { + bbot = au_br_index(sb, brid); + for (finfo->fi_btop = 0; finfo->fi_btop < bbot; + finfo->fi_btop++, p++) + if (p->hf_file) -+ au_hfput(p, execed); ++ au_hfput(p, /*execed*/0); + bbot = au_sbbot(sb); + } + @@ -12907,7 +14675,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + if (p->hf_file) { + if (file_inode(p->hf_file)) + break; -+ au_hfput(p, execed); ++ au_hfput(p, /*execed*/0); + } + AuDebugOn(fidir->fd_bbot < finfo->fi_btop); +} @@ -12969,7 +14737,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + +/* common function to regular file and dir */ +int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), -+ int wlock) ++ int wlock, unsigned int fi_lsc) +{ + int err; + unsigned int sigen, figen; @@ -12982,9 +14750,12 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + dentry = file->f_path.dentry; + inode = d_inode(dentry); + sigen = au_sigen(dentry->d_sb); -+ fi_write_lock(file); ++ fi_write_lock_nested(file, fi_lsc); + figen = au_figen(file); -+ di_write_lock_child(dentry); ++ if (!fi_lsc) ++ di_write_lock_child(dentry); ++ else ++ di_write_lock_child2(dentry); + btop = au_dbtop(dentry); + pseudo_link = (btop != au_ibtop(inode)); + if (sigen == figen && !pseudo_link && au_fbtop(file) == btop) { @@ -13106,10 +14877,10 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c +}; diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h --- /usr/share/empty/fs/aufs/file.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/file.h 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,294 @@ ++++ linux/fs/aufs/file.h 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,330 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -13172,11 +14943,8 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h + }; + struct au_fidir *fi_hdir; /* for dir only */ + -+ struct hlist_node fi_hlist; -+ union { -+ struct file *fi_file; /* very ugly */ -+ struct llist_node fi_lnode; /* delayed free */ -+ }; ++ struct hlist_bl_node fi_hlist; ++ struct file *fi_file; /* very ugly */ +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ @@ -13187,7 +14955,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h +struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, + struct file *file, int force_wr); +struct au_do_open_args { -+ int no_lock; ++ int aopen; + int (*open)(struct file *file, int flags, + struct file *h_file); + struct au_fidir *fidir; @@ -13198,7 +14966,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h +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 wlock, unsigned int fi_lsc); +int au_do_flush(struct file *file, fl_owner_t id, + int (*flush)(struct file *file, fl_owner_t id)); + @@ -13224,7 +14992,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h +extern const struct file_operations aufs_file_fop; +int au_do_open_nondir(struct file *file, int flags, struct file *h_file); +int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file); -+struct file *au_read_pre(struct file *file, int keep_fi); ++struct file *au_read_pre(struct file *file, int keep_fi, unsigned int lsc); + +/* finfo.c */ +void au_hfput(struct au_hfile *hf, int execed); @@ -13236,7 +15004,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h +int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink); + +void au_fi_init_once(void *_fi); -+void au_finfo_fin(struct file *file, int atonce); ++void au_finfo_fin(struct file *file); +int au_finfo_init(struct file *file, struct au_fidir *fidir); + +/* ioctl.c */ @@ -13263,6 +15031,45 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h + */ +AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem); + ++/* lock subclass for finfo */ ++enum { ++ AuLsc_FI_1, ++ AuLsc_FI_2 ++}; ++ ++static inline void fi_read_lock_nested(struct file *f, unsigned int lsc) ++{ ++ au_rw_read_lock_nested(&au_fi(f)->fi_rwsem, lsc); ++} ++ ++static inline void fi_write_lock_nested(struct file *f, unsigned int lsc) ++{ ++ au_rw_write_lock_nested(&au_fi(f)->fi_rwsem, lsc); ++} ++ ++/* ++ * fi_read_lock_1, fi_write_lock_1, ++ * fi_read_lock_2, fi_write_lock_2 ++ */ ++#define AuReadLockFunc(name) \ ++static inline void fi_read_lock_##name(struct file *f) \ ++{ fi_read_lock_nested(f, AuLsc_FI_##name); } ++ ++#define AuWriteLockFunc(name) \ ++static inline void fi_write_lock_##name(struct file *f) \ ++{ fi_write_lock_nested(f, AuLsc_FI_##name); } ++ ++#define AuRWLockFuncs(name) \ ++ AuReadLockFunc(name) \ ++ AuWriteLockFunc(name) ++ ++AuRWLockFuncs(1); ++AuRWLockFuncs(2); ++ ++#undef AuReadLockFunc ++#undef AuWriteLockFunc ++#undef AuRWLockFuncs ++ +#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) @@ -13404,10 +15211,10 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h +#endif /* __AUFS_FILE_H__ */ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c --- /usr/share/empty/fs/aufs/finfo.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/finfo.c 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,151 @@ ++++ linux/fs/aufs/finfo.c 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,148 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -13510,7 +15317,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c + +/* ---------------------------------------------------------------------- */ + -+void au_finfo_fin(struct file *file, int atonce) ++void au_finfo_fin(struct file *file) +{ + struct au_finfo *finfo; + @@ -13519,10 +15326,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c + finfo = au_fi(file); + AuDebugOn(finfo->fi_hdir); + AuRwDestroy(&finfo->fi_rwsem); -+ if (!atonce) -+ au_cache_dfree_finfo(finfo); -+ else -+ au_cache_free_finfo(finfo); ++ au_cache_free_finfo(finfo); +} + +void au_fi_init_once(void *_finfo) @@ -13559,10 +15363,10 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c +} diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c --- /usr/share/empty/fs/aufs/f_op.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/f_op.c 2016-10-09 16:55:36.489368218 +0200 -@@ -0,0 +1,772 @@ ++++ linux/fs/aufs/f_op.c 2018-04-15 08:49:13.397817296 +0200 +@@ -0,0 +1,817 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -13662,17 +15466,15 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c +{ + struct au_finfo *finfo; + aufs_bindex_t bindex; -+ int delayed; + + finfo = au_fi(file); -+ au_sphl_del(&finfo->fi_hlist, -+ &au_sbi(file->f_path.dentry->d_sb)->si_files); ++ au_hbl_del(&finfo->fi_hlist, ++ &au_sbi(file->f_path.dentry->d_sb)->si_files); + bindex = finfo->fi_btop; + if (bindex >= 0) + au_set_h_fptr(file, bindex, NULL); + -+ delayed = (current->flags & PF_KTHREAD) || in_interrupt(); -+ au_finfo_fin(file, delayed); ++ au_finfo_fin(file); + return 0; +} + @@ -13705,12 +15507,12 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + */ + +/* Callers should call au_read_post() or fput() in the end */ -+struct file *au_read_pre(struct file *file, int keep_fi) ++struct file *au_read_pre(struct file *file, int keep_fi, unsigned int lsc) +{ + struct file *h_file; + int err; + -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0, lsc); + if (!err) { + di_read_unlock(file->f_path.dentry, AuLock_IR); + h_file = au_hf_top(file); @@ -13731,6 +15533,10 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c +} + +struct au_write_pre { ++ /* input */ ++ unsigned int lsc; ++ ++ /* output */ + blkcnt_t blks; + aufs_bindex_t btop; +}; @@ -13746,9 +15552,13 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + struct file *h_file; + struct dentry *dentry; + int err; ++ unsigned int lsc; + struct au_pin pin; + -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ lsc = 0; ++ if (wpre) ++ lsc = wpre->lsc; ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1, lsc); + h_file = ERR_PTR(err); + if (unlikely(err)) + goto out; @@ -13790,12 +15600,11 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + h_inode = file_inode(h_file); + inode->i_mode = h_inode->i_mode; + ii_write_unlock(inode); -+ fput(h_file); -+ + /* AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); */ + if (written > 0) + au_fhsm_wrote(inode->i_sb, wpre->btop, + /*force*/h_inode->i_blocks > wpre->blks); ++ fput(h_file); +} + +static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, @@ -13810,7 +15619,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + -+ h_file = au_read_pre(file, /*keep_fi*/0); ++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; @@ -13860,6 +15669,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + inode = file_inode(file); + au_mtx_and_read_lock(inode); + ++ wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) @@ -13919,7 +15729,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + -+ h_file = au_read_pre(file, /*keep_fi*/1); ++ h_file = au_read_pre(file, /*keep_fi*/1, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; @@ -13954,6 +15764,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + inode = file_inode(file); + au_mtx_and_read_lock(inode); + ++ wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) @@ -13981,7 +15792,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + -+ h_file = au_read_pre(file, /*keep_fi*/0); ++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; @@ -14008,6 +15819,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + inode = file_inode(file); + au_mtx_and_read_lock(inode); + ++ wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) @@ -14033,6 +15845,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + inode = file_inode(file); + au_mtx_and_read_lock(inode); + ++ wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) @@ -14049,6 +15862,88 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + return err; +} + ++static ssize_t aufs_copy_file_range(struct file *src, loff_t src_pos, ++ struct file *dst, loff_t dst_pos, ++ size_t len, unsigned int flags) ++{ ++ ssize_t err; ++ struct au_write_pre wpre; ++ enum { SRC, DST }; ++ struct { ++ struct inode *inode; ++ struct file *h_file; ++ struct super_block *h_sb; ++ } a[2]; ++#define a_src a[SRC] ++#define a_dst a[DST] ++ ++ err = -EINVAL; ++ a_src.inode = file_inode(src); ++ if (unlikely(!S_ISREG(a_src.inode->i_mode))) ++ goto out; ++ a_dst.inode = file_inode(dst); ++ if (unlikely(!S_ISREG(a_dst.inode->i_mode))) ++ goto out; ++ ++ au_mtx_and_read_lock(a_dst.inode); ++ /* ++ * in order to match the order in di_write_lock2_{child,parent}(), ++ * use f_path.dentry for this comparision. ++ */ ++ if (src->f_path.dentry < dst->f_path.dentry) { ++ a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_1); ++ err = PTR_ERR(a_src.h_file); ++ if (IS_ERR(a_src.h_file)) ++ goto out_si; ++ ++ wpre.lsc = AuLsc_FI_2; ++ a_dst.h_file = au_write_pre(dst, /*do_ready*/1, &wpre); ++ err = PTR_ERR(a_dst.h_file); ++ if (IS_ERR(a_dst.h_file)) { ++ au_read_post(a_src.inode, a_src.h_file); ++ goto out_si; ++ } ++ } else { ++ wpre.lsc = AuLsc_FI_1; ++ a_dst.h_file = au_write_pre(dst, /*do_ready*/1, &wpre); ++ err = PTR_ERR(a_dst.h_file); ++ if (IS_ERR(a_dst.h_file)) ++ goto out_si; ++ ++ a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_2); ++ err = PTR_ERR(a_src.h_file); ++ if (IS_ERR(a_src.h_file)) { ++ au_write_post(a_dst.inode, a_dst.h_file, &wpre, ++ /*written*/0); ++ goto out_si; ++ } ++ } ++ ++ err = -EXDEV; ++ a_src.h_sb = file_inode(a_src.h_file)->i_sb; ++ a_dst.h_sb = file_inode(a_dst.h_file)->i_sb; ++ if (unlikely(a_src.h_sb != a_dst.h_sb)) { ++ AuDbgFile(src); ++ AuDbgFile(dst); ++ goto out_file; ++ } ++ ++ err = vfsub_copy_file_range(a_src.h_file, src_pos, a_dst.h_file, ++ dst_pos, len, flags); ++ ++out_file: ++ au_write_post(a_dst.inode, a_dst.h_file, &wpre, err); ++ fi_read_unlock(src); ++ au_read_post(a_src.inode, a_src.h_file); ++out_si: ++ si_read_unlock(a_dst.inode->i_sb); ++ inode_unlock(a_dst.inode); ++out: ++ return err; ++#undef a_src ++#undef a_dst ++} ++ +/* ---------------------------------------------------------------------- */ + +/* @@ -14178,6 +16073,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + inode = file_inode(file); + au_mtx_and_read_lock(inode); + ++ wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) @@ -14193,54 +16089,6 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + 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_write_pre wpre; -+ struct inode *inode, *h_inode; -+ struct file *file, *h_file; -+ -+ err = 0; /* -EBADF; */ /* posix? */ -+ if (unlikely(!(file->f_mode & FMODE_WRITE))) -+ goto out; -+ -+ file = kio->ki_filp; -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); -+ -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out_unlock; -+ -+ err = -ENOSYS; -+ h_file = au_hf_top(file); -+ if (h_file->f_op->aio_fsync) { -+ h_inode = file_inode(h_file); -+ 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); -+ inode_lock_nested(h_inode, AuLsc_I_CHILD); -+ if (!err) -+ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); -+ /*ignore*/ -+ inode_unlock(h_inode); -+ } -+ au_write_post(inode, h_file, &wpre, /*written*/0); -+ -+out_unlock: -+ si_read_unlock(inode->sb); -+ inode_unlock(inode); -+out: -+ return err; -+} -+#endif -+ +static int aufs_fasync(int fd, struct file *file, int flag) +{ + int err; @@ -14250,7 +16098,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + -+ h_file = au_read_pre(file, /*keep_fi*/0); ++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; @@ -14273,12 +16121,13 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + -+ h_file = au_read_pre(file, /*keep_fi*/0); ++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + -+ arg |= vfsub_file_flags(file) & FASYNC; /* stop calling h_file->fasync */ ++ /* stop calling h_file->fasync */ ++ arg |= vfsub_file_flags(file) & FASYNC; + err = setfl(/*unused fd*/-1, h_file, arg); + fput(h_file); /* instead of au_read_post() */ + @@ -14321,7 +16170,6 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + .flush = aufs_flush_nondir, + .release = aufs_release_nondir, + .fsync = aufs_fsync_nondir, -+ /* .aio_fsync = aufs_aio_fsync_nondir, */ + .fasync = aufs_fasync, + /* .sendpage = aufs_sendpage, */ + .setfl = aufs_setfl, @@ -14331,14 +16179,15 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + .aio_splice_write = aufs_aio_splice_write, + .aio_splice_read = aufs_aio_splice_read, +#endif -+ .fallocate = aufs_fallocate ++ .fallocate = aufs_fallocate, ++ .copy_file_range = aufs_copy_file_range +}; diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h --- /usr/share/empty/fs/aufs/fstype.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/fstype.h 2016-10-09 16:55:36.492701639 +0200 ++++ linux/fs/aufs/fstype.h 2018-04-15 08:49:13.397817296 +0200 @@ -0,0 +1,400 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -14737,12 +16586,80 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +#endif /* __KERNEL__ */ +#endif /* __AUFS_FSTYPE_H__ */ +diff -urN /usr/share/empty/fs/aufs/hbl.h linux/fs/aufs/hbl.h +--- /usr/share/empty/fs/aufs/hbl.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/aufs/hbl.h 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,64 @@ ++/* ++ * Copyright (C) 2017-2018 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, see . ++ */ ++ ++/* ++ * helpers for hlist_bl.h ++ */ ++ ++#ifndef __AUFS_HBL_H__ ++#define __AUFS_HBL_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++static inline void au_hbl_add(struct hlist_bl_node *node, ++ struct hlist_bl_head *hbl) ++{ ++ hlist_bl_lock(hbl); ++ hlist_bl_add_head(node, hbl); ++ hlist_bl_unlock(hbl); ++} ++ ++static inline void au_hbl_del(struct hlist_bl_node *node, ++ struct hlist_bl_head *hbl) ++{ ++ hlist_bl_lock(hbl); ++ hlist_bl_del(node); ++ hlist_bl_unlock(hbl); ++} ++ ++#define au_hbl_for_each(pos, head) \ ++ for (pos = hlist_bl_first(head); \ ++ pos; \ ++ pos = pos->next) ++ ++static inline unsigned long au_hbl_count(struct hlist_bl_head *hbl) ++{ ++ unsigned long cnt; ++ struct hlist_bl_node *pos; ++ ++ cnt = 0; ++ hlist_bl_lock(hbl); ++ au_hbl_for_each(pos, hbl) ++ cnt++; ++ hlist_bl_unlock(hbl); ++ return cnt; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_HBL_H__ */ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c --- /usr/share/empty/fs/aufs/hfsnotify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/hfsnotify.c 2016-10-09 16:55:36.492701639 +0200 ++++ linux/fs/aufs/hfsnotify.c 2018-04-15 08:49:13.401150731 +0200 @@ -0,0 +1,287 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -14775,8 +16692,8 @@ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c + struct au_hnotify *hn = container_of(mark, struct au_hnotify, + hn_mark); + /* AuDbg("here\n"); */ -+ au_cache_dfree_hnotify(hn); -+ smp_mb__before_atomic(); ++ au_cache_free_hnotify(hn); ++ smp_mb__before_atomic(); /* for atomic64_dec */ + if (atomic64_dec_and_test(&au_hfsn_ifree)) + wake_up(&au_hfsn_wq); +} @@ -14899,7 +16816,7 @@ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c + struct au_br_hfsnotify *hfsn = group->private; + + /* AuDbg("here\n"); */ -+ au_delayed_kfree(hfsn); ++ kfree(hfsn); +} + +static int au_hfsn_handle_event(struct fsnotify_group *group, @@ -14993,7 +16910,7 @@ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c + goto out; /* success */ + +out_hfsn: -+ au_delayed_kfree(hfsn); ++ kfree(hfsn); +out: + return err; +} @@ -15030,10 +16947,10 @@ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c +}; diff -urN /usr/share/empty/fs/aufs/hfsplus.c linux/fs/aufs/hfsplus.c --- /usr/share/empty/fs/aufs/hfsplus.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/hfsplus.c 2016-10-09 16:55:36.492701639 +0200 ++++ linux/fs/aufs/hfsplus.c 2018-04-15 08:49:13.401150731 +0200 @@ -0,0 +1,56 @@ +/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima ++ * Copyright (C) 2010-2018 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 @@ -15090,10 +17007,10 @@ diff -urN /usr/share/empty/fs/aufs/hfsplus.c linux/fs/aufs/hfsplus.c +} diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c --- /usr/share/empty/fs/aufs/hnotify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/hnotify.c 2016-10-09 16:55:36.492701639 +0200 -@@ -0,0 +1,723 @@ ++++ linux/fs/aufs/hnotify.c 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,719 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -15129,7 +17046,7 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + AuTraceErr(err); + if (unlikely(err)) { + hinode->hi_notify = NULL; -+ au_cache_dfree_hnotify(hn); ++ 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 @@ -15153,7 +17070,7 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + if (hn) { + hinode->hi_notify = NULL; + if (au_hnotify_op.free(hinode, hn)) -+ au_cache_dfree_hnotify(hn); ++ au_cache_free_hnotify(hn); + } +} + @@ -15416,11 +17333,11 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + if (au_ftest_hnjob(a->flags, TRYXINO0) + && a->inode + && a->h_inode) { -+ inode_lock_nested(a->h_inode, AuLsc_I_CHILD); ++ vfsub_inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD); + if (!a->h_inode->i_nlink + && !(a->h_inode->i_state & I_LINKABLE)) + hn_xino(a->inode, a->h_inode); /* ignore this error */ -+ inode_unlock(a->h_inode); ++ inode_unlock_shared(a->h_inode); + } + + /* make the generation obsolete */ @@ -15556,6 +17473,14 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + AuDebugOn(!sbinfo); + si_write_lock(sb, AuLock_NOPLMW); + ++ if (au_opt_test(sbinfo->si_mntflags, DIRREN)) ++ switch (a->mask & FS_EVENTS_POSS_ON_CHILD) { ++ case FS_MOVED_FROM: ++ case FS_MOVED_TO: ++ AuWarn1("DIRREN with UDBA may not work correctly " ++ "for the direct rename(2)\n"); ++ } ++ + ii_read_lock_parent(a->dir); + bfound = -1; + bbot = au_ibbot(a->dir); @@ -15626,7 +17551,7 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + iput(a->dir); + si_write_unlock(sb); + au_nwt_done(&sbinfo->si_nowait); -+ au_delayed_kfree(a); ++ kfree(a); +} + +/* ---------------------------------------------------------------------- */ @@ -15732,7 +17657,7 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + iput(args->h_child_inode); + iput(args->h_dir); + iput(args->dir); -+ au_delayed_kfree(args); ++ kfree(args); + } + +out: @@ -15773,26 +17698,17 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + +static void au_hn_destroy_cache(void) +{ -+ struct au_cache *cp; -+ -+ flush_delayed_work(&au_dfree.dwork); -+ cp = au_dfree.cache + AuCache_HNOTIFY; -+ AuDebugOn(!llist_empty(&cp->llist)); -+ kmem_cache_destroy(cp->cache); -+ cp->cache = NULL; ++ kmem_cache_destroy(au_cache[AuCache_HNOTIFY]); ++ au_cache[AuCache_HNOTIFY] = NULL; +} + -+AU_CACHE_DFREE_FUNC(hnotify, HNOTIFY, hn_lnode); -+ +int __init au_hnotify_init(void) +{ + int err; -+ struct au_cache *cp; + + err = -ENOMEM; -+ cp = au_dfree.cache + AuCache_HNOTIFY; -+ cp->cache = AuCache(au_hnotify); -+ if (cp->cache) { ++ au_cache[AuCache_HNOTIFY] = AuCache(au_hnotify); ++ if (au_cache[AuCache_HNOTIFY]) { + err = 0; + if (au_hnotify_op.init) + err = au_hnotify_op.init(); @@ -15805,22 +17721,19 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + +void au_hnotify_fin(void) +{ -+ struct au_cache *cp; -+ + if (au_hnotify_op.fin) + au_hnotify_op.fin(); + + /* cf. au_cache_fin() */ -+ cp = au_dfree.cache + AuCache_HNOTIFY; -+ if (cp->cache) ++ if (au_cache[AuCache_HNOTIFY]) + au_hn_destroy_cache(); +} diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c --- /usr/share/empty/fs/aufs/iinfo.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/iinfo.c 2016-10-09 16:55:38.889431135 +0200 ++++ linux/fs/aufs/iinfo.c 2018-04-15 08:49:13.401150731 +0200 @@ -0,0 +1,285 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -16089,7 +18002,7 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c + + iinfo = au_ii(inode); + if (iinfo->ii_vdir) -+ au_vdir_free(iinfo->ii_vdir, /*atonce*/0); ++ au_vdir_free(iinfo->ii_vdir); + + bindex = iinfo->ii_btop; + if (bindex >= 0) { @@ -16101,15 +18014,15 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c + hi++; + } + } -+ au_delayed_kfree(iinfo->ii_hinode); ++ kfree(iinfo->ii_hinode); + AuRwDestroy(&iinfo->ii_rwsem); +} diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c --- /usr/share/empty/fs/aufs/inode.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/inode.c 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,519 @@ ++++ linux/fs/aufs/inode.c 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,527 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -16445,32 +18358,34 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c +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; ++ int err, idx; ++ const int isnondir = d_type != DT_DIR; + + /* 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); ++ if (isnondir) { ++ err = au_xinondir_enter(sb, bindex, h_ino, &idx); ++ if (unlikely(err)) ++ goto out; + } ++ + err = au_xino_read(sb, bindex, h_ino, ino); + if (unlikely(err)) -+ goto out; ++ goto out_xinondir; + + if (!*ino) { + err = -EIO; + *ino = au_xino_new_ino(sb); + if (unlikely(!*ino)) -+ goto out; ++ goto out_xinondir; + err = au_xino_write(sb, bindex, h_ino, *ino); + if (unlikely(err)) -+ goto out; ++ goto out_xinondir; + } + ++out_xinondir: ++ if (isnondir && idx >= 0) ++ au_xinondir_leave(sb, bindex, h_ino, idx); +out: -+ if (mtx) -+ mutex_unlock(mtx); + return err; +} + @@ -16481,9 +18396,8 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + struct inode *inode, *h_inode; + struct dentry *h_dentry; + struct super_block *sb; -+ struct mutex *mtx; + ino_t h_ino, ino; -+ int err; ++ int err, idx, hlinked; + aufs_bindex_t btop; + + sb = dentry->d_sb; @@ -16491,28 +18405,30 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + h_dentry = au_h_dptr(dentry, btop); + h_inode = d_inode(h_dentry); + h_ino = h_inode->i_ino; ++ hlinked = !d_is_dir(h_dentry) && h_inode->i_nlink > 1; + ++new_ino: + /* + * stop 'race'-ing between hardlinks under different + * parents. + */ -+ mtx = NULL; -+ if (!d_is_dir(h_dentry)) -+ mtx = &au_sbr(sb, btop)->br_xino.xi_nondir_mtx; ++ if (hlinked) { ++ err = au_xinondir_enter(sb, btop, h_ino, &idx); ++ inode = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ } + -+new_ino: -+ if (mtx) -+ mutex_lock(mtx); + err = au_xino_read(sb, btop, h_ino, &ino); + inode = ERR_PTR(err); + if (unlikely(err)) -+ goto out; ++ goto out_xinondir; + + if (!ino) { + ino = au_xino_new_ino(sb); + if (unlikely(!ino)) { + inode = ERR_PTR(-EIO); -+ goto out; ++ goto out_xinondir; + } + } + @@ -16520,7 +18436,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + inode = au_iget_locked(sb, ino); + err = PTR_ERR(inode); + if (IS_ERR(inode)) -+ goto out; ++ goto out_xinondir; + + AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); + if (inode->i_state & I_NEW) { @@ -16528,7 +18444,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + err = set_inode(inode, dentry); + if (!err) { + unlock_new_inode(inode); -+ goto out; /* success */ ++ goto out_xinondir; /* success */ + } + + /* @@ -16547,19 +18463,23 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + * horrible race condition between lookup, readdir and copyup + * (or something). + */ -+ if (mtx) -+ mutex_unlock(mtx); ++ if (hlinked && idx >= 0) ++ au_xinondir_leave(sb, btop, h_ino, idx); + err = reval_inode(inode, dentry); + if (unlikely(err < 0)) { -+ mtx = NULL; ++ hlinked = 0; + goto out_iput; + } -+ -+ if (!err) { -+ mtx = NULL; ++ if (!err) + goto out; /* success */ -+ } else if (mtx) -+ mutex_lock(mtx); ++ else if (hlinked && idx >= 0) { ++ err = au_xinondir_enter(sb, btop, h_ino, &idx); ++ if (unlikely(err)) { ++ iput(inode); ++ inode = ERR_PTR(err); ++ goto out; ++ } ++ } + } + + if (unlikely(au_test_fs_unique_ino(h_inode))) @@ -16571,17 +18491,18 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + err = au_xino_write(sb, btop, h_ino, /*ino*/0); + if (!err) { + iput(inode); -+ if (mtx) -+ mutex_unlock(mtx); ++ if (hlinked && idx >= 0) ++ au_xinondir_leave(sb, btop, h_ino, idx); + goto new_ino; + } + +out_iput: + iput(inode); + inode = ERR_PTR(err); ++out_xinondir: ++ if (hlinked && idx >= 0) ++ au_xinondir_leave(sb, btop, h_ino, idx); +out: -+ if (mtx) -+ mutex_unlock(mtx); + return inode; +} + @@ -16629,10 +18550,10 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c +} diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h --- /usr/share/empty/fs/aufs/inode.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/inode.h 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,700 @@ ++++ linux/fs/aufs/inode.h 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,696 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -16668,10 +18589,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h + /* never use fsnotify_add_vfsmount_mark() */ + struct fsnotify_mark hn_mark; +#endif -+ union { -+ struct inode *hn_aufs_inode; /* no get/put */ -+ struct llist_node hn_lnode; /* delayed free */ -+ }; ++ struct inode *hn_aufs_inode; /* no get/put */ +#endif +} ____cacheline_aligned_in_smp; + @@ -16714,10 +18632,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +struct au_icntnr { + struct au_iinfo iinfo; + struct inode vfs_inode; -+ union { -+ struct hlist_node plink; -+ struct llist_node lnode; /* delayed free */ -+ }; ++ struct hlist_bl_node plink; +} ____cacheline_aligned_in_smp; + +/* au_pin flags */ @@ -16843,7 +18758,8 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, + struct au_icpup_args *a); + -+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path); ++int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path, ++ int locked); + +/* i_op_add.c */ +int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, @@ -16871,7 +18787,8 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +/* 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); ++ struct inode *dir, struct dentry *dentry, ++ unsigned int flags); + +/* iinfo.c */ +struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); @@ -16944,17 +18861,11 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, + unsigned int verbose); +ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size); -+ssize_t aufs_getxattr(struct dentry *dentry, struct inode *inode, -+ const char *name, void *value, size_t size); -+int aufs_setxattr(struct dentry *dentry, struct inode *inode, const char *name, -+ const void *value, size_t size, int flags); -+int aufs_removexattr(struct dentry *dentry, const char *name); -+ -+/* void au_xattr_init(struct super_block *sb); */ ++void au_xattr_init(struct super_block *sb); +#else +AuStubInt0(au_cpup_xattr, struct dentry *h_dst, struct dentry *h_src, + int ignore_flags, unsigned int verbose); -+/* AuStubVoid(au_xattr_init, struct super_block *sb); */ ++AuStubVoid(au_xattr_init, struct super_block *sb); +#endif + +#ifdef CONFIG_FS_POSIX_ACL @@ -16965,11 +18876,10 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) +enum { + AU_XATTR_SET, -+ AU_XATTR_REMOVE, + AU_ACL_SET +}; + -+struct au_srxattr { ++struct au_sxattr { + int type; + union { + struct { @@ -16979,16 +18889,13 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h + int flags; + } set; + struct { -+ const char *name; -+ } remove; -+ struct { + struct posix_acl *acl; + int type; + } acl_set; + } u; +}; -+ssize_t au_srxattr(struct dentry *dentry, struct inode *inode, -+ struct au_srxattr *arg); ++ssize_t au_sxattr(struct dentry *dentry, struct inode *inode, ++ struct au_sxattr *arg); +#endif + +/* ---------------------------------------------------------------------- */ @@ -17323,6 +19230,16 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h + au_hn_suspend(hdir); +} + ++#if 0 /* unused */ ++#include "vfsub.h" ++static inline void au_hn_inode_lock_shared_nested(struct au_hinode *hdir, ++ unsigned int sc) ++{ ++ vfsub_inode_lock_shared_nested(hdir->hi_inode, sc); ++ au_hn_suspend(hdir); ++} ++#endif ++ +static inline void au_hn_inode_unlock(struct au_hinode *hdir) +{ + au_hn_resume(hdir); @@ -17333,10 +19250,10 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +#endif /* __AUFS_INODE_H__ */ diff -urN /usr/share/empty/fs/aufs/ioctl.c linux/fs/aufs/ioctl.c --- /usr/share/empty/fs/aufs/ioctl.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/ioctl.c 2016-10-09 16:55:36.492701639 +0200 ++++ linux/fs/aufs/ioctl.c 2018-04-15 08:49:13.401150731 +0200 @@ -0,0 +1,219 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -17556,10 +19473,10 @@ diff -urN /usr/share/empty/fs/aufs/ioctl.c linux/fs/aufs/ioctl.c +#endif diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c --- /usr/share/empty/fs/aufs/i_op_add.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/i_op_add.c 2016-10-09 16:55:36.492701639 +0200 -@@ -0,0 +1,924 @@ ++++ linux/fs/aufs/i_op_add.c 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,928 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -17905,7 +19822,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + if (!try_aopen) + aufs_read_unlock(dentry, AuLock_DW); +out_free: -+ au_delayed_kfree(a); ++ kfree(a); +out: + return err; +} @@ -18272,6 +20189,10 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + goto out_parent; + } + ++ /* ++ * aufs doesn't touch the credential so ++ * security_dentry_create_files_as() is unnecrssary. ++ */ + if (au_opt_test(au_mntflags(sb), PLINK)) { + if (a->bdst < a->bsrc + /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) @@ -18369,7 +20290,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + } + aufs_read_and_write_unlock2(dentry, src_dentry); +out_kfree: -+ au_delayed_kfree(a); ++ kfree(a); +out: + AuTraceErr(err); + return err; @@ -18478,16 +20399,16 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + } + aufs_read_unlock(dentry, AuLock_DW); +out_free: -+ au_delayed_kfree(a); ++ kfree(a); +out: + return err; +} diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c --- /usr/share/empty/fs/aufs/i_op.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/i_op.c 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,1451 @@ ++++ linux/fs/aufs/i_op.c 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,1459 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -18740,27 +20661,28 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c +/* ---------------------------------------------------------------------- */ + +struct aopen_node { -+ struct hlist_node hlist; ++ struct hlist_bl_node hblist; + struct file *file, *h_file; +}; + +static int au_do_aopen(struct inode *inode, struct file *file) +{ -+ struct au_sphlhead *aopen; ++ struct hlist_bl_head *aopen; ++ struct hlist_bl_node *pos; + struct aopen_node *node; + struct au_do_open_args args = { -+ .no_lock = 1, -+ .open = au_do_open_nondir ++ .aopen = 1, ++ .open = au_do_open_nondir + }; + + aopen = &au_sbi(inode->i_sb)->si_aopen; -+ spin_lock(&aopen->spin); -+ hlist_for_each_entry(node, &aopen->head, hlist) ++ hlist_bl_lock(aopen); ++ hlist_bl_for_each_entry(node, pos, aopen, hblist) + if (node->file == file) { + args.h_file = node->h_file; + break; + } -+ spin_unlock(&aopen->spin); ++ hlist_bl_unlock(aopen); + /* AuDebugOn(!args.h_file); */ + + return au_do_open(file, &args); @@ -18770,10 +20692,10 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + struct file *file, unsigned int open_flag, + umode_t create_mode, int *opened) +{ -+ int err, h_opened = *opened; ++ int err, unlocked, h_opened = *opened; + unsigned int lkup_flags; + struct dentry *parent, *d; -+ struct au_sphlhead *aopen; ++ struct hlist_bl_head *aopen; + struct vfsub_aopen_args args = { + .open_flag = open_flag, + .create_mode = create_mode, @@ -18815,6 +20737,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + || !(open_flag & O_CREAT)) + goto out_no_open; + ++ unlocked = 0; + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); + if (unlikely(err)) + goto out; @@ -18845,6 +20768,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + put_filp(args.file); + goto out_unlock; + } ++ di_write_unlock(parent); ++ di_write_unlock(dentry); ++ unlocked = 1; + + /* some filesystems don't set FILE_CREATED while succeeded? */ + *opened |= FILE_CREATED; @@ -18855,17 +20781,21 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + args.file = NULL; + } + aopen = &au_sbi(dir->i_sb)->si_aopen; -+ au_sphl_add(&aopen_node.hlist, aopen); ++ au_hbl_add(&aopen_node.hblist, aopen); + err = finish_open(file, dentry, au_do_aopen, opened); -+ au_sphl_del(&aopen_node.hlist, aopen); ++ au_hbl_del(&aopen_node.hblist, aopen); + AuTraceErr(err); + AuDbgFile(file); + if (aopen_node.h_file) + fput(aopen_node.h_file); + +out_unlock: -+ di_write_unlock(parent); -+ aufs_read_unlock(dentry, AuLock_DW); ++ if (unlocked) ++ si_read_unlock(dentry->d_sb); ++ else { ++ di_write_unlock(parent); ++ aufs_read_unlock(dentry, AuLock_DW); ++ } + AuDbgDentry(dentry); + if (unlikely(err < 0)) + goto out; @@ -18911,10 +20841,10 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) { + h_parent = au_h_dptr(parent, bcpup); + h_dir = d_inode(h_parent); -+ inode_lock_nested(h_dir, AuLsc_I_PARENT); ++ vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT); + err = au_lkup_neg(dentry, bcpup, /*wh*/0); + /* todo: no unlock here */ -+ inode_unlock(h_dir); ++ inode_unlock_shared(h_dir); + + AuDbg("bcpup %d\n", bcpup); + if (!err) { @@ -19298,10 +21228,10 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + a->h_path.dentry = au_h_dptr(dentry, btop); + a->h_inode = d_inode(a->h_path.dentry); + if (ia && (ia->ia_valid & ATTR_SIZE)) { -+ inode_lock_nested(a->h_inode, AuLsc_I_CHILD); ++ vfsub_inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD); + if (ia->ia_size < i_size_read(a->h_inode)) + sz = ia->ia_size; -+ inode_unlock(a->h_inode); ++ inode_unlock_shared(a->h_inode); + } + + hi_wh = NULL; @@ -19376,6 +21306,10 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + inode = d_inode(dentry); + IMustLock(inode); + ++ err = setattr_prepare(dentry, ia); ++ if (unlikely(err)) ++ goto out; ++ + err = -ENOMEM; + a = kzalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) @@ -19394,7 +21328,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + /* currently ftruncate(2) only */ + AuDebugOn(!d_is_reg(dentry)); + file = ia->ia_file; -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1, ++ /*fi_lsc*/0); + if (unlikely(err)) + goto out_si; + ia->ia_file = au_hf_top(file); @@ -19484,7 +21419,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c +out_si: + si_read_unlock(sb); +out_kfree: -+ au_delayed_kfree(a); ++ kfree(a); +out: + AuTraceErr(err); + return err; @@ -19519,8 +21454,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + return err; +} + -+ssize_t au_srxattr(struct dentry *dentry, struct inode *inode, -+ struct au_srxattr *arg) ++ssize_t au_sxattr(struct dentry *dentry, struct inode *inode, ++ struct au_sxattr *arg) +{ + int err; + struct path h_path; @@ -19554,13 +21489,11 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + arg->u.set.name, arg->u.set.value, + arg->u.set.size, arg->u.set.flags); + break; -+ case AU_XATTR_REMOVE: -+ err = vfsub_removexattr(h_path.dentry, arg->u.remove.name); -+ break; + case AU_ACL_SET: + err = -EOPNOTSUPP; + h_inode = d_inode(h_path.dentry); + if (h_inode->i_op->set_acl) ++ /* this will call posix_acl_update_mode */ + err = h_inode->i_op->set_acl(h_inode, + arg->u.acl_set.acl, + arg->u.acl_set.type); @@ -19577,7 +21510,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + di_write_unlock(dentry); + si_read_unlock(sb); +out_kfree: -+ au_delayed_kfree(a); ++ kfree(a); +out: + AuTraceErr(err); + return err; @@ -19614,11 +21547,12 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c +} + +/* -+ * common routine for aufs_getattr() and aufs_getxattr(). ++ * common routine for aufs_getattr() and au_getxattr(). + * returns zero or negative (an error). + * @dentry will be read-locked in success. + */ -+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path) ++int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path, ++ int locked) +{ + int err; + unsigned int mnt_flags, sigen; @@ -19635,6 +21569,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + mnt_flags = au_mntflags(sb); + udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); + ++ if (unlikely(locked)) ++ goto body; /* skip locking dinfo */ ++ + /* support fstat(2) */ + if (!d_unlinked(dentry) && !udba_none) { + sigen = au_sigen(sb); @@ -19662,6 +21599,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + } else + di_read_lock_child(dentry, AuLock_IR); + ++body: + inode = d_inode(dentry); + bindex = au_ibtop(inode); + h_path->mnt = au_sbr_mnt(sb, bindex); @@ -19700,7 +21638,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out; -+ err = au_h_path_getattr(dentry, /*force*/0, &h_path); ++ err = au_h_path_getattr(dentry, /*force*/0, &h_path, /*locked*/0); + if (unlikely(err)) + goto out_si; + if (unlikely(!h_path.dentry)) @@ -19781,7 +21719,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + err = 0; + AuDbg("%pf\n", h_inode->i_op->get_link); + AuDbgDentry(h_dentry); -+ ret = h_inode->i_op->get_link(h_dentry, h_inode, done); ++ ret = vfs_get_link(h_dentry, done); + dput(h_dentry); + if (IS_ERR(ret)) + err = PTR_ERR(ret); @@ -19875,10 +21813,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + .getattr = aufs_getattr, + +#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, + .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, +#endif + + .readlink = generic_readlink, @@ -19907,10 +21842,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + .getattr = aufs_getattr, + +#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, + .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, +#endif + + .update_time = aufs_update_time, @@ -19928,10 +21860,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + .getattr = aufs_getattr, + +#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, + .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, +#endif + + .update_time = aufs_update_time @@ -19939,10 +21868,10 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c +}; diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c --- /usr/share/empty/fs/aufs/i_op_del.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/i_op_del.c 2016-10-09 16:55:36.492701639 +0200 ++++ linux/fs/aufs/i_op_del.c 2018-04-15 08:49:13.401150731 +0200 @@ -0,0 +1,511 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -20337,7 +22266,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c +out_unlock: + aufs_read_unlock(dentry, AuLock_DW); +out_free: -+ au_delayed_kfree(a); ++ kfree(a); +out: + return err; +} @@ -20447,17 +22376,17 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c +out_unlock: + aufs_read_unlock(dentry, AuLock_DW); +out_free: -+ au_delayed_kfree(a); ++ kfree(a); +out: + AuTraceErr(err); + return err; +} diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c --- /usr/share/empty/fs/aufs/i_op_ren.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/i_op_ren.c 2016-10-09 16:55:36.492701639 +0200 -@@ -0,0 +1,1015 @@ ++++ linux/fs/aufs/i_op_ren.c 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,1246 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -20483,27 +22412,37 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c +enum { AuSRC, AuDST, AuSrcDst }; +enum { AuPARENT, AuCHILD, AuParentChild }; + -+#define AuRen_ISDIR 1 -+#define AuRen_ISSAMEDIR (1 << 1) -+#define AuRen_WHSRC (1 << 2) -+#define AuRen_WHDST (1 << 3) -+#define AuRen_MNT_WRITE (1 << 4) -+#define AuRen_DT_DSTDIR (1 << 5) -+#define AuRen_DIROPQ (1 << 6) ++#define AuRen_ISDIR_SRC 1 ++#define AuRen_ISDIR_DST (1 << 1) ++#define AuRen_ISSAMEDIR (1 << 2) ++#define AuRen_WHSRC (1 << 3) ++#define AuRen_WHDST (1 << 4) ++#define AuRen_MNT_WRITE (1 << 5) ++#define AuRen_DT_DSTDIR (1 << 6) ++#define AuRen_DIROPQ_SRC (1 << 7) ++#define AuRen_DIROPQ_DST (1 << 8) ++#define AuRen_DIRREN (1 << 9) ++#define AuRen_DROPPED_SRC (1 << 10) ++#define AuRen_DROPPED_DST (1 << 11) +#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) +#define au_fset_ren(flags, name) \ + do { (flags) |= AuRen_##name; } while (0) +#define au_fclr_ren(flags, name) \ + do { (flags) &= ~AuRen_##name; } while (0) + ++#ifndef CONFIG_AUFS_DIRREN ++#undef AuRen_DIRREN ++#define AuRen_DIRREN 0 ++#endif ++ +struct au_ren_args { + struct { + struct dentry *dentry, *h_dentry, *parent, *h_parent, + *wh_dentry; + struct inode *dir, *inode; -+ struct au_hinode *hdir; ++ struct au_hinode *hdir, *hinode; + struct au_dtime dt[AuParentChild]; -+ aufs_bindex_t btop; ++ aufs_bindex_t btop, bdiropq; + } sd[AuSrcDst]; + +#define src_dentry sd[AuSRC].dentry @@ -20514,9 +22453,11 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c +#define src_h_parent sd[AuSRC].h_parent +#define src_wh_dentry sd[AuSRC].wh_dentry +#define src_hdir sd[AuSRC].hdir ++#define src_hinode sd[AuSRC].hinode +#define src_h_dir sd[AuSRC].hdir->hi_inode +#define src_dt sd[AuSRC].dt +#define src_btop sd[AuSRC].btop ++#define src_bdiropq sd[AuSRC].bdiropq + +#define dst_dentry sd[AuDST].dentry +#define dst_dir sd[AuDST].dir @@ -20526,21 +22467,27 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c +#define dst_h_parent sd[AuDST].h_parent +#define dst_wh_dentry sd[AuDST].wh_dentry +#define dst_hdir sd[AuDST].hdir ++#define dst_hinode sd[AuDST].hinode +#define dst_h_dir sd[AuDST].hdir->hi_inode +#define dst_dt sd[AuDST].dt +#define dst_btop sd[AuDST].btop ++#define dst_bdiropq sd[AuDST].bdiropq + + struct dentry *h_trap; + struct au_branch *br; -+ struct au_hinode *src_hinode; + struct path h_path; + struct au_nhash whlist; -+ aufs_bindex_t btgt, src_bwh, src_bdiropq; ++ aufs_bindex_t btgt, src_bwh; + -+ unsigned int flags; ++ struct { ++ unsigned short auren_flags; ++ unsigned char flags; /* syscall parameter */ ++ unsigned char exchange; ++ } __packed; + + struct au_whtmp_rmdir *thargs; + struct dentry *h_dst; ++ struct au_hinode *h_root; +}; + +/* ---------------------------------------------------------------------- */ @@ -20559,16 +22506,29 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + err = -EIO; \ +} while (0) + -+static void au_ren_rev_diropq(int err, struct au_ren_args *a) ++static void au_ren_do_rev_diropq(int err, struct au_ren_args *a, int idx) +{ + int rerr; ++ struct dentry *d; ++#define src_or_dst(member) a->sd[idx].member + -+ au_hn_inode_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(a->src_dentry, a->btgt); -+ au_hn_inode_unlock(a->src_hinode); -+ au_set_dbdiropq(a->src_dentry, a->src_bdiropq); ++ d = src_or_dst(dentry); /* {src,dst}_dentry */ ++ au_hn_inode_lock_nested(src_or_dst(hinode), AuLsc_I_CHILD); ++ rerr = au_diropq_remove(d, a->btgt); ++ au_hn_inode_unlock(src_or_dst(hinode)); ++ au_set_dbdiropq(d, src_or_dst(bdiropq)); + if (rerr) -+ RevertFailure("remove diropq %pd", a->src_dentry); ++ RevertFailure("remove diropq %pd", d); ++ ++#undef src_or_dst_ ++} ++ ++static void au_ren_rev_diropq(int err, struct au_ren_args *a) ++{ ++ if (au_ftest_ren(a->auren_flags, DIROPQ_SRC)) ++ au_ren_do_rev_diropq(err, a, AuSRC); ++ if (au_ftest_ren(a->auren_flags, DIROPQ_DST)) ++ au_ren_do_rev_diropq(err, a, AuDST); +} + +static void au_ren_rev_rename(int err, struct au_ren_args *a) @@ -20587,7 +22547,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + delegated = NULL; + rerr = vfsub_rename(a->dst_h_dir, + au_h_dptr(a->src_dentry, a->btgt), -+ a->src_h_dir, &a->h_path, &delegated); ++ a->src_h_dir, &a->h_path, &delegated, a->flags); + if (unlikely(rerr == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal rename\n"); @@ -20620,7 +22580,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + + delegated = NULL; + rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, -+ &delegated); ++ &delegated, a->flags); + if (unlikely(rerr == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal rename\n"); @@ -20662,13 +22622,11 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + d = a->src_dentry; + if (au_dbtop(d) == a->btgt) { + a->h_path.dentry = a->dst_h_dentry; -+ if (au_ftest_ren(a->flags, DIROPQ) -+ && au_dbdiropq(d) == a->btgt) -+ au_fclr_ren(a->flags, DIROPQ); + AuDebugOn(au_dbtop(d) != a->btgt); + delegated = NULL; + err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), -+ a->dst_h_dir, &a->h_path, &delegated); ++ a->dst_h_dir, &a->h_path, &delegated, ++ a->flags); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal rename\n"); @@ -20712,127 +22670,186 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c +} + +/* make it 'opaque' dir. */ -+static int au_ren_diropq(struct au_ren_args *a) ++static int au_ren_do_diropq(struct au_ren_args *a, int idx) +{ + int err; -+ struct dentry *diropq; ++ struct dentry *d, *diropq; ++#define src_or_dst(member) a->sd[idx].member + + err = 0; -+ a->src_bdiropq = au_dbdiropq(a->src_dentry); -+ a->src_hinode = au_hi(a->src_inode, a->btgt); -+ au_hn_inode_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ diropq = au_diropq_create(a->src_dentry, a->btgt); -+ au_hn_inode_unlock(a->src_hinode); ++ d = src_or_dst(dentry); /* {src,dst}_dentry */ ++ src_or_dst(bdiropq) = au_dbdiropq(d); ++ src_or_dst(hinode) = au_hi(src_or_dst(inode), a->btgt); ++ au_hn_inode_lock_nested(src_or_dst(hinode), AuLsc_I_CHILD); ++ diropq = au_diropq_create(d, a->btgt); ++ au_hn_inode_unlock(src_or_dst(hinode)); + if (IS_ERR(diropq)) + err = PTR_ERR(diropq); + else + dput(diropq); + ++#undef src_or_dst_ + return err; +} + -+static int do_rename(struct au_ren_args *a) ++static int au_ren_diropq(struct au_ren_args *a) +{ + int err; -+ struct dentry *d, *h_d; ++ unsigned char always; ++ struct dentry *d; + -+ /* prepare workqueue args for asynchronous rmdir */ -+ h_d = a->dst_h_dentry; -+ if (au_ftest_ren(a->flags, ISDIR) && d_is_positive(h_d)) { -+ err = -ENOMEM; -+ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); -+ if (unlikely(!a->thargs)) ++ err = 0; ++ d = a->dst_dentry; /* already renamed on the branch */ ++ always = !!au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ); ++ if (au_ftest_ren(a->auren_flags, ISDIR_SRC) ++ && !au_ftest_ren(a->auren_flags, DIRREN) ++ && a->btgt != au_dbdiropq(a->src_dentry) ++ && (a->dst_wh_dentry ++ || a->btgt <= au_dbdiropq(d) ++ /* hide the lower to keep xino */ ++ /* the lowers may not be a dir, but we hide them anyway */ ++ || a->btgt < au_dbbot(d) ++ || always)) { ++ AuDbg("here\n"); ++ err = au_ren_do_diropq(a, AuSRC); ++ if (unlikely(err)) + goto out; -+ a->h_dst = dget(h_d); -+ } -+ -+ /* create whiteout for src_dentry */ -+ if (au_ftest_ren(a->flags, WHSRC)) { -+ a->src_bwh = au_dbwh(a->src_dentry); -+ AuDebugOn(a->src_bwh >= 0); -+ a->src_wh_dentry -+ = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); -+ err = PTR_ERR(a->src_wh_dentry); -+ if (IS_ERR(a->src_wh_dentry)) -+ goto out_thargs; -+ } -+ -+ /* lookup whiteout for dentry */ -+ if (au_ftest_ren(a->flags, WHDST)) { -+ h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, -+ a->br); -+ err = PTR_ERR(h_d); -+ if (IS_ERR(h_d)) -+ goto out_whsrc; -+ if (d_is_negative(h_d)) -+ dput(h_d); -+ else -+ a->dst_wh_dentry = h_d; ++ au_fset_ren(a->auren_flags, DIROPQ_SRC); + } ++ if (!a->exchange) ++ goto out; /* success */ + -+ /* rename dentry to tmpwh */ -+ if (a->thargs) { -+ err = au_whtmp_ren(a->dst_h_dentry, a->br); ++ d = a->src_dentry; /* already renamed on the branch */ ++ if (au_ftest_ren(a->auren_flags, ISDIR_DST) ++ && a->btgt != au_dbdiropq(a->dst_dentry) ++ && (a->btgt < au_dbdiropq(d) ++ || a->btgt < au_dbbot(d) ++ || always)) { ++ AuDbgDentry(a->src_dentry); ++ AuDbgDentry(a->dst_dentry); ++ err = au_ren_do_diropq(a, AuDST); + if (unlikely(err)) -+ goto out_whdst; ++ goto out_rev_src; ++ au_fset_ren(a->auren_flags, DIROPQ_DST); ++ } ++ goto out; /* success */ + -+ d = a->dst_dentry; -+ au_set_h_dptr(d, a->btgt, NULL); -+ err = au_lkup_neg(d, a->btgt, /*wh*/0); -+ if (unlikely(err)) -+ goto out_whtmp; -+ a->dst_h_dentry = au_h_dptr(d, a->btgt); ++out_rev_src: ++ AuDbg("err %d, reverting src\n", err); ++ au_ren_rev_diropq(err, a); ++out: ++ return err; ++} ++ ++static int do_rename(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d, *h_d; ++ ++ if (!a->exchange) { ++ /* prepare workqueue args for asynchronous rmdir */ ++ h_d = a->dst_h_dentry; ++ if (au_ftest_ren(a->auren_flags, ISDIR_DST) ++ /* && !au_ftest_ren(a->auren_flags, DIRREN) */ ++ && d_is_positive(h_d)) { ++ err = -ENOMEM; ++ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, ++ GFP_NOFS); ++ if (unlikely(!a->thargs)) ++ goto out; ++ a->h_dst = dget(h_d); ++ } ++ ++ /* create whiteout for src_dentry */ ++ if (au_ftest_ren(a->auren_flags, WHSRC)) { ++ a->src_bwh = au_dbwh(a->src_dentry); ++ AuDebugOn(a->src_bwh >= 0); ++ a->src_wh_dentry = au_wh_create(a->src_dentry, a->btgt, ++ a->src_h_parent); ++ err = PTR_ERR(a->src_wh_dentry); ++ if (IS_ERR(a->src_wh_dentry)) ++ goto out_thargs; ++ } ++ ++ /* lookup whiteout for dentry */ ++ if (au_ftest_ren(a->auren_flags, WHDST)) { ++ h_d = au_wh_lkup(a->dst_h_parent, ++ &a->dst_dentry->d_name, a->br); ++ err = PTR_ERR(h_d); ++ if (IS_ERR(h_d)) ++ goto out_whsrc; ++ if (d_is_negative(h_d)) ++ dput(h_d); ++ else ++ a->dst_wh_dentry = h_d; ++ } ++ ++ /* rename dentry to tmpwh */ ++ if (a->thargs) { ++ err = au_whtmp_ren(a->dst_h_dentry, a->br); ++ if (unlikely(err)) ++ goto out_whdst; ++ ++ d = a->dst_dentry; ++ au_set_h_dptr(d, a->btgt, NULL); ++ err = au_lkup_neg(d, a->btgt, /*wh*/0); ++ if (unlikely(err)) ++ goto out_whtmp; ++ a->dst_h_dentry = au_h_dptr(d, a->btgt); ++ } + } + + BUG_ON(d_is_positive(a->dst_h_dentry) && a->src_btop != a->btgt); ++#if 0 ++ BUG_ON(!au_ftest_ren(a->auren_flags, DIRREN) ++ && d_is_positive(a->dst_h_dentry) ++ && a->src_btop != a->btgt); ++#endif + + /* rename by vfs_rename or cpup */ -+ d = a->dst_dentry; -+ if (au_ftest_ren(a->flags, ISDIR) -+ && (a->dst_wh_dentry -+ || au_dbdiropq(d) == a->btgt -+ /* hide the lower to keep xino */ -+ || a->btgt < au_dbbot(d) -+ || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) -+ au_fset_ren(a->flags, DIROPQ); + err = au_ren_or_cpup(a); + if (unlikely(err)) + /* leave the copied-up one */ + goto out_whtmp; + + /* make dir opaque */ -+ if (au_ftest_ren(a->flags, DIROPQ)) { -+ err = au_ren_diropq(a); -+ if (unlikely(err)) -+ goto out_rename; -+ } ++ err = au_ren_diropq(a); ++ if (unlikely(err)) ++ goto out_rename; + + /* update target timestamps */ ++ if (a->exchange) { ++ AuDebugOn(au_dbtop(a->dst_dentry) != a->btgt); ++ a->h_path.dentry = au_h_dptr(a->dst_dentry, a->btgt); ++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ ++ a->dst_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; ++ } + AuDebugOn(au_dbtop(a->src_dentry) != a->btgt); + a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); + vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ + a->src_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; + -+ /* remove whiteout for dentry */ -+ if (a->dst_wh_dentry) { -+ a->h_path.dentry = a->dst_wh_dentry; -+ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, -+ a->dst_dentry); -+ if (unlikely(err)) -+ goto out_diropq; -+ } ++ if (!a->exchange) { ++ /* remove whiteout for dentry */ ++ if (a->dst_wh_dentry) { ++ a->h_path.dentry = a->dst_wh_dentry; ++ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, ++ a->dst_dentry); ++ if (unlikely(err)) ++ goto out_diropq; ++ } + -+ /* remove whtmp */ -+ if (a->thargs) -+ au_ren_del_whtmp(a); /* ignore this error */ ++ /* remove whtmp */ ++ if (a->thargs) ++ au_ren_del_whtmp(a); /* ignore this error */ + -+ au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); ++ au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); ++ } + err = 0; + goto out_success; + +out_diropq: -+ if (au_ftest_ren(a->flags, DIROPQ)) -+ au_ren_rev_diropq(err, a); ++ au_ren_rev_diropq(err, a); +out_rename: + au_ren_rev_rename(err, a); + dput(a->h_dst); @@ -20870,25 +22887,35 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c +} + +/* -+ * test if @dentry dir can be rename source or not. -+ * if it can, return 0 and @children is filled. ++ * test if @a->src_dentry dir can be rename source or not. ++ * if it can, return 0. + * success means, + * - it is a logically empty dir. + * - or, it exists on writable branch and has no children including whiteouts -+ * on the lower branch. ++ * on the lower branch unless DIRREN is on. + */ -+static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) ++static int may_rename_srcdir(struct au_ren_args *a) +{ + int err; + unsigned int rdhash; -+ aufs_bindex_t btop; ++ aufs_bindex_t btop, btgt; ++ struct dentry *dentry; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ dentry = a->src_dentry; ++ sb = dentry->d_sb; ++ sbinfo = au_sbi(sb); ++ if (au_opt_test(sbinfo->si_mntflags, DIRREN)) ++ au_fset_ren(a->auren_flags, DIRREN); + ++ btgt = a->btgt; + btop = au_dbtop(dentry); + if (btop != btgt) { + struct au_nhash whlist; + -+ SiMustAnyLock(dentry->d_sb); -+ rdhash = au_sbi(dentry->d_sb)->si_rdhash; ++ SiMustAnyLock(sb); ++ rdhash = sbinfo->si_rdhash; + if (!rdhash) + rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, + dentry)); @@ -20907,9 +22934,13 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + +out: + if (err == -ENOTEMPTY) { -+ AuWarn1("renaming dir who has child(ren) on multiple branches," -+ " is not supported\n"); -+ err = -EXDEV; ++ if (au_ftest_ren(a->auren_flags, DIRREN)) { ++ err = 0; ++ } else { ++ AuWarn1("renaming dir who has child(ren) on multiple " ++ "branches, is not supported\n"); ++ err = -EXDEV; ++ } + } + return err; +} @@ -20925,7 +22956,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + SiMustAnyLock(d->d_sb); + + err = 0; -+ if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { ++ if (au_ftest_ren(a->auren_flags, ISDIR_DST) && a->dst_inode) { + rdhash = au_sbi(d->d_sb)->si_rdhash; + if (!rdhash) + rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); @@ -20933,9 +22964,12 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + if (unlikely(err)) + goto out; + -+ au_set_dbtop(d, a->dst_btop); -+ err = may_rename_dstdir(d, &a->whlist); -+ au_set_dbtop(d, a->btgt); ++ if (!a->exchange) { ++ au_set_dbtop(d, a->dst_btop); ++ err = may_rename_dstdir(d, &a->whlist); ++ au_set_dbtop(d, a->btgt); ++ } else ++ err = may_rename_srcdir(a); + } + a->dst_h_dentry = au_h_dptr(d, au_dbtop(d)); + if (unlikely(err)) @@ -20943,8 +22977,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + + d = a->src_dentry; + a->src_h_dentry = au_h_dptr(d, au_dbtop(d)); -+ if (au_ftest_ren(a->flags, ISDIR)) { -+ err = may_rename_srcdir(d, a->btgt); ++ if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) { ++ err = may_rename_srcdir(a); + if (unlikely(err)) { + au_nhash_wh_free(&a->whlist); + a->whlist.nh_num = 0; @@ -20967,7 +23001,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + + if (a->src_btop == a->btgt) { + err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, -+ au_ftest_ren(a->flags, ISDIR)); ++ au_ftest_ren(a->auren_flags, ISDIR_SRC)); + if (unlikely(err)) + goto out; + err = -EINVAL; @@ -20984,7 +23018,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + goto out; + + err = -EIO; -+ isdir = !!au_ftest_ren(a->flags, ISDIR); ++ isdir = !!au_ftest_ren(a->auren_flags, ISDIR_DST); + if (d_really_is_negative(a->dst_dentry)) { + if (d_is_negative(a->dst_h_dentry)) + err = au_may_add(a->dst_dentry, a->btgt, @@ -21034,7 +23068,10 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c +{ + vfsub_unlock_rename(a->src_h_parent, a->src_hdir, + a->dst_h_parent, a->dst_hdir); -+ if (au_ftest_ren(a->flags, MNT_WRITE)) ++ if (au_ftest_ren(a->auren_flags, DIRREN) ++ && a->h_root) ++ au_hn_inode_unlock(a->h_root); ++ if (au_ftest_ren(a->auren_flags, MNT_WRITE)) + vfsub_mnt_drop_write(au_br_mnt(a->br)); +} + @@ -21052,7 +23089,24 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + err = vfsub_mnt_want_write(au_br_mnt(a->br)); + if (unlikely(err)) + goto out; -+ au_fset_ren(a->flags, MNT_WRITE); ++ au_fset_ren(a->auren_flags, MNT_WRITE); ++ if (au_ftest_ren(a->auren_flags, DIRREN)) { ++ struct dentry *root; ++ struct inode *dir; ++ ++ /* ++ * sbinfo is already locked, so this ii_read_lock is ++ * unnecessary. but our debugging feature checks it. ++ */ ++ root = a->src_inode->i_sb->s_root; ++ if (root != a->src_parent && root != a->dst_parent) { ++ dir = d_inode(root); ++ ii_read_lock_parent3(dir); ++ a->h_root = au_hi(dir, a->btgt); ++ ii_read_unlock(dir); ++ au_hn_inode_lock_nested(a->h_root, AuLsc_I_PARENT3); ++ } ++ } + a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, + a->dst_h_parent, a->dst_hdir); + udba = au_opt_udba(a->src_dentry->d_sb); @@ -21085,20 +23139,30 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + + dir = a->dst_dir; + dir->i_version++; -+ if (au_ftest_ren(a->flags, ISDIR)) { ++ if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) { + /* is this updating defined in POSIX? */ + au_cpup_attr_timesizes(a->src_inode); + au_cpup_attr_nlink(dir, /*force*/1); + } -+ + au_dir_ts(dir, a->btgt); + -+ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ if (a->exchange) { ++ dir = a->src_dir; ++ dir->i_version++; ++ if (au_ftest_ren(a->auren_flags, ISDIR_DST)) { ++ /* is this updating defined in POSIX? */ ++ au_cpup_attr_timesizes(a->dst_inode); ++ au_cpup_attr_nlink(dir, /*force*/1); ++ } ++ au_dir_ts(dir, a->btgt); ++ } ++ ++ if (au_ftest_ren(a->auren_flags, ISSAMEDIR)) + return; + + dir = a->src_dir; + dir->i_version++; -+ if (au_ftest_ren(a->flags, ISDIR)) ++ if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) + au_cpup_attr_nlink(dir, /*force*/1); + au_dir_ts(dir, a->btgt); +} @@ -21118,13 +23182,16 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + + i = a->dst_inode; + if (i) { -+ if (!au_ftest_ren(a->flags, ISDIR)) -+ vfsub_drop_nlink(i); -+ else { -+ vfsub_dead_dir(i); -+ au_cpup_attr_timesizes(i); -+ } -+ au_update_dbrange(d, /*do_put_zero*/1); ++ if (!a->exchange) { ++ if (!au_ftest_ren(a->auren_flags, ISDIR_DST)) ++ vfsub_drop_nlink(i); ++ else { ++ vfsub_dead_dir(i); ++ au_cpup_attr_timesizes(i); ++ } ++ au_update_dbrange(d, /*do_put_zero*/1); ++ } else ++ au_cpup_attr_nlink(i, /*force*/1); + } else { + bbot = a->btgt; + for (bindex = au_dbtop(d); bindex < bbot; bindex++) @@ -21135,6 +23202,14 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + au_update_dbrange(d, /*do_put_zero*/0); + } + ++ if (a->exchange ++ || au_ftest_ren(a->auren_flags, DIRREN)) { ++ d_drop(a->src_dentry); ++ if (au_ftest_ren(a->auren_flags, DIRREN)) ++ au_set_dbwh(a->src_dentry, -1); ++ return; ++ } ++ + d = a->src_dentry; + au_set_dbwh(d, -1); + bbot = au_dbbot(d); @@ -21197,7 +23272,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + + a->src_btop = au_dbtop(a->src_dentry); + a->dst_btop = au_dbtop(a->dst_dentry); -+ if (au_ftest_ren(a->flags, ISDIR)) ++ if (au_ftest_ren(a->auren_flags, ISDIR_SRC) ++ || au_ftest_ren(a->auren_flags, ISDIR_DST)) + au_fset_wrdir(wr_dir_args.flags, ISDIR); + wr_dir_args.force_btgt = a->src_btop; + if (a->dst_inode && a->dst_btop < a->src_btop) @@ -21205,6 +23281,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); + err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); + a->btgt = err; ++ if (a->exchange) ++ au_update_dbtop(a->dst_dentry); + + return err; +} @@ -21213,19 +23291,20 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c +{ + a->h_path.dentry = a->src_h_parent; + au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); -+ if (!au_ftest_ren(a->flags, ISSAMEDIR)) { ++ if (!au_ftest_ren(a->auren_flags, ISSAMEDIR)) { + a->h_path.dentry = a->dst_h_parent; + au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); + } + -+ au_fclr_ren(a->flags, DT_DSTDIR); -+ if (!au_ftest_ren(a->flags, ISDIR)) ++ au_fclr_ren(a->auren_flags, DT_DSTDIR); ++ if (!au_ftest_ren(a->auren_flags, ISDIR_SRC) ++ && !a->exchange) + return; + + a->h_path.dentry = a->src_h_dentry; + au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); + if (d_is_positive(a->dst_h_dentry)) { -+ au_fset_ren(a->flags, DT_DSTDIR); ++ au_fset_ren(a->auren_flags, DT_DSTDIR); + a->h_path.dentry = a->dst_h_dentry; + au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); + } @@ -21237,17 +23316,17 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + struct inode *h_inode; + + au_dtime_revert(a->src_dt + AuPARENT); -+ if (!au_ftest_ren(a->flags, ISSAMEDIR)) ++ if (!au_ftest_ren(a->auren_flags, ISSAMEDIR)) + au_dtime_revert(a->dst_dt + AuPARENT); + -+ if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { ++ if (au_ftest_ren(a->auren_flags, ISDIR_SRC) && err != -EIO) { + h_d = a->src_dt[AuCHILD].dt_h_path.dentry; + h_inode = d_inode(h_d); + inode_lock_nested(h_inode, AuLsc_I_CHILD); + au_dtime_revert(a->src_dt + AuCHILD); + inode_unlock(h_inode); + -+ if (au_ftest_ren(a->flags, DT_DSTDIR)) { ++ if (au_ftest_ren(a->auren_flags, DT_DSTDIR)) { + h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; + h_inode = d_inode(h_d); + inode_lock_nested(h_inode, AuLsc_I_CHILD); @@ -21260,22 +23339,31 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c +/* ---------------------------------------------------------------------- */ + +int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, -+ struct inode *_dst_dir, struct dentry *_dst_dentry) ++ struct inode *_dst_dir, struct dentry *_dst_dentry, ++ unsigned int _flags) +{ -+ int err, flags; ++ int err, lock_flags; ++ void *rev; + /* reduce stack space */ + struct au_ren_args *a; ++ struct au_pin pin; + -+ AuDbg("%pd, %pd\n", _src_dentry, _dst_dentry); ++ AuDbg("%pd, %pd, 0x%x\n", _src_dentry, _dst_dentry, _flags); + IMustLock(_src_dir); + IMustLock(_dst_dir); + ++ err = -EINVAL; ++ if (unlikely(_flags & RENAME_WHITEOUT)) ++ goto out; ++ + err = -ENOMEM; + BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); + a = kzalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + ++ a->flags = _flags; ++ a->exchange = _flags & RENAME_EXCHANGE; + a->src_dir = _src_dir; + a->src_dentry = _src_dentry; + a->src_inode = NULL; @@ -21289,20 +23377,34 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + a->dst_inode = d_inode(a->dst_dentry); + a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ + if (a->dst_inode) { -+ IMustLock(a->dst_inode); ++ /* ++ * if EXCHANGE && src is non-dir && dst is dir, ++ * dst is not locked. ++ */ ++ /* IMustLock(a->dst_inode); */ + au_igrab(a->dst_inode); + } + + err = -ENOTDIR; -+ flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; ++ lock_flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; + if (d_is_dir(a->src_dentry)) { -+ au_fset_ren(a->flags, ISDIR); -+ if (unlikely(d_really_is_positive(a->dst_dentry) ++ au_fset_ren(a->auren_flags, ISDIR_SRC); ++ if (unlikely(!a->exchange ++ && d_really_is_positive(a->dst_dentry) + && !d_is_dir(a->dst_dentry))) + goto out_free; -+ flags |= AuLock_DIRS; ++ lock_flags |= AuLock_DIRS; + } -+ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, flags); ++ if (a->dst_inode && d_is_dir(a->dst_dentry)) { ++ au_fset_ren(a->auren_flags, ISDIR_DST); ++ if (unlikely(!a->exchange ++ && d_really_is_positive(a->src_dentry) ++ && !d_is_dir(a->src_dentry))) ++ goto out_free; ++ lock_flags |= AuLock_DIRS; ++ } ++ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, ++ lock_flags); + if (unlikely(err)) + goto out_free; + @@ -21312,14 +23414,14 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + err = -ENOENT; + if (a->dst_inode) { + /* -+ * If it is a dir, VFS unhash dst_dentry before this ++ * If it is a dir, VFS unhash it before this + * function. It means we cannot rely upon d_unhashed(). + */ + if (unlikely(!a->dst_inode->i_nlink)) + goto out_unlock; -+ if (!S_ISDIR(a->dst_inode->i_mode)) { ++ if (!au_ftest_ren(a->auren_flags, ISDIR_DST)) { + err = au_d_hashed_positive(a->dst_dentry); -+ if (unlikely(err)) ++ if (unlikely(err && !a->exchange)) + goto out_unlock; + } else if (unlikely(IS_DEADDIR(a->dst_inode))) + goto out_unlock; @@ -21335,7 +23437,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + if (unlikely(d_inode(a->dst_parent) == d_inode(a->src_dentry))) + goto out_unlock; + -+ au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ ++ au_fset_ren(a->auren_flags, ISSAMEDIR); /* temporary */ + di_write_lock_parent(a->dst_parent); + + /* which branch we process */ @@ -21352,39 +23454,43 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + + /* prepare the writable parent dir on the same branch */ + if (a->dst_btop == a->btgt) { -+ au_fset_ren(a->flags, WHDST); ++ au_fset_ren(a->auren_flags, WHDST); + } else { + err = au_cpup_dirs(a->dst_dentry, a->btgt); + if (unlikely(err)) + goto out_children; + } + -+ if (a->src_dir != a->dst_dir) { -+ /* -+ * this temporary unlock is safe, -+ * because both dir->i_mutex are locked. -+ */ -+ di_write_unlock(a->dst_parent); -+ di_write_lock_parent(a->src_parent); -+ err = au_wr_dir_need_wh(a->src_dentry, -+ au_ftest_ren(a->flags, ISDIR), -+ &a->btgt); -+ di_write_unlock(a->src_parent); -+ di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); -+ au_fclr_ren(a->flags, ISSAMEDIR); -+ } else -+ err = au_wr_dir_need_wh(a->src_dentry, -+ au_ftest_ren(a->flags, ISDIR), -+ &a->btgt); ++ err = 0; ++ if (!a->exchange) { ++ if (a->src_dir != a->dst_dir) { ++ /* ++ * this temporary unlock is safe, ++ * because both dir->i_mutex are locked. ++ */ ++ di_write_unlock(a->dst_parent); ++ di_write_lock_parent(a->src_parent); ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->auren_flags, ++ ISDIR_SRC), ++ &a->btgt); ++ di_write_unlock(a->src_parent); ++ di_write_lock2_parent(a->src_parent, a->dst_parent, ++ /*isdir*/1); ++ au_fclr_ren(a->auren_flags, ISSAMEDIR); ++ } else ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->auren_flags, ++ ISDIR_SRC), ++ &a->btgt); ++ } + if (unlikely(err < 0)) + goto out_children; + if (err) -+ au_fset_ren(a->flags, WHSRC); ++ au_fset_ren(a->auren_flags, WHSRC); + + /* cpup src */ + if (a->src_btop != a->btgt) { -+ struct au_pin pin; -+ + err = au_pin(&pin, a->src_dentry, a->btgt, + au_opt_udba(a->src_dentry->d_sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); @@ -21405,7 +23511,32 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + goto out_children; + a->src_btop = a->btgt; + a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); -+ au_fset_ren(a->flags, WHSRC); ++ if (!a->exchange) ++ au_fset_ren(a->auren_flags, WHSRC); ++ } ++ ++ /* cpup dst */ ++ if (a->exchange && a->dst_inode ++ && a->dst_btop != a->btgt) { ++ err = au_pin(&pin, a->dst_dentry, a->btgt, ++ au_opt_udba(a->dst_dentry->d_sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (!err) { ++ struct au_cp_generic cpg = { ++ .dentry = a->dst_dentry, ++ .bdst = a->btgt, ++ .bsrc = a->dst_btop, ++ .len = -1, ++ .pin = &pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN ++ }; ++ err = au_sio_cpup_simple(&cpg); ++ au_unpin(&pin); ++ } ++ if (unlikely(err)) ++ goto out_children; ++ a->dst_btop = a->btgt; ++ a->dst_h_dentry = au_h_dptr(a->dst_dentry, a->btgt); + } + + /* lock them all */ @@ -21414,20 +23545,34 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + /* leave the copied-up one */ + goto out_children; + -+ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) -+ err = au_may_ren(a); -+ else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ err = -ENAMETOOLONG; -+ if (unlikely(err)) -+ goto out_hdir; ++ if (!a->exchange) { ++ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) ++ err = au_may_ren(a); ++ else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ err = -ENAMETOOLONG; ++ if (unlikely(err)) ++ goto out_hdir; ++ } + + /* store timestamps to be revertible */ + au_ren_dt(a); + ++ /* store dirren info */ ++ if (au_ftest_ren(a->auren_flags, DIRREN)) { ++ err = au_dr_rename(a->src_dentry, a->btgt, ++ &a->dst_dentry->d_name, &rev); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ goto out_dt; ++ } ++ + /* here we go */ + err = do_rename(a); + if (unlikely(err)) -+ goto out_dt; ++ goto out_dirren; ++ ++ if (au_ftest_ren(a->auren_flags, DIRREN)) ++ au_dr_rename_fin(a->src_dentry, a->btgt, rev); + + /* update dir attributes */ + au_ren_refresh_dir(a); @@ -21437,6 +23582,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + + goto out_hdir; /* success */ + ++out_dirren: ++ if (au_ftest_ren(a->auren_flags, DIRREN)) ++ au_dr_rename_rev(a->src_dentry, a->btgt, rev); +out_dt: + au_ren_rev_dt(err, a); +out_hdir: @@ -21449,14 +23597,26 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + au_set_dbtop(a->dst_dentry, a->dst_btop); + } +out_parent: -+ if (!err) -+ d_move(a->src_dentry, a->dst_dentry); -+ else { ++ if (!err) { ++ if (d_unhashed(a->src_dentry)) ++ au_fset_ren(a->auren_flags, DROPPED_SRC); ++ if (d_unhashed(a->dst_dentry)) ++ au_fset_ren(a->auren_flags, DROPPED_DST); ++ if (!a->exchange) ++ d_move(a->src_dentry, a->dst_dentry); ++ else { ++ d_exchange(a->src_dentry, a->dst_dentry); ++ if (au_ftest_ren(a->auren_flags, DROPPED_DST)) ++ d_drop(a->dst_dentry); ++ } ++ if (au_ftest_ren(a->auren_flags, DROPPED_SRC)) ++ d_drop(a->src_dentry); ++ } else { + au_update_dbtop(a->dst_dentry); + if (!a->dst_inode) + d_drop(a->dst_dentry); + } -+ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ if (au_ftest_ren(a->auren_flags, ISSAMEDIR)) + di_write_unlock(a->dst_parent); + else + di_write_unlock2(a->src_parent, a->dst_parent); @@ -21466,15 +23626,15 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + iput(a->dst_inode); + if (a->thargs) + au_whtmp_rmdir_free(a->thargs); -+ au_delayed_kfree(a); ++ kfree(a); +out: + AuTraceErr(err); + return err; +} diff -urN /usr/share/empty/fs/aufs/Kconfig linux/fs/aufs/Kconfig --- /usr/share/empty/fs/aufs/Kconfig 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/Kconfig 2016-10-09 16:55:36.482701377 +0200 -@@ -0,0 +1,185 @@ ++++ linux/fs/aufs/Kconfig 2018-04-15 08:49:13.394483860 +0200 +@@ -0,0 +1,198 @@ +config AUFS_FS + tristate "Aufs (Advanced multi layered unification filesystem) support" + help @@ -21593,6 +23753,19 @@ diff -urN /usr/share/empty/fs/aufs/Kconfig linux/fs/aufs/Kconfig + shows better performance in most cases. + See detail in aufs.5. + ++config AUFS_DIRREN ++ bool "Workaround for rename(2)-ing a directory" ++ help ++ By default, aufs returns EXDEV error in renameing a dir who has ++ his child on the lower branch, since it is a bad idea to issue ++ rename(2) internally for every lower branch. But user may not ++ accept this behaviour. So here is a workaround to allow such ++ rename(2) and store some extra infromation on the writable ++ branch. Obviously this costs high (and I don't like it). ++ To use this feature, you need to enable this configuration AND ++ to specify the mount option `dirren.' ++ See details in aufs.5 and the design documents. ++ +config AUFS_SHWH + bool "Show whiteouts" + help @@ -21662,10 +23835,10 @@ diff -urN /usr/share/empty/fs/aufs/Kconfig linux/fs/aufs/Kconfig +endif diff -urN /usr/share/empty/fs/aufs/loop.c linux/fs/aufs/loop.c --- /usr/share/empty/fs/aufs/loop.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/loop.c 2016-10-09 16:55:38.889431135 +0200 ++++ linux/fs/aufs/loop.c 2018-04-15 08:49:13.401150731 +0200 @@ -0,0 +1,147 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -21809,14 +23982,14 @@ diff -urN /usr/share/empty/fs/aufs/loop.c linux/fs/aufs/loop.c +{ + if (backing_file_func) + symbol_put(loop_backing_file); -+ au_delayed_kfree(au_warn_loopback_array); ++ kfree(au_warn_loopback_array); +} diff -urN /usr/share/empty/fs/aufs/loop.h linux/fs/aufs/loop.h --- /usr/share/empty/fs/aufs/loop.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/loop.h 2016-10-09 16:55:36.492701639 +0200 ++++ linux/fs/aufs/loop.h 2018-04-15 08:49:13.401150731 +0200 @@ -0,0 +1,52 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -21869,7 +24042,7 @@ diff -urN /usr/share/empty/fs/aufs/loop.h linux/fs/aufs/loop.h +#endif /* __AUFS_LOOP_H__ */ diff -urN /usr/share/empty/fs/aufs/magic.mk linux/fs/aufs/magic.mk --- /usr/share/empty/fs/aufs/magic.mk 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/magic.mk 2016-10-09 16:55:36.492701639 +0200 ++++ linux/fs/aufs/magic.mk 2018-04-15 08:49:11.027744356 +0200 @@ -0,0 +1,30 @@ + +# defined in ${srctree}/fs/fuse/inode.c @@ -21903,8 +24076,8 @@ diff -urN /usr/share/empty/fs/aufs/magic.mk linux/fs/aufs/magic.mk +endif diff -urN /usr/share/empty/fs/aufs/Makefile linux/fs/aufs/Makefile --- /usr/share/empty/fs/aufs/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/Makefile 2016-10-09 16:55:36.486034798 +0200 -@@ -0,0 +1,44 @@ ++++ linux/fs/aufs/Makefile 2018-04-15 08:49:13.394483860 +0200 +@@ -0,0 +1,45 @@ + +include ${src}/magic.mk +ifeq (${CONFIG_AUFS_FS},m) @@ -21943,6 +24116,7 @@ diff -urN /usr/share/empty/fs/aufs/Makefile linux/fs/aufs/Makefile +aufs-$(CONFIG_AUFS_EXPORT) += export.o +aufs-$(CONFIG_AUFS_XATTR) += xattr.o +aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o ++aufs-$(CONFIG_AUFS_DIRREN) += dirren.o +aufs-$(CONFIG_AUFS_FHSM) += fhsm.o +aufs-$(CONFIG_AUFS_POLL) += poll.o +aufs-$(CONFIG_AUFS_RDU) += rdu.o @@ -21951,10 +24125,10 @@ diff -urN /usr/share/empty/fs/aufs/Makefile linux/fs/aufs/Makefile +aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c --- /usr/share/empty/fs/aufs/module.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/module.c 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,333 @@ ++++ linux/fs/aufs/module.c 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,266 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -21989,7 +24163,7 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c + if (p) { +#if 0 /* unused */ + if (!new_sz) { -+ au_delayed_kfree(p); ++ kfree(p); + p = NULL; + goto out; + } @@ -22013,7 +24187,7 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c + if (q) { + if (p) { + memcpy(q, p, new_sz); -+ au_delayed_kfree(p); ++ kfree(p); + } + p = q; + } else @@ -22037,61 +24211,11 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c +/* + * aufs caches + */ -+ -+struct au_dfree au_dfree; -+ -+/* delayed free */ -+static void au_do_dfree(struct work_struct *work __maybe_unused) -+{ -+ struct llist_head *head; -+ struct llist_node *node, *next; -+ -+#define AU_CACHE_DFREE_DO_BODY(name, idx, lnode) do { \ -+ head = &au_dfree.cache[AuCache_##idx].llist; \ -+ node = llist_del_all(head); \ -+ for (; node; node = next) { \ -+ struct au_##name *p \ -+ = llist_entry(node, struct au_##name, \ -+ lnode); \ -+ next = llist_next(node); \ -+ au_cache_free_##name(p); \ -+ } \ -+ } while (0) -+ -+ AU_CACHE_DFREE_DO_BODY(dinfo, DINFO, di_lnode); -+ AU_CACHE_DFREE_DO_BODY(icntnr, ICNTNR, lnode); -+ AU_CACHE_DFREE_DO_BODY(finfo, FINFO, fi_lnode); -+ AU_CACHE_DFREE_DO_BODY(vdir, VDIR, vd_lnode); -+ AU_CACHE_DFREE_DO_BODY(vdir_dehstr, DEHSTR, lnode); -+#ifdef CONFIG_AUFS_HNOTIFY -+ AU_CACHE_DFREE_DO_BODY(hnotify, HNOTIFY, hn_lnode); -+#endif -+ -+#define AU_DFREE_DO_BODY(llist, func) do { \ -+ node = llist_del_all(llist); \ -+ for (; node; node = next) { \ -+ next = llist_next(node); \ -+ func(node); \ -+ } \ -+ } while (0) -+ -+ AU_DFREE_DO_BODY(au_dfree.llist + AU_DFREE_KFREE, kfree); -+ AU_DFREE_DO_BODY(au_dfree.llist + AU_DFREE_FREE_PAGE, au_free_page); -+ -+#undef AU_CACHE_DFREE_DO_BODY -+#undef AU_DFREE_DO_BODY -+} -+ -+AU_CACHE_DFREE_FUNC(dinfo, DINFO, di_lnode); -+AU_CACHE_DFREE_FUNC(icntnr, ICNTNR, lnode); -+AU_CACHE_DFREE_FUNC(finfo, FINFO, fi_lnode); -+AU_CACHE_DFREE_FUNC(vdir, VDIR, vd_lnode); -+AU_CACHE_DFREE_FUNC(vdir_dehstr, DEHSTR, lnode); ++struct kmem_cache *au_cache[AuCache_Last]; + +static void au_cache_fin(void) +{ + int i; -+ struct au_cache *cp; + + /* + * Make sure all delayed rcu free inodes are flushed before we @@ -22101,33 +24225,27 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c + + /* excluding AuCache_HNOTIFY */ + BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last); -+ flush_delayed_work(&au_dfree.dwork); + for (i = 0; i < AuCache_HNOTIFY; i++) { -+ cp = au_dfree.cache + i; -+ AuDebugOn(!llist_empty(&cp->llist)); -+ kmem_cache_destroy(cp->cache); -+ cp->cache = NULL; ++ kmem_cache_destroy(au_cache[i]); ++ au_cache[i] = NULL; + } +} + +static int __init au_cache_init(void) +{ -+ struct au_cache *cp; -+ -+ cp = au_dfree.cache; -+ cp[AuCache_DINFO].cache = AuCacheCtor(au_dinfo, au_di_init_once); -+ if (cp[AuCache_DINFO].cache) ++ au_cache[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); ++ if (au_cache[AuCache_DINFO]) + /* SLAB_DESTROY_BY_RCU */ -+ cp[AuCache_ICNTNR].cache = AuCacheCtor(au_icntnr, ++ au_cache[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, + au_icntnr_init_once); -+ if (cp[AuCache_ICNTNR].cache) -+ cp[AuCache_FINFO].cache = AuCacheCtor(au_finfo, ++ if (au_cache[AuCache_ICNTNR]) ++ au_cache[AuCache_FINFO] = AuCacheCtor(au_finfo, + au_fi_init_once); -+ if (cp[AuCache_FINFO].cache) -+ cp[AuCache_VDIR].cache = AuCache(au_vdir); -+ if (cp[AuCache_VDIR].cache) -+ cp[AuCache_DEHSTR].cache = AuCache(au_vdir_dehstr); -+ if (cp[AuCache_DEHSTR].cache) ++ if (au_cache[AuCache_FINFO]) ++ au_cache[AuCache_VDIR] = AuCache(au_vdir); ++ if (au_cache[AuCache_VDIR]) ++ au_cache[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); ++ if (au_cache[AuCache_DEHSTR]) + return 0; + + au_cache_fin(); @@ -22143,7 +24261,7 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c + * iterate_supers_type() doesn't protect us from + * remounting (branch management) + */ -+struct au_sphlhead au_sbilist; ++struct hlist_bl_head au_sbilist; +#endif + +/* @@ -22176,9 +24294,9 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c + int err; + + err = seq_path(seq, path, au_esc_chars); -+ if (err > 0) ++ if (err >= 0) + err = 0; -+ else if (err < 0) ++ else + err = -ENOMEM; + + return err; @@ -22190,7 +24308,6 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c +{ + int err, i; + char *p; -+ struct au_cache *cp; + + p = au_esc_chars; + for (i = 1; i <= ' '; i++) @@ -22205,15 +24322,7 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c + for (i = 0; i < AuIop_Last; i++) + aufs_iop_nogetattr[i].getattr = NULL; + -+ /* First, initialize au_dfree */ -+ for (i = 0; i < AuCache_Last; i++) { /* including hnotify */ -+ cp = au_dfree.cache + i; -+ cp->cache = NULL; -+ init_llist_head(&cp->llist); -+ } -+ for (i = 0; i < AU_DFREE_Last; i++) -+ init_llist_head(au_dfree.llist + i); -+ INIT_DELAYED_WORK(&au_dfree.dwork, au_do_dfree); ++ memset(au_cache, 0, sizeof(au_cache)); /* including hnotify */ + + au_sbilist_init(); + sysaufs_brs_init(); @@ -22265,7 +24374,6 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c +out_sysaufs: + sysaufs_fin(); + au_dy_fin(); -+ flush_delayed_work(&au_dfree.dwork); +out: + return err; +} @@ -22281,17 +24389,16 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c + au_procfs_fin(); + sysaufs_fin(); + au_dy_fin(); -+ flush_delayed_work(&au_dfree.dwork); +} + +module_init(aufs_init); +module_exit(aufs_exit); diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h --- /usr/share/empty/fs/aufs/module.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/module.h 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,156 @@ ++++ linux/fs/aufs/module.h 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,101 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -22317,7 +24424,6 @@ diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h +#ifdef __KERNEL__ + +#include -+#include "debug.h" + +struct path; +struct seq_file; @@ -22356,7 +24462,7 @@ diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h + +/* ---------------------------------------------------------------------- */ + -+/* kmem cache and delayed free */ ++/* kmem cache */ +enum { + AuCache_DINFO, + AuCache_ICNTNR, @@ -22367,28 +24473,7 @@ diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h + AuCache_Last +}; + -+enum { -+ AU_DFREE_KFREE, -+ AU_DFREE_FREE_PAGE, -+ AU_DFREE_Last -+}; -+ -+struct au_cache { -+ struct kmem_cache *cache; -+ struct llist_head llist; /* delayed free */ -+}; -+ -+/* -+ * in order to reduce the cost of the internal timer, consolidate all the -+ * delayed free works into a single delayed_work. -+ */ -+struct au_dfree { -+ struct au_cache cache[AuCache_Last]; -+ struct llist_head llist[AU_DFREE_Last]; -+ struct delayed_work dwork; -+}; -+ -+extern struct au_dfree au_dfree; ++extern struct kmem_cache *au_cache[AuCache_Last]; + +#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD) +#define AuCache(type) KMEM_CACHE(type, AuCacheFlags) @@ -22396,25 +24481,11 @@ diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h + kmem_cache_create(#type, sizeof(struct type), \ + __alignof__(struct type), AuCacheFlags, ctor) + -+#define AU_DFREE_DELAY msecs_to_jiffies(10) -+#define AU_DFREE_BODY(lnode, llist) do { \ -+ if (llist_add(lnode, llist)) \ -+ schedule_delayed_work(&au_dfree.dwork, \ -+ AU_DFREE_DELAY); \ -+ } while (0) -+#define AU_CACHE_DFREE_FUNC(name, idx, lnode) \ -+ void au_cache_dfree_##name(struct au_##name *p) \ -+ { \ -+ struct au_cache *cp = au_dfree.cache + AuCache_##idx; \ -+ AU_DFREE_BODY(&p->lnode, &cp->llist); \ -+ } -+ +#define AuCacheFuncs(name, index) \ +static inline struct au_##name *au_cache_alloc_##name(void) \ -+{ return kmem_cache_alloc(au_dfree.cache[AuCache_##index].cache, GFP_NOFS); } \ ++{ return kmem_cache_alloc(au_cache[AuCache_##index], GFP_NOFS); } \ +static inline void au_cache_free_##name(struct au_##name *p) \ -+{ kmem_cache_free(au_dfree.cache[AuCache_##index].cache, p); } \ -+void au_cache_dfree_##name(struct au_##name *p) ++{ kmem_cache_free(au_cache[AuCache_##index], p); } + +AuCacheFuncs(dinfo, DINFO); +AuCacheFuncs(icntnr, ICNTNR); @@ -22425,33 +24496,14 @@ diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h +AuCacheFuncs(hnotify, HNOTIFY); +#endif + -+static inline void au_delayed_kfree(const void *p) -+{ -+ AuDebugOn(!p); -+ AuDebugOn(ksize(p) < sizeof(struct llist_node)); -+ -+ AU_DFREE_BODY((void *)p, au_dfree.llist + AU_DFREE_KFREE); -+} -+ -+/* cast only */ -+static inline void au_free_page(void *p) -+{ -+ free_page((unsigned long)p); -+} -+ -+static inline void au_delayed_free_page(unsigned long addr) -+{ -+ AU_DFREE_BODY((void *)addr, au_dfree.llist + AU_DFREE_FREE_PAGE); -+} -+ +#endif /* __KERNEL__ */ +#endif /* __AUFS_MODULE_H__ */ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c --- /usr/share/empty/fs/aufs/mvdown.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/mvdown.c 2016-10-09 16:55:36.492701639 +0200 ++++ linux/fs/aufs/mvdown.c 2018-04-15 08:49:13.401150731 +0200 @@ -0,0 +1,704 @@ +/* -+ * Copyright (C) 2011-2016 Junjiro R. Okajima ++ * Copyright (C) 2011-2018 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 @@ -23149,17 +25201,17 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c + e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); + if (unlikely(e)) + err = -EFAULT; -+ au_delayed_kfree(args); ++ kfree(args); +out: + AuTraceErr(err); + return err; +} diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c --- /usr/share/empty/fs/aufs/opts.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/opts.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,1860 @@ ++++ linux/fs/aufs/opts.c 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,1913 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -23207,6 +25259,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + Opt_verbose, Opt_noverbose, + Opt_sum, Opt_nosum, Opt_wsum, + Opt_dirperm1, Opt_nodirperm1, ++ Opt_dirren, Opt_nodirren, + Opt_acl, Opt_noacl, + Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err +}; @@ -23261,10 +25314,18 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + {Opt_dio, "dio"}, + {Opt_nodio, "nodio"}, + ++#ifdef CONFIG_AUFS_DIRREN ++ {Opt_dirren, "dirren"}, ++ {Opt_nodirren, "nodirren"}, ++#else ++ {Opt_ignore, "dirren"}, ++ {Opt_ignore_silent, "nodirren"}, ++#endif ++ +#ifdef CONFIG_AUFS_FHSM + {Opt_fhsm_sec, "fhsm_sec=%d"}, +#else -+ {Opt_ignore_silent, "fhsm_sec=%d"}, ++ {Opt_ignore, "fhsm_sec=%d"}, +#endif + + {Opt_diropq_a, "diropq=always"}, @@ -23277,7 +25338,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + + /* keep them temporary */ + {Opt_ignore_silent, "nodlgt"}, -+ {Opt_ignore_silent, "clean_plink"}, ++ {Opt_ignore, "clean_plink"}, + +#ifdef CONFIG_AUFS_SHWH + {Opt_shwh, "shwh"}, @@ -23315,7 +25376,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + {Opt_acl, "acl"}, + {Opt_noacl, "noacl"}, +#else -+ {Opt_ignore_silent, "acl"}, ++ {Opt_ignore, "acl"}, + {Opt_ignore_silent, "noacl"}, +#endif + @@ -23584,6 +25645,10 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + {AuWbrCreate_MFSV, "mfs:%d"}, + {AuWbrCreate_MFSV, "most-free-space:%d"}, + ++ /* top-down regardless the parent, and then mfs */ ++ {AuWbrCreate_TDMFS, "tdmfs:%d"}, ++ {AuWbrCreate_TDMFSV, "tdmfs:%d:%d"}, ++ + {AuWbrCreate_MFSRR, "mfsrr:%d"}, + {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, + {AuWbrCreate_PMFS, "pmfs"}, @@ -23659,6 +25724,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + create->wbr_create = err; + switch (err) { + case AuWbrCreate_MFSRRV: ++ case AuWbrCreate_TDMFSV: + case AuWbrCreate_PMFSRRV: + e = au_wbr_mfs_wmark(&args[0], str, create); + if (!e) @@ -23667,6 +25733,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + err = e; + break; + case AuWbrCreate_MFSRR: ++ case AuWbrCreate_TDMFS: + case AuWbrCreate_PMFSRR: + e = au_wbr_mfs_wmark(&args[0], str, create); + if (unlikely(e)) { @@ -23877,10 +25944,12 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + AuDbg("%d sec\n", u.create->mfs_second); + break; + case AuWbrCreate_MFSRR: ++ case AuWbrCreate_TDMFS: + AuDbg("%llu watermark\n", + u.create->mfsrr_watermark); + break; + case AuWbrCreate_MFSRRV: ++ case AuWbrCreate_TDMFSV: + case AuWbrCreate_PMFSRRV: + AuDbg("%llu watermark, %d sec\n", + u.create->mfsrr_watermark, @@ -23895,6 +25964,12 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + case Opt_fhsm_sec: + AuDbg("fhsm_sec %u\n", opt->fhsm_second); + break; ++ case Opt_dirren: ++ AuLabel(dirren); ++ break; ++ case Opt_nodirren: ++ AuLabel(nodirren); ++ break; + case Opt_acl: + AuLabel(acl); + break; @@ -24345,6 +26420,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + case Opt_wsum: + case Opt_rdblk_def: + case Opt_rdhash_def: ++ case Opt_dirren: ++ case Opt_nodirren: + case Opt_acl: + case Opt_noacl: + err = 0; @@ -24416,7 +26493,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + } + } + -+ au_delayed_kfree(a); ++ kfree(a); + dump_opts(opts); + if (unlikely(err)) + au_opts_free(opts); @@ -24446,6 +26523,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + switch (create->wbr_create) { + case AuWbrCreate_MFSRRV: + case AuWbrCreate_MFSRR: ++ case AuWbrCreate_TDMFS: ++ case AuWbrCreate_TDMFSV: + case AuWbrCreate_PMFSRR: + case AuWbrCreate_PMFSRRV: + sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; @@ -24609,6 +26688,28 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + au_fclr_opts(opts->flags, TRUNC_XIB); + break; + ++ case Opt_dirren: ++ err = 1; ++ if (!au_opt_test(sbinfo->si_mntflags, DIRREN)) { ++ err = au_dr_opt_set(sb); ++ if (!err) ++ err = 1; ++ } ++ if (err == 1) ++ au_opt_set(sbinfo->si_mntflags, DIRREN); ++ break; ++ case Opt_nodirren: ++ err = 1; ++ if (au_opt_test(sbinfo->si_mntflags, DIRREN)) { ++ err = au_dr_opt_clr(sb, au_ftest_opts(opts->flags, ++ DR_FLUSHED)); ++ if (!err) ++ err = 1; ++ } ++ if (err == 1) ++ au_opt_clr(sbinfo->si_mntflags, DIRREN); ++ break; ++ + case Opt_acl: + sb->s_flags |= MS_POSIXACL; + break; @@ -24677,7 +26778,6 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + } + break; + } -+ + return err; +} + @@ -24838,8 +26938,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + au_hn_inode_unlock(hdir); + + if (!err && do_free) { -+ if (wbr) -+ au_delayed_kfree(wbr); ++ kfree(wbr); + br->br_wbr = NULL; + } + } @@ -24968,7 +27067,11 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + + SiMustWriteLock(sb); + -+ err = 0; ++ err = au_dr_opt_flush(sb); ++ if (unlikely(err)) ++ goto out; ++ au_fset_opts(opts->flags, DR_FLUSHED); ++ + dir = d_inode(sb->s_root); + sbinfo = au_sbi(sb); + opt_xino = NULL; @@ -25009,6 +27112,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + au_fset_opts(opts->flags, REFRESH); + + AuDbg("status 0x%x\n", opts->flags); ++ ++out: + return err; +} + @@ -25020,10 +27125,10 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c +} diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h --- /usr/share/empty/fs/aufs/opts.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/opts.h 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,211 @@ ++++ linux/fs/aufs/opts.h 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,224 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -25051,7 +27156,6 @@ diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h +#include + +struct file; -+struct super_block; + +/* ---------------------------------------------------------------------- */ + @@ -25072,11 +27176,16 @@ diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h +#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 */ ++#define AuOpt_DIRREN (1 << 15) /* directory rename */ + +#ifndef CONFIG_AUFS_HNOTIFY +#undef AuOpt_UDBA_HNOTIFY +#define AuOpt_UDBA_HNOTIFY 0 +#endif ++#ifndef CONFIG_AUFS_DIRREN ++#undef AuOpt_DIRREN ++#define AuOpt_DIRREN 0 ++#endif +#ifndef CONFIG_AUFS_SHWH +#undef AuOpt_SHWH +#define AuOpt_SHWH 0 @@ -25123,6 +27232,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h + AuWbrCreate_MFSV, /* mfs with seconds */ + AuWbrCreate_MFSRR, /* mfs then rr */ + AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ ++ AuWbrCreate_TDMFS, /* top down regardless parent and mfs */ ++ AuWbrCreate_TDMFSV, /* top down regardless parent and mfs */ + AuWbrCreate_PMFS, /* parent and mfs */ + AuWbrCreate_PMFSV, /* parent and mfs with seconds */ + AuWbrCreate_PMFSRR, /* parent, mfs and round-robin */ @@ -25199,12 +27310,18 @@ diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h +#define AuOpts_TRUNC_XIB (1 << 2) +#define AuOpts_REFRESH_DYAOP (1 << 3) +#define AuOpts_REFRESH_IDOP (1 << 4) ++#define AuOpts_DR_FLUSHED (1 << 5) +#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) + ++#ifndef CONFIG_AUFS_DIRREN ++#undef AuOpts_DR_FLUSHED ++#define AuOpts_DR_FLUSHED 0 ++#endif ++ +struct au_opts { + struct au_opt *opt; + int max_opt; @@ -25223,6 +27340,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h +const char *au_optstr_wbr_create(int wbr_create); + +void au_opts_free(struct au_opts *opts); ++struct super_block; +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); @@ -25235,10 +27353,10 @@ diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h +#endif /* __AUFS_OPTS_H__ */ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c --- /usr/share/empty/fs/aufs/plink.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/plink.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,514 @@ ++++ linux/fs/aufs/plink.c 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,515 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -25374,7 +27492,8 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c +{ + int i; + struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; + struct au_icntnr *icntnr; + + SiMustAnyLock(sb); @@ -25384,11 +27503,11 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + + for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ rcu_read_lock(); -+ hlist_for_each_entry_rcu(icntnr, plink_hlist, plink) ++ hbl = sbinfo->si_plink + i; ++ hlist_bl_lock(hbl); ++ hlist_bl_for_each_entry(icntnr, pos, hbl, plink) + AuDbg("%lu\n", icntnr->vfs_inode.i_ino); -+ rcu_read_unlock(); ++ hlist_bl_unlock(hbl); + } +} +#endif @@ -25398,7 +27517,8 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c +{ + int found, i; + struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; + struct au_icntnr *icntnr; + + sbinfo = au_sbi(inode->i_sb); @@ -25408,14 +27528,14 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + + found = 0; + i = au_plink_hash(inode->i_ino); -+ plink_hlist = &sbinfo->si_plink[i].head; -+ rcu_read_lock(); -+ hlist_for_each_entry_rcu(icntnr, plink_hlist, plink) ++ hbl = sbinfo->si_plink + i; ++ hlist_bl_lock(hbl); ++ hlist_bl_for_each_entry(icntnr, pos, hbl, plink) + if (&icntnr->vfs_inode == inode) { + found = 1; + break; + } -+ rcu_read_unlock(); ++ hlist_bl_unlock(hbl); + return found; +} + @@ -25454,9 +27574,9 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + struct inode *h_inode; + + h_inode = d_inode(h_parent); -+ inode_lock_nested(h_inode, AuLsc_I_CHILD2); ++ vfsub_inode_lock_shared_nested(h_inode, AuLsc_I_CHILD2); + h_dentry = vfsub_lkup_one(tgtname, h_parent); -+ inode_unlock(h_inode); ++ inode_unlock_shared(h_inode); + return h_dentry; +} + @@ -25603,9 +27723,9 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c +{ + struct super_block *sb; + struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; + struct au_icntnr *icntnr; -+ struct au_sphlhead *sphl; + int found, err, cnt, i; + + sb = inode->i_sb; @@ -25618,12 +27738,11 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + return; + + i = au_plink_hash(inode->i_ino); -+ sphl = sbinfo->si_plink + i; -+ plink_hlist = &sphl->head; ++ hbl = sbinfo->si_plink + i; + au_igrab(inode); + -+ spin_lock(&sphl->spin); -+ hlist_for_each_entry(icntnr, plink_hlist, plink) { ++ hlist_bl_lock(hbl); ++ hlist_bl_for_each_entry(icntnr, pos, hbl, plink) { + if (&icntnr->vfs_inode == inode) { + found = 1; + break; @@ -25631,11 +27750,11 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + } + if (!found) { + icntnr = container_of(inode, struct au_icntnr, vfs_inode); -+ hlist_add_head_rcu(&icntnr->plink, plink_hlist); ++ hlist_bl_add_head(&icntnr->plink, hbl); + } -+ spin_unlock(&sphl->spin); ++ hlist_bl_unlock(hbl); + if (!found) { -+ cnt = au_sphl_count(sphl); ++ cnt = au_hbl_count(hbl); +#define msg "unexpectedly unblanced or too many pseudo-links" + if (cnt > AUFS_PLINK_WARN) + AuWarn1(msg ", %d\n", cnt); @@ -25643,7 +27762,7 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); + if (unlikely(err)) { + pr_warn("err %d, damaged pseudo link.\n", err); -+ au_sphl_del_rcu(&icntnr->plink, sphl); ++ au_hbl_del(&icntnr->plink, hbl); + iput(&icntnr->vfs_inode); + } + } else @@ -25655,8 +27774,8 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c +{ + int i, warned; + struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct hlist_node *tmp; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos, *tmp; + struct au_icntnr *icntnr; + + SiMustWriteLock(sb); @@ -25668,14 +27787,14 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + /* no spin_lock since sbinfo is write-locked */ + warned = 0; + for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ if (!warned && verbose && !hlist_empty(plink_hlist)) { ++ hbl = sbinfo->si_plink + i; ++ if (!warned && verbose && !hlist_bl_empty(hbl)) { + pr_warn("pseudo-link is not flushed"); + warned = 1; + } -+ hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink) ++ hlist_bl_for_each_entry_safe(icntnr, pos, tmp, hbl, plink) + iput(&icntnr->vfs_inode); -+ INIT_HLIST_HEAD(plink_hlist); ++ INIT_HLIST_BL_HEAD(hbl); + } +} + @@ -25723,8 +27842,8 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c +void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) +{ + struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct hlist_node *tmp; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos, *tmp; + struct au_icntnr *icntnr; + struct inode *inode; + int i, do_put; @@ -25735,15 +27854,15 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + -+ /* no spin_lock since sbinfo is write-locked */ ++ /* no bit_lock since sbinfo is write-locked */ + for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink) { ++ hbl = sbinfo->si_plink + i; ++ hlist_bl_for_each_entry_safe(icntnr, pos, tmp, hbl, plink) { + inode = au_igrab(&icntnr->vfs_inode); + ii_write_lock_child(inode); + do_put = au_plink_do_half_refresh(inode, br_id); + if (do_put) { -+ hlist_del(&icntnr->plink); ++ hlist_bl_del(&icntnr->plink); + iput(inode); + } + ii_write_unlock(inode); @@ -25753,10 +27872,10 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c +} diff -urN /usr/share/empty/fs/aufs/poll.c linux/fs/aufs/poll.c --- /usr/share/empty/fs/aufs/poll.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/poll.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,52 @@ ++++ linux/fs/aufs/poll.c 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,53 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -25791,7 +27910,7 @@ diff -urN /usr/share/empty/fs/aufs/poll.c linux/fs/aufs/poll.c + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + -+ h_file = au_read_pre(file, /*keep_fi*/0); ++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; @@ -25804,15 +27923,16 @@ diff -urN /usr/share/empty/fs/aufs/poll.c linux/fs/aufs/poll.c + +out: + si_read_unlock(sb); -+ AuTraceErr((int)mask); ++ if (mask & POLLERR) ++ AuDbg("mask 0x%x\n", mask); + return mask; +} diff -urN /usr/share/empty/fs/aufs/posix_acl.c linux/fs/aufs/posix_acl.c --- /usr/share/empty/fs/aufs/posix_acl.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/posix_acl.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,98 @@ ++++ linux/fs/aufs/posix_acl.c 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,102 @@ +/* -+ * Copyright (C) 2014-2016 Junjiro R. Okajima ++ * Copyright (C) 2014-2018 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 @@ -25862,6 +27982,8 @@ diff -urN /usr/share/empty/fs/aufs/posix_acl.c linux/fs/aufs/posix_acl.c + + /* always topmost only */ + acl = get_acl(h_inode, type); ++ if (!IS_ERR_OR_NULL(acl)) ++ set_cached_acl(inode, type, acl); + +out: + ii_read_unlock(inode); @@ -25876,7 +27998,7 @@ diff -urN /usr/share/empty/fs/aufs/posix_acl.c linux/fs/aufs/posix_acl.c + int err; + ssize_t ssz; + struct dentry *dentry; -+ struct au_srxattr arg = { ++ struct au_sxattr arg = { + .type = AU_ACL_SET, + .u.acl_set = { + .acl = acl, @@ -25900,21 +28022,23 @@ diff -urN /usr/share/empty/fs/aufs/posix_acl.c linux/fs/aufs/posix_acl.c + } + } + -+ ssz = au_srxattr(dentry, inode, &arg); ++ ssz = au_sxattr(dentry, inode, &arg); + dput(dentry); + err = ssz; -+ if (ssz >= 0) ++ if (ssz >= 0) { + err = 0; ++ set_cached_acl(inode, type, acl); ++ } + +out: + return err; +} diff -urN /usr/share/empty/fs/aufs/procfs.c linux/fs/aufs/procfs.c --- /usr/share/empty/fs/aufs/procfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/procfs.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,169 @@ ++++ linux/fs/aufs/procfs.c 2018-04-15 08:49:13.401150731 +0200 +@@ -0,0 +1,170 @@ +/* -+ * Copyright (C) 2010-2016 Junjiro R. Okajima ++ * Copyright (C) 2010-2018 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 @@ -25964,6 +28088,7 @@ diff -urN /usr/share/empty/fs/aufs/procfs.c linux/fs/aufs/procfs.c + int err; + struct super_block *sb; + struct au_sbinfo *sbinfo; ++ struct hlist_bl_node *pos; + + err = -EBUSY; + if (unlikely(file->private_data)) @@ -25971,14 +28096,14 @@ diff -urN /usr/share/empty/fs/aufs/procfs.c linux/fs/aufs/procfs.c + + sb = NULL; + /* don't use au_sbilist_lock() here */ -+ spin_lock(&au_sbilist.spin); -+ hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list) ++ hlist_bl_lock(&au_sbilist); ++ hlist_bl_for_each_entry(sbinfo, pos, &au_sbilist, si_list) + if (id == sysaufs_si_id(sbinfo)) { + kobject_get(&sbinfo->si_kobj); + sb = sbinfo->si_sb; + break; + } -+ spin_unlock(&au_sbilist.spin); ++ hlist_bl_unlock(&au_sbilist); + + err = -EINVAL; + if (unlikely(!sb)) @@ -26084,10 +28209,10 @@ diff -urN /usr/share/empty/fs/aufs/procfs.c linux/fs/aufs/procfs.c +} diff -urN /usr/share/empty/fs/aufs/rdu.c linux/fs/aufs/rdu.c --- /usr/share/empty/fs/aufs/rdu.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/rdu.c 2016-10-09 16:55:36.496035060 +0200 ++++ linux/fs/aufs/rdu.c 2018-04-15 08:49:13.404484168 +0200 @@ -0,0 +1,381 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -26469,10 +28594,10 @@ diff -urN /usr/share/empty/fs/aufs/rdu.c linux/fs/aufs/rdu.c +#endif diff -urN /usr/share/empty/fs/aufs/rwsem.h linux/fs/aufs/rwsem.h --- /usr/share/empty/fs/aufs/rwsem.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/rwsem.h 2016-10-09 16:55:36.496035060 +0200 ++++ linux/fs/aufs/rwsem.h 2018-04-15 08:49:13.404484168 +0200 @@ -0,0 +1,198 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -26671,10 +28796,10 @@ diff -urN /usr/share/empty/fs/aufs/rwsem.h linux/fs/aufs/rwsem.h +#endif /* __AUFS_RWSEM_H__ */ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c --- /usr/share/empty/fs/aufs/sbinfo.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/sbinfo.c 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,355 @@ ++++ linux/fs/aufs/sbinfo.c 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,304 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -26707,7 +28832,7 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c + + sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); + for (i = 0; i < AuPlink_NHASH; i++) -+ AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head)); ++ AuDebugOn(!hlist_bl_empty(sbinfo->si_plink + i)); + AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); + + AuDebugOn(percpu_counter_sum(&sbinfo->si_ninodes)); @@ -26719,15 +28844,11 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c + au_br_free(sbinfo); + au_rw_write_unlock(&sbinfo->si_rwsem); + -+ au_delayed_kfree(sbinfo->si_branch); -+ for (i = 0; i < AU_NPIDMAP; i++) -+ if (sbinfo->au_si_pid.pid_bitmap[i]) -+ au_delayed_kfree(sbinfo->au_si_pid.pid_bitmap[i]); -+ mutex_destroy(&sbinfo->au_si_pid.pid_mtx); ++ kfree(sbinfo->si_branch); + mutex_destroy(&sbinfo->si_xib_mtx); + AuRwDestroy(&sbinfo->si_rwsem); + -+ au_delayed_kfree(sbinfo); ++ kfree(sbinfo); +} + +int au_si_alloc(struct super_block *sb) @@ -26751,7 +28872,6 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c + + au_nwt_init(&sbinfo->si_nowait); + au_rw_init_wlock(&sbinfo->si_rwsem); -+ mutex_init(&sbinfo->au_si_pid.pid_mtx); + + percpu_counter_init(&sbinfo->si_ninodes, 0, GFP_NOFS); + percpu_counter_init(&sbinfo->si_nfiles, 0, GFP_NOFS); @@ -26775,7 +28895,7 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c + sbinfo->si_xino_brid = -1; + /* leave si_xib_last_pindex and si_xib_next_bit */ + -+ au_sphl_init(&sbinfo->si_aopen); ++ INIT_HLIST_BL_HEAD(&sbinfo->si_aopen); + + sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC); + sbinfo->si_rdblk = AUFS_RDBLK_DEF; @@ -26783,11 +28903,11 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c + sbinfo->si_dirwh = AUFS_DIRWH_DEF; + + for (i = 0; i < AuPlink_NHASH; i++) -+ au_sphl_init(sbinfo->si_plink + i); ++ INIT_HLIST_BL_HEAD(sbinfo->si_plink + i); + init_waitqueue_head(&sbinfo->si_plink_wq); + spin_lock_init(&sbinfo->si_plink_maint_lock); + -+ au_sphl_init(&sbinfo->si_files); ++ INIT_HLIST_BL_HEAD(&sbinfo->si_files); + + /* with getattr by default */ + sbinfo->si_iop_array = aufs_iop; @@ -26799,9 +28919,9 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c + return 0; /* success */ + +out_br: -+ au_delayed_kfree(sbinfo->si_branch); ++ kfree(sbinfo->si_branch); +out_sbinfo: -+ au_delayed_kfree(sbinfo); ++ kfree(sbinfo); +out: + return err; +} @@ -26982,175 +29102,12 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c + di_write_unlock2(d1, d2); + si_read_unlock(d1->d_sb); +} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void si_pid_alloc(struct au_si_pid *au_si_pid, int idx) -+{ -+ unsigned long *p; -+ -+ BUILD_BUG_ON(sizeof(unsigned long) != -+ sizeof(*au_si_pid->pid_bitmap)); -+ -+ mutex_lock(&au_si_pid->pid_mtx); -+ p = au_si_pid->pid_bitmap[idx]; -+ while (!p) { -+ /* -+ * bad approach. -+ * but keeping 'si_pid_set()' void is more important. -+ */ -+ p = kcalloc(BITS_TO_LONGS(AU_PIDSTEP), -+ sizeof(*au_si_pid->pid_bitmap), -+ GFP_NOFS); -+ if (p) -+ break; -+ cond_resched(); -+ } -+ au_si_pid->pid_bitmap[idx] = p; -+ mutex_unlock(&au_si_pid->pid_mtx); -+} -+ -+void si_pid_set(struct super_block *sb) -+{ -+ pid_t bit; -+ int idx; -+ unsigned long *bitmap; -+ struct au_si_pid *au_si_pid; -+ -+ si_pid_idx_bit(&idx, &bit); -+ au_si_pid = &au_sbi(sb)->au_si_pid; -+ bitmap = au_si_pid->pid_bitmap[idx]; -+ if (!bitmap) { -+ si_pid_alloc(au_si_pid, idx); -+ bitmap = au_si_pid->pid_bitmap[idx]; -+ } -+ AuDebugOn(test_bit(bit, bitmap)); -+ set_bit(bit, bitmap); -+ /* smp_mb(); */ -+} -diff -urN /usr/share/empty/fs/aufs/spl.h linux/fs/aufs/spl.h ---- /usr/share/empty/fs/aufs/spl.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/spl.h 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,113 @@ -+/* -+ * Copyright (C) 2005-2016 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, see . -+ */ -+ -+/* -+ * simple list protected by a spinlock -+ */ -+ -+#ifndef __AUFS_SPL_H__ -+#define __AUFS_SPL_H__ -+ -+#ifdef __KERNEL__ -+ -+#if 0 -+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 -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_sphlhead { -+ spinlock_t spin; -+ struct hlist_head head; -+}; -+ -+static inline void au_sphl_init(struct au_sphlhead *sphl) -+{ -+ spin_lock_init(&sphl->spin); -+ INIT_HLIST_HEAD(&sphl->head); -+} -+ -+static inline void au_sphl_add(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_add_head(hlist, &sphl->head); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline void au_sphl_del(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_del(hlist); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline void au_sphl_del_rcu(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_del_rcu(hlist); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline unsigned long au_sphl_count(struct au_sphlhead *sphl) -+{ -+ unsigned long cnt; -+ struct hlist_node *pos; -+ -+ cnt = 0; -+ spin_lock(&sphl->spin); -+ hlist_for_each(pos, &sphl->head) -+ cnt++; -+ spin_unlock(&sphl->spin); -+ return cnt; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_SPL_H__ */ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c --- /usr/share/empty/fs/aufs/super.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/super.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,1038 @@ ++++ linux/fs/aufs/super.c 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,1046 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -27197,7 +29154,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c +{ + struct inode *inode = container_of(head, struct inode, i_rcu); + -+ au_cache_dfree_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); ++ au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); +} + +static void aufs_destroy_inode(struct inode *inode) @@ -27269,15 +29226,31 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + return err; +} + ++static void au_gen_fmt(char *fmt, int len __maybe_unused, const char *pat, ++ const char *append) ++{ ++ char *p; ++ ++ p = fmt; ++ while (*pat != ':') ++ *p++ = *pat++; ++ *p++ = *pat++; ++ strcpy(p, append); ++ AuDebugOn(strlen(fmt) >= len); ++} ++ +static void au_show_wbr_create(struct seq_file *m, int v, + struct au_sbinfo *sbinfo) +{ + const char *pat; ++ char fmt[32]; ++ struct au_wbr_mfs *mfs; + + AuRwMustAnyLock(&sbinfo->si_rwsem); + + seq_puts(m, ",create="); + pat = au_optstr_wbr_create(v); ++ mfs = &sbinfo->si_wbr_mfs; + switch (v) { + case AuWbrCreate_TDP: + case AuWbrCreate_RR: @@ -27285,36 +29258,28 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + case AuWbrCreate_PMFS: + seq_puts(m, pat); + break; -+ case AuWbrCreate_MFSV: -+ seq_printf(m, /*pat*/"mfs:%lu", -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); ++ case AuWbrCreate_MFSRR: ++ case AuWbrCreate_TDMFS: ++ case AuWbrCreate_PMFSRR: ++ au_gen_fmt(fmt, sizeof(fmt), pat, "%llu"); ++ seq_printf(m, fmt, mfs->mfsrr_watermark); + break; ++ case AuWbrCreate_MFSV: + case AuWbrCreate_PMFSV: -+ seq_printf(m, /*pat*/"pmfs:%lu", -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ au_gen_fmt(fmt, sizeof(fmt), pat, "%lu"); ++ seq_printf(m, fmt, ++ jiffies_to_msecs(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; -+ case AuWbrCreate_PMFSRR: -+ seq_printf(m, /*pat*/"pmfsrr:%llu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark); -+ break; ++ case AuWbrCreate_TDMFSV: + case AuWbrCreate_PMFSRRV: -+ seq_printf(m, /*pat*/"pmfsrr:%llu:%lu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark, -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); ++ au_gen_fmt(fmt, sizeof(fmt), pat, "%llu:%lu"); ++ seq_printf(m, fmt, mfs->mfsrr_watermark, ++ jiffies_to_msecs(mfs->mfs_expire) / MSEC_PER_SEC); + break; ++ default: ++ BUG(); + } +} + @@ -27431,6 +29396,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + + au_fhsm_show(m, sbinfo); + ++ AuBool(DIRREN, dirren); + AuBool(SUM, sum); + /* AuBool(SUM_W, wsum); */ + AuBool(WARN_PERM, warn_perm); @@ -27590,12 +29556,10 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + continue; + + h_sb = au_sbr_sb(sb, bindex); -+ if (h_sb->s_op->sync_fs) { -+ e = h_sb->s_op->sync_fs(h_sb, wait); -+ if (unlikely(e && !err)) -+ err = e; -+ /* go on even if an error happens */ -+ } ++ e = vfsub_sync_filesystem(h_sb, wait); ++ if (unlikely(e && !err)) ++ err = e; ++ /* go on even if an error happens */ + } + si_read_unlock(sb); + @@ -27979,7 +29943,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c +out_mtx: + inode_unlock(inode); +out_opts: -+ au_delayed_free_page((unsigned long)opts.opt); ++ free_page((unsigned long)opts.opt); +out: + err = cvt_err(err); + AuTraceErr(err); @@ -28074,7 +30038,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + sb->s_maxbytes = 0; + sb->s_stack_depth = 1; + au_export_init(sb); -+ /* au_xattr_init(sb); */ ++ au_xattr_init(sb); + + err = alloc_root(sb); + if (unlikely(err)) { @@ -28120,7 +30084,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + kobject_put(&sbinfo->si_kobj); + sb->s_fs_info = NULL; +out_opts: -+ au_delayed_free_page((unsigned long)opts.opt); ++ free_page((unsigned long)opts.opt); +out: + AuTraceErr(err); + err = cvt_err(err); @@ -28171,6 +30135,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + if (au_opt_test(sbinfo->si_mntflags, PLINK)) + au_plink_put(sb, /*verbose*/1); + au_xino_clr(sb); ++ au_dr_opt_flush(sb); + sbinfo->si_sb = NULL; + aufs_write_unlock(sb->s_root); + au_nwt_flush(&sbinfo->si_nowait); @@ -28189,10 +30154,10 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c +}; diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h --- /usr/share/empty/fs/aufs/super.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/super.h 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,638 @@ ++++ linux/fs/aufs/super.h 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,617 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -28219,8 +30184,8 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + +#include +#include ++#include "hbl.h" +#include "rwsem.h" -+#include "spl.h" +#include "wkq.h" + +/* policies to select one among multiple writable branches */ @@ -28272,13 +30237,6 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h +#endif +}; + -+#define AU_PIDSTEP (int)(BITS_TO_LONGS(PID_MAX_DEFAULT) * BITS_PER_LONG) -+#define AU_NPIDMAP (int)DIV_ROUND_UP(PID_MAX_LIMIT, AU_PIDSTEP) -+struct au_si_pid { -+ unsigned long *pid_bitmap[AU_NPIDMAP]; -+ struct mutex pid_mtx; -+}; -+ +struct au_branch; +struct au_sbinfo { + /* nowait tasks in the system-wide workqueue */ @@ -28290,9 +30248,6 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + */ + struct au_rwsem si_rwsem; + -+ /* prevent recursive locking in deleting inode */ -+ struct au_si_pid au_si_pid; -+ + /* + * dirty approach to protect sb->sb_inodes and ->s_files (gone) from + * remount. @@ -28352,7 +30307,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h +#endif + + /* dirty trick to suppoer atomic_open */ -+ struct au_sphlhead si_aopen; ++ struct hlist_bl_head si_aopen; + + /* vdir parameters */ + unsigned long si_rdcache; /* max cache time in jiffies */ @@ -28368,13 +30323,13 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + unsigned int si_dirwh; + + /* pseudo_link list */ -+ struct au_sphlhead si_plink[AuPlink_NHASH]; ++ struct hlist_bl_head si_plink[AuPlink_NHASH]; + wait_queue_head_t si_plink_wq; + spinlock_t si_plink_maint_lock; + pid_t si_plink_maint_pid; + + /* file list */ -+ struct au_sphlhead si_files; ++ struct hlist_bl_head si_files; + + /* with/without getattr, brother of sb->s_d_op */ + struct inode_operations *si_iop_array; @@ -28396,7 +30351,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h +#endif + +#ifdef CONFIG_AUFS_SBILIST -+ struct hlist_node si_list; ++ struct hlist_bl_node si_list; +#endif + + /* dirty, necessary for unmounting, sysfs and sysrq */ @@ -28448,6 +30403,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h +#define AuLock_IW (1 << 2) /* write-lock inode */ +#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ +#define AuLock_DIRS (1 << 4) /* target is a pair of dirs */ ++ /* except RENAME_EXCHANGE */ +#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 */ @@ -28570,32 +30526,32 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + +#ifdef CONFIG_AUFS_SBILIST +/* module.c */ -+extern struct au_sphlhead au_sbilist; ++extern struct hlist_bl_head au_sbilist; + +static inline void au_sbilist_init(void) +{ -+ au_sphl_init(&au_sbilist); ++ INIT_HLIST_BL_HEAD(&au_sbilist); +} + +static inline void au_sbilist_add(struct super_block *sb) +{ -+ au_sphl_add(&au_sbi(sb)->si_list, &au_sbilist); ++ au_hbl_add(&au_sbi(sb)->si_list, &au_sbilist); +} + +static inline void au_sbilist_del(struct super_block *sb) +{ -+ au_sphl_del(&au_sbi(sb)->si_list, &au_sbilist); ++ au_hbl_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); ++ hlist_bl_lock(&au_sbilist); +} + +static inline void au_sbilist_unlock(void) +{ -+ spin_unlock(&au_sbilist.spin); ++ hlist_bl_unlock(&au_sbilist); +} +#define AuGFP_SBILIST GFP_ATOMIC +#else @@ -28633,42 +30589,30 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + +/* ---------------------------------------------------------------------- */ + -+static inline void si_pid_idx_bit(int *idx, pid_t *bit) -+{ -+ /* the origin of pid is 1, but the bitmap's is 0 */ -+ *bit = current->pid - 1; -+ *idx = *bit / AU_PIDSTEP; -+ *bit %= AU_PIDSTEP; -+} ++/* current->atomic_flags */ ++/* this value should never corrupt the ones defined in linux/sched.h */ ++#define PFA_AUFS 7 ++ ++TASK_PFA_TEST(AUFS, test_aufs) /* task_test_aufs */ ++TASK_PFA_SET(AUFS, aufs) /* task_set_aufs */ ++TASK_PFA_CLEAR(AUFS, aufs) /* task_clear_aufs */ + +static inline int si_pid_test(struct super_block *sb) +{ -+ pid_t bit; -+ int idx; -+ unsigned long *bitmap; -+ -+ si_pid_idx_bit(&idx, &bit); -+ bitmap = au_sbi(sb)->au_si_pid.pid_bitmap[idx]; -+ if (bitmap) -+ return test_bit(bit, bitmap); -+ return 0; ++ return !!task_test_aufs(current); +} + +static inline void si_pid_clr(struct super_block *sb) +{ -+ pid_t bit; -+ int idx; -+ unsigned long *bitmap; -+ -+ si_pid_idx_bit(&idx, &bit); -+ bitmap = au_sbi(sb)->au_si_pid.pid_bitmap[idx]; -+ BUG_ON(!bitmap); -+ AuDebugOn(!test_bit(bit, bitmap)); -+ clear_bit(bit, bitmap); -+ /* smp_mb(); */ ++ AuDebugOn(!task_test_aufs(current)); ++ task_clear_aufs(current); +} + -+void si_pid_set(struct super_block *sb); ++static inline void si_pid_set(struct super_block *sb) ++{ ++ AuDebugOn(task_test_aufs(current)); ++ task_set_aufs(current); ++} + +/* ---------------------------------------------------------------------- */ + @@ -28831,10 +30775,10 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h +#endif /* __AUFS_SUPER_H__ */ diff -urN /usr/share/empty/fs/aufs/sysaufs.c linux/fs/aufs/sysaufs.c --- /usr/share/empty/fs/aufs/sysaufs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/sysaufs.c 2016-10-09 16:55:36.496035060 +0200 ++++ linux/fs/aufs/sysaufs.c 2018-04-15 08:49:13.404484168 +0200 @@ -0,0 +1,104 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -28939,10 +30883,10 @@ diff -urN /usr/share/empty/fs/aufs/sysaufs.c linux/fs/aufs/sysaufs.c +} diff -urN /usr/share/empty/fs/aufs/sysaufs.h linux/fs/aufs/sysaufs.h --- /usr/share/empty/fs/aufs/sysaufs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/sysaufs.h 2016-10-09 16:55:36.496035060 +0200 ++++ linux/fs/aufs/sysaufs.h 2018-04-15 08:49:13.404484168 +0200 @@ -0,0 +1,101 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -29044,10 +30988,10 @@ diff -urN /usr/share/empty/fs/aufs/sysaufs.h linux/fs/aufs/sysaufs.h +#endif /* __SYSAUFS_H__ */ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c --- /usr/share/empty/fs/aufs/sysfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/sysfs.c 2016-10-09 16:55:36.496035060 +0200 ++++ linux/fs/aufs/sysfs.c 2018-04-15 08:49:13.404484168 +0200 @@ -0,0 +1,376 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -29257,7 +31201,7 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c + if (unlikely(err == PAGE_SIZE)) + err = -EFBIG; + } -+ au_delayed_kfree(seq); ++ kfree(seq); +out_unlock: + si_read_unlock(sb); +out: @@ -29328,9 +31272,9 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c + err = -EFAULT; + +out_seq: -+ au_delayed_kfree(seq); ++ kfree(seq); +out_buf: -+ au_delayed_free_page((unsigned long)buf); ++ free_page((unsigned long)buf); +out: + si_read_unlock(sb); + return err; @@ -29424,10 +31368,10 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c +} diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c --- /usr/share/empty/fs/aufs/sysrq.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/sysrq.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,157 @@ ++++ linux/fs/aufs/sysrq.c 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,159 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -29458,7 +31402,8 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c + char *plevel; + struct au_sbinfo *sbinfo; + struct file *file; -+ struct au_sphlhead *files; ++ struct hlist_bl_head *files; ++ struct hlist_bl_node *pos; + struct au_finfo *finfo; + + plevel = au_plevel; @@ -29517,8 +31462,8 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c +#endif + pr("files\n"); + files = &au_sbi(sb)->si_files; -+ spin_lock(&files->spin); -+ hlist_for_each_entry(finfo, &files->head, fi_hlist) { ++ hlist_bl_lock(files); ++ hlist_bl_for_each_entry(finfo, pos, files, fi_hlist) { + umode_t mode; + + file = finfo->fi_file; @@ -29526,7 +31471,7 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c + if (!special_file(mode)) + au_dpri_file(file); + } -+ spin_unlock(&files->spin); ++ hlist_bl_unlock(files); + pr("done\n"); + +#undef pr @@ -29543,10 +31488,11 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c +static void au_sysrq(int key __maybe_unused) +{ + struct au_sbinfo *sbinfo; ++ struct hlist_bl_node *pos; + + lockdep_off(); + au_sbilist_lock(); -+ hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list) ++ hlist_bl_for_each_entry(sbinfo, pos, &au_sbilist, si_list) + sysrq_sb(sbinfo->si_sb); + au_sbilist_unlock(); + lockdep_on(); @@ -29585,10 +31531,10 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c +} diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c --- /usr/share/empty/fs/aufs/vdir.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/vdir.c 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,900 @@ ++++ linux/fs/aufs/vdir.c 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,893 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -29699,7 +31645,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + struct hlist_node *node; + + hlist_for_each_entry_safe(pos, node, head, wh_hash) -+ au_delayed_kfree(pos); ++ kfree(pos); +} + +static void au_nhash_de_do_free(struct hlist_head *head) @@ -29708,7 +31654,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + struct hlist_node *node; + + hlist_for_each_entry_safe(pos, node, head, hash) -+ au_cache_dfree_vdir_dehstr(pos); ++ au_cache_free_vdir_dehstr(pos); +} + +static void au_nhash_do_free(struct au_nhash *nhash, @@ -29726,7 +31672,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + nhash_count(head); + free(head++); + } -+ au_delayed_kfree(nhash->nh_head); ++ kfree(nhash->nh_head); +} + +void au_nhash_wh_free(struct au_nhash *whlist) @@ -29939,23 +31885,15 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + +/* ---------------------------------------------------------------------- */ + -+void au_vdir_free(struct au_vdir *vdir, int atonce) ++void au_vdir_free(struct au_vdir *vdir) +{ + unsigned char **deblk; + + deblk = vdir->vd_deblk; -+ if (!atonce) { -+ while (vdir->vd_nblk--) -+ au_delayed_kfree(*deblk++); -+ au_delayed_kfree(vdir->vd_deblk); -+ au_cache_dfree_vdir(vdir); -+ } else { -+ /* not delayed */ -+ while (vdir->vd_nblk--) -+ kfree(*deblk++); -+ kfree(vdir->vd_deblk); -+ au_cache_free_vdir(vdir); -+ } ++ while (vdir->vd_nblk--) ++ kfree(*deblk++); ++ kfree(vdir->vd_deblk); ++ au_cache_free_vdir(vdir); +} + +static struct au_vdir *alloc_vdir(struct file *file) @@ -29989,10 +31927,10 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + if (!err) + return vdir; /* success */ + -+ au_delayed_kfree(vdir->vd_deblk); ++ kfree(vdir->vd_deblk); + +out_free: -+ au_cache_dfree_vdir(vdir); ++ au_cache_free_vdir(vdir); +out: + vdir = ERR_PTR(err); + return vdir; @@ -30004,7 +31942,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + union au_vdir_deblk_p p, deblk_end; + + while (vdir->vd_nblk > 1) { -+ au_delayed_kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); ++ kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); + /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ + vdir->vd_nblk--; + } @@ -30135,7 +32073,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + } + } + -+ au_delayed_free_page((unsigned long)o); ++ free_page((unsigned long)o); + +out: + AuTraceErr(err); @@ -30274,7 +32212,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + if (allocated) + au_set_ivdir(inode, allocated); + } else if (allocated) -+ au_vdir_free(allocated, /*atonce*/0); ++ au_vdir_free(allocated); + +out: + return err; @@ -30369,7 +32307,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + if (allocated) + au_set_fvdir_cache(file, allocated); + } else if (allocated) -+ au_vdir_free(allocated, /*atonce*/0); ++ au_vdir_free(allocated); + +out: + return err; @@ -30438,7 +32376,8 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + +out: + /* smp_mb(); */ -+ AuTraceErr(!valid); ++ if (!valid) ++ AuDbg("valid %d\n", !valid); + return valid; +} + @@ -30489,10 +32428,10 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c +} diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c --- /usr/share/empty/fs/aufs/vfsub.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/vfsub.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,884 @@ ++++ linux/fs/aufs/vfsub.c 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,894 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -30512,27 +32451,36 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c + * sub-routines for VFS + */ + ++#include +#include +#include +#include +#include -+#include "../fs/mount.h" +#include "aufs.h" + +#ifdef CONFIG_AUFS_BR_FUSE +int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb) +{ -+ struct nsproxy *ns; -+ + if (!au_test_fuse(h_sb) || !au_userns) + return 0; + -+ ns = current->nsproxy; -+ /* no {get,put}_nsproxy(ns) */ -+ return real_mount(mnt)->mnt_ns == ns->mnt_ns ? 0 : -EACCES; ++ return is_current_mnt_ns(mnt) ? 0 : -EACCES; +} +#endif + ++int vfsub_sync_filesystem(struct super_block *h_sb, int wait) ++{ ++ int err; ++ ++ lockdep_off(); ++ down_read(&h_sb->s_umount); ++ err = __sync_filesystem(h_sb, wait); ++ up_read(&h_sb->s_umount); ++ lockdep_on(); ++ ++ return err; ++} ++ +/* ---------------------------------------------------------------------- */ + +int vfsub_update_h_iattr(struct path *h_path, int *did) @@ -30876,7 +32824,7 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c + +int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct path *path, -+ struct inode **delegated_inode) ++ struct inode **delegated_inode, unsigned int flags) +{ + int err; + struct path tmp = { @@ -30897,7 +32845,7 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c + + lockdep_off(); + err = vfs_rename(src_dir, src_dentry, dir, path->dentry, -+ delegated_inode, /*flags*/0); ++ delegated_inode, flags); + lockdep_on(); + if (!err) { + int did; @@ -31076,6 +33024,7 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ + return err; +} + @@ -31377,10 +33326,10 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c +} diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h --- /usr/share/empty/fs/aufs/vfsub.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/vfsub.h 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,316 @@ ++++ linux/fs/aufs/vfsub.h 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,355 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -31435,6 +33384,13 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h +#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) +#define IMustLock(i) AuDebugOn(!inode_is_locked(i)) + ++/* why VFS doesn't define it? */ ++static inline ++void vfsub_inode_lock_shared_nested(struct inode *inode, unsigned int sc) ++{ ++ down_read_nested(&inode->i_rwsem, sc); ++} ++ +/* ---------------------------------------------------------------------- */ + +static inline void vfsub_drop_nlink(struct inode *inode) @@ -31464,6 +33420,8 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h +AuStubInt0(vfsub_test_mntns, struct vfsmount *mnt, struct super_block *h_sb); +#endif + ++int vfsub_sync_filesystem(struct super_block *h_sb, int wait); ++ +/* ---------------------------------------------------------------------- */ + +int vfsub_update_h_iattr(struct path *h_path, int *did); @@ -31544,7 +33502,7 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h + struct path *path, struct inode **delegated_inode); +int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry, + struct inode *hdir, struct path *path, -+ struct inode **delegated_inode); ++ struct inode **delegated_inode, unsigned int flags); +int vfsub_mkdir(struct inode *dir, struct path *path, int mode); +int vfsub_rmdir(struct inode *dir, struct path *path); + @@ -31645,6 +33603,36 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h + struct file *h_file); +int vfsub_fsync(struct file *file, struct path *path, int datasync); + ++/* ++ * re-use branch fs's ioctl(FICLONE) while aufs itself doesn't support such ++ * ioctl. ++ */ ++static inline int vfsub_clone_file_range(struct file *src, struct file *dst, ++ u64 len) ++{ ++ int err; ++ ++ lockdep_off(); ++ err = vfs_clone_file_range(src, 0, dst, 0, len); ++ lockdep_on(); ++ ++ return err; ++} ++ ++/* copy_file_range(2) is a systemcall */ ++static inline ssize_t vfsub_copy_file_range(struct file *src, loff_t src_pos, ++ struct file *dst, loff_t dst_pos, ++ size_t len, unsigned int flags) ++{ ++ ssize_t ssz; ++ ++ lockdep_off(); ++ ssz = vfs_copy_file_range(src, src_pos, dst, dst_pos, len, flags); ++ lockdep_on(); ++ ++ return ssz; ++} ++ +/* ---------------------------------------------------------------------- */ + +static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) @@ -31697,10 +33685,10 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h +#endif /* __AUFS_VFSUB_H__ */ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c --- /usr/share/empty/fs/aufs/wbr_policy.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/wbr_policy.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,765 @@ ++++ linux/fs/aufs/wbr_policy.c 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,830 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -32162,7 +34150,7 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + + mfs->mfsrr_bytes = bavail; + AuDbg("b%d\n", mfs->mfs_bindex); -+ au_delayed_kfree(st); ++ kfree(st); +} + +static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) @@ -32218,6 +34206,61 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + +/* ---------------------------------------------------------------------- */ + ++/* top down regardless parent, and then mfs */ ++static int au_wbr_create_tdmfs(struct dentry *dentry, ++ unsigned int flags __maybe_unused) ++{ ++ int err; ++ aufs_bindex_t bwh, btail, bindex, bfound, bmfs; ++ unsigned long long watermark; ++ struct super_block *sb; ++ struct au_wbr_mfs *mfs; ++ struct au_branch *br; ++ struct dentry *parent; ++ ++ 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_mfs(dentry, /*parent*/NULL); ++ watermark = mfs->mfsrr_watermark; ++ bmfs = mfs->mfs_bindex; ++ mutex_unlock(&mfs->mfs_lock); ++ ++ /* another style of au_wbr_create_exp() */ ++ bwh = au_dbwh(dentry); ++ parent = dget_parent(dentry); ++ btail = au_dbtaildir(parent); ++ if (bwh >= 0 && bwh < btail) ++ btail = bwh; ++ ++ err = au_wbr_nonopq(dentry, btail); ++ if (unlikely(err < 0)) ++ goto out; ++ btail = err; ++ bfound = -1; ++ for (bindex = 0; bindex <= btail; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_rdonly(br)) ++ continue; ++ if (br->br_wbr->wbr_bytes > watermark) { ++ bfound = bindex; ++ break; ++ } ++ } ++ err = bfound; ++ if (err < 0) ++ err = bmfs; ++ ++out: ++ dput(parent); ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ +/* most free space and then round robin */ +static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags) +{ @@ -32443,6 +34486,16 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + }, ++ [AuWbrCreate_TDMFS] = { ++ .create = au_wbr_create_tdmfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_TDMFSV] = { ++ .create = au_wbr_create_tdmfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, + [AuWbrCreate_PMFS] = { + .create = au_wbr_create_pmfs, + .init = au_wbr_create_init_mfs, @@ -32466,10 +34519,10 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c +}; diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c --- /usr/share/empty/fs/aufs/whout.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/whout.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,1060 @@ ++++ linux/fs/aufs/whout.c 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,1061 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -32632,7 +34685,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + +out_name: + if (name != defname) -+ au_delayed_kfree(name); ++ kfree(name); +out: + AuTraceErrPtr(dentry); + return dentry; @@ -32661,7 +34714,8 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + + /* under the same dir, no need to lock_rename() */ + delegated = NULL; -+ err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated); ++ err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated, ++ /*flags*/0); + AuTraceErr(err); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" @@ -33071,7 +35125,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + au_br_put(a->br); + si_write_unlock(a->sb); + au_nwt_done(&au_sbi(a->sb)->si_nowait); -+ au_delayed_kfree(arg); ++ kfree(arg); + if (unlikely(err)) + AuIOErr("err %d\n", err); +} @@ -33099,7 +35153,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + if (unlikely(wkq_err)) { + atomic_dec(&br->br_wbr->wbr_wh_running); + au_br_put(br); -+ au_delayed_kfree(arg); ++ kfree(arg); + } + do_dec = 0; + } @@ -33258,7 +35312,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + wh_dentry = ERR_PTR(err); + if (!err) { + wh_dentry = vfsub_lkup_one(&wh_name, h_parent); -+ au_delayed_kfree(wh_name.name); ++ kfree(wh_name.name); + } + return wh_dentry; +} @@ -33334,7 +35388,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + break; + } + } -+ au_delayed_free_page((unsigned long)wh_name.name); ++ free_page((unsigned long)wh_name.name); + +out: + return err; @@ -33376,7 +35430,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + rdhash = AUFS_RDHASH_DEF; + err = au_nhash_alloc(&whtmp->whlist, rdhash, gfp); + if (unlikely(err)) { -+ au_delayed_kfree(whtmp); ++ kfree(whtmp); + whtmp = ERR_PTR(err); + } + @@ -33391,7 +35445,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + dput(whtmp->wh_dentry); + iput(whtmp->dir); + au_nhash_wh_free(&whtmp->whlist); -+ au_delayed_kfree(whtmp); ++ kfree(whtmp); +} + +/* @@ -33530,10 +35584,10 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c +} diff -urN /usr/share/empty/fs/aufs/whout.h linux/fs/aufs/whout.h --- /usr/share/empty/fs/aufs/whout.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/whout.h 2016-10-09 16:55:36.496035060 +0200 ++++ linux/fs/aufs/whout.h 2018-04-15 08:49:13.404484168 +0200 @@ -0,0 +1,85 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -33619,10 +35673,10 @@ diff -urN /usr/share/empty/fs/aufs/whout.h linux/fs/aufs/whout.h +#endif /* __AUFS_WHOUT_H__ */ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c --- /usr/share/empty/fs/aufs/wkq.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/wkq.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,213 @@ ++++ linux/fs/aufs/wkq.c 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,212 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -33677,7 +35731,7 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c + else { + kobject_put(wkinfo->kobj); + module_put(THIS_MODULE); /* todo: ?? */ -+ au_delayed_kfree(wkinfo); ++ kfree(wkinfo); + } +} + @@ -33700,7 +35754,7 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c + +static void au_wkq_comp_free(struct completion *comp) +{ -+ au_delayed_kfree(comp); ++ kfree(comp); +} + +#else @@ -33764,11 +35818,10 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c + /* no timeout, no interrupt */ + wait_for_completion(wkinfo.comp); + au_wkq_comp_free(comp); -+ destroy_work_on_stack(&wkinfo.wk); + } + ++ destroy_work_on_stack(&wkinfo.wk); + return err; -+ +} + +/* @@ -33836,10 +35889,10 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c +} diff -urN /usr/share/empty/fs/aufs/wkq.h linux/fs/aufs/wkq.h --- /usr/share/empty/fs/aufs/wkq.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/wkq.h 2016-10-09 16:55:36.496035060 +0200 ++++ linux/fs/aufs/wkq.h 2018-04-15 08:49:13.404484168 +0200 @@ -0,0 +1,93 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -33865,7 +35918,7 @@ diff -urN /usr/share/empty/fs/aufs/wkq.h linux/fs/aufs/wkq.h + +#ifdef __KERNEL__ + -+#include ++#include + +struct super_block; + @@ -33933,10 +35986,10 @@ diff -urN /usr/share/empty/fs/aufs/wkq.h linux/fs/aufs/wkq.h +#endif /* __AUFS_WKQ_H__ */ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c --- /usr/share/empty/fs/aufs/xattr.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/xattr.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,347 @@ ++++ linux/fs/aufs/xattr.c 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,355 @@ +/* -+ * Copyright (C) 2014-2016 Junjiro R. Okajima ++ * Copyright (C) 2014-2018 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 @@ -33956,6 +36009,8 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + * handling xattr functions + */ + ++#include ++#include +#include +#include "aufs.h" + @@ -34051,7 +36106,7 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + h_isrc = d_inode(h_src); + h_idst = d_inode(h_dst); + inode_unlock(h_idst); -+ inode_lock_nested(h_isrc, AuLsc_I_CHILD); ++ vfsub_inode_lock_shared_nested(h_isrc, AuLsc_I_CHILD); + inode_lock_nested(h_idst, AuLsc_I_CHILD2); + unlocked = 0; + @@ -34077,7 +36132,7 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + goto out; + err = vfs_listxattr(h_src, p, ssz); + } -+ inode_unlock(h_isrc); ++ inode_unlock_shared(h_isrc); + unlocked = 1; + AuDbg("err %d, ssz %zd\n", err, ssz); + if (unlikely(err < 0)) @@ -34113,21 +36168,32 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + AuTraceErr(err); + } + -+ if (value) -+ au_delayed_kfree(value); ++ kfree(value); + +out_free: -+ if (o) -+ au_delayed_kfree(o); ++ kfree(o); +out: + if (!unlocked) -+ inode_unlock(h_isrc); ++ inode_unlock_shared(h_isrc); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + ++static int au_smack_reentering(struct super_block *sb) ++{ ++#if IS_ENABLED(CONFIG_SECURITY_SMACK) ++ /* ++ * as a part of lookup, smack_d_instantiate() is called, and it calls ++ * i_op->getxattr(). ouch. ++ */ ++ return si_pid_test(sb); ++#else ++ return 0; ++#endif ++} ++ +enum { + AU_XATTR_LIST, + AU_XATTR_GET @@ -34151,14 +36217,18 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c +static ssize_t au_lgxattr(struct dentry *dentry, struct au_lgxattr *arg) +{ + ssize_t err; ++ int reenter; + struct path h_path; + struct super_block *sb; + + sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ err = au_h_path_getattr(dentry, /*force*/1, &h_path); ++ reenter = au_smack_reentering(sb); ++ if (!reenter) { ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ } ++ err = au_h_path_getattr(dentry, /*force*/1, &h_path, reenter); + if (unlikely(err)) + goto out_si; + if (unlikely(!h_path.dentry)) @@ -34180,9 +36250,11 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + } + +out_di: -+ di_read_unlock(dentry, AuLock_IR); ++ if (!reenter) ++ di_read_unlock(dentry, AuLock_IR); +out_si: -+ si_read_unlock(sb); ++ if (!reenter) ++ si_read_unlock(sb); +out: + AuTraceErr(err); + return err; @@ -34201,8 +36273,9 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + return au_lgxattr(dentry, &arg); +} + -+ssize_t aufs_getxattr(struct dentry *dentry, struct inode *inode __maybe_unused, -+ const char *name, void *value, size_t size) ++static ssize_t au_getxattr(struct dentry *dentry, ++ struct inode *inode __maybe_unused, ++ const char *name, void *value, size_t size) +{ + struct au_lgxattr arg = { + .type = AU_XATTR_GET, @@ -34216,10 +36289,11 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + return au_lgxattr(dentry, &arg); +} + -+int aufs_setxattr(struct dentry *dentry, struct inode *inode, const char *name, -+ const void *value, size_t size, int flags) ++static int au_setxattr(struct dentry *dentry, struct inode *inode, ++ const char *name, const void *value, size_t size, ++ int flags) +{ -+ struct au_srxattr arg = { ++ struct au_sxattr arg = { + .type = AU_XATTR_SET, + .u.set = { + .name = name, @@ -34229,65 +36303,52 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + }, + }; + -+ return au_srxattr(dentry, inode, &arg); -+} -+ -+int aufs_removexattr(struct dentry *dentry, const char *name) -+{ -+ struct au_srxattr arg = { -+ .type = AU_XATTR_REMOVE, -+ .u.remove = { -+ .name = name -+ }, -+ }; -+ -+ return au_srxattr(dentry, d_inode(dentry), &arg); ++ return au_sxattr(dentry, inode, &arg); +} + +/* ---------------------------------------------------------------------- */ + -+#if 0 -+static size_t au_xattr_list(struct dentry *dentry, char *list, size_t list_size, -+ const char *name, size_t name_len, int type) -+{ -+ return aufs_listxattr(dentry, list, list_size); -+} -+ -+static int au_xattr_get(struct dentry *dentry, const char *name, void *buffer, -+ size_t size, int type) ++static int au_xattr_get(const struct xattr_handler *handler, ++ struct dentry *dentry, struct inode *inode, ++ const char *name, void *buffer, size_t size) +{ -+ return aufs_getxattr(dentry, name, buffer, size); ++ return au_getxattr(dentry, inode, name, buffer, size); +} + -+static int au_xattr_set(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags, int type) ++static int au_xattr_set(const struct xattr_handler *handler, ++ struct dentry *dentry, struct inode *inode, ++ const char *name, const void *value, size_t size, ++ int flags) +{ -+ return aufs_setxattr(dentry, name, value, size, flags); ++ return au_setxattr(dentry, inode, name, value, size, flags); +} + +static const struct xattr_handler au_xattr_handler = { -+ /* no prefix, no flags */ -+ .list = au_xattr_list, ++ .name = "", ++ .prefix = "", + .get = au_xattr_get, + .set = au_xattr_set -+ /* why no remove? */ +}; + +static const struct xattr_handler *au_xattr_handlers[] = { -+ &au_xattr_handler ++#ifdef CONFIG_FS_POSIX_ACL ++ &posix_acl_access_xattr_handler, ++ &posix_acl_default_xattr_handler, ++#endif ++ &au_xattr_handler, /* must be last */ ++ NULL +}; + +void au_xattr_init(struct super_block *sb) +{ -+ /* sb->s_xattr = au_xattr_handlers; */ ++ sb->s_xattr = au_xattr_handlers; +} -+#endif diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c --- /usr/share/empty/fs/aufs/xino.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux/fs/aufs/xino.c 2016-10-09 16:55:36.496035060 +0200 -@@ -0,0 +1,1318 @@ ++++ linux/fs/aufs/xino.c 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,1415 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -34626,7 +36687,7 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + AuErr1("statfs err %d, ignored\n", err); + +out_st: -+ au_delayed_kfree(st); ++ kfree(st); +out: + return err; +} @@ -34661,7 +36722,7 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + au_br_put(br); + si_write_unlock(sb); + au_nwt_done(&au_sbi(sb)->si_nowait); -+ au_delayed_kfree(args); ++ kfree(args); +} + +static int xino_trunc_test(struct super_block *sb, struct au_branch *br) @@ -34715,7 +36776,7 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + + pr_err("wkq %d\n", wkq_err); + au_br_put(br); -+ au_delayed_kfree(args); ++ kfree(args); + +out: + atomic_dec(&br->br_xino_running); @@ -35239,7 +37300,7 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); + else + AuDbg("b%d\n", bindex); -+ au_delayed_free_page((unsigned long)page); ++ free_page((unsigned long)page); + +out: + return err; @@ -35317,7 +37378,7 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + fput(sbinfo->si_xib); + sbinfo->si_xib = NULL; + if (sbinfo->si_xib_buf) -+ au_delayed_free_page((unsigned long)sbinfo->si_xib_buf); ++ free_page((unsigned long)sbinfo->si_xib_buf); + sbinfo->si_xib_buf = NULL; +} + @@ -35361,7 +37422,7 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + +out_free: + if (sbinfo->si_xib_buf) -+ au_delayed_free_page((unsigned long)sbinfo->si_xib_buf); ++ free_page((unsigned long)sbinfo->si_xib_buf); + sbinfo->si_xib_buf = NULL; + if (err >= 0) + err = -EIO; @@ -35454,7 +37515,7 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + fput(p->new); + else + break; -+ au_delayed_kfree(fpair); ++ kfree(fpair); +out: + return err; +} @@ -35565,7 +37626,7 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + if (!IS_ERR(file)) + au_xino_brid_set(sb, br->br_id); + } -+ au_delayed_free_page((unsigned long)page); ++ free_page((unsigned long)page); + } else { + file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0); + if (IS_ERR(file)) @@ -35604,12 +37665,109 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c +out: + return err; +} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex, ++ ino_t h_ino, int idx) ++{ ++ struct au_xino_file *xino; ++ ++ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); ++ xino = &au_sbr(sb, bindex)->br_xino; ++ AuDebugOn(idx < 0 || xino->xi_nondir.total <= idx); ++ ++ spin_lock(&xino->xi_nondir.spin); ++ AuDebugOn(xino->xi_nondir.array[idx] != h_ino); ++ xino->xi_nondir.array[idx] = 0; ++ spin_unlock(&xino->xi_nondir.spin); ++ wake_up_all(&xino->xi_nondir.wqh); ++} ++ ++static int au_xinondir_find(struct au_xino_file *xino, ino_t h_ino) ++{ ++ int found, total, i; ++ ++ found = -1; ++ total = xino->xi_nondir.total; ++ for (i = 0; i < total; i++) { ++ if (xino->xi_nondir.array[i] != h_ino) ++ continue; ++ found = i; ++ break; ++ } ++ ++ return found; ++} ++ ++static int au_xinondir_expand(struct au_xino_file *xino) ++{ ++ int err, sz; ++ ino_t *p; ++ ++ BUILD_BUG_ON(KMALLOC_MAX_SIZE > INT_MAX); ++ ++ err = -ENOMEM; ++ sz = xino->xi_nondir.total * sizeof(ino_t); ++ if (unlikely(sz > KMALLOC_MAX_SIZE / 2)) ++ goto out; ++ p = au_kzrealloc(xino->xi_nondir.array, sz, sz << 1, GFP_ATOMIC, ++ /*may_shrink*/0); ++ if (p) { ++ xino->xi_nondir.array = p; ++ xino->xi_nondir.total <<= 1; ++ AuDbg("xi_nondir.total %d\n", xino->xi_nondir.total); ++ err = 0; ++ } ++ ++out: ++ return err; ++} ++ ++int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ int *idx) ++{ ++ int err, found, empty; ++ struct au_xino_file *xino; ++ ++ err = 0; ++ *idx = -1; ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ goto out; /* no xino */ ++ ++ xino = &au_sbr(sb, bindex)->br_xino; ++ ++again: ++ spin_lock(&xino->xi_nondir.spin); ++ found = au_xinondir_find(xino, h_ino); ++ if (found == -1) { ++ empty = au_xinondir_find(xino, /*h_ino*/0); ++ if (empty == -1) { ++ empty = xino->xi_nondir.total; ++ err = au_xinondir_expand(xino); ++ if (unlikely(err)) ++ goto out_unlock; ++ } ++ xino->xi_nondir.array[empty] = h_ino; ++ *idx = empty; ++ } else { ++ spin_unlock(&xino->xi_nondir.spin); ++ wait_event(xino->xi_nondir.wqh, ++ xino->xi_nondir.array[found] != h_ino); ++ goto again; ++ } ++ ++out_unlock: ++ spin_unlock(&xino->xi_nondir.spin); ++out: ++ return err; ++} diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/linux/aufs_type.h --- /usr/share/empty/include/uapi/linux/aufs_type.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux/include/uapi/linux/aufs_type.h 2016-10-09 16:55:38.889431135 +0200 -@@ -0,0 +1,419 @@ ++++ linux/include/uapi/linux/aufs_type.h 2018-04-15 08:49:13.404484168 +0200 +@@ -0,0 +1,447 @@ +/* -+ * Copyright (C) 2005-2016 Junjiro R. Okajima ++ * Copyright (C) 2005-2018 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 @@ -35649,7 +37807,7 @@ diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/lin + +#include + -+#define AUFS_VERSION "4.x-rcN-20161003" ++#define AUFS_VERSION "4.9-20180409" + +/* todo? move this to linux-2.6.19/include/magic.h */ +#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') @@ -35711,6 +37869,13 @@ diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/lin +#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME +#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME + ++/* dirren, renamed dir */ ++#define AUFS_DR_INFO_PFX AUFS_WH_PFX ".dr." ++#define AUFS_DR_BRHINO_NAME AUFS_WH_PFX "hino" ++/* whiteouted doubly */ ++#define AUFS_WH_DR_INFO_PFX AUFS_WH_PFX AUFS_DR_INFO_PFX ++#define AUFS_WH_DR_BRHINO AUFS_WH_PFX AUFS_DR_BRHINO_NAME ++ +#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ +#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME + @@ -35929,6 +38094,27 @@ diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/lin + +/* ---------------------------------------------------------------------- */ + ++/* dirren. the branch is identified by the filename who contains this */ ++struct au_drinfo { ++ uint64_t ino; ++ union { ++ uint8_t oldnamelen; ++ uint64_t _padding; ++ }; ++ uint8_t oldname[0]; ++} __aligned(8); ++ ++struct au_drinfo_fdata { ++ uint32_t magic; ++ struct au_drinfo drinfo; ++} __aligned(8); ++ ++#define AUFS_DRINFO_MAGIC_V1 ('a' << 24 | 'd' << 16 | 'r' << 8 | 0x01) ++/* future */ ++#define AUFS_DRINFO_MAGIC_V2 ('a' << 24 | 'd' << 16 | 'r' << 8 | 0x02) ++ ++/* ---------------------------------------------------------------------- */ ++ +struct aufs_wbr_fd { + uint32_t oflags; + int16_t brid; @@ -36027,10 +38213,10 @@ diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/lin +#define AUFS_CTL_FHSM_FD _IOW(AuCtlType, AuCtl_FHSM_FD, int) + +#endif /* __AUFS_TYPE_H__ */ -aufs4.x-rcN loopback patch +aufs4.9 loopback patch diff --git a/drivers/block/loop.c b/drivers/block/loop.c -index 005e292..e9e517d 100644 +index 6ee9235..f64161f 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -551,7 +551,7 @@ static int do_req_filebacked(struct loop_device *lo, struct request *rq) @@ -36065,10 +38251,10 @@ index 005e292..e9e517d 100644 /* freeze queue and wait for completion of scheduled requests */ blk_mq_freeze_queue(lo->lo_queue); -@@ -612,7 +615,16 @@ static int loop_switch(struct loop_device *lo, struct file *file) - */ - static int loop_flush(struct loop_device *lo) - { +@@ -619,7 +619,16 @@ static int loop_flush(struct loop_device + /* loop not yet configured, no running thread, nothing to flush */ + if (lo->lo_state != Lo_bound) + return 0; - return loop_switch(lo, NULL); + return loop_switch(lo, NULL, NULL); +} @@ -36091,7 +38277,7 @@ index 005e292..e9e517d 100644 struct inode *inode; int error; -@@ -665,9 +678,16 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, +@@ -665,13 +678,20 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, file = fget(arg); if (!file) goto out; @@ -36102,6 +38288,10 @@ index 005e292..e9e517d 100644 + get_file(file); + } + error = loop_validate_file(file, bdev); + if (error) + goto out_putf; + inode = file->f_mapping->host; old_file = lo->lo_backing_file; + old_virt_file = lo->lo_backing_virt_file; @@ -36135,7 +38325,7 @@ index 005e292..e9e517d 100644 static int loop_set_fd(struct loop_device *lo, fmode_t mode, struct block_device *bdev, unsigned int arg) { -- struct file *file, *f; +- struct file *file; + struct file *file, *f, *virt_file = NULL; struct inode *inode; struct address_space *mapping; @@ -36209,10 +38399,10 @@ index fb2237c..c3888c5 100644 unsigned lo_blocksize; void *key_data; diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c -index 00475fb..01390e1 100644 +index 22e93ee..4586e8d 100644 --- a/fs/aufs/f_op.c +++ b/fs/aufs/f_op.c -@@ -348,7 +348,7 @@ static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) +@@ -357,7 +357,7 @@ static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) if (IS_ERR(h_file)) goto out; @@ -36222,12 +38412,12 @@ index 00475fb..01390e1 100644 if (file->f_mapping != h_file->f_mapping) { file->f_mapping = h_file->f_mapping; diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c -index c3ca50f..a3dbdaf 100644 +index c1dbe59..1fc4b49 100644 --- a/fs/aufs/loop.c +++ b/fs/aufs/loop.c @@ -132,3 +132,19 @@ void au_loopback_fin(void) symbol_put(loop_backing_file); - au_delayed_kfree(au_warn_loopback_array); + kfree(au_warn_loopback_array); } + +/* ---------------------------------------------------------------------- */ @@ -36246,7 +38436,7 @@ index c3ca50f..a3dbdaf 100644 + return f; +} diff --git a/fs/aufs/loop.h b/fs/aufs/loop.h -index 48bf070..66afec7 100644 +index c08374e..5e05083 100644 --- a/fs/aufs/loop.h +++ b/fs/aufs/loop.h @@ -25,7 +25,11 @@ void au_warn_loopback(struct super_block *h_sb); @@ -36271,10 +38461,10 @@ index 48bf070..66afec7 100644 #endif /* __KERNEL__ */ diff --git a/fs/aufs/super.c b/fs/aufs/super.c -index 58a773c..75f212c 100644 +index dd38e63..2486242 100644 --- a/fs/aufs/super.c +++ b/fs/aufs/super.c -@@ -831,7 +831,10 @@ static const struct super_operations aufs_sop = { +@@ -838,7 +838,10 @@ static const struct super_operations aufs_sop = { .statfs = aufs_statfs, .put_super = aufs_put_super, .sync_fs = aufs_sync_fs, @@ -36287,10 +38477,10 @@ index 58a773c..75f212c 100644 /* ---------------------------------------------------------------------- */ diff --git a/include/linux/fs.h b/include/linux/fs.h -index fc80663..0c4c6fa 100644 +index 42c7695..3e4f068 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h -@@ -1814,6 +1814,10 @@ struct super_operations { +@@ -1823,6 +1823,10 @@ struct super_operations { struct shrink_control *); long (*free_cached_objects)(struct super_block *, struct shrink_control *);