X-Git-Url: http://git.pld-linux.org/?a=blobdiff_plain;f=kernel-aufs4.patch;h=01990861c20b22785fb48c4ef8e58e4bbd686256;hb=9f237c513c6235e85fb8c1eab1c003aeb434b70f;hp=0614bab4af98789624a8e77dc434a56fe5924833;hpb=be52b2493105e67b80c5d7c0939df35c0ba6377b;p=packages%2Fkernel.git diff --git a/kernel-aufs4.patch b/kernel-aufs4.patch index 0614bab4..01990861 100644 --- a/kernel-aufs4.patch +++ b/kernel-aufs4.patch @@ -1,10 +1,11 @@ +SPDX-License-Identifier: GPL-2.0 aufs4.x-rcN kbuild patch diff --git a/fs/Kconfig b/fs/Kconfig -index 6ce72d8..4aa31ea 100644 +index ac474a6..284cee9 100644 --- a/fs/Kconfig +++ b/fs/Kconfig -@@ -221,6 +221,7 @@ source "fs/pstore/Kconfig" +@@ -255,6 +255,7 @@ source "fs/pstore/Kconfig" source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" @@ -13,33 +14,22 @@ index 6ce72d8..4aa31ea 100644 endif # MISC_FILESYSTEMS diff --git a/fs/Makefile b/fs/Makefile -index 79f5225..a7c7f16 100644 +index 293733f..12d19d0 100644 --- a/fs/Makefile +++ b/fs/Makefile -@@ -126,3 +126,4 @@ obj-y += exofs/ # Multiple modules +@@ -128,3 +128,4 @@ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ +obj-$(CONFIG_AUFS_FS) += aufs/ -diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild -index c2e5d6c..d736c11 100644 ---- a/include/uapi/linux/Kbuild -+++ b/include/uapi/linux/Kbuild -@@ -59,6 +59,7 @@ header-y += atmsvc.h - header-y += atm_tcp.h - header-y += atm_zatm.h - header-y += audit.h -+header-y += aufs_type.h - header-y += auto_fs4.h - header-y += auto_fs.h - header-y += auxvec.h +SPDX-License-Identifier: GPL-2.0 aufs4.x-rcN base patch diff --git a/MAINTAINERS b/MAINTAINERS -index 233f834..c250892 100644 +index 8119141..5e84420 100644 --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -2029,6 +2029,19 @@ F: include/linux/audit.h +@@ -2590,6 +2590,19 @@ F: include/linux/audit.h F: include/uapi/linux/audit.h F: kernel/audit* @@ -58,13 +48,13 @@ index 233f834..c250892 100644 + AUXILIARY DISPLAY DRIVERS M: Miguel Ojeda Sandonis - W: http://miguelojeda.es/auxdisplay.htm + S: Maintained diff --git a/drivers/block/loop.c b/drivers/block/loop.c -index 423f4ca..abfdd2b 100644 +index cb0cc86..470dd02 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c -@@ -706,6 +706,24 @@ static inline int is_loop_device(struct file *file) - return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR; +@@ -738,6 +738,24 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, + return error; } +/* @@ -83,31 +73,83 @@ index 423f4ca..abfdd2b 100644 + } + return ret; +} -+EXPORT_SYMBOL(loop_backing_file); ++EXPORT_SYMBOL_GPL(loop_backing_file); + /* loop sysfs attributes */ static ssize_t loop_attr_show(struct device *dev, char *page, diff --git a/fs/dcache.c b/fs/dcache.c -index 5c33aeb..8aa7f26 100644 +index 2593153..6369b30 100644 --- a/fs/dcache.c +++ b/fs/dcache.c -@@ -1167,7 +1167,7 @@ enum d_walk_ret { +@@ -1224,7 +1224,7 @@ enum d_walk_ret { * - * The @enter() and @finish() callbacks are called with d_lock held. + * The @enter() callbacks are called with d_lock held. */ -static void d_walk(struct dentry *parent, void *data, +void d_walk(struct dentry *parent, void *data, - enum d_walk_ret (*enter)(void *, struct dentry *), - void (*finish)(void *)) + enum d_walk_ret (*enter)(void *, struct dentry *)) + { + struct dentry *this_parent; +diff --git a/fs/fcntl.c b/fs/fcntl.c +index 0831851..78234ee 100644 +--- a/fs/fcntl.c ++++ b/fs/fcntl.c +@@ -32,7 +32,7 @@ + + #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME) + +-static int setfl(int fd, struct file * filp, unsigned long arg) ++int setfl(int fd, struct file * filp, unsigned long arg) + { + struct inode * inode = file_inode(filp); + int error = 0; +@@ -63,6 +63,8 @@ static int setfl(int fd, struct file * filp, unsigned long arg) + + if (filp->f_op->check_flags) + error = filp->f_op->check_flags(arg); ++ if (!error && filp->f_op->setfl) ++ error = filp->f_op->setfl(filp, arg); + if (error) + return error; + +diff --git a/fs/inode.c b/fs/inode.c +index 35d2108..d2395eb 100644 +--- a/fs/inode.c ++++ b/fs/inode.c +@@ -1660,7 +1660,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. + */ +-static int update_time(struct inode *inode, struct timespec64 *time, int flags) ++int update_time(struct inode *inode, struct timespec64 *time, int flags) { + int (*update_time)(struct inode *, struct timespec64 *, int); + +diff --git a/fs/namespace.c b/fs/namespace.c +index a7f9126..46ed643 100644 +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -770,6 +770,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 819ef3f..fd0414e 100644 +index 58f3053..a2a55ea 100644 --- a/fs/read_write.c +++ b/fs/read_write.c -@@ -494,6 +494,28 @@ ssize_t __vfs_write(struct file *file, const char __user *p, size_t count, +@@ -489,6 +489,28 @@ ssize_t __vfs_write(struct file *file, const char __user *p, size_t count, + return -EINVAL; } - EXPORT_SYMBOL(__vfs_write); +vfs_readf_t vfs_readf(struct file *file) +{ @@ -131,14 +173,14 @@ index 819ef3f..fd0414e 100644 + return ERR_PTR(-ENOSYS); +} + - ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) + ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos) { mm_segment_t old_fs; diff --git a/fs/splice.c b/fs/splice.c -index 4cf700d..30a091d 100644 +index de2ede0..5dcf77b 100644 --- a/fs/splice.c +++ b/fs/splice.c -@@ -1110,8 +1110,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); +@@ -837,8 +837,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); /* * Attempt to initiate a splice from pipe to file. */ @@ -149,7 +191,7 @@ index 4cf700d..30a091d 100644 { ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); -@@ -1127,9 +1127,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +@@ -854,9 +854,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. */ @@ -162,23 +204,40 @@ index 4cf700d..30a091d 100644 { ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); -diff --git a/include/linux/file.h b/include/linux/file.h -index f87d308..9a290b3 100644 ---- a/include/linux/file.h -+++ b/include/linux/file.h -@@ -19,6 +19,7 @@ struct dentry; - struct path; - extern struct file *alloc_file(struct path *, fmode_t mode, - const struct file_operations *fop); -+extern struct file *get_empty_filp(void); - - static inline void fput_light(struct file *file, int fput_needed) +diff --git a/fs/sync.c b/fs/sync.c +index b54e054..2860782 100644 +--- a/fs/sync.c ++++ b/fs/sync.c +@@ -28,7 +28,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/fs.h b/include/linux/fs.h -index 3aa5142..8d48506 100644 +index c95c080..0e44705 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h -@@ -1672,6 +1672,12 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, +@@ -1305,6 +1305,7 @@ extern void fasync_free(struct fasync_struct *); + /* can be called from interrupts */ + extern void kill_fasync(struct fasync_struct **, int, int); + ++extern int setfl(int fd, struct file * filp, unsigned long arg); + extern void __f_setown(struct file *filp, struct pid *, enum pid_type, int force); + extern int f_setown(struct file *filp, unsigned long arg, int force); + extern void f_delown(struct file *filp); +@@ -1797,6 +1798,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); ++ int (*setfl)(struct file *, unsigned long); + 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); +@@ -1867,6 +1869,12 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, struct iovec *fast_pointer, struct iovec **ret_pointer); @@ -189,16 +248,72 @@ index 3aa5142..8d48506 100644 +vfs_writef_t vfs_writef(struct file *file); + 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 *); + extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *); +@@ -2292,6 +2300,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 timespec64 *, int); ++extern int update_time(struct inode *, struct timespec64 *, int); + + /* /sys/fs */ + extern struct kobject *fs_kobj; +@@ -2579,6 +2588,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/lockdep.h b/include/linux/lockdep.h +index 1fd82ff..a5ccac5 100644 +--- a/include/linux/lockdep.h ++++ b/include/linux/lockdep.h +@@ -308,6 +308,8 @@ static inline int lockdep_match_key(struct lockdep_map *lock, + return lock->key == key; + } + ++struct lock_class *lockdep_hlock_class(struct held_lock *hlock); ++ + /* + * Acquire a lock. + * +@@ -434,6 +436,7 @@ struct lockdep_map { }; + + #define lockdep_depth(tsk) (0) + ++#define lockdep_is_held(lock) (1) + #define lockdep_is_held_type(l, r) (1) + + #define lockdep_assert_held(l) do { (void)(l); } while (0) +diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h +index 3594208..24f5fd1 100644 +--- a/include/linux/mnt_namespace.h ++++ b/include/linux/mnt_namespace.h +@@ -6,11 +6,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 74b4911..19789fb 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); +@@ -87,4 +87,10 @@ extern void splice_shrink_spd(struct splice_pipe_desc *); 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); @@ -206,13 +321,35 @@ index da2751d..2e0fca6 100644 + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); #endif +diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c +index 1efada2..447bc0b 100644 +--- a/kernel/locking/lockdep.c ++++ b/kernel/locking/lockdep.c +@@ -140,7 +140,7 @@ static struct lock_list list_entries[MAX_LOCKDEP_ENTRIES]; + unsigned long nr_lock_classes; + struct lock_class lock_classes[MAX_LOCKDEP_KEYS]; + +-static inline struct lock_class *hlock_class(struct held_lock *hlock) ++inline struct lock_class *lockdep_hlock_class(struct held_lock *hlock) + { + if (!hlock->class_idx) { + /* +@@ -151,6 +151,7 @@ static inline struct lock_class *hlock_class(struct held_lock *hlock) + } + return lock_classes + hlock->class_idx - 1; + } ++#define hlock_class(hlock) lockdep_hlock_class(hlock) + + #ifdef CONFIG_LOCK_STAT + static DEFINE_PER_CPU(struct lock_class_stats[MAX_LOCKDEP_KEYS], cpu_lock_stats); +SPDX-License-Identifier: GPL-2.0 aufs4.x-rcN mmap patch diff --git a/fs/proc/base.c b/fs/proc/base.c -index 4bd5d31..aa41f2a 100644 +index ce34654..28508b1 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c -@@ -1921,7 +1921,7 @@ static int proc_map_files_get_link(struct dentry *dentry, struct path *path) +@@ -2016,7 +2016,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) { @@ -222,7 +359,7 @@ index 4bd5d31..aa41f2a 100644 rc = 0; } diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c -index f8595e8..cb8eda0 100644 +index 3b63be6..fb9913b 100644 --- a/fs/proc/nommu.c +++ b/fs/proc/nommu.c @@ -45,7 +45,10 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) @@ -238,10 +375,10 @@ index f8595e8..cb8eda0 100644 ino = inode->i_ino; } diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c -index 187b3b5..e03793e 100644 +index 47c3764..e37e4b5 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c -@@ -281,7 +281,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) +@@ -305,7 +305,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma) const char *name = NULL; if (file) { @@ -253,7 +390,7 @@ index 187b3b5..e03793e 100644 dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; -@@ -1505,7 +1508,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) +@@ -1729,7 +1732,7 @@ static int show_numa_map(struct seq_file *m, void *v) struct proc_maps_private *proc_priv = &numa_priv->proc_maps; struct vm_area_struct *vma = v; struct numa_maps *md = &numa_priv->md; @@ -263,10 +400,10 @@ index 187b3b5..e03793e 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 e0d64c9..7aa92db 100644 +index 0b63d68..400d1c5 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c -@@ -160,7 +160,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) { @@ -279,12 +416,12 @@ index e0d64c9..7aa92db 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 00bad77..cc616b0 100644 +index 5411de9..b3cd025 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h -@@ -1183,6 +1183,28 @@ static inline int fixup_user_fault(struct task_struct *tsk, +@@ -1460,6 +1460,28 @@ static inline void unmap_shared_mapping_range(struct address_space *mapping, + unmap_mapping_range(mapping, holebegin, holelen, 0); } - #endif +extern void vma_do_file_update_time(struct vm_area_struct *, const char[], int); +extern struct file *vma_do_pr_or_file(struct vm_area_struct *, const char[], @@ -308,14 +445,14 @@ index 00bad77..cc616b0 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 f8d1492..c3a3760 100644 +index 5ed8f62..0122975 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h -@@ -272,6 +272,7 @@ struct vm_region { +@@ -239,6 +239,7 @@ struct vm_region { unsigned long vm_top; /* region allocated to here */ unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ struct file *vm_file; /* the backing file or NULL */ @@ -323,19 +460,19 @@ index f8d1492..c3a3760 100644 int vm_usage; /* region usage count (access under nommu_region_sem) */ bool vm_icache_flushed : 1; /* true if the icache has been flushed for -@@ -346,6 +347,7 @@ struct vm_area_struct { +@@ -313,6 +314,7 @@ struct vm_area_struct { unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE - units, *not* PAGE_CACHE_SIZE */ + units */ struct file * vm_file; /* File we map to (can be NULL). */ + struct file *vm_prfile; /* shadow of vm_file */ void * vm_private_data; /* was vm_pte (shared mem) */ - #ifndef CONFIG_MMU + atomic_long_t swap_readahead_info; diff --git a/kernel/fork.c b/kernel/fork.c -index fce002e..2230a0f 100644 +index 07cddff..d837e55 100644 --- a/kernel/fork.c +++ b/kernel/fork.c -@@ -464,7 +464,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) +@@ -546,7 +546,7 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm, struct inode *inode = file_inode(file); struct address_space *mapping = file->f_mapping; @@ -345,10 +482,10 @@ index fce002e..2230a0f 100644 atomic_dec(&inode->i_writecount); i_mmap_lock_write(mapping); diff --git a/mm/Makefile b/mm/Makefile -index 2ed4319..e3a53f5 100644 +index d210cc9..e77e80c 100644 --- a/mm/Makefile +++ b/mm/Makefile -@@ -21,7 +21,7 @@ obj-y := filemap.o mempool.o oom_kill.o \ +@@ -39,7 +39,7 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \ mm_init.o mmu_context.o percpu.o slab_common.o \ compaction.o vmacache.o \ interval_tree.o list_lru.o workingset.o \ @@ -356,47 +493,34 @@ index 2ed4319..e3a53f5 100644 + prfile.o debug.o $(mmu-y) obj-y += init-mm.o - + obj-y += memblock.o diff --git a/mm/filemap.c b/mm/filemap.c -index 1bb0076..8eaece8 100644 +index 81adec8..8507cec 100644 --- a/mm/filemap.c +++ b/mm/filemap.c -@@ -2128,7 +2128,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) - int ret = VM_FAULT_LOCKED; +@@ -2609,7 +2609,7 @@ vm_fault_t filemap_page_mkwrite(struct vm_fault *vmf) + vm_fault_t ret = VM_FAULT_LOCKED; sb_start_pagefault(inode->i_sb); -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); +- file_update_time(vmf->vma->vm_file); ++ vma_file_update_time(vmf->vma); lock_page(page); if (page->mapping != inode->i_mapping) { unlock_page(page); -diff --git a/mm/memory.c b/mm/memory.c -index c387430..d434404 100644 ---- a/mm/memory.c -+++ b/mm/memory.c -@@ -2035,7 +2035,7 @@ static inline int wp_page_reuse(struct mm_struct *mm, - } - - if (!page_mkwrite) -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - } - - return VM_FAULT_WRITE; diff --git a/mm/mmap.c b/mm/mmap.c -index 2ce04a6..f555c0a 100644 +index 6c04292..f3629c1 100644 --- a/mm/mmap.c +++ b/mm/mmap.c -@@ -275,7 +275,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) +@@ -180,7 +180,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) - fput(vma->vm_file); + vma_fput(vma); mpol_put(vma_policy(vma)); - kmem_cache_free(vm_area_cachep, vma); + vm_area_free(vma); return next; -@@ -887,7 +887,7 @@ again: remove_next = 1 + (end > next->vm_end); +@@ -929,7 +929,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); @@ -405,7 +529,7 @@ index 2ce04a6..f555c0a 100644 } if (next->anon_vma) anon_vma_merge(vma, next); -@@ -1681,8 +1681,8 @@ out: +@@ -1845,8 +1845,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr, return addr; unmap_and_free_vma: @@ -415,7 +539,7 @@ index 2ce04a6..f555c0a 100644 /* Undo any partial mapping done by a device driver. */ unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); -@@ -2488,7 +2488,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, +@@ -2665,7 +2665,7 @@ int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, goto out_free_mpol; if (new->vm_file) @@ -424,7 +548,7 @@ index 2ce04a6..f555c0a 100644 if (new->vm_ops && new->vm_ops->open) new->vm_ops->open(new); -@@ -2507,7 +2507,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, +@@ -2684,7 +2684,7 @@ 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) @@ -433,28 +557,45 @@ index 2ce04a6..f555c0a 100644 unlink_anon_vmas(new); out_free_mpol: mpol_put(vma_policy(new)); -@@ -2649,7 +2649,6 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, +@@ -2874,7 +2874,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; - struct file *file; ++ struct file *file, *prfile; - pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. " - "See Documentation/vm/remap_file_pages.txt.\n", -@@ -2693,10 +2692,10 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, - munlock_vma_pages_range(vma, start, start + size); + pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/vm/remap_file_pages.rst.\n", + current->comm, current->pid); +@@ -2949,10 +2949,27 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, + } } - file = get_file(vma->vm_file); + vma_get_file(vma); ++ file = vma->vm_file; ++ prfile = vma->vm_prfile; ret = do_mmap_pgoff(vma->vm_file, start, size, - prot, flags, pgoff, &populate); -- fput(file); -+ vma_fput(vma); + prot, flags, pgoff, &populate, NULL); ++ if (!IS_ERR_VALUE(ret) && file && prfile) { ++ struct vm_area_struct *new_vma; ++ ++ new_vma = find_vma(mm, ret); ++ if (!new_vma->vm_prfile) ++ new_vma->vm_prfile = prfile; ++ if (new_vma != vma) ++ get_file(prfile); ++ } ++ /* ++ * two fput()s instead of vma_fput(vma), ++ * coz vma may not be available anymore. ++ */ + fput(file); ++ if (prfile) ++ fput(prfile); out: up_write(&mm->mmap_sem); if (populate) -@@ -2966,7 +2965,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, +@@ -3258,7 +3275,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) @@ -464,10 +605,10 @@ index 2ce04a6..f555c0a 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 92be862..29179f7 100644 +index 749276b..d56f8f2 100644 --- a/mm/nommu.c +++ b/mm/nommu.c -@@ -671,7 +671,7 @@ static void __put_nommu_region(struct vm_region *region) +@@ -625,7 +625,7 @@ static void __put_nommu_region(struct vm_region *region) up_write(&nommu_region_sem); if (region->vm_file) @@ -476,16 +617,16 @@ index 92be862..29179f7 100644 /* IO memory and memory shared directly out of the pagecache * from ramfs/tmpfs mustn't be released here */ -@@ -829,7 +829,7 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma) +@@ -763,7 +763,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) - fput(vma->vm_file); + vma_fput(vma); put_nommu_region(vma->vm_region); - kmem_cache_free(vm_area_cachep, vma); + vm_area_free(vma); } -@@ -1355,7 +1355,7 @@ unsigned long do_mmap(struct file *file, +@@ -1286,7 +1286,7 @@ unsigned long do_mmap(struct file *file, goto error_just_free; } } @@ -494,7 +635,7 @@ index 92be862..29179f7 100644 kmem_cache_free(vm_region_jar, region); region = pregion; result = start; -@@ -1430,10 +1430,10 @@ error_just_free: +@@ -1361,7 +1361,7 @@ unsigned long do_mmap(struct file *file, up_write(&nommu_region_sem); error: if (region->vm_file) @@ -502,25 +643,22 @@ index 92be862..29179f7 100644 + vmr_fput(region); kmem_cache_free(vm_region_jar, region); if (vma->vm_file) -- fput(vma->vm_file); -+ vma_fput(vma); - kmem_cache_free(vm_area_cachep, vma); - return ret; - + fput(vma->vm_file); diff --git a/mm/prfile.c b/mm/prfile.c new file mode 100644 -index 0000000..b323b8a +index 0000000..a27ac36 --- /dev/null +++ b/mm/prfile.c @@ -0,0 +1,86 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * 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 + */ + @@ -534,8 +672,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 +} + @@ -599,57 +736,98 @@ index 0000000..b323b8a + fput(pr); +} +#endif /* !CONFIG_MMU */ +SPDX-License-Identifier: GPL-2.0 aufs4.x-rcN standalone patch diff --git a/fs/dcache.c b/fs/dcache.c -index 8aa7f26..f997345 100644 +index 6369b30..df4a5fe 100644 --- a/fs/dcache.c +++ b/fs/dcache.c -@@ -1272,6 +1272,7 @@ rename_retry: +@@ -1329,6 +1329,7 @@ void d_walk(struct dentry *parent, void *data, seq = 1; goto again; } -+EXPORT_SYMBOL(d_walk); ++EXPORT_SYMBOL_GPL(d_walk); - /* - * Search for at least 1 mount point in the dentry's subdirs. + struct check_mount { + struct vfsmount *mnt; +@@ -2817,6 +2818,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 b06623a..b9206c5 100644 +index fc281b7..65eaaca 100644 --- a/fs/exec.c +++ b/fs/exec.c -@@ -103,6 +103,7 @@ bool path_noexec(const struct path *path) +@@ -109,6 +109,7 @@ bool path_noexec(const struct path *path) return (path->mnt->mnt_flags & MNT_NOEXEC) || (path->mnt->mnt_sb->s_iflags & SB_I_NOEXEC); } -+EXPORT_SYMBOL(path_noexec); ++EXPORT_SYMBOL_GPL(path_noexec); #ifdef CONFIG_USELIB /* +diff --git a/fs/fcntl.c b/fs/fcntl.c +index 78234ee..2072f69 100644 +--- a/fs/fcntl.c ++++ b/fs/fcntl.c +@@ -85,6 +85,7 @@ int setfl(int fd, struct file * filp, unsigned long arg) + out: + return error; + } ++EXPORT_SYMBOL_GPL(setfl); + + static void f_modown(struct file *filp, struct pid *pid, enum pid_type type, + int force) diff --git a/fs/file_table.c b/fs/file_table.c -index ad17e05..df66450 100644 +index e49af4c..569020f 100644 --- a/fs/file_table.c +++ b/fs/file_table.c -@@ -147,6 +147,7 @@ over: +@@ -161,6 +161,7 @@ struct file *alloc_empty_file(int flags, const struct cred *cred) } return ERR_PTR(-ENFILE); } -+EXPORT_SYMBOL(get_empty_filp); ++EXPORT_SYMBOL_GPL(alloc_empty_file); - /** - * alloc_file - allocate and initialize a 'struct file' -@@ -308,6 +309,7 @@ void put_filp(struct file *file) - file_free(file); - } + /* + * Variant of alloc_empty_file() that doesn't check and modify nr_files. +@@ -323,6 +324,7 @@ void flush_delayed_fput(void) + { + delayed_fput(NULL); } -+EXPORT_SYMBOL(put_filp); ++EXPORT_SYMBOL_GPL(flush_delayed_fput); + + static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput); + +@@ -365,6 +367,7 @@ void __fput_sync(struct file *file) + } + + EXPORT_SYMBOL(fput); ++EXPORT_SYMBOL_GPL(__fput_sync); void __init files_init(void) - { + { +diff --git a/fs/inode.c b/fs/inode.c +index d2395eb..b8be7be 100644 +--- a/fs/inode.c ++++ b/fs/inode.c +@@ -1669,6 +1669,7 @@ int update_time(struct inode *inode, struct timespec64 *time, int flags) + + return update_time(inode, time, flags); + } ++EXPORT_SYMBOL_GPL(update_time); + + /** + * touch_atime - update the access time diff --git a/fs/namespace.c b/fs/namespace.c -index 0570729..ec560d8 100644 +index 46ed643..44502c2 100644 --- a/fs/namespace.c +++ b/fs/namespace.c -@@ -463,6 +463,7 @@ void __mnt_drop_write(struct vfsmount *mnt) +@@ -437,6 +437,7 @@ void __mnt_drop_write(struct vfsmount *mnt) mnt_dec_writers(real_mount(mnt)); preempt_enable(); } @@ -657,182 +835,231 @@ index 0570729..ec560d8 100644 /** * mnt_drop_write - give up write access to a mount -@@ -1803,6 +1804,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, +@@ -775,6 +776,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 +@@ -1844,6 +1846,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, } return 0; } -+EXPORT_SYMBOL(iterate_mounts); ++EXPORT_SYMBOL_GPL(iterate_mounts); static void cleanup_group_ids(struct mount *mnt, struct mount *end) { diff --git a/fs/notify/group.c b/fs/notify/group.c -index d16b62c..06ca6bc 100644 +index c03b836..817f22c 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c -@@ -22,6 +22,7 @@ - #include +@@ -23,6 +23,7 @@ #include #include + #include +#include #include #include "fsnotify.h" -@@ -72,6 +73,7 @@ void fsnotify_get_group(struct fsnotify_group *group) +@@ -112,6 +113,7 @@ void fsnotify_get_group(struct fsnotify_group *group) { - atomic_inc(&group->refcnt); + refcount_inc(&group->refcnt); } -+EXPORT_SYMBOL(fsnotify_get_group); ++EXPORT_SYMBOL_GPL(fsnotify_get_group); /* * Drop a reference to a group. Free it if it's through. -@@ -81,6 +83,7 @@ void fsnotify_put_group(struct fsnotify_group *group) - if (atomic_dec_and_test(&group->refcnt)) +@@ -121,6 +123,7 @@ void fsnotify_put_group(struct fsnotify_group *group) + if (refcount_dec_and_test(&group->refcnt)) fsnotify_final_destroy_group(group); } -+EXPORT_SYMBOL(fsnotify_put_group); ++EXPORT_SYMBOL_GPL(fsnotify_put_group); /* * Create a new fsnotify_group and hold a reference for the group returned. -@@ -109,6 +112,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) +@@ -150,6 +153,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) return group; } -+EXPORT_SYMBOL(fsnotify_alloc_group); ++EXPORT_SYMBOL_GPL(fsnotify_alloc_group); int fsnotify_fasync(int fd, struct file *file, int on) { diff --git a/fs/notify/mark.c b/fs/notify/mark.c -index fc0df44..325b5c6 100644 +index d2dd16c..cf709b7 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c -@@ -109,6 +109,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) - mark->free_mark(mark); - } +@@ -289,6 +289,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) + queue_delayed_work(system_unbound_wq, &reaper_work, + FSNOTIFY_REAPER_DELAY); } -+EXPORT_SYMBOL(fsnotify_put_mark); ++EXPORT_SYMBOL_GPL(fsnotify_put_mark); - /* Calculate mask of events for a list of marks */ - u32 fsnotify_recalc_mask(struct hlist_head *head) -@@ -208,6 +209,7 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark, + /* + * Get mark reference when we found the mark via lockless traversal of object +@@ -443,6 +444,7 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark, mutex_unlock(&group->mark_mutex); fsnotify_free_mark(mark); } -+EXPORT_SYMBOL(fsnotify_destroy_mark); - - void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock) - { -@@ -392,6 +394,7 @@ err: ++EXPORT_SYMBOL_GPL(fsnotify_destroy_mark); + /* + * Sorting function for lists of fsnotify marks. +@@ -658,6 +660,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, fsnotify_connp_t *connp, + mutex_unlock(&group->mark_mutex); return ret; } -+EXPORT_SYMBOL(fsnotify_add_mark); ++EXPORT_SYMBOL_GPL(fsnotify_add_mark); - int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, - struct inode *inode, struct vfsmount *mnt, int allow_dups) -@@ -492,6 +495,7 @@ void fsnotify_init_mark(struct fsnotify_mark *mark, - atomic_set(&mark->refcnt, 1); - mark->free_mark = free_mark; + /* + * Given a list of marks, find the mark associated with given group. If found +@@ -781,6 +784,7 @@ void fsnotify_init_mark(struct fsnotify_mark *mark, + fsnotify_get_group(group); + mark->group = group; } -+EXPORT_SYMBOL(fsnotify_init_mark); ++EXPORT_SYMBOL_GPL(fsnotify_init_mark); - static int fsnotify_mark_destroy(void *ignored) - { + /* + * Destroy all marks in destroy_list, waits for SRCU period to finish before diff --git a/fs/open.c b/fs/open.c -index b6f1e96..4ab0d4e 100644 +index 0285ce7..cb81623 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, - mutex_unlock(&dentry->d_inode->i_mutex); + inode_unlock(dentry->d_inode); return ret; } -+EXPORT_SYMBOL(do_truncate); ++EXPORT_SYMBOL_GPL(do_truncate); - long vfs_truncate(struct path *path, loff_t length) + long vfs_truncate(const struct path *path, loff_t length) { -@@ -678,6 +679,7 @@ int open_check_o_direct(struct file *f) - } - return 0; - } -+EXPORT_SYMBOL(open_check_o_direct); - - static int do_dentry_open(struct file *f, - struct inode *inode, diff --git a/fs/read_write.c b/fs/read_write.c -index fd0414e..8ace6ec 100644 +index a2a55ea..a1366ed 100644 --- a/fs/read_write.c +++ b/fs/read_write.c -@@ -504,6 +504,7 @@ vfs_readf_t vfs_readf(struct file *file) +@@ -459,6 +459,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) + + return ret; + } ++EXPORT_SYMBOL_GPL(vfs_read); + + static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) + { +@@ -499,6 +500,7 @@ vfs_readf_t vfs_readf(struct file *file) return new_sync_read; return ERR_PTR(-ENOSYS); } -+EXPORT_SYMBOL(vfs_readf); ++EXPORT_SYMBOL_GPL(vfs_readf); vfs_writef_t vfs_writef(struct file *file) { -@@ -515,6 +516,7 @@ vfs_writef_t vfs_writef(struct file *file) +@@ -510,6 +512,7 @@ vfs_writef_t vfs_writef(struct file *file) return new_sync_write; return ERR_PTR(-ENOSYS); } -+EXPORT_SYMBOL(vfs_writef); ++EXPORT_SYMBOL_GPL(vfs_writef); - ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) + ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos) + { +@@ -579,6 +582,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ + + return ret; + } ++EXPORT_SYMBOL_GPL(vfs_write); + + static inline loff_t file_pos_read(struct file *file) { diff --git a/fs/splice.c b/fs/splice.c -index 30a091d..c37c311 100644 +index 5dcf77b..63fe265 100644 --- a/fs/splice.c +++ b/fs/splice.c -@@ -1123,6 +1123,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +@@ -850,6 +850,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out, return splice_write(pipe, out, ppos, len, flags); } -+EXPORT_SYMBOL(do_splice_from); ++EXPORT_SYMBOL_GPL(do_splice_from); /* * Attempt to initiate a splice from a file to a pipe. -@@ -1149,6 +1150,7 @@ long do_splice_to(struct file *in, loff_t *ppos, +@@ -879,6 +880,7 @@ long do_splice_to(struct file *in, loff_t *ppos, return splice_read(in, ppos, pipe, len, flags); } -+EXPORT_SYMBOL(do_splice_to); ++EXPORT_SYMBOL_GPL(do_splice_to); /** * splice_direct_to_actor - splices data directly between two non-pipes +diff --git a/fs/sync.c b/fs/sync.c +index 2860782..ffd7ea4 100644 +--- a/fs/sync.c ++++ b/fs/sync.c +@@ -39,6 +39,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 9b932b9..44c457a 100644 +index 0d6a6a4..7ce4701 100644 --- a/fs/xattr.c +++ b/fs/xattr.c -@@ -207,6 +207,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, +@@ -295,6 +295,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, *xattr_value = value; return error; } -+EXPORT_SYMBOL(vfs_getxattr_alloc); ++EXPORT_SYMBOL_GPL(vfs_getxattr_alloc); - /* Compare an extended attribute value with the given value */ - int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name, + ssize_t + __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name, +diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c +index 447bc0b..4e7581c 100644 +--- a/kernel/locking/lockdep.c ++++ b/kernel/locking/lockdep.c +@@ -151,6 +151,7 @@ inline struct lock_class *lockdep_hlock_class(struct held_lock *hlock) + } + return lock_classes + hlock->class_idx - 1; + } ++EXPORT_SYMBOL_GPL(lockdep_hlock_class); + #define hlock_class(hlock) lockdep_hlock_class(hlock) + + #ifdef CONFIG_LOCK_STAT +diff --git a/kernel/task_work.c b/kernel/task_work.c +index 0fef395..83fb1ec 100644 +--- a/kernel/task_work.c ++++ b/kernel/task_work.c +@@ -116,3 +116,4 @@ void task_work_run(void) + } while (work); + } + } ++EXPORT_SYMBOL_GPL(task_work_run); diff --git a/security/commoncap.c b/security/commoncap.c -index 1832cf7..987ff5f 100644 +index 18a4fdf..e49f723 100644 --- a/security/commoncap.c +++ b/security/commoncap.c -@@ -1053,12 +1053,14 @@ int cap_mmap_addr(unsigned long addr) +@@ -1333,12 +1333,14 @@ int cap_mmap_addr(unsigned long addr) } return ret; } -+EXPORT_SYMBOL(cap_mmap_addr); ++EXPORT_SYMBOL_GPL(cap_mmap_addr); int cap_mmap_file(struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags) { return 0; } -+EXPORT_SYMBOL(cap_mmap_file); ++EXPORT_SYMBOL_GPL(cap_mmap_file); #ifdef CONFIG_SECURITY diff --git a/security/device_cgroup.c b/security/device_cgroup.c -index 03c1652..b00aa76 100644 +index cd97929..424fd23 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c -@@ -7,6 +7,7 @@ +@@ -8,6 +8,7 @@ #include #include #include @@ -840,102 +1067,99 @@ index 03c1652..b00aa76 100644 #include #include #include -@@ -849,6 +850,7 @@ int __devcgroup_inode_permission(struct inode *inode, int mask) - return __devcgroup_check_permission(type, imajor(inode), iminor(inode), - access); - } -+EXPORT_SYMBOL(__devcgroup_inode_permission); +@@ -824,3 +825,4 @@ int __devcgroup_check_permission(short type, u32 major, u32 minor, - int devcgroup_inode_mknod(int mode, dev_t dev) - { + return 0; + } ++EXPORT_SYMBOL_GPL(__devcgroup_check_permission); diff --git a/security/security.c b/security/security.c -index 46f405c..54488b0 100644 +index 04d173e..470af62 100644 --- a/security/security.c +++ b/security/security.c -@@ -433,6 +433,7 @@ int security_path_rmdir(struct path *dir, struct dentry *dentry) +@@ -553,6 +553,7 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry) return 0; return call_int_hook(path_rmdir, 0, dir, dentry); } -+EXPORT_SYMBOL(security_path_rmdir); ++EXPORT_SYMBOL_GPL(security_path_rmdir); - int security_path_unlink(struct path *dir, struct dentry *dentry) + int security_path_unlink(const struct path *dir, struct dentry *dentry) { -@@ -449,6 +450,7 @@ int security_path_symlink(struct path *dir, struct dentry *dentry, +@@ -569,6 +570,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); } -+EXPORT_SYMBOL(security_path_symlink); ++EXPORT_SYMBOL_GPL(security_path_symlink); - int security_path_link(struct dentry *old_dentry, struct path *new_dir, + int security_path_link(struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) -@@ -457,6 +459,7 @@ int security_path_link(struct dentry *old_dentry, struct path *new_dir, +@@ -577,6 +579,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); } -+EXPORT_SYMBOL(security_path_link); ++EXPORT_SYMBOL_GPL(security_path_link); - int security_path_rename(struct path *old_dir, struct dentry *old_dentry, - struct path *new_dir, struct dentry *new_dentry, -@@ -484,6 +487,7 @@ int security_path_truncate(struct path *path) + int security_path_rename(const struct path *old_dir, struct dentry *old_dentry, + const struct path *new_dir, struct dentry *new_dentry, +@@ -604,6 +607,7 @@ int security_path_truncate(const struct path *path) return 0; return call_int_hook(path_truncate, 0, path); } -+EXPORT_SYMBOL(security_path_truncate); ++EXPORT_SYMBOL_GPL(security_path_truncate); - int security_path_chmod(struct path *path, umode_t mode) + int security_path_chmod(const struct path *path, umode_t mode) { -@@ -491,6 +495,7 @@ int security_path_chmod(struct path *path, umode_t mode) +@@ -611,6 +615,7 @@ int security_path_chmod(const struct path *path, umode_t mode) return 0; return call_int_hook(path_chmod, 0, path, mode); } -+EXPORT_SYMBOL(security_path_chmod); ++EXPORT_SYMBOL_GPL(security_path_chmod); - int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) + int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) { -@@ -498,6 +503,7 @@ int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) +@@ -618,6 +623,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); } -+EXPORT_SYMBOL(security_path_chown); ++EXPORT_SYMBOL_GPL(security_path_chown); - int security_path_chroot(struct path *path) + int security_path_chroot(const struct path *path) { -@@ -583,6 +589,7 @@ int security_inode_readlink(struct dentry *dentry) +@@ -703,6 +709,7 @@ int security_inode_readlink(struct dentry *dentry) return 0; return call_int_hook(inode_readlink, 0, dentry); } -+EXPORT_SYMBOL(security_inode_readlink); ++EXPORT_SYMBOL_GPL(security_inode_readlink); int security_inode_follow_link(struct dentry *dentry, struct inode *inode, bool rcu) -@@ -598,6 +605,7 @@ int security_inode_permission(struct inode *inode, int mask) +@@ -718,6 +725,7 @@ int security_inode_permission(struct inode *inode, int mask) return 0; return call_int_hook(inode_permission, 0, inode, mask); } -+EXPORT_SYMBOL(security_inode_permission); ++EXPORT_SYMBOL_GPL(security_inode_permission); int security_inode_setattr(struct dentry *dentry, struct iattr *attr) { -@@ -736,6 +744,7 @@ int security_file_permission(struct file *file, int mask) +@@ -889,6 +897,7 @@ int security_file_permission(struct file *file, int mask) return fsnotify_perm(file, mask); } -+EXPORT_SYMBOL(security_file_permission); ++EXPORT_SYMBOL_GPL(security_file_permission); int security_file_alloc(struct file *file) { -@@ -795,6 +804,7 @@ int security_mmap_file(struct file *file, unsigned long prot, +@@ -948,6 +957,7 @@ int security_mmap_file(struct file *file, unsigned long prot, return ret; return ima_file_mmap(file, prot); } -+EXPORT_SYMBOL(security_mmap_file); ++EXPORT_SYMBOL_GPL(security_mmap_file); int security_mmap_addr(unsigned long addr) { 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-01-13 20:11:11.663093444 +0100 -@@ -0,0 +1,50 @@ ++++ linux/Documentation/ABI/testing/debugfs-aufs 2018-08-12 23:43:05.450124426 +0200 +@@ -0,0 +1,55 @@ +What: /debug/aufs/si_/ +Date: March 2009 +Contact: J. R. Okajima @@ -966,15 +1190,20 @@ diff -urN /usr/share/empty/Documentation/ABI/testing/debugfs-aufs linux/Document + When the aufs mount option 'noxino' is specified, it + will be empty. About XINO files, see the aufs manual. + -+What: /debug/aufs/si_/xino0, xino1 ... xinoN ++What: /debug/aufs/si_/xi0, xi1 ... xiN and xiN-N +Date: March 2009 +Contact: J. R. Okajima +Description: + It shows the consumed blocks by xino (External Inode Number + Translation Table), its link count, block size and file + size. -+ When the aufs mount option 'noxino' is specified, it -+ will be empty. About XINO files, see the aufs manual. ++ Due to the file size limit, there may exist multiple ++ xino files per branch. In this case, "-N" is added to ++ the filename and it corresponds to the index of the ++ internal xino array. "-0" is omitted. ++ When the aufs mount option 'noxino' is specified, Those ++ entries won't exist. About XINO files, see the aufs ++ manual. + +What: /debug/aufs/si_/xigen +Date: March 2009 @@ -988,7 +1217,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-01-13 20:11:11.663093444 +0100 ++++ 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 @@ -1023,10 +1252,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-01-13 20:11:11.663093444 +0100 -@@ -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-2015 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 @@ -1044,12 +1273,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 @@ -1197,10 +1427,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-01-13 20:11:11.663093444 +0100 ++++ linux/Documentation/filesystems/aufs/design/02struct.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,258 @@ + -+# Copyright (C) 2005-2015 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 @@ -1459,10 +1689,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-01-13 20:11:11.663093444 +0100 ++++ linux/Documentation/filesystems/aufs/design/03atomic_open.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,85 @@ + -+# Copyright (C) 2015 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 @@ -1548,10 +1778,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-01-13 20:11:11.663093444 +0100 ++++ linux/Documentation/filesystems/aufs/design/03lookup.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,113 @@ + -+# Copyright (C) 2005-2015 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 @@ -1665,10 +1895,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-01-13 20:11:11.663093444 +0100 ++++ linux/Documentation/filesystems/aufs/design/04branch.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,74 @@ + -+# Copyright (C) 2005-2015 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 @@ -1743,10 +1973,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-01-13 20:11:11.663093444 +0100 ++++ linux/Documentation/filesystems/aufs/design/05wbr_policy.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,64 @@ + -+# Copyright (C) 2005-2015 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 @@ -1809,12 +2039,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-01-13 20:11:11.663093444 +0100 ++++ linux/Documentation/filesystems/aufs/design/06fhsm.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,120 @@ + -+# Copyright (C) 2011-2015 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 @@ -1935,10 +2306,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-01-13 20:11:11.663093444 +0100 ++++ linux/Documentation/filesystems/aufs/design/06mmap.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,72 @@ + -+# Copyright (C) 2005-2015 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 @@ -2011,10 +2382,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-01-13 20:11:11.663093444 +0100 ++++ linux/Documentation/filesystems/aufs/design/06xattr.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,96 @@ + -+# Copyright (C) 2014-2015 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 @@ -2111,10 +2482,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-01-13 20:11:11.663093444 +0100 ++++ linux/Documentation/filesystems/aufs/design/07export.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,58 @@ + -+# Copyright (C) 2005-2015 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 @@ -2173,10 +2544,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-01-13 20:11:11.663093444 +0100 ++++ linux/Documentation/filesystems/aufs/design/08shwh.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,52 @@ + -+# Copyright (C) 2005-2015 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 @@ -2229,10 +2600,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-01-13 20:11:11.663093444 +0100 ++++ linux/Documentation/filesystems/aufs/design/10dynop.txt 2018-04-15 08:49:13.394483860 +0200 @@ -0,0 +1,47 @@ + -+# Copyright (C) 2010-2015 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 @@ -2280,8 +2651,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-01-13 20:11:11.663093444 +0100 -@@ -0,0 +1,390 @@ ++++ linux/Documentation/filesystems/aufs/README 2018-12-27 13:19:17.705082621 +0100 +@@ -0,0 +1,395 @@ + +Aufs4 -- advanced multi layered unification filesystem version 4.x +http://aufs.sf.net @@ -2655,6 +3026,11 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta +James B made a donation (2014/7 and 2015/7). +Stefano Di Biase made a donation (2014/8). +Daniel Epellei made a donation (2015/1). ++OmegaPhil made a donation (2016/1, 2018/4). ++Tomasz Szewczyk made a donation (2016/4). ++James Burry made a donation (2016/12). ++Carsten Rose made a donation (2018/9). ++Porteus Kiosk made a donation (2018/10). + +Thank you very much. +Donations are always, including future donations, very important and @@ -2674,10 +3050,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,59 @@ ++++ linux/fs/aufs/aufs.h 2018-10-23 12:33:35.592708932 +0200 +@@ -0,0 +1,62 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -2718,15 +3095,17 @@ 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 "lcnt.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" @@ -2737,10 +3116,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,1407 @@ ++++ linux/fs/aufs/branch.c 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,1422 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -2774,12 +3154,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); ++ au_xino_put(br); + -+ if (br->br_xino.xi_file) -+ fput(br->br_xino.xi_file); -+ mutex_destroy(&br->br_xino.xi_nondir_mtx); -+ -+ AuDebugOn(atomic_read(&br->br_count)); ++ AuLCntZero(au_lcnt_read(&br->br_nfiles, /*do_rev*/0)); ++ au_lcnt_fin(&br->br_nfiles, /*do_sync*/0); ++ AuLCntZero(au_lcnt_read(&br->br_count, /*do_rev*/0)); ++ au_lcnt_fin(&br->br_count, /*do_sync*/0); + + wbr = br->br_wbr; + if (wbr) { @@ -2791,7 +3173,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); -+ kfree(br->br_fhsm); ++ au_kfree_try_rcu(br->br_fhsm); + } + + key = br->br_dykey; @@ -2802,11 +3184,16 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + break; + + /* recursive lock, s_umount of branch's */ ++ /* synchronize_rcu(); */ /* why? */ + lockdep_off(); + path_put(&br->br_path); + lockdep_on(); -+ kfree(wbr); -+ kfree(br); ++ au_kfree_rcu(wbr); ++ au_lcnt_wait_for_fin(&br->br_nfiles); ++ au_lcnt_wait_for_fin(&br->br_count); ++ /* I don't know why, but percpu_refcount requires this */ ++ /* synchronize_rcu(); */ ++ au_kfree_rcu(br); +} + +/* @@ -2819,7 +3206,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + + AuRwMustWriteLock(&sbinfo->si_rwsem); + -+ bmax = sbinfo->si_bend + 1; ++ bmax = sbinfo->si_bbot + 1; + br = sbinfo->si_branch; + while (bmax--) + au_br_do_free(*br++); @@ -2830,10 +3217,10 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + */ +int au_br_index(struct super_block *sb, aufs_bindex_t br_id) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) + if (au_sbr_id(sb, bindex) == br_id) + return bindex; + return -1; @@ -2870,14 +3257,15 @@ 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 = au_xino_alloc(/*nfile*/1); ++ if (unlikely(!add_branch->br_xino)) ++ goto out_br; + err = au_hnotify_init_br(add_branch, perm); + if (unlikely(err)) -+ goto out_br; ++ goto out_xino; + + if (au_br_writable(perm)) { + /* may be freed separately at changing the branch permission */ @@ -2893,22 +3281,26 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + goto out_wbr; + } + -+ err = au_sbr_realloc(au_sbi(sb), new_nbranch); ++ 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); ++ err = au_di_realloc(au_di(root), new_nbranch, /*may_shrink*/0); + if (!err) { + inode = d_inode(root); -+ err = au_ii_realloc(au_ii(inode), new_nbranch); ++ err = au_hinode_realloc(au_ii(inode), new_nbranch, ++ /*may_shrink*/0); + } + if (!err) + return add_branch; /* success */ + +out_wbr: -+ kfree(add_branch->br_wbr); ++ au_kfree_rcu(add_branch->br_wbr); +out_hnotify: + au_hnotify_fin_br(add_branch); ++out_xino: ++ au_xino_put(add_branch); +out_br: -+ kfree(add_branch); ++ au_kfree_rcu(add_branch); +out: + return ERR_PTR(err); +} @@ -2940,13 +3332,13 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) +{ + int err; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct dentry *root, *h_dentry; + struct inode *inode, *h_inode; + + root = sb->s_root; -+ bend = au_sbend(sb); -+ if (unlikely(bend >= 0 ++ bbot = au_sbbot(sb); ++ if (unlikely(bbot >= 0 + && au_find_dbindex(root, add->path.dentry) >= 0)) { + err = 1; + if (!remount) { @@ -2958,13 +3350,13 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + + err = -ENOSPC; /* -E2BIG; */ + if (unlikely(AUFS_BRANCH_MAX <= add->bindex -+ || AUFS_BRANCH_MAX - 1 <= bend)) { ++ || AUFS_BRANCH_MAX - 1 <= bbot)) { + pr_err("number of branches exceeded %s\n", add->pathname); + goto out; + } + + err = -EDOM; -+ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) { ++ if (unlikely(add->bindex < 0 || bbot + 1 < add->bindex)) { + pr_err("bad index %d\n", add->bindex); + goto out; + } @@ -2998,11 +3390,11 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + if (unlikely(err)) + goto out; + -+ if (bend < 0) ++ if (bbot < 0) + return 0; /* success */ + + err = -EINVAL; -+ for (bindex = 0; bindex <= bend; bindex++) ++ for (bindex = 0; bindex <= bbot; bindex++) + if (unlikely(test_overlap(sb, add->path.dentry, + au_h_dptr(root, bindex)))) { + pr_err("%s is overlapped\n", add->pathname); @@ -3036,7 +3428,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +{ + int err, old_perm; + aufs_bindex_t bindex; -+ struct mutex *h_mtx; ++ struct inode *h_inode; + struct au_wbr *wbr; + struct au_hinode *hdir; + struct dentry *h_dentry; @@ -3049,15 +3441,15 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + old_perm = br->br_perm; + br->br_perm = new_perm; + hdir = NULL; -+ h_mtx = NULL; ++ h_inode = NULL; + bindex = au_br_index(sb, br->br_id); + if (0 <= bindex) { + hdir = au_hi(d_inode(sb->s_root), bindex); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + } else { + h_dentry = au_br_dentry(br); -+ h_mtx = &d_inode(h_dentry)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_PARENT); ++ h_inode = d_inode(h_dentry); ++ inode_lock_nested(h_inode, AuLsc_I_PARENT); + } + if (!wbr) + err = au_wh_init(br, sb); @@ -3067,14 +3459,14 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + wbr_wh_write_unlock(wbr); + } + if (hdir) -+ au_hn_imtx_unlock(hdir); ++ au_hn_inode_unlock(hdir); + else -+ mutex_unlock(h_mtx); ++ inode_unlock(h_inode); + vfsub_mnt_drop_write(au_br_mnt(br)); + br->br_perm = old_perm; + + if (!err && wbr && !au_br_writable(new_perm)) { -+ kfree(wbr); ++ au_kfree_rcu(wbr); + br->br_wbr = NULL; + } + @@ -3117,18 +3509,24 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + struct au_opt_add *add) +{ + int err; ++ struct au_branch *brbase; ++ struct file *xf; + struct inode *h_inode; + + err = 0; -+ mutex_init(&br->br_xino.xi_nondir_mtx); + br->br_perm = add->perm; + br->br_path = add->path; /* set first, path_get() later */ + spin_lock_init(&br->br_dykey_lock); -+ atomic_set(&br->br_count, 0); -+ atomic_set(&br->br_xino_running, 0); ++ au_lcnt_init(&br->br_nfiles, /*release*/NULL); ++ au_lcnt_init(&br->br_count, /*release*/NULL); + 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)) @@ -3136,11 +3534,13 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + } + + if (au_opt_test(au_mntflags(sb), XINO)) { ++ brbase = au_sbr(sb, 0); ++ xf = au_xino_file(brbase->br_xino, /*idx*/-1); ++ AuDebugOn(!xf); + h_inode = d_inode(add->path.dentry); -+ err = au_xino_br(sb, br, h_inode->i_ino, -+ au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1); ++ err = au_xino_init_br(sb, br, h_inode->i_ino, &xf->f_path); + if (unlikely(err)) { -+ AuDebugOn(br->br_xino.xi_file); ++ AuDebugOn(au_xino_file(br->br_xino, /*idx*/-1)); + goto out_err; + } + } @@ -3156,7 +3556,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +} + +static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, -+ struct au_branch *br, aufs_bindex_t bend, ++ struct au_branch *br, aufs_bindex_t bbot, + aufs_bindex_t amount) +{ + struct au_branch **brp; @@ -3166,40 +3566,39 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + brp = sbinfo->si_branch + bindex; + memmove(brp + 1, brp, sizeof(*brp) * amount); + *brp = br; -+ sbinfo->si_bend++; -+ if (unlikely(bend < 0)) -+ sbinfo->si_bend = 0; ++ sbinfo->si_bbot++; ++ if (unlikely(bbot < 0)) ++ sbinfo->si_bbot = 0; +} + +static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, -+ aufs_bindex_t bend, aufs_bindex_t amount) ++ aufs_bindex_t bbot, aufs_bindex_t amount) +{ + struct au_hdentry *hdp; + + AuRwMustWriteLock(&dinfo->di_rwsem); + -+ hdp = dinfo->di_hdentry + bindex; ++ hdp = au_hdentry(dinfo, bindex); + memmove(hdp + 1, hdp, sizeof(*hdp) * amount); + au_h_dentry_init(hdp); -+ dinfo->di_bend++; -+ if (unlikely(bend < 0)) -+ dinfo->di_bstart = 0; ++ dinfo->di_bbot++; ++ if (unlikely(bbot < 0)) ++ dinfo->di_btop = 0; +} + +static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, -+ aufs_bindex_t bend, aufs_bindex_t amount) ++ aufs_bindex_t bbot, aufs_bindex_t amount) +{ + struct au_hinode *hip; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + -+ hip = iinfo->ii_hinode + bindex; ++ hip = au_hinode(iinfo, bindex); + memmove(hip + 1, hip, sizeof(*hip) * amount); -+ hip->hi_inode = NULL; -+ au_hn_init(hip); -+ iinfo->ii_bend++; -+ if (unlikely(bend < 0)) -+ iinfo->ii_bstart = 0; ++ au_hinode_init(hip); ++ iinfo->ii_bbot++; ++ if (unlikely(bbot < 0)) ++ iinfo->ii_btop = 0; +} + +static void au_br_do_add(struct super_block *sb, struct au_branch *br, @@ -3207,17 +3606,17 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +{ + struct dentry *root, *h_dentry; + struct inode *root_inode, *h_inode; -+ aufs_bindex_t bend, amount; ++ aufs_bindex_t bbot, amount; + + root = sb->s_root; + root_inode = d_inode(root); -+ bend = au_sbend(sb); -+ amount = bend + 1 - bindex; ++ bbot = au_sbbot(sb); ++ amount = bbot + 1 - bindex; + h_dentry = au_br_dentry(br); + au_sbilist_lock(); -+ au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); -+ au_br_do_add_hdp(au_di(root), bindex, bend, amount); -+ au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount); ++ au_br_do_add_brp(au_sbi(sb), bindex, br, bbot, amount); ++ au_br_do_add_hdp(au_di(root), bindex, bbot, amount); ++ au_br_do_add_hip(au_ii(root_inode), bindex, bbot, amount); + au_set_h_dptr(root, bindex, dget(h_dentry)); + h_inode = d_inode(h_dentry); + au_set_h_iptr(root_inode, bindex, au_igrab(h_inode), /*flags*/0); @@ -3227,7 +3626,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) +{ + int err; -+ aufs_bindex_t bend, add_bindex; ++ aufs_bindex_t bbot, add_bindex; + struct dentry *root, *h_dentry; + struct inode *root_inode; + struct au_branch *add_branch; @@ -3235,6 +3634,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + root = sb->s_root; + root_inode = d_inode(root); + IMustLock(root_inode); ++ IiMustWriteLock(root_inode); + err = test_add(sb, add, remount); + if (unlikely(err < 0)) + goto out; @@ -3243,8 +3643,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + goto out; /* success */ + } + -+ bend = au_sbend(sb); -+ add_branch = au_br_alloc(sb, bend + 2, add->perm); ++ bbot = au_sbbot(sb); ++ add_branch = au_br_alloc(sb, bbot + 2, add->perm); + err = PTR_ERR(add_branch); + if (IS_ERR(add_branch)) + goto out; @@ -3256,13 +3656,10 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + } + + add_bindex = add->bindex; -+ if (!remount) -+ au_br_do_add(sb, add_branch, add_bindex); -+ else { -+ sysaufs_brs_del(sb, add_bindex); -+ au_br_do_add(sb, add_branch, add_bindex); -+ sysaufs_brs_add(sb, add_bindex); -+ } ++ sysaufs_brs_del(sb, add_bindex); /* remove successors */ ++ au_br_do_add(sb, add_branch, add_bindex); ++ sysaufs_brs_add(sb, add_bindex); /* append successors */ ++ dbgaufs_brs_add(sb, add_bindex, /*topdown*/0); /* rename successors */ + + h_dentry = add->path.dentry; + if (!add_bindex) { @@ -3271,18 +3668,6 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + } else + au_add_nlink(root_inode, d_inode(h_dentry)); + -+ /* -+ * this test/set prevents aufs from handling unnecesary notify events -+ * of xino files, in case of re-adding a writable branch which was -+ * once detached from aufs. -+ */ -+ if (au_xino_brid(sb) < 0 -+ && au_br_writable(add_branch->br_perm) -+ && !au_test_fs_bad_xino(h_dentry->d_sb) -+ && add_branch->br_xino.xi_file -+ && add_branch->br_xino.xi_file->f_path.dentry->d_parent == h_dentry) -+ au_xino_brid_set(sb, add_branch->br_id); -+ +out: + return err; +} @@ -3295,14 +3680,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)) { @@ -3312,7 +3698,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; +} @@ -3320,7 +3706,10 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +static struct file **au_farray_alloc(struct super_block *sb, + unsigned long long *max) +{ -+ *max = atomic_long_read(&au_sbi(sb)->si_nfiles); ++ struct au_sbinfo *sbi; ++ ++ sbi = au_sbi(sb); ++ *max = au_lcnt_read(&sbi->si_nfiles, /*do_rev*/1); + return au_array_alloc(max, au_farray_cb, sb, /*arg*/NULL); +} + @@ -3346,16 +3735,16 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) + -+static int au_test_ibusy(struct inode *inode, aufs_bindex_t bstart, -+ aufs_bindex_t bend) ++static int au_test_ibusy(struct inode *inode, aufs_bindex_t btop, ++ aufs_bindex_t bbot) +{ -+ return (inode && !S_ISDIR(inode->i_mode)) || bstart == bend; ++ return (inode && !S_ISDIR(inode->i_mode)) || btop == bbot; +} + -+static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t bstart, -+ aufs_bindex_t bend) ++static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t btop, ++ aufs_bindex_t bbot) +{ -+ return au_test_ibusy(d_inode(dentry), bstart, bend); ++ return au_test_ibusy(d_inode(dentry), btop, bbot); +} + +/* @@ -3365,7 +3754,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + unsigned int sigen, const unsigned int verbose) +{ + int err, i, j, ndentry; -+ aufs_bindex_t bstart, bend; ++ aufs_bindex_t btop, bbot; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry *d; @@ -3405,12 +3794,12 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + } + + /* AuDbgDentry(d); */ -+ bstart = au_dbstart(d); -+ bend = au_dbend(d); -+ if (bstart <= bindex -+ && bindex <= bend ++ btop = au_dbtop(d); ++ bbot = au_dbbot(d); ++ if (btop <= bindex ++ && bindex <= bbot + && au_h_dptr(d, bindex) -+ && au_test_dbusy(d, bstart, bend)) { ++ && au_test_dbusy(d, btop, bbot)) { + err = -EBUSY; + AuVerbose(verbose, "busy %pd\n", d); + AuDbgDentry(d); @@ -3431,7 +3820,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + int err; + unsigned long long max, ull; + struct inode *i, **array; -+ aufs_bindex_t bstart, bend; ++ aufs_bindex_t btop, bbot; + + array = au_iarray_alloc(sb, &max); + err = PTR_ERR(array); @@ -3462,12 +3851,12 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + } + } + -+ bstart = au_ibstart(i); -+ bend = au_ibend(i); -+ if (bstart <= bindex -+ && bindex <= bend ++ btop = au_ibtop(i); ++ bbot = au_ibbot(i); ++ if (btop <= bindex ++ && bindex <= bbot + && au_h_iptr(i, bindex) -+ && au_test_ibusy(i, bstart, bend)) { ++ && au_test_ibusy(i, btop, bbot)) { + err = -EBUSY; + AuVerbose(verbose, "busy i%lu\n", i->i_ino); + AuDbgInode(i); @@ -3503,7 +3892,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +{ + int err; + unsigned char matched, root; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_fidir *fidir; + struct au_hfile *hfile; + @@ -3519,8 +3908,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + matched = 0; + fidir = au_fi(file)->fi_hdir; + AuDebugOn(!fidir); -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); bindex <= bend; bindex++) { ++ bbot = au_fbbot_dir(file); ++ for (bindex = au_fbtop(file); bindex <= bbot; bindex++) { + hfile = fidir->fd_hfile + bindex; + if (!hfile->hf_file) + continue; @@ -3542,7 +3931,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +{ + int err, idx; + unsigned long long ull, max; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + struct file *file, **array; + struct dentry *root; + struct au_hfile *hfile; @@ -3563,7 +3952,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + + /* AuDbg("%pD\n", file); */ + fi_read_lock(file); -+ bstart = au_fbstart(file); ++ btop = au_fbtop(file); + if (!d_is_dir(file->f_path.dentry)) { + hfile = &au_fi(file)->fi_htop; + if (hfile->hf_br->br_id == br_id) @@ -3583,10 +3972,10 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +} + +static void br_del_file(struct file **to_free, unsigned long long opened, -+ aufs_bindex_t br_id) ++ aufs_bindex_t br_id) +{ + unsigned long long ull; -+ aufs_bindex_t bindex, bstart, bend, bfound; ++ aufs_bindex_t bindex, btop, bbot, bfound; + struct file *file; + struct au_fidir *fidir; + struct au_hfile *hfile; @@ -3602,9 +3991,9 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + fidir = au_fi(file)->fi_hdir; + AuDebugOn(!fidir); + fi_write_lock(file); -+ bstart = au_fbstart(file); -+ bend = au_fbend_dir(file); -+ for (bindex = bstart; bindex <= bend; bindex++) { ++ btop = au_fbtop(file); ++ bbot = au_fbbot_dir(file); ++ for (bindex = btop; bindex <= bbot; bindex++) { + hfile = fidir->fd_hfile + bindex; + if (!hfile->hf_file) + continue; @@ -3616,10 +4005,10 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + } + AuDebugOn(bfound < 0); + au_set_h_fptr(file, bfound, NULL); -+ if (bfound == bstart) { -+ for (bstart++; bstart <= bend; bstart++) -+ if (au_hf_dir(file, bstart)) { -+ au_set_fbstart(file, bstart); ++ if (bfound == btop) { ++ for (btop++; btop <= bbot; btop++) ++ if (au_hf_dir(file, btop)) { ++ au_set_fbtop(file, btop); + break; + } + } @@ -3629,59 +4018,60 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + +static void au_br_do_del_brp(struct au_sbinfo *sbinfo, + const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) ++ const aufs_bindex_t bbot) +{ + struct au_branch **brp, **p; + + AuRwMustWriteLock(&sbinfo->si_rwsem); + + brp = sbinfo->si_branch + bindex; -+ if (bindex < bend) -+ memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex)); -+ sbinfo->si_branch[0 + bend] = NULL; -+ sbinfo->si_bend--; ++ if (bindex < bbot) ++ memmove(brp, brp + 1, sizeof(*brp) * (bbot - bindex)); ++ sbinfo->si_branch[0 + bbot] = NULL; ++ sbinfo->si_bbot--; + -+ p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, AuGFP_SBILIST); ++ p = au_krealloc(sbinfo->si_branch, sizeof(*p) * bbot, AuGFP_SBILIST, ++ /*may_shrink*/1); + if (p) + sbinfo->si_branch = p; + /* harmless error */ +} + +static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) ++ const aufs_bindex_t bbot) +{ + struct au_hdentry *hdp, *p; + + AuRwMustWriteLock(&dinfo->di_rwsem); + -+ hdp = dinfo->di_hdentry; -+ if (bindex < bend) -+ memmove(hdp + bindex, hdp + bindex + 1, -+ sizeof(*hdp) * (bend - bindex)); -+ hdp[0 + bend].hd_dentry = NULL; -+ dinfo->di_bend--; ++ hdp = au_hdentry(dinfo, bindex); ++ if (bindex < bbot) ++ memmove(hdp, hdp + 1, sizeof(*hdp) * (bbot - bindex)); ++ /* au_h_dentry_init(au_hdentry(dinfo, bbot); */ ++ dinfo->di_bbot--; + -+ p = krealloc(hdp, sizeof(*p) * bend, AuGFP_SBILIST); ++ p = au_krealloc(dinfo->di_hdentry, sizeof(*p) * bbot, AuGFP_SBILIST, ++ /*may_shrink*/1); + if (p) + dinfo->di_hdentry = p; + /* harmless error */ +} + +static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) ++ const aufs_bindex_t bbot) +{ + struct au_hinode *hip, *p; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + -+ hip = iinfo->ii_hinode + bindex; -+ if (bindex < bend) -+ memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex)); -+ iinfo->ii_hinode[0 + bend].hi_inode = NULL; -+ au_hn_init(iinfo->ii_hinode + bend); -+ iinfo->ii_bend--; ++ hip = au_hinode(iinfo, bindex); ++ if (bindex < bbot) ++ memmove(hip, hip + 1, sizeof(*hip) * (bbot - bindex)); ++ /* au_hinode_init(au_hinode(iinfo, bbot)); */ ++ iinfo->ii_bbot--; + -+ p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, AuGFP_SBILIST); ++ p = au_krealloc(iinfo->ii_hinode, sizeof(*p) * bbot, AuGFP_SBILIST, ++ /*may_shrink*/1); + if (p) + iinfo->ii_hinode = p; + /* harmless error */ @@ -3690,7 +4080,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, + struct au_branch *br) +{ -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + struct au_sbinfo *sbinfo; + struct dentry *root, *h_root; + struct inode *inode, *h_inode; @@ -3701,7 +4091,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + root = sb->s_root; + inode = d_inode(root); + sbinfo = au_sbi(sb); -+ bend = sbinfo->si_bend; ++ bbot = sbinfo->si_bbot; + + h_root = au_h_dptr(root, bindex); + hinode = au_hi(inode, bindex); @@ -3709,11 +4099,14 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + au_hiput(hinode); + + au_sbilist_lock(); -+ au_br_do_del_brp(sbinfo, bindex, bend); -+ au_br_do_del_hdp(au_di(root), bindex, bend); -+ au_br_do_del_hip(au_ii(inode), bindex, bend); ++ au_br_do_del_brp(sbinfo, bindex, bbot); ++ au_br_do_del_hdp(au_di(root), bindex, bbot); ++ 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); @@ -3730,7 +4123,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + int err, rerr, i; + unsigned long long opened; + unsigned int mnt_flags; -+ aufs_bindex_t bindex, bend, br_id; ++ aufs_bindex_t bindex, bbot, br_id; + unsigned char do_wh, verbose; + struct au_branch *br; + struct au_wbr *wbr; @@ -3754,16 +4147,21 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + err = -EBUSY; + mnt_flags = au_mntflags(sb); + verbose = !!au_opt_test(mnt_flags, VERBOSE); -+ bend = au_sbend(sb); -+ if (unlikely(!bend)) { ++ bbot = au_sbbot(sb); ++ if (unlikely(!bbot)) { + AuVerbose(verbose, "no more branches left\n"); + goto out; + } ++ + br = au_sbr(sb, bindex); + AuDebugOn(!path_equal(&br->br_path, &del->h_path)); ++ if (unlikely(au_lcnt_read(&br->br_count, /*do_rev*/1))) { ++ AuVerbose(verbose, "br %pd2 is busy now\n", del->h_path.dentry); ++ goto out; ++ } + + br_id = br->br_id; -+ opened = atomic_read(&br->br_count); ++ opened = au_lcnt_read(&br->br_nfiles, /*do_rev*/1); + if (unlikely(opened)) { + to_free = au_array_alloc(&opened, empty_cb, sb, NULL); + err = PTR_ERR(to_free); @@ -3806,13 +4204,11 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + di_write_lock_child(root); + } + -+ if (!remount) -+ au_br_do_del(sb, bindex, br); -+ else { -+ sysaufs_brs_del(sb, bindex); -+ au_br_do_del(sb, bindex, br); -+ sysaufs_brs_add(sb, bindex); -+ } ++ sysaufs_brs_del(sb, bindex); /* remove successors */ ++ dbgaufs_xino_del(br); /* remove one */ ++ au_br_do_del(sb, bindex, br); ++ sysaufs_brs_add(sb, bindex); /* append successors */ ++ dbgaufs_brs_add(sb, bindex, /*topdown*/1); /* rename successors */ + + if (!bindex) { + au_cpup_attr_all(d_inode(root), /*force*/1); @@ -3822,8 +4218,6 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + if (au_opt_test(mnt_flags, PLINK)) + au_plink_half_refresh(sb, br_id); + -+ if (au_xino_brid(sb) == br_id) -+ au_xino_brid_set(sb, -1); + goto out; /* success */ + +out_wh: @@ -3843,7 +4237,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c +static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) +{ + int err; -+ aufs_bindex_t bstart, bend; ++ aufs_bindex_t btop, bbot; + struct aufs_ibusy ibusy; + struct inode *inode, *h_inode; + @@ -3862,7 +4256,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + + err = -EINVAL; + si_read_lock(sb, AuLock_FLUSH); -+ if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbend(sb))) ++ if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbbot(sb))) + goto out_unlock; + + err = 0; @@ -3870,15 +4264,15 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + inode = ilookup(sb, ibusy.ino); + if (!inode + || inode->i_ino == AUFS_ROOT_INO -+ || is_bad_inode(inode)) ++ || au_is_bad_inode(inode)) + goto out_unlock; + + ii_read_lock_child(inode); -+ bstart = au_ibstart(inode); -+ bend = au_ibend(inode); -+ if (bstart <= ibusy.bindex && ibusy.bindex <= bend) { ++ btop = au_ibtop(inode); ++ bbot = au_ibbot(inode); ++ if (btop <= ibusy.bindex && ibusy.bindex <= bbot) { + h_inode = au_h_iptr(inode, ibusy.bindex); -+ if (h_inode && au_test_ibusy(inode, bstart, bend)) ++ if (h_inode && au_test_ibusy(inode, btop, bbot)) + ibusy.h_ino = h_inode->i_ino; + } + ii_read_unlock(inode); @@ -4099,7 +4493,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)) { -+ kfree(br->br_wbr); ++ au_kfree_rcu(br->br_wbr); + br->br_wbr = NULL; + } + } @@ -4111,7 +4505,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); -+ kfree(br->br_fhsm); ++ au_kfree_rcu(br->br_fhsm); + br->br_fhsm = NULL; + } + } else if (au_br_fhsm(mod->perm)) @@ -4123,7 +4517,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c + goto out; /* success */ + +out_bf: -+ kfree(bf); ++ au_kfree_try_rcu(bf); +out: + AuTraceErr(err); + return err; @@ -4148,10 +4542,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,279 @@ ++++ linux/fs/aufs/branch.h 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,365 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -4177,22 +4572,34 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h +#ifdef __KERNEL__ + +#include ++#include "dirren.h" +#include "dynop.h" ++#include "lcnt.h" +#include "rwsem.h" +#include "super.h" + +/* ---------------------------------------------------------------------- */ + +/* a xino file */ -+struct au_xino_file { -+ struct file *xi_file; -+ struct mutex xi_nondir_mtx; ++struct au_xino { ++ struct file **xi_file; ++ unsigned int xi_nfile; + -+ /* todo: make xino files an array to support huge inode number */ ++ struct { ++ spinlock_t spin; ++ ino_t *array; ++ int total; ++ /* reserved for future use */ ++ /* unsigned long *bitmap; */ ++ wait_queue_head_t wqh; ++ } xi_nondir; + -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *xi_dbgaufs; -+#endif ++ struct mutex xi_mtx; /* protects xi_file array */ ++ struct hlist_bl_head xi_writing; ++ ++ atomic_t xi_truncating; ++ ++ struct kref xi_kref; +}; + +/* File-based Hierarchical Storage Management */ @@ -4243,7 +4650,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h + +/* protected by superblock rwsem */ +struct au_branch { -+ struct au_xino_file br_xino; ++ struct au_xino *br_xino; + + aufs_bindex_t br_id; + @@ -4251,14 +4658,12 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h + struct path br_path; + spinlock_t br_dykey_lock; + struct au_dykey *br_dykey[AuBrDynOp]; -+ atomic_t br_count; ++ au_lcnt_t br_nfiles; /* opened files */ ++ au_lcnt_t br_count; /* in-use for other */ + + struct au_wbr *br_wbr; + struct au_br_fhsm *br_fhsm; + -+ /* xino truncation */ -+ atomic_t br_xino_running; -+ +#ifdef CONFIG_AUFS_HFSNOTIFY + struct au_br_hfsnotify *br_hfsn; +#endif @@ -4267,6 +4672,12 @@ 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 ++ ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *br_dbgaufs; /* xino */ ++#endif ++ ++ struct au_dr_br br_dirren; +}; + +/* ---------------------------------------------------------------------- */ @@ -4288,7 +4699,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h + +static inline int au_br_rdonly(struct au_branch *br) +{ -+ return ((au_br_sb(br)->s_flags & MS_RDONLY) ++ return (sb_rdonly(au_br_sb(br)) + || !au_br_writable(br->br_perm)) + ? -EROFS : 0; +} @@ -4314,6 +4725,28 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h + return err; +} + ++static inline void au_xino_get(struct au_branch *br) ++{ ++ struct au_xino *xi; ++ ++ xi = br->br_xino; ++ if (xi) ++ kref_get(&xi->xi_kref); ++} ++ ++static inline int au_xino_count(struct au_branch *br) ++{ ++ int v; ++ struct au_xino *xi; ++ ++ v = 0; ++ xi = br->br_xino; ++ if (xi) ++ v = kref_read(&xi->xi_kref); ++ ++ return v; ++} ++ +/* ---------------------------------------------------------------------- */ + +/* branch.c */ @@ -4337,31 +4770,74 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h +/* xino.c */ +static const loff_t au_loff_max = LLONG_MAX; + -+int au_xib_trunc(struct super_block *sb); ++aufs_bindex_t au_xi_root(struct super_block *sb, struct dentry *dentry); ++struct file *au_xino_create(struct super_block *sb, char *fpath, int silent); ++struct file *au_xino_create2(struct super_block *sb, struct path *base, ++ struct file *copy_src); ++struct au_xi_new { ++ struct au_xino *xi; /* switch between xino and xigen */ ++ int idx; ++ struct path *base; ++ struct file *copy_src; ++}; ++struct file *au_xi_new(struct super_block *sb, struct au_xi_new *xinew); ++ ++int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t *ino); ++int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t ino); +ssize_t xino_fread(vfs_readf_t func, struct file *file, void *buf, size_t size, + loff_t *pos); +ssize_t xino_fwrite(vfs_writef_t func, struct file *file, void *buf, + size_t size, loff_t *pos); -+struct file *au_xino_create2(struct file *base_file, struct file *copy_src); -+struct file *au_xino_create(struct super_block *sb, char *fname, int silent); -+ino_t au_xino_new_ino(struct super_block *sb); -+void au_xino_delete_inode(struct inode *inode, const int unlinked); -+int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t ino); -+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t *ino); -+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino, -+ struct file *base_file, int do_test); -+int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex); ++ ++int au_xib_trunc(struct super_block *sb); ++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex, int idx_begin); ++ ++struct au_xino *au_xino_alloc(unsigned int nfile); ++int au_xino_put(struct au_branch *br); ++struct file *au_xino_file1(struct au_xino *xi); + +struct au_opt_xino; -+int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount); +void au_xino_clr(struct super_block *sb); ++int au_xino_set(struct super_block *sb, struct au_opt_xino *xiopt, int remount); +struct file *au_xino_def(struct super_block *sb); ++int au_xino_init_br(struct super_block *sb, struct au_branch *br, ino_t hino, ++ struct path *base); ++ ++ino_t au_xino_new_ino(struct super_block *sb); ++void au_xino_delete_inode(struct inode *inode, const int unlinked); ++ ++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); ++ +int au_xino_path(struct seq_file *seq, struct file *file); + +/* ---------------------------------------------------------------------- */ + ++/* @idx is signed to accept -1 meaning the first file */ ++static inline struct file *au_xino_file(struct au_xino *xi, int idx) ++{ ++ struct file *file; ++ ++ file = NULL; ++ if (!xi) ++ goto out; ++ ++ if (idx >= 0) { ++ if (idx < xi->xi_nfile) ++ file = xi->xi_file[idx]; ++ } else ++ file = au_xino_file1(xi); ++ ++out: ++ return file; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ +/* Superblock to branch */ +static inline +aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) @@ -4381,11 +4857,6 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h + return au_br_sb(au_sbr(sb, bindex)); +} + -+static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ atomic_dec(&au_sbr(sb, bindex)->br_count); -+} -+ +static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_sbr(sb, bindex)->br_perm; @@ -4398,15 +4869,24 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h + +/* ---------------------------------------------------------------------- */ + ++#define wbr_wh_read_lock(wbr) au_rw_read_lock(&(wbr)->wbr_wh_rwsem) ++#define wbr_wh_write_lock(wbr) au_rw_write_lock(&(wbr)->wbr_wh_rwsem) ++#define wbr_wh_read_trylock(wbr) au_rw_read_trylock(&(wbr)->wbr_wh_rwsem) ++#define wbr_wh_write_trylock(wbr) au_rw_write_trylock(&(wbr)->wbr_wh_rwsem) +/* -+ * wbr_wh_read_lock, wbr_wh_write_lock -+ * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock -+ */ -+AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem); ++#define wbr_wh_read_trylock_nested(wbr) \ ++ au_rw_read_trylock_nested(&(wbr)->wbr_wh_rwsem) ++#define wbr_wh_write_trylock_nested(wbr) \ ++ au_rw_write_trylock_nested(&(wbr)->wbr_wh_rwsem) ++*/ + -+#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&wbr->wbr_wh_rwsem) -+#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&wbr->wbr_wh_rwsem) -+#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&wbr->wbr_wh_rwsem) ++#define wbr_wh_read_unlock(wbr) au_rw_read_unlock(&(wbr)->wbr_wh_rwsem) ++#define wbr_wh_write_unlock(wbr) au_rw_write_unlock(&(wbr)->wbr_wh_rwsem) ++#define wbr_wh_downgrade_lock(wbr) au_rw_dgrade_lock(&(wbr)->wbr_wh_rwsem) ++ ++#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&(wbr)->wbr_wh_rwsem) ++#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&(wbr)->wbr_wh_rwsem) ++#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&(wbr)->wbr_wh_rwsem) + +/* ---------------------------------------------------------------------- */ + @@ -4431,8 +4911,9 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,38 @@ ++++ linux/fs/aufs/conf.mk 2018-06-04 09:08:09.181412645 +0200 +@@ -0,0 +1,40 @@ ++# SPDX-License-Identifier: GPL-2.0 + +AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS} + @@ -4449,6 +4930,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 \ @@ -4473,10 +4955,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,1319 @@ ++++ linux/fs/aufs/cpup.c 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,1458 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -4498,6 +4981,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + +#include +#include ++#include +#include "aufs.h" + +void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) @@ -4516,7 +5000,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c +{ + struct inode *h_inode; + -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ h_inode = au_h_iptr(inode, au_ibtop(inode)); + fsstack_copy_attr_times(inode, h_inode); + fsstack_copy_inode_size(inode, h_inode); +} @@ -4525,10 +5009,10 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c +{ + struct inode *h_inode; + struct super_block *sb; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + + sb = inode->i_sb; -+ bindex = au_ibstart(inode); ++ bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (!force + && !S_ISDIR(h_inode->i_mode) @@ -4551,8 +5035,8 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + * it may includes whplink directory. + */ + if (S_ISDIR(h_inode->i_mode)) { -+ bend = au_ibend(inode); -+ for (bindex++; bindex <= bend; bindex++) { ++ bbot = au_ibbot(inode); ++ for (bindex++; bindex <= bbot; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (h_inode) + au_add_nlink(inode, h_inode); @@ -4564,7 +5048,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c +{ + struct inode *h_inode; + -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ h_inode = au_h_iptr(inode, au_ibtop(inode)); + inode->i_mode = h_inode->i_mode; + inode->i_uid = h_inode->i_uid; + inode->i_gid = h_inode->i_gid; @@ -4586,7 +5070,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c +{ + struct inode *h_inode; + -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ h_inode = au_h_iptr(inode, au_ibtop(inode)); + au_cpup_attr_changeable(inode); + if (inode->i_nlink > 0) + au_cpup_attr_nlink(inode, force); @@ -4714,7 +5198,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + size_t sz, rbytes, wbytes; + unsigned char all_zero; + char *p, *zp; -+ struct mutex *h_mtx; ++ struct inode *h_inode; + /* reduce stack usage */ + struct iattr *ia; + @@ -4794,12 +5278,12 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + ia->ia_size = dst->f_pos; + ia->ia_valid = ATTR_SIZE | ATTR_FILE; + ia->ia_file = dst; -+ h_mtx = &file_inode(dst)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); ++ h_inode = file_inode(dst); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD2); + /* no delegation since it is just created */ + err = vfsub_notify_change(&dst->f_path, ia, + /*delegated*/NULL); -+ mutex_unlock(h_mtx); ++ inode_unlock(h_inode); + } + } + @@ -4812,9 +5296,11 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + unsigned long blksize; + unsigned char do_kfree; + char *buf; ++ struct super_block *h_sb; + + err = -ENOMEM; -+ blksize = dst->f_path.dentry->d_sb->s_blocksize; ++ h_sb = file_inode(dst)->i_sb; ++ blksize = h_sb->s_blocksize; + if (!blksize || PAGE_SIZE < blksize) + blksize = PAGE_SIZE; + AuDbg("blksize %lu\n", blksize); @@ -4832,15 +5318,79 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + src->f_pos = 0; + dst->f_pos = 0; + err = au_do_copy_file(dst, src, len, buf, blksize); -+ if (do_kfree) -+ kfree(buf); -+ else ++ if (do_kfree) { ++ AuDebugOn(!au_kfree_do_sz_test(blksize)); ++ au_kfree_do_rcu(buf); ++ } else + free_page((unsigned long)buf); + +out: + return err; +} + ++static int au_do_copy(struct file *dst, struct file *src, loff_t len) ++{ ++ int err; ++ struct super_block *h_src_sb; ++ struct inode *h_src_inode; ++ ++ h_src_inode = file_inode(src); ++ h_src_sb = h_src_inode->i_sb; ++ ++ /* XFS acquires inode_lock */ ++ if (!au_test_xfs(h_src_sb)) ++ err = au_copy_file(dst, src, len); ++ else { ++ inode_unlock_shared(h_src_inode); ++ err = au_copy_file(dst, src, len); ++ inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); ++ } ++ ++ return err; ++} ++ ++static int au_clone_or_copy(struct file *dst, struct file *src, loff_t len) ++{ ++ int err; ++ loff_t lo; ++ struct super_block *h_src_sb; ++ struct inode *h_src_inode; ++ ++ h_src_inode = file_inode(src); ++ h_src_sb = h_src_inode->i_sb; ++ if (h_src_sb != file_inode(dst)->i_sb ++ || !dst->f_op->remap_file_range) { ++ err = au_do_copy(dst, src, len); ++ goto out; ++ } ++ ++ if (!au_test_nfs(h_src_sb)) { ++ inode_unlock_shared(h_src_inode); ++ lo = vfsub_clone_file_range(src, dst, len); ++ inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); ++ } else ++ lo = vfsub_clone_file_range(src, dst, len); ++ if (lo == len) { ++ err = 0; ++ goto out; /* success */ ++ } else if (lo >= 0) ++ /* todo: possible? */ ++ /* paritially succeeded */ ++ AuDbg("lo %lld, len %lld. Retrying.\n", lo, len); ++ else if (lo != -EOPNOTSUPP) { ++ /* older XFS has a condition in cloning */ ++ err = lo; ++ goto out; ++ } ++ ++ /* the backend fs on NFS may not support cloning */ ++ err = au_do_copy(dst, src, len); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ +/* + * to support a sparse file which is opened with O_APPEND, + * we need to close the file. @@ -4855,21 +5405,21 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + struct dentry *dentry; + int force_wr; + struct file *file; -+ void *label; + } *f, file[] = { + { + .bindex = cpg->bsrc, + .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, -+ .label = &&out + }, + { + .bindex = cpg->bdst, + .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, + .force_wr = !!au_ftest_cpup(cpg->flags, RWDST), -+ .label = &&out_src + } + }; -+ struct super_block *sb; ++ struct au_branch *br; ++ struct super_block *sb, *h_src_sb; ++ struct inode *h_src_inode; ++ struct task_struct *tsk = current; + + /* bsrc branch can be ro/rw. */ + sb = cpg->dentry->d_sb; @@ -4878,21 +5428,43 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + f->dentry = au_h_dptr(cpg->dentry, f->bindex); + f->file = au_h_open(cpg->dentry, f->bindex, f->flags, + /*file*/NULL, f->force_wr); -+ err = PTR_ERR(f->file); -+ if (IS_ERR(f->file)) -+ goto *f->label; ++ if (IS_ERR(f->file)) { ++ err = PTR_ERR(f->file); ++ if (i == SRC) ++ goto out; ++ else ++ goto out_src; ++ } + } + + /* try stopping to update while we copyup */ -+ IMustLock(d_inode(file[SRC].dentry)); -+ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len); -+ -+ fput(file[DST].file); -+ au_sbr_put(sb, file[DST].bindex); ++ h_src_inode = d_inode(file[SRC].dentry); ++ h_src_sb = h_src_inode->i_sb; ++ if (!au_test_nfs(h_src_sb)) ++ IMustLock(h_src_inode); ++ err = au_clone_or_copy(file[DST].file, file[SRC].file, cpg->len); ++ ++ /* i wonder if we had O_NO_DELAY_FPUT flag */ ++ if (tsk->flags & PF_KTHREAD) ++ __fput_sync(file[DST].file); ++ else { ++ /* it happened actually */ ++ fput(file[DST].file); ++ /* ++ * too bad. ++ * we have to call both since we don't know which place the file ++ * was added to. ++ */ ++ task_work_run(); ++ flush_delayed_fput(); ++ } ++ br = au_sbr(sb, file[DST].bindex); ++ au_lcnt_dec(&br->br_nfiles); + +out_src: + fput(file[SRC].file); -+ au_sbr_put(sb, file[SRC].bindex); ++ br = au_sbr(sb, file[SRC].bindex); ++ au_lcnt_dec(&br->br_nfiles); +out: + return err; +} @@ -4912,26 +5484,31 @@ 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 */ -+ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); ++ 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); + h_path.mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc); + h_src_attr->iflags = h_src_inode->i_flags; + if (!au_test_nfs(h_src_inode->i_sb)) -+ err = vfs_getattr(&h_path, &h_src_attr->st); ++ err = vfsub_getattr(&h_path, &h_src_attr->st); + else { -+ mutex_unlock(&h_src_inode->i_mutex); -+ err = vfs_getattr(&h_path, &h_src_attr->st); -+ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); ++ inode_unlock_shared(h_src_inode); ++ err = vfsub_getattr(&h_path, &h_src_attr->st); ++ inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); + } + if (unlikely(err)) { -+ mutex_unlock(&h_src_inode->i_mutex); ++ inode_unlock_shared(h_src_inode); + goto out; + } + h_src_attr->valid = 1; -+ err = au_cp_regular(cpg); -+ mutex_unlock(&h_src_inode->i_mutex); ++ if (!au_test_nfs(h_src_inode->i_sb)) { ++ err = au_cp_regular(cpg); ++ inode_unlock_shared(h_src_inode); ++ } else { ++ inode_unlock_shared(h_src_inode); ++ err = au_cp_regular(cpg); ++ } + rerr = au_pin_hdir_relock(cpg->pin); + if (!err && rerr) + err = rerr; @@ -4957,12 +5534,6 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + char *k; + char __user *u; + } sym; -+ struct inode *h_inode = d_inode(h_src); -+ const struct inode_operations *h_iop = h_inode->i_op; -+ -+ err = -ENOSYS; -+ if (unlikely(!h_iop->readlink)) -+ goto out; + + err = -ENOMEM; + sym.k = (void *)__get_free_page(GFP_NOFS); @@ -4972,7 +5543,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + /* unnecessary to support mmap_sem since symlink is not mmap-able */ + old_fs = get_fs(); + set_fs(KERNEL_DS); -+ symlen = h_iop->readlink(h_src, sym.u, PATH_MAX); ++ symlen = vfs_readlink(h_src, sym.u, PATH_MAX); + err = symlen; + set_fs(old_fs); + @@ -4986,6 +5557,57 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + return err; +} + ++/* ++ * regardless 'acl' option, reset all ACL. ++ * All ACL will be copied up later from the original entry on the lower branch. ++ */ ++static int au_reset_acl(struct inode *h_dir, struct path *h_path, umode_t mode) ++{ ++ int err; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ ++ h_dentry = h_path->dentry; ++ h_inode = d_inode(h_dentry); ++ /* forget_all_cached_acls(h_inode)); */ ++ err = vfsub_removexattr(h_dentry, XATTR_NAME_POSIX_ACL_ACCESS); ++ AuTraceErr(err); ++ if (err == -EOPNOTSUPP) ++ err = 0; ++ if (!err) ++ err = vfsub_acl_chmod(h_inode, mode); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_do_cpup_dir(struct au_cp_generic *cpg, struct dentry *dst_parent, ++ struct inode *h_dir, struct path *h_path) ++{ ++ int err; ++ struct inode *dir, *inode; ++ ++ err = vfsub_removexattr(h_path->dentry, XATTR_NAME_POSIX_ACL_DEFAULT); ++ AuTraceErr(err); ++ if (err == -EOPNOTSUPP) ++ err = 0; ++ if (unlikely(err)) ++ goto out; ++ ++ /* ++ * strange behaviour from the users view, ++ * particularly setattr case ++ */ ++ dir = d_inode(dst_parent); ++ if (au_ibtop(dir) == cpg->bdst) ++ au_cpup_attr_nlink(dir, /*force*/1); ++ inode = d_inode(cpg->dentry); ++ au_cpup_attr_nlink(inode, /*force*/1); ++ ++out: ++ return err; ++} ++ +static noinline_for_stack +int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent, + struct au_cpup_reg_attr *h_src_attr) @@ -4998,7 +5620,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + struct au_dtime dt; + struct path h_path; + struct dentry *h_src, *h_dst, *h_parent; -+ struct inode *h_inode, *h_dir, *dir, *inode; ++ struct inode *h_inode, *h_dir; + struct super_block *sb; + + /* bsrc branch can be ro/rw. */ @@ -5030,25 +5652,15 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + switch (mode & S_IFMT) { + case S_IFREG: + isreg = 1; -+ err = vfsub_create(h_dir, &h_path, mode | S_IWUSR, -+ /*want_excl*/true); ++ err = vfsub_create(h_dir, &h_path, 0600, /*want_excl*/true); + if (!err) + err = au_do_cpup_regular(cpg, h_src_attr); + break; + case S_IFDIR: + isdir = 1; + err = vfsub_mkdir(h_dir, &h_path, mode); -+ if (!err) { -+ /* -+ * strange behaviour from the users view, -+ * particularry setattr case -+ */ -+ dir = d_inode(dst_parent); -+ if (au_ibstart(dir) == cpg->bdst) -+ au_cpup_attr_nlink(dir, /*force*/1); -+ inode = d_inode(cpg->dentry); -+ au_cpup_attr_nlink(inode, /*force*/1); -+ } ++ if (!err) ++ err = au_do_cpup_dir(cpg, dst_parent, h_dir, &h_path); + break; + case S_IFLNK: + err = au_do_cpup_symlink(&h_path, h_src, h_dir); @@ -5065,6 +5677,8 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + AuIOErr("Unknown inode type 0%o\n", mode); + err = -EIO; + } ++ if (!err) ++ err = au_reset_acl(h_dir, &h_path, mode); + + mnt_flags = au_mntflags(sb); + if (!au_opt_test(mnt_flags, UDBA_NONE) @@ -5128,7 +5742,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: @@ -5141,17 +5756,19 @@ 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 unnecessary. + */ +static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) +{ + int err, rerr; -+ aufs_bindex_t old_ibstart; ++ aufs_bindex_t old_ibtop; + unsigned char isdir, plink; + struct dentry *h_src, *h_dst, *h_parent; + struct inode *dst_inode, *h_dir, *inode, *delegated, *src_inode; + struct super_block *sb; + struct au_branch *br; -+ /* to reuduce stack size */ ++ /* to reduce stack size */ + struct { + struct au_dtime dt; + struct path h_path; @@ -5233,12 +5850,12 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + } + + isdir = S_ISDIR(inode->i_mode); -+ old_ibstart = au_ibstart(inode); ++ old_ibtop = au_ibtop(inode); + err = cpup_entry(cpg, dst_parent, &a->h_src_attr); + if (unlikely(err)) + goto out_rev; + dst_inode = d_inode(h_dst); -+ mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); ++ inode_lock_nested(dst_inode, AuLsc_I_CHILD2); + /* todo: necessary? */ + /* au_pin_hdir_unlock(cpg->pin); */ + @@ -5246,29 +5863,29 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + if (unlikely(err)) { + /* todo: necessary? */ + /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */ -+ mutex_unlock(&dst_inode->i_mutex); ++ inode_unlock(dst_inode); + goto out_rev; + } + -+ if (cpg->bdst < old_ibstart) { ++ if (cpg->bdst < old_ibtop) { + if (S_ISREG(inode->i_mode)) { + err = au_dy_iaop(inode, cpg->bdst, dst_inode); + if (unlikely(err)) { + /* ignore an error */ + /* au_pin_hdir_relock(cpg->pin); */ -+ mutex_unlock(&dst_inode->i_mutex); ++ inode_unlock(dst_inode); + goto out_rev; + } + } -+ au_set_ibstart(inode, cpg->bdst); ++ au_set_ibtop(inode, cpg->bdst); + } else -+ au_set_ibend(inode, cpg->bdst); ++ au_set_ibbot(inode, cpg->bdst); + au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode), + au_hi_flags(inode, isdir)); + + /* todo: necessary? */ + /* err = au_pin_hdir_relock(cpg->pin); */ -+ mutex_unlock(&dst_inode->i_mutex); ++ inode_unlock(dst_inode); + if (unlikely(err)) + goto out_rev; + @@ -5307,7 +5924,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + } +out_parent: + dput(dst_parent); -+ kfree(a); ++ au_kfree_rcu(a); +out: + return err; +} @@ -5418,7 +6035,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + + /* revert */ + au_set_h_dptr(dentry, cpg->bdst, NULL); -+ au_set_dbstart(dentry, cpg->bsrc); ++ au_set_dbtop(dentry, cpg->bsrc); + } + + return err; @@ -5480,20 +6097,20 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + +int au_sio_cpup_simple(struct au_cp_generic *cpg) +{ -+ aufs_bindex_t bsrc, bend; ++ aufs_bindex_t bsrc, bbot; + struct dentry *dentry, *h_dentry; + + if (cpg->bsrc < 0) { + dentry = cpg->dentry; -+ bend = au_dbend(dentry); -+ for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) { ++ bbot = au_dbbot(dentry); ++ for (bsrc = cpg->bdst + 1; bsrc <= bbot; bsrc++) { + h_dentry = au_h_dptr(dentry, bsrc); + if (h_dentry) { + AuDebugOn(d_is_negative(h_dentry)); + break; + } + } -+ AuDebugOn(bsrc > bend); ++ AuDebugOn(bsrc > bbot); + cpg->bsrc = bsrc; + } + AuDebugOn(cpg->bsrc <= cpg->bdst); @@ -5517,23 +6134,27 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + int err; + unsigned int flags_orig; + aufs_bindex_t bsrc_orig; -+ struct dentry *h_d_dst, *h_d_start; + struct au_dinfo *dinfo; -+ struct au_hdentry *hdp; ++ struct { ++ struct au_hdentry *hd; ++ struct dentry *h_dentry; ++ } hdst, hsrc; + + dinfo = au_di(cpg->dentry); + AuRwMustWriteLock(&dinfo->di_rwsem); + + bsrc_orig = cpg->bsrc; -+ cpg->bsrc = dinfo->di_bstart; -+ hdp = dinfo->di_hdentry; -+ h_d_dst = hdp[0 + cpg->bdst].hd_dentry; -+ dinfo->di_bstart = cpg->bdst; -+ hdp[0 + cpg->bdst].hd_dentry = wh_dentry; -+ h_d_start = NULL; ++ cpg->bsrc = dinfo->di_btop; ++ hdst.hd = au_hdentry(dinfo, cpg->bdst); ++ hdst.h_dentry = hdst.hd->hd_dentry; ++ hdst.hd->hd_dentry = wh_dentry; ++ dinfo->di_btop = cpg->bdst; ++ ++ hsrc.h_dentry = NULL; + if (file) { -+ h_d_start = hdp[0 + cpg->bsrc].hd_dentry; -+ hdp[0 + cpg->bsrc].hd_dentry = au_hf_top(file)->f_path.dentry; ++ hsrc.hd = au_hdentry(dinfo, cpg->bsrc); ++ hsrc.h_dentry = hsrc.hd->hd_dentry; ++ hsrc.hd->hd_dentry = au_hf_top(file)->f_path.dentry; + } + flags_orig = cpg->flags; + cpg->flags = !AuCpup_DTIME; @@ -5542,10 +6163,10 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + if (file) { + if (!err) + err = au_reopen_nondir(file); -+ hdp[0 + cpg->bsrc].hd_dentry = h_d_start; ++ hsrc.hd->hd_dentry = hsrc.h_dentry; + } -+ hdp[0 + cpg->bdst].hd_dentry = h_d_dst; -+ dinfo->di_bstart = cpg->bsrc; ++ hdst.hd->hd_dentry = hdst.h_dentry; ++ dinfo->di_btop = cpg->bsrc; + cpg->bsrc = bsrc_orig; + + return err; @@ -5642,7 +6263,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + h_tmpdir = d_inode(h_orph); + au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); + -+ mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3); ++ inode_lock_nested(h_tmpdir, AuLsc_I_PARENT3); + /* todo: au_h_open_pre()? */ + + pin_orig = cpg->pin; @@ -5666,7 +6287,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + } + + if (h_orph) { -+ mutex_unlock(&h_tmpdir->i_mutex); ++ inode_unlock(h_tmpdir); + /* todo: au_h_open_post()? */ + au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); + au_set_h_dptr(parent, bdst, h_parent); @@ -5729,7 +6350,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c + h_dentry = au_h_dptr(d, bdst); + if (!h_dentry || d_is_negative(h_dentry)) { + if (h_dentry) -+ au_update_dbstart(d); ++ au_update_dbtop(d); + + au_pin_set_dentry(&pin, d); + err = au_do_pin(&pin); @@ -5796,10 +6417,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,94 @@ ++++ linux/fs/aufs/cpup.h 2018-08-12 23:43:05.453457863 +0200 +@@ -0,0 +1,100 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -5859,6 +6481,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) @@ -5884,7 +6511,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.h linux/fs/aufs/cpup.h +struct au_dtime { + struct dentry *dt_dentry; + struct path dt_h_path; -+ struct timespec dt_atime, dt_mtime; ++ struct timespec64 dt_atime, dt_mtime; +}; +void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, + struct path *h_path); @@ -5894,10 +6521,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,432 @@ ++++ linux/fs/aufs/dbgaufs.c 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,526 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -5925,7 +6553,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c +#endif + +static struct dentry *dbgaufs; -+static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH; ++static const mode_t dbgaufs_mode = 0444; + +/* 20 is max digits length of ulong 64 */ +struct dbgaufs_arg { @@ -5939,11 +6567,19 @@ 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) +{ -+ kfree(file->private_data); ++ void *p; ++ ++ p = file->private_data; ++ if (p) { ++ /* this is struct dbgaufs_arg */ ++ AuDebugOn(!au_kfree_sz_test(p)); ++ au_kfree_do_rcu(p); ++ } + return 0; +} + -+static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt) ++static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt, ++ int cnt) +{ + int err; + struct kstat st; @@ -5960,15 +6596,15 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c + if (!xf) + goto out; + -+ err = vfs_getattr(&xf->f_path, &st); ++ err = vfsub_getattr(&xf->f_path, &st); + if (!err) { + if (do_fcnt) + p->n = snprintf -+ (p->a, sizeof(p->a), "%ld, %llux%lu %lld\n", -+ (long)file_count(xf), st.blocks, st.blksize, ++ (p->a, sizeof(p->a), "%d, %llux%u %lld\n", ++ cnt, st.blocks, st.blksize, + (long long)st.size); + else -+ p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n", ++ p->n = snprintf(p->a, sizeof(p->a), "%llux%u %lld\n", + st.blocks, st.blksize, + (long long)st.size); + AuDebugOn(p->n >= sizeof(p->a)); @@ -5979,7 +6615,6 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c + +out: + return err; -+ +} + +static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, @@ -6012,7 +6647,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); @@ -6032,10 +6667,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); @@ -6097,7 +6731,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); -+ err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0); ++ err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0, /*cnt*/0); + si_read_unlock(sb); + return err; +} @@ -6115,35 +6749,61 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c + +static int dbgaufs_xino_open(struct inode *inode, struct file *file) +{ -+ int err; ++ int err, idx; + long l; ++ aufs_bindex_t bindex; ++ char *p, a[sizeof(DbgaufsXi_PREFIX) + 8]; + struct au_sbinfo *sbinfo; + struct super_block *sb; ++ struct au_xino *xi; + struct file *xf; + struct qstr *name; ++ struct au_branch *br; + + err = -ENOENT; -+ xf = NULL; + name = &file->f_path.dentry->d_name; + if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) + || memcmp(name->name, DbgaufsXi_PREFIX, + sizeof(DbgaufsXi_PREFIX) - 1))) + goto out; -+ err = kstrtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); ++ ++ AuDebugOn(name->len >= sizeof(a)); ++ memcpy(a, name->name, name->len); ++ a[name->len] = '\0'; ++ p = strchr(a, '-'); ++ if (p) ++ *p = '\0'; ++ err = kstrtol(a + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); + if (unlikely(err)) + goto out; ++ bindex = l; ++ idx = 0; ++ if (p) { ++ err = kstrtol(p + 1, 10, &l); ++ if (unlikely(err)) ++ goto out; ++ idx = l; ++ } + ++ err = -ENOENT; + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); -+ if (l <= au_sbend(sb)) { -+ xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; -+ err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); -+ } else -+ err = -ENOENT; -+ si_read_unlock(sb); ++ if (unlikely(bindex < 0 || bindex > au_sbbot(sb))) ++ goto out_si; ++ br = au_sbr(sb, bindex); ++ xi = br->br_xino; ++ if (unlikely(idx >= xi->xi_nfile)) ++ goto out_si; ++ xf = au_xino_file(xi, idx); ++ if (xf) ++ err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1, ++ au_xino_count(br)); + ++out_si: ++ si_read_unlock(sb); +out: ++ AuTraceErr(err); + return err; +} + @@ -6154,50 +6814,109 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c + .read = dbgaufs_xi_read +}; + ++void dbgaufs_xino_del(struct au_branch *br) ++{ ++ struct dentry *dbgaufs; ++ ++ dbgaufs = br->br_dbgaufs; ++ if (!dbgaufs) ++ return; ++ ++ br->br_dbgaufs = NULL; ++ /* debugfs acquires the parent i_mutex */ ++ lockdep_off(); ++ debugfs_remove(dbgaufs); ++ lockdep_on(); ++} ++ +void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) +{ -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + struct au_branch *br; -+ struct au_xino_file *xi; + + if (!au_sbi(sb)->si_dbgaufs) + return; + -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); -+ xi = &br->br_xino; -+ debugfs_remove(xi->xi_dbgaufs); -+ xi->xi_dbgaufs = NULL; ++ dbgaufs_xino_del(br); ++ } ++} ++ ++static void dbgaufs_br_do_add(struct super_block *sb, aufs_bindex_t bindex, ++ unsigned int idx, struct dentry *parent, ++ struct au_sbinfo *sbinfo) ++{ ++ struct au_branch *br; ++ struct dentry *d; ++ /* "xi" bindex(5) "-" idx(2) NULL */ ++ char name[sizeof(DbgaufsXi_PREFIX) + 8]; ++ ++ if (!idx) ++ snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); ++ else ++ snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d-%u", ++ bindex, idx); ++ br = au_sbr(sb, bindex); ++ if (br->br_dbgaufs) { ++ struct qstr qstr = QSTR_INIT(name, strlen(name)); ++ ++ if (!au_qstreq(&br->br_dbgaufs->d_name, &qstr)) { ++ /* debugfs acquires the parent i_mutex */ ++ lockdep_off(); ++ d = debugfs_rename(parent, br->br_dbgaufs, parent, ++ name); ++ lockdep_on(); ++ if (unlikely(!d)) ++ pr_warn("failed renaming %pd/%s, ignored.\n", ++ parent, name); ++ } ++ } else { ++ lockdep_off(); ++ br->br_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, ++ sbinfo, &dbgaufs_xino_fop); ++ lockdep_on(); ++ if (unlikely(!br->br_dbgaufs)) ++ pr_warn("failed creating %pd/%s, ignored.\n", ++ parent, name); + } +} + -+void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) ++static void dbgaufs_br_add(struct super_block *sb, aufs_bindex_t bindex, ++ struct dentry *parent, struct au_sbinfo *sbinfo) ++{ ++ struct au_branch *br; ++ struct au_xino *xi; ++ unsigned int u; ++ ++ br = au_sbr(sb, bindex); ++ xi = br->br_xino; ++ for (u = 0; u < xi->xi_nfile; u++) ++ dbgaufs_br_do_add(sb, bindex, u, parent, sbinfo); ++} ++ ++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex, int topdown) +{ + struct au_sbinfo *sbinfo; + struct dentry *parent; -+ struct au_branch *br; -+ struct au_xino_file *xi; -+ aufs_bindex_t bend; -+ char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ ++ aufs_bindex_t bbot; ++ ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return; + + sbinfo = au_sbi(sb); + parent = sbinfo->si_dbgaufs; + if (!parent) + return; + -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); -+ br = au_sbr(sb, bindex); -+ xi = &br->br_xino; -+ AuDebugOn(xi->xi_dbgaufs); -+ xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, -+ sbinfo, &dbgaufs_xino_fop); -+ /* ignore an error */ -+ if (unlikely(!xi->xi_dbgaufs)) -+ AuWarn1("failed %s under debugfs\n", name); -+ } ++ bbot = au_sbbot(sb); ++ if (topdown) ++ for (; bindex <= bbot; bindex++) ++ dbgaufs_br_add(sb, bindex, parent, sbinfo); ++ else ++ for (; bbot >= bindex; bbot--) ++ dbgaufs_br_add(sb, bbot, parent, sbinfo); +} + +/* ---------------------------------------------------------------------- */ @@ -6212,7 +6931,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); -+ err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0); ++ err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0, /*cnt*/0); + si_read_unlock(sb); + return err; +} @@ -6262,7 +6981,6 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c + + debugfs_remove_recursive(sbinfo->si_dbgaufs); + sbinfo->si_dbgaufs = NULL; -+ kobject_put(&sbinfo->si_kobj); +} + +int dbgaufs_si_init(struct au_sbinfo *sbinfo) @@ -6287,20 +7005,21 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c + sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); + if (unlikely(!sbinfo->si_dbgaufs)) + goto out; -+ kobject_get(&sbinfo->si_kobj); -+ -+ sbinfo->si_dbgaufs_xib = debugfs_create_file -+ ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, -+ &dbgaufs_xib_fop); -+ if (unlikely(!sbinfo->si_dbgaufs_xib)) -+ goto out_dir; + ++ /* regardless plink/noplink option */ + sbinfo->si_dbgaufs_plink = debugfs_create_file + ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, + &dbgaufs_plink_fop); + if (unlikely(!sbinfo->si_dbgaufs_plink)) + goto out_dir; + ++ /* regardless xino/noxino option */ ++ sbinfo->si_dbgaufs_xib = debugfs_create_file ++ ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_xib_fop); ++ if (unlikely(!sbinfo->si_dbgaufs_xib)) ++ goto out_dir; ++ + err = dbgaufs_xigen_init(sbinfo); + if (!err) + goto out; /* success */ @@ -6308,6 +7027,8 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c +out_dir: + dbgaufs_si_fin(sbinfo); +out: ++ if (unlikely(err)) ++ pr_err("debugfs/aufs failed\n"); + return err; +} + @@ -6330,10 +7051,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,48 @@ ++++ linux/fs/aufs/dbgaufs.h 2018-08-12 23:43:05.453457863 +0200 +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -6360,18 +7082,22 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.h linux/fs/aufs/dbgaufs.h + +struct super_block; +struct au_sbinfo; ++struct au_branch; + +#ifdef CONFIG_DEBUG_FS +/* dbgaufs.c */ ++void dbgaufs_xino_del(struct au_branch *br); +void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); -+void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); ++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex, int topdown); +void dbgaufs_si_fin(struct au_sbinfo *sbinfo); +int dbgaufs_si_init(struct au_sbinfo *sbinfo); +void dbgaufs_fin(void); +int __init dbgaufs_init(void); +#else ++AuStubVoid(dbgaufs_xino_del, struct au_branch *br) +AuStubVoid(dbgaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex, ++ int topdown) +AuStubVoid(dbgaufs_si_fin, struct au_sbinfo *sbinfo) +AuStubInt0(dbgaufs_si_init, struct au_sbinfo *sbinfo) +AuStubVoid(dbgaufs_fin, void) @@ -6382,10 +7108,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,224 @@ ++++ linux/fs/aufs/dcsub.c 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,225 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -6438,7 +7165,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c + return 0; /* success */ + +out_dpages: -+ kfree(dpages->dpages); ++ au_kfree_try_rcu(dpages->dpages); +out: + return err; +} @@ -6451,7 +7178,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++); -+ kfree(dpages->dpages); ++ au_kfree_try_rcu(dpages->dpages); +} + +static int au_dpages_append(struct au_dcsub_pages *dpages, @@ -6468,7 +7195,8 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c + err = -ENOMEM; + sz = dpages->ndpage * sizeof(*dpages->dpages); + p = au_kzrealloc(dpages->dpages, sz, -+ sz + sizeof(*dpages->dpages), gfp); ++ sz + sizeof(*dpages->dpages), gfp, ++ /*may_shrink*/0); + if (unlikely(!p)) + goto out; + @@ -6501,8 +7229,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c +}; + +extern void d_walk(struct dentry *parent, void *data, -+ enum d_walk_ret (*enter)(void *, struct dentry *), -+ void (*finish)(void *)); ++ enum d_walk_ret (*enter)(void *, struct dentry *)); + +struct ac_dpages_arg { + int err; @@ -6542,7 +7269,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c + .arg = arg + }; + -+ d_walk(root, &args, au_call_dpages_append, NULL); ++ d_walk(root, &args, au_call_dpages_append); + + return args.err; +} @@ -6610,10 +7337,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,136 @@ ++++ linux/fs/aufs/dcsub.h 2018-08-12 23:43:05.453457863 +0200 +@@ -0,0 +1,137 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -6750,10 +7478,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,438 @@ ++++ linux/fs/aufs/debug.c 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,440 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -6807,7 +7536,7 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c + +atomic_t aufs_debug = ATOMIC_INIT(0); +MODULE_PARM_DESC(debug, "debug print"); -+module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP); ++module_param_named(debug, aufs_debug, atomic_t, 0664); + +DEFINE_MUTEX(au_dbg_mtx); /* just to serialize the dbg msgs */ +char *au_plevel = KERN_DEBUG; @@ -6849,7 +7578,7 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c + return; + } + -+ dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n", ++ dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %llu\n", + vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk, + vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version); + for (ul = 0; ul < vdir->vd_nblk; ul++) { @@ -6884,9 +7613,9 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c + inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", + atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, + i_size_read(inode), (unsigned long long)inode->i_blocks, -+ hn, (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff, ++ hn, (long long)timespec64_to_ns(&inode->i_ctime) & 0x0ffff, + inode->i_mapping ? inode->i_mapping->nrpages : 0, -+ inode->i_state, inode->i_flags, inode->i_version, ++ inode->i_state, inode->i_flags, inode_peek_iversion(inode), + inode->i_generation, + l ? ", wh " : "", l, n); + return 0; @@ -6895,25 +7624,24 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c +void au_dpri_inode(struct inode *inode) +{ + struct au_iinfo *iinfo; ++ struct au_hinode *hi; + aufs_bindex_t bindex; + int err, hn; + + err = do_pri_inode(-1, inode, -1, NULL); -+ if (err || !au_test_aufs(inode->i_sb)) ++ if (err || !au_test_aufs(inode->i_sb) || au_is_bad_inode(inode)) + return; + + iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ dpri("i-1: bstart %d, bend %d, gen %d\n", -+ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode, NULL)); -+ if (iinfo->ii_bstart < 0) ++ dpri("i-1: btop %d, bbot %d, gen %d\n", ++ iinfo->ii_btop, iinfo->ii_bbot, au_iigen(inode, NULL)); ++ if (iinfo->ii_btop < 0) + return; + hn = 0; -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) { -+ hn = !!au_hn(iinfo->ii_hinode + bindex); -+ do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, hn, -+ iinfo->ii_hinode[0 + bindex].hi_whdentry); ++ for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; bindex++) { ++ hi = au_hinode(iinfo, bindex); ++ hn = !!au_hn(hi); ++ do_pri_inode(bindex, hi->hi_inode, hn, hi->hi_whdentry); + } +} + @@ -6931,7 +7659,9 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c +{ + struct dentry *wh = NULL; + int hn; ++ struct inode *inode; + struct au_iinfo *iinfo; ++ struct au_hinode *hi; + + if (!dentry || IS_ERR(dentry)) { + dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); @@ -6945,16 +7675,19 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c + au_dcount(dentry), dentry->d_flags, + d_unhashed(dentry) ? "un" : ""); + hn = -1; -+ if (bindex >= 0 -+ && d_is_positive(dentry) -+ && au_test_aufs(dentry->d_sb)) { -+ iinfo = au_ii(d_inode(dentry)); -+ if (iinfo) { -+ hn = !!au_hn(iinfo->ii_hinode + bindex); -+ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; -+ } -+ } -+ do_pri_inode(bindex, d_inode(dentry), hn, wh); ++ inode = NULL; ++ if (d_is_positive(dentry)) ++ inode = d_inode(dentry); ++ if (inode ++ && au_test_aufs(dentry->d_sb) ++ && bindex >= 0 ++ && !au_is_bad_inode(inode)) { ++ iinfo = au_ii(inode); ++ hi = au_hinode(iinfo, bindex); ++ hn = !!au_hn(hi); ++ wh = hi->hi_whdentry; ++ } ++ do_pri_inode(bindex, inode, hn, wh); + return 0; +} + @@ -6963,7 +7696,6 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c + struct au_dinfo *dinfo; + aufs_bindex_t bindex; + int err; -+ struct au_hdentry *hdp; + + err = do_pri_dentry(-1, dentry); + if (err || !au_test_aufs(dentry->d_sb)) @@ -6972,15 +7704,14 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c + dinfo = au_di(dentry); + if (!dinfo) + return; -+ dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", -+ dinfo->di_bstart, dinfo->di_bend, ++ dpri("d-1: btop %d, bbot %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", ++ dinfo->di_btop, dinfo->di_bbot, + dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry), + dinfo->di_tmpfile); -+ if (dinfo->di_bstart < 0) ++ if (dinfo->di_btop < 0) + return; -+ hdp = dinfo->di_hdentry; -+ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) -+ do_pri_dentry(bindex, hdp[0 + bindex].hd_dentry); ++ for (bindex = dinfo->di_btop; bindex <= dinfo->di_bbot; bindex++) ++ do_pri_dentry(bindex, au_hdentry(dinfo, bindex)->hd_dentry); +} + +static int do_pri_file(aufs_bindex_t bindex, struct file *file) @@ -7051,13 +7782,14 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c + if (!sb || IS_ERR(sb)) + goto out; + -+ dpri("s%d: {perm 0x%x, id %d, cnt %d, wbr %p}, " ++ dpri("s%d: {perm 0x%x, id %d, wbr %p}, " + "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " + "xino %d\n", -+ bindex, br->br_perm, br->br_id, atomic_read(&br->br_count), -+ br->br_wbr, au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ bindex, br->br_perm, br->br_id, br->br_wbr, ++ au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), + sb->s_flags, sb->s_count, -+ atomic_read(&sb->s_active), !!br->br_xino.xi_file); ++ atomic_read(&sb->s_active), ++ !!au_xino_file(br->br_xino, /*idx*/-1)); + return 0; + +out: @@ -7070,7 +7802,7 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c + struct au_sbinfo *sbinfo; + aufs_bindex_t bindex; + int err; -+ /* to reuduce stack size */ ++ /* to reduce stack size */ + struct { + struct vfsmount mnt; + struct au_branch fake; @@ -7085,10 +7817,8 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c + + a->mnt.mnt_sb = sb; + a->fake.br_path.mnt = &a->mnt; -+ atomic_set(&a->fake.br_count, 0); -+ smp_mb(); /* atomic_set */ + err = do_pri_br(-1, &a->fake); -+ kfree(a); ++ au_kfree_rcu(a); + dpri("dev 0x%x\n", sb->s_dev); + if (err || !au_test_aufs(sb)) + return; @@ -7098,8 +7828,8 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c + return; + dpri("nw %d, gen %u, kobj %d\n", + atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, -+ atomic_read(&sbinfo->si_kobj.kref.refcount)); -+ for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) ++ kref_read(&sbinfo->si_kobj.kref)); ++ for (bindex = 0; bindex <= sbinfo->si_bbot; bindex++) + do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); +} + @@ -7109,21 +7839,21 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c +{ + struct inode *h_inode, *inode = d_inode(dentry); + struct dentry *h_dentry; -+ aufs_bindex_t bindex, bend, bi; ++ aufs_bindex_t bindex, bbot, bi; + + if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) + return; + -+ bend = au_dbend(dentry); -+ bi = au_ibend(inode); -+ if (bi < bend) -+ bend = bi; -+ bindex = au_dbstart(dentry); -+ bi = au_ibstart(inode); ++ bbot = au_dbbot(dentry); ++ bi = au_ibbot(inode); ++ if (bi < bbot) ++ bbot = bi; ++ bindex = au_dbtop(dentry); ++ bi = au_ibtop(inode); + if (bi > bindex) + bindex = bi; + -+ for (; bindex <= bend; bindex++) { ++ for (; bindex <= bbot; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; @@ -7192,10 +7922,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,225 @@ ++++ linux/fs/aufs/debug.h 2018-08-12 23:43:05.453457863 +0200 +@@ -0,0 +1,226 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -7421,10 +8152,11 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,1136 @@ ++++ linux/fs/aufs/dentry.c 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,1153 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -7447,27 +8179,13 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c +#include +#include "aufs.h" + -+#define AuLkup_ALLOW_NEG 1 -+#define AuLkup_IGNORE_PERM (1 << 1) -+#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) -+ -+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; @@ -7482,7 +8200,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, /*try_sio*/0); ++ wh_found = au_wh_test(h_parent, &args->whname, ignore_perm); + h_dentry = ERR_PTR(wh_found); + if (!wh_found) + goto real_lookup; @@ -7490,16 +8208,16 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + goto out; + + /* We found a whiteout */ -+ /* au_set_dbend(dentry, bindex); */ ++ /* au_set_dbbot(dentry, bindex); */ + au_set_dbwh(dentry, bindex); + if (!allow_neg) + return NULL; /* success */ + +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) @@ -7514,11 +8232,18 @@ 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_dbend(dentry) <= bindex) -+ au_set_dbend(dentry, bindex); -+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) -+ au_set_dbstart(dentry, bindex); ++ if (au_dbbot(dentry) <= bindex) ++ au_set_dbbot(dentry, bindex); ++ if (au_dbtop(dentry) < 0 || bindex < au_dbtop(dentry)) ++ au_set_dbtop(dentry, bindex); + au_set_h_dptr(dentry, bindex, h_dentry); + + if (!d_is_dir(h_dentry) @@ -7526,9 +8251,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 */ + -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_shared_nested(h_inode, AuLsc_I_CHILD); + opq = au_diropq_test(h_dentry); -+ mutex_unlock(&h_inode->i_mutex); ++ inode_unlock_shared(h_inode); + if (opq > 0) + au_set_dbdiropq(dentry, bindex); + else if (unlikely(opq < 0)) { @@ -7557,58 +8282,63 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + * otherwise an error. + * can be called at unlinking with @type is zero. + */ -+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) ++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop, ++ unsigned int flags) +{ + 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 = 0, -+ .type = type ++ .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); -+ if (!type) -+ au_fset_lkup(args.flags, ALLOW_NEG); + 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); + btail = au_dbtaildir(parent); -+ for (bindex = bstart; bindex <= btail; bindex++) { ++ 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) { + if (d_is_positive(h_dentry)) + npositive++; -+ if (type != S_IFDIR) -+ break; -+ continue; ++ break; + } + h_parent = au_h_dptr(parent, bindex); + 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); -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, -+ &args); -+ mutex_unlock(&h_dir->i_mutex); ++ 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; @@ -7635,15 +8365,24 @@ 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) { + AuLabel(positive); -+ au_update_dbstart(dentry); ++ au_update_dbtop(dentry); + } + err = npositive; + if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) -+ && au_dbstart(dentry) < 0)) { ++ && au_dbtop(dentry) < 0)) { + err = -EIO; + AuIOErr("both of real entry and whiteout found, %pd, err %d\n", + dentry, err); @@ -7651,7 +8390,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + +out_parent: + dput(parent); -+ kfree(whname.name); ++ au_kfree_try_rcu(args.whname.name); ++ if (dirren) ++ au_dr_lkup_fin(&args); +out: + return err; +} @@ -7705,10 +8446,10 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + } + + err = 0; -+ if (bindex < au_dbstart(dentry)) -+ au_set_dbstart(dentry, bindex); -+ if (au_dbend(dentry) < bindex) -+ au_set_dbend(dentry, bindex); ++ if (bindex < au_dbtop(dentry)) ++ au_set_dbtop(dentry, bindex); ++ if (au_dbbot(dentry) < bindex) ++ au_set_dbbot(dentry, bindex); + au_set_h_dptr(dentry, bindex, h_dentry); + +out: @@ -7738,7 +8479,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + /* ia->i_nlink = h_inode->i_nlink; */ + ia->i_uid = h_inode->i_uid; + ia->i_gid = h_inode->i_gid; -+ ia->i_version = h_inode->i_version; ++ ia->i_version = inode_query_iversion(h_inode); +/* + ia->i_size = h_inode->i_size; + ia->i_blocks = h_inode->i_blocks; @@ -7752,7 +8493,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + /* || ia->i_nlink != h_inode->i_nlink */ + || !uid_eq(ia->i_uid, h_inode->i_uid) + || !gid_eq(ia->i_gid, h_inode->i_gid) -+ || ia->i_version != h_inode->i_version ++ || !inode_eq_iversion(h_inode, ia->i_version) +/* + || ia->i_size != h_inode->i_size + || ia->i_blocks != h_inode->i_blocks @@ -7820,7 +8561,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c +static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) +{ + int err; -+ aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; ++ aufs_bindex_t new_bindex, bindex, bbot, bwh, bdiropq; + struct au_hdentry tmp, *p, *q; + struct au_dinfo *dinfo; + struct super_block *sb; @@ -7829,11 +8570,12 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + + sb = dentry->d_sb; + dinfo = au_di(dentry); -+ bend = dinfo->di_bend; ++ bbot = dinfo->di_bbot; + bwh = dinfo->di_bwh; + bdiropq = dinfo->di_bdiropq; -+ p = dinfo->di_hdentry + dinfo->di_bstart; -+ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { ++ bindex = dinfo->di_btop; ++ p = au_hdentry(dinfo, bindex); ++ for (; bindex <= bbot; bindex++, p++) { + if (!p->hd_dentry) + continue; + @@ -7852,7 +8594,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + } + + /* swap two lower dentries, and loop again */ -+ q = dinfo->di_hdentry + new_bindex; ++ q = au_hdentry(dinfo, new_bindex); + tmp = *q; + *q = *p; + *p = tmp; @@ -7863,31 +8605,33 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + } + + dinfo->di_bwh = -1; -+ if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) ++ if (bwh >= 0 && bwh <= au_sbbot(sb) && au_sbr_whable(sb, bwh)) + dinfo->di_bwh = bwh; + + dinfo->di_bdiropq = -1; + if (bdiropq >= 0 -+ && bdiropq <= au_sbend(sb) ++ && bdiropq <= au_sbbot(sb) + && au_sbr_whable(sb, bdiropq)) + dinfo->di_bdiropq = bdiropq; + + err = -EIO; -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; -+ bend = au_dbend(parent); -+ p = dinfo->di_hdentry; -+ for (bindex = 0; bindex <= bend; bindex++, p++) ++ dinfo->di_btop = -1; ++ dinfo->di_bbot = -1; ++ bbot = au_dbbot(parent); ++ bindex = 0; ++ p = au_hdentry(dinfo, bindex); ++ for (; bindex <= bbot; bindex++, p++) + if (p->hd_dentry) { -+ dinfo->di_bstart = bindex; ++ dinfo->di_btop = bindex; + break; + } + -+ if (dinfo->di_bstart >= 0) { -+ p = dinfo->di_hdentry + bend; -+ for (bindex = bend; bindex >= 0; bindex--, p--) ++ if (dinfo->di_btop >= 0) { ++ bindex = bbot; ++ p = au_hdentry(dinfo, bindex); ++ for (; bindex >= 0; bindex--, p--) + if (p->hd_dentry) { -+ dinfo->di_bend = bindex; ++ dinfo->di_bbot = bindex; + err = 0; + break; + } @@ -7987,7 +8731,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + struct au_dinfo *tmp) +{ + int err; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct { + struct dentry *dentry; + struct inode *inode; @@ -8000,16 +8744,16 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + struct dentry *h_dentry; + + err = 0; -+ AuDebugOn(dinfo->di_bstart < 0); ++ AuDebugOn(dinfo->di_btop < 0); + orig_h.mode = 0; -+ orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; ++ orig_h.dentry = au_hdentry(dinfo, dinfo->di_btop)->hd_dentry; + orig_h.inode = NULL; + if (d_is_positive(orig_h.dentry)) { + orig_h.inode = d_inode(orig_h.dentry); + orig_h.mode = orig_h.inode->i_mode & S_IFMT; + } -+ if (tmp->di_bstart >= 0) { -+ tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; ++ if (tmp->di_btop >= 0) { ++ tmp_h.dentry = au_hdentry(tmp, tmp->di_btop)->hd_dentry; + if (d_is_positive(tmp_h.dentry)) { + tmp_h.inode = d_inode(tmp_h.dentry); + tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; @@ -8020,26 +8764,26 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + if (d_really_is_positive(dentry)) + inode = d_inode(dentry); + if (!orig_h.inode) { -+ AuDbg("nagative originally\n"); ++ AuDbg("negative originally\n"); + if (inode) { + au_hide(dentry); + goto out; + } + AuDebugOn(inode); -+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); ++ AuDebugOn(dinfo->di_btop != dinfo->di_bbot); + AuDebugOn(dinfo->di_bdiropq != -1); + + if (!tmp_h.inode) { + AuDbg("negative --> negative\n"); + /* should have only one negative lower */ -+ if (tmp->di_bstart >= 0 -+ && tmp->di_bstart < dinfo->di_bstart) { -+ AuDebugOn(tmp->di_bstart != tmp->di_bend); -+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); -+ au_set_h_dptr(dentry, dinfo->di_bstart, NULL); ++ if (tmp->di_btop >= 0 ++ && tmp->di_btop < dinfo->di_btop) { ++ AuDebugOn(tmp->di_btop != tmp->di_bbot); ++ AuDebugOn(dinfo->di_btop != dinfo->di_bbot); ++ au_set_h_dptr(dentry, dinfo->di_btop, NULL); + au_di_cp(dinfo, tmp); -+ hd = tmp->di_hdentry + tmp->di_bstart; -+ au_set_h_dptr(dentry, tmp->di_bstart, ++ hd = au_hdentry(tmp, tmp->di_btop); ++ au_set_h_dptr(dentry, tmp->di_btop, + dget(hd->hd_dentry)); + } + au_dbg_verify_dinode(dentry); @@ -8064,7 +8808,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + AuDbg("positive --> negative\n"); + /* or bypassing aufs */ + au_hide(dentry); -+ if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) ++ if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_btop) + dinfo->di_bwh = tmp->di_bwh; + if (inode) + err = au_refresh_hinode_self(inode); @@ -8072,7 +8816,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + } else if (orig_h.mode == tmp_h.mode) { + AuDbg("positive --> positive, same type\n"); + if (!S_ISDIR(orig_h.mode) -+ && dinfo->di_bstart > tmp->di_bstart) { ++ && dinfo->di_btop > tmp->di_btop) { + /* + * similar to the behaviour of removing and + * creating. @@ -8083,19 +8827,19 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + au_dbg_verify_dinode(dentry); + } else { + /* fill empty slots */ -+ if (dinfo->di_bstart > tmp->di_bstart) -+ dinfo->di_bstart = tmp->di_bstart; -+ if (dinfo->di_bend < tmp->di_bend) -+ dinfo->di_bend = tmp->di_bend; ++ if (dinfo->di_btop > tmp->di_btop) ++ dinfo->di_btop = tmp->di_btop; ++ if (dinfo->di_bbot < tmp->di_bbot) ++ dinfo->di_bbot = tmp->di_bbot; + dinfo->di_bwh = tmp->di_bwh; + dinfo->di_bdiropq = tmp->di_bdiropq; -+ hd = tmp->di_hdentry; -+ bend = dinfo->di_bend; -+ for (bindex = tmp->di_bstart; bindex <= bend; -+ bindex++) { ++ bbot = dinfo->di_bbot; ++ bindex = tmp->di_btop; ++ hd = au_hdentry(tmp, bindex); ++ for (; bindex <= bbot; bindex++, hd++) { + if (au_h_dptr(dentry, bindex)) + continue; -+ h_dentry = hd[bindex].hd_dentry; ++ h_dentry = hd->hd_dentry; + if (!h_dentry) + continue; + AuDebugOn(d_is_negative(h_dentry)); @@ -8106,7 +8850,8 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + au_set_h_dptr(dentry, bindex, + dget(h_dentry)); + } -+ err = au_refresh_hinode(inode, dentry); ++ if (inode) ++ err = au_refresh_hinode(inode, dentry); + au_dbg_verify_dinode(dentry); + } + } else { @@ -8147,7 +8892,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + +int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) +{ -+ int err, ebrange; ++ int err, ebrange, nbr; + unsigned int sigen; + struct au_dinfo *dinfo, *tmp; + struct super_block *sb; @@ -8163,8 +8908,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + if (unlikely(err)) + goto out; + ++ nbr = au_sbbot(sb) + 1; + dinfo = au_di(dentry); -+ err = au_di_realloc(dinfo, au_sbend(sb) + 1); ++ err = au_di_realloc(dinfo, nbr, /*may_shrink*/0); + if (unlikely(err)) + goto out; + ebrange = au_dbrange_test(dentry); @@ -8172,7 +8918,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + ebrange = au_do_refresh_hdentry(dentry, parent); + + if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { -+ AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); ++ AuDebugOn(au_dbtop(dentry) < 0 && au_dbbot(dentry) >= 0); + if (d_really_is_positive(dentry)) { + inode = d_inode(dentry); + err = au_refresh_hinode_self(inode); @@ -8195,7 +8941,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + * if current working dir is removed, it returns an error. + * but the dentry is legal. + */ -+ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); ++ err = au_lkup_dentry(dentry, /*btop*/0, AuLkup_ALLOW_NEG); + AuDbgDentry(dentry); + au_di_swap(tmp, dinfo); + if (err == -ENOENT) @@ -8207,6 +8953,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + au_dbg_verify_dinode(dentry); + AuTraceErr(err); + } ++ au_di_realloc(dinfo, nbr, /*may_shrink*/1); /* harmless if err */ + au_rw_write_unlock(&tmp->di_rwsem); + au_di_free(tmp); + if (unlikely(err)) @@ -8254,11 +9001,11 @@ 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; -+ aufs_bindex_t bindex, btail, bstart, ibs, ibe; ++ aufs_bindex_t bindex, btail, btop, ibs, ibe; + unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; + struct inode *h_inode, *h_cached_inode; + struct dentry *h_dentry; @@ -8284,15 +9031,15 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + if (do_udba && inode) { + mode = (inode->i_mode & S_IFMT); + plus = (inode->i_nlink > 0); -+ ibs = au_ibstart(inode); -+ ibe = au_ibend(inode); ++ ibs = au_ibtop(inode); ++ ibe = au_ibbot(inode); + } + -+ bstart = au_dbstart(dentry); -+ btail = bstart; ++ btop = au_dbtop(dentry); ++ btail = btop; + if (inode && S_ISDIR(inode->i_mode)) + btail = au_dbtaildir(dentry); -+ for (bindex = bstart; bindex <= btail; bindex++) { ++ for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; @@ -8305,7 +9052,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 @@ -8446,7 +9193,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; + @@ -8474,7 +9221,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + inode = NULL; + if (d_really_is_positive(dentry)) + inode = d_inode(dentry); -+ if (unlikely(inode && is_bad_inode(inode))) { ++ if (unlikely(inode && au_is_bad_inode(inode))) { + err = -EINVAL; + AuTraceErr(err); + goto out_dgrade; @@ -8507,11 +9254,11 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + + do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); + if (do_udba && inode) { -+ aufs_bindex_t bstart = au_ibstart(inode); ++ aufs_bindex_t btop = au_ibtop(inode); + struct inode *h_inode; + -+ if (bstart >= 0) { -+ h_inode = au_h_iptr(inode, bstart); ++ if (btop >= 0) { ++ h_inode = au_h_iptr(inode, btop); + if (h_inode && au_test_higen(inode, h_inode)) { + AuTraceErr(err); + goto out_inval; @@ -8519,8 +9266,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c + } + } + -+ err = h_d_revalidate(dentry, inode, flags, do_udba); -+ if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { ++ 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", + dentry, err); @@ -8561,10 +9309,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,234 @@ ++++ linux/fs/aufs/dentry.h 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,268 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -8590,6 +9339,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 { @@ -8601,13 +9351,38 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h + atomic_t di_generation; + + struct au_rwsem di_rwsem; -+ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq; ++ aufs_bindex_t di_btop, di_bbot, di_bwh, di_bdiropq; + unsigned char di_tmpfile; /* to allow the different name */ + struct au_hdentry *di_hdentry; ++ struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ + ++/* 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 */ +extern const struct dentry_operations aufs_dop, aufs_dop_noreval; +struct au_branch; @@ -8615,7 +9390,8 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h +int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, + struct dentry *h_parent, struct au_branch *br); + -+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type); ++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop, ++ unsigned int flags); +int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); +int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); +int au_reval_dpath(struct dentry *dentry, unsigned int sigen); @@ -8629,7 +9405,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h +void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src); +int au_di_init(struct dentry *dentry); +void au_di_fin(struct dentry *dentry); -+int au_di_realloc(struct au_dinfo *dinfo, int nbr); ++int au_di_realloc(struct au_dinfo *dinfo, int nbr, int may_shrink); + +void di_read_lock(struct dentry *d, int flags, unsigned int lsc); +void di_read_unlock(struct dentry *d, int flags); @@ -8651,8 +9427,8 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h +int au_dbrange_test(struct dentry *dentry); +void au_update_digen(struct dentry *dentry); +void au_update_dbrange(struct dentry *dentry, int do_put_zero); -+void au_update_dbstart(struct dentry *dentry); -+void au_update_dbend(struct dentry *dentry); ++void au_update_dbtop(struct dentry *dentry); ++void au_update_dbbot(struct dentry *dentry); +int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); + +/* ---------------------------------------------------------------------- */ @@ -8723,22 +9499,28 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h + hdentry->hd_dentry = NULL; +} + ++static inline struct au_hdentry *au_hdentry(struct au_dinfo *di, ++ aufs_bindex_t bindex) ++{ ++ return di->di_hdentry + bindex; ++} ++ +static inline void au_hdput(struct au_hdentry *hd) +{ + if (hd) + dput(hd->hd_dentry); +} + -+static inline aufs_bindex_t au_dbstart(struct dentry *dentry) ++static inline aufs_bindex_t au_dbtop(struct dentry *dentry) +{ + DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bstart; ++ return au_di(dentry)->di_btop; +} + -+static inline aufs_bindex_t au_dbend(struct dentry *dentry) ++static inline aufs_bindex_t au_dbbot(struct dentry *dentry) +{ + DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bend; ++ return au_di(dentry)->di_bbot; +} + +static inline aufs_bindex_t au_dbwh(struct dentry *dentry) @@ -8754,22 +9536,22 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h +} + +/* todo: hard/soft set? */ -+static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex) ++static inline void au_set_dbtop(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); -+ au_di(dentry)->di_bstart = bindex; ++ au_di(dentry)->di_btop = bindex; +} + -+static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex) ++static inline void au_set_dbbot(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); -+ au_di(dentry)->di_bend = bindex; ++ au_di(dentry)->di_bbot = bindex; +} + +static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); -+ /* dbwh can be outside of bstart - bend range */ ++ /* dbwh can be outside of btop - bbot range */ + au_di(dentry)->di_bwh = bindex; +} + @@ -8799,10 +9581,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,550 @@ ++++ linux/fs/aufs/dinfo.c 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,554 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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,10 +9610,8 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c +void au_di_init_once(void *_dinfo) +{ + struct au_dinfo *dinfo = _dinfo; -+ static struct lock_class_key aufs_di; + + au_rw_init(&dinfo->di_rwsem); -+ au_rw_class(&dinfo->di_rwsem, &aufs_di); +} + +struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) @@ -8842,14 +9623,14 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + if (unlikely(!dinfo)) + goto out; + -+ nbr = au_sbend(sb) + 1; ++ nbr = au_sbbot(sb) + 1; + if (nbr <= 0) + nbr = 1; + dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); + if (dinfo->di_hdentry) { + au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; ++ dinfo->di_btop = -1; ++ dinfo->di_bbot = -1; + dinfo->di_bwh = -1; + dinfo->di_bdiropq = -1; + dinfo->di_tmpfile = 0; @@ -8868,17 +9649,17 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c +void au_di_free(struct au_dinfo *dinfo) +{ + struct au_hdentry *p; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + + /* dentry may not be revalidated */ -+ bindex = dinfo->di_bstart; ++ bindex = dinfo->di_btop; + if (bindex >= 0) { -+ bend = dinfo->di_bend; -+ p = dinfo->di_hdentry + bindex; -+ while (bindex++ <= bend) ++ bbot = dinfo->di_bbot; ++ p = au_hdentry(dinfo, bindex); ++ while (bindex++ <= bbot) + au_hdput(p++); + } -+ kfree(dinfo->di_hdentry); ++ au_kfree_try_rcu(dinfo->di_hdentry); + au_cache_free_dinfo(dinfo); +} + @@ -8898,8 +9679,8 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + } while (0) + + DiSwap(p, hdentry); -+ DiSwap(bi, bstart); -+ DiSwap(bi, bend); ++ DiSwap(bi, btop); ++ DiSwap(bi, bbot); + DiSwap(bi, bwh); + DiSwap(bi, bdiropq); + /* smp_mb(); */ @@ -8912,8 +9693,8 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + AuRwMustWriteLock(&dst->di_rwsem); + AuRwMustWriteLock(&src->di_rwsem); + -+ dst->di_bstart = src->di_bstart; -+ dst->di_bend = src->di_bend; ++ dst->di_btop = src->di_btop; ++ dst->di_bbot = src->di_bbot; + dst->di_bwh = src->di_bwh; + dst->di_bdiropq = src->di_bdiropq; + /* smp_mb(); */ @@ -8947,7 +9728,7 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + au_di_free(dinfo); +} + -+int au_di_realloc(struct au_dinfo *dinfo, int nbr) ++int au_di_realloc(struct au_dinfo *dinfo, int nbr, int may_shrink) +{ + int err, sz; + struct au_hdentry *hdp; @@ -8955,10 +9736,11 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + AuRwMustWriteLock(&dinfo->di_rwsem); + + err = -ENOMEM; -+ sz = sizeof(*hdp) * (dinfo->di_bend + 1); ++ sz = sizeof(*hdp) * (dinfo->di_bbot + 1); + if (!sz) + sz = sizeof(*hdp); -+ hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS); ++ hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS, ++ may_shrink); + if (hdp) { + dinfo->di_hdentry = hdp; + err = 0; @@ -9080,11 +9862,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); + } @@ -9096,11 +9878,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); + } @@ -9123,10 +9905,10 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + + DiMustAnyLock(dentry); + -+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) ++ if (au_dbtop(dentry) < 0 || bindex < au_dbtop(dentry)) + return NULL; + AuDebugOn(bindex < 0); -+ d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; ++ d = au_hdentry(au_di(dentry), bindex)->hd_dentry; + AuDebugOn(d && au_dcount(d) <= 0); + return d; +} @@ -9144,8 +9926,8 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + AuDebugOn(d_really_is_negative(dentry)); + + h_dentry = NULL; -+ if (au_dbstart(dentry) <= bindex -+ && bindex <= au_dbend(dentry)) ++ if (au_dbtop(dentry) <= bindex ++ && bindex <= au_dbbot(dentry)) + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && !au_d_linkable(h_dentry)) { + dget(h_dentry); @@ -9153,8 +9935,8 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + } + + inode = d_inode(dentry); -+ AuDebugOn(bindex < au_ibstart(inode)); -+ AuDebugOn(au_ibend(inode) < bindex); ++ AuDebugOn(bindex < au_ibtop(inode)); ++ AuDebugOn(au_ibbot(inode) < bindex); + h_inode = au_h_iptr(inode, bindex); + h_dentry = d_find_alias(h_inode); + if (h_dentry) { @@ -9184,30 +9966,30 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + +aufs_bindex_t au_dbtail(struct dentry *dentry) +{ -+ aufs_bindex_t bend, bwh; ++ aufs_bindex_t bbot, bwh; + -+ bend = au_dbend(dentry); -+ if (0 <= bend) { ++ bbot = au_dbbot(dentry); ++ if (0 <= bbot) { + bwh = au_dbwh(dentry); + if (!bwh) + return bwh; -+ if (0 < bwh && bwh < bend) ++ if (0 < bwh && bwh < bbot) + return bwh - 1; + } -+ return bend; ++ return bbot; +} + +aufs_bindex_t au_dbtaildir(struct dentry *dentry) +{ -+ aufs_bindex_t bend, bopq; ++ aufs_bindex_t bbot, bopq; + -+ bend = au_dbtail(dentry); -+ if (0 <= bend) { ++ bbot = au_dbtail(dentry); ++ if (0 <= bbot) { + bopq = au_dbdiropq(dentry); -+ if (0 <= bopq && bopq < bend) -+ bend = bopq; ++ if (0 <= bopq && bopq < bbot) ++ bbot = bopq; + } -+ return bend; ++ return bbot; +} + +/* ---------------------------------------------------------------------- */ @@ -9215,11 +9997,14 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c +void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_dentry) +{ -+ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; ++ struct au_dinfo *dinfo; ++ struct au_hdentry *hd; + struct au_branch *br; + + DiMustWriteLock(dentry); + ++ dinfo = au_di(dentry); ++ hd = au_hdentry(dinfo, bindex); + au_hdput(hd); + hd->hd_dentry = h_dentry; + if (h_dentry) { @@ -9231,16 +10016,16 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c +int au_dbrange_test(struct dentry *dentry) +{ + int err; -+ aufs_bindex_t bstart, bend; ++ aufs_bindex_t btop, bbot; + + err = 0; -+ bstart = au_dbstart(dentry); -+ bend = au_dbend(dentry); -+ if (bstart >= 0) -+ AuDebugOn(bend < 0 && bstart > bend); ++ btop = au_dbtop(dentry); ++ bbot = au_dbbot(dentry); ++ if (btop >= 0) ++ AuDebugOn(bbot < 0 && btop > bbot); + else { + err = -EIO; -+ AuDebugOn(bend >= 0); ++ AuDebugOn(bbot >= 0); + } + + return err; @@ -9269,72 +10054,73 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + struct au_dinfo *dinfo; + struct dentry *h_d; + struct au_hdentry *hdp; ++ aufs_bindex_t bindex, bbot; + + DiMustWriteLock(dentry); + + dinfo = au_di(dentry); -+ if (!dinfo || dinfo->di_bstart < 0) ++ if (!dinfo || dinfo->di_btop < 0) + return; + -+ hdp = dinfo->di_hdentry; + if (do_put_zero) { -+ aufs_bindex_t bindex, bend; -+ -+ bend = dinfo->di_bend; -+ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { -+ h_d = hdp[0 + bindex].hd_dentry; ++ bbot = dinfo->di_bbot; ++ bindex = dinfo->di_btop; ++ hdp = au_hdentry(dinfo, bindex); ++ for (; bindex <= bbot; bindex++, hdp++) { ++ h_d = hdp->hd_dentry; + if (h_d && d_is_negative(h_d)) + au_set_h_dptr(dentry, bindex, NULL); + } + } + -+ dinfo->di_bstart = -1; -+ while (++dinfo->di_bstart <= dinfo->di_bend) -+ if (hdp[0 + dinfo->di_bstart].hd_dentry) ++ dinfo->di_btop = 0; ++ hdp = au_hdentry(dinfo, dinfo->di_btop); ++ for (; dinfo->di_btop <= dinfo->di_bbot; dinfo->di_btop++, hdp++) ++ if (hdp->hd_dentry) + break; -+ if (dinfo->di_bstart > dinfo->di_bend) { -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; ++ if (dinfo->di_btop > dinfo->di_bbot) { ++ dinfo->di_btop = -1; ++ dinfo->di_bbot = -1; + return; + } + -+ dinfo->di_bend++; -+ while (0 <= --dinfo->di_bend) -+ if (hdp[0 + dinfo->di_bend].hd_dentry) ++ hdp = au_hdentry(dinfo, dinfo->di_bbot); ++ for (; dinfo->di_bbot >= 0; dinfo->di_bbot--, hdp--) ++ if (hdp->hd_dentry) + break; -+ AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); ++ AuDebugOn(dinfo->di_btop > dinfo->di_bbot || dinfo->di_bbot < 0); +} + -+void au_update_dbstart(struct dentry *dentry) ++void au_update_dbtop(struct dentry *dentry) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct dentry *h_dentry; + -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ bbot = au_dbbot(dentry); ++ for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + if (d_is_positive(h_dentry)) { -+ au_set_dbstart(dentry, bindex); ++ au_set_dbtop(dentry, bindex); + return; + } + au_set_h_dptr(dentry, bindex, NULL); + } +} + -+void au_update_dbend(struct dentry *dentry) ++void au_update_dbbot(struct dentry *dentry) +{ -+ aufs_bindex_t bindex, bstart; ++ aufs_bindex_t bindex, btop; + struct dentry *h_dentry; + -+ bstart = au_dbstart(dentry); -+ for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) { ++ btop = au_dbtop(dentry); ++ for (bindex = au_dbbot(dentry); bindex >= btop; bindex--) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + if (d_is_positive(h_dentry)) { -+ au_set_dbend(dentry, bindex); ++ au_set_dbbot(dentry, bindex); + return; + } + au_set_h_dptr(dentry, bindex, NULL); @@ -9343,20 +10129,21 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c + +int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) ++ bbot = au_dbbot(dentry); ++ for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) + if (au_h_dptr(dentry, bindex) == h_dentry) + return bindex; + return -1; +} 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,753 @@ ++++ linux/fs/aufs/dir.c 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,762 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -9412,7 +10199,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c +loff_t au_dir_size(struct file *file, struct dentry *dentry) +{ + loff_t sz; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct file *h_file; + struct dentry *h_dentry; + @@ -9420,9 +10207,9 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + if (file) { + AuDebugOn(!d_is_dir(file->f_path.dentry)); + -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); -+ bindex <= bend && sz < KMALLOC_MAX_SIZE; ++ bbot = au_fbbot_dir(file); ++ for (bindex = au_fbtop(file); ++ bindex <= bbot && sz < KMALLOC_MAX_SIZE; + bindex++) { + h_file = au_hf_dir(file, bindex); + if (h_file && file_inode(h_file)) @@ -9432,9 +10219,9 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + AuDebugOn(!dentry); + AuDebugOn(!d_is_dir(dentry)); + -+ bend = au_dbtaildir(dentry); -+ for (bindex = au_dbstart(dentry); -+ bindex <= bend && sz < KMALLOC_MAX_SIZE; ++ bbot = au_dbtaildir(dentry); ++ for (bindex = au_dbtop(dentry); ++ bindex <= bbot && sz < KMALLOC_MAX_SIZE; + bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && d_is_positive(h_dentry)) @@ -9467,7 +10254,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + struct au_branch *br; + struct au_hinode *hdir; + int err; -+ aufs_bindex_t bstart, bindex; ++ aufs_bindex_t btop, bindex; + + sb = a->dentry->d_sb; + if (d_really_is_negative(a->dentry)) @@ -9476,9 +10263,9 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + aufs_read_lock(a->dentry, AuLock_DW); /* noflush */ + + dir = d_inode(a->dentry); -+ bstart = au_ibstart(dir); ++ btop = au_ibtop(dir); + bindex = au_br_index(sb, a->brid); -+ if (bindex < bstart) ++ if (bindex < btop) + goto out_unlock; + + br = au_sbr(sb, bindex); @@ -9488,23 +10275,23 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + h_path.mnt = au_br_mnt(br); + au_dtime_store(&dt, a->dentry, &h_path); + -+ br = au_sbr(sb, bstart); ++ br = au_sbr(sb, btop); + if (!au_br_writable(br->br_perm)) + goto out_unlock; -+ h_path.dentry = au_h_dptr(a->dentry, bstart); ++ h_path.dentry = au_h_dptr(a->dentry, btop); + h_path.mnt = au_br_mnt(br); + err = vfsub_mnt_want_write(h_path.mnt); + if (err) + goto out_unlock; -+ hdir = au_hi(dir, bstart); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ h_dir = au_h_iptr(dir, bstart); ++ hdir = au_hi(dir, btop); ++ au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); ++ h_dir = au_h_iptr(dir, btop); + if (h_dir->i_nlink -+ && timespec_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { ++ && timespec64_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { + dt.dt_h_path = h_path; + au_dtime_revert(&dt); + } -+ au_hn_imtx_unlock(hdir); ++ au_hn_inode_unlock(hdir); + vfsub_mnt_drop_write(h_path.mnt); + au_cpup_attr_timesizes(dir); + @@ -9513,13 +10300,13 @@ 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); -+ kfree(arg); ++ au_kfree_try_rcu(arg); +} + +void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) +{ + int perm, wkq_err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + struct au_dir_ts_arg *arg; + struct dentry *dentry; + struct super_block *sb; @@ -9529,13 +10316,13 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + dentry = d_find_any_alias(dir); + AuDebugOn(!dentry); + sb = dentry->d_sb; -+ bstart = au_ibstart(dir); -+ if (bstart == bindex) { ++ btop = au_ibtop(dir); ++ if (btop == bindex) { + au_cpup_attr_timesizes(dir); + goto out; + } + -+ perm = au_sbr_perm(sb, bstart); ++ perm = au_sbr_perm(sb, btop); + if (!au_br_writable(perm)) + goto out; + @@ -9549,7 +10336,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); -+ kfree(arg); ++ au_kfree_try_rcu(arg); + } + +out: @@ -9562,24 +10349,24 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c +{ + int err; + unsigned int flags; -+ aufs_bindex_t bindex, btail, bstart; ++ aufs_bindex_t bindex, btail, btop; + struct dentry *dentry, *h_dentry; + struct file *h_file; + + /* open all lower dirs */ + dentry = file->f_path.dentry; -+ bstart = au_dbstart(dentry); -+ for (bindex = au_fbstart(file); bindex < bstart; bindex++) ++ btop = au_dbtop(dentry); ++ for (bindex = au_fbtop(file); bindex < btop; bindex++) + au_set_h_fptr(file, bindex, NULL); -+ au_set_fbstart(file, bstart); ++ au_set_fbtop(file, btop); + + btail = au_dbtaildir(dentry); -+ for (bindex = au_fbend_dir(file); btail < bindex; bindex--) ++ for (bindex = au_fbbot_dir(file); btail < bindex; bindex--) + au_set_h_fptr(file, bindex, NULL); -+ au_set_fbend_dir(file, btail); ++ au_set_fbbot_dir(file, btail); + + flags = vfsub_file_flags(file); -+ for (bindex = bstart; bindex <= btail; bindex++) { ++ for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; @@ -9607,22 +10394,27 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + int err; + aufs_bindex_t bindex, btail; + struct dentry *dentry, *h_dentry; ++ struct vfsmount *mnt; + + FiMustWriteLock(file); + AuDebugOn(h_file); + + err = 0; ++ mnt = file->f_path.mnt; + dentry = file->f_path.dentry; -+ file->f_version = d_inode(dentry)->i_version; -+ bindex = au_dbstart(dentry); -+ au_set_fbstart(file, bindex); ++ file->f_version = inode_query_iversion(d_inode(dentry)); ++ bindex = au_dbtop(dentry); ++ au_set_fbtop(file, bindex); + btail = au_dbtaildir(dentry); -+ au_set_fbend_dir(file, btail); ++ au_set_fbbot_dir(file, btail); + for (; !err && bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + ++ err = vfsub_test_mntns(mnt, h_dentry->d_sb); ++ if (unlikely(err)) ++ break; + h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); + if (IS_ERR(h_file)) { + err = PTR_ERR(h_file); @@ -9637,10 +10429,10 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + return 0; /* success */ + + /* close all */ -+ for (bindex = au_fbstart(file); bindex <= btail; bindex++) ++ for (bindex = au_fbtop(file); bindex <= btail; bindex++) + au_set_h_fptr(file, bindex, NULL); -+ au_set_fbstart(file, -1); -+ au_set_fbend_dir(file, -1); ++ au_set_fbtop(file, -1); ++ au_set_fbbot_dir(file, -1); + + return err; +} @@ -9663,7 +10455,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + }; + err = au_do_open(file, &args); + if (unlikely(err)) -+ kfree(fidir); ++ au_kfree_rcu(fidir); + } + si_read_unlock(sb); + return err; @@ -9675,28 +10467,31 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + struct au_vdir *vdir_cache; + struct au_finfo *finfo; + struct au_fidir *fidir; -+ aufs_bindex_t bindex, bend; ++ struct au_hfile *hf; ++ aufs_bindex_t bindex, bbot; + + 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); + + bindex = finfo->fi_btop; + if (bindex >= 0) { ++ hf = fidir->fd_hfile + bindex; + /* + * calls fput() instead of filp_close(), + * since no dnotify or lock for the lower file. + */ -+ bend = fidir->fd_bbot; -+ for (; bindex <= bend; bindex++) -+ au_set_h_fptr(file, bindex, NULL); ++ bbot = fidir->fd_bbot; ++ for (; bindex <= bbot; bindex++, hf++) ++ if (hf->hf_file) ++ au_hfput(hf, /*execed*/0); + } -+ kfree(fidir); ++ au_kfree_rcu(fidir); + finfo->fi_hdir = NULL; + } + au_finfo_fin(file); @@ -9708,12 +10503,12 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c +static int au_do_flush_dir(struct file *file, fl_owner_t id) +{ + int err; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct file *h_file; + + err = 0; -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ bbot = au_fbbot_dir(file); ++ for (bindex = au_fbtop(file); !err && bindex <= bbot; bindex++) { + h_file = au_hf_dir(file, bindex); + if (h_file) + err = vfsub_flush(h_file, id); @@ -9731,7 +10526,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c +static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) +{ + int err; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct inode *inode; + struct super_block *sb; + @@ -9739,8 +10534,8 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + sb = dentry->d_sb; + inode = d_inode(dentry); + IMustLock(inode); -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { ++ bbot = au_dbbot(dentry); ++ for (bindex = au_dbtop(dentry); !err && bindex <= bbot; bindex++) { + struct path h_path; + + if (au_test_ro(sb, bindex, inode)) @@ -9759,19 +10554,19 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c +static int au_do_fsync_dir(struct file *file, int datasync) +{ + int err; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct file *h_file; + 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; + + inode = file_inode(file); + sb = inode->i_sb; -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ bbot = au_fbbot_dir(file); ++ for (bindex = au_fbtop(file); !err && bindex <= bbot; bindex++) { + h_file = au_hf_dir(file, bindex); + if (!h_file || au_test_ro(sb, bindex, inode)) + continue; @@ -9793,13 +10588,11 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + struct dentry *dentry; + struct inode *inode; + struct super_block *sb; -+ struct mutex *mtx; + + err = 0; + dentry = file->f_path.dentry; + inode = d_inode(dentry); -+ mtx = &inode->i_mutex; -+ mutex_lock(mtx); ++ inode_lock(inode); + sb = dentry->d_sb; + si_noflush_read_lock(sb); + if (file) @@ -9814,20 +10607,20 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + fi_write_unlock(file); + + si_read_unlock(sb); -+ mutex_unlock(mtx); ++ inode_unlock(inode); + return err; +} + +/* ---------------------------------------------------------------------- */ + -+static int aufs_iterate(struct file *file, struct dir_context *ctx) ++static int aufs_iterate_shared(struct file *file, struct dir_context *ctx) +{ + int err; + struct dentry *dentry; + struct inode *inode, *h_inode; + struct super_block *sb; + -+ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); ++ AuDbg("%pD, ctx{%ps, %llu}\n", file, ctx->actor, ctx->pos); + + dentry = file->f_path.dentry; + inode = d_inode(dentry); @@ -9835,7 +10628,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); @@ -9845,7 +10638,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + if (unlikely(err)) + goto out_unlock; + -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ h_inode = au_h_iptr(inode, au_ibtop(inode)); + if (!au_test_nfsd()) { + err = au_vdir_fill_de(file, ctx); + fsstack_copy_attr_atime(inode, h_inode); @@ -9938,6 +10731,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c +{ + int err; + struct file *h_file; ++ struct au_branch *br; + + h_file = au_h_open(dentry, arg->bindex, + O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, @@ -9962,7 +10756,8 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + +out_put: + fput(h_file); -+ au_sbr_put(dentry->d_sb, arg->bindex); ++ br = au_sbr(dentry->d_sb, arg->bindex); ++ au_lcnt_dec(&br->br_nfiles); +out: + return err; +} @@ -9988,9 +10783,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? */ -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_shared_nested(h_inode, AuLsc_I_CHILD); + err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); -+ mutex_unlock(&h_inode->i_mutex); ++ inode_unlock_shared(h_inode); + if (!err) + err = do_test_empty(dentry, arg); + else { @@ -10014,7 +10809,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c +{ + int err; + unsigned int rdhash; -+ aufs_bindex_t bindex, bstart, btail; ++ aufs_bindex_t bindex, btop, btail; + struct au_nhash whlist; + struct test_empty_arg arg = { + .ctx = { @@ -10034,20 +10829,20 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + + arg.flags = 0; + arg.whlist = &whlist; -+ bstart = au_dbstart(dentry); ++ btop = au_dbtop(dentry); + if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) + au_fset_testempty(arg.flags, SHWH); + test_empty = do_test_empty; + if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)) + test_empty = sio_test_empty; -+ arg.bindex = bstart; ++ arg.bindex = btop; + err = test_empty(dentry, &arg); + if (unlikely(err)) + goto out_whlist; + + au_fset_testempty(arg.flags, WHONLY); + btail = au_dbtaildir(dentry); -+ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { ++ for (bindex = btop + 1; !err && bindex <= btail; bindex++) { + struct dentry *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); @@ -10079,7 +10874,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) + au_fset_testempty(arg.flags, SHWH); + btail = au_dbtaildir(dentry); -+ for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { ++ for (bindex = au_dbtop(dentry); !err && bindex <= btail; bindex++) { + struct dentry *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); @@ -10098,7 +10893,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c + .owner = THIS_MODULE, + .llseek = default_llseek, + .read = generic_read_dir, -+ .iterate = aufs_iterate, ++ .iterate_shared = aufs_iterate_shared, + .unlocked_ioctl = aufs_ioctl_dir, +#ifdef CONFIG_COMPAT + .compat_ioctl = aufs_compat_ioctl_dir, @@ -10110,10 +10905,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,131 @@ ++++ linux/fs/aufs/dir.h 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,134 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -10157,6 +10953,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h +struct au_vdir_dehstr { + struct hlist_node hash; + struct au_vdir_destr *str; ++ struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +struct au_vdir_de { @@ -10192,9 +10989,10 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h + union au_vdir_deblk_p p; + } vd_last; + -+ unsigned long vd_version; ++ u64 vd_version; + unsigned int vd_deblk_sz; + unsigned long vd_jiffy; ++ struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ @@ -10243,12 +11041,13 @@ 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/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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,369 @@ +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-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,1316 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2010-2015 Junjiro R. Okajima ++ * 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 @@ -10265,291 +11064,1756 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c + */ + +/* -+ * dynamically customizable operations for regular files ++ * special handling in renaming a directory ++ * in order to support looking-up the before-renamed name on the lower readonly ++ * branches + */ + ++#include +#include "aufs.h" + -+#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop) ++static void au_dr_hino_del(struct au_dr_br *dr, struct au_dr_hino *ent) ++{ ++ int idx; + -+/* -+ * How large will these lists be? -+ * Usually just a few elements, 20-30 at most for each, I guess. -+ */ -+static struct au_splhead dynop[AuDyLast]; ++ idx = au_dr_ihash(ent->dr_h_ino); ++ au_hbl_del(&ent->dr_hnode, dr->dr_h_ino + idx); ++} + -+static struct au_dykey *dy_gfind_get(struct au_splhead *spl, const void *h_op) ++static int au_dr_hino_test_empty(struct au_dr_br *dr) +{ -+ struct au_dykey *key, *tmp; -+ struct list_head *head; ++ int ret, i; ++ struct hlist_bl_head *hbl; + -+ key = NULL; -+ head = &spl->head; -+ rcu_read_lock(); -+ list_for_each_entry_rcu(tmp, head, dk_list) -+ if (tmp->dk_op.dy_hop == h_op) { -+ key = tmp; -+ kref_get(&key->dk_kref); -+ break; -+ } -+ rcu_read_unlock(); ++ 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 key; ++ return ret; +} + -+static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key) ++static struct au_dr_hino *au_dr_hino_find(struct au_dr_br *dr, ino_t ino) +{ -+ struct au_dykey **k, *found; -+ const void *h_op = key->dk_op.dy_hop; -+ int i; ++ struct au_dr_hino *found, *ent; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; ++ int idx; + + found = NULL; -+ k = br->br_dykey; -+ for (i = 0; i < AuBrDynOp; i++) -+ if (k[i]) { -+ if (k[i]->dk_op.dy_hop == h_op) { -+ found = k[i]; -+ break; -+ } -+ } else ++ 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; -+ if (!found) { -+ spin_lock(&br->br_dykey_lock); -+ for (; i < AuBrDynOp; i++) -+ if (k[i]) { -+ if (k[i]->dk_op.dy_hop == h_op) { -+ found = k[i]; -+ break; -+ } -+ } else { -+ k[i] = key; -+ break; -+ } -+ spin_unlock(&br->br_dykey_lock); -+ BUG_ON(i == AuBrDynOp); /* expand the array */ -+ } ++ } ++ hlist_bl_unlock(hbl); + + return found; +} + -+/* kref_get() if @key is already added */ -+static struct au_dykey *dy_gadd(struct au_splhead *spl, struct au_dykey *key) ++int au_dr_hino_test_add(struct au_dr_br *dr, ino_t ino, ++ struct au_dr_hino *add_ent) +{ -+ struct au_dykey *tmp, *found; -+ struct list_head *head; -+ const void *h_op = key->dk_op.dy_hop; ++ int found, idx; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; ++ struct au_dr_hino *ent; + -+ found = NULL; -+ head = &spl->head; -+ spin_lock(&spl->spin); -+ list_for_each_entry(tmp, head, dk_list) -+ if (tmp->dk_op.dy_hop == h_op) { -+ kref_get(&tmp->dk_kref); -+ found = tmp; ++ 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) -+ list_add_rcu(&key->dk_list, head); -+ spin_unlock(&spl->spin); ++ 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); + -+ if (!found) -+ DyPrSym(key); + return found; +} + -+static void dy_free_rcu(struct rcu_head *rcu) ++void au_dr_hino_free(struct au_dr_br *dr) +{ -+ struct au_dykey *key; -+ -+ key = container_of(rcu, struct au_dykey, dk_rcu); -+ DyPrSym(key); -+ kfree(key); -+} ++ int i; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos, *tmp; ++ struct au_dr_hino *ent; + -+static void dy_free(struct kref *kref) -+{ -+ struct au_dykey *key; -+ struct au_splhead *spl; ++ /* SiMustWriteLock(sb); */ + -+ key = container_of(kref, struct au_dykey, dk_kref); -+ spl = dynop + key->dk_op.dy_type; -+ au_spl_del_rcu(&key->dk_list, spl); -+ call_rcu(&key->dk_rcu, dy_free_rcu); ++ for (i = 0; i < AuDirren_NHASH; i++) { ++ hbl = dr->dr_h_ino + i; ++ /* no spinlock since sbinfo must be write-locked */ ++ hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode) ++ au_kfree_rcu(ent); ++ INIT_HLIST_BL_HEAD(hbl); ++ } +} + -+void au_dy_put(struct au_dykey *key) ++/* 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) +{ -+ kref_put(&key->dk_kref, dy_free); -+} -+ -+/* ---------------------------------------------------------------------- */ ++ int err, i; ++ ssize_t ssz; ++ loff_t pos, oldsize; ++ __be64 u64; ++ struct inode *hinoinode; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *n1, *n2; ++ struct au_dr_hino *ent; + -+#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *)) ++ SiMustWriteLock(sb); ++ AuDebugOn(!au_br_writable(br->br_perm)); + -+#ifdef CONFIG_AUFS_DEBUG -+#define DyDbgDeclare(cnt) unsigned int cnt = 0 -+#define DyDbgInc(cnt) do { cnt++; } while (0) -+#else -+#define DyDbgDeclare(cnt) do {} while (0) -+#define DyDbgInc(cnt) do {} while (0) -+#endif ++ hinoinode = file_inode(hinofile); ++ oldsize = i_size_read(hinoinode); + -+#define DySet(func, dst, src, h_op, h_sb) do { \ -+ DyDbgInc(cnt); \ -+ if (h_op->func) { \ -+ if (src.func) \ -+ dst.func = src.func; \ -+ else \ -+ AuDbg("%s %s\n", au_sbtype(h_sb), #func); \ -+ } \ -+} while (0) ++ 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; + -+#define DySetForce(func, dst, src) do { \ -+ AuDebugOn(!src.func); \ -+ DyDbgInc(cnt); \ -+ dst.func = src.func; \ -+} while (0) ++ /* 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); ++ } + -+#define DySetAop(func) \ -+ DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb) -+#define DySetAopForce(func) \ -+ DySetForce(func, dyaop->da_op, aufs_aop) ++ AuTraceErr(err); ++ return err; ++} + -+static void dy_aop(struct au_dykey *key, const void *h_op, -+ struct super_block *h_sb __maybe_unused) ++static int au_dr_hino_load(struct au_dr_br *dr, struct file *hinofile) +{ -+ struct au_dyaop *dyaop = (void *)key; -+ const struct address_space_operations *h_aop = h_op; -+ DyDbgDeclare(cnt); ++ 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; + -+ AuDbg("%s\n", au_sbtype(h_sb)); ++ 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; ++ } + -+ DySetAop(writepage); -+ DySetAopForce(readpage); /* force */ -+ DySetAop(writepages); -+ DySetAop(set_page_dirty); -+ DySetAop(readpages); -+ DySetAop(write_begin); -+ DySetAop(write_end); -+ DySetAop(bmap); -+ DySetAop(invalidatepage); -+ DySetAop(releasepage); -+ DySetAop(freepage); -+ /* this one will be changed according to an aufs mount option */ -+ DySetAop(direct_IO); -+ DySetAop(migratepage); -+ DySetAop(launder_page); -+ DySetAop(is_partially_uptodate); -+ DySetAop(is_dirty_writeback); -+ DySetAop(error_remove_page); -+ DySetAop(swap_activate); -+ DySetAop(swap_deactivate); ++ ent = kmalloc(sizeof(*ent), GFP_NOFS); ++ if (!ent) { ++ err = -ENOMEM; ++ AuTraceErr(err); ++ goto out_free; ++ } ++ ent->dr_h_ino = be64_to_cpu((__force __be64)u64); ++ AuDbg("hi%llu, %pD2\n", ++ (unsigned long long)ent->dr_h_ino, hinofile); ++ hidx = au_dr_ihash(ent->dr_h_ino); ++ au_hbl_add(&ent->dr_hnode, hbl + hidx); ++ } ++ goto out; /* success */ + -+ DyDbgSize(cnt, *h_aop); ++out_free: ++ au_dr_hino_free(dr); ++out: ++ AuTraceErr(err); ++ return err; +} + -+/* ---------------------------------------------------------------------- */ -+ -+static void dy_bug(struct kref *kref) ++/* ++ * @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) +{ -+ BUG(); -+} ++ 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); + -+static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) -+{ -+ struct au_dykey *key, *old; -+ struct au_splhead *spl; -+ struct op { -+ unsigned int sz; -+ void (*set)(struct au_dykey *key, const void *h_op, -+ struct super_block *h_sb __maybe_unused); -+ }; -+ static const struct op a[] = { -+ [AuDy_AOP] = { -+ .sz = sizeof(struct au_dyaop), -+ .set = dy_aop -+ } -+ }; -+ const struct op *p; ++ AuDebugOn(bindex < 0 && !br); ++ AuDebugOn(bindex >= 0 && br); + -+ spl = dynop + op->dy_type; -+ key = dy_gfind_get(spl, op->dy_hop); -+ if (key) -+ goto out_add; /* success */ ++ 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; ++ } + -+ p = a + op->dy_type; -+ key = kzalloc(p->sz, GFP_NOFS); -+ if (unlikely(!key)) { -+ key = ERR_PTR(-ENOMEM); -+ 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; + -+ 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(spl, key); -+ if (old) { -+ kfree(key); -+ key = old; ++ 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; + } + -+out_add: -+ old = dy_bradd(br, key); -+ if (old) -+ /* its ref-count should never be zero here */ -+ kref_put(&key->dk_kref, dy_bug); ++ 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: -+ return key; ++ AuTraceErr(err); ++ return err; +} + +/* ---------------------------------------------------------------------- */ -+/* -+ * Aufs prohibits O_DIRECT by defaut even if the branch supports it. -+ * This behaviour is necessary to return an error from open(O_DIRECT) instead -+ * of the succeeding I/O. The dio mount option enables O_DIRECT and makes -+ * open(O_DIRECT) always succeed, but the succeeding I/O may return an error. -+ * See the aufs manual in detail. -+ */ -+static void dy_adx(struct au_dyaop *dyaop, int do_dx) -+{ -+ if (!do_dx) -+ dyaop->da_op.direct_IO = NULL; -+ else -+ dyaop->da_op.direct_IO = aufs_aop.direct_IO; -+} + -+static struct au_dyaop *dy_aget(struct au_branch *br, -+ const struct address_space_operations *h_aop, -+ int do_dx) ++static int au_dr_brid_init(struct au_dr_brid *brid, const struct path *path) +{ -+ struct au_dyaop *dyaop; -+ struct au_dynop op; ++ int err; ++ struct kstatfs kstfs; ++ dev_t dev; ++ struct dentry *dentry; ++ struct super_block *sb; + -+ op.dy_type = AuDy_AOP; -+ op.dy_haop = h_aop; -+ dyaop = (void *)dy_get(&op, br); -+ if (IS_ERR(dyaop)) ++ err = vfs_statfs((void *)path, &kstfs); ++ AuTraceErr(err); ++ if (unlikely(err)) + goto out; -+ dy_adx(dyaop, do_dx); ++ ++ /* 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 dyaop; ++ return err; +} + -+int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode) ++int au_dr_br_init(struct super_block *sb, struct au_branch *br, ++ const struct path *path) +{ -+ int err, do_dx; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_dyaop *dyaop; ++ int err, i; ++ struct au_dr_br *dr; ++ struct hlist_bl_head *hbl; + -+ AuDebugOn(!S_ISREG(h_inode->i_mode)); -+ IiMustWriteLock(inode); ++ dr = &br->br_dirren; ++ hbl = dr->dr_h_ino; ++ for (i = 0; i < AuDirren_NHASH; i++, hbl++) ++ INIT_HLIST_BL_HEAD(hbl); + -+ sb = inode->i_sb; -+ br = au_sbr(sb, bindex); -+ do_dx = !!au_opt_test(au_mntflags(sb), DIO); ++ 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.b); ++ 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 = (__force uint64_t)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((__force __be64)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((__force __be32)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((__force __be64)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)) { ++ au_kfree_rcu(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 generated 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 = (__force uint32_t)htonl(AUFS_DRINFO_MAGIC_V1); ++ err = 0; ++ ++out: ++ return err; ++} ++ ++static void au_drinfo_store_work_fin(struct au_drinfo_store *w) ++{ ++ au_kfree_rcu(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); ++ au_kfree_rcu(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((__force __be64)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); ++ au_kfree_try_rcu(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); ++ au_kfree_rcu(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); ++ au_kfree_rcu(elm->info_last); ++ } ++ au_kfree_try_rcu(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); ++ au_kfree_rcu(ent); ++ ++out: ++ au_kfree_try_rcu(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); ++ 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 */ ++ au_kfree_rcu(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); ++ } ++ au_kfree_rcu(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) ++ au_kfree_rcu(*drinfo++); ++ au_kfree_try_rcu(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); ++ au_kfree_rcu(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; ++ ++ au_kfree_try_rcu(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-08-12 23:43:05.453457863 +0200 +@@ -0,0 +1,140 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * 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 ++#include "hbl.h" ++ ++#define AuDirren_NHASH 100 ++ ++#ifdef CONFIG_AUFS_DIRREN ++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 + UUID_STRING_LEN + 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 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,370 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * 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 ++ * 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 . ++ */ ++ ++/* ++ * dynamically customizable operations for regular files ++ */ ++ ++#include "aufs.h" ++ ++#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop) ++ ++/* ++ * How large will these lists be? ++ * Usually just a few elements, 20-30 at most for each, I guess. ++ */ ++static struct hlist_bl_head dynop[AuDyLast]; ++ ++static struct au_dykey *dy_gfind_get(struct hlist_bl_head *hbl, ++ const void *h_op) ++{ ++ struct au_dykey *key, *tmp; ++ struct hlist_bl_node *pos; ++ ++ key = NULL; ++ 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; ++ } ++ hlist_bl_unlock(hbl); ++ ++ return key; ++} ++ ++static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key) ++{ ++ struct au_dykey **k, *found; ++ const void *h_op = key->dk_op.dy_hop; ++ int i; ++ ++ found = NULL; ++ k = br->br_dykey; ++ for (i = 0; i < AuBrDynOp; i++) ++ if (k[i]) { ++ if (k[i]->dk_op.dy_hop == h_op) { ++ found = k[i]; ++ break; ++ } ++ } else ++ break; ++ if (!found) { ++ spin_lock(&br->br_dykey_lock); ++ for (; i < AuBrDynOp; i++) ++ if (k[i]) { ++ if (k[i]->dk_op.dy_hop == h_op) { ++ found = k[i]; ++ break; ++ } ++ } else { ++ k[i] = key; ++ break; ++ } ++ spin_unlock(&br->br_dykey_lock); ++ BUG_ON(i == AuBrDynOp); /* expand the array */ ++ } ++ ++ return found; ++} ++ ++/* kref_get() if @key is already added */ ++static struct au_dykey *dy_gadd(struct hlist_bl_head *hbl, struct au_dykey *key) ++{ ++ struct au_dykey *tmp, *found; ++ struct hlist_bl_node *pos; ++ const void *h_op = key->dk_op.dy_hop; ++ ++ found = NULL; ++ 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_bl_add_head(&key->dk_hnode, hbl); ++ hlist_bl_unlock(hbl); ++ ++ if (!found) ++ DyPrSym(key); ++ return found; ++} ++ ++static void dy_free_rcu(struct rcu_head *rcu) ++{ ++ struct au_dykey *key; ++ ++ key = container_of(rcu, struct au_dykey, dk_rcu); ++ DyPrSym(key); ++ au_kfree_rcu(key); ++} ++ ++static void dy_free(struct kref *kref) ++{ ++ struct au_dykey *key; ++ struct hlist_bl_head *hbl; ++ ++ key = container_of(kref, struct au_dykey, dk_kref); ++ hbl = dynop + key->dk_op.dy_type; ++ au_hbl_del(&key->dk_hnode, hbl); ++ call_rcu(&key->dk_rcu, dy_free_rcu); ++} ++ ++void au_dy_put(struct au_dykey *key) ++{ ++ kref_put(&key->dk_kref, dy_free); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *)) ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define DyDbgDeclare(cnt) unsigned int cnt = 0 ++#define DyDbgInc(cnt) do { cnt++; } while (0) ++#else ++#define DyDbgDeclare(cnt) do {} while (0) ++#define DyDbgInc(cnt) do {} while (0) ++#endif ++ ++#define DySet(func, dst, src, h_op, h_sb) do { \ ++ DyDbgInc(cnt); \ ++ if (h_op->func) { \ ++ if (src.func) \ ++ dst.func = src.func; \ ++ else \ ++ AuDbg("%s %s\n", au_sbtype(h_sb), #func); \ ++ } \ ++} while (0) ++ ++#define DySetForce(func, dst, src) do { \ ++ AuDebugOn(!src.func); \ ++ DyDbgInc(cnt); \ ++ dst.func = src.func; \ ++} while (0) ++ ++#define DySetAop(func) \ ++ DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb) ++#define DySetAopForce(func) \ ++ DySetForce(func, dyaop->da_op, aufs_aop) ++ ++static void dy_aop(struct au_dykey *key, const void *h_op, ++ struct super_block *h_sb __maybe_unused) ++{ ++ struct au_dyaop *dyaop = (void *)key; ++ const struct address_space_operations *h_aop = h_op; ++ DyDbgDeclare(cnt); ++ ++ AuDbg("%s\n", au_sbtype(h_sb)); ++ ++ DySetAop(writepage); ++ DySetAopForce(readpage); /* force */ ++ DySetAop(writepages); ++ DySetAop(set_page_dirty); ++ DySetAop(readpages); ++ DySetAop(write_begin); ++ DySetAop(write_end); ++ DySetAop(bmap); ++ DySetAop(invalidatepage); ++ DySetAop(releasepage); ++ DySetAop(freepage); ++ /* this one will be changed according to an aufs mount option */ ++ DySetAop(direct_IO); ++ DySetAop(migratepage); ++ DySetAop(isolate_page); ++ DySetAop(putback_page); ++ DySetAop(launder_page); ++ DySetAop(is_partially_uptodate); ++ DySetAop(is_dirty_writeback); ++ DySetAop(error_remove_page); ++ DySetAop(swap_activate); ++ DySetAop(swap_deactivate); ++ ++ DyDbgSize(cnt, *h_aop); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void dy_bug(struct kref *kref) ++{ ++ BUG(); ++} ++ ++static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) ++{ ++ struct au_dykey *key, *old; ++ struct hlist_bl_head *hbl; ++ struct op { ++ unsigned int sz; ++ void (*set)(struct au_dykey *key, const void *h_op, ++ struct super_block *h_sb __maybe_unused); ++ }; ++ static const struct op a[] = { ++ [AuDy_AOP] = { ++ .sz = sizeof(struct au_dyaop), ++ .set = dy_aop ++ } ++ }; ++ const struct op *p; ++ ++ hbl = dynop + op->dy_type; ++ key = dy_gfind_get(hbl, op->dy_hop); ++ if (key) ++ goto out_add; /* success */ ++ ++ p = a + op->dy_type; ++ key = kzalloc(p->sz, GFP_NOFS); ++ if (unlikely(!key)) { ++ key = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ key->dk_op.dy_hop = op->dy_hop; ++ kref_init(&key->dk_kref); ++ p->set(key, op->dy_hop, au_br_sb(br)); ++ old = dy_gadd(hbl, key); ++ if (old) { ++ au_kfree_rcu(key); ++ key = old; ++ } ++ ++out_add: ++ old = dy_bradd(br, key); ++ if (old) ++ /* its ref-count should never be zero here */ ++ kref_put(&key->dk_kref, dy_bug); ++out: ++ return key; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * Aufs prohibits O_DIRECT by default even if the branch supports it. ++ * This behaviour is necessary to return an error from open(O_DIRECT) instead ++ * of the succeeding I/O. The dio mount option enables O_DIRECT and makes ++ * open(O_DIRECT) always succeed, but the succeeding I/O may return an error. ++ * See the aufs manual in detail. ++ */ ++static void dy_adx(struct au_dyaop *dyaop, int do_dx) ++{ ++ if (!do_dx) ++ dyaop->da_op.direct_IO = NULL; ++ else ++ dyaop->da_op.direct_IO = aufs_aop.direct_IO; ++} ++ ++static struct au_dyaop *dy_aget(struct au_branch *br, ++ const struct address_space_operations *h_aop, ++ int do_dx) ++{ ++ struct au_dyaop *dyaop; ++ struct au_dynop op; ++ ++ op.dy_type = AuDy_AOP; ++ op.dy_haop = h_aop; ++ dyaop = (void *)dy_get(&op, br); ++ if (IS_ERR(dyaop)) ++ goto out; ++ dy_adx(dyaop, do_dx); ++ ++out: ++ return dyaop; ++} ++ ++int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode) ++{ ++ int err, do_dx; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_dyaop *dyaop; ++ ++ AuDebugOn(!S_ISREG(h_inode->i_mode)); ++ IiMustWriteLock(inode); ++ ++ sb = inode->i_sb; ++ br = au_sbr(sb, bindex); ++ do_dx = !!au_opt_test(au_mntflags(sb), DIO); + dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx); + err = PTR_ERR(dyaop); + if (IS_ERR(dyaop)) @@ -10570,30 +12834,29 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c +int au_dy_irefresh(struct inode *inode) +{ + int err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + struct inode *h_inode; + + err = 0; + if (S_ISREG(inode->i_mode)) { -+ bstart = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bstart); -+ err = au_dy_iaop(inode, bstart, h_inode); ++ btop = au_ibtop(inode); ++ h_inode = au_h_iptr(inode, btop); ++ err = au_dy_iaop(inode, btop, h_inode); + } + return err; +} + +void au_dy_arefresh(int do_dx) +{ -+ struct au_splhead *spl; -+ struct list_head *head; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; + struct au_dykey *key; + -+ spl = dynop + AuDy_AOP; -+ head = &spl->head; -+ spin_lock(&spl->spin); -+ list_for_each_entry(key, head, dk_list) ++ 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(&spl->spin); ++ hlist_bl_unlock(hbl); +} + +/* ---------------------------------------------------------------------- */ @@ -10606,7 +12869,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_spl_init(dynop + i); ++ INIT_HLIST_BL_HEAD(dynop + i); +} + +void au_dy_fin(void) @@ -10614,14 +12877,15 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c + int i; + + for (i = 0; i < AuDyLast; i++) -+ WARN_ON(!list_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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,74 @@ ++++ linux/fs/aufs/dynop.h 2018-08-12 23:43:05.456791299 +0200 +@@ -0,0 +1,75 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2010-2015 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 @@ -10661,7 +12925,7 @@ diff -urN /usr/share/empty/fs/aufs/dynop.h linux/fs/aufs/dynop.h + +struct au_dykey { + union { -+ struct list_head dk_list; ++ struct hlist_bl_node dk_hnode; + struct rcu_head dk_rcu; + }; + struct au_dynop dk_op; @@ -10696,10 +12960,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,832 @@ ++++ linux/fs/aufs/export.c 2018-10-23 12:33:35.596042364 +0200 +@@ -0,0 +1,838 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -10725,7 +12990,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 { @@ -10882,7 +13146,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + return err; +} + -+int au_xigen_set(struct super_block *sb, struct file *base) ++int au_xigen_set(struct super_block *sb, struct path *path) +{ + int err; + struct au_sbinfo *sbinfo; @@ -10891,7 +13155,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); -+ file = au_xino_create2(base, sbinfo->si_xigen); ++ file = au_xino_create2(sb, path, sbinfo->si_xigen); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; @@ -10901,6 +13165,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + sbinfo->si_xigen = file; + +out: ++ AuTraceErr(err); + return err; +} + @@ -10933,7 +13198,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + + dentry = ERR_PTR(-ESTALE); + sigen = au_sigen(sb); -+ if (unlikely(is_bad_inode(inode) ++ if (unlikely(au_is_bad_inode(inode) + || IS_DEADDIR(inode) + || sigen != au_iigen(inode, NULL))) + goto out_iput; @@ -11106,9 +13371,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + + /* do not call vfsub_lkup_one() */ + dir = d_inode(parent); -+ mutex_lock(&dir->i_mutex); -+ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); -+ mutex_unlock(&dir->i_mutex); ++ dentry = vfsub_lookup_one_len_unlocked(arg.name, parent, arg.namelen); + AuTraceErrPtr(dentry); + if (IS_ERR(dentry)) + goto out_name; @@ -11215,9 +13478,11 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + h_mnt = au_br_mnt(br); + h_sb = h_mnt->mnt_sb; + /* todo: call lower fh_to_dentry()? fh_to_parent()? */ ++ lockdep_off(); + h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), + fh_len - Fh_tail, fh[Fh_h_type], + h_acceptable, /*context*/NULL); ++ lockdep_on(); + dentry = h_parent; + if (unlikely(!h_parent || IS_ERR(h_parent))) { + AuWarn1("%s decode_fh failed, %ld\n", @@ -11310,7 +13575,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + ino = decode_ino(fh + Fh_ino); + /* it should never happen */ + if (unlikely(ino == AUFS_ROOT_INO)) -+ goto out; ++ goto out_unlock; + + dir_ino = decode_ino(fh + Fh_dir_ino); + dentry = decode_by_ino(sb, ino, dir_ino); @@ -11321,7 +13586,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + + /* is the parent dir cached? */ + br = au_sbr(sb, nsi_lock.bindex); -+ atomic_inc(&br->br_count); ++ au_lcnt_inc(&br->br_nfiles); + dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); + if (IS_ERR(dentry)) + goto out_unlock; @@ -11345,7 +13610,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + dentry = ERR_PTR(-ESTALE); +out_unlock: + if (br) -+ atomic_dec(&br->br_count); ++ au_lcnt_dec(&br->br_nfiles); + si_read_unlock(sb); +out: + AuTraceErrPtr(dentry); @@ -11412,7 +13677,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + err = -EIO; + parent = NULL; + ii_read_lock_child(inode); -+ bindex = au_ibstart(inode); ++ bindex = au_ibtop(inode); + if (!dir) { + dentry = d_find_any_alias(inode); + if (unlikely(!dentry)) @@ -11487,7 +13752,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + ii_write_lock_child(inode); -+ bindex = au_ibstart(inode); ++ bindex = au_ibtop(inode); + AuDebugOn(bindex < 0); + h_inode = au_h_iptr(inode, bindex); + @@ -11523,6 +13788,11 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c + struct au_sbinfo *sbinfo; + __u32 u; + ++ BUILD_BUG_ON_MSG(IS_BUILTIN(CONFIG_AUFS_FS) ++ && IS_MODULE(CONFIG_EXPORTFS), ++ AUFS_NAME ": unsupported configuration " ++ "CONFIG_EXPORTFS=m and CONFIG_AUFS_FS=y"); ++ + sb->s_export_op = &aufs_export_op; + sbinfo = au_sbi(sb); + sbinfo->si_xigen = NULL; @@ -11532,10 +13802,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,426 @@ ++++ linux/fs/aufs/fhsm.c 2018-10-23 12:33:35.596042364 +0200 +@@ -0,0 +1,427 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2011-2015 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 @@ -11691,12 +13962,12 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c + +void au_fhsm_wrote_all(struct super_block *sb, int force) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_branch *br; + + /* exclude the bottom */ -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; bindex < bend; bindex++) { ++ bbot = au_fhsm_bottom(sb); ++ for (bindex = 0; bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) + au_fhsm_wrote(sb, bindex, force); @@ -11705,10 +13976,9 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c + +/* ---------------------------------------------------------------------- */ + -+static unsigned int au_fhsm_poll(struct file *file, -+ struct poll_table_struct *wait) ++static __poll_t au_fhsm_poll(struct file *file, struct poll_table_struct *wait) +{ -+ unsigned int mask; ++ __poll_t mask; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + @@ -11717,9 +13987,10 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c + fhsm = &sbinfo->si_fhsm; + poll_wait(file, &fhsm->fhsm_wqh, wait); + if (atomic_read(&fhsm->fhsm_readable)) -+ mask = POLLIN /* | POLLRDNORM */; ++ mask = EPOLLIN /* | EPOLLRDNORM */; + -+ AuTraceErr((int)mask); ++ if (!mask) ++ AuDbg("mask 0x%x\n", mask); + return mask; +} + @@ -11742,15 +14013,15 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c +{ + ssize_t err; + int nstbr; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_branch *br; + struct au_br_fhsm *bf; + + /* except the bottom branch */ + err = 0; + nstbr = 0; -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; !err && bindex < bend; bindex++) { ++ bbot = au_fhsm_bottom(sb); ++ for (bindex = 0; !err && bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_fhsm(br->br_perm)) + continue; @@ -11781,7 +14052,7 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c +{ + ssize_t err; + int readable; -+ aufs_bindex_t nfhsm, bindex, bend; ++ aufs_bindex_t nfhsm, bindex, bbot; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + struct au_branch *br; @@ -11812,8 +14083,8 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c + AuDebugOn(!sb); + /* exclude the bottom branch */ + nfhsm = 0; -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; bindex < bend; bindex++) { ++ bbot = au_fhsm_bottom(sb); ++ for (bindex = 0; bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) + nfhsm++; @@ -11890,7 +14161,7 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c + if (unlikely(fd < 0)) + goto out_pid; + -+ /* succeed reglardless 'fhsm' status */ ++ /* succeed regardless 'fhsm' status */ + kobject_get(&sbinfo->si_kobj); + si_noflush_read_lock(sb); + if (au_ftest_si(sbinfo, FHSM)) @@ -11962,10 +14233,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,844 @@ ++++ linux/fs/aufs/file.c 2018-10-23 12:33:35.596042364 +0200 +@@ -0,0 +1,863 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -12048,7 +14320,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + } + } + flags &= ~O_CREAT; -+ atomic_inc(&br->br_count); ++ au_lcnt_inc(&br->br_nfiles); + h_path.dentry = h_dentry; + h_path.mnt = au_br_mnt(br); + h_file = vfsub_dentry_open(&h_path, flags); @@ -12067,14 +14339,14 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + goto out; /* success */ + +out_br: -+ atomic_dec(&br->br_count); ++ au_lcnt_dec(&br->br_nfiles); +out: + return h_file; +} + +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; @@ -12101,7 +14373,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + err = 0; + if (IS_ROOT(dentry)) + goto out; -+ cpg.bsrc = au_dbstart(dentry); ++ cpg.bsrc = au_dbtop(dentry); + if (!cpg.bsrc) + goto out; + @@ -12109,9 +14381,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); @@ -12188,11 +14463,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(); @@ -12204,33 +14479,28 @@ 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) ++ di_write_lock_child(dentry); ++ err = au_cmoo(dentry); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (!err) { ++ if (!aopen) + err = args->open(file, vfsub_file_flags(file), NULL); -+ di_read_unlock(dentry, AuLock_IR); -+ } else { -+ err = au_cmoo(dentry); -+ if (!err) ++ else { ++ lockdep_off(); + err = args->open(file, vfsub_file_flags(file), + args->h_file); -+ if (!err && au_fbstart(file) != au_dbstart(dentry)) -+ /* -+ * cmoo happens after h_file was opened. -+ * need to refresh file later. -+ */ -+ atomic_dec(&au_fi(file)->fi_generation); ++ lockdep_on(); ++ } + } ++ 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(); @@ -12243,62 +14513,68 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + } + +out: ++ AuTraceErr(err); + return err; +} + +int au_reopen_nondir(struct file *file) +{ + int err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + struct dentry *dentry; ++ struct au_branch *br; + struct file *h_file, *h_file_tmp; + + dentry = file->f_path.dentry; -+ bstart = au_dbstart(dentry); ++ btop = au_dbtop(dentry); ++ br = au_sbr(dentry->d_sb, btop); + h_file_tmp = NULL; -+ if (au_fbstart(file) == bstart) { ++ if (au_fbtop(file) == btop) { + h_file = au_hf_top(file); + if (file->f_mode == h_file->f_mode) + return 0; /* success */ + h_file_tmp = h_file; + get_file(h_file_tmp); -+ au_set_h_fptr(file, bstart, NULL); ++ au_lcnt_inc(&br->br_nfiles); ++ au_set_h_fptr(file, btop, NULL); + } + AuDebugOn(au_fi(file)->fi_hdir); + /* + * it can happen + * file exists on both of rw and ro -+ * open --> dbstart and fbstart are both 0 ++ * open --> dbtop and fbtop are both 0 + * prepend a branch as rw, "rw" become ro + * remove rw/file + * delete the top branch, "rw" becomes rw again -+ * --> dbstart is 1, fbstart is still 0 -+ * write --> fbstart is 0 but dbstart is 1 ++ * --> dbtop is 1, fbtop is still 0 ++ * write --> fbtop is 0 but dbtop is 1 + */ -+ /* AuDebugOn(au_fbstart(file) < bstart); */ ++ /* AuDebugOn(au_fbtop(file) < btop); */ + -+ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, ++ h_file = au_h_open(dentry, btop, vfsub_file_flags(file) & ~O_TRUNC, + file, /*force_wr*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) { + if (h_file_tmp) { -+ atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); -+ au_set_h_fptr(file, bstart, h_file_tmp); ++ /* revert */ ++ au_set_h_fptr(file, btop, h_file_tmp); + h_file_tmp = NULL; + } + goto out; /* todo: close all? */ + } + + err = 0; -+ au_set_fbstart(file, bstart); -+ au_set_h_fptr(file, bstart, h_file); ++ au_set_fbtop(file, btop); ++ au_set_h_fptr(file, btop, h_file); + au_update_figen(file); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + +out: -+ if (h_file_tmp) ++ if (h_file_tmp) { + fput(h_file_tmp); ++ au_lcnt_dec(&br->br_nfiles); ++ } + return err; +} + @@ -12308,7 +14584,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + struct dentry *hi_wh) +{ + int err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + struct au_dinfo *dinfo; + struct dentry *h_dentry; + struct au_hdentry *hdp; @@ -12316,14 +14592,14 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + dinfo = au_di(file->f_path.dentry); + AuRwMustWriteLock(&dinfo->di_rwsem); + -+ bstart = dinfo->di_bstart; -+ dinfo->di_bstart = btgt; -+ hdp = dinfo->di_hdentry; -+ h_dentry = hdp[0 + btgt].hd_dentry; -+ hdp[0 + btgt].hd_dentry = hi_wh; ++ btop = dinfo->di_btop; ++ dinfo->di_btop = btgt; ++ hdp = au_hdentry(dinfo, btgt); ++ h_dentry = hdp->hd_dentry; ++ hdp->hd_dentry = hi_wh; + err = au_reopen_nondir(file); -+ hdp[0 + btgt].hd_dentry = h_dentry; -+ dinfo->di_bstart = bstart; ++ hdp->hd_dentry = h_dentry; ++ dinfo->di_btop = btop; + + return err; +} @@ -12342,11 +14618,11 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + .pin = pin + }; + -+ au_update_dbstart(cpg.dentry); ++ au_update_dbtop(cpg.dentry); + inode = d_inode(cpg.dentry); + h_inode = NULL; -+ if (au_dbstart(cpg.dentry) <= bcpup -+ && au_dbend(cpg.dentry) >= bcpup) { ++ if (au_dbtop(cpg.dentry) <= bcpup ++ && au_dbbot(cpg.dentry) >= bcpup) { + h_dentry = au_h_dptr(cpg.dentry, bcpup); + if (h_dentry && d_is_positive(h_dentry)) + h_inode = d_inode(h_dentry); @@ -12373,7 +14649,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c +int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) +{ + int err; -+ aufs_bindex_t dbstart; ++ aufs_bindex_t dbtop; + struct dentry *parent; + struct inode *inode; + struct super_block *sb; @@ -12389,7 +14665,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + + sb = cpg.dentry->d_sb; + inode = d_inode(cpg.dentry); -+ cpg.bsrc = au_fbstart(file); ++ cpg.bsrc = au_fbtop(file); + err = au_test_ro(sb, cpg.bsrc, inode); + if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { + err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, @@ -12417,11 +14693,11 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + if (unlikely(err)) + goto out_dgrade; + -+ dbstart = au_dbstart(cpg.dentry); -+ if (dbstart <= cpg.bdst) ++ dbtop = au_dbtop(cpg.dentry); ++ if (dbtop <= cpg.bdst) + cpg.bsrc = cpg.bdst; + -+ if (dbstart <= cpg.bdst /* just reopen */ ++ if (dbtop <= cpg.bdst /* just reopen */ + || !d_unhashed(cpg.dentry) /* copyup and reopen */ + ) { + h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); @@ -12429,7 +14705,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + err = PTR_ERR(h_file); + else { + di_downgrade_lock(parent, AuLock_IR); -+ if (dbstart > cpg.bdst) ++ if (dbtop > cpg.bdst) + err = au_sio_cpup_simple(&cpg); + if (!err) + err = au_reopen_nondir(file); @@ -12510,7 +14786,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + finfo = au_fi(file); + sb = cpg.dentry->d_sb; + inode = d_inode(cpg.dentry); -+ cpg.bdst = au_ibstart(inode); ++ cpg.bdst = au_ibtop(inode); + if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) + goto out; + @@ -12531,7 +14807,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + && au_opt_test(au_mntflags(sb), PLINK) + && au_plink_test(inode) + && !d_unhashed(cpg.dentry) -+ && cpg.bdst < au_dbstart(cpg.dentry)) { ++ && cpg.bdst < au_dbtop(cpg.dentry)) { + err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); + if (unlikely(err)) + goto out_unlock; @@ -12559,7 +14835,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + +static void au_do_refresh_dir(struct file *file) +{ -+ aufs_bindex_t bindex, bend, new_bindex, brid; ++ aufs_bindex_t bindex, bbot, new_bindex, brid; + struct au_hfile *p, tmp, *q; + struct au_finfo *finfo; + struct super_block *sb; @@ -12573,8 +14849,8 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + AuDebugOn(!fidir); + p = fidir->fd_hfile + finfo->fi_btop; + brid = p->hf_br->br_id; -+ bend = fidir->fd_bbot; -+ for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { ++ bbot = fidir->fd_bbot; ++ for (bindex = finfo->fi_btop; bindex <= bbot; bindex++, p++) { + if (!p->hf_file) + continue; + @@ -12599,30 +14875,30 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + + p = fidir->fd_hfile; + if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) { -+ bend = au_sbend(sb); -+ for (finfo->fi_btop = 0; finfo->fi_btop <= bend; ++ bbot = au_sbbot(sb); ++ for (finfo->fi_btop = 0; finfo->fi_btop <= bbot; + finfo->fi_btop++, p++) + if (p->hf_file) { + if (file_inode(p->hf_file)) + break; -+ au_hfput(p, file); ++ au_hfput(p, /*execed*/0); + } + } else { -+ bend = au_br_index(sb, brid); -+ for (finfo->fi_btop = 0; finfo->fi_btop < bend; ++ 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, file); -+ bend = au_sbend(sb); ++ au_hfput(p, /*execed*/0); ++ bbot = au_sbbot(sb); + } + -+ p = fidir->fd_hfile + bend; -+ for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; ++ p = fidir->fd_hfile + bbot; ++ for (fidir->fd_bbot = bbot; fidir->fd_bbot >= finfo->fi_btop; + fidir->fd_bbot--, p--) + if (p->hf_file) { + if (file_inode(p->hf_file)) + break; -+ au_hfput(p, file); ++ au_hfput(p, /*execed*/0); + } + AuDebugOn(fidir->fd_bbot < finfo->fi_btop); +} @@ -12632,23 +14908,26 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + */ +static int refresh_file(struct file *file, int (*reopen)(struct file *file)) +{ -+ int err, need_reopen; -+ aufs_bindex_t bend, bindex; ++ int err, need_reopen, nbr; ++ aufs_bindex_t bbot, bindex; + struct dentry *dentry; ++ struct super_block *sb; + struct au_finfo *finfo; + struct au_hfile *hfile; + + dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ nbr = au_sbbot(sb) + 1; + finfo = au_fi(file); + if (!finfo->fi_hdir) { + hfile = &finfo->fi_htop; + AuDebugOn(!hfile->hf_file); -+ bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id); ++ bindex = au_br_index(sb, hfile->hf_br->br_id); + AuDebugOn(bindex < 0); + if (bindex != finfo->fi_btop) -+ au_set_fbstart(file, bindex); ++ au_set_fbtop(file, bindex); + } else { -+ err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1); ++ err = au_fidir_realloc(finfo, nbr, /*may_shrink*/0); + if (unlikely(err)) + goto out; + au_do_refresh_dir(file); @@ -12658,6 +14937,9 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + need_reopen = 1; + if (!au_test_mmapped(file)) + err = au_file_refresh_by_inode(file, &need_reopen); ++ if (finfo->fi_hdir) ++ /* harmless if err */ ++ au_fidir_realloc(finfo, nbr, /*may_shrink*/1); + if (!err && need_reopen && !d_unlinked(dentry)) + err = reopen(file); + if (!err) { @@ -12667,8 +14949,8 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + + /* error, close all lower files */ + if (finfo->fi_hdir) { -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); bindex <= bend; bindex++) ++ bbot = au_fbbot_dir(file); ++ for (bindex = au_fbtop(file); bindex <= bbot; bindex++) + au_set_h_fptr(file, bindex, NULL); + } + @@ -12678,11 +14960,11 @@ 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; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + unsigned char pseudo_link; + struct dentry *dentry; + struct inode *inode; @@ -12691,12 +14973,15 @@ 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); -+ bstart = au_dbstart(dentry); -+ pseudo_link = (bstart != au_ibstart(inode)); -+ if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { ++ 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) { + if (!wlock) { + di_downgrade_lock(dentry, AuLock_IR); + fi_downgrade_lock(file); @@ -12737,8 +15022,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c +} + +/* it will never be called, but necessary to support O_DIRECT */ -+static ssize_t aufs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, -+ loff_t offset) ++static ssize_t aufs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) +{ BUG(); return 0; } + +/* they will never be called. */ @@ -12766,6 +15050,10 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + struct page *page, enum migrate_mode mode) +{ AuUnsupport(); return 0; } +#endif ++static bool aufs_isolate_page(struct page *page, isolate_mode_t mode) ++{ AuUnsupport(); return true; } ++static void aufs_putback_page(struct page *page) ++{ AuUnsupport(); } +static int aufs_launder_page(struct page *page) +{ AuUnsupport(); return 0; } +static int aufs_is_partially_uptodate(struct page *page, @@ -12800,6 +15088,8 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c + .releasepage = aufs_releasepage, + /* is fallback_migrate_page ok? */ + /* .migratepage = aufs_migratepage, */ ++ .isolate_page = aufs_isolate_page, ++ .putback_page = aufs_putback_page, + .launder_page = aufs_launder_page, + .is_partially_uptodate = aufs_is_partially_uptodate, + .is_dirty_writeback = aufs_is_dirty_writeback, @@ -12810,10 +15100,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,291 @@ ++++ linux/fs/aufs/file.h 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,342 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -12840,6 +15131,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h + +#include +#include ++#include +#include +#include "rwsem.h" + @@ -12876,8 +15168,9 @@ 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; ++ struct hlist_bl_node fi_hlist; + struct file *fi_file; /* very ugly */ ++ struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ @@ -12888,7 +15181,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; @@ -12899,13 +15192,13 @@ 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)); + +/* poll.c */ +#ifdef CONFIG_AUFS_POLL -+unsigned int aufs_poll(struct file *file, poll_table *wait); ++__poll_t aufs_poll(struct file *file, struct poll_table_struct *pt); +#endif + +#ifdef CONFIG_AUFS_BR_HFSPLUS @@ -12925,16 +15218,16 @@ 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, struct file *file); ++void au_hfput(struct au_hfile *hf, int execed); +void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, + struct file *h_file); + +void au_update_figen(struct file *file); +struct au_fidir *au_fidir_alloc(struct super_block *sb); -+int au_fidir_realloc(struct au_finfo *finfo, int nbr); ++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); @@ -12958,11 +15251,59 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h + +/* ---------------------------------------------------------------------- */ + ++#define fi_read_lock(f) au_rw_read_lock(&au_fi(f)->fi_rwsem) ++#define fi_write_lock(f) au_rw_write_lock(&au_fi(f)->fi_rwsem) ++#define fi_read_trylock(f) au_rw_read_trylock(&au_fi(f)->fi_rwsem) ++#define fi_write_trylock(f) au_rw_write_trylock(&au_fi(f)->fi_rwsem) +/* -+ * fi_read_lock, fi_write_lock, -+ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock ++#define fi_read_trylock_nested(f) \ ++ au_rw_read_trylock_nested(&au_fi(f)->fi_rwsem) ++#define fi_write_trylock_nested(f) \ ++ au_rw_write_trylock_nested(&au_fi(f)->fi_rwsem) ++*/ ++ ++#define fi_read_unlock(f) au_rw_read_unlock(&au_fi(f)->fi_rwsem) ++#define fi_write_unlock(f) au_rw_write_unlock(&au_fi(f)->fi_rwsem) ++#define fi_downgrade_lock(f) au_rw_dgrade_lock(&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 + */ -+AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem); ++#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) @@ -12971,13 +15312,13 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h +/* ---------------------------------------------------------------------- */ + +/* todo: hard/soft set? */ -+static inline aufs_bindex_t au_fbstart(struct file *file) ++static inline aufs_bindex_t au_fbtop(struct file *file) +{ + FiMustAnyLock(file); + return au_fi(file)->fi_btop; +} + -+static inline aufs_bindex_t au_fbend_dir(struct file *file) ++static inline aufs_bindex_t au_fbbot_dir(struct file *file) +{ + FiMustAnyLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); @@ -12991,13 +15332,13 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h + return au_fi(file)->fi_hdir->fd_vdir_cache; +} + -+static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex) ++static inline void au_set_fbtop(struct file *file, aufs_bindex_t bindex) +{ + FiMustWriteLock(file); + au_fi(file)->fi_btop = bindex; +} + -+static inline void au_set_fbend_dir(struct file *file, aufs_bindex_t bindex) ++static inline void au_set_fbbot_dir(struct file *file, aufs_bindex_t bindex) +{ + FiMustWriteLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); @@ -13105,10 +15446,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,156 @@ ++++ linux/fs/aufs/finfo.c 2018-10-23 12:33:35.596042364 +0200 +@@ -0,0 +1,149 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -13130,14 +15472,13 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c + +#include "aufs.h" + -+void au_hfput(struct au_hfile *hf, struct file *file) ++void au_hfput(struct au_hfile *hf, int execed) +{ -+ /* todo: direct access f_flags */ -+ if (vfsub_file_flags(file) & __FMODE_EXEC) ++ if (execed) + allow_write_access(hf->hf_file); + fput(hf->hf_file); + hf->hf_file = NULL; -+ atomic_dec(&hf->hf_br->br_count); ++ au_lcnt_dec(&hf->hf_br->br_nfiles); + hf->hf_br = NULL; +} + @@ -13155,7 +15496,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c + hf = fidir->fd_hfile + bindex; + + if (hf && hf->hf_file) -+ au_hfput(hf, file); ++ au_hfput(hf, vfsub_file_execed(file)); + if (val) { + FiMustWriteLock(file); + AuDebugOn(IS_ERR_OR_NULL(file->f_path.dentry)); @@ -13177,7 +15518,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c + struct au_fidir *fidir; + int nbr; + -+ nbr = au_sbend(sb) + 1; ++ nbr = au_sbbot(sb) + 1; + if (nbr < 2) + nbr = 2; /* initial allocate for 2 branches */ + fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); @@ -13189,7 +15530,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c + return fidir; +} + -+int au_fidir_realloc(struct au_finfo *finfo, int nbr) ++int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink) +{ + int err; + struct au_fidir *fidir, *p; @@ -13200,7 +15541,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c + + err = -ENOMEM; + p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr), -+ GFP_NOFS); ++ GFP_NOFS, may_shrink); + if (p) { + p->fd_nent = nbr; + finfo->fi_hdir = p; @@ -13216,7 +15557,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c +{ + struct au_finfo *finfo; + -+ au_nfiles_dec(file->f_path.dentry->d_sb); ++ au_lcnt_dec(&au_sbi(file->f_path.dentry->d_sb)->si_nfiles); + + finfo = au_fi(file); + AuDebugOn(finfo->fi_hdir); @@ -13227,10 +15568,8 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c +void au_fi_init_once(void *_finfo) +{ + struct au_finfo *finfo = _finfo; -+ static struct lock_class_key aufs_fi; + + au_rw_init(&finfo->fi_rwsem); -+ au_rw_class(&finfo->fi_rwsem, &aufs_fi); +} + +int au_finfo_init(struct file *file, struct au_fidir *fidir) @@ -13246,12 +15585,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c + goto out; + + err = 0; -+ au_nfiles_inc(dentry->d_sb); -+ /* verbose coding for lock class name */ -+ if (!fidir) -+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcNonDir_FIINFO); -+ else -+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcDir_FIINFO); ++ au_lcnt_inc(&au_sbi(dentry->d_sb)->si_nfiles); + au_rw_write_lock(&finfo->fi_rwsem); + finfo->fi_btop = -1; + finfo->fi_hdir = fidir; @@ -13265,10 +15599,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,738 @@ ++++ linux/fs/aufs/f_op.c 2018-10-23 12:33:35.596042364 +0200 +@@ -0,0 +1,819 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -13298,7 +15633,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c +{ + int err; + aufs_bindex_t bindex; -+ struct dentry *dentry; ++ struct dentry *dentry, *h_dentry; + struct au_finfo *finfo; + struct inode *h_inode; + @@ -13310,28 +15645,39 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + finfo = au_fi(file); + memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); + atomic_set(&finfo->fi_mmapped, 0); -+ bindex = au_dbstart(dentry); -+ if (!h_file) ++ bindex = au_dbtop(dentry); ++ if (!h_file) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); ++ if (unlikely(err)) ++ goto out; + h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); -+ else -+ get_file(h_file); -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ if ((flags & __O_TMPFILE) -+ && !(flags & O_EXCL)) { -+ h_inode = file_inode(h_file); -+ spin_lock(&h_inode->i_lock); -+ h_inode->i_state |= I_LINKABLE; -+ spin_unlock(&h_inode->i_lock); -+ } -+ au_set_fbstart(file, bindex); -+ au_set_h_fptr(file, bindex, h_file); -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ goto out; ++ } ++ } else { ++ h_dentry = h_file->f_path.dentry; ++ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); ++ if (unlikely(err)) ++ goto out; ++ /* br ref is already inc-ed */ + } + ++ if ((flags & __O_TMPFILE) ++ && !(flags & O_EXCL)) { ++ h_inode = file_inode(h_file); ++ spin_lock(&h_inode->i_lock); ++ h_inode->i_state |= I_LINKABLE; ++ spin_unlock(&h_inode->i_lock); ++ } ++ au_set_fbtop(file, bindex); ++ au_set_h_fptr(file, bindex, h_file); ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ ++out: + return err; +} + @@ -13360,8 +15706,8 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + aufs_bindex_t bindex; + + 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); @@ -13393,18 +15739,18 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c +/* + * read and write functions acquire [fdi]_rwsem once, but release before + * mmap_sem. This is because to stop a race condition between mmap(2). -+ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping ++ * Releasing these aufs-rwsem should be safe, no branch-management (by keeping + * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in + * read functions after [fdi]_rwsem are released, but it should be harmless. + */ + +/* 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); @@ -13425,8 +15771,12 @@ 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 bstart; ++ aufs_bindex_t btop; +}; + +/* @@ -13440,9 +15790,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; @@ -13459,7 +15813,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + + di_downgrade_lock(dentry, /*flags*/0); + if (wpre) -+ wpre->bstart = au_fbstart(file); ++ wpre->btop = au_fbtop(file); + h_file = au_hf_top(file); + get_file(h_file); + if (wpre) @@ -13480,16 +15834,15 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + struct inode *h_inode; + + au_cpup_attr_timesizes(inode); -+ AuDebugOn(au_ibstart(inode) != wpre->bstart); ++ AuDebugOn(au_ibtop(inode) != wpre->btop); + 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->bstart, ++ 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, @@ -13504,7 +15857,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; @@ -13532,11 +15885,11 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + struct super_block *sb = inode->i_sb; + + while (1) { -+ mutex_lock(&inode->i_mutex); ++ inode_lock(inode); + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (!err) + break; -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); + si_read_lock(sb, AuLock_NOPLMW); + si_read_unlock(sb); + } @@ -13554,6 +15907,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)) @@ -13564,7 +15918,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + +out: + si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); + return err; +} + @@ -13613,11 +15967,20 @@ 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*/1, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + ++ if (au_test_loopback_kthread()) { ++ au_warn_loopback(h_file->f_path.dentry->d_sb); ++ if (file->f_mapping != h_file->f_mapping) { ++ file->f_mapping = h_file->f_mapping; ++ smp_mb(); /* unnecessary? */ ++ } ++ } ++ fi_read_unlock(file); ++ + err = au_do_iter(h_file, MAY_READ, kio, iov_iter); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ @@ -13639,6 +16002,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)) @@ -13649,7 +16013,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + +out: + si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); + return err; +} + @@ -13666,22 +16030,13 @@ 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*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + -+ if (au_test_loopback_kthread()) { -+ au_warn_loopback(h_file->f_path.dentry->d_sb); -+ if (file->f_mapping != h_file->f_mapping) { -+ file->f_mapping = h_file->f_mapping; -+ smp_mb(); /* unnecessary? */ -+ } -+ } -+ fi_read_unlock(file); -+ + err = vfsub_splice_to(h_file, ppos, pipe, len, flags); -+ /* todo: necessasry? */ ++ /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + au_read_post(inode, h_file); + @@ -13702,6 +16057,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)) @@ -13712,7 +16068,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + +out: + si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); + return err; +} + @@ -13727,6 +16083,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)) @@ -13739,10 +16096,92 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + +out: + si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); + 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 comparison. ++ */ ++ 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 ++} ++ +/* ---------------------------------------------------------------------- */ + +/* @@ -13753,9 +16192,9 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + * (Note that [fdi]i_rwsem are released before mmap_sem). + * - in mmap case + * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem -+ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for -+ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in -+ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though. ++ * This AB-BA order is definitely bad, but is not a problem since "si_rwsem for ++ * read" allows multiple processes to acquire it and [fdi]i_rwsem are not held ++ * in file I/O. Aufs needs to stop lockdep in aufs_mmap() though. + * It means that when aufs acquires si_rwsem for write, the process should never + * acquire mmap_sem. + * @@ -13833,7 +16272,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + * au_flag_conv(vma->vm_flags)); + */ + if (!err) -+ err = h_file->f_op->mmap(h_file, vma); ++ err = call_mmap(h_file, vma); + if (!err) { + au_vm_prfile_set(vma, file); + fsstack_copy_attr_atime(inode, file_inode(h_file)); @@ -13872,6 +16311,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)) @@ -13882,62 +16322,35 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c + +out_unlock: + si_read_unlock(inode->i_sb); -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); +out: + return err; +} + -+/* no one supports this operation, currently */ -+#if 0 -+static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync) ++static int aufs_fasync(int fd, struct file *file, int flag) +{ + int err; -+ struct au_write_pre wpre; -+ struct inode *inode; -+ struct file *file, *h_file; -+ -+ err = 0; /* -EBADF; */ /* posix? */ -+ if (unlikely(!(file->f_mode & FMODE_WRITE))) -+ goto out; ++ struct file *h_file; ++ struct super_block *sb; + -+ file = kio->ki_filp; -+ inode = file_inode(file); -+ au_mtx_and_read_lock(inode); ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + -+ h_file = au_write_pre(file, /*do_ready*/1, &wpre); ++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + 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) { -+ struct mutex *h_mtx; ++ goto out; + -+ h_mtx = &file_inode(h_file)->i_mutex; -+ if (!is_sync_kiocb(kio)) { -+ get_file(h_file); -+ fput(file); -+ } -+ kio->ki_filp = h_file; -+ err = h_file->f_op->aio_fsync(kio, datasync); -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ if (!err) -+ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); -+ /*ignore*/ -+ mutex_unlock(h_mtx); -+ } -+ au_write_post(inode, h_file, &wpre, /*written*/0); ++ if (h_file->f_op->fasync) ++ err = h_file->f_op->fasync(fd, h_file, flag); ++ fput(h_file); /* instead of au_read_post() */ + -+out_unlock: -+ si_read_unlock(inode->sb); -+ mutex_unlock(&inode->i_mutex); +out: ++ si_read_unlock(sb); + return err; +} -+#endif + -+static int aufs_fasync(int fd, struct file *file, int flag) ++static int aufs_setfl(struct file *file, unsigned long arg) +{ + int err; + struct file *h_file; @@ -13946,13 +16359,14 @@ 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; + -+ if (h_file->f_op->fasync) -+ err = h_file->f_op->fasync(fd, h_file, flag); ++ /* 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() */ + +out: @@ -13994,23 +16408,25 @@ 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, + .splice_write = aufs_splice_write, + .splice_read = aufs_splice_read, +#if 0 + .aio_splice_write = aufs_aio_splice_write, + .aio_splice_read = aufs_aio_splice_read, +#endif -+ .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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,400 @@ ++++ linux/fs/aufs/fstype.h 2018-08-12 23:43:05.456791299 +0200 +@@ -0,0 +1,401 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -14052,7 +16468,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_iso9660(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE) ++#if IS_ENABLED(CONFIG_ISO9660_FS) + return sb->s_magic == ISOFS_SUPER_MAGIC; +#else + return 0; @@ -14061,7 +16477,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_romfs(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE) ++#if IS_ENABLED(CONFIG_ROMFS_FS) + return sb->s_magic == ROMFS_MAGIC; +#else + return 0; @@ -14070,7 +16486,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_cramfs(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE) ++#if IS_ENABLED(CONFIG_CRAMFS) + return sb->s_magic == CRAMFS_MAGIC; +#endif + return 0; @@ -14078,7 +16494,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_nfs(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE) ++#if IS_ENABLED(CONFIG_NFS_FS) + return sb->s_magic == NFS_SUPER_MAGIC; +#else + return 0; @@ -14087,7 +16503,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_fuse(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE) ++#if IS_ENABLED(CONFIG_FUSE_FS) + return sb->s_magic == FUSE_SUPER_MAGIC; +#else + return 0; @@ -14096,7 +16512,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_xfs(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE) ++#if IS_ENABLED(CONFIG_XFS_FS) + return sb->s_magic == XFS_SB_MAGIC; +#else + return 0; @@ -14114,7 +16530,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE) ++#if IS_ENABLED(CONFIG_ECRYPT_FS) + return !strcmp(au_sbtype(sb), "ecryptfs"); +#else + return 0; @@ -14128,7 +16544,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_ubifs(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE) ++#if IS_ENABLED(CONFIG_UBIFS_FS) + return sb->s_magic == UBIFS_SUPER_MAGIC; +#else + return 0; @@ -14155,7 +16571,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_configfs(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE) ++#if IS_ENABLED(CONFIG_CONFIGFS_FS) + return sb->s_magic == CONFIGFS_MAGIC; +#else + return 0; @@ -14164,7 +16580,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_minix(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE) ++#if IS_ENABLED(CONFIG_MINIX_FS) + return sb->s_magic == MINIX3_SUPER_MAGIC + || sb->s_magic == MINIX2_SUPER_MAGIC + || sb->s_magic == MINIX2_SUPER_MAGIC2 @@ -14177,7 +16593,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_fat(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE) ++#if IS_ENABLED(CONFIG_FAT_FS) + return sb->s_magic == MSDOS_SUPER_MAGIC; +#else + return 0; @@ -14205,7 +16621,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_squashfs(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE) ++#if IS_ENABLED(CONFIG_SQUASHFS) + return sb->s_magic == SQUASHFS_MAGIC; +#else + return 0; @@ -14214,7 +16630,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_btrfs(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE) ++#if IS_ENABLED(CONFIG_BTRFS_FS) + return sb->s_magic == BTRFS_SUPER_MAGIC; +#else + return 0; @@ -14223,7 +16639,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_xenfs(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE) ++#if IS_ENABLED(CONFIG_XENFS) + return sb->s_magic == XENFS_SUPER_MAGIC; +#else + return 0; @@ -14241,7 +16657,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_nilfs(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_NILFS) || defined(CONFIG_NILFS_MODULE) ++#if IS_ENABLED(CONFIG_NILFS) + return sb->s_magic == NILFS_SUPER_MAGIC; +#else + return 0; @@ -14250,7 +16666,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +static inline int au_test_hfsplus(struct super_block *sb __maybe_unused) +{ -+#if defined(CONFIG_HFSPLUS_FS) || defined(CONFIG_HFSPLUS_FS_MODULE) ++#if IS_ENABLED(CONFIG_HFSPLUS_FS) + return sb->s_magic == HFSPLUS_SUPER_MAGIC; +#else + return 0; @@ -14398,7 +16814,7 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h + +/* + * test if the @inode is nfs with 'noacl' option -+ * NFS always sets MS_POSIXACL regardless its mount option 'noacl.' ++ * NFS always sets SB_POSIXACL regardless its mount option 'noacl.' + */ +static inline int au_test_nfs_noacl(struct inode *inode) +{ @@ -14409,12 +16825,82 @@ 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-08-12 23:43:05.456791299 +0200 +@@ -0,0 +1,65 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,288 @@ ++++ linux/fs/aufs/hfsnotify.c 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,289 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -14446,9 +16932,9 @@ 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"); ++ /* AuDbg("here\n"); */ + au_cache_free_hnotify(hn); -+ smp_mb__before_atomic(); ++ smp_mb__before_atomic(); /* for atomic64_dec */ + if (atomic64_dec_and_test(&au_hfsn_ifree)) + wake_up(&au_hfsn_wq); +} @@ -14469,17 +16955,14 @@ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c + AuDebugOn(!br->br_hfsn); + + mark = &hn->hn_mark; -+ fsnotify_init_mark(mark, au_hfsn_free_mark); ++ fsnotify_init_mark(mark, br->br_hfsn->hfsn_group); + mark->mask = AuHfsnMask; + /* + * by udba rename or rmdir, aufs assign a new inode to the known + * h_inode, so specify 1 to allow dups. + */ + lockdep_off(); -+ err = fsnotify_add_mark(mark, br->br_hfsn->hfsn_group, hinode->hi_inode, -+ /*mnt*/NULL, /*allow_dups*/1); -+ /* even if err */ -+ fsnotify_put_mark(mark); ++ err = fsnotify_add_inode_mark(mark, hinode->hi_inode, /*allow_dups*/1); + lockdep_on(); + + return err; @@ -14501,6 +16984,7 @@ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c + spin_unlock(&mark->lock); + lockdep_off(); + fsnotify_destroy_mark(mark, group); ++ fsnotify_put_mark(mark); + fsnotify_put_group(group); + lockdep_on(); + @@ -14571,21 +17055,21 @@ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c +{ + struct au_br_hfsnotify *hfsn = group->private; + -+ AuDbg("here\n"); -+ kfree(hfsn); ++ /* AuDbg("here\n"); */ ++ au_kfree_try_rcu(hfsn); +} + +static int au_hfsn_handle_event(struct fsnotify_group *group, + struct inode *inode, -+ struct fsnotify_mark *inode_mark, -+ struct fsnotify_mark *vfsmount_mark, -+ u32 mask, void *data, int data_type, -+ const unsigned char *file_name, u32 cookie) ++ u32 mask, const void *data, int data_type, ++ const unsigned char *file_name, u32 cookie, ++ struct fsnotify_iter_info *iter_info) +{ + int err; + struct au_hnotify *hnotify; + struct inode *h_dir, *h_inode; + struct qstr h_child_qstr = QSTR_INIT(file_name, strlen(file_name)); ++ struct fsnotify_mark *inode_mark; + + AuDebugOn(data_type != FSNOTIFY_EVENT_INODE); + @@ -14609,6 +17093,7 @@ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c + au_debug_off(); +#endif + ++ inode_mark = fsnotify_iter_inode_mark(iter_info); + AuDebugOn(!inode_mark); + hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); + err = au_hnotify(h_dir, hnotify, mask, &h_child_qstr, h_inode); @@ -14619,7 +17104,8 @@ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c + +static struct fsnotify_ops au_hfsn_ops = { + .handle_event = au_hfsn_handle_event, -+ .free_group_priv = au_hfsn_free_group ++ .free_group_priv = au_hfsn_free_group, ++ .free_mark = au_hfsn_free_mark +}; + +/* ---------------------------------------------------------------------- */ @@ -14666,7 +17152,7 @@ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c + goto out; /* success */ + +out_hfsn: -+ kfree(hfsn); ++ au_kfree_try_rcu(hfsn); +out: + return err; +} @@ -14703,10 +17189,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,56 @@ ++++ linux/fs/aufs/hfsplus.c 2018-10-23 12:33:35.596042364 +0200 +@@ -0,0 +1,60 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2010-2015 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 @@ -14723,11 +17210,11 @@ diff -urN /usr/share/empty/fs/aufs/hfsplus.c linux/fs/aufs/hfsplus.c + */ + +/* -+ * special support for filesystems which aqucires an inode mutex ++ * special support for filesystems which acquires an inode mutex + * at final closing a file, eg, hfsplus. + * + * This trick is very simple and stupid, just to open the file before really -+ * neceeary open to tell hfsplus that this is not the final closing. ++ * necessary open to tell hfsplus that this is not the final closing. + * The caller should call au_h_open_pre() after acquiring the inode mutex, + * and au_h_open_post() after releasing it. + */ @@ -14756,17 +17243,21 @@ diff -urN /usr/share/empty/fs/aufs/hfsplus.c linux/fs/aufs/hfsplus.c +void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, + struct file *h_file) +{ ++ struct au_branch *br; ++ + if (h_file) { + fput(h_file); -+ au_sbr_put(dentry->d_sb, bindex); ++ br = au_sbr(dentry->d_sb, bindex); ++ au_lcnt_dec(&br->br_nfiles); + } +} 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,710 @@ ++++ linux/fs/aufs/hnotify.c 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,720 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -14805,9 +17296,9 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + 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 ++ * dir left. In this case, aufs assigns a new inode + * number and set the monitor again. -+ * For the lower dir, the old monitnor is still left. ++ * For the lower dir, the old monitor is still left. + */ + if (err == -EEXIST) + err = 0; @@ -14840,17 +17331,17 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + +void au_hn_reset(struct inode *inode, unsigned int flags) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct inode *hi; + struct dentry *iwhdentry; + -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ bbot = au_ibbot(inode); ++ for (bindex = au_ibtop(inode); bindex <= bbot; bindex++) { + hi = au_h_iptr(inode, bindex); + if (!hi) + continue; + -+ /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */ ++ /* inode_lock_nested(hi, AuLsc_I_CHILD); */ + iwhdentry = au_hi_wh(inode, bindex); + if (iwhdentry) + dget(iwhdentry); @@ -14860,7 +17351,7 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + flags & ~AuHi_XINO); + iput(hi); + dput(iwhdentry); -+ /* mutex_unlock(&hi->i_mutex); */ ++ /* inode_unlock(hi); */ + } +} + @@ -14869,7 +17360,7 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c +static int hn_xino(struct inode *inode, struct inode *h_inode) +{ + int err; -+ aufs_bindex_t bindex, bend, bfound, bstart; ++ aufs_bindex_t bindex, bbot, bfound, btop; + struct inode *h_i; + + err = 0; @@ -14879,15 +17370,15 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + } + + bfound = -1; -+ bend = au_ibend(inode); -+ bstart = au_ibstart(inode); ++ bbot = au_ibbot(inode); ++ btop = au_ibtop(inode); +#if 0 /* reserved for future use */ -+ if (bindex == bend) { ++ if (bindex == bbot) { + /* keep this ino in rename case */ + goto out; + } +#endif -+ for (bindex = bstart; bindex <= bend; bindex++) ++ for (bindex = btop; bindex <= bbot; bindex++) + if (au_h_iptr(inode, bindex) == h_inode) { + bfound = bindex; + break; @@ -14895,7 +17386,7 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + if (bfound < 0) + goto out; + -+ for (bindex = bstart; bindex <= bend; bindex++) { ++ for (bindex = btop; bindex <= bbot; bindex++) { + h_i = au_h_iptr(inode, bindex); + if (!h_i) + continue; @@ -15089,11 +17580,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) { -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ 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 */ -+ mutex_unlock(&a->h_inode->i_mutex); ++ inode_unlock_shared(a->h_inode); + } + + /* make the generation obsolete */ @@ -15115,7 +17606,7 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + if (vdir) + vdir->vd_jiffy = 0; + /* IMustLock(a->inode); */ -+ /* a->inode->i_version++; */ ++ /* inode_inc_iversion(a->inode); */ + } + + /* can do nothing but warn */ @@ -15200,7 +17691,7 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c +{ + struct au_hnotify_args *a = _args; + struct super_block *sb; -+ aufs_bindex_t bindex, bend, bfound; ++ aufs_bindex_t bindex, bbot, bfound; + unsigned char xino, try_iput; + int err; + struct inode *inode; @@ -15229,10 +17720,18 @@ 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; -+ bend = au_ibend(a->dir); -+ for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++) ++ bbot = au_ibbot(a->dir); ++ for (bindex = au_ibtop(a->dir); bindex <= bbot; bindex++) + if (au_h_iptr(a->dir, bindex) == a->h_dir) { + bfound = bindex; + break; @@ -15260,7 +17759,7 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + || au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) { + inode = lookup_wlock_by_ino(sb, bfound, h_ino); + try_iput = 1; -+ } ++ } + + args.flags = a->flags[AuHn_CHILD]; + args.dentry = dentry; @@ -15299,7 +17798,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); -+ kfree(a); ++ au_kfree_rcu(a); +} + +/* ---------------------------------------------------------------------- */ @@ -15405,7 +17904,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); -+ kfree(args); ++ au_kfree_rcu(args); + } + +out: @@ -15446,8 +17945,8 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + +static void au_hn_destroy_cache(void) +{ -+ kmem_cache_destroy(au_cachep[AuCache_HNOTIFY]); -+ au_cachep[AuCache_HNOTIFY] = NULL; ++ kmem_cache_destroy(au_cache[AuCache_HNOTIFY]); ++ au_cache[AuCache_HNOTIFY] = NULL; +} + +int __init au_hnotify_init(void) @@ -15455,8 +17954,8 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c + int err; + + err = -ENOMEM; -+ au_cachep[AuCache_HNOTIFY] = AuCache(au_hnotify); -+ if (au_cachep[AuCache_HNOTIFY]) { ++ au_cache[AuCache_HNOTIFY] = AuCache(au_hnotify); ++ if (au_cache[AuCache_HNOTIFY]) { + err = 0; + if (au_hnotify_op.init) + err = au_hnotify_op.init(); @@ -15471,16 +17970,18 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c +{ + if (au_hnotify_op.fin) + au_hnotify_op.fin(); ++ + /* cf. au_cache_fin() */ -+ if (au_cachep[AuCache_HNOTIFY]) ++ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,277 @@ ++++ linux/fs/aufs/iinfo.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,286 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -15505,10 +18006,12 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c +struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) +{ + struct inode *h_inode; ++ struct au_hinode *hinode; + + IiMustAnyLock(inode); + -+ h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode; ++ hinode = au_hinode(au_ii(inode), bindex); ++ h_inode = hinode->hi_inode; + AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); + return h_inode; +} @@ -15543,7 +18046,7 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c + + IiMustWriteLock(inode); + -+ hinode = iinfo->ii_hinode + bindex; ++ hinode = au_hinode(iinfo, bindex); + hi = hinode->hi_inode; + AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); + @@ -15558,7 +18061,7 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c + AuDebugOn(inode->i_mode + && (h_inode->i_mode & S_IFMT) + != (inode->i_mode & S_IFMT)); -+ if (bindex == iinfo->ii_bstart) ++ if (bindex == iinfo->ii_btop) + au_cpup_igen(inode, h_inode); + br = au_sbr(sb, bindex); + hinode->hi_id = br->br_id; @@ -15585,7 +18088,7 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c + + IiMustWriteLock(inode); + -+ hinode = au_ii(inode)->ii_hinode + bindex; ++ hinode = au_hinode(au_ii(inode), bindex); + AuDebugOn(hinode->hi_whdentry); + hinode->hi_whdentry = h_wh; +} @@ -15612,20 +18115,18 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c +void au_update_ibrange(struct inode *inode, int do_put_zero) +{ + struct au_iinfo *iinfo; -+ aufs_bindex_t bindex, bend; -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; ++ aufs_bindex_t bindex, bbot; + ++ AuDebugOn(au_is_bad_inode(inode)); + IiMustWriteLock(inode); + -+ if (do_put_zero && iinfo->ii_bstart >= 0) { -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ iinfo = au_ii(inode); ++ if (do_put_zero && iinfo->ii_btop >= 0) { ++ for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; + bindex++) { + struct inode *h_i; + -+ h_i = iinfo->ii_hinode[0 + bindex].hi_inode; ++ h_i = au_hinode(iinfo, bindex)->hi_inode; + if (h_i + && !h_i->i_nlink + && !(h_i->i_state & I_LINKABLE)) @@ -15633,21 +18134,21 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c + } + } + -+ iinfo->ii_bstart = -1; -+ iinfo->ii_bend = -1; -+ bend = au_sbend(inode->i_sb); -+ for (bindex = 0; bindex <= bend; bindex++) -+ if (iinfo->ii_hinode[0 + bindex].hi_inode) { -+ iinfo->ii_bstart = bindex; ++ iinfo->ii_btop = -1; ++ iinfo->ii_bbot = -1; ++ bbot = au_sbbot(inode->i_sb); ++ for (bindex = 0; bindex <= bbot; bindex++) ++ if (au_hinode(iinfo, bindex)->hi_inode) { ++ iinfo->ii_btop = bindex; + break; + } -+ if (iinfo->ii_bstart >= 0) -+ for (bindex = bend; bindex >= iinfo->ii_bstart; bindex--) -+ if (iinfo->ii_hinode[0 + bindex].hi_inode) { -+ iinfo->ii_bend = bindex; ++ if (iinfo->ii_btop >= 0) ++ for (bindex = bbot; bindex >= iinfo->ii_btop; bindex--) ++ if (au_hinode(iinfo, bindex)->hi_inode) { ++ iinfo->ii_bbot = bindex; + break; + } -+ AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend); ++ AuDebugOn(iinfo->ii_btop > iinfo->ii_bbot); +} + +/* ---------------------------------------------------------------------- */ @@ -15656,54 +18157,65 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c +{ + struct au_icntnr *c = _c; + struct au_iinfo *iinfo = &c->iinfo; -+ static struct lock_class_key aufs_ii; + + spin_lock_init(&iinfo->ii_generation.ig_spin); + au_rw_init(&iinfo->ii_rwsem); -+ au_rw_class(&iinfo->ii_rwsem, &aufs_ii); + inode_init_once(&c->vfs_inode); +} + ++void au_hinode_init(struct au_hinode *hinode) ++{ ++ hinode->hi_inode = NULL; ++ hinode->hi_id = -1; ++ au_hn_init(hinode); ++ hinode->hi_whdentry = NULL; ++} ++ +int au_iinfo_init(struct inode *inode) +{ + struct au_iinfo *iinfo; + struct super_block *sb; ++ struct au_hinode *hi; + int nbr, i; + + sb = inode->i_sb; + iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); -+ nbr = au_sbend(sb) + 1; ++ nbr = au_sbbot(sb) + 1; + if (unlikely(nbr <= 0)) + nbr = 1; -+ iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); -+ if (iinfo->ii_hinode) { -+ au_ninodes_inc(sb); -+ for (i = 0; i < nbr; i++) -+ iinfo->ii_hinode[i].hi_id = -1; ++ hi = kmalloc_array(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); ++ if (hi) { ++ au_lcnt_inc(&au_sbi(sb)->si_ninodes); ++ ++ iinfo->ii_hinode = hi; ++ for (i = 0; i < nbr; i++, hi++) ++ au_hinode_init(hi); + + iinfo->ii_generation.ig_generation = au_sigen(sb); -+ iinfo->ii_bstart = -1; -+ iinfo->ii_bend = -1; ++ iinfo->ii_btop = -1; ++ iinfo->ii_bbot = -1; + iinfo->ii_vdir = NULL; + return 0; + } + return -ENOMEM; +} + -+int au_ii_realloc(struct au_iinfo *iinfo, int nbr) ++int au_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink) +{ -+ int err, sz; ++ int err, i; + struct au_hinode *hip; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + + err = -ENOMEM; -+ sz = sizeof(*hip) * (iinfo->ii_bend + 1); -+ if (!sz) -+ sz = sizeof(*hip); -+ hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS); ++ hip = au_krealloc(iinfo->ii_hinode, sizeof(*hip) * nbr, GFP_NOFS, ++ may_shrink); + if (hip) { + iinfo->ii_hinode = hip; ++ i = iinfo->ii_bbot + 1; ++ hip += i; ++ for (; i < nbr; i++, hip++) ++ au_hinode_init(hip); + err = 0; + } + @@ -15715,16 +18227,13 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c + struct au_iinfo *iinfo; + struct au_hinode *hi; + struct super_block *sb; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + const unsigned char unlinked = !inode->i_nlink; + -+ iinfo = au_ii(inode); -+ /* bad_inode case */ -+ if (!iinfo) -+ return; ++ AuDebugOn(au_is_bad_inode(inode)); + + sb = inode->i_sb; -+ au_ninodes_dec(sb); ++ au_lcnt_dec(&au_sbi(sb)->si_ninodes); + if (si_pid_test(sb)) + au_xino_delete_inode(inode, unlinked); + else { @@ -15739,29 +18248,30 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c + lockdep_on(); + } + ++ iinfo = au_ii(inode); + if (iinfo->ii_vdir) + au_vdir_free(iinfo->ii_vdir); + -+ bindex = iinfo->ii_bstart; ++ bindex = iinfo->ii_btop; + if (bindex >= 0) { -+ hi = iinfo->ii_hinode + bindex; -+ bend = iinfo->ii_bend; -+ while (bindex++ <= bend) { ++ hi = au_hinode(iinfo, bindex); ++ bbot = iinfo->ii_bbot; ++ while (bindex++ <= bbot) { + if (hi->hi_inode) + au_hiput(hi); + hi++; + } + } -+ kfree(iinfo->ii_hinode); -+ iinfo->ii_hinode = NULL; ++ au_kfree_rcu(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-01-13 20:11:11.669760262 +0100 ++++ linux/fs/aufs/inode.c 2018-08-12 23:43:05.460124736 +0200 @@ -0,0 +1,528 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -15797,31 +18307,33 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + au_cpup_attr_all(inode, /*force*/0); + au_update_iigen(inode, /*half*/1); + if (do_version) -+ inode->i_version++; ++ inode_inc_iversion(inode); +} + +static int au_ii_refresh(struct inode *inode, int *update) +{ -+ int err, e; ++ int err, e, nbr; + umode_t type; + aufs_bindex_t bindex, new_bindex; + struct super_block *sb; + struct au_iinfo *iinfo; + struct au_hinode *p, *q, tmp; + ++ AuDebugOn(au_is_bad_inode(inode)); + IiMustWriteLock(inode); + + *update = 0; + sb = inode->i_sb; ++ nbr = au_sbbot(sb) + 1; + type = inode->i_mode & S_IFMT; + iinfo = au_ii(inode); -+ err = au_ii_realloc(iinfo, au_sbend(sb) + 1); ++ err = au_hinode_realloc(iinfo, nbr, /*may_shrink*/0); + if (unlikely(err)) + goto out; + -+ AuDebugOn(iinfo->ii_bstart < 0); -+ p = iinfo->ii_hinode + iinfo->ii_bstart; -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ AuDebugOn(iinfo->ii_btop < 0); ++ p = au_hinode(iinfo, iinfo->ii_btop); ++ for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; + bindex++, p++) { + if (!p->hi_inode) + continue; @@ -15838,12 +18350,12 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + continue; + } + -+ if (new_bindex < iinfo->ii_bstart) -+ iinfo->ii_bstart = new_bindex; -+ if (iinfo->ii_bend < new_bindex) -+ iinfo->ii_bend = new_bindex; ++ if (new_bindex < iinfo->ii_btop) ++ iinfo->ii_btop = new_bindex; ++ if (iinfo->ii_bbot < new_bindex) ++ iinfo->ii_bbot = new_bindex; + /* swap two lower inode, and loop again */ -+ q = iinfo->ii_hinode + new_bindex; ++ q = au_hinode(iinfo, new_bindex); + tmp = *q; + *q = *p; + *p = tmp; @@ -15853,6 +18365,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + } + } + au_update_ibrange(inode, /*do_put_zero*/0); ++ au_hinode_realloc(iinfo, nbr, /*may_shrink*/1); /* harmless if err */ + e = au_dy_irefresh(inode); + if (unlikely(e && !err)) + err = e; @@ -15905,7 +18418,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + int err, e, update; + unsigned int flags; + umode_t mode; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + unsigned char isdir; + struct au_hinode *p; + struct au_iinfo *iinfo; @@ -15916,12 +18429,12 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + + update = 0; + iinfo = au_ii(inode); -+ p = iinfo->ii_hinode + iinfo->ii_bstart; ++ p = au_hinode(iinfo, iinfo->ii_btop); + mode = (inode->i_mode & S_IFMT); + isdir = S_ISDIR(mode); + flags = au_hi_flags(inode, isdir); -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ bbot = au_dbbot(dentry); ++ for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) { + struct inode *h_i, *h_inode; + struct dentry *h_d; + @@ -15931,7 +18444,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + + h_inode = d_inode(h_d); + AuDebugOn(mode != (h_inode->i_mode & S_IFMT)); -+ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { ++ if (iinfo->ii_btop <= bindex && bindex <= iinfo->ii_bbot) { + h_i = au_h_iptr(inode, bindex); + if (h_i) { + if (h_i == h_inode) @@ -15940,10 +18453,10 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + break; + } + } -+ if (bindex < iinfo->ii_bstart) -+ iinfo->ii_bstart = bindex; -+ if (iinfo->ii_bend < bindex) -+ iinfo->ii_bend = bindex; ++ if (bindex < iinfo->ii_btop) ++ iinfo->ii_btop = bindex; ++ if (iinfo->ii_bbot < bindex) ++ iinfo->ii_bbot = bindex; + au_set_h_iptr(inode, bindex, au_igrab(h_inode), flags); + update = 1; + } @@ -15964,7 +18477,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + int err; + unsigned int flags; + umode_t mode; -+ aufs_bindex_t bindex, bstart, btail; ++ aufs_bindex_t bindex, btop, btail; + unsigned char isdir; + struct dentry *h_dentry; + struct inode *h_inode; @@ -15976,8 +18489,8 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + err = 0; + isdir = 0; + iop = au_sbi(inode->i_sb)->si_iop_array; -+ bstart = au_dbstart(dentry); -+ h_dentry = au_h_dptr(dentry, bstart); ++ btop = au_dbtop(dentry); ++ h_dentry = au_h_dptr(dentry, btop); + h_inode = d_inode(h_dentry); + mode = h_inode->i_mode; + switch (mode & S_IFMT) { @@ -15985,7 +18498,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + btail = au_dbtail(dentry); + inode->i_op = iop + AuIop_OTHER; + inode->i_fop = &aufs_file_fop; -+ err = au_dy_iaop(inode, bstart, h_inode); ++ err = au_dy_iaop(inode, btop, h_inode); + if (unlikely(err)) + goto out; + break; @@ -16021,9 +18534,9 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) + au_fclr_hi(flags, HNOTIFY); + iinfo = au_ii(inode); -+ iinfo->ii_bstart = bstart; -+ iinfo->ii_bend = btail; -+ for (bindex = bstart; bindex <= btail; bindex++) { ++ iinfo->ii_btop = btop; ++ iinfo->ii_bbot = btail; ++ for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry) + au_set_h_iptr(inode, bindex, @@ -16048,9 +18561,8 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c +static int reval_inode(struct inode *inode, struct dentry *dentry) +{ + int err; -+ unsigned int gen; -+ struct au_iigen iigen; -+ aufs_bindex_t bindex, bend; ++ unsigned int gen, igflags; ++ aufs_bindex_t bindex, bbot; + struct inode *h_inode, *h_dinode; + struct dentry *h_dentry; + @@ -16065,18 +18577,18 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + + err = 1; + ii_write_lock_new_child(inode); -+ h_dentry = au_h_dptr(dentry, au_dbstart(dentry)); ++ h_dentry = au_h_dptr(dentry, au_dbtop(dentry)); + h_dinode = d_inode(h_dentry); -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ bbot = au_ibbot(inode); ++ for (bindex = au_ibtop(inode); bindex <= bbot; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (!h_inode || h_inode != h_dinode) + continue; + + err = 0; -+ gen = au_iigen(inode, &iigen); ++ gen = au_iigen(inode, &igflags); + if (gen == au_digen(dentry) -+ && !au_ig_ftest(iigen.ig_flags, HALF_REFRESHED)) ++ && !au_ig_ftest(igflags, HALF_REFRESHED)) + break; + + /* fully refresh inode using dentry */ @@ -16095,32 +18607,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; +} + @@ -16131,38 +18645,39 @@ 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; -+ aufs_bindex_t bstart; ++ int err, idx, hlinked; ++ aufs_bindex_t btop; + + sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ h_dentry = au_h_dptr(dentry, bstart); ++ btop = au_dbtop(dentry); ++ 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, bstart)->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, bstart, h_ino, &ino); ++ 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; + } + } + @@ -16170,26 +18685,15 @@ 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) { -+ /* verbose coding for lock class name */ -+ if (unlikely(d_is_symlink(h_dentry))) -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcSymlink_IIINFO); -+ else if (unlikely(d_is_dir(h_dentry))) -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcDir_IIINFO); -+ else /* likely */ -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcNonDir_IIINFO); -+ + ii_write_lock_new_child(inode); + err = set_inode(inode, dentry); + if (!err) { + unlock_new_inode(inode); -+ goto out; /* success */ ++ goto out_xinondir; /* success */ + } + + /* @@ -16200,7 +18704,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + atomic_inc(&inode->i_count); + iget_failed(inode); + ii_write_unlock(inode); -+ au_xino_write(sb, bstart, h_ino, /*ino*/0); ++ au_xino_write(sb, btop, h_ino, /*ino*/0); + /* ignore this error */ + goto out_iput; + } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { @@ -16208,41 +18712,46 @@ 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))) + AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," + " b%d, %s, %pd, hi%lu, i%lu.\n", -+ bstart, au_sbtype(h_dentry->d_sb), dentry, ++ btop, au_sbtype(h_dentry->d_sb), dentry, + (unsigned long)h_ino, (unsigned long)ino); + ino = 0; -+ err = au_xino_write(sb, bstart, h_ino, /*ino*/0); ++ 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; +} + @@ -16259,8 +18768,8 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c + /* pseudo-link after flushed may happen out of bounds */ + if (!err + && inode -+ && au_ibstart(inode) <= bindex -+ && bindex <= au_ibend(inode)) { ++ && au_ibtop(inode) <= bindex ++ && bindex <= au_ibbot(inode)) { + /* + * permission check is unnecessary since vfsub routine + * will be called later @@ -16290,10 +18799,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,685 @@ ++++ linux/fs/aufs/inode.h 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,698 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -16329,7 +18839,8 @@ 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 -+ struct inode *hn_aufs_inode; /* no get/put */ ++ struct inode *hn_aufs_inode; /* no get/put */ ++ struct rcu_head rcu; +#endif +} ____cacheline_aligned_in_smp; + @@ -16363,15 +18874,17 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h + struct super_block *ii_hsb1; /* no get/put */ + + struct au_rwsem ii_rwsem; -+ aufs_bindex_t ii_bstart, ii_bend; ++ aufs_bindex_t ii_btop, ii_bbot; + __u32 ii_higen; + struct au_hinode *ii_hinode; + struct au_vdir *ii_vdir; +}; + +struct au_icntnr { -+ struct au_iinfo iinfo; -+ struct inode vfs_inode; ++ struct au_iinfo iinfo; ++ struct inode vfs_inode; ++ struct hlist_bl_node plink; ++ struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* au_pin flags */ @@ -16404,7 +18917,6 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +void au_pin_hdir_unlock(struct au_pin *p); +int au_pin_hdir_lock(struct au_pin *p); +int au_pin_hdir_relock(struct au_pin *p); -+void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task); +void au_pin_hdir_acquire_nest(struct au_pin *p); +void au_pin_hdir_release(struct au_pin *p); + @@ -16412,12 +18924,8 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h + +static inline struct au_iinfo *au_ii(struct inode *inode) +{ -+ struct au_iinfo *iinfo; -+ -+ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); -+ if (iinfo->ii_hinode) -+ return iinfo; -+ return NULL; /* debugging bad_inode case */ ++ BUG_ON(is_bad_inode(inode)); ++ return &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); +} + +/* ---------------------------------------------------------------------- */ @@ -16502,7 +19010,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, @@ -16530,7 +19039,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); @@ -16560,9 +19070,10 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +void au_update_ibrange(struct inode *inode, int do_put_zero); + +void au_icntnr_init_once(void *_c); ++void au_hinode_init(struct au_hinode *hinode); +int au_iinfo_init(struct inode *inode); +void au_iinfo_fin(struct inode *inode); -+int au_ii_realloc(struct au_iinfo *iinfo, int nbr); ++int au_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink); + +#ifdef CONFIG_PROC_FS +/* plink.c */ @@ -16602,17 +19113,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, const char *name, void *value, -+ size_t size); -+int aufs_setxattr(struct dentry *dentry, 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 @@ -16623,11 +19128,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 { @@ -16637,15 +19141,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 au_srxattr *arg); ++ssize_t au_sxattr(struct dentry *dentry, struct inode *inode, ++ struct au_sxattr *arg); +#endif + +/* ---------------------------------------------------------------------- */ @@ -16698,10 +19200,9 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +#undef AuWriteLockFunc +#undef AuRWLockFuncs + -+/* -+ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock -+ */ -+AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem); ++#define ii_read_unlock(i) au_rw_read_unlock(&au_ii(i)->ii_rwsem) ++#define ii_write_unlock(i) au_rw_write_unlock(&au_ii(i)->ii_rwsem) ++#define ii_downgrade_lock(i) au_rw_dgrade_lock(&au_ii(i)->ii_rwsem) + +#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) +#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem) @@ -16716,7 +19217,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +#endif +} + -+static inline unsigned int au_iigen(struct inode *inode, struct au_iigen *iigen_arg) ++static inline unsigned int au_iigen(struct inode *inode, unsigned int *igflags) +{ + unsigned int gen; + struct au_iinfo *iinfo; @@ -16725,8 +19226,8 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h + iinfo = au_ii(inode); + iigen = &iinfo->ii_generation; + spin_lock(&iigen->ig_spin); -+ if (iigen_arg) -+ *iigen_arg = *iigen; ++ if (igflags) ++ *igflags = iigen->ig_flags; + gen = iigen->ig_generation; + spin_unlock(&iigen->ig_spin); + @@ -16770,23 +19271,34 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h + +/* ---------------------------------------------------------------------- */ + ++static inline struct au_hinode *au_hinode(struct au_iinfo *iinfo, ++ aufs_bindex_t bindex) ++{ ++ return iinfo->ii_hinode + bindex; ++} ++ ++static inline int au_is_bad_inode(struct inode *inode) ++{ ++ return !!(is_bad_inode(inode) || !au_hinode(au_ii(inode), 0)); ++} ++ +static inline aufs_bindex_t au_ii_br_id(struct inode *inode, + aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode[0 + bindex].hi_id; ++ return au_hinode(au_ii(inode), bindex)->hi_id; +} + -+static inline aufs_bindex_t au_ibstart(struct inode *inode) ++static inline aufs_bindex_t au_ibtop(struct inode *inode) +{ + IiMustAnyLock(inode); -+ return au_ii(inode)->ii_bstart; ++ return au_ii(inode)->ii_btop; +} + -+static inline aufs_bindex_t au_ibend(struct inode *inode) ++static inline aufs_bindex_t au_ibbot(struct inode *inode) +{ + IiMustAnyLock(inode); -+ return au_ii(inode)->ii_bend; ++ return au_ii(inode)->ii_bbot; +} + +static inline struct au_vdir *au_ivdir(struct inode *inode) @@ -16798,19 +19310,19 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry; ++ return au_hinode(au_ii(inode), bindex)->hi_whdentry; +} + -+static inline void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex) ++static inline void au_set_ibtop(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustWriteLock(inode); -+ au_ii(inode)->ii_bstart = bindex; ++ au_ii(inode)->ii_btop = bindex; +} + -+static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex) ++static inline void au_set_ibbot(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustWriteLock(inode); -+ au_ii(inode)->ii_bend = bindex; ++ au_ii(inode)->ii_bbot = bindex; +} + +static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) @@ -16822,7 +19334,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h +static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode + bindex; ++ return au_hinode(au_ii(inode), bindex); +} + +/* ---------------------------------------------------------------------- */ @@ -16956,33 +19468,44 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h + au_hn_ctl(hdir, /*do_set*/1); +} + -+static inline void au_hn_imtx_lock(struct au_hinode *hdir) ++static inline void au_hn_inode_lock(struct au_hinode *hdir) +{ -+ mutex_lock(&hdir->hi_inode->i_mutex); ++ inode_lock(hdir->hi_inode); + au_hn_suspend(hdir); +} + -+static inline void au_hn_imtx_lock_nested(struct au_hinode *hdir, ++static inline void au_hn_inode_lock_nested(struct au_hinode *hdir, + unsigned int sc __maybe_unused) +{ -+ mutex_lock_nested(&hdir->hi_inode->i_mutex, sc); ++ inode_lock_nested(hdir->hi_inode, sc); + au_hn_suspend(hdir); +} + -+static inline void au_hn_imtx_unlock(struct au_hinode *hdir) ++#if 0 /* unused */ ++#include "vfsub.h" ++static inline void au_hn_inode_lock_shared_nested(struct au_hinode *hdir, ++ unsigned int sc) ++{ ++ 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); -+ mutex_unlock(&hdir->hi_inode->i_mutex); ++ inode_unlock(hdir->hi_inode); +} + +#endif /* __KERNEL__ */ +#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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,219 @@ ++++ linux/fs/aufs/ioctl.c 2018-10-23 12:33:35.599375796 +0200 +@@ -0,0 +1,220 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -17013,7 +19536,7 @@ diff -urN /usr/share/empty/fs/aufs/ioctl.c linux/fs/aufs/ioctl.c +static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) +{ + int err, fd; -+ aufs_bindex_t wbi, bindex, bend; ++ aufs_bindex_t wbi, bindex, bbot; + struct file *h_file; + struct super_block *sb; + struct dentry *root; @@ -17053,10 +19576,10 @@ diff -urN /usr/share/empty/fs/aufs/ioctl.c linux/fs/aufs/ioctl.c + sb = path->dentry->d_sb; + root = sb->s_root; + aufs_read_lock(root, AuLock_IR); -+ bend = au_sbend(sb); ++ bbot = au_sbbot(sb); + if (wbrfd.brid >= 0) { + wbi = au_br_index(sb, wbrfd.brid); -+ if (unlikely(wbi < 0 || wbi > bend)) ++ if (unlikely(wbi < 0 || wbi > bbot)) + goto out_unlock; + } + @@ -17068,7 +19591,7 @@ diff -urN /usr/share/empty/fs/aufs/ioctl.c linux/fs/aufs/ioctl.c + + bindex = wbi + 1; + wbi = -1; -+ for (; bindex <= bend; bindex++) { ++ for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_writable(br->br_perm)) { + wbi = bindex; @@ -17088,7 +19611,7 @@ diff -urN /usr/share/empty/fs/aufs/ioctl.c linux/fs/aufs/ioctl.c + if (IS_ERR(h_file)) + goto out_fd; + -+ atomic_dec(&br->br_count); /* cf. au_h_open() */ ++ au_lcnt_dec(&br->br_nfiles); /* cf. au_h_open() */ + fd_install(fd, h_file); + err = fd; + goto out; /* success */ @@ -17202,10 +19725,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,932 @@ ++++ linux/fs/aufs/i_op_add.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,935 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -17264,7 +19788,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + dir = d_inode(dentry->d_parent); /* dir inode is locked */ + IMustLock(dir); + au_dir_ts(dir, bindex); -+ dir->i_version++; ++ inode_inc_iversion(dir); + au_fhsm_wrote(sb, bindex, /*force*/0); + return 0; /* success */ + } @@ -17386,7 +19910,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + + h_parent = au_pinned_h_parent(pin); + if (udba != AuOpt_UDBA_NONE -+ && au_dbstart(dentry) == bcpup) ++ && au_dbtop(dentry) == bcpup) + err = au_may_add(dentry, bcpup, h_parent, + au_ftest_wrdir(wr_dir_args->flags, ISDIR)); + else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) @@ -17448,15 +19972,16 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + struct simple_arg *arg) +{ + int err, rerr; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + unsigned char created; + const unsigned char try_aopen + = (arg->type == Creat && arg->u.c.try_aopen); ++ struct vfsub_aopen_args *aopen = arg->u.c.aopen; + struct dentry *wh_dentry, *parent; + struct inode *h_dir; + struct super_block *sb; + struct au_branch *br; -+ /* to reuduce stack size */ ++ /* to reduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; @@ -17491,38 +20016,50 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + if (IS_ERR(wh_dentry)) + goto out_parent; + -+ bstart = au_dbstart(dentry); ++ btop = au_dbtop(dentry); + sb = dentry->d_sb; -+ br = au_sbr(sb, bstart); -+ a->h_path.dentry = au_h_dptr(dentry, bstart); ++ br = au_sbr(sb, btop); ++ a->h_path.dentry = au_h_dptr(dentry, btop); + a->h_path.mnt = au_br_mnt(br); + h_dir = au_pinned_h_dir(&a->pin); + switch (arg->type) { + case Creat: -+ err = 0; -+ if (!try_aopen || !h_dir->i_op->atomic_open) ++ if (!try_aopen || !h_dir->i_op->atomic_open) { + err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, + arg->u.c.want_excl); -+ else -+ err = vfsub_atomic_open(h_dir, a->h_path.dentry, -+ arg->u.c.aopen, br); ++ created = !err; ++ if (!err && try_aopen) ++ aopen->file->f_mode |= FMODE_CREATED; ++ } else { ++ aopen->br = br; ++ err = vfsub_atomic_open(h_dir, a->h_path.dentry, aopen); ++ AuDbg("err %d\n", err); ++ AuDbgFile(aopen->file); ++ created = err >= 0 ++ && !!(aopen->file->f_mode & FMODE_CREATED); ++ } + break; + case Symlink: + err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); ++ created = !err; + break; + case Mknod: + err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, + arg->u.m.dev); ++ created = !err; + break; + default: + BUG(); + } -+ created = !err; ++ if (unlikely(err < 0)) ++ goto out_unpin; ++ ++ err = epilog(dir, btop, wh_dentry, dentry); + if (!err) -+ err = epilog(dir, bstart, wh_dentry, dentry); ++ goto out_unpin; /* success */ + + /* revert */ -+ if (unlikely(created && err && d_is_positive(a->h_path.dentry))) { ++ if (created /* && d_is_positive(a->h_path.dentry) */) { + /* no delegation since it is just created */ + rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL, + /*force*/0); @@ -17533,25 +20070,26 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + } + au_dtime_revert(&a->dt); + } ++ if (try_aopen && h_dir->i_op->atomic_open ++ && (aopen->file->f_mode & FMODE_OPENED)) ++ /* aopen->file is still opened */ ++ au_lcnt_dec(&aopen->br->br_nfiles); + -+ if (!err && try_aopen && !h_dir->i_op->atomic_open) -+ *arg->u.c.aopen->opened |= FILE_CREATED; -+ ++out_unpin: + au_unpin(&a->pin); + dput(wh_dentry); -+ +out_parent: + if (!try_aopen) + di_write_unlock(parent); +out_unlock: + if (unlikely(err)) { -+ au_update_dbstart(dentry); ++ au_update_dbtop(dentry); + d_drop(dentry); + } + if (!try_aopen) + aufs_read_unlock(dentry, AuLock_DW); +out_free: -+ kfree(a); ++ au_kfree_rcu(a); +out: + return err; +} @@ -17620,7 +20158,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + }; + + /* copy-up may happen */ -+ mutex_lock(&dir->i_mutex); ++ inode_lock(dir); + + sb = dir->i_sb; + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); @@ -17642,9 +20180,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + if (unlikely(err)) + goto out_parent; + -+ bindex = au_dbstart(parent); -+ au_set_dbstart(dentry, bindex); -+ au_set_dbend(dentry, bindex); ++ bindex = au_dbtop(parent); ++ au_set_dbtop(dentry, bindex); ++ au_set_dbbot(dentry, bindex); + err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); + bindex = err; + if (unlikely(err < 0)) @@ -17661,28 +20199,21 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + goto out_parent; + + h_parent = au_h_dptr(parent, bindex); -+ err = inode_permission(d_inode(h_parent), MAY_WRITE | MAY_EXEC); -+ if (unlikely(err)) -+ goto out_mnt; -+ -+ err = -ENOMEM; -+ h_dentry = d_alloc(h_parent, &dentry->d_name); -+ if (unlikely(!h_dentry)) ++ h_dentry = vfs_tmpfile(h_parent, mode, /*open_flag*/0); ++ if (IS_ERR(h_dentry)) { ++ err = PTR_ERR(h_dentry); + goto out_mnt; ++ } + -+ err = h_dir->i_op->tmpfile(h_dir, h_dentry, mode); -+ if (unlikely(err)) -+ goto out_dentry; -+ -+ au_set_dbstart(dentry, bindex); -+ au_set_dbend(dentry, bindex); ++ au_set_dbtop(dentry, bindex); ++ au_set_dbbot(dentry, bindex); + au_set_h_dptr(dentry, bindex, dget(h_dentry)); + inode = au_new_inode(dentry, /*must_new*/1); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + au_set_h_dptr(dentry, bindex, NULL); -+ au_set_dbstart(dentry, -1); -+ au_set_dbend(dentry, -1); ++ au_set_dbtop(dentry, -1); ++ au_set_dbbot(dentry, -1); + } else { + if (!inode->i_nlink) + set_nlink(inode, 1); @@ -17690,34 +20221,25 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + au_di(dentry)->di_tmpfile = 1; + + /* update without i_mutex */ -+ if (au_ibstart(dir) == au_dbstart(dentry)) ++ if (au_ibtop(dir) == au_dbtop(dentry)) + au_cpup_attr_timesizes(dir); + } -+ -+out_dentry: + dput(h_dentry); ++ +out_mnt: + vfsub_mnt_drop_write(h_mnt); +out_parent: + di_write_unlock(parent); + dput(parent); + di_write_unlock(dentry); -+ if (!err) -+#if 0 -+ /* verbose coding for lock class name */ -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcNonDir_DIINFO); -+#else -+ ; -+#endif -+ else { ++ if (unlikely(err)) { + au_di_fin(dentry); + dentry->d_fsdata = NULL; + } +out_si: + si_read_unlock(sb); +out: -+ mutex_unlock(&dir->i_mutex); ++ inode_unlock(dir); + return err; +} + @@ -17769,7 +20291,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c +{ + int err; + unsigned char plink; -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + struct dentry *h_src_dentry; + struct inode *h_inode, *inode, *delegated; + struct super_block *sb; @@ -17779,13 +20301,13 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + h_inode = NULL; + sb = src_dentry->d_sb; + inode = d_inode(src_dentry); -+ if (au_ibstart(inode) <= a->bdst) ++ if (au_ibtop(inode) <= a->bdst) + h_inode = au_h_iptr(inode, a->bdst); + if (!h_inode || !h_inode->i_nlink) { + /* copyup src_dentry as the name of dentry. */ -+ bend = au_dbend(dentry); -+ if (bend < a->bsrc) -+ au_set_dbend(dentry, a->bsrc); ++ bbot = au_dbbot(dentry); ++ if (bbot < a->bsrc) ++ au_set_dbbot(dentry, a->bsrc); + au_set_h_dptr(dentry, a->bsrc, + dget(au_h_dptr(src_dentry, a->bsrc))); + dget(a->h_path.dentry); @@ -17820,7 +20342,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + spin_unlock(&dentry->d_lock); + AuDbg("temporary d_inode...done\n"); + au_set_h_dptr(dentry, a->bsrc, NULL); -+ au_set_dbend(dentry, bend); ++ au_set_dbbot(dentry, bbot); + } else { + /* the inode of src_dentry already exists on a.bdst branch */ + h_src_dentry = d_find_alias(h_inode); @@ -17898,7 +20420,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + goto out_unlock; + + a->src_parent = dget_parent(src_dentry); -+ wr_dir_args.force_btgt = au_ibstart(inode); ++ wr_dir_args.force_btgt = au_ibtop(inode); + + di_write_lock_parent(a->parent); + wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); @@ -17910,15 +20432,15 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + + err = 0; + sb = dentry->d_sb; -+ a->bdst = au_dbstart(dentry); ++ a->bdst = au_dbtop(dentry); + a->h_path.dentry = au_h_dptr(dentry, a->bdst); + a->h_path.mnt = au_sbr_mnt(sb, a->bdst); -+ a->bsrc = au_ibstart(inode); ++ a->bsrc = au_ibtop(inode); + h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); + if (!h_src_dentry && au_di(src_dentry)->di_tmpfile) + h_src_dentry = dget(au_hi_wh(inode, a->bsrc)); + if (!h_src_dentry) { -+ a->bsrc = au_dbstart(src_dentry); ++ a->bsrc = au_dbtop(src_dentry); + h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); + AuDebugOn(!h_src_dentry); + } else if (IS_ERR(h_src_dentry)) { @@ -17926,6 +20448,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 unnecessary. ++ */ + if (au_opt_test(au_mntflags(sb), PLINK)) { + if (a->bdst < a->bsrc + /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) @@ -17989,7 +20515,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + } + + au_dir_ts(dir, a->bdst); -+ dir->i_version++; ++ inode_inc_iversion(dir); + inc_nlink(inode); + inode->i_ctime = dir->i_ctime; + d_instantiate(dentry, au_igrab(inode)); @@ -18018,12 +20544,12 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + dput(a->src_parent); +out_unlock: + if (unlikely(err)) { -+ au_update_dbstart(dentry); ++ au_update_dbtop(dentry); + d_drop(dentry); + } + aufs_read_and_write_unlock2(dentry, src_dentry); +out_kfree: -+ kfree(a); ++ au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; @@ -18036,7 +20562,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + unsigned char diropq; + struct path h_path; + struct dentry *wh_dentry, *parent, *opq_dentry; -+ struct mutex *h_mtx; ++ struct inode *h_inode; + struct super_block *sb; + struct { + struct au_pin pin; @@ -18070,7 +20596,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + goto out_parent; + + sb = dentry->d_sb; -+ bindex = au_dbstart(dentry); ++ bindex = au_dbtop(dentry); + h_path.dentry = au_h_dptr(dentry, bindex); + h_path.mnt = au_sbr_mnt(sb, bindex); + err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); @@ -18079,12 +20605,12 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + + /* make the dir opaque */ + diropq = 0; -+ h_mtx = &d_inode(h_path.dentry)->i_mutex; ++ h_inode = d_inode(h_path.dentry); + if (wh_dentry + || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); + opq_dentry = au_diropq_create(dentry, bindex); -+ mutex_unlock(h_mtx); ++ inode_unlock(h_inode); + err = PTR_ERR(opq_dentry); + if (IS_ERR(opq_dentry)) + goto out_dir; @@ -18101,9 +20627,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + /* revert */ + if (diropq) { + AuLabel(revert opq); -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); + rerr = au_diropq_remove(dentry, bindex); -+ mutex_unlock(h_mtx); ++ inode_unlock(h_inode); + if (rerr) { + AuIOErr("%pd reverting diropq failed(%d, %d)\n", + dentry, err, rerr); @@ -18127,21 +20653,22 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c + di_write_unlock(parent); +out_unlock: + if (unlikely(err)) { -+ au_update_dbstart(dentry); ++ au_update_dbtop(dentry); + d_drop(dentry); + } + aufs_read_unlock(dentry, AuLock_DW); +out_free: -+ kfree(a); ++ au_kfree_rcu(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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,1484 @@ ++++ linux/fs/aufs/i_op.c 2018-12-27 13:19:17.708416053 +0100 +@@ -0,0 +1,1506 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -18173,19 +20700,22 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + int err; + const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); + ++ err = -EPERM; ++ if (write_mask && IS_IMMUTABLE(h_inode)) ++ goto out; ++ + err = -EACCES; -+ if ((write_mask && IS_IMMUTABLE(h_inode)) -+ || ((mask & MAY_EXEC) -+ && S_ISREG(h_inode->i_mode) -+ && (path_noexec(h_path) -+ || !(h_inode->i_mode & S_IXUGO)))) ++ if (((mask & MAY_EXEC) ++ && S_ISREG(h_inode->i_mode) ++ && (path_noexec(h_path) ++ || !(h_inode->i_mode & 0111)))) + goto out; + + /* + * - skip the lower fs test in the case of write to ro branch. + * - nfs dir permission write check is optimized, but a policy for + * link/rename requires a real check. -+ * - nfs always sets MS_POSIXACL regardless its mount option 'noacl.' ++ * - nfs always sets SB_POSIXACL regardless its mount option 'noacl.' + * in this case, generic_permission() returns -EOPNOTSUPP. + */ + if ((write_mask && !au_br_writable(brperm)) @@ -18193,7 +20723,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + && write_mask && !(mask & MAY_READ)) + || !h_inode->i_op->permission) { + /* AuLabel(generic_permission); */ -+ /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ ++ /* AuDbg("get_acl %ps\n", h_inode->i_op->get_acl); */ + err = generic_permission(h_inode, mask); + if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) + err = h_inode->i_op->permission(h_inode, mask); @@ -18229,7 +20759,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c +static int aufs_permission(struct inode *inode, int mask) +{ + int err; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + const unsigned char isdir = !!S_ISDIR(inode->i_mode), + write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); + struct inode *h_inode; @@ -18253,14 +20783,14 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + || write_mask + || au_opt_test(au_mntflags(sb), DIRPERM1)) { + err = au_busy_or_stale(); -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ h_inode = au_h_iptr(inode, au_ibtop(inode)); + if (unlikely(!h_inode + || (h_inode->i_mode & S_IFMT) + != (inode->i_mode & S_IFMT))) + goto out; + + err = 0; -+ bindex = au_ibstart(inode); ++ bindex = au_ibtop(inode); + br = au_sbr(sb, bindex); + err = h_permission(h_inode, mask, &br->br_path, br->br_perm); + if (write_mask @@ -18279,8 +20809,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + + /* non-write to dir */ + err = 0; -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { ++ bbot = au_ibbot(inode); ++ for (bindex = au_ibtop(inode); !err && bindex <= bbot; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (h_inode) { + err = au_busy_or_stale(); @@ -18339,8 +20869,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + if (!err) + err = au_digen_test(parent, au_sigen(sb)); + if (!err) { -+ npositive = au_lkup_dentry(dentry, au_dbstart(parent), -+ /*type*/0); ++ /* regardless LOOKUP_CREATE, always ALLOW_NEG */ ++ npositive = au_lkup_dentry(dentry, au_dbtop(parent), ++ AuLkup_ALLOW_NEG); + err = npositive; + } + di_read_unlock(parent, AuLock_IR); @@ -18381,18 +20912,6 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + +out_unlock: + di_write_unlock(dentry); -+ if (inode) { -+ /* verbose coding for lock class name */ -+ if (unlikely(S_ISLNK(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcSymlink_DIINFO); -+ else if (unlikely(S_ISDIR(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcDir_DIINFO); -+ else /* likely */ -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcNonDir_DIINFO); -+ } +out_si: + si_read_unlock(sb); +out: @@ -18401,59 +20920,118 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + +/* ---------------------------------------------------------------------- */ + ++/* ++ * very dirty and complicated aufs ->atomic_open(). ++ * aufs_atomic_open() ++ * + au_aopen_or_create() ++ * + add_simple() ++ * + vfsub_atomic_open() ++ * + branch fs ->atomic_open() ++ * may call the actual 'open' for h_file ++ * + inc br_nfiles only if opened ++ * + au_aopen_no_open() or au_aopen_do_open() ++ * ++ * au_aopen_do_open() ++ * + finish_open() ++ * + au_do_aopen() ++ * + au_do_open() the body of all 'open' ++ * + au_do_open_nondir() ++ * set the passed h_file ++ * ++ * au_aopen_no_open() ++ * + finish_no_open() ++ */ ++ +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); +} + ++static int au_aopen_do_open(struct file *file, struct dentry *dentry, ++ struct aopen_node *aopen_node) ++{ ++ int err; ++ struct hlist_bl_head *aopen; ++ ++ AuLabel(here); ++ aopen = &au_sbi(dentry->d_sb)->si_aopen; ++ au_hbl_add(&aopen_node->hblist, aopen); ++ err = finish_open(file, dentry, au_do_aopen); ++ au_hbl_del(&aopen_node->hblist, aopen); ++ /* AuDbgFile(file); */ ++ AuDbg("%pd%s%s\n", dentry, ++ (file->f_mode & FMODE_CREATED) ? " created" : "", ++ (file->f_mode & FMODE_OPENED) ? " opened" : ""); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_aopen_no_open(struct file *file, struct dentry *dentry) ++{ ++ int err; ++ ++ AuLabel(here); ++ dget(dentry); ++ err = finish_no_open(file, dentry); ++ ++ AuTraceErr(err); ++ return err; ++} ++ +static int aufs_atomic_open(struct inode *dir, struct dentry *dentry, + struct file *file, unsigned int open_flag, -+ umode_t create_mode, int *opened) ++ umode_t create_mode) +{ -+ int err, h_opened = *opened; -+ struct dentry *parent; -+ struct dentry *d; -+ struct au_sphlhead *aopen; ++ int err, did_open; ++ unsigned int lkup_flags; ++ aufs_bindex_t bindex; ++ struct super_block *sb; ++ struct dentry *parent, *d; + struct vfsub_aopen_args args = { + .open_flag = open_flag, -+ .create_mode = create_mode, -+ .opened = &h_opened ++ .create_mode = create_mode + }; + struct aopen_node aopen_node = { + .file = file + }; + + IMustLock(dir); -+ AuDbg("open_flag 0x%x\n", open_flag); ++ AuDbg("open_flag 0%o\n", open_flag); + AuDbgDentry(dentry); + + err = 0; + if (!au_di(dentry)) { -+ d = aufs_lookup(dir, dentry, /*flags*/0); ++ lkup_flags = LOOKUP_OPEN; ++ if (open_flag & O_CREAT) ++ lkup_flags |= LOOKUP_CREATE; ++ d = aufs_lookup(dir, dentry, lkup_flags); + if (IS_ERR(d)) { + err = PTR_ERR(d); ++ AuTraceErr(err); + goto out; + } else if (d) { + /* @@ -18461,8 +21039,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + * another error will be returned later. + */ + d_drop(d); -+ dput(d); + AuDbgDentry(d); ++ dput(d); + } + AuDbgDentry(dentry); + } @@ -18470,74 +21048,73 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + if (d_is_positive(dentry) + || d_unhashed(dentry) + || d_unlinked(dentry) -+ || !(open_flag & O_CREAT)) -+ goto out_no_open; ++ || !(open_flag & O_CREAT)) { ++ err = au_aopen_no_open(file, dentry); ++ goto out; /* success */ ++ } + + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); + if (unlikely(err)) + goto out; + ++ sb = dentry->d_sb; + parent = dentry->d_parent; /* dir is locked */ + di_write_lock_parent(parent); -+ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); -+ if (unlikely(err)) -+ goto out_unlock; ++ err = au_lkup_dentry(dentry, /*btop*/0, AuLkup_ALLOW_NEG); ++ if (unlikely(err < 0)) ++ goto out_parent; + + AuDbgDentry(dentry); -+ if (d_is_positive(dentry)) -+ goto out_unlock; ++ if (d_is_positive(dentry)) { ++ err = au_aopen_no_open(file, dentry); ++ goto out_parent; /* success */ ++ } + -+ args.file = get_empty_filp(); ++ args.file = alloc_empty_file(file->f_flags, current_cred()); + err = PTR_ERR(args.file); + if (IS_ERR(args.file)) -+ goto out_unlock; ++ goto out_parent; + -+ args.file->f_flags = file->f_flags; ++ bindex = au_dbtop(dentry); + err = au_aopen_or_create(dir, dentry, &args); + AuTraceErr(err); + AuDbgFile(args.file); ++ file->f_mode = args.file->f_mode & ~FMODE_OPENED; ++ did_open = !!(args.file->f_mode & FMODE_OPENED); ++ if (!did_open) { ++ fput(args.file); ++ args.file = NULL; ++ } ++ di_write_unlock(parent); ++ di_write_unlock(dentry); + if (unlikely(err < 0)) { -+ if (h_opened & FILE_OPENED) ++ if (args.file) + fput(args.file); -+ else -+ put_filp(args.file); -+ goto out_unlock; ++ goto out_sb; + } + -+ /* some filesystems don't set FILE_CREATED while succeeded? */ -+ *opened |= FILE_CREATED; -+ if (h_opened & FILE_OPENED) -+ aopen_node.h_file = args.file; ++ if (!did_open) ++ err = au_aopen_no_open(file, dentry); + else { -+ put_filp(args.file); -+ args.file = NULL; ++ aopen_node.h_file = args.file; ++ err = au_aopen_do_open(file, dentry, &aopen_node); + } -+ aopen = &au_sbi(dir->i_sb)->si_aopen; -+ au_sphl_add(&aopen_node.hlist, aopen); -+ err = finish_open(file, dentry, au_do_aopen, opened); -+ au_sphl_del(&aopen_node.hlist, aopen); -+ AuTraceErr(err); -+ AuDbgFile(file); -+ if (aopen_node.h_file) -+ fput(aopen_node.h_file); ++ if (unlikely(err < 0)) { ++ if (args.file) ++ fput(args.file); ++ if (did_open) ++ au_lcnt_dec(&args.br->br_nfiles); ++ } ++ goto out_sb; /* success */ + -+out_unlock: ++out_parent: + di_write_unlock(parent); -+ aufs_read_unlock(dentry, AuLock_DW); -+ AuDbgDentry(dentry); -+ if (unlikely(err)) -+ goto out; -+out_no_open: -+ if (!err && !(*opened & FILE_CREATED)) { -+ AuLabel(out_no_open); -+ dget(dentry); -+ err = finish_no_open(file, dentry); -+ } ++ di_write_unlock(dentry); ++out_sb: ++ si_read_unlock(sb); +out: -+ AuDbg("%pd%s%s\n", dentry, -+ (*opened & FILE_CREATED) ? " created" : "", -+ (*opened & FILE_OPENED) ? " opened" : ""); + AuTraceErr(err); ++ AuDbgFile(file); + return err; +} + @@ -18546,7 +21123,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + +static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, + const unsigned char add_entry, aufs_bindex_t bcpup, -+ aufs_bindex_t bstart) ++ aufs_bindex_t btop) +{ + int err; + struct dentry *h_parent; @@ -18559,9 +21136,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + + err = 0; + if (!au_h_dptr(parent, bcpup)) { -+ if (bstart > bcpup) ++ if (btop > bcpup) + err = au_cpup_dirs(dentry, bcpup); -+ else if (bstart < bcpup) ++ else if (btop < bcpup) + err = au_cpdown_dirs(dentry, bcpup); + else + BUG(); @@ -18569,15 +21146,15 @@ 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); -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ inode_lock_shared_nested(h_dir, AuLsc_I_PARENT); + err = au_lkup_neg(dentry, bcpup, /*wh*/0); + /* todo: no unlock here */ -+ mutex_unlock(&h_dir->i_mutex); ++ inode_unlock_shared(h_dir); + + AuDbg("bcpup %d\n", bcpup); + if (!err) { + if (d_really_is_negative(dentry)) -+ au_set_h_dptr(dentry, bstart, NULL); ++ au_set_h_dptr(dentry, btop, NULL); + au_update_dbrange(dentry, /*do_put_zero*/0); + } + } @@ -18601,7 +21178,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c +{ + int err; + unsigned int flags; -+ aufs_bindex_t bcpup, bstart, src_bstart; ++ aufs_bindex_t bcpup, btop, src_btop; + const unsigned char add_entry + = au_ftest_wrdir(args->flags, ADD_ENTRY) + | au_ftest_wrdir(args->flags, TMPFILE); @@ -18612,13 +21189,13 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + sb = dentry->d_sb; + sbinfo = au_sbi(sb); + parent = dget_parent(dentry); -+ bstart = au_dbstart(dentry); -+ bcpup = bstart; ++ btop = au_dbtop(dentry); ++ bcpup = btop; + if (args->force_btgt < 0) { + if (src_dentry) { -+ src_bstart = au_dbstart(src_dentry); -+ if (src_bstart < bstart) -+ bcpup = src_bstart; ++ src_btop = au_dbtop(src_dentry); ++ if (src_btop < btop) ++ bcpup = src_btop; + } else if (add_entry) { + flags = 0; + if (au_ftest_wrdir(args->flags, ISDIR)) @@ -18647,18 +21224,18 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + AuDebugOn(au_test_ro(sb, bcpup, d_inode(dentry))); + } + -+ AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); ++ AuDbg("btop %d, bcpup %d\n", btop, bcpup); + err = bcpup; -+ if (bcpup == bstart) ++ if (bcpup == btop) + goto out; /* success */ + + /* copyup the new parent into the branch we process */ -+ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); ++ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, btop); + if (err >= 0) { + if (d_really_is_negative(dentry)) { -+ au_set_h_dptr(dentry, bstart, NULL); -+ au_set_dbstart(dentry, bcpup); -+ au_set_dbend(dentry, bcpup); ++ au_set_h_dptr(dentry, btop, NULL); ++ au_set_dbtop(dentry, bcpup); ++ au_set_dbbot(dentry, bcpup); + } + AuDebugOn(add_entry + && !au_ftest_wrdir(args->flags, TMPFILE) @@ -18675,7 +21252,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c +void au_pin_hdir_unlock(struct au_pin *p) +{ + if (p->hdir) -+ au_hn_imtx_unlock(p->hdir); ++ au_hn_inode_unlock(p->hdir); +} + +int au_pin_hdir_lock(struct au_pin *p) @@ -18687,7 +21264,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + goto out; + + /* even if an error happens later, keep this lock */ -+ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); ++ au_hn_inode_lock_nested(p->hdir, p->lsc_hi); + + err = -EBUSY; + if (unlikely(p->hdir->hi_inode != d_inode(p->h_parent))) @@ -18728,17 +21305,17 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + return err; +} + -+void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) ++static void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) +{ -+#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) -+ p->hdir->hi_inode->i_mutex.owner = task; ++#if !defined(CONFIG_RWSEM_GENERIC_SPINLOCK) && defined(CONFIG_RWSEM_SPIN_ON_OWNER) ++ p->hdir->hi_inode->i_rwsem.owner = task; +#endif +} + +void au_pin_hdir_acquire_nest(struct au_pin *p) +{ + if (p->hdir) { -+ mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map, ++ rwsem_acquire_nest(&p->hdir->hi_inode->i_rwsem.dep_map, + p->lsc_hi, 0, NULL, _RET_IP_); + au_pin_hdir_set_owner(p, current); + } @@ -18748,7 +21325,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c +{ + if (p->hdir) { + au_pin_hdir_set_owner(p, p->task); -+ mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); ++ rwsem_release(&p->hdir->hi_inode->i_rwsem.dep_map, 1, _RET_IP_); + } +} + @@ -18800,7 +21377,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + } + + p->h_dentry = NULL; -+ if (p->bindex <= au_dbend(p->dentry)) ++ if (p->bindex <= au_dbbot(p->dentry)) + p->h_dentry = au_h_dptr(p->dentry, p->bindex); + + p->parent = dget_parent(p->dentry); @@ -18916,7 +21493,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c +{ + int err; + loff_t sz; -+ aufs_bindex_t bstart, ibstart; ++ aufs_bindex_t btop, ibtop; + struct dentry *hi_wh, *parent; + struct inode *inode; + struct au_wr_dir_args wr_dir_args = { @@ -18927,16 +21504,16 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + if (d_is_dir(dentry)) + au_fset_wrdir(wr_dir_args.flags, ISDIR); + /* plink or hi_wh() case */ -+ bstart = au_dbstart(dentry); ++ btop = au_dbtop(dentry); + inode = d_inode(dentry); -+ ibstart = au_ibstart(inode); -+ if (bstart != ibstart && !au_test_ro(inode->i_sb, ibstart, inode)) -+ wr_dir_args.force_btgt = ibstart; ++ ibtop = au_ibtop(inode); ++ if (btop != ibtop && !au_test_ro(inode->i_sb, ibtop, inode)) ++ wr_dir_args.force_btgt = ibtop; + err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); + if (unlikely(err < 0)) + goto out; + a->btgt = err; -+ if (err != bstart) ++ if (err != btop) + au_fset_icpup(a->flags, DID_CPUP); + + err = 0; @@ -18952,14 +21529,14 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + if (unlikely(err)) + goto out_parent; + -+ a->h_path.dentry = au_h_dptr(dentry, bstart); + sz = -1; ++ 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)) { -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD); + if (ia->ia_size < i_size_read(a->h_inode)) + sz = ia->ia_size; -+ mutex_unlock(&a->h_inode->i_mutex); ++ inode_unlock_shared(a->h_inode); + } + + hi_wh = NULL; @@ -18994,7 +21571,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = a->btgt, -+ .bsrc = bstart, ++ .bsrc = btop, + .len = sz, + .pin = &a->pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN @@ -19019,7 +21596,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + } +out: + if (!err) -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(a->h_inode, AuLsc_I_CHILD); + return err; +} + @@ -19034,6 +21611,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)) @@ -19052,7 +21633,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); @@ -19103,9 +21685,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + f = NULL; + if (ia->ia_valid & ATTR_FILE) + f = ia->ia_file; -+ mutex_unlock(&a->h_inode->i_mutex); ++ inode_unlock(a->h_inode); + err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(a->h_inode, AuLsc_I_CHILD); + } else { + delegated = NULL; + while (1) { @@ -19118,14 +21700,20 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + break; + } + } ++ /* ++ * regardless aufs 'acl' option setting. ++ * why don't all acl-aware fs call this func from their ->setattr()? ++ */ ++ if (!err && (ia->ia_valid & ATTR_MODE)) ++ err = vfsub_acl_chmod(a->h_inode, ia->ia_mode); + if (!err) + au_cpup_attr_changeable(inode); + +out_unlock: -+ mutex_unlock(&a->h_inode->i_mutex); ++ inode_unlock(a->h_inode); + au_unpin(&a->pin); + if (unlikely(err)) -+ au_update_dbstart(dentry); ++ au_update_dbtop(dentry); +out_dentry: + di_write_unlock(dentry); + if (file) { @@ -19136,7 +21724,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: -+ kfree(a); ++ au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; @@ -19171,15 +21759,15 @@ 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 au_srxattr *arg) ++ssize_t au_sxattr(struct dentry *dentry, struct inode *inode, ++ struct au_sxattr *arg) +{ + int err; + struct path h_path; + struct super_block *sb; + struct au_icpup_args *a; -+ struct inode *inode, *h_inode; ++ struct inode *h_inode; + -+ inode = d_inode(dentry); + IMustLock(inode); + + err = -ENOMEM; @@ -19198,20 +21786,19 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + if (unlikely(err)) + goto out_di; + -+ mutex_unlock(&a->h_inode->i_mutex); ++ inode_unlock(a->h_inode); + switch (arg->type) { + case AU_XATTR_SET: ++ AuDebugOn(d_is_negative(h_path.dentry)); + err = vfsub_setxattr(h_path.dentry, + 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); @@ -19222,13 +21809,13 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + + au_unpin(&a->pin); + if (unlikely(err)) -+ au_update_dbstart(dentry); ++ au_update_dbtop(dentry); + +out_di: + di_write_unlock(dentry); + si_read_unlock(sb); +out_kfree: -+ kfree(a); ++ au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; @@ -19265,11 +21852,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; @@ -19286,6 +21874,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); @@ -19313,8 +21904,9 @@ 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_ibstart(inode); ++ bindex = au_ibtop(inode); + h_path->mnt = au_sbr_mnt(sb, bindex); + h_sb = h_path->mnt->mnt_sb; + if (!force @@ -19322,7 +21914,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + && udba_none) + goto out; /* success */ + -+ if (au_dbstart(dentry) == bindex) ++ if (au_dbtop(dentry) == bindex) + h_path->dentry = au_h_dptr(dentry, bindex); + else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { + h_path->dentry = au_plink_lkup(inode, bindex); @@ -19337,21 +21929,23 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + return err; +} + -+static int aufs_getattr(struct vfsmount *mnt __maybe_unused, -+ struct dentry *dentry, struct kstat *st) ++static int aufs_getattr(const struct path *path, struct kstat *st, ++ u32 request, unsigned int query) +{ + int err; + unsigned char positive; + struct path h_path; ++ struct dentry *dentry; + struct inode *inode; + struct super_block *sb; + ++ dentry = path->dentry; + inode = d_inode(dentry); + 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*/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)) @@ -19360,7 +21954,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + + positive = d_is_positive(h_path.dentry); + if (positive) -+ err = vfs_getattr(&h_path, st); ++ /* no vfsub version */ ++ err = vfs_getattr(&h_path, st, request, query); + if (!err) { + if (positive) + au_refresh_iattr(inode, st, @@ -19383,62 +21978,20 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + +/* ---------------------------------------------------------------------- */ + -+/* -+ * Assumption: -+ * - the number of symlinks is not so many. -+ * -+ * Structure: -+ * - sbinfo (instead of iinfo) contains an hlist of struct au_symlink. -+ * If iinfo contained the hlist, then it would be rather large waste of memory -+ * I am afraid. -+ * - struct au_symlink contains the necessary info for h_inode follow_link() and -+ * put_link(). -+ */ -+ -+struct au_symlink { -+ union { -+ struct hlist_node hlist; -+ struct rcu_head rcu; -+ }; -+ -+ struct inode *h_inode; -+ void *h_cookie; -+}; -+ -+static void au_symlink_add(struct super_block *sb, struct au_symlink *slink, -+ struct inode *h_inode, void *cookie) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ ihold(h_inode); -+ slink->h_inode = h_inode; -+ slink->h_cookie = cookie; -+ sbinfo = au_sbi(sb); -+ au_sphl_add(&slink->hlist, &sbinfo->si_symlink); -+} -+ -+static void au_symlink_del(struct super_block *sb, struct au_symlink *slink) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ /* do not iput() within rcu */ -+ iput(slink->h_inode); -+ slink->h_inode = NULL; -+ sbinfo = au_sbi(sb); -+ au_sphl_del_rcu(&slink->hlist, &sbinfo->si_symlink); -+ kfree_rcu(slink, rcu); -+} -+ -+static const char *aufs_follow_link(struct dentry *dentry, void **cookie) ++static const char *aufs_get_link(struct dentry *dentry, struct inode *inode, ++ struct delayed_call *done) +{ + const char *ret; -+ struct inode *inode, *h_inode; + struct dentry *h_dentry; -+ struct au_symlink *slink; ++ struct inode *h_inode; + int err; + aufs_bindex_t bindex; + + ret = NULL; /* suppress a warning */ ++ err = -ECHILD; ++ if (!dentry) ++ goto out; ++ + err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); + if (unlikely(err)) + goto out; @@ -19449,19 +22002,14 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + + err = -EINVAL; + inode = d_inode(dentry); -+ bindex = au_ibstart(inode); ++ bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); -+ if (unlikely(!h_inode->i_op->follow_link)) -+ goto out_unlock; -+ -+ err = -ENOMEM; -+ slink = kmalloc(sizeof(*slink), GFP_NOFS); -+ if (unlikely(!slink)) ++ if (unlikely(!h_inode->i_op->get_link)) + goto out_unlock; + + err = -EBUSY; + h_dentry = NULL; -+ if (au_dbstart(dentry) <= bindex) { ++ if (au_dbtop(dentry) <= bindex) { + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry) + dget(h_dentry); @@ -19470,28 +22018,20 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + h_dentry = d_find_any_alias(h_inode); + if (IS_ERR(h_dentry)) { + err = PTR_ERR(h_dentry); -+ goto out_free; ++ goto out_unlock; + } + } + if (unlikely(!h_dentry)) -+ goto out_free; ++ goto out_unlock; + + err = 0; -+ AuDbg("%pf\n", h_inode->i_op->follow_link); ++ AuDbg("%ps\n", h_inode->i_op->get_link); + AuDbgDentry(h_dentry); -+ ret = h_inode->i_op->follow_link(h_dentry, cookie); ++ ret = vfs_get_link(h_dentry, done); + dput(h_dentry); ++ if (IS_ERR(ret)) ++ err = PTR_ERR(ret); + -+ if (!IS_ERR_OR_NULL(ret)) { -+ au_symlink_add(inode->i_sb, slink, h_inode, *cookie); -+ *cookie = slink; -+ AuDbg("slink %p\n", slink); -+ goto out_unlock; /* success */ -+ } -+ -+out_free: -+ slink->h_inode = NULL; -+ kfree_rcu(slink, rcu); +out_unlock: + aufs_read_unlock(dentry, AuLock_IR); +out: @@ -19501,38 +22041,57 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c + return ret; +} + -+static void aufs_put_link(struct inode *inode, void *cookie) -+{ -+ struct au_symlink *slink; -+ struct inode *h_inode; ++/* ---------------------------------------------------------------------- */ + -+ slink = cookie; -+ AuDbg("slink %p\n", slink); -+ h_inode = slink->h_inode; -+ AuDbg("%pf\n", h_inode->i_op->put_link); -+ AuDbgInode(h_inode); -+ if (h_inode->i_op->put_link) -+ h_inode->i_op->put_link(h_inode, slink->h_cookie); -+ au_symlink_del(inode->i_sb, slink); ++static int au_is_special(struct inode *inode) ++{ ++ return (inode->i_mode & (S_IFBLK | S_IFCHR | S_IFIFO | S_IFSOCK)); +} + -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags) ++static int aufs_update_time(struct inode *inode, struct timespec64 *ts, ++ int flags) +{ + int err; ++ aufs_bindex_t bindex; + struct super_block *sb; + struct inode *h_inode; ++ struct vfsmount *h_mnt; + + sb = inode->i_sb; ++ WARN_ONCE((flags & S_ATIME) && !IS_NOATIME(inode), ++ "unexpected s_flags 0x%lx", sb->s_flags); ++ + /* mmap_sem might be acquired already, cf. aufs_mmap() */ + lockdep_off(); + si_read_lock(sb, AuLock_FLUSH); + ii_write_lock_child(inode); -+ lockdep_on(); -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ err = vfsub_update_time(h_inode, ts, flags); -+ lockdep_off(); ++ ++ err = 0; ++ bindex = au_ibtop(inode); ++ h_inode = au_h_iptr(inode, bindex); ++ if (!au_test_ro(sb, bindex, inode)) { ++ h_mnt = au_sbr_mnt(sb, bindex); ++ err = vfsub_mnt_want_write(h_mnt); ++ if (!err) { ++ err = vfsub_update_time(h_inode, ts, flags); ++ vfsub_mnt_drop_write(h_mnt); ++ } ++ } else if (au_is_special(h_inode)) { ++ /* ++ * Never copy-up here. ++ * These special files may already be opened and used for ++ * communicating. If we copied it up, then the communication ++ * would be corrupted. ++ */ ++ AuWarn1("timestamps for i%lu are ignored " ++ "since it is on readonly branch (hi%lu).\n", ++ inode->i_ino, h_inode->i_ino); ++ } else if (flags & ~S_ATIME) { ++ err = -EIO; ++ AuIOErr1("unexpected flags 0x%x\n", flags); ++ AuDebugOn(1); ++ } ++ + if (!err) + au_cpup_attr_timesizes(inode); + ii_write_unlock(inode); @@ -19561,15 +22120,10 @@ 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, -+ .follow_link = aufs_follow_link, -+ .put_link = aufs_put_link, ++ .get_link = aufs_get_link, + + /* .update_time = aufs_update_time */ + }, @@ -19594,10 +22148,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, @@ -19615,10 +22166,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 @@ -19626,10 +22174,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,510 @@ ++++ linux/fs/aufs/i_op_del.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,512 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -19664,25 +22213,25 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c +int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) +{ + int need_wh, err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + struct super_block *sb; + + sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); ++ btop = au_dbtop(dentry); + if (*bcpup < 0) { -+ *bcpup = bstart; -+ if (au_test_ro(sb, bstart, d_inode(dentry))) { ++ *bcpup = btop; ++ if (au_test_ro(sb, btop, d_inode(dentry))) { + err = AuWbrCopyup(au_sbi(sb), dentry); + *bcpup = err; + if (unlikely(err < 0)) + goto out; + } + } else -+ AuDebugOn(bstart < *bcpup ++ AuDebugOn(btop < *bcpup + || au_test_ro(sb, *bcpup, d_inode(dentry))); -+ AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); ++ AuDbg("bcpup %d, btop %d\n", *bcpup, btop); + -+ if (*bcpup != bstart) { ++ if (*bcpup != btop) { + err = au_cpup_dirs(dentry, *bcpup); + if (unlikely(err)) + goto out; @@ -19697,7 +22246,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c + au_di_cp(tmp, dinfo); + au_di_swap(tmp, dinfo); + /* returns the number of positive dentries */ -+ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0); ++ need_wh = au_lkup_dentry(dentry, btop + 1, ++ /* AuLkup_IGNORE_PERM */ 0); + au_di_swap(tmp, dinfo); + au_rw_write_unlock(&tmp->di_rwsem); + au_di_free(tmp); @@ -19777,7 +22327,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c + +/* + * decide the branch where we operate for @dentry. the branch index will be set -+ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent ++ * @rbcpup. after deciding it, 'pin' it and store the timestamps of the parent + * dir for reverting. + * when a new whiteout is necessary, create it. + */ @@ -19808,7 +22358,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c + + h_path.dentry = au_pinned_h_parent(pin); + if (udba != AuOpt_UDBA_NONE -+ && au_dbstart(dentry) == bcpup) { ++ && au_dbtop(dentry) == bcpup) { + err = au_may_del(dentry, bcpup, h_path.dentry, isdir); + wh_dentry = ERR_PTR(err); + if (unlikely(err)) @@ -19898,7 +22448,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c + inode->i_ctime = dir->i_ctime; + + au_dir_ts(dir, bindex); -+ dir->i_version++; ++ inode_inc_iversion(dir); +} + +/* @@ -19930,10 +22480,10 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c +int aufs_unlink(struct inode *dir, struct dentry *dentry) +{ + int err; -+ aufs_bindex_t bwh, bindex, bstart; ++ aufs_bindex_t bwh, bindex, btop; + struct inode *inode, *h_dir, *delegated; + struct dentry *parent, *wh_dentry; -+ /* to reuduce stack size */ ++ /* to reduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; @@ -19959,7 +22509,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c + if (unlikely(d_is_dir(dentry))) + goto out_unlock; /* possible? */ + -+ bstart = au_dbstart(dentry); ++ btop = au_dbtop(dentry); + bwh = au_dbwh(dentry); + bindex = -1; + parent = dentry->d_parent; /* dir inode is locked */ @@ -19970,10 +22520,10 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c + if (IS_ERR(wh_dentry)) + goto out_parent; + -+ a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); -+ a->h_path.dentry = au_h_dptr(dentry, bstart); ++ a->h_path.mnt = au_sbr_mnt(dentry->d_sb, btop); ++ a->h_path.dentry = au_h_dptr(dentry, btop); + dget(a->h_path.dentry); -+ if (bindex == bstart) { ++ if (bindex == btop) { + h_dir = au_pinned_h_dir(&a->pin); + delegated = NULL; + err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); @@ -19994,7 +22544,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c + epilog(dir, dentry, bindex); + + /* update target timestamps */ -+ if (bindex == bstart) { ++ if (bindex == btop) { + vfsub_update_h_iattr(&a->h_path, /*did*/NULL); + /*ignore*/ + inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; @@ -20023,7 +22573,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: -+ kfree(a); ++ au_kfree_rcu(a); +out: + return err; +} @@ -20031,11 +22581,11 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c +int aufs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int err, rmdir_later; -+ aufs_bindex_t bwh, bindex, bstart; ++ aufs_bindex_t bwh, bindex, btop; + struct inode *inode; + struct dentry *parent, *wh_dentry, *h_dentry; + struct au_whtmp_rmdir *args; -+ /* to reuduce stack size */ ++ /* to reduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; @@ -20071,7 +22621,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c + if (unlikely(err)) + goto out_parent; + -+ bstart = au_dbstart(dentry); ++ btop = au_dbtop(dentry); + bwh = au_dbwh(dentry); + bindex = -1; + wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, @@ -20080,18 +22630,18 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c + if (IS_ERR(wh_dentry)) + goto out_parent; + -+ h_dentry = au_h_dptr(dentry, bstart); ++ h_dentry = au_h_dptr(dentry, btop); + dget(h_dentry); + rmdir_later = 0; -+ if (bindex == bstart) { -+ err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); ++ if (bindex == btop) { ++ err = renwh_and_rmdir(dentry, btop, &args->whlist, dir); + if (err > 0) { + rmdir_later = err; + err = 0; + } + } else { + /* stop monitoring */ -+ au_hn_free(au_hi(inode, bstart)); ++ au_hn_free(au_hi(inode, btop)); + + /* dir inode is locked */ + IMustLock(d_inode(wh_dentry->d_parent)); @@ -20104,7 +22654,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c + epilog(dir, dentry, bindex); + + if (rmdir_later) { -+ au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); ++ au_whtmp_kick_rmdir(dir, btop, h_dentry, args); + args = NULL; + } + @@ -20133,17 +22683,18 @@ 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: -+ kfree(a); ++ au_kfree_rcu(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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,1015 @@ ++++ linux/fs/aufs/i_op_ren.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,1249 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -20169,27 +22720,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 bstart; ++ aufs_bindex_t btop, bdiropq; + } sd[AuSrcDst]; + +#define src_dentry sd[AuSRC].dentry @@ -20200,9 +22761,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_bstart sd[AuSRC].bstart ++#define src_btop sd[AuSRC].btop ++#define src_bdiropq sd[AuSRC].bdiropq + +#define dst_dentry sd[AuDST].dentry +#define dst_dir sd[AuDST].dir @@ -20212,21 +22775,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_bstart sd[AuDST].bstart ++#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; +}; + +/* ---------------------------------------------------------------------- */ @@ -20245,16 +22814,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_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(a->src_dentry, a->btgt); -+ au_hn_imtx_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) @@ -20273,7 +22855,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"); @@ -20306,7 +22888,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"); @@ -20346,15 +22928,13 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + struct inode *delegated; + + d = a->src_dentry; -+ if (au_dbstart(d) == a->btgt) { ++ 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_dbstart(d) != a->btgt); ++ 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"); @@ -20398,127 +22978,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_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ diropq = au_diropq_create(a->src_dentry, a->btgt); -+ au_hn_imtx_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_bstart != 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_dbend(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 */ -+ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); ++ 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); @@ -20556,25 +23195,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 bstart; ++ 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); + -+ bstart = au_dbstart(dentry); -+ if (bstart != btgt) { ++ 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)); @@ -20586,16 +23235,20 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + goto out; + } + -+ if (bstart == au_dbtaildir(dentry)) ++ if (btop == au_dbtaildir(dentry)) + return 0; /* success */ + + err = au_test_empty_lower(dentry); + +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; +} @@ -20611,7 +23264,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)); @@ -20619,18 +23272,21 @@ 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_dbstart(d, a->dst_bstart); -+ err = may_rename_dstdir(d, &a->whlist); -+ au_set_dbstart(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_dbstart(d)); ++ a->dst_h_dentry = au_h_dptr(d, au_dbtop(d)); + if (unlikely(err)) + goto out; + + d = a->src_dentry; -+ a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); -+ if (au_ftest_ren(a->flags, ISDIR)) { -+ err = may_rename_srcdir(d, a->btgt); ++ a->src_h_dentry = au_h_dptr(d, au_dbtop(d)); ++ 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; @@ -20651,9 +23307,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + int err, isdir; + struct inode *h_inode; + -+ if (a->src_bstart == a->btgt) { ++ 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; @@ -20662,7 +23318,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + } + + err = 0; -+ if (a->dst_bstart != a->btgt) ++ if (a->dst_btop != a->btgt) + goto out; + + err = -ENOTEMPTY; @@ -20670,7 +23326,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, @@ -20697,7 +23353,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + * locking order + * (VFS) + * - src_dir and dir by lock_rename() -+ * - inode if exitsts ++ * - inode if exists + * (aufs) + * - lock all + * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, @@ -20720,7 +23376,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)); +} + @@ -20738,18 +23397,35 @@ 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); + if (unlikely(a->src_hdir->hi_inode != d_inode(a->src_h_parent) + || a->dst_hdir->hi_inode != d_inode(a->dst_h_parent))) + err = au_busy_or_stale(); -+ if (!err && au_dbstart(a->src_dentry) == a->btgt) ++ if (!err && au_dbtop(a->src_dentry) == a->btgt) + err = au_h_verify(a->src_h_dentry, udba, + d_inode(a->src_h_parent), a->src_h_parent, + a->br); -+ if (!err && au_dbstart(a->dst_dentry) == a->btgt) ++ if (!err && au_dbtop(a->dst_dentry) == a->btgt) + err = au_h_verify(a->dst_h_dentry, udba, + d_inode(a->dst_h_parent), a->dst_h_parent, + a->br); @@ -20770,28 +23446,38 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + struct inode *dir; + + dir = a->dst_dir; -+ dir->i_version++; -+ if (au_ftest_ren(a->flags, ISDIR)) { ++ inode_inc_iversion(dir); ++ 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; ++ inode_inc_iversion(dir); ++ 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)) ++ inode_inc_iversion(dir); ++ if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) + au_cpup_attr_nlink(dir, /*force*/1); + au_dir_ts(dir, a->btgt); +} + +static void au_ren_refresh(struct au_ren_args *a) +{ -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct dentry *d, *h_d; + struct inode *i, *h_i; + struct super_block *sb; @@ -20804,40 +23490,51 @@ 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 { -+ bend = a->btgt; -+ for (bindex = au_dbstart(d); bindex < bend; bindex++) ++ bbot = a->btgt; ++ for (bindex = au_dbtop(d); bindex < bbot; bindex++) + au_set_h_dptr(d, bindex, NULL); -+ bend = au_dbend(d); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) ++ bbot = au_dbbot(d); ++ for (bindex = a->btgt + 1; bindex <= bbot; bindex++) + au_set_h_dptr(d, bindex, NULL); + 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); -+ bend = au_dbend(d); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { ++ bbot = au_dbbot(d); ++ for (bindex = a->btgt + 1; bindex <= bbot; bindex++) { + h_d = au_h_dptr(d, bindex); + if (h_d) + au_set_h_dptr(d, bindex, NULL); + } -+ au_set_dbend(d, a->btgt); ++ au_set_dbbot(d, a->btgt); + + sb = d->d_sb; + i = a->src_inode; + if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) + return; /* success */ + -+ bend = au_ibend(i); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { ++ bbot = au_ibbot(i); ++ for (bindex = a->btgt + 1; bindex <= bbot; bindex++) { + h_i = au_h_iptr(i, bindex); + if (h_i) { + au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); @@ -20845,7 +23542,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + au_set_h_iptr(i, bindex, NULL, 0); + } + } -+ au_set_ibend(i, a->btgt); ++ au_set_ibbot(i, a->btgt); +} + +/* ---------------------------------------------------------------------- */ @@ -20872,7 +23569,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + return btgt; +} + -+/* sets src_bstart, dst_bstart and btgt */ ++/* sets src_btop, dst_btop and btgt */ +static int au_ren_wbr(struct au_ren_args *a) +{ + int err; @@ -20881,16 +23578,19 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + .flags = AuWrDir_ADD_ENTRY + }; + -+ a->src_bstart = au_dbstart(a->src_dentry); -+ a->dst_bstart = au_dbstart(a->dst_dentry); -+ if (au_ftest_ren(a->flags, ISDIR)) ++ a->src_btop = au_dbtop(a->src_dentry); ++ a->dst_btop = au_dbtop(a->dst_dentry); ++ 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_bstart; -+ if (a->dst_inode && a->dst_bstart < a->src_bstart) -+ wr_dir_args.force_btgt = a->dst_bstart; ++ wr_dir_args.force_btgt = a->src_btop; ++ if (a->dst_inode && a->dst_btop < a->src_btop) ++ wr_dir_args.force_btgt = a->dst_btop; + 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; +} @@ -20899,19 +23599,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); + } @@ -20920,25 +23621,25 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c +static void au_ren_rev_dt(int err, struct au_ren_args *a) +{ + struct dentry *h_d; -+ struct mutex *h_mtx; ++ 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_mtx = &d_inode(h_d)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ h_inode = d_inode(h_d); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); + au_dtime_revert(a->src_dt + AuCHILD); -+ mutex_unlock(h_mtx); ++ 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_mtx = &d_inode(h_d)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ h_inode = d_inode(h_d); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); + au_dtime_revert(a->dst_dt + AuCHILD); -+ mutex_unlock(h_mtx); ++ inode_unlock(h_inode); + } + } +} @@ -20946,22 +23647,33 @@ 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; ++ BUILD_BUG_ON(sizeof(a->exchange) == sizeof(u8) ++ && RENAME_EXCHANGE > U8_MAX); ++ a->exchange = _flags & RENAME_EXCHANGE; + a->src_dir = _src_dir; + a->src_dentry = _src_dentry; + a->src_inode = NULL; @@ -20975,20 +23687,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; ++ } ++ 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, flags); ++ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, ++ lock_flags); + if (unlikely(err)) + goto out_free; + @@ -20998,14 +23724,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; @@ -21021,7 +23747,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 */ @@ -21037,40 +23763,44 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + goto out_children; + + /* prepare the writable parent dir on the same branch */ -+ if (a->dst_bstart == a->btgt) { -+ au_fset_ren(a->flags, WHDST); ++ if (a->dst_btop == a->btgt) { ++ 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_bstart != a->btgt) { -+ struct au_pin pin; -+ ++ if (a->src_btop != a->btgt) { + err = au_pin(&pin, a->src_dentry, a->btgt, + au_opt_udba(a->src_dentry->d_sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); @@ -21078,20 +23808,45 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c + struct au_cp_generic cpg = { + .dentry = a->src_dentry, + .bdst = a->btgt, -+ .bsrc = a->src_bstart, ++ .bsrc = a->src_btop, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; -+ AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); ++ AuDebugOn(au_dbtop(a->src_dentry) != a->src_btop); + err = au_sio_cpup_simple(&cpg); + au_unpin(&pin); + } + if (unlikely(err)) + goto out_children; -+ a->src_bstart = a->btgt; ++ 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 */ @@ -21100,20 +23855,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); @@ -21123,26 +23892,41 @@ 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: + au_ren_unlock(a); +out_children: + au_nhash_wh_free(&a->whlist); -+ if (err && a->dst_inode && a->dst_bstart != a->btgt) { -+ AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); ++ if (err && a->dst_inode && a->dst_btop != a->btgt) { ++ AuDbg("btop %d, btgt %d\n", a->dst_btop, a->btgt); + au_set_h_dptr(a->dst_dentry, a->btgt, NULL); -+ au_set_dbstart(a->dst_dentry, a->dst_bstart); ++ au_set_dbtop(a->dst_dentry, a->dst_btop); + } +out_parent: -+ if (!err) -+ d_move(a->src_dentry, a->dst_dentry); -+ else { -+ au_update_dbstart(a->dst_dentry); ++ 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); @@ -21152,15 +23936,16 @@ 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); -+ kfree(a); ++ au_kfree_rcu(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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,185 @@ ++++ linux/fs/aufs/Kconfig 2018-06-04 09:08:09.181412645 +0200 +@@ -0,0 +1,199 @@ ++# SPDX-License-Identifier: GPL-2.0 +config AUFS_FS + tristate "Aufs (Advanced multi layered unification filesystem) support" + help @@ -21279,6 +24064,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 @@ -21346,12 +24144,203 @@ diff -urN /usr/share/empty/fs/aufs/Kconfig linux/fs/aufs/Kconfig + Automatic configuration for internal use. + When aufs supports Magic SysRq, enabled automatically. +endif +diff -urN /usr/share/empty/fs/aufs/lcnt.h linux/fs/aufs/lcnt.h +--- /usr/share/empty/fs/aufs/lcnt.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/aufs/lcnt.h 2018-10-23 12:33:35.599375796 +0200 +@@ -0,0 +1,186 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 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 . ++ */ ++ ++/* ++ * simple long counter wrapper ++ */ ++ ++#ifndef __AUFS_LCNT_H__ ++#define __AUFS_LCNT_H__ ++ ++#ifdef __KERNEL__ ++ ++#include "debug.h" ++ ++#define AuLCntATOMIC 1 ++#define AuLCntPCPUCNT 2 ++/* ++ * why does percpu_refcount require extra synchronize_rcu()s in ++ * au_br_do_free() ++ */ ++#define AuLCntPCPUREF 3 ++ ++/* #define AuLCntChosen AuLCntATOMIC */ ++#define AuLCntChosen AuLCntPCPUCNT ++/* #define AuLCntChosen AuLCntPCPUREF */ ++ ++#if AuLCntChosen == AuLCntATOMIC ++#include ++ ++typedef atomic_long_t au_lcnt_t; ++ ++static inline int au_lcnt_init(au_lcnt_t *cnt, void *release __maybe_unused) ++{ ++ atomic_long_set(cnt, 0); ++ return 0; ++} ++ ++static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) ++{ ++ /* empty */ ++} ++ ++static inline void au_lcnt_fin(au_lcnt_t *cnt __maybe_unused, ++ int do_sync __maybe_unused) ++{ ++ /* empty */ ++} ++ ++static inline void au_lcnt_inc(au_lcnt_t *cnt) ++{ ++ atomic_long_inc(cnt); ++} ++ ++static inline void au_lcnt_dec(au_lcnt_t *cnt) ++{ ++ atomic_long_dec(cnt); ++} ++ ++static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev __maybe_unused) ++{ ++ return atomic_long_read(cnt); ++} ++#endif ++ ++#if AuLCntChosen == AuLCntPCPUCNT ++#include ++ ++typedef struct percpu_counter au_lcnt_t; ++ ++static inline int au_lcnt_init(au_lcnt_t *cnt, void *release __maybe_unused) ++{ ++ return percpu_counter_init(cnt, 0, GFP_NOFS); ++} ++ ++static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) ++{ ++ /* empty */ ++} ++ ++static inline void au_lcnt_fin(au_lcnt_t *cnt, int do_sync __maybe_unused) ++{ ++ percpu_counter_destroy(cnt); ++} ++ ++static inline void au_lcnt_inc(au_lcnt_t *cnt) ++{ ++ percpu_counter_inc(cnt); ++} ++ ++static inline void au_lcnt_dec(au_lcnt_t *cnt) ++{ ++ percpu_counter_dec(cnt); ++} ++ ++static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev __maybe_unused) ++{ ++ s64 n; ++ ++ n = percpu_counter_sum(cnt); ++ BUG_ON(n < 0); ++ if (LONG_MAX != LLONG_MAX ++ && n > LONG_MAX) ++ AuWarn1("%s\n", "wrap-around"); ++ ++ return n; ++} ++#endif ++ ++#if AuLCntChosen == AuLCntPCPUREF ++#include ++ ++typedef struct percpu_ref au_lcnt_t; ++ ++static inline int au_lcnt_init(au_lcnt_t *cnt, percpu_ref_func_t *release) ++{ ++ if (!release) ++ release = percpu_ref_exit; ++ return percpu_ref_init(cnt, release, /*percpu mode*/0, GFP_NOFS); ++} ++ ++static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) ++{ ++ synchronize_rcu(); ++} ++ ++static inline void au_lcnt_fin(au_lcnt_t *cnt, int do_sync) ++{ ++ percpu_ref_kill(cnt); ++ if (do_sync) ++ au_lcnt_wait_for_fin(cnt); ++} ++ ++static inline void au_lcnt_inc(au_lcnt_t *cnt) ++{ ++ percpu_ref_get(cnt); ++} ++ ++static inline void au_lcnt_dec(au_lcnt_t *cnt) ++{ ++ percpu_ref_put(cnt); ++} ++ ++/* ++ * avoid calling this func as possible. ++ */ ++static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev) ++{ ++ long l; ++ ++ percpu_ref_switch_to_atomic_sync(cnt); ++ l = atomic_long_read(&cnt->count); ++ if (do_rev) ++ percpu_ref_switch_to_percpu(cnt); ++ ++ /* percpu_ref is initialized by 1 instead of 0 */ ++ return l - 1; ++} ++#endif ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define AuLCntZero(val) do { \ ++ long l = val; \ ++ if (l) \ ++ AuDbg("%s = %ld\n", #val, l); \ ++} while (0) ++#else ++#define AuLCntZero(val) do {} while (0) ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_LCNT_H__ */ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,146 @@ ++++ linux/fs/aufs/loop.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,148 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -21456,7 +24445,8 @@ diff -urN /usr/share/empty/fs/aufs/loop.c linux/fs/aufs/loop.c + new_nelem = au_warn_loopback_nelem + au_warn_loopback_step; + a = au_kzrealloc(au_warn_loopback_array, + au_warn_loopback_nelem * sizeof(unsigned long), -+ new_nelem * sizeof(unsigned long), GFP_ATOMIC); ++ new_nelem * sizeof(unsigned long), GFP_ATOMIC, ++ /*may_shrink*/0); + if (a) { + au_warn_loopback_nelem = new_nelem; + au_warn_loopback_array = a; @@ -21494,14 +24484,15 @@ diff -urN /usr/share/empty/fs/aufs/loop.c linux/fs/aufs/loop.c +{ + if (backing_file_func) + symbol_put(loop_backing_file); -+ kfree(au_warn_loopback_array); ++ au_kfree_try_rcu(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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,52 @@ ++++ linux/fs/aufs/loop.h 2018-08-12 23:43:05.460124736 +0200 +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -21554,8 +24545,9 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,30 @@ ++++ linux/fs/aufs/magic.mk 2018-06-04 09:08:09.188079511 +0200 +@@ -0,0 +1,31 @@ ++# SPDX-License-Identifier: GPL-2.0 + +# defined in ${srctree}/fs/fuse/inode.c +# tristate @@ -21588,8 +24580,9 @@ 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-01-13 20:11:11.666426853 +0100 -@@ -0,0 +1,44 @@ ++++ linux/fs/aufs/Makefile 2018-06-04 09:08:09.181412645 +0200 +@@ -0,0 +1,46 @@ ++# SPDX-License-Identifier: GPL-2.0 + +include ${src}/magic.mk +ifeq (${CONFIG_AUFS_FS},m) @@ -21628,6 +24621,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 @@ -21636,10 +24630,11 @@ 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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,221 @@ ++++ linux/fs/aufs/module.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,273 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -21663,42 +24658,66 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c +#include +#include "aufs.h" + -+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp) ++/* shrinkable realloc */ ++void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink) +{ -+ if (new_sz <= nused) -+ return p; ++ size_t sz; ++ int diff; + -+ p = krealloc(p, new_sz, gfp); -+ if (p) ++ sz = 0; ++ diff = -1; ++ if (p) { ++#if 0 /* unused */ ++ if (!new_sz) { ++ au_kfree_rcu(p); ++ p = NULL; ++ goto out; ++ } ++#else ++ AuDebugOn(!new_sz); ++#endif ++ sz = ksize(p); ++ diff = au_kmidx_sub(sz, new_sz); ++ } ++ if (sz && !diff) ++ goto out; ++ ++ if (sz < new_sz) ++ /* expand or SLOB */ ++ p = krealloc(p, new_sz, gfp); ++ else if (new_sz < sz && may_shrink) { ++ /* shrink */ ++ void *q; ++ ++ q = kmalloc(new_sz, gfp); ++ if (q) { ++ if (p) { ++ memcpy(q, p, new_sz); ++ au_kfree_try_rcu(p); ++ } ++ p = q; ++ } else ++ p = NULL; ++ } ++ ++out: ++ return p; ++} ++ ++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp, ++ int may_shrink) ++{ ++ p = au_krealloc(p, new_sz, gfp, may_shrink); ++ if (p && new_sz > nused) + memset(p + nused, 0, new_sz - nused); + return p; +} + +/* ---------------------------------------------------------------------- */ -+ +/* + * aufs caches + */ -+struct kmem_cache *au_cachep[AuCache_Last]; -+static int __init au_cache_init(void) -+{ -+ au_cachep[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); -+ if (au_cachep[AuCache_DINFO]) -+ /* SLAB_DESTROY_BY_RCU */ -+ au_cachep[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, -+ au_icntnr_init_once); -+ if (au_cachep[AuCache_ICNTNR]) -+ au_cachep[AuCache_FINFO] = AuCacheCtor(au_finfo, -+ au_fi_init_once); -+ if (au_cachep[AuCache_FINFO]) -+ au_cachep[AuCache_VDIR] = AuCache(au_vdir); -+ if (au_cachep[AuCache_VDIR]) -+ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); -+ if (au_cachep[AuCache_DEHSTR]) -+ return 0; -+ -+ return -ENOMEM; -+} ++struct kmem_cache *au_cache[AuCache_Last]; + +static void au_cache_fin(void) +{ @@ -21713,11 +24732,32 @@ 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); + for (i = 0; i < AuCache_HNOTIFY; i++) { -+ kmem_cache_destroy(au_cachep[i]); -+ au_cachep[i] = NULL; ++ kmem_cache_destroy(au_cache[i]); ++ au_cache[i] = NULL; + } +} + ++static int __init au_cache_init(void) ++{ ++ au_cache[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); ++ if (au_cache[AuCache_DINFO]) ++ /* SLAB_DESTROY_BY_RCU */ ++ au_cache[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, ++ au_icntnr_init_once); ++ if (au_cache[AuCache_ICNTNR]) ++ au_cache[AuCache_FINFO] = AuCacheCtor(au_finfo, ++ au_fi_init_once); ++ 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(); ++ return -ENOMEM; ++} ++ +/* ---------------------------------------------------------------------- */ + +int au_dir_roflags; @@ -21727,11 +24767,9 @@ 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_splhead au_sbilist; ++struct hlist_bl_head au_sbilist; +#endif + -+struct lock_class_key au_lc_key[AuLcKey_Last]; -+ +/* + * functions for module interface. + */ @@ -21746,12 +24784,12 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c +/* this module parameter has no meaning when SYSFS is disabled */ +int sysaufs_brs = 1; +MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); -+module_param_named(brs, sysaufs_brs, int, S_IRUGO); ++module_param_named(brs, sysaufs_brs, int, 0444); + +/* this module parameter has no meaning when USER_NS is disabled */ -+static bool au_userns; ++bool au_userns; +MODULE_PARM_DESC(allow_userns, "allow unprivileged to mount under userns"); -+module_param_named(allow_userns, au_userns, bool, S_IRUGO); ++module_param_named(allow_userns, au_userns, bool, 0444); + +/* ---------------------------------------------------------------------- */ + @@ -21762,9 +24800,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; @@ -21790,6 +24828,8 @@ 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; + ++ memset(au_cache, 0, sizeof(au_cache)); /* including hnotify */ ++ + au_sbilist_init(); + sysaufs_brs_init(); + au_debug_init(); @@ -21797,9 +24837,12 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c + err = sysaufs_init(); + if (unlikely(err)) + goto out; -+ err = au_procfs_init(); ++ err = dbgaufs_init(); + if (unlikely(err)) + goto out_sysaufs; ++ err = au_procfs_init(); ++ if (unlikely(err)) ++ goto out_dbgaufs; + err = au_wkq_init(); + if (unlikely(err)) + goto out_procfs; @@ -21837,6 +24880,8 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c + au_wkq_fin(); +out_procfs: + au_procfs_fin(); ++out_dbgaufs: ++ dbgaufs_fin(); +out_sysaufs: + sysaufs_fin(); + au_dy_fin(); @@ -21853,6 +24898,7 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c + au_loopback_fin(); + au_wkq_fin(); + au_procfs_fin(); ++ dbgaufs_fin(); + sysaufs_fin(); + au_dy_fin(); +} @@ -21861,10 +24907,11 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c +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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,104 @@ ++++ linux/fs/aufs/module.h 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,166 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -21890,34 +24937,83 @@ diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h +#ifdef __KERNEL__ + +#include ++#include "debug.h" ++#include "dentry.h" ++#include "dir.h" ++#include "file.h" ++#include "inode.h" + +struct path; +struct seq_file; + +/* module parameters */ +extern int sysaufs_brs; ++extern bool au_userns; + +/* ---------------------------------------------------------------------- */ + +extern int au_dir_roflags; + -+enum { -+ AuLcNonDir_FIINFO, -+ AuLcNonDir_DIINFO, -+ AuLcNonDir_IIINFO, ++void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink); ++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp, ++ int may_shrink); + -+ AuLcDir_FIINFO, -+ AuLcDir_DIINFO, -+ AuLcDir_IIINFO, ++/* ++ * Comparing the size of the object with sizeof(struct rcu_head) ++ * case 1: object is always larger ++ * --> au_kfree_rcu() or au_kfree_do_rcu() ++ * case 2: object is always smaller ++ * --> au_kfree_small() ++ * case 3: object can be any size ++ * --> au_kfree_try_rcu() ++ */ + -+ AuLcSymlink_DIINFO, -+ AuLcSymlink_IIINFO, ++static inline void au_kfree_do_rcu(const void *p) ++{ ++ struct { ++ struct rcu_head rcu; ++ } *a = (void *)p; + -+ AuLcKey_Last -+}; -+extern struct lock_class_key au_lc_key[AuLcKey_Last]; ++ kfree_rcu(a, rcu); ++} ++ ++#define au_kfree_rcu(_p) do { \ ++ typeof(_p) p = (_p); \ ++ BUILD_BUG_ON(sizeof(*p) < sizeof(struct rcu_head)); \ ++ if (p) \ ++ au_kfree_do_rcu(p); \ ++ } while (0) ++ ++#define au_kfree_do_sz_test(sz) (sz >= sizeof(struct rcu_head)) ++#define au_kfree_sz_test(p) (p && au_kfree_do_sz_test(ksize(p))) ++ ++static inline void au_kfree_try_rcu(const void *p) ++{ ++ if (!p) ++ return; ++ if (au_kfree_sz_test(p)) ++ au_kfree_do_rcu(p); ++ else ++ kfree(p); ++} ++ ++static inline void au_kfree_small(const void *p) ++{ ++ if (!p) ++ return; ++ AuDebugOn(au_kfree_sz_test(p)); ++ kfree(p); ++} ++ ++static inline int au_kmidx_sub(size_t sz, size_t new_sz) ++{ ++#ifndef CONFIG_SLOB ++ return kmalloc_index(sz) - kmalloc_index(new_sz); ++#else ++ return -1; /* SLOB is untested */ ++#endif ++} + -+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp); +int au_seq_path(struct seq_file *seq, struct path *path); + +#ifdef CONFIG_PROC_FS @@ -21942,19 +25038,31 @@ diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h + AuCache_Last +}; + ++extern struct kmem_cache *au_cache[AuCache_Last]; ++ +#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD) +#define AuCache(type) KMEM_CACHE(type, AuCacheFlags) +#define AuCacheCtor(type, ctor) \ + kmem_cache_create(#type, sizeof(struct type), \ + __alignof__(struct type), AuCacheFlags, ctor) + -+extern struct kmem_cache *au_cachep[]; -+ -+#define AuCacheFuncs(name, index) \ -+static inline struct au_##name *au_cache_alloc_##name(void) \ -+{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \ -+static inline void au_cache_free_##name(struct au_##name *p) \ -+{ kmem_cache_free(au_cachep[AuCache_##index], p); } ++#define AuCacheFuncs(name, index) \ ++ static inline struct au_##name *au_cache_alloc_##name(void) \ ++ { return kmem_cache_alloc(au_cache[AuCache_##index], GFP_NOFS); } \ ++ static inline void au_cache_free_##name##_norcu(struct au_##name *p) \ ++ { kmem_cache_free(au_cache[AuCache_##index], p); } \ ++ \ ++ static inline void au_cache_free_##name##_rcu_cb(struct rcu_head *rcu) \ ++ { void *p = rcu; \ ++ p -= offsetof(struct au_##name, rcu); \ ++ kmem_cache_free(au_cache[AuCache_##index], p); } \ ++ static inline void au_cache_free_##name##_rcu(struct au_##name *p) \ ++ { BUILD_BUG_ON(sizeof(struct au_##name) < sizeof(struct rcu_head)); \ ++ call_rcu(&p->rcu, au_cache_free_##name##_rcu_cb); } \ ++ \ ++ static inline void au_cache_free_##name(struct au_##name *p) \ ++ { /* au_cache_free_##name##_norcu(p); */ \ ++ au_cache_free_##name##_rcu(p); } + +AuCacheFuncs(dinfo, DINFO); +AuCacheFuncs(icntnr, ICNTNR); @@ -21969,10 +25077,11 @@ diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h +#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-01-13 20:11:11.669760262 +0100 -@@ -0,0 +1,703 @@ ++++ linux/fs/aufs/mvdown.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,705 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2011-2015 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 @@ -22039,29 +25148,29 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c +static int find_lower_writable(struct au_mvd_args *a) +{ + struct super_block *sb; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_branch *br; + + sb = a->sb; + bindex = a->mvd_bsrc; -+ bend = au_sbend(sb); ++ bbot = au_sbbot(sb); + if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) -+ for (bindex++; bindex <= bend; bindex++) { ++ for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm) -+ && (!(au_br_sb(br)->s_flags & MS_RDONLY))) ++ && !sb_rdonly(au_br_sb(br))) + return bindex; + } + else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) -+ for (bindex++; bindex <= bend; bindex++) { ++ for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_rdonly(br)) + return bindex; + } + else -+ for (bindex++; bindex <= bend; bindex++) { ++ for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); -+ if (!(au_br_sb(br)->s_flags & MS_RDONLY)) { ++ if (!sb_rdonly(au_br_sb(br))) { + if (au_br_rdonly(br)) + a->mvdown.flags + |= AUFS_MVDOWN_ROLOWER_R; @@ -22082,7 +25191,7 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c + a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); + a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); + a->mvd_h_dst_parent = NULL; -+ if (au_dbend(a->parent) >= a->mvd_bdst) ++ if (au_dbbot(a->parent) >= a->mvd_bdst) + a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); + if (!a->mvd_h_dst_parent) { + err = au_cpdown_dirs(a->dentry, a->mvd_bdst); @@ -22336,20 +25445,20 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c + /* maintain internal array */ + if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { + au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); -+ au_set_dbstart(a->dentry, a->mvd_bdst); ++ au_set_dbtop(a->dentry, a->mvd_bdst); + au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); -+ au_set_ibstart(a->inode, a->mvd_bdst); ++ au_set_ibtop(a->inode, a->mvd_bdst); + } else { + /* hide the lower */ + au_set_h_dptr(a->dentry, a->mvd_bdst, NULL); -+ au_set_dbend(a->dentry, a->mvd_bsrc); ++ au_set_dbbot(a->dentry, a->mvd_bsrc); + au_set_h_iptr(a->inode, a->mvd_bdst, NULL, /*flags*/0); -+ au_set_ibend(a->inode, a->mvd_bsrc); ++ au_set_ibbot(a->inode, a->mvd_bsrc); + } -+ if (au_dbend(a->dentry) < a->mvd_bdst) -+ au_set_dbend(a->dentry, a->mvd_bdst); -+ if (au_ibend(a->inode) < a->mvd_bdst) -+ au_set_ibend(a->inode, a->mvd_bdst); ++ if (au_dbbot(a->dentry) < a->mvd_bdst) ++ au_set_dbbot(a->dentry, a->mvd_bdst); ++ if (au_ibbot(a->inode) < a->mvd_bdst) ++ au_set_ibbot(a->inode, a->mvd_bdst); + +out_unlock: + au_do_unlock(dmsg, a); @@ -22367,7 +25476,7 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c + + err = 0; + plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); -+ if (au_dbstart(a->dentry) == a->mvd_bsrc ++ if (au_dbtop(a->dentry) == a->mvd_bsrc + && au_dcount(a->dentry) == 1 + && atomic_read(&a->inode->i_count) == 1 + /* && a->mvd_h_src_inode->i_nlink == 1 */ @@ -22378,7 +25487,7 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c + err = -EBUSY; + AU_MVD_PR(dmsg, + "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", -+ a->mvd_bsrc, au_dbstart(a->dentry), au_dcount(a->dentry), ++ a->mvd_bsrc, au_dbtop(a->dentry), au_dcount(a->dentry), + atomic_read(&a->inode->i_count), a->inode->i_nlink, + a->mvd_h_src_inode->i_nlink, + plinked, plinked ? au_plink_test(a->inode) : 0); @@ -22437,11 +25546,12 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c + au_di_swap(tmp, dinfo); + + /* returns the number of positive dentries */ -+ err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, /*type*/0); ++ err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, ++ /* AuLkup_IGNORE_PERM */ 0); + if (!err) + a->bwh = au_dbwh(a->dentry); + else if (err > 0) -+ a->bfound = au_dbstart(a->dentry); ++ a->bfound = au_dbtop(a->dentry); + + au_di_swap(tmp, dinfo); + au_rw_write_unlock(&tmp->di_rwsem); @@ -22528,22 +25638,22 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c + + err = -EINVAL; + if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) -+ a->mvd_bsrc = au_ibstart(a->inode); ++ a->mvd_bsrc = au_ibtop(a->inode); + else { + a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); + if (unlikely(a->mvd_bsrc < 0 -+ || (a->mvd_bsrc < au_dbstart(a->dentry) -+ || au_dbend(a->dentry) < a->mvd_bsrc ++ || (a->mvd_bsrc < au_dbtop(a->dentry) ++ || au_dbbot(a->dentry) < a->mvd_bsrc + || !au_h_dptr(a->dentry, a->mvd_bsrc)) -+ || (a->mvd_bsrc < au_ibstart(a->inode) -+ || au_ibend(a->inode) < a->mvd_bsrc ++ || (a->mvd_bsrc < au_ibtop(a->inode) ++ || au_ibbot(a->inode) < a->mvd_bsrc + || !au_h_iptr(a->inode, a->mvd_bsrc)))) { + a->mvd_errno = EAU_MVDOWN_NOUPPER; + AU_MVD_PR(dmsg, "no upper\n"); + goto out; + } + } -+ if (unlikely(a->mvd_bsrc == au_sbend(a->sb))) { ++ if (unlikely(a->mvd_bsrc == au_sbbot(a->sb))) { + a->mvd_errno = EAU_MVDOWN_BOTTOM; + AU_MVD_PR(dmsg, "on the bottom\n"); + goto out; @@ -22573,7 +25683,7 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c + } else { + a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); + if (unlikely(a->mvd_bdst < 0 -+ || au_sbend(a->sb) < a->mvd_bdst)) { ++ || au_sbbot(a->sb) < a->mvd_bdst)) { + a->mvd_errno = EAU_MVDOWN_NOLOWERBR; + AU_MVD_PR(dmsg, "no lower brid\n"); + goto out; @@ -22631,14 +25741,14 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c + dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); + args->parent = dget_parent(dentry); + args->dir = d_inode(args->parent); -+ mutex_lock_nested(&args->dir->i_mutex, I_MUTEX_PARENT); ++ inode_lock_nested(args->dir, I_MUTEX_PARENT); + dput(args->parent); + if (unlikely(args->parent != dentry->d_parent)) { + AU_MVD_PR(dmsg, "parent dir is moved\n"); + goto out_dir; + } + -+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); ++ inode_lock_nested(inode, I_MUTEX_CHILD); + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_NOPLMW); + if (unlikely(err)) + goto out_inode; @@ -22662,24 +25772,25 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c + di_write_unlock(args->parent); + aufs_read_unlock(dentry, AuLock_DW); +out_inode: -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); +out_dir: -+ mutex_unlock(&args->dir->i_mutex); ++ inode_unlock(args->dir); +out_free: + e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); + if (unlikely(e)) + err = -EFAULT; -+ kfree(args); ++ au_kfree_rcu(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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,1859 @@ ++++ linux/fs/aufs/opts.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,1877 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -22727,6 +25838,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 +}; @@ -22781,10 +25893,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"}, @@ -22797,7 +25917,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"}, @@ -22835,7 +25955,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 + @@ -23104,6 +26224,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"}, @@ -23114,28 +26238,6 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + {-1, NULL} +}; + -+/* -+ * cf. linux/lib/parser.c and cmdline.c -+ * gave up calling memparse() since it uses simple_strtoull() instead of -+ * kstrto...(). -+ */ -+static int noinline_for_stack -+au_match_ull(substring_t *s, unsigned long long *result) -+{ -+ int err; -+ unsigned int len; -+ char a[32]; -+ -+ err = -ERANGE; -+ len = s->to - s->from; -+ if (len + 1 <= sizeof(a)) { -+ memcpy(a, s->from, len); -+ a[len] = '\0'; -+ err = kstrtoull(a, 0, result); -+ } -+ return err; -+} -+ +static int au_wbr_mfs_wmark(substring_t *arg, char *str, + struct au_opt_wbr_create *create) +{ @@ -23143,7 +26245,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + unsigned long long ull; + + err = 0; -+ if (!au_match_ull(arg, &ull)) ++ if (!match_u64(arg, &ull)) + create->mfsrr_watermark = ull; + else { + pr_err("bad integer in %s\n", str); @@ -23179,6 +26281,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) @@ -23187,6 +26290,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)) { @@ -23397,10 +26501,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, @@ -23415,6 +26521,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; @@ -23480,7 +26592,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + add->perm = AuBrPerm_RO; + if (au_test_fs_rr(add->path.dentry->d_sb)) + add->perm = AuBrPerm_RR; -+ else if (!bindex && !(sb_flags & MS_RDONLY)) ++ else if (!bindex && !(sb_flags & SB_RDONLY)) + add->perm = AuBrPerm_RW; + } + opt->type = Opt_add; @@ -23517,7 +26629,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + err = -EINVAL; + root = sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); -+ if (bindex < 0 || au_sbend(sb) < bindex) { ++ if (bindex < 0 || au_sbbot(sb) < bindex) { + pr_err("out of bounds, %d\n", bindex); + goto out; + } @@ -23543,7 +26655,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + mod->path = args[0].from; + p = strchr(mod->path, '='); + if (unlikely(!p)) { -+ pr_err("no permssion %s\n", args[0].from); ++ pr_err("no permission %s\n", args[0].from); + goto out; + } + @@ -23573,7 +26685,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + err = -EINVAL; + root = sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); -+ if (bindex < 0 || au_sbend(sb) < bindex) { ++ if (bindex < 0 || au_sbbot(sb) < bindex) { + pr_err("out of bounds, %d\n", bindex); + goto out; + } @@ -23622,7 +26734,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + substring_t args[]) +{ + int err; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct path path; + struct dentry *root; + @@ -23635,8 +26747,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + xino_itrunc->bindex = -1; + root = sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + if (au_h_dptr(root, bindex) == path.dentry) { + xino_itrunc->bindex = bindex; + break; @@ -23780,7 +26892,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + } + u.xino_itrunc->bindex = n; + aufs_read_lock(root, AuLock_FLUSH); -+ if (n < 0 || au_sbend(sb) < n) { ++ if (n < 0 || au_sbbot(sb) < n) { + pr_err("out of bounds, %d\n", n); + aufs_read_unlock(root, !AuLock_IR); + break; @@ -23865,6 +26977,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; @@ -23936,7 +27050,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + } + } + -+ kfree(a); ++ au_kfree_rcu(a); + dump_opts(opts); + if (unlikely(err)) + au_opts_free(opts); @@ -23966,6 +27080,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; @@ -24117,7 +27233,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + + case Opt_trunc_xino_path: + case Opt_itrunc_xino: -+ err = au_xino_trunc(sb, opt->xino_itrunc.bindex); ++ err = au_xino_trunc(sb, opt->xino_itrunc.bindex, ++ /*idx_begin*/0); + if (!err) + err = 1; + break; @@ -24129,11 +27246,33 @@ 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; ++ sb->s_flags |= SB_POSIXACL; + break; + case Opt_noacl: -+ sb->s_flags &= ~MS_POSIXACL; ++ sb->s_flags &= ~SB_POSIXACL; + break; + + default: @@ -24158,7 +27297,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + err = 0; + switch (opt->type) { + case Opt_append: -+ opt->add.bindex = au_sbend(sb) + 1; ++ opt->add.bindex = au_sbbot(sb) + 1; + if (opt->add.bindex < 0) + opt->add.bindex = 0; + goto add; @@ -24197,7 +27336,6 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + } + break; + } -+ + return err; +} + @@ -24206,8 +27344,6 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + struct au_opts *opts) +{ + int err; -+ aufs_bindex_t bend, bindex; -+ struct dentry *root, *parent, *h_root; + + err = 0; + switch (opt->type) { @@ -24218,24 +27354,10 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + break; + + *opt_xino = &opt->xino; -+ au_xino_brid_set(sb, -1); -+ -+ /* safe d_parent access */ -+ parent = opt->xino.file->f_path.dentry->d_parent; -+ root = sb->s_root; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ h_root = au_h_dptr(root, bindex); -+ if (h_root == parent) { -+ au_xino_brid_set(sb, au_sbr_id(sb, bindex)); -+ break; -+ } -+ } + break; + + case Opt_noxino: + au_xino_clr(sb); -+ au_xino_brid_set(sb, -1); + *opt_xino = (void *)-1; + break; + } @@ -24247,7 +27369,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + unsigned int pending) +{ + int err, fhsm; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + unsigned char do_plink, skip, do_free, can_no_dreval; + struct au_branch *br; + struct au_wbr *wbr; @@ -24261,20 +27383,20 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + sbinfo = au_sbi(sb); + AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); + -+ if (!(sb_flags & MS_RDONLY)) { ++ if (!(sb_flags & SB_RDONLY)) { + if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) + pr_warn("first branch should be rw\n"); + if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) -+ pr_warn("shwh should be used with ro\n"); ++ pr_warn_once("shwh should be used with ro\n"); + } + + if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) + && !au_opt_test(sbinfo->si_mntflags, XINO)) -+ pr_warn("udba=*notify requires xino\n"); ++ pr_warn_once("udba=*notify requires xino\n"); + + if (au_opt_test(sbinfo->si_mntflags, DIRPERM1)) -+ pr_warn("dirperm1 breaks the protection" -+ " by the permission bits on the lower branch\n"); ++ pr_warn_once("dirperm1 breaks the protection" ++ " by the permission bits on the lower branch\n"); + + err = 0; + fhsm = 0; @@ -24283,8 +27405,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); + can_no_dreval = !!au_opt_test((sbinfo->si_mntflags | pending), + UDBA_NONE); -+ bend = au_sbend(sb); -+ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; !err && bindex <= bbot; bindex++) { + skip = 0; + h_dir = au_h_iptr(dir, bindex); + br = au_sbr(sb, bindex); @@ -24294,7 +27416,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + br->br_perm &= ~AuBrAttr_ICEX; +#if 0 + if ((br->br_perm & AuBrAttr_ICEX_SEC) -+ && (au_br_sb(br)->s_flags & MS_NOSEC)) ++ && (au_br_sb(br)->s_flags & SB_NOSEC)) + br->br_perm &= ~AuBrAttr_ICEX_SEC; +#endif + @@ -24349,16 +27471,16 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + continue; + + hdir = au_hi(dir, bindex); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + if (wbr) + wbr_wh_write_lock(wbr); + err = au_wh_init(br, sb); + if (wbr) + wbr_wh_write_unlock(wbr); -+ au_hn_imtx_unlock(hdir); ++ au_hn_inode_unlock(hdir); + + if (!err && do_free) { -+ kfree(wbr); ++ au_kfree_rcu(wbr); + br->br_wbr = NULL; + } + } @@ -24370,7 +27492,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + + if (fhsm >= 2) { + au_fset_si(sbinfo, FHSM); -+ for (bindex = bend; bindex >= 0; bindex--) { ++ for (bindex = bbot; bindex >= 0; bindex--) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) { + au_fhsm_set_bottom(sb, bindex); @@ -24389,7 +27511,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c +{ + int err; + unsigned int tmp; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_opt *opt; + struct au_opt_xino *opt_xino, xino; + struct au_sbinfo *sbinfo; @@ -24422,8 +27544,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + else if (unlikely(err < 0)) + goto out; + -+ bend = au_sbend(sb); -+ if (unlikely(bend < 0)) { ++ bbot = au_sbbot(sb); ++ if (unlikely(bbot < 0)) { + err = -EINVAL; + pr_err("no branches\n"); + goto out; @@ -24458,8 +27580,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c + tmp &= AuOptMask_UDBA; + sbinfo->si_mntflags &= ~AuOptMask_UDBA; + sbinfo->si_mntflags |= tmp; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_hnotify_reset_br(tmp, br, br->br_perm); + if (unlikely(err)) @@ -24487,7 +27609,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; @@ -24528,6 +27654,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; +} + @@ -24539,10 +27667,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,211 @@ ++++ linux/fs/aufs/opts.h 2018-08-12 23:43:05.460124736 +0200 +@@ -0,0 +1,225 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -24570,7 +27699,6 @@ diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h +#include + +struct file; -+struct super_block; + +/* ---------------------------------------------------------------------- */ + @@ -24591,11 +27719,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 @@ -24642,6 +27775,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 */ @@ -24718,12 +27853,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; @@ -24742,6 +27883,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); @@ -24754,10 +27896,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,528 @@ ++++ linux/fs/aufs/plink.c 2018-10-23 12:33:35.599375796 +0200 +@@ -0,0 +1,516 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -24803,6 +27946,7 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c +{ + int err; + pid_t pid, ppid; ++ struct task_struct *parent, *prev; + struct au_sbinfo *sbi; + + SiMustAnyLock(sb); @@ -24817,11 +27961,22 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + goto out; + + /* todo: it highly depends upon /sbin/mount.aufs */ ++ prev = NULL; ++ parent = current; ++ ppid = 0; + rcu_read_lock(); -+ ppid = task_pid_vnr(rcu_dereference(current->real_parent)); ++ while (1) { ++ parent = rcu_dereference(parent->real_parent); ++ if (parent == prev) ++ break; ++ ppid = task_pid_vnr(parent); ++ if (pid == ppid) { ++ rcu_read_unlock(); ++ goto out; ++ } ++ prev = parent; ++ } + rcu_read_unlock(); -+ if (pid == ppid) -+ goto out; + + if (au_ftest_lock(flags, NOPLMW)) { + /* if there is no i_mutex lock in VFS, we don't need to wait */ @@ -24881,8 +28036,9 @@ 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 pseudo_link *plink; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; ++ struct au_icntnr *icntnr; + + SiMustAnyLock(sb); + @@ -24891,11 +28047,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(plink, plink_hlist, hlist) -+ AuDbg("%lu\n", plink->inode->i_ino); -+ rcu_read_unlock(); ++ 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); ++ hlist_bl_unlock(hbl); + } +} +#endif @@ -24905,8 +28061,9 @@ 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 pseudo_link *plink; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; ++ struct au_icntnr *icntnr; + + sbinfo = au_sbi(inode->i_sb); + AuRwMustAnyLock(&sbinfo->si_rwsem); @@ -24915,14 +28072,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(plink, plink_hlist, hlist) -+ if (plink->inode == inode) { ++ 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; +} + @@ -24958,12 +28115,12 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + struct au_branch *br) +{ + struct dentry *h_dentry; -+ struct mutex *h_mtx; ++ struct inode *h_inode; + -+ h_mtx = &d_inode(h_parent)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); ++ h_inode = d_inode(h_parent); ++ inode_lock_shared_nested(h_inode, AuLsc_I_CHILD2); + h_dentry = vfsub_lkup_one(tgtname, h_parent); -+ mutex_unlock(h_mtx); ++ inode_unlock_shared(h_inode); + return h_dentry; +} + @@ -25016,7 +28173,7 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + struct inode *h_dir, *delegated; + + h_dir = d_inode(h_parent); -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); ++ inode_lock_nested(h_dir, AuLsc_I_CHILD2); +again: + h_path.dentry = vfsub_lkup_one(tgt, h_parent); + err = PTR_ERR(h_path.dentry); @@ -25052,7 +28209,7 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + dput(h_path.dentry); + +out: -+ mutex_unlock(&h_dir->i_mutex); ++ inode_unlock(h_dir); + return err; +} + @@ -25101,24 +28258,6 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + return err; +} + -+/* free a single plink */ -+static void do_put_plink(struct pseudo_link *plink, int do_del) -+{ -+ if (do_del) -+ hlist_del(&plink->hlist); -+ iput(plink->inode); -+ kfree(plink); -+} -+ -+static void do_put_plink_rcu(struct rcu_head *rcu) -+{ -+ struct pseudo_link *plink; -+ -+ plink = container_of(rcu, struct pseudo_link, rcu); -+ iput(plink->inode); -+ kfree(plink); -+} -+ +/* + * create a new pseudo-link for @h_dentry on @bindex. + * the linked inode is held in aufs @inode. @@ -25128,9 +28267,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 pseudo_link *plink, *tmp; -+ struct au_sphlhead *sphl; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; ++ struct au_icntnr *icntnr; + int found, err, cnt, i; + + sb = inode->i_sb; @@ -25143,46 +28282,35 @@ 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; -+ tmp = kmalloc(sizeof(*plink), GFP_NOFS); -+ if (tmp) -+ tmp->inode = au_igrab(inode); -+ else { -+ err = -ENOMEM; -+ goto out; -+ } ++ hbl = sbinfo->si_plink + i; ++ au_igrab(inode); + -+ spin_lock(&sphl->spin); -+ hlist_for_each_entry(plink, plink_hlist, hlist) { -+ if (plink->inode == inode) { ++ hlist_bl_lock(hbl); ++ hlist_bl_for_each_entry(icntnr, pos, hbl, plink) { ++ if (&icntnr->vfs_inode == inode) { + found = 1; + break; + } + } -+ if (!found) -+ hlist_add_head_rcu(&tmp->hlist, plink_hlist); -+ spin_unlock(&sphl->spin); + if (!found) { -+ cnt = au_sphl_count(sphl); -+#define msg "unexpectedly unblanced or too many pseudo-links" ++ icntnr = container_of(inode, struct au_icntnr, vfs_inode); ++ hlist_bl_add_head(&icntnr->plink, hbl); ++ } ++ hlist_bl_unlock(hbl); ++ if (!found) { ++ cnt = au_hbl_count(hbl); ++#define msg "unexpectedly unbalanced or too many pseudo-links" + if (cnt > AUFS_PLINK_WARN) + AuWarn1(msg ", %d\n", cnt); +#undef msg + err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); -+ } else { -+ do_put_plink(tmp, 0); -+ return; -+ } -+ -+out: -+ if (unlikely(err)) { -+ pr_warn("err %d, damaged pseudo link.\n", err); -+ if (tmp) { -+ au_sphl_del_rcu(&tmp->hlist, sphl); -+ call_rcu(&tmp->rcu, do_put_plink_rcu); ++ if (unlikely(err)) { ++ pr_warn("err %d, damaged pseudo link.\n", err); ++ au_hbl_del(&icntnr->plink, hbl); ++ iput(&icntnr->vfs_inode); + } -+ } ++ } else ++ iput(&icntnr->vfs_inode); +} + +/* free all plinks */ @@ -25190,9 +28318,9 @@ 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 pseudo_link *plink; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos, *tmp; ++ struct au_icntnr *icntnr; + + SiMustWriteLock(sb); + @@ -25203,14 +28331,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(plink, tmp, plink_hlist, hlist) -+ do_put_plink(plink, 0); -+ INIT_HLIST_HEAD(plink_hlist); ++ hlist_bl_for_each_entry_safe(icntnr, pos, tmp, hbl, plink) ++ iput(&icntnr->vfs_inode); ++ INIT_HLIST_BL_HEAD(hbl); + } +} + @@ -25228,13 +28356,13 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c +static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) +{ + int do_put; -+ aufs_bindex_t bstart, bend, bindex; ++ aufs_bindex_t btop, bbot, bindex; + + do_put = 0; -+ bstart = au_ibstart(inode); -+ bend = au_ibend(inode); -+ if (bstart >= 0) { -+ for (bindex = bstart; bindex <= bend; bindex++) { ++ btop = au_ibtop(inode); ++ bbot = au_ibbot(inode); ++ if (btop >= 0) { ++ for (bindex = btop; bindex <= bbot; bindex++) { + if (!au_h_iptr(inode, bindex) + || au_ii_br_id(inode, bindex) != br_id) + continue; @@ -25243,7 +28371,7 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c + break; + } + if (do_put) -+ for (bindex = bstart; bindex <= bend; bindex++) ++ for (bindex = btop; bindex <= bbot; bindex++) + if (au_h_iptr(inode, bindex)) { + do_put = 0; + break; @@ -25258,9 +28386,9 @@ 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 pseudo_link *plink; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos, *tmp; ++ struct au_icntnr *icntnr; + struct inode *inode; + int i, do_put; + @@ -25270,15 +28398,17 @@ 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(plink, tmp, plink_hlist, hlist) { -+ inode = au_igrab(plink->inode); ++ 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) -+ do_put_plink(plink, 1); ++ if (do_put) { ++ hlist_bl_del(&icntnr->plink); ++ iput(inode); ++ } + ii_write_unlock(inode); + iput(inode); + } @@ -25286,10 +28416,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,52 @@ ++++ linux/fs/aufs/poll.c 2018-08-12 23:43:05.460124736 +0200 +@@ -0,0 +1,51 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -25312,40 +28443,39 @@ diff -urN /usr/share/empty/fs/aufs/poll.c linux/fs/aufs/poll.c + +#include "aufs.h" + -+unsigned int aufs_poll(struct file *file, poll_table *wait) ++__poll_t aufs_poll(struct file *file, struct poll_table_struct *pt) +{ -+ unsigned int mask; -+ int err; ++ __poll_t mask; + struct file *h_file; + struct super_block *sb; + + /* We should pretend an error happened. */ -+ mask = POLLERR /* | POLLIN | POLLOUT */; ++ mask = EPOLLERR /* | EPOLLIN | EPOLLOUT */; + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + -+ h_file = au_read_pre(file, /*keep_fi*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) ++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); ++ if (IS_ERR(h_file)) { ++ AuDbg("h_file %ld\n", PTR_ERR(h_file)); + goto out; ++ } + -+ /* it is not an error if h_file has no operation */ -+ mask = DEFAULT_POLLMASK; -+ if (h_file->f_op->poll) -+ mask = h_file->f_op->poll(h_file, wait); ++ mask = vfs_poll(h_file, pt); + fput(h_file); /* instead of au_read_post() */ + +out: + si_read_unlock(sb); -+ AuTraceErr((int)mask); ++ if (mask & EPOLLERR) ++ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,99 @@ ++++ linux/fs/aufs/posix_acl.c 2018-08-12 23:43:05.460124736 +0200 +@@ -0,0 +1,103 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2014-2015 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 @@ -25366,7 +28496,6 @@ diff -urN /usr/share/empty/fs/aufs/posix_acl.c linux/fs/aufs/posix_acl.c + */ + +#include -+#include +#include "aufs.h" + +struct posix_acl *aufs_get_acl(struct inode *inode, int type) @@ -25381,10 +28510,10 @@ diff -urN /usr/share/empty/fs/aufs/posix_acl.c linux/fs/aufs/posix_acl.c + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH); + ii_read_lock_child(inode); -+ if (!(sb->s_flags & MS_POSIXACL)) ++ if (!(sb->s_flags & SB_POSIXACL)) + goto out; + -+ bindex = au_ibstart(inode); ++ bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (unlikely(!h_inode + || ((h_inode->i_mode & S_IFMT) @@ -25396,6 +28525,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); @@ -25410,7 +28541,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, @@ -25418,7 +28549,8 @@ diff -urN /usr/share/empty/fs/aufs/posix_acl.c linux/fs/aufs/posix_acl.c + }, + }; + -+ mutex_lock(&inode->i_mutex); ++ IMustLock(inode); ++ + if (inode->i_ino == AUFS_ROOT_INO) + dentry = dget(inode->i_sb->s_root); + else { @@ -25433,22 +28565,24 @@ diff -urN /usr/share/empty/fs/aufs/posix_acl.c linux/fs/aufs/posix_acl.c + } + } + -+ ssz = au_srxattr(dentry, &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: -+ mutex_unlock(&inode->i_mutex); + 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,169 @@ ++++ linux/fs/aufs/procfs.c 2018-08-12 23:43:05.460124736 +0200 +@@ -0,0 +1,171 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2010-2015 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 @@ -25498,6 +28632,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)) @@ -25505,14 +28640,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); -+ list_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)) @@ -25602,7 +28737,7 @@ diff -urN /usr/share/empty/fs/aufs/procfs.c linux/fs/aufs/procfs.c + if (unlikely(!au_procfs_dir)) + goto out; + -+ entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | S_IWUSR, ++ entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | 0200, + au_procfs_dir, &au_procfs_plm_fop); + if (unlikely(!entry)) + goto out_dir; @@ -25618,10 +28753,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,388 @@ ++++ linux/fs/aufs/rdu.c 2018-08-12 23:43:05.463458173 +0200 +@@ -0,0 +1,382 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -25744,7 +28880,7 @@ diff -urN /usr/share/empty/fs/aufs/rdu.c linux/fs/aufs/rdu.c +static int au_rdu(struct file *file, struct aufs_rdu *rdu) +{ + int err; -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + struct au_rdu_arg arg = { + .ctx = { + .actor = au_rdu_fill @@ -25770,7 +28906,7 @@ diff -urN /usr/share/empty/fs/aufs/rdu.c linux/fs/aufs/rdu.c + arg.end += rdu->sz; + + err = -ENOTDIR; -+ if (unlikely(!file->f_op->iterate)) ++ if (unlikely(!file->f_op->iterate && !file->f_op->iterate_shared)) + goto out; + + err = security_file_permission(file, MAY_READ); @@ -25780,14 +28916,7 @@ diff -urN /usr/share/empty/fs/aufs/rdu.c linux/fs/aufs/rdu.c + + dentry = file->f_path.dentry; + inode = d_inode(dentry); -+#if 1 -+ mutex_lock(&inode->i_mutex); -+#else -+ err = mutex_lock_killable(&inode->i_mutex); -+ AuTraceErr(err); -+ if (unlikely(err)) -+ goto out; -+#endif ++ inode_lock_shared(inode); + + arg.sb = inode->i_sb; + err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM); @@ -25810,12 +28939,12 @@ diff -urN /usr/share/empty/fs/aufs/rdu.c linux/fs/aufs/rdu.c + if (!rdu->blk) + rdu->blk = au_dir_size(file, /*dentry*/NULL); + } -+ bend = au_fbstart(file); -+ if (cookie->bindex < bend) -+ cookie->bindex = bend; -+ bend = au_fbend_dir(file); -+ /* AuDbg("b%d, b%d\n", cookie->bindex, bend); */ -+ for (; !err && cookie->bindex <= bend; ++ bbot = au_fbtop(file); ++ if (cookie->bindex < bbot) ++ cookie->bindex = bbot; ++ bbot = au_fbbot_dir(file); ++ /* AuDbg("b%d, b%d\n", cookie->bindex, bbot); */ ++ for (; !err && cookie->bindex <= bbot; + cookie->bindex++, cookie->h_pos = 0) { + h_file = au_hf_dir(file, cookie->bindex); + if (!h_file) @@ -25836,7 +28965,7 @@ diff -urN /usr/share/empty/fs/aufs/rdu.c linux/fs/aufs/rdu.c + } + + ii_read_lock_child(inode); -+ fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode))); ++ fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibtop(inode))); + ii_read_unlock(inode); + +out_unlock: @@ -25844,7 +28973,7 @@ diff -urN /usr/share/empty/fs/aufs/rdu.c linux/fs/aufs/rdu.c +out_si: + si_read_unlock(arg.sb); +out_mtx: -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock_shared(inode); +out: + AuTraceErr(err); + return err; @@ -26010,10 +29139,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,191 @@ ++++ linux/fs/aufs/rwsem.h 2018-10-23 12:33:35.599375796 +0200 +@@ -0,0 +1,73 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -26040,175 +29170,57 @@ diff -urN /usr/share/empty/fs/aufs/rwsem.h linux/fs/aufs/rwsem.h + +#include "debug.h" + -+struct au_rwsem { -+ struct rw_semaphore rwsem; -+#ifdef CONFIG_AUFS_DEBUG -+ /* just for debugging, not almighty counter */ -+ atomic_t rcnt, wcnt; -+#endif -+}; -+ -+#ifdef CONFIG_AUFS_DEBUG -+#define AuDbgCntInit(rw) do { \ -+ atomic_set(&(rw)->rcnt, 0); \ -+ atomic_set(&(rw)->wcnt, 0); \ -+ smp_mb(); /* atomic set */ \ -+} while (0) -+ -+#define AuDbgRcntInc(rw) atomic_inc(&(rw)->rcnt) -+#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0) -+#define AuDbgWcntInc(rw) atomic_inc(&(rw)->wcnt) -+#define AuDbgWcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->wcnt) < 0) -+#else -+#define AuDbgCntInit(rw) do {} while (0) -+#define AuDbgRcntInc(rw) do {} while (0) -+#define AuDbgRcntDec(rw) do {} while (0) -+#define AuDbgWcntInc(rw) do {} while (0) -+#define AuDbgWcntDec(rw) do {} while (0) -+#endif /* CONFIG_AUFS_DEBUG */ ++/* in the future, the name 'au_rwsem' will be totally gone */ ++#define au_rwsem rw_semaphore + +/* to debug easier, do not make them inlined functions */ -+#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list)) ++#define AuRwMustNoWaiters(rw) AuDebugOn(rwsem_is_contended(rw)) +/* rwsem_is_locked() is unusable */ -+#define AuRwMustReadLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0) -+#define AuRwMustWriteLock(rw) AuDebugOn(atomic_read(&(rw)->wcnt) <= 0) -+#define AuRwMustAnyLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0 \ -+ && atomic_read(&(rw)->wcnt) <= 0) -+#define AuRwDestroy(rw) AuDebugOn(atomic_read(&(rw)->rcnt) \ -+ || atomic_read(&(rw)->wcnt)) -+ -+#define au_rw_class(rw, key) lockdep_set_class(&(rw)->rwsem, key) -+ -+static inline void au_rw_init(struct au_rwsem *rw) -+{ -+ AuDbgCntInit(rw); -+ init_rwsem(&rw->rwsem); -+} -+ -+static inline void au_rw_init_wlock(struct au_rwsem *rw) -+{ -+ au_rw_init(rw); -+ down_write(&rw->rwsem); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_init_wlock_nested(struct au_rwsem *rw, -+ unsigned int lsc) -+{ -+ au_rw_init(rw); -+ down_write_nested(&rw->rwsem, lsc); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_read_lock(struct au_rwsem *rw) -+{ -+ down_read(&rw->rwsem); -+ AuDbgRcntInc(rw); -+} -+ -+static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc) -+{ -+ down_read_nested(&rw->rwsem, lsc); -+ AuDbgRcntInc(rw); -+} -+ -+static inline void au_rw_read_unlock(struct au_rwsem *rw) -+{ -+ AuRwMustReadLock(rw); -+ AuDbgRcntDec(rw); -+ up_read(&rw->rwsem); -+} -+ -+static inline void au_rw_dgrade_lock(struct au_rwsem *rw) -+{ -+ AuRwMustWriteLock(rw); -+ AuDbgRcntInc(rw); -+ AuDbgWcntDec(rw); -+ downgrade_write(&rw->rwsem); -+} -+ -+static inline void au_rw_write_lock(struct au_rwsem *rw) -+{ -+ down_write(&rw->rwsem); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_write_lock_nested(struct au_rwsem *rw, -+ unsigned int lsc) -+{ -+ down_write_nested(&rw->rwsem, lsc); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_write_unlock(struct au_rwsem *rw) -+{ -+ AuRwMustWriteLock(rw); -+ AuDbgWcntDec(rw); -+ up_write(&rw->rwsem); -+} -+ -+/* why is not _nested version defined */ -+static inline int au_rw_read_trylock(struct au_rwsem *rw) -+{ -+ int ret; -+ -+ ret = down_read_trylock(&rw->rwsem); -+ if (ret) -+ AuDbgRcntInc(rw); -+ return ret; -+} -+ -+static inline int au_rw_write_trylock(struct au_rwsem *rw) -+{ -+ int ret; ++#define AuRwMustReadLock(rw) AuDebugOn(!lockdep_recursing(current) \ ++ && debug_locks \ ++ && !lockdep_is_held_type(rw, 1)) ++#define AuRwMustWriteLock(rw) AuDebugOn(!lockdep_recursing(current) \ ++ && debug_locks \ ++ && !lockdep_is_held_type(rw, 0)) ++#define AuRwMustAnyLock(rw) AuDebugOn(!lockdep_recursing(current) \ ++ && debug_locks \ ++ && !lockdep_is_held(rw)) ++#define AuRwDestroy(rw) AuDebugOn(!lockdep_recursing(current) \ ++ && debug_locks \ ++ && lockdep_is_held(rw)) ++ ++#define au_rw_init(rw) init_rwsem(rw) ++ ++#define au_rw_init_wlock(rw) do { \ ++ au_rw_init(rw); \ ++ down_write(rw); \ ++ } while (0) + -+ ret = down_write_trylock(&rw->rwsem); -+ if (ret) -+ AuDbgWcntInc(rw); -+ return ret; -+} ++#define au_rw_init_wlock_nested(rw, lsc) do { \ ++ au_rw_init(rw); \ ++ down_write_nested(rw, lsc); \ ++ } while (0) + -+#undef AuDbgCntInit -+#undef AuDbgRcntInc -+#undef AuDbgRcntDec -+#undef AuDbgWcntInc -+#undef AuDbgWcntDec -+ -+#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ -+static inline void prefix##_read_lock(param) \ -+{ au_rw_read_lock(rwsem); } \ -+static inline void prefix##_write_lock(param) \ -+{ au_rw_write_lock(rwsem); } \ -+static inline int prefix##_read_trylock(param) \ -+{ return au_rw_read_trylock(rwsem); } \ -+static inline int prefix##_write_trylock(param) \ -+{ return au_rw_write_trylock(rwsem); } -+/* why is not _nested version defined */ -+/* static inline void prefix##_read_trylock_nested(param, lsc) -+{ au_rw_read_trylock_nested(rwsem, lsc)); } -+static inline void prefix##_write_trylock_nestd(param, lsc) -+{ au_rw_write_trylock_nested(rwsem, lsc); } */ -+ -+#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \ -+static inline void prefix##_read_unlock(param) \ -+{ au_rw_read_unlock(rwsem); } \ -+static inline void prefix##_write_unlock(param) \ -+{ au_rw_write_unlock(rwsem); } \ -+static inline void prefix##_downgrade_lock(param) \ -+{ au_rw_dgrade_lock(rwsem); } -+ -+#define AuSimpleRwsemFuncs(prefix, param, rwsem) \ -+ AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ -+ AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) ++#define au_rw_read_lock(rw) down_read(rw) ++#define au_rw_read_lock_nested(rw, lsc) down_read_nested(rw, lsc) ++#define au_rw_read_unlock(rw) up_read(rw) ++#define au_rw_dgrade_lock(rw) downgrade_write(rw) ++#define au_rw_write_lock(rw) down_write(rw) ++#define au_rw_write_lock_nested(rw, lsc) down_write_nested(rw, lsc) ++#define au_rw_write_unlock(rw) up_write(rw) ++/* why is not _nested version defined? */ ++#define au_rw_read_trylock(rw) down_read_trylock(rw) ++#define au_rw_write_trylock(rw) down_write_trylock(rw) + +#endif /* __KERNEL__ */ +#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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,366 @@ ++++ linux/fs/aufs/sbinfo.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,313 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -26241,66 +29253,60 @@ 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(!hlist_empty(&sbinfo->si_symlink.head)); ++ AuLCntZero(au_lcnt_read(&sbinfo->si_ninodes, /*do_rev*/0)); ++ au_lcnt_fin(&sbinfo->si_ninodes, /*do_sync*/0); ++ AuLCntZero(au_lcnt_read(&sbinfo->si_nfiles, /*do_rev*/0)); ++ au_lcnt_fin(&sbinfo->si_nfiles, /*do_sync*/0); + ++ dbgaufs_si_fin(sbinfo); + au_rw_write_lock(&sbinfo->si_rwsem); + au_br_free(sbinfo); + au_rw_write_unlock(&sbinfo->si_rwsem); + -+ AuDebugOn(radix_tree_gang_lookup -+ (&sbinfo->au_si_pid.tree, (void **)&locked, -+ /*first_index*/PID_MAX_DEFAULT - 1, -+ /*max_items*/sizeof(locked)/sizeof(*locked))); -+ -+ kfree(sbinfo->si_branch); -+ kfree(sbinfo->au_si_pid.bitmap); ++ au_kfree_try_rcu(sbinfo->si_branch); + mutex_destroy(&sbinfo->si_xib_mtx); + AuRwDestroy(&sbinfo->si_rwsem); + -+ kfree(sbinfo); ++ au_lcnt_wait_for_fin(&sbinfo->si_ninodes); ++ /* si_nfiles is waited too */ ++ au_kfree_rcu(sbinfo); +} + +int au_si_alloc(struct super_block *sb) +{ + int err, i; + struct au_sbinfo *sbinfo; -+ static struct lock_class_key aufs_si; + + err = -ENOMEM; + sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS); + if (unlikely(!sbinfo)) + goto out; + -+ BUILD_BUG_ON(sizeof(unsigned long) != -+ sizeof(*sbinfo->au_si_pid.bitmap)); -+ sbinfo->au_si_pid.bitmap = kcalloc(BITS_TO_LONGS(PID_MAX_DEFAULT), -+ sizeof(*sbinfo->au_si_pid.bitmap), -+ GFP_NOFS); -+ if (unlikely(!sbinfo->au_si_pid.bitmap)) -+ goto out_sbinfo; -+ + /* will be reallocated separately */ + sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); + if (unlikely(!sbinfo->si_branch)) -+ goto out_pidmap; ++ goto out_sbinfo; + + err = sysaufs_si_init(sbinfo); ++ if (!err) { ++ dbgaufs_si_null(sbinfo); ++ err = dbgaufs_si_init(sbinfo); ++ if (unlikely(err)) ++ kobject_put(&sbinfo->si_kobj); ++ } + if (unlikely(err)) + goto out_br; + + au_nwt_init(&sbinfo->si_nowait); + au_rw_init_wlock(&sbinfo->si_rwsem); -+ au_rw_class(&sbinfo->si_rwsem, &aufs_si); -+ spin_lock_init(&sbinfo->au_si_pid.tree_lock); -+ INIT_RADIX_TREE(&sbinfo->au_si_pid.tree, GFP_ATOMIC | __GFP_NOFAIL); + -+ atomic_long_set(&sbinfo->si_ninodes, 0); -+ atomic_long_set(&sbinfo->si_nfiles, 0); ++ au_lcnt_init(&sbinfo->si_ninodes, /*release*/NULL); ++ au_lcnt_init(&sbinfo->si_nfiles, /*release*/NULL); + -+ sbinfo->si_bend = -1; ++ sbinfo->si_bbot = -1; + sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; + + sbinfo->si_wbr_copyup = AuWbrCopyup_Def; @@ -26312,16 +29318,13 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c + + sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); + -+ au_sphl_init(&sbinfo->si_symlink); -+ + sbinfo->si_xino_jiffy = jiffies; + sbinfo->si_xino_expire + = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC); + mutex_init(&sbinfo->si_xib_mtx); -+ 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; @@ -26329,11 +29332,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; @@ -26345,16 +29348,14 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c + return 0; /* success */ + +out_br: -+ kfree(sbinfo->si_branch); -+out_pidmap: -+ kfree(sbinfo->au_si_pid.bitmap); ++ au_kfree_try_rcu(sbinfo->si_branch); +out_sbinfo: -+ kfree(sbinfo); ++ au_kfree_rcu(sbinfo); +out: + return err; +} + -+int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr) ++int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr, int may_shrink) +{ + int err, sz; + struct au_branch **brp; @@ -26362,10 +29363,11 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c + AuRwMustWriteLock(&sbinfo->si_rwsem); + + err = -ENOMEM; -+ sz = sizeof(*brp) * (sbinfo->si_bend + 1); ++ sz = sizeof(*brp) * (sbinfo->si_bbot + 1); + if (unlikely(!sz)) + sz = sizeof(*brp); -+ brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS); ++ brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS, ++ may_shrink); + if (brp) { + sbinfo->si_branch = brp; + err = 0; @@ -26387,7 +29389,7 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c + au_update_digen(sb->s_root); + inode = d_inode(sb->s_root); + au_update_iigen(inode, /*half*/0); -+ inode->i_version++; ++ inode_inc_iversion(inode); + return gen; +} + @@ -26529,171 +29531,13 @@ 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); +} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int si_pid_test_slow(struct super_block *sb) -+{ -+ void *p; -+ -+ rcu_read_lock(); -+ p = radix_tree_lookup(&au_sbi(sb)->au_si_pid.tree, current->pid); -+ rcu_read_unlock(); -+ -+ return (long)!!p; -+} -+ -+void si_pid_set_slow(struct super_block *sb) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ -+ AuDebugOn(si_pid_test_slow(sb)); -+ -+ sbinfo = au_sbi(sb); -+ err = radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); -+ AuDebugOn(err); -+ spin_lock(&sbinfo->au_si_pid.tree_lock); -+ err = radix_tree_insert(&sbinfo->au_si_pid.tree, current->pid, -+ /*any valid ptr*/sb); -+ spin_unlock(&sbinfo->au_si_pid.tree_lock); -+ AuDebugOn(err); -+ radix_tree_preload_end(); -+} -+ -+void si_pid_clr_slow(struct super_block *sb) -+{ -+ void *p; -+ struct au_sbinfo *sbinfo; -+ -+ AuDebugOn(!si_pid_test_slow(sb)); -+ -+ sbinfo = au_sbi(sb); -+ spin_lock(&sbinfo->au_si_pid.tree_lock); -+ p = radix_tree_delete(&sbinfo->au_si_pid.tree, current->pid); -+ spin_unlock(&sbinfo->au_si_pid.tree_lock); -+} -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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,111 @@ -+/* -+ * Copyright (C) 2005-2015 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__ -+ -+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); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,1039 @@ ++++ linux/fs/aufs/super.c 2018-10-23 12:33:35.599375796 +0200 +@@ -0,0 +1,1048 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -26729,7 +29573,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + c = au_cache_alloc_icntnr(); + if (c) { + au_icntnr_init(c); -+ c->vfs_inode.i_version = 1; /* sigen(sb); */ ++ inode_set_iversion(&c->vfs_inode, 1); /* sigen(sb); */ + c->iinfo.ii_hinode = NULL; + return &c->vfs_inode; + } @@ -26740,13 +29584,13 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c +{ + struct inode *inode = container_of(head, struct inode, i_rcu); + -+ INIT_HLIST_HEAD(&inode->i_dentry); + au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); +} + +static void aufs_destroy_inode(struct inode *inode) +{ -+ au_iinfo_fin(inode); ++ if (!au_is_bad_inode(inode)) ++ au_iinfo_fin(inode); + call_rcu(&inode->i_rcu, aufs_destroy_inode_cb); +} + @@ -26767,7 +29611,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + if (!err) + err = au_iinfo_init(inode); + if (!err) -+ inode->i_version++; ++ inode_inc_iversion(inode); + else { + iget_failed(inode); + inode = ERR_PTR(err); @@ -26784,24 +29628,25 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c +static int au_show_brs(struct seq_file *seq, struct super_block *sb) +{ + int err; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct path path; + struct au_hdentry *hdp; + struct au_branch *br; + au_br_perm_str_t perm; + + err = 0; -+ bend = au_sbend(sb); -+ hdp = au_di(sb->s_root)->di_hdentry; -+ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ bindex = 0; ++ hdp = au_hdentry(au_di(sb->s_root), bindex); ++ for (; !err && bindex <= bbot; bindex++, hdp++) { + br = au_sbr(sb, bindex); + path.mnt = au_br_mnt(br); -+ path.dentry = hdp[bindex].hd_dentry; ++ path.dentry = hdp->hd_dentry; + err = au_seq_path(seq, &path); + if (!err) { + au_optstr_br_perm(&perm, br->br_perm); + seq_printf(seq, "=%s", perm.a); -+ if (bindex != bend) ++ if (bindex != bbot) + seq_putc(seq, ':'); + } + } @@ -26811,15 +29656,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: @@ -26827,36 +29688,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(); + } +} + @@ -26871,7 +29724,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + struct qstr *name; + struct file *f; + struct dentry *d, *h_root; -+ struct au_hdentry *hdp; ++ struct au_branch *br; + + AuRwMustAnyLock(&sbinfo->si_rwsem); + @@ -26882,12 +29735,12 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + + /* stop printing the default xino path on the first writable branch */ + h_root = NULL; -+ brid = au_xino_brid(sb); -+ if (brid >= 0) { -+ bindex = au_br_index(sb, brid); -+ hdp = au_di(sb->s_root)->di_hdentry; -+ h_root = hdp[0 + bindex].hd_dentry; ++ bindex = au_xi_root(sb, f->f_path.dentry); ++ if (bindex >= 0) { ++ br = au_sbr_sb(sb, bindex); ++ h_root = au_br_dentry(br); + } ++ + d = f->f_path.dentry; + name = &d->d_name; + /* safe ->d_parent because the file is unlinked */ @@ -26930,8 +29783,12 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c +} while (0) + + sb = dentry->d_sb; -+ if (sb->s_flags & MS_POSIXACL) ++ if (sb->s_flags & SB_POSIXACL) + seq_puts(m, ",acl"); ++#if 0 ++ if (sb->s_flags & SB_I_VERSION) ++ seq_puts(m, ",i_version"); ++#endif + + /* lock free root dinfo */ + si_noflush_read_lock(sb); @@ -26975,6 +29832,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); @@ -27025,7 +29883,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + int err; + long bsize, factor; + u64 blocks, bfree, bavail, files, ffree; -+ aufs_bindex_t bend, bindex, i; ++ aufs_bindex_t bbot, bindex, i; + unsigned char shared; + struct path h_path; + struct super_block *h_sb; @@ -27037,8 +29895,8 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + blocks = 0; + bfree = 0; + bavail = 0; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + h_path.mnt = au_sbr_mnt(sb, bindex); + h_sb = h_path.mnt->mnt_sb; + shared = 0; @@ -27121,25 +29979,23 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c +static int aufs_sync_fs(struct super_block *sb, int wait) +{ + int err, e; -+ aufs_bindex_t bend, bindex; ++ aufs_bindex_t bbot, bindex; + struct au_branch *br; + struct super_block *h_sb; + + err = 0; + si_noflush_read_lock(sb); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_writable(br->br_perm)) + 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); + @@ -27154,11 +30010,8 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + struct au_sbinfo *sbinfo; + + sbinfo = au_sbi(sb); -+ if (!sbinfo) -+ return; -+ -+ dbgaufs_si_fin(sbinfo); -+ kobject_put(&sbinfo->si_kobj); ++ if (sbinfo) ++ kobject_put(&sbinfo->si_kobj); +} + +/* ---------------------------------------------------------------------- */ @@ -27210,8 +30063,8 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + head = arg; + spin_lock(&sb->s_inode_list_lock); + list_for_each_entry(inode, head, i_sb_list) { -+ if (!is_bad_inode(inode) -+ && au_ii(inode)->ii_bstart >= 0) { ++ if (!au_is_bad_inode(inode) ++ && au_ii(inode)->ii_btop >= 0) { + spin_lock(&inode->i_lock); + if (atomic_read(&inode->i_count)) { + au_igrab(inode); @@ -27229,7 +30082,10 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + +struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max) +{ -+ *max = atomic_long_read(&au_sbi(sb)->si_ninodes); ++ struct au_sbinfo *sbi; ++ ++ sbi = au_sbi(sb); ++ *max = au_lcnt_read(&sbi->si_ninodes, /*do_rev*/1); + return au_array_alloc(max, au_iarray_cb, sb, &sb->s_inodes); +} + @@ -27388,7 +30244,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c +{ + int err, e; + unsigned int udba; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct dentry *root; + struct inode *inode; + struct au_branch *br; @@ -27404,8 +30260,8 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + IiMustNoWaiters(inode); + + udba = au_opt_udba(sb); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_hnotify_reset_br(udba, br, br->br_perm); + if (unlikely(err)) @@ -27427,7 +30283,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + AuDebugOn(sbi->si_iop_array == aufs_iop); + sbi->si_iop_array = aufs_iop; + } -+ pr_info("reset to %pf and %pf\n", ++ pr_info("reset to %ps and %ps\n", + sb->s_d_op, sbi->si_iop_array); + } + @@ -27498,7 +30354,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + + sbinfo = au_sbi(sb); + inode = d_inode(root); -+ mutex_lock(&inode->i_mutex); ++ inode_lock(inode); + err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out_mtx; @@ -27521,7 +30377,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + aufs_write_unlock(root); + +out_mtx: -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); +out_opts: + free_page((unsigned long)opts.opt); +out: @@ -27611,14 +30467,15 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + sbinfo = au_sbi(sb); + + /* all timestamps always follow the ones on the branch */ -+ sb->s_flags |= MS_NOATIME | MS_NODIRATIME; ++ sb->s_flags |= SB_NOATIME | SB_NODIRATIME; ++ sb->s_flags |= SB_I_VERSION; /* do we really need this? */ + sb->s_op = &aufs_sop; + sb->s_d_op = &aufs_dop; + sb->s_magic = AUFS_SUPER_MAGIC; + 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)) { @@ -27640,19 +30497,19 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + goto out_root; + + /* lock vfs_inode first, then aufs. */ -+ mutex_lock(&inode->i_mutex); ++ inode_lock(inode); + aufs_write_lock(root); + err = au_opts_mount(sb, &opts); + au_opts_free(&opts); + if (!err && au_ftest_si(sbinfo, NO_DREVAL)) { + sb->s_d_op = &aufs_dop_noreval; -+ pr_info("%pf\n", sb->s_d_op); ++ pr_info("%ps\n", sb->s_d_op); + au_refresh_dop(root, /*force_reval*/0); + sbinfo->si_iop_array = aufs_iop_nogetattr; + au_refresh_iop(inode, /*force_getattr*/0); + } + aufs_write_unlock(root); -+ mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); + if (!err) + goto out_opts; /* success */ + @@ -27660,7 +30517,6 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + dput(root); + sb->s_root = NULL; +out_info: -+ dbgaufs_si_fin(sbinfo); + kobject_put(&sbinfo->si_kobj); + sb->s_fs_info = NULL; +out_opts: @@ -27679,7 +30535,6 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + void *raw_data) +{ + struct dentry *root; -+ struct super_block *sb; + + /* all timestamps always follow the ones on the branch */ + /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */ @@ -27687,11 +30542,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c + if (IS_ERR(root)) + goto out; + -+ sb = root->d_sb; -+ si_write_lock(sb, !AuLock_FLUSH); -+ sysaufs_brs_add(sb, 0); -+ si_write_unlock(sb); -+ au_sbilist_add(sb); ++ au_sbilist_add(root->d_sb); + +out: + return root; @@ -27715,6 +30566,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); @@ -27733,10 +30585,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,641 @@ ++++ linux/fs/aufs/super.h 2018-10-23 12:33:35.599375796 +0200 +@@ -0,0 +1,589 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -27763,8 +30616,9 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + +#include +#include ++#include "hbl.h" ++#include "lcnt.h" +#include "rwsem.h" -+#include "spl.h" +#include "wkq.h" + +/* policies to select one among multiple writable branches */ @@ -27795,14 +30649,6 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + unsigned long long mfsrr_watermark; +}; + -+struct pseudo_link { -+ union { -+ struct hlist_node hlist; -+ struct rcu_head rcu; -+ }; -+ struct inode *inode; -+}; -+ +#define AuPlink_NHASH 100 +static inline int au_plink_hash(ino_t ino) +{ @@ -27830,23 +30676,16 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + struct au_nowait_tasks si_nowait; + + /* -+ * tried sb->s_umount, but failed due to the dependecy between i_mutex. ++ * tried sb->s_umount, but failed due to the dependency between i_mutex. + * rwsem for au_sbinfo is necessary. + */ + struct au_rwsem si_rwsem; + -+ /* prevent recursive locking in deleting inode */ -+ struct { -+ unsigned long *bitmap; -+ spinlock_t tree_lock; -+ struct radix_tree_root tree; -+ } au_si_pid; -+ + /* + * dirty approach to protect sb->sb_inodes and ->s_files (gone) from + * remount. + */ -+ atomic_long_t si_ninodes, si_nfiles; ++ au_lcnt_t si_ninodes, si_nfiles; + + /* branch management */ + unsigned int si_generation; @@ -27854,7 +30693,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + /* see AuSi_ flags */ + unsigned char au_si_status; + -+ aufs_bindex_t si_bend; ++ aufs_bindex_t si_bbot; + + /* dirty trick to keep br_id plus */ + unsigned int si_last_br_id : @@ -27880,18 +30719,17 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + /* include/asm-ia64/siginfo.h defines a macro named si_flags */ + unsigned int si_mntflags; + -+ /* symlink to follow_link() and put_link() */ -+ struct au_sphlhead si_symlink; -+ + /* external inode number (bitmap and translation table) */ + vfs_readf_t si_xread; + vfs_writef_t si_xwrite; ++ loff_t si_ximaxent; /* max entries in a xino */ ++ + struct file *si_xib; + struct mutex si_xib_mtx; /* protect xib members */ + unsigned long *si_xib_buf; + unsigned long si_xib_last_pindex; + int si_xib_next_bit; -+ aufs_bindex_t si_xino_brid; ++ + unsigned long si_xino_jiffy; + unsigned long si_xino_expire; + /* reserved for future use */ @@ -27899,12 +30737,13 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + +#ifdef CONFIG_AUFS_EXPORT + /* i_generation */ ++ /* todo: make xigen file an array to support many inode numbers */ + struct file *si_xigen; + atomic_t si_xigen_next; +#endif + -+ /* dirty trick to suppoer atomic_open */ -+ struct au_sphlhead si_aopen; ++ /* dirty trick to support atomic_open */ ++ struct hlist_bl_head si_aopen; + + /* vdir parameters */ + unsigned long si_rdcache; /* max cache time in jiffies */ @@ -27920,13 +30759,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; @@ -27934,7 +30773,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + /* + * sysfs and lifetime management. + * this is not a small structure and it may be a waste of memory in case -+ * of sysfs is disabled, particulary when many aufs-es are mounted. ++ * of sysfs is disabled, particularly when many aufs-es are mounted. + * but using sysfs is majority. + */ + struct kobject si_kobj; @@ -27948,7 +30787,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h +#endif + +#ifdef CONFIG_AUFS_SBILIST -+ struct list_head si_list; ++ struct hlist_bl_node si_list; +#endif + + /* dirty, necessary for unmounting, sysfs and sysrq */ @@ -27959,7 +30798,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h +/* + * set true when refresh_dirs() failed at remount time. + * then try refreshing dirs at access time again. -+ * if it is false, refreshing dirs at access time is unnecesary ++ * if it is false, refreshing dirs at access time is unnecessary + */ +#define AuSi_FAILED_REFRESH_DIR 1 +#define AuSi_FHSM (1 << 1) /* fhsm is active now */ @@ -28000,6 +30839,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 */ @@ -28024,7 +30864,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h +/* sbinfo.c */ +void au_si_free(struct kobject *kobj); +int au_si_alloc(struct super_block *sb); -+int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr); ++int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr, int may_shrink); + +unsigned int au_sigen_inc(struct super_block *sb); +aufs_bindex_t au_new_br_id(struct super_block *sb); @@ -28038,16 +30878,12 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h +int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags); +void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); + -+int si_pid_test_slow(struct super_block *sb); -+void si_pid_set_slow(struct super_block *sb); -+void si_pid_clr_slow(struct super_block *sb); -+ +/* wbr_policy.c */ +extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; +extern struct au_wbr_create_operations au_wbr_create_ops[]; +int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst); +int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex); -+int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart); ++int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t btop); + +/* mvdown.c */ +int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *arg); @@ -28103,7 +30939,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h +void au_export_init(struct super_block *sb); +void au_xigen_inc(struct inode *inode); +int au_xigen_new(struct inode *inode); -+int au_xigen_set(struct super_block *sb, struct file *base); ++int au_xigen_set(struct super_block *sb, struct path *path); +void au_xigen_clr(struct super_block *sb); + +static inline int au_busy_or_stale(void) @@ -28117,7 +30953,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h +AuStubVoid(au_export_init, struct super_block *sb) +AuStubVoid(au_xigen_inc, struct inode *inode) +AuStubInt0(au_xigen_new, struct inode *inode) -+AuStubInt0(au_xigen_set, struct super_block *sb, struct file *base) ++AuStubInt0(au_xigen_set, struct super_block *sb, struct path *path) +AuStubVoid(au_xigen_clr, struct super_block *sb) +AuStub(int, au_busy_or_stale, return -EBUSY, void) +#endif /* CONFIG_AUFS_EXPORT */ @@ -28126,32 +30962,32 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + +#ifdef CONFIG_AUFS_SBILIST +/* module.c */ -+extern struct au_splhead au_sbilist; ++extern struct hlist_bl_head au_sbilist; + +static inline void au_sbilist_init(void) +{ -+ au_spl_init(&au_sbilist); ++ INIT_HLIST_BL_HEAD(&au_sbilist); +} + +static inline void au_sbilist_add(struct super_block *sb) +{ -+ au_spl_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_spl_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 @@ -28189,56 +31025,48 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + +/* ---------------------------------------------------------------------- */ + -+static inline pid_t si_pid_bit(void) -+{ -+ /* the origin of pid is 1, but the bitmap's is 0 */ -+ return current->pid - 1; -+} ++/* 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; -+ -+ bit = si_pid_bit(); -+ if (bit < PID_MAX_DEFAULT) -+ return test_bit(bit, au_sbi(sb)->au_si_pid.bitmap); -+ return si_pid_test_slow(sb); ++ return !!task_test_aufs(current); +} + -+static inline void si_pid_set(struct super_block *sb) ++static inline void si_pid_clr(struct super_block *sb) +{ -+ pid_t bit; -+ -+ bit = si_pid_bit(); -+ if (bit < PID_MAX_DEFAULT) { -+ AuDebugOn(test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); -+ set_bit(bit, au_sbi(sb)->au_si_pid.bitmap); -+ /* smp_mb(); */ -+ } else -+ si_pid_set_slow(sb); ++ AuDebugOn(!task_test_aufs(current)); ++ task_clear_aufs(current); +} + -+static inline void si_pid_clr(struct super_block *sb) ++static inline void si_pid_set(struct super_block *sb) +{ -+ pid_t bit; -+ -+ bit = si_pid_bit(); -+ if (bit < PID_MAX_DEFAULT) { -+ AuDebugOn(!test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); -+ clear_bit(bit, au_sbi(sb)->au_si_pid.bitmap); -+ /* smp_mb(); */ -+ } else -+ si_pid_clr_slow(sb); ++ AuDebugOn(task_test_aufs(current)); ++ task_set_aufs(current); +} + +/* ---------------------------------------------------------------------- */ + +/* lock superblock. mainly for entry point functions */ -+/* -+ * __si_read_lock, __si_write_lock, -+ * __si_read_unlock, __si_write_unlock, __si_downgrade_lock -+ */ -+AuSimpleRwsemFuncs(__si, struct super_block *sb, &au_sbi(sb)->si_rwsem); ++#define __si_read_lock(sb) au_rw_read_lock(&au_sbi(sb)->si_rwsem) ++#define __si_write_lock(sb) au_rw_write_lock(&au_sbi(sb)->si_rwsem) ++#define __si_read_trylock(sb) au_rw_read_trylock(&au_sbi(sb)->si_rwsem) ++#define __si_write_trylock(sb) au_rw_write_trylock(&au_sbi(sb)->si_rwsem) ++/* ++#define __si_read_trylock_nested(sb) \ ++ au_rw_read_trylock_nested(&au_sbi(sb)->si_rwsem) ++#define __si_write_trylock_nested(sb) \ ++ au_rw_write_trylock_nested(&au_sbi(sb)->si_rwsem) ++*/ ++ ++#define __si_read_unlock(sb) au_rw_read_unlock(&au_sbi(sb)->si_rwsem) ++#define __si_write_unlock(sb) au_rw_write_unlock(&au_sbi(sb)->si_rwsem) ++#define __si_downgrade_lock(sb) au_rw_dgrade_lock(&au_sbi(sb)->si_rwsem) + +#define SiMustNoWaiters(sb) AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem) +#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem) @@ -28315,10 +31143,10 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + +/* ---------------------------------------------------------------------- */ + -+static inline aufs_bindex_t au_sbend(struct super_block *sb) ++static inline aufs_bindex_t au_sbbot(struct super_block *sb) +{ + SiMustAnyLock(sb); -+ return au_sbi(sb)->si_bend; ++ return au_sbi(sb)->si_bbot; +} + +static inline unsigned int au_mntflags(struct super_block *sb) @@ -28333,28 +31161,6 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + return au_sbi(sb)->si_generation; +} + -+static inline void au_ninodes_inc(struct super_block *sb) -+{ -+ atomic_long_inc(&au_sbi(sb)->si_ninodes); -+} -+ -+static inline void au_ninodes_dec(struct super_block *sb) -+{ -+ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_ninodes)); -+ atomic_long_dec(&au_sbi(sb)->si_ninodes); -+} -+ -+static inline void au_nfiles_inc(struct super_block *sb) -+{ -+ atomic_long_inc(&au_sbi(sb)->si_nfiles); -+} -+ -+static inline void au_nfiles_dec(struct super_block *sb) -+{ -+ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_nfiles)); -+ atomic_long_dec(&au_sbi(sb)->si_nfiles); -+} -+ +static inline struct au_branch *au_sbr(struct super_block *sb, + aufs_bindex_t bindex) +{ @@ -28362,26 +31168,21 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h + return au_sbi(sb)->si_branch[0 + bindex]; +} + -+static inline void au_xino_brid_set(struct super_block *sb, aufs_bindex_t brid) -+{ -+ SiMustWriteLock(sb); -+ au_sbi(sb)->si_xino_brid = brid; -+} -+ -+static inline aufs_bindex_t au_xino_brid(struct super_block *sb) ++static inline loff_t au_xi_maxent(struct super_block *sb) +{ + SiMustAnyLock(sb); -+ return au_sbi(sb)->si_xino_brid; ++ return au_sbi(sb)->si_ximaxent; +} + +#endif /* __KERNEL__ */ +#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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,104 @@ ++++ linux/fs/aufs/sysaufs.c 2018-08-12 23:43:05.463458173 +0200 +@@ -0,0 +1,93 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -28441,18 +31242,11 @@ diff -urN /usr/share/empty/fs/aufs/sysaufs.c linux/fs/aufs/sysaufs.c + (&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL, + SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo)); + -+ dbgaufs_si_null(sbinfo); -+ if (!err) { -+ err = dbgaufs_si_init(sbinfo); -+ if (unlikely(err)) -+ kobject_put(&sbinfo->si_kobj); -+ } + return err; +} + +void sysaufs_fin(void) +{ -+ dbgaufs_fin(); + sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group); + kset_unregister(sysaufs_kset); +} @@ -28473,23 +31267,19 @@ diff -urN /usr/share/empty/fs/aufs/sysaufs.c linux/fs/aufs/sysaufs.c + if (IS_ERR(sysaufs_kset)) + goto out; + err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group); -+ if (unlikely(err)) { ++ if (unlikely(err)) + kset_unregister(sysaufs_kset); -+ goto out; -+ } + -+ err = dbgaufs_init(); -+ if (unlikely(err)) -+ sysaufs_fin(); +out: + return err; +} 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,101 @@ ++++ linux/fs/aufs/sysaufs.h 2018-08-12 23:43:05.463458173 +0200 +@@ -0,0 +1,102 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -28591,10 +31381,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,376 @@ ++++ linux/fs/aufs/sysfs.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,373 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -28737,7 +31528,7 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c + ssize_t err; + int idx; + long l; -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + struct au_sbinfo *sbinfo; + struct super_block *sb; + struct seq_file *seq; @@ -28790,8 +31581,8 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c + + err = kstrtol(name, 10, &l); + if (!err) { -+ bend = au_sbend(sb); -+ if (l <= bend) ++ bbot = au_sbbot(sb); ++ if (l <= bbot) + err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); + else + err = -ENOENT; @@ -28804,7 +31595,7 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c + if (unlikely(err == PAGE_SIZE)) + err = -EFBIG; + } -+ kfree(seq); ++ au_kfree_rcu(seq); +out_unlock: + si_read_unlock(sb); +out: @@ -28817,15 +31608,15 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c +{ + int err; + int16_t brid; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + size_t sz; + char *buf; + struct seq_file *seq; + struct au_branch *br; + + si_read_lock(sb, AuLock_FLUSH); -+ bend = au_sbend(sb); -+ err = bend + 1; ++ bbot = au_sbbot(sb); ++ err = bbot + 1; + if (!arg) + goto out; + @@ -28840,7 +31631,7 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c + goto out_buf; + + sz = sizeof(*arg) - offsetof(union aufs_brinfo, path); -+ for (bindex = 0; bindex <= bend; bindex++, arg++) { ++ for (bindex = 0; bindex <= bbot; bindex++, arg++) { + err = !access_ok(VERIFY_WRITE, arg, sizeof(*arg)); + if (unlikely(err)) + break; @@ -28875,7 +31666,7 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c + err = -EFAULT; + +out_seq: -+ kfree(seq); ++ au_kfree_rcu(seq); +out_buf: + free_page((unsigned long)buf); +out: @@ -28908,7 +31699,7 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c + attr = &br_sysfs->attr; + sysfs_attr_init(attr); + attr->name = br_sysfs->name; -+ attr->mode = S_IRUGO; ++ attr->mode = 0444; + br_sysfs++; + } +} @@ -28919,16 +31710,14 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c + struct kobject *kobj; + struct au_brsysfs *br_sysfs; + int i; -+ aufs_bindex_t bend; -+ -+ dbgaufs_brs_del(sb, bindex); ++ aufs_bindex_t bbot; + + if (!sysaufs_brs) + return; + + kobj = &au_sbi(sb)->si_kobj; -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + br_sysfs = br->br_sysfs; + for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { @@ -28941,19 +31730,17 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c +void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) +{ + int err, i; -+ aufs_bindex_t bend; ++ aufs_bindex_t bbot; + struct kobject *kobj; + struct au_branch *br; + struct au_brsysfs *br_sysfs; + -+ dbgaufs_brs_add(sb, bindex); -+ + if (!sysaufs_brs) + return; + + kobj = &au_sbi(sb)->si_kobj; -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + br_sysfs = br->br_sysfs; + snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), @@ -28971,10 +31758,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,157 @@ ++++ linux/fs/aufs/sysrq.c 2018-10-23 12:33:35.599375796 +0200 +@@ -0,0 +1,160 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -28991,7 +31779,7 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c + */ + +/* -+ * magic sysrq hanlder ++ * magic sysrq handler + */ + +/* #include */ @@ -29005,7 +31793,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; @@ -29064,8 +31853,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; @@ -29073,7 +31862,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 @@ -29084,16 +31873,17 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c + +/* module parameter */ +static char *aufs_sysrq_key = "a"; -+module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO); ++module_param_named(sysrq, aufs_sysrq_key, charp, 0444); +MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); + +static void au_sysrq(int key __maybe_unused) +{ + struct au_sbinfo *sbinfo; ++ struct hlist_bl_node *pos; + + lockdep_off(); + au_sbilist_lock(); -+ list_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(); @@ -29132,10 +31922,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,888 @@ ++++ linux/fs/aufs/vdir.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,895 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -29246,7 +32037,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) -+ kfree(pos); ++ au_kfree_rcu(pos); +} + +static void au_nhash_de_do_free(struct hlist_head *head) @@ -29273,7 +32064,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + nhash_count(head); + free(head++); + } -+ kfree(nhash->nh_head); ++ au_kfree_try_rcu(nhash->nh_head); +} + +void au_nhash_wh_free(struct au_nhash *whlist) @@ -29316,6 +32107,8 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + AuDebugOn(!nhash->nh_num || !nhash->nh_head); + + v = 0; ++ if (len > 8) ++ len = 8; + while (len--) + v += *name++; + /* v = hash_long(v, magic_bit); */ @@ -29413,8 +32206,8 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + unsigned char **o; + + err = -ENOMEM; -+ o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), -+ GFP_NOFS); ++ o = au_krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), ++ GFP_NOFS, /*may_shrink*/0); + if (unlikely(!o)) + goto out; + @@ -29490,8 +32283,8 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + + deblk = vdir->vd_deblk; + while (vdir->vd_nblk--) -+ kfree(*deblk++); -+ kfree(vdir->vd_deblk); ++ au_kfree_try_rcu(*deblk++); ++ au_kfree_try_rcu(vdir->vd_deblk); + au_cache_free_vdir(vdir); +} + @@ -29526,7 +32319,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + if (!err) + return vdir; /* success */ + -+ kfree(vdir->vd_deblk); ++ au_kfree_try_rcu(vdir->vd_deblk); + +out_free: + au_cache_free_vdir(vdir); @@ -29541,7 +32334,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) { -+ kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); ++ au_kfree_try_rcu(vdir->vd_deblk[vdir->vd_nblk - 1]); + /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ + vdir->vd_nblk--; + } @@ -29617,6 +32410,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) + goto out; /* already whiteouted */ + ++ ino = 0; /* just to suppress a warning */ + if (shwh) + arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, + &ino); @@ -29687,7 +32481,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + int err; + unsigned int rdhash; + loff_t offset; -+ aufs_bindex_t bend, bindex, bstart; ++ aufs_bindex_t bbot, bindex, btop; + unsigned char shwh; + struct file *hf, *file; + struct super_block *sb; @@ -29713,9 +32507,9 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + shwh = 1; + au_fset_fillvdir(arg->flags, SHWH); + } -+ bstart = au_fbstart(file); -+ bend = au_fbend_dir(file); -+ for (bindex = bstart; !err && bindex <= bend; bindex++) { ++ btop = au_fbtop(file); ++ bbot = au_fbbot_dir(file); ++ for (bindex = btop; !err && bindex <= bbot; bindex++) { + hf = au_hf_dir(file, bindex); + if (!hf) + continue; @@ -29728,7 +32522,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + arg->bindex = bindex; + au_fclr_fillvdir(arg->flags, WHABLE); + if (shwh -+ || (bindex != bend ++ || (bindex != bbot + && au_br_whable(au_sbr_perm(sb, bindex)))) + au_fset_fillvdir(arg->flags, WHABLE); + do { @@ -29773,6 +32567,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + err = 0; + inode = file_inode(file); + IMustLock(inode); ++ IiMustWriteLock(inode); + SiMustAnyLock(inode->i_sb); + + allocated = NULL; @@ -29788,7 +32583,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + err = 0; + allocated = vdir; + } else if (may_read -+ && (inode->i_version != vdir->vd_version ++ && (!inode_eq_iversion(inode, vdir->vd_version) + || time_after(jiffies, vdir->vd_jiffy + expire))) { + do_read = 1; + err = reinit_vdir(vdir); @@ -29804,7 +32599,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + err = au_do_read_vdir(&arg); + if (!err) { + /* file->f_pos = 0; */ /* todo: ctx->pos? */ -+ vdir->vd_version = inode->i_version; ++ vdir->vd_version = inode_query_iversion(inode); + vdir->vd_last.ul = 0; + vdir->vd_last.p.deblk = vdir->vd_deblk[0]; + if (allocated) @@ -29828,8 +32623,8 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + if (tgt->vd_nblk < src->vd_nblk) { + unsigned char **p; + -+ p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, -+ GFP_NOFS); ++ p = au_krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, ++ GFP_NOFS, /*may_shrink*/0); + if (unlikely(!p)) + goto out; + tgt->vd_deblk = p; @@ -29839,7 +32634,8 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + unsigned char *p; + + tgt->vd_deblk_sz = deblk_sz; -+ p = krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS); ++ p = au_krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS, ++ /*may_shrink*/1); + if (unlikely(!p)) + goto out; + tgt->vd_deblk[0] = p; @@ -29900,7 +32696,7 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + inode = file_inode(file); + err = copy_vdir(vdir_cache, au_ivdir(inode)); + if (!err) { -+ file->f_version = inode->i_version; ++ file->f_version = inode_query_iversion(inode); + if (allocated) + au_set_fvdir_cache(file, allocated); + } else if (allocated) @@ -29973,7 +32769,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; +} + @@ -29984,10 +32781,10 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c + struct au_vdir *vdir_cache; + struct au_vdir_de *de; + -+ vdir_cache = au_fvdir_cache(file); + if (!seek_vdir(file, ctx)) + return 0; + ++ vdir_cache = au_fvdir_cache(file); + deblk_sz = vdir_cache->vd_deblk_sz; + while (1) { + deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; @@ -30024,10 +32821,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,848 @@ ++++ linux/fs/aufs/vfsub.c 2018-10-23 12:33:35.599375796 +0200 +@@ -0,0 +1,902 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -30047,11 +32845,38 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c + * sub-routines for VFS + */ + ++#include +#include ++#include +#include +#include +#include "aufs.h" + ++#ifdef CONFIG_AUFS_BR_FUSE ++int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb) ++{ ++ if (!au_test_fuse(h_sb) || !au_userns) ++ return 0; ++ ++ 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) +{ + int err; @@ -30067,7 +32892,7 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c + h_sb = h_path->dentry->d_sb; + *did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb)); + if (*did) -+ err = vfs_getattr(h_path, &st); ++ err = vfsub_getattr(h_path, &st); + + return err; +} @@ -30111,9 +32936,10 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c + * cf. linux/fs/namei.c:do_last(), lookup_open() and atomic_open(). + */ +int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *args, struct au_branch *br) ++ struct vfsub_aopen_args *args) +{ + int err; ++ struct au_branch *br = args->br; + struct file *file = args->file; + /* copied from linux/fs/namei.c:atomic_open() */ + struct dentry *const DENTRY_NOT_SET = (void *)-1UL; @@ -30125,31 +32951,37 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c + if (unlikely(err)) + goto out; + -+ args->file->f_path.dentry = DENTRY_NOT_SET; -+ args->file->f_path.mnt = au_br_mnt(br); ++ au_lcnt_inc(&br->br_nfiles); ++ file->f_path.dentry = DENTRY_NOT_SET; ++ file->f_path.mnt = au_br_mnt(br); ++ AuDbg("%ps\n", dir->i_op->atomic_open); + err = dir->i_op->atomic_open(dir, dentry, file, args->open_flag, -+ args->create_mode, args->opened); -+ if (err >= 0) { -+ /* some filesystems don't set FILE_CREATED while succeeded? */ -+ if (*args->opened & FILE_CREATED) -+ fsnotify_create(dir, dentry); -+ } else ++ args->create_mode); ++ if (unlikely(err < 0)) { ++ au_lcnt_dec(&br->br_nfiles); + goto out; ++ } + ++ /* temporary workaround for nfsv4 branch */ ++ if (au_test_nfs(dir->i_sb)) ++ nfs_mark_for_revalidate(dir); + -+ if (!err) { -+ /* todo: call VFS:may_open() here */ -+ err = open_check_o_direct(file); -+ /* todo: ima_file_check() too? */ -+ if (!err && (args->open_flag & __FMODE_EXEC)) -+ err = deny_write_access(file); -+ if (unlikely(err)) -+ /* note that the file is created and still opened */ -+ goto out; ++ if (file->f_mode & FMODE_CREATED) ++ fsnotify_create(dir, dentry); ++ if (!(file->f_mode & FMODE_OPENED)) { ++ au_lcnt_dec(&br->br_nfiles); ++ goto out; + } + -+ atomic_inc(&br->br_count); -+ fsnotify_open(file); ++ /* todo: call VFS:may_open() here */ ++ /* todo: ima_file_check() too? */ ++ if (!err && (args->open_flag & __FMODE_EXEC)) ++ err = deny_write_access(file); ++ if (!err) ++ fsnotify_open(file); ++ else ++ au_lcnt_dec(&br->br_nfiles); ++ /* note that the file is created and still opened */ + +out: + return err; @@ -30165,6 +32997,24 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c + return err; +} + ++struct dentry *vfsub_lookup_one_len_unlocked(const char *name, ++ struct dentry *parent, int len) ++{ ++ struct path path = { ++ .mnt = NULL ++ }; ++ ++ path.dentry = lookup_one_len_unlocked(name, parent, len); ++ if (IS_ERR(path.dentry)) ++ goto out; ++ if (d_is_positive(path.dentry)) ++ vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ ++ ++out: ++ AuTraceErrPtr(path.dentry); ++ return path.dentry; ++} ++ +struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, + int len) +{ @@ -30375,7 +33225,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 = { @@ -30396,7 +33246,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; @@ -30568,13 +33418,14 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c +{ + int err; + -+ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); ++ AuDbg("%pD, ctx{%ps, %llu}\n", file, ctx->actor, ctx->pos); + + lockdep_off(); + err = iterate_dir(file, ctx); + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ + return err; +} + @@ -30876,10 +33727,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,287 @@ ++++ linux/fs/aufs/vfsub.h 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,355 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -30905,14 +33757,16 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h +#ifdef __KERNEL__ + +#include ++#include +#include ++#include +#include +#include "debug.h" + +/* copied from linux/fs/internal.h */ +/* todo: BAD approach!! */ +extern void __mnt_drop_write(struct vfsmount *); -+extern int open_check_o_direct(struct file *f); ++extern struct file *alloc_empty_file(int, const struct cred *); + +/* ---------------------------------------------------------------------- */ + @@ -30931,7 +33785,7 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h + +/* to debug easier, do not make them inlined functions */ +#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) -+#define IMustLock(i) MtxMustLock(&(i)->i_mutex) ++#define IMustLock(i) AuDebugOn(!inode_is_locked(i)) + +/* ---------------------------------------------------------------------- */ + @@ -30950,28 +33804,38 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h + +static inline int vfsub_native_ro(struct inode *inode) +{ -+ return (inode->i_sb->s_flags & MS_RDONLY) ++ return sb_rdonly(inode->i_sb) + || IS_RDONLY(inode) + /* || IS_APPEND(inode) */ + || IS_IMMUTABLE(inode); +} + ++#ifdef CONFIG_AUFS_BR_FUSE ++int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb); ++#else ++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); +struct file *vfsub_dentry_open(struct path *path, int flags); +struct file *vfsub_filp_open(const char *path, int oflags, int mode); ++struct au_branch; +struct vfsub_aopen_args { -+ struct file *file; -+ unsigned int open_flag; -+ umode_t create_mode; -+ int *opened; ++ struct file *file; ++ unsigned int open_flag; ++ umode_t create_mode; ++ struct au_branch *br; +}; -+struct au_branch; +int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *args, struct au_branch *br); ++ struct vfsub_aopen_args *args); +int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); + ++struct dentry *vfsub_lookup_one_len_unlocked(const char *name, ++ struct dentry *parent, int len); +struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, + int len); + @@ -31034,7 +33898,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); + @@ -31067,6 +33931,12 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h + return flags; +} + ++static inline int vfsub_file_execed(struct file *file) ++{ ++ /* todo: direct access f_flags */ ++ return !!(vfsub_file_flags(file) & __FMODE_EXEC); ++} ++ +#if 0 /* reserved */ +static inline void vfsub_file_accessed(struct file *h_file) +{ @@ -31088,13 +33958,27 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h +} +#endif + -+static inline int vfsub_update_time(struct inode *h_inode, struct timespec *ts, -+ int flags) ++static inline int vfsub_update_time(struct inode *h_inode, ++ struct timespec64 *ts, int flags) +{ -+ return generic_update_time(h_inode, ts, flags); ++ return update_time(h_inode, ts, flags); + /* no vfsub_update_h_iattr() since we don't have struct path */ +} + ++#ifdef CONFIG_FS_POSIX_ACL ++static inline int vfsub_acl_chmod(struct inode *h_inode, umode_t h_mode) ++{ ++ int err; ++ ++ err = posix_acl_chmod(h_inode, h_mode); ++ if (err == -EOPNOTSUPP) ++ err = 0; ++ return err; ++} ++#else ++AuStubInt0(vfsub_acl_chmod, struct inode *h_inode, umode_t h_mode); ++#endif ++ +long vfsub_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); @@ -31115,6 +33999,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 loff_t vfsub_clone_file_range(struct file *src, struct file *dst, ++ loff_t len) ++{ ++ loff_t err; ++ ++ lockdep_off(); ++ err = vfs_clone_file_range(src, 0, dst, 0, len, /*remap_flags*/0); ++ 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) @@ -31138,6 +34052,11 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h +int vfsub_unlink(struct inode *dir, struct path *path, + struct inode **delegated_inode, int force); + ++static inline int vfsub_getattr(const struct path *path, struct kstat *st) ++{ ++ return vfs_getattr(path, st, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT); ++} ++ +/* ---------------------------------------------------------------------- */ + +static inline int vfsub_setxattr(struct dentry *dentry, const char *name, @@ -31167,10 +34086,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,765 @@ ++++ linux/fs/aufs/wbr_policy.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,830 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -31278,13 +34198,13 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + struct dentry *h_parent, void *arg) +{ + int err, rerr; -+ aufs_bindex_t bopq, bstart; ++ aufs_bindex_t bopq, btop; + struct path h_path; + struct dentry *parent; + struct inode *h_dir, *h_inode, *inode, *dir; + unsigned int *flags = arg; + -+ bstart = au_dbstart(dentry); ++ btop = au_dbtop(dentry); + /* dentry is di-locked */ + parent = dget_parent(dentry); + dir = d_inode(parent); @@ -31297,8 +34217,7 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + goto out; + h_path.dentry = au_h_dptr(dentry, bdst); + h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); -+ err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, -+ S_IRWXU | S_IRUGO | S_IXUGO); ++ err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, 0755); + if (unlikely(err)) + goto out_put; + au_fset_cpdown(*flags, MADE_DIR); @@ -31311,17 +34230,17 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst) + au_fset_cpdown(*flags, PARENT_OPQ); + h_inode = d_inode(h_path.dentry); -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); + if (au_ftest_cpdown(*flags, WHED)) { + err = au_cpdown_dir_opq(dentry, bdst, flags); + if (unlikely(err)) { -+ mutex_unlock(&h_inode->i_mutex); ++ inode_unlock(h_inode); + goto out_dir; + } + } + -+ err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart)); -+ mutex_unlock(&h_inode->i_mutex); ++ err = au_cpdown_attr(&h_path, au_h_dptr(dentry, btop)); ++ inode_unlock(h_inode); + if (unlikely(err)) + goto out_opq; + @@ -31332,8 +34251,8 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + } + + inode = d_inode(dentry); -+ if (au_ibend(inode) < bdst) -+ au_set_ibend(inode, bdst); ++ if (au_ibbot(inode) < bdst) ++ au_set_ibbot(inode, bdst); + au_set_h_iptr(inode, bdst, au_igrab(h_inode), + au_hi_flags(inode, /*isdir*/1)); + au_fhsm_wrote(dentry->d_sb, bdst, /*force*/0); @@ -31342,9 +34261,9 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + /* revert */ +out_opq: + if (au_ftest_cpdown(*flags, DIROPQ)) { -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(h_inode, AuLsc_I_CHILD); + rerr = au_diropq_remove(dentry, bdst); -+ mutex_unlock(&h_inode->i_mutex); ++ inode_unlock(h_inode); + if (unlikely(rerr)) { + AuIOErr("failed removing diropq for %pd b%d (%d)\n", + dentry, bdst, rerr); @@ -31363,8 +34282,8 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + } +out_put: + au_set_h_dptr(dentry, bdst, NULL); -+ if (au_dbend(dentry) == bdst) -+ au_update_dbend(dentry); ++ if (au_dbbot(dentry) == bdst) ++ au_update_dbbot(dentry); +out: + dput(parent); + return err; @@ -31436,19 +34355,19 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + unsigned int flags __maybe_unused) +{ + int err; -+ aufs_bindex_t bstart, bindex; ++ aufs_bindex_t btop, bindex; + struct super_block *sb; + struct dentry *parent, *h_parent; + + sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ err = bstart; -+ if (!au_br_rdonly(au_sbr(sb, bstart))) ++ btop = au_dbtop(dentry); ++ err = btop; ++ if (!au_br_rdonly(au_sbr(sb, btop))) + goto out; + + err = -EROFS; + parent = dget_parent(dentry); -+ for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { ++ for (bindex = au_dbtop(parent); bindex < btop; bindex++) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; @@ -31462,7 +34381,7 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + + /* bottom up here */ + if (unlikely(err < 0)) { -+ err = au_wbr_bu(sb, bstart - 1); ++ err = au_wbr_bu(sb, btop - 1); + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + } @@ -31514,7 +34433,7 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c +{ + int err; + -+ err = au_wbr_bu(sb, au_sbend(sb)); ++ err = au_wbr_bu(sb, au_sbbot(sb)); + atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ + /* smp_mb(); */ + @@ -31526,7 +34445,7 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c +{ + int err, nbr; + unsigned int u; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct super_block *sb; + atomic_t *next; + @@ -31536,9 +34455,9 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + + sb = dentry->d_sb; + next = &au_sbi(sb)->si_wbr_rr_next; -+ bend = au_sbend(sb); -+ nbr = bend + 1; -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ nbr = bbot + 1; ++ for (bindex = 0; bindex <= bbot; bindex++) { + if (!au_ftest_wbr(flags, DIR)) { + err = atomic_dec_return(next) + 1; + /* modulo for 0 is meaningless */ @@ -31572,7 +34491,7 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + struct au_branch *br; + struct au_wbr_mfs *mfs; + struct dentry *h_parent; -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + int err; + unsigned long long b, bavail; + struct path h_path; @@ -31593,13 +34512,13 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + mfs->mfsrr_bytes = 0; + if (!parent) { + bindex = 0; -+ bend = au_sbend(sb); ++ bbot = au_sbbot(sb); + } else { -+ bindex = au_dbstart(parent); -+ bend = au_dbtaildir(parent); ++ bindex = au_dbtop(parent); ++ bbot = au_dbtaildir(parent); + } + -+ for (; bindex <= bend; bindex++) { ++ for (; bindex <= bbot; bindex++) { + if (parent) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) @@ -31632,7 +34551,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); -+ kfree(st); ++ au_kfree_rcu(st); +} + +static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) @@ -31688,6 +34607,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) +{ @@ -31724,7 +34698,7 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c +{ + int err, e2; + unsigned long long b; -+ aufs_bindex_t bindex, bstart, bend; ++ aufs_bindex_t bindex, btop, bbot; + struct super_block *sb; + struct dentry *parent, *h_parent; + struct au_branch *br; @@ -31733,9 +34707,9 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + if (unlikely(err < 0)) + goto out; + parent = dget_parent(dentry); -+ bstart = au_dbstart(parent); -+ bend = au_dbtaildir(parent); -+ if (bstart == bend) ++ btop = au_dbtop(parent); ++ bbot = au_dbtaildir(parent); ++ if (btop == bbot) + goto out_parent; /* success */ + + e2 = au_wbr_create_mfs(dentry, flags); @@ -31748,7 +34722,7 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + b = br->br_wbr->wbr_bytes; + AuDbg("b%d, %llu\n", err, b); + -+ for (bindex = bstart; bindex <= bend; bindex++) { ++ for (bindex = btop; bindex <= bbot; bindex++) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; @@ -31819,15 +34793,15 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c +static int au_wbr_copyup_bup(struct dentry *dentry) +{ + int err; -+ aufs_bindex_t bindex, bstart; ++ aufs_bindex_t bindex, btop; + struct dentry *parent, *h_parent; + struct super_block *sb; + + err = -EROFS; + sb = dentry->d_sb; + parent = dget_parent(dentry); -+ bstart = au_dbstart(parent); -+ for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) { ++ btop = au_dbtop(parent); ++ for (bindex = au_dbtop(dentry); bindex >= btop; bindex--) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; @@ -31841,20 +34815,20 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c + + /* bottom up here */ + if (unlikely(err < 0)) -+ err = au_wbr_bu(sb, bstart - 1); ++ err = au_wbr_bu(sb, btop - 1); + + AuDbg("b%d\n", err); + return err; +} + +/* bottom up */ -+int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart) ++int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t btop) +{ + int err; + -+ err = au_wbr_bu(dentry->d_sb, bstart); ++ err = au_wbr_bu(dentry->d_sb, btop); + AuDbg("b%d\n", err); -+ if (err > bstart) ++ if (err > btop) + err = au_wbr_nonopq(dentry, err); + + AuDbg("b%d\n", err); @@ -31864,10 +34838,10 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c +static int au_wbr_copyup_bu(struct dentry *dentry) +{ + int err; -+ aufs_bindex_t bstart; ++ aufs_bindex_t btop; + -+ bstart = au_dbstart(dentry); -+ err = au_wbr_do_copyup_bu(dentry, bstart); ++ btop = au_dbtop(dentry); ++ err = au_wbr_do_copyup_bu(dentry, btop); + return err; +} + @@ -31913,6 +34887,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, @@ -31936,10 +34920,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,1060 @@ ++++ linux/fs/aufs/whout.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,1062 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -31961,7 +34946,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + +#include "aufs.h" + -+#define WH_MASK S_IRUGO ++#define WH_MASK 0444 + +/* + * If a directory contains this file, then it is opaque. We start with the @@ -32102,7 +35087,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + +out_name: + if (name != defname) -+ kfree(name); ++ au_kfree_try_rcu(name); +out: + AuTraceErrPtr(dentry); + return dentry; @@ -32131,7 +35116,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" @@ -32253,10 +35239,10 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + + err = -EEXIST; + if (d_is_negative(path->dentry)) { -+ int mode = S_IRWXU; ++ int mode = 0700; + + if (au_test_nfs(path->dentry->d_sb)) -+ mode |= S_IXUGO; ++ mode |= 0111; + err = vfsub_mkdir(h_dir, path, mode); + } else if (d_is_dir(path->dentry)) + err = 0; @@ -32285,7 +35271,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c +/* + * returns tri-state, + * minus: error, caller should print the message -+ * zero: succuess ++ * zero: success + * plus: error, caller should NOT print the message + */ +static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr, @@ -32506,7 +35492,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + h_root = au_h_dptr(a->sb->s_root, bindex); + AuDebugOn(h_root != au_br_dentry(a->br)); + -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + wbr_wh_write_lock(wbr); + err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, + h_root, a->br); @@ -32530,7 +35516,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + if (!err) + err = au_wh_init(a->br, a->sb); + wbr_wh_write_unlock(wbr); -+ au_hn_imtx_unlock(hdir); ++ au_hn_inode_unlock(hdir); + di_read_unlock(a->sb->s_root, AuLock_IR); + if (!err) + au_fhsm_wrote(a->sb, bindex, /*force*/0); @@ -32538,10 +35524,10 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c +out: + if (wbr) + atomic_dec(&wbr->wbr_wh_running); -+ atomic_dec(&a->br->br_count); ++ au_lcnt_dec(&a->br->br_count); + si_write_unlock(a->sb); + au_nwt_done(&au_sbi(a->sb)->si_nowait); -+ kfree(arg); ++ au_kfree_rcu(a); + if (unlikely(err)) + AuIOErr("err %d\n", err); +} @@ -32564,12 +35550,12 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + */ + arg->sb = sb; + arg->br = br; -+ atomic_inc(&br->br_count); ++ au_lcnt_inc(&br->br_count); + wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*flags*/0); + if (unlikely(wkq_err)) { + atomic_dec(&br->br_wbr->wbr_wh_running); -+ atomic_dec(&br->br_count); -+ kfree(arg); ++ au_lcnt_dec(&br->br_count); ++ au_kfree_rcu(arg); + } + do_dec = 0; + } @@ -32728,7 +35714,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); -+ kfree(wh_name.name); ++ au_kfree_try_rcu(wh_name.name); + } + return wh_dentry; +} @@ -32846,7 +35832,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)) { -+ kfree(whtmp); ++ au_kfree_rcu(whtmp); + whtmp = ERR_PTR(err); + } + @@ -32857,11 +35843,11 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c +void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) +{ + if (whtmp->br) -+ atomic_dec(&whtmp->br->br_count); ++ au_lcnt_dec(&whtmp->br->br_count); + dput(whtmp->wh_dentry); + iput(whtmp->dir); + au_nhash_wh_free(&whtmp->whlist); -+ kfree(whtmp); ++ au_kfree_rcu(whtmp); +} + +/* @@ -32882,7 +35868,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + + br = au_sbr(dir->i_sb, bindex); + wh_inode = d_inode(wh_dentry); -+ mutex_lock_nested(&wh_inode->i_mutex, AuLsc_I_CHILD); ++ inode_lock_nested(wh_inode, AuLsc_I_CHILD); + + /* + * someone else might change some whiteouts while we were sleeping. @@ -32904,7 +35890,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + if (unlikely(wkq_err)) + err = wkq_err; + } -+ mutex_unlock(&wh_inode->i_mutex); ++ inode_unlock(wh_inode); + + if (!err) { + h_tmp.dentry = wh_dentry; @@ -32916,7 +35902,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + } + + if (!err) { -+ if (au_ibstart(dir) == bindex) { ++ if (au_ibtop(dir) == bindex) { + /* todo: dir->i_mutex is necessary */ + au_cpup_attr_timesizes(dir); + if (h_nlink) @@ -32940,7 +35926,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + struct au_hinode *hdir; + + /* rmdir by nfsd may cause deadlock with this i_mutex */ -+ /* mutex_lock(&a->dir->i_mutex); */ ++ /* inode_lock(a->dir); */ + err = -EROFS; + sb = a->dir->i_sb; + si_read_lock(sb, !AuLock_FLUSH); @@ -32958,19 +35944,19 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + err = vfsub_mnt_want_write(au_br_mnt(a->br)); + if (unlikely(err)) + goto out_mnt; -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, + a->br); + if (!err) + err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); -+ au_hn_imtx_unlock(hdir); ++ au_hn_inode_unlock(hdir); + vfsub_mnt_drop_write(au_br_mnt(a->br)); + +out_mnt: + dput(h_parent); + ii_write_unlock(a->dir); +out: -+ /* mutex_unlock(&a->dir->i_mutex); */ ++ /* inode_unlock(a->dir); */ + au_whtmp_rmdir_free(a); + si_read_unlock(sb); + au_nwt_done(&au_sbi(sb)->si_nowait); @@ -32990,7 +35976,7 @@ diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c + sb = dir->i_sb; + args->dir = au_igrab(dir); + args->br = au_sbr(sb, bindex); -+ atomic_inc(&args->br->br_count); ++ au_lcnt_inc(&args->br->br_count); + args->wh_dentry = dget(wh_dentry); + wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, sb, /*flags*/0); + if (unlikely(wkq_err)) { @@ -33000,10 +35986,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,85 @@ ++++ linux/fs/aufs/whout.h 2018-08-12 23:43:05.463458173 +0200 +@@ -0,0 +1,86 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -33089,10 +36076,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,213 @@ ++++ linux/fs/aufs/wkq.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,392 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -33110,7 +36098,7 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c + +/* + * workqueue for asynchronous/super-io operations -+ * todo: try new dredential scheme ++ * todo: try new credential scheme + */ + +#include @@ -33129,10 +36117,177 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c + au_wkq_func_t func; + void *args; + ++#ifdef CONFIG_LOCKDEP ++ int dont_check; ++ struct held_lock **hlock; ++#endif ++ + struct completion *comp; +}; + +/* ---------------------------------------------------------------------- */ ++/* ++ * Aufs passes some operations to the workqueue such as the internal copyup. ++ * This scheme looks rather unnatural for LOCKDEP debugging feature, since the ++ * job run by workqueue depends upon the locks acquired in the other task. ++ * Delegating a small operation to the workqueue, aufs passes its lockdep ++ * information too. And the job in the workqueue restores the info in order to ++ * pretend as if it acquired those locks. This is just to make LOCKDEP work ++ * correctly and expectedly. ++ */ ++ ++#ifndef CONFIG_LOCKDEP ++AuStubInt0(au_wkq_lockdep_alloc, struct au_wkinfo *wkinfo); ++AuStubVoid(au_wkq_lockdep_free, struct au_wkinfo *wkinfo); ++AuStubVoid(au_wkq_lockdep_pre, struct au_wkinfo *wkinfo); ++AuStubVoid(au_wkq_lockdep_post, struct au_wkinfo *wkinfo); ++AuStubVoid(au_wkq_lockdep_init, struct au_wkinfo *wkinfo); ++#else ++static void au_wkq_lockdep_init(struct au_wkinfo *wkinfo) ++{ ++ wkinfo->hlock = NULL; ++ wkinfo->dont_check = 0; ++} ++ ++/* ++ * 1: matched ++ * 0: unmatched ++ */ ++static int au_wkq_lockdep_test(struct lock_class_key *key, const char *name) ++{ ++ static DEFINE_SPINLOCK(spin); ++ static struct { ++ char *name; ++ struct lock_class_key *key; ++ } a[] = { ++ { .name = "&sbinfo->si_rwsem" }, ++ { .name = "&finfo->fi_rwsem" }, ++ { .name = "&dinfo->di_rwsem" }, ++ { .name = "&iinfo->ii_rwsem" } ++ }; ++ static int set; ++ int i; ++ ++ /* lockless read from 'set.' see below */ ++ if (set == ARRAY_SIZE(a)) { ++ for (i = 0; i < ARRAY_SIZE(a); i++) ++ if (a[i].key == key) ++ goto match; ++ goto unmatch; ++ } ++ ++ spin_lock(&spin); ++ if (set) ++ for (i = 0; i < ARRAY_SIZE(a); i++) ++ if (a[i].key == key) { ++ spin_unlock(&spin); ++ goto match; ++ } ++ for (i = 0; i < ARRAY_SIZE(a); i++) { ++ if (a[i].key) { ++ if (unlikely(a[i].key == key)) { /* rare but possible */ ++ spin_unlock(&spin); ++ goto match; ++ } else ++ continue; ++ } ++ if (strstr(a[i].name, name)) { ++ /* ++ * the order of these three lines is important for the ++ * lockless read above. ++ */ ++ a[i].key = key; ++ spin_unlock(&spin); ++ set++; ++ /* AuDbg("%d, %s\n", set, name); */ ++ goto match; ++ } ++ } ++ spin_unlock(&spin); ++ goto unmatch; ++ ++match: ++ return 1; ++unmatch: ++ return 0; ++} ++ ++static int au_wkq_lockdep_alloc(struct au_wkinfo *wkinfo) ++{ ++ int err, n; ++ struct task_struct *curr; ++ struct held_lock **hl, *held_locks, *p; ++ ++ err = 0; ++ curr = current; ++ wkinfo->dont_check = lockdep_recursing(curr); ++ if (wkinfo->dont_check) ++ goto out; ++ n = curr->lockdep_depth; ++ if (!n) ++ goto out; ++ ++ err = -ENOMEM; ++ wkinfo->hlock = kmalloc_array(n + 1, sizeof(*wkinfo->hlock), GFP_NOFS); ++ if (unlikely(!wkinfo->hlock)) ++ goto out; ++ ++ err = 0; ++#if 0 ++ if (0 && au_debug_test()) /* left for debugging */ ++ lockdep_print_held_locks(curr); ++#endif ++ held_locks = curr->held_locks; ++ hl = wkinfo->hlock; ++ while (n--) { ++ p = held_locks++; ++ if (au_wkq_lockdep_test(p->instance->key, p->instance->name)) ++ *hl++ = p; ++ } ++ *hl = NULL; ++ ++out: ++ return err; ++} ++ ++static void au_wkq_lockdep_free(struct au_wkinfo *wkinfo) ++{ ++ au_kfree_try_rcu(wkinfo->hlock); ++} ++ ++static void au_wkq_lockdep_pre(struct au_wkinfo *wkinfo) ++{ ++ struct held_lock *p, **hl = wkinfo->hlock; ++ int subclass; ++ ++ if (wkinfo->dont_check) ++ lockdep_off(); ++ if (!hl) ++ return; ++ while ((p = *hl++)) { /* assignment */ ++ subclass = lockdep_hlock_class(p)->subclass; ++ /* AuDbg("%s, %d\n", p->instance->name, subclass); */ ++ if (p->read) ++ rwsem_acquire_read(p->instance, subclass, 0, ++ /*p->acquire_ip*/_RET_IP_); ++ else ++ rwsem_acquire(p->instance, subclass, 0, ++ /*p->acquire_ip*/_RET_IP_); ++ } ++} ++ ++static void au_wkq_lockdep_post(struct au_wkinfo *wkinfo) ++{ ++ struct held_lock *p, **hl = wkinfo->hlock; ++ ++ if (wkinfo->dont_check) ++ lockdep_on(); ++ if (!hl) ++ return; ++ while ((p = *hl++)) /* assignment */ ++ rwsem_release(p->instance, 0, /*p->acquire_ip*/_RET_IP_); ++} ++#endif + +static void wkq_func(struct work_struct *wk) +{ @@ -33141,13 +36296,15 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c + AuDebugOn(!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)); + AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY); + ++ au_wkq_lockdep_pre(wkinfo); + wkinfo->func(wkinfo->args); ++ au_wkq_lockdep_post(wkinfo); + if (au_ftest_wkq(wkinfo->flags, WAIT)) + complete(wkinfo->comp); + else { + kobject_put(wkinfo->kobj); + module_put(THIS_MODULE); /* todo: ?? */ -+ kfree(wkinfo); ++ au_kfree_rcu(wkinfo); + } +} + @@ -33170,7 +36327,7 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c + +static void au_wkq_comp_free(struct completion *comp) +{ -+ kfree(comp); ++ au_kfree_rcu(comp); +} + +#else @@ -33197,7 +36354,8 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c + if (au_ftest_wkq(wkinfo->flags, NEST)) { + if (au_wkq_test()) { + AuWarn1("wkq from wkq, unless silly-rename on NFS," -+ " due to a dead dir by UDBA?\n"); ++ " due to a dead dir by UDBA," ++ " or async xino write?\n"); + AuDebugOn(au_ftest_wkq(wkinfo->flags, WAIT)); + } + } else @@ -33229,16 +36387,23 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c + }; + + err = au_wkq_comp_alloc(&wkinfo, &comp); ++ if (unlikely(err)) ++ goto out; ++ err = au_wkq_lockdep_alloc(&wkinfo); ++ if (unlikely(err)) ++ goto out_comp; + if (!err) { + au_wkq_run(&wkinfo); + /* no timeout, no interrupt */ + wait_for_completion(wkinfo.comp); -+ au_wkq_comp_free(comp); -+ destroy_work_on_stack(&wkinfo.wk); + } ++ au_wkq_lockdep_free(&wkinfo); + ++out_comp: ++ au_wkq_comp_free(comp); ++out: ++ destroy_work_on_stack(&wkinfo.wk); + return err; -+ +} + +/* @@ -33265,6 +36430,7 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c + wkinfo->func = func; + wkinfo->args = args; + wkinfo->comp = NULL; ++ au_wkq_lockdep_init(wkinfo); + kobject_get(wkinfo->kobj); + __module_get(THIS_MODULE); /* todo: ?? */ + @@ -33306,10 +36472,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,91 @@ ++++ linux/fs/aufs/wkq.h 2018-10-23 12:33:35.599375796 +0200 +@@ -0,0 +1,89 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -33335,6 +36502,8 @@ diff -urN /usr/share/empty/fs/aufs/wkq.h linux/fs/aufs/wkq.h + +#ifdef __KERNEL__ + ++#include ++ +struct super_block; + +/* ---------------------------------------------------------------------- */ @@ -33360,11 +36529,6 @@ diff -urN /usr/share/empty/fs/aufs/wkq.h linux/fs/aufs/wkq.h +#define au_fclr_wkq(flags, name) \ + do { (flags) &= ~AuWkq_##name; } while (0) + -+#ifndef CONFIG_AUFS_HNOTIFY -+#undef AuWkq_NEST -+#define AuWkq_NEST 0 -+#endif -+ +/* wkq.c */ +int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args); +int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, @@ -33401,10 +36565,11 @@ 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,344 @@ ++++ linux/fs/aufs/xattr.c 2018-12-27 13:19:17.711749485 +0100 +@@ -0,0 +1,356 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2014-2015 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 @@ -33424,6 +36589,8 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + * handling xattr functions + */ + ++#include ++#include +#include +#include "aufs.h" + @@ -33493,9 +36660,9 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + + /* unlock it temporary */ + h_idst = d_inode(h_dst); -+ mutex_unlock(&h_idst->i_mutex); ++ inode_unlock(h_idst); + err = vfsub_setxattr(h_dst, name, *buf, ssz, /*flags*/0); -+ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); ++ inode_lock_nested(h_idst, AuLsc_I_CHILD2); + if (unlikely(err)) { + if (verbose || au_debug_test()) + pr_err("%s, err %d\n", name, err); @@ -33518,9 +36685,9 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + /* there should not be the parent-child relationship between them */ + h_isrc = d_inode(h_src); + h_idst = d_inode(h_dst); -+ mutex_unlock(&h_idst->i_mutex); -+ mutex_lock_nested(&h_isrc->i_mutex, AuLsc_I_CHILD); -+ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); ++ inode_unlock(h_idst); ++ inode_lock_shared_nested(h_isrc, AuLsc_I_CHILD); ++ inode_lock_nested(h_idst, AuLsc_I_CHILD2); + unlocked = 0; + + /* some filesystems don't list POSIX ACL, for example tmpfs */ @@ -33545,7 +36712,7 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + goto out; + err = vfs_listxattr(h_src, p, ssz); + } -+ mutex_unlock(&h_isrc->i_mutex); ++ inode_unlock_shared(h_isrc); + unlocked = 1; + AuDbg("err %d, ssz %zd\n", err, ssz); + if (unlikely(err < 0)) @@ -33581,19 +36748,32 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + AuTraceErr(err); + } + -+ kfree(value); ++ au_kfree_try_rcu(value); + +out_free: -+ kfree(o); ++ au_kfree_try_rcu(o); +out: + if (!unlocked) -+ mutex_unlock(&h_isrc->i_mutex); ++ 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 @@ -33617,14 +36797,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)) @@ -33638,6 +36822,7 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + arg->u.list.list, arg->u.list.size); + break; + case AU_XATTR_GET: ++ AuDebugOn(d_is_negative(h_path.dentry)); + err = vfs_getxattr(h_path.dentry, + arg->u.get.name, arg->u.get.value, + arg->u.get.size); @@ -33645,9 +36830,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; @@ -33666,8 +36853,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, 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, @@ -33681,10 +36869,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, 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, @@ -33694,65 +36883,53 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c + }, + }; + -+ return au_srxattr(dentry, &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, &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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,1318 @@ ++++ linux/fs/aufs/xino.c 2018-12-27 13:19:17.715082917 +0100 +@@ -0,0 +1,1956 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright (C) 2005-2015 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 @@ -33770,194 +36947,265 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + +/* + * external inode number translation table and bitmap ++ * ++ * things to consider ++ * - the lifetime ++ * + au_xino object ++ * + XINO files (xino, xib, xigen) ++ * + dynamic debugfs entries (xiN) ++ * + static debugfs entries (xib, xigen) ++ * + static sysfs entry (xi_path) ++ * - several entry points to handle them. ++ * + mount(2) without xino option (default) ++ * + mount(2) with xino option ++ * + mount(2) with noxino option ++ * + umount(2) ++ * + remount with add/del branches ++ * + remount with xino/noxino options + */ + +#include +#include +#include "aufs.h" + -+/* todo: unnecessary to support mmap_sem since kernel-space? */ -+ssize_t xino_fread(vfs_readf_t func, struct file *file, void *kbuf, size_t size, -+ loff_t *pos) ++static aufs_bindex_t sbr_find_shared(struct super_block *sb, aufs_bindex_t btop, ++ aufs_bindex_t bbot, ++ struct super_block *h_sb) +{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ char __user *u; -+ } buf; ++ /* todo: try binary-search if the branches are many */ ++ for (; btop <= bbot; btop++) ++ if (h_sb == au_sbr_sb(sb, btop)) ++ return btop; ++ return -1; ++} + -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ do { -+ /* todo: signal_pending? */ -+ err = func(file, buf.u, size, pos); -+ } while (err == -EAGAIN || err == -EINTR); -+ set_fs(oldfs); ++/* ++ * find another branch who is on the same filesystem of the specified ++ * branch{@btgt}. search until @bbot. ++ */ ++static aufs_bindex_t is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, ++ aufs_bindex_t bbot) ++{ ++ aufs_bindex_t bindex; ++ struct super_block *tgt_sb; + -+#if 0 /* reserved for future use */ -+ if (err > 0) -+ fsnotify_access(file->f_path.dentry); -+#endif ++ tgt_sb = au_sbr_sb(sb, btgt); ++ bindex = sbr_find_shared(sb, /*btop*/0, btgt - 1, tgt_sb); ++ if (bindex < 0) ++ bindex = sbr_find_shared(sb, btgt + 1, bbot, tgt_sb); + -+ return err; ++ return bindex; +} + +/* ---------------------------------------------------------------------- */ + -+static ssize_t xino_fwrite_wkq(vfs_writef_t func, struct file *file, void *buf, -+ size_t size, loff_t *pos); ++/* ++ * stop unnecessary notify events at creating xino files ++ */ + -+static ssize_t do_xino_fwrite(vfs_writef_t func, struct file *file, void *kbuf, -+ size_t size, loff_t *pos) ++aufs_bindex_t au_xi_root(struct super_block *sb, struct dentry *dentry) +{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ const char __user *u; -+ } buf; -+ int i; -+ const int prevent_endless = 10; ++ aufs_bindex_t bfound, bindex, bbot; ++ struct dentry *parent; ++ struct au_branch *br; + -+ i = 0; -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ do { -+ err = func(file, buf.u, size, pos); -+ if (err == -EINTR -+ && !au_wkq_test() -+ && fatal_signal_pending(current)) { -+ set_fs(oldfs); -+ err = xino_fwrite_wkq(func, file, kbuf, size, pos); -+ BUG_ON(err == -EINTR); -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); ++ bfound = -1; ++ parent = dentry->d_parent; /* safe d_parent access */ ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_dentry(br) == parent) { ++ bfound = bindex; ++ break; + } -+ } while (i++ < prevent_endless -+ && (err == -EAGAIN || err == -EINTR)); -+ set_fs(oldfs); -+ -+#if 0 /* reserved for future use */ -+ if (err > 0) -+ fsnotify_modify(file->f_path.dentry); -+#endif ++ } + -+ return err; ++ AuDbg("bfound b%d\n", bfound); ++ return bfound; +} + -+struct do_xino_fwrite_args { -+ ssize_t *errp; -+ vfs_writef_t func; -+ struct file *file; -+ void *buf; -+ size_t size; -+ loff_t *pos; ++struct au_xino_lock_dir { ++ struct au_hinode *hdir; ++ struct dentry *parent; ++ struct inode *dir; +}; + -+static void call_do_xino_fwrite(void *args) ++static struct dentry *au_dget_parent_lock(struct dentry *dentry, ++ unsigned int lsc) +{ -+ struct do_xino_fwrite_args *a = args; -+ *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); ++ struct dentry *parent; ++ struct inode *dir; ++ ++ parent = dget_parent(dentry); ++ dir = d_inode(parent); ++ inode_lock_nested(dir, lsc); ++#if 0 /* it should not happen */ ++ spin_lock(&dentry->d_lock); ++ if (unlikely(dentry->d_parent != parent)) { ++ spin_unlock(&dentry->d_lock); ++ inode_unlock(dir); ++ dput(parent); ++ parent = NULL; ++ goto out; ++ } ++ spin_unlock(&dentry->d_lock); ++ ++out: ++#endif ++ return parent; +} + -+static ssize_t xino_fwrite_wkq(vfs_writef_t func, struct file *file, void *buf, -+ size_t size, loff_t *pos) ++static void au_xino_lock_dir(struct super_block *sb, struct path *xipath, ++ struct au_xino_lock_dir *ldir) +{ -+ ssize_t err; -+ int wkq_err; -+ struct do_xino_fwrite_args args = { -+ .errp = &err, -+ .func = func, -+ .file = file, -+ .buf = buf, -+ .size = size, -+ .pos = pos -+ }; ++ aufs_bindex_t bindex; + -+ /* -+ * it breaks RLIMIT_FSIZE and normal user's limit, -+ * users should care about quota and real 'filesystem full.' -+ */ -+ wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; ++ ldir->hdir = NULL; ++ bindex = au_xi_root(sb, xipath->dentry); ++ if (bindex >= 0) { ++ /* rw branch root */ ++ ldir->hdir = au_hi(d_inode(sb->s_root), bindex); ++ au_hn_inode_lock_nested(ldir->hdir, AuLsc_I_PARENT); ++ } else { ++ /* other */ ++ ldir->parent = au_dget_parent_lock(xipath->dentry, ++ AuLsc_I_PARENT); ++ ldir->dir = d_inode(ldir->parent); ++ } ++} + -+ return err; ++static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) ++{ ++ if (ldir->hdir) ++ au_hn_inode_unlock(ldir->hdir); ++ else { ++ inode_unlock(ldir->dir); ++ dput(ldir->parent); ++ } +} + -+ssize_t xino_fwrite(vfs_writef_t func, struct file *file, void *buf, -+ size_t size, loff_t *pos) ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create and set a new xino file ++ */ ++struct file *au_xino_create(struct super_block *sb, char *fpath, int silent) +{ -+ ssize_t err; ++ struct file *file; ++ struct dentry *h_parent, *d; ++ struct inode *h_dir, *inode; ++ int err; + -+ if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { -+ lockdep_off(); -+ err = do_xino_fwrite(func, file, buf, size, pos); -+ lockdep_on(); -+ } else -+ err = xino_fwrite_wkq(func, file, buf, size, pos); ++ /* ++ * at mount-time, and the xino file is the default path, ++ * hnotify is disabled so we have no notify events to ignore. ++ * when a user specified the xino, we cannot get au_hdir to be ignored. ++ */ ++ file = vfsub_filp_open(fpath, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE ++ /* | __FMODE_NONOTIFY */, ++ 0666); ++ if (IS_ERR(file)) { ++ if (!silent) ++ pr_err("open %s(%ld)\n", fpath, PTR_ERR(file)); ++ return file; ++ } + -+ return err; -+} ++ /* keep file count */ ++ err = 0; ++ d = file->f_path.dentry; ++ h_parent = au_dget_parent_lock(d, AuLsc_I_PARENT); ++ /* mnt_want_write() is unnecessary here */ ++ h_dir = d_inode(h_parent); ++ inode = file_inode(file); ++ /* no delegation since it is just created */ ++ if (inode->i_nlink) ++ err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, ++ /*force*/0); ++ inode_unlock(h_dir); ++ dput(h_parent); ++ if (unlikely(err)) { ++ if (!silent) ++ pr_err("unlink %s(%d)\n", fpath, err); ++ goto out; ++ } + -+/* ---------------------------------------------------------------------- */ ++ err = -EINVAL; ++ if (unlikely(sb == d->d_sb)) { ++ if (!silent) ++ pr_err("%s must be outside\n", fpath); ++ goto out; ++ } ++ if (unlikely(au_test_fs_bad_xino(d->d_sb))) { ++ if (!silent) ++ pr_err("xino doesn't support %s(%s)\n", ++ fpath, au_sbtype(d->d_sb)); ++ goto out; ++ } ++ return file; /* success */ ++ ++out: ++ fput(file); ++ file = ERR_PTR(err); ++ return file; ++} + +/* -+ * create a new xinofile at the same place/path as @base_file. ++ * create a new xinofile at the same place/path as @base. + */ -+struct file *au_xino_create2(struct file *base_file, struct file *copy_src) ++struct file *au_xino_create2(struct super_block *sb, struct path *base, ++ struct file *copy_src) +{ + struct file *file; -+ struct dentry *base, *parent; ++ struct dentry *dentry, *parent; + struct inode *dir, *delegated; + struct qstr *name; + struct path path; -+ int err; ++ int err, do_unlock; ++ struct au_xino_lock_dir ldir; + -+ base = base_file->f_path.dentry; -+ parent = base->d_parent; /* dir inode is locked */ ++ do_unlock = 1; ++ au_xino_lock_dir(sb, base, &ldir); ++ dentry = base->dentry; ++ parent = dentry->d_parent; /* dir inode is locked */ + dir = d_inode(parent); + IMustLock(dir); + -+ file = ERR_PTR(-EINVAL); -+ name = &base->d_name; ++ name = &dentry->d_name; + path.dentry = vfsub_lookup_one_len(name->name, parent, name->len); + if (IS_ERR(path.dentry)) { + file = (void *)path.dentry; -+ pr_err("%pd lookup err %ld\n", -+ base, PTR_ERR(path.dentry)); ++ pr_err("%pd lookup err %ld\n", dentry, PTR_ERR(path.dentry)); + goto out; + } + + /* no need to mnt_want_write() since we call dentry_open() later */ -+ err = vfs_create(dir, path.dentry, S_IRUGO | S_IWUGO, NULL); ++ err = vfs_create(dir, path.dentry, 0666, NULL); + if (unlikely(err)) { + file = ERR_PTR(err); -+ pr_err("%pd create err %d\n", base, err); ++ pr_err("%pd create err %d\n", dentry, err); + goto out_dput; + } + -+ path.mnt = base_file->f_path.mnt; ++ path.mnt = base->mnt; + file = vfsub_dentry_open(&path, + O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE + /* | __FMODE_NONOTIFY */); + if (IS_ERR(file)) { -+ pr_err("%pd open err %ld\n", base, PTR_ERR(file)); ++ pr_err("%pd open err %ld\n", dentry, PTR_ERR(file)); + goto out_dput; + } + + delegated = NULL; + err = vfsub_unlink(dir, &file->f_path, &delegated, /*force*/0); ++ au_xino_unlock_dir(&ldir); ++ do_unlock = 0; + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + if (unlikely(err)) { -+ pr_err("%pd unlink err %d\n", base, err); ++ pr_err("%pd unlink err %d\n", dentry, err); + goto out_fput; + } + @@ -33965,7 +37213,7 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + /* no one can touch copy_src xino */ + err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src)); + if (unlikely(err)) { -+ pr_err("%pd copy err %d\n", base, err); ++ pr_err("%pd copy err %d\n", dentry, err); + goto out_fput; + } + } @@ -33977,121 +37225,183 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c +out_dput: + dput(path.dentry); +out: ++ if (do_unlock) ++ au_xino_unlock_dir(&ldir); + return file; +} + -+struct au_xino_lock_dir { -+ struct au_hinode *hdir; -+ struct dentry *parent; -+ struct mutex *mtx; -+}; ++struct file *au_xino_file1(struct au_xino *xi) ++{ ++ struct file *file; ++ unsigned int u, nfile; + -+static void au_xino_lock_dir(struct super_block *sb, struct file *xino, -+ struct au_xino_lock_dir *ldir) ++ file = NULL; ++ nfile = xi->xi_nfile; ++ for (u = 0; u < nfile; u++) { ++ file = xi->xi_file[u]; ++ if (file) ++ break; ++ } ++ ++ return file; ++} ++ ++static int au_xino_file_set(struct au_xino *xi, int idx, struct file *file) +{ -+ aufs_bindex_t brid, bindex; ++ int err; ++ struct file *f; ++ void *p; + -+ ldir->hdir = NULL; -+ bindex = -1; -+ brid = au_xino_brid(sb); -+ if (brid >= 0) -+ bindex = au_br_index(sb, brid); -+ if (bindex >= 0) { -+ ldir->hdir = au_hi(d_inode(sb->s_root), bindex); -+ au_hn_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT); ++ if (file) ++ get_file(file); ++ ++ err = 0; ++ f = NULL; ++ if (idx < xi->xi_nfile) { ++ f = xi->xi_file[idx]; ++ if (f) ++ fput(f); + } else { -+ ldir->parent = dget_parent(xino->f_path.dentry); -+ ldir->mtx = &d_inode(ldir->parent)->i_mutex; -+ mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT); ++ p = au_kzrealloc(xi->xi_file, ++ sizeof(*xi->xi_file) * xi->xi_nfile, ++ sizeof(*xi->xi_file) * (idx + 1), ++ GFP_NOFS, /*may_shrink*/0); ++ if (p) { ++ MtxMustLock(&xi->xi_mtx); ++ xi->xi_file = p; ++ xi->xi_nfile = idx + 1; ++ } else { ++ err = -ENOMEM; ++ if (file) ++ fput(file); ++ goto out; ++ } + } ++ xi->xi_file[idx] = file; ++ ++out: ++ return err; +} + -+static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) ++/* ++ * if @xinew->xi is not set, then create new xigen file. ++ */ ++struct file *au_xi_new(struct super_block *sb, struct au_xi_new *xinew) +{ -+ if (ldir->hdir) -+ au_hn_imtx_unlock(ldir->hdir); ++ struct file *file; ++ int err; ++ ++ SiMustAnyLock(sb); ++ ++ file = au_xino_create2(sb, xinew->base, xinew->copy_src); ++ if (IS_ERR(file)) { ++ err = PTR_ERR(file); ++ pr_err("%s[%d], err %d\n", ++ xinew->xi ? "xino" : "xigen", ++ xinew->idx, err); ++ goto out; ++ } ++ ++ if (xinew->xi) ++ err = au_xino_file_set(xinew->xi, xinew->idx, file); + else { -+ mutex_unlock(ldir->mtx); -+ dput(ldir->parent); ++ BUG(); ++ /* todo: make xigen file an array */ ++ /* err = au_xigen_file_set(sb, xinew->idx, file); */ + } ++ fput(file); ++ if (unlikely(err)) ++ file = ERR_PTR(err); ++ ++out: ++ return file; +} + +/* ---------------------------------------------------------------------- */ + -+/* trucate xino files asynchronously */ -+ -+int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) ++/* ++ * truncate xino files ++ */ ++static int au_xino_do_trunc(struct super_block *sb, aufs_bindex_t bindex, ++ int idx, struct kstatfs *st) +{ + int err; -+ unsigned long jiffy; + blkcnt_t blocks; -+ aufs_bindex_t bi, bend; -+ struct kstatfs *st; -+ struct au_branch *br; -+ struct file *new_xino, *file; -+ struct super_block *h_sb; -+ struct au_xino_lock_dir ldir; -+ -+ err = -ENOMEM; -+ st = kmalloc(sizeof(*st), GFP_NOFS); -+ if (unlikely(!st)) -+ goto out; ++ struct file *file, *new_xino; ++ struct au_xi_new xinew = { ++ .idx = idx ++ }; + -+ err = -EINVAL; -+ bend = au_sbend(sb); -+ if (unlikely(bindex < 0 || bend < bindex)) -+ goto out_st; -+ br = au_sbr(sb, bindex); -+ file = br->br_xino.xi_file; ++ err = 0; ++ xinew.xi = au_sbr(sb, bindex)->br_xino; ++ file = au_xino_file(xinew.xi, idx); + if (!file) -+ goto out_st; ++ goto out; + -+ err = vfs_statfs(&file->f_path, st); -+ if (unlikely(err)) ++ xinew.base = &file->f_path; ++ err = vfs_statfs(xinew.base, st); ++ if (unlikely(err)) { + AuErr1("statfs err %d, ignored\n", err); -+ jiffy = jiffies; -+ blocks = file_inode(file)->i_blocks; -+ pr_info("begin truncating xino(b%d), ib%llu, %llu/%llu free blks\n", -+ bindex, (u64)blocks, st->f_bfree, st->f_blocks); -+ -+ au_xino_lock_dir(sb, file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ new_xino = au_xino_create2(file, file); -+ au_xino_unlock_dir(&ldir); -+ err = PTR_ERR(new_xino); -+ if (IS_ERR(new_xino)) { -+ pr_err("err %d, ignored\n", err); -+ goto out_st; ++ err = 0; ++ goto out; + } -+ err = 0; -+ fput(file); -+ br->br_xino.xi_file = new_xino; + -+ h_sb = au_br_sb(br); -+ for (bi = 0; bi <= bend; bi++) { -+ if (unlikely(bi == bindex)) -+ continue; -+ br = au_sbr(sb, bi); -+ if (au_br_sb(br) != h_sb) -+ continue; ++ blocks = file_inode(file)->i_blocks; ++ pr_info("begin truncating xino(b%d-%d), ib%llu, %llu/%llu free blks\n", ++ bindex, idx, (u64)blocks, st->f_bfree, st->f_blocks); + -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = new_xino; -+ get_file(new_xino); ++ xinew.copy_src = file; ++ new_xino = au_xi_new(sb, &xinew); ++ if (IS_ERR(new_xino)) { ++ err = PTR_ERR(new_xino); ++ pr_err("xino(b%d-%d), err %d, ignored\n", bindex, idx, err); ++ goto out; + } + + err = vfs_statfs(&new_xino->f_path, st); -+ if (!err) { -+ pr_info("end truncating xino(b%d), ib%llu, %llu/%llu free blks\n", -+ bindex, (u64)file_inode(new_xino)->i_blocks, ++ if (!err) ++ pr_info("end truncating xino(b%d-%d), ib%llu, %llu/%llu free blks\n", ++ bindex, idx, (u64)file_inode(new_xino)->i_blocks, + st->f_bfree, st->f_blocks); -+ if (file_inode(new_xino)->i_blocks < blocks) -+ au_sbi(sb)->si_xino_jiffy = jiffy; -+ } else ++ else { + AuErr1("statfs err %d, ignored\n", err); ++ err = 0; ++ } ++ ++out: ++ return err; ++} ++ ++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex, int idx_begin) ++{ ++ int err, i; ++ unsigned long jiffy; ++ aufs_bindex_t bbot; ++ struct kstatfs *st; ++ struct au_branch *br; ++ struct au_xino *xi; ++ ++ err = -ENOMEM; ++ st = kmalloc(sizeof(*st), GFP_NOFS); ++ if (unlikely(!st)) ++ goto out; ++ ++ err = -EINVAL; ++ bbot = au_sbbot(sb); ++ if (unlikely(bindex < 0 || bbot < bindex)) ++ goto out_st; ++ ++ err = 0; ++ jiffy = jiffies; ++ br = au_sbr(sb, bindex); ++ xi = br->br_xino; ++ for (i = idx_begin; !err && i < xi->xi_nfile; i++) ++ err = au_xino_do_trunc(sb, bindex, i, st); ++ if (!err) ++ au_sbi(sb)->si_xino_jiffy = jiffy; + +out_st: -+ kfree(st); ++ au_kfree_rcu(st); +out: + return err; +} @@ -34099,6 +37409,7 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c +struct xino_do_trunc_args { + struct super_block *sb; + struct au_branch *br; ++ int idx; +}; + +static void xino_do_trunc(void *_args) @@ -34107,101 +37418,318 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + struct super_block *sb; + struct au_branch *br; + struct inode *dir; -+ int err; ++ int err, idx; + aufs_bindex_t bindex; + + err = 0; + sb = args->sb; + dir = d_inode(sb->s_root); + br = args->br; ++ idx = args->idx; + + si_noflush_write_lock(sb); + ii_read_lock_parent(dir); + bindex = au_br_index(sb, br->br_id); -+ err = au_xino_trunc(sb, bindex); ++ err = au_xino_trunc(sb, bindex, idx); + ii_read_unlock(dir); + if (unlikely(err)) + pr_warn("err b%d, (%d)\n", bindex, err); -+ atomic_dec(&br->br_xino_running); -+ atomic_dec(&br->br_count); ++ atomic_dec(&br->br_xino->xi_truncating); ++ au_lcnt_dec(&br->br_count); + si_write_unlock(sb); + au_nwt_done(&au_sbi(sb)->si_nowait); -+ kfree(args); ++ au_kfree_rcu(args); +} + ++/* ++ * returns the index in the xi_file array whose corresponding file is necessary ++ * to truncate, or -1 which means no need to truncate. ++ */ +static int xino_trunc_test(struct super_block *sb, struct au_branch *br) +{ + int err; ++ unsigned int u; + struct kstatfs st; + struct au_sbinfo *sbinfo; ++ struct au_xino *xi; ++ struct file *file; + + /* todo: si_xino_expire and the ratio should be customizable */ + sbinfo = au_sbi(sb); + if (time_before(jiffies, + sbinfo->si_xino_jiffy + sbinfo->si_xino_expire)) -+ return 0; ++ return -1; + + /* truncation border */ -+ err = vfs_statfs(&br->br_xino.xi_file->f_path, &st); -+ if (unlikely(err)) { -+ AuErr1("statfs err %d, ignored\n", err); -+ return 0; ++ xi = br->br_xino; ++ for (u = 0; u < xi->xi_nfile; u++) { ++ file = au_xino_file(xi, u); ++ if (!file) ++ continue; ++ ++ err = vfs_statfs(&file->f_path, &st); ++ if (unlikely(err)) { ++ AuErr1("statfs err %d, ignored\n", err); ++ return -1; ++ } ++ if (div64_u64(st.f_bfree * 100, st.f_blocks) ++ >= AUFS_XINO_DEF_TRUNC) ++ return u; + } -+ if (div64_u64(st.f_bfree * 100, st.f_blocks) >= AUFS_XINO_DEF_TRUNC) -+ return 0; + -+ return 1; ++ return -1; +} + +static void xino_try_trunc(struct super_block *sb, struct au_branch *br) +{ ++ int idx; + struct xino_do_trunc_args *args; + int wkq_err; + -+ if (!xino_trunc_test(sb, br)) ++ idx = xino_trunc_test(sb, br); ++ if (idx < 0) + return; + -+ if (atomic_inc_return(&br->br_xino_running) > 1) ++ if (atomic_inc_return(&br->br_xino->xi_truncating) > 1) ++ goto out; ++ ++ /* lock and kfree() will be called in trunc_xino() */ ++ args = kmalloc(sizeof(*args), GFP_NOFS); ++ if (unlikely(!args)) { ++ AuErr1("no memory\n"); ++ goto out; ++ } ++ ++ au_lcnt_inc(&br->br_count); ++ args->sb = sb; ++ args->br = br; ++ args->idx = idx; ++ wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); ++ if (!wkq_err) ++ return; /* success */ ++ ++ pr_err("wkq %d\n", wkq_err); ++ au_lcnt_dec(&br->br_count); ++ au_kfree_rcu(args); ++ ++out: ++ atomic_dec(&br->br_xino->xi_truncating); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_xi_calc { ++ int idx; ++ loff_t pos; ++}; ++ ++static void au_xi_calc(struct super_block *sb, ino_t h_ino, ++ struct au_xi_calc *calc) ++{ ++ loff_t maxent; ++ ++ maxent = au_xi_maxent(sb); ++ calc->idx = div64_u64_rem(h_ino, maxent, &calc->pos); ++ calc->pos *= sizeof(ino_t); ++} ++ ++static int au_xino_do_new_async(struct super_block *sb, struct au_branch *br, ++ struct au_xi_calc *calc) ++{ ++ int err; ++ struct file *file; ++ struct au_xino *xi = br->br_xino; ++ struct au_xi_new xinew = { ++ .xi = xi ++ }; ++ ++ SiMustAnyLock(sb); ++ ++ err = 0; ++ if (!xi) ++ goto out; ++ ++ mutex_lock(&xi->xi_mtx); ++ file = au_xino_file(xi, calc->idx); ++ if (file) ++ goto out_mtx; ++ ++ file = au_xino_file(xi, /*idx*/-1); ++ AuDebugOn(!file); ++ xinew.idx = calc->idx; ++ xinew.base = &file->f_path; ++ /* xinew.copy_src = NULL; */ ++ file = au_xi_new(sb, &xinew); ++ if (IS_ERR(file)) ++ err = PTR_ERR(file); ++ ++out_mtx: ++ mutex_unlock(&xi->xi_mtx); ++out: ++ return err; ++} ++ ++struct au_xino_do_new_async_args { ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_xi_calc calc; ++ ino_t ino; ++}; ++ ++struct au_xi_writing { ++ struct hlist_bl_node node; ++ ino_t h_ino, ino; ++}; ++ ++static int au_xino_do_write(vfs_writef_t write, struct file *file, ++ struct au_xi_calc *calc, ino_t ino); ++ ++static void au_xino_call_do_new_async(void *args) ++{ ++ struct au_xino_do_new_async_args *a = args; ++ struct au_branch *br; ++ struct super_block *sb; ++ struct au_sbinfo *sbi; ++ struct inode *root; ++ struct file *file; ++ struct au_xi_writing *del, *p; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; ++ int err; ++ ++ br = a->br; ++ sb = a->sb; ++ sbi = au_sbi(sb); ++ si_noflush_read_lock(sb); ++ root = d_inode(sb->s_root); ++ ii_read_lock_child(root); ++ err = au_xino_do_new_async(sb, br, &a->calc); ++ if (unlikely(err)) { ++ AuIOErr("err %d\n", err); ++ goto out; ++ } ++ ++ file = au_xino_file(br->br_xino, a->calc.idx); ++ AuDebugOn(!file); ++ err = au_xino_do_write(sbi->si_xwrite, file, &a->calc, a->ino); ++ if (unlikely(err)) { ++ AuIOErr("err %d\n", err); ++ goto out; ++ } ++ ++ del = NULL; ++ hbl = &br->br_xino->xi_writing; ++ hlist_bl_lock(hbl); ++ au_hbl_for_each(pos, hbl) { ++ p = container_of(pos, struct au_xi_writing, node); ++ if (p->ino == a->ino) { ++ del = p; ++ hlist_bl_del(&p->node); ++ break; ++ } ++ } ++ hlist_bl_unlock(hbl); ++ au_kfree_rcu(del); ++ ++out: ++ au_lcnt_dec(&br->br_count); ++ ii_read_unlock(root); ++ si_read_unlock(sb); ++ au_nwt_done(&sbi->si_nowait); ++ au_kfree_rcu(a); ++} ++ ++/* ++ * create a new xino file asynchronously ++ */ ++static int au_xino_new_async(struct super_block *sb, struct au_branch *br, ++ struct au_xi_calc *calc, ino_t ino) ++{ ++ int err; ++ struct au_xino_do_new_async_args *arg; ++ ++ err = -ENOMEM; ++ arg = kmalloc(sizeof(*arg), GFP_NOFS); ++ if (unlikely(!arg)) + goto out; + -+ /* lock and kfree() will be called in trunc_xino() */ -+ args = kmalloc(sizeof(*args), GFP_NOFS); -+ if (unlikely(!args)) { -+ AuErr1("no memory\n"); -+ goto out_args; -+ } ++ arg->sb = sb; ++ arg->br = br; ++ arg->calc = *calc; ++ arg->ino = ino; ++ au_lcnt_inc(&br->br_count); ++ err = au_wkq_nowait(au_xino_call_do_new_async, arg, sb, AuWkq_NEST); ++ if (unlikely(err)) { ++ pr_err("wkq %d\n", err); ++ au_lcnt_dec(&br->br_count); ++ au_kfree_rcu(arg); ++ } ++ ++out: ++ return err; ++} ++ ++/* ++ * read @ino from xinofile for the specified branch{@sb, @bindex} ++ * at the position of @h_ino. ++ */ ++int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t *ino) ++{ ++ int err; ++ ssize_t sz; ++ struct au_xi_calc calc; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ struct au_xino *xi; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos; ++ struct au_xi_writing *p; ++ ++ *ino = 0; ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return 0; /* no xino */ + -+ atomic_inc(&br->br_count); -+ args->sb = sb; -+ args->br = br; -+ wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); -+ if (!wkq_err) -+ return; /* success */ ++ err = 0; ++ au_xi_calc(sb, h_ino, &calc); ++ xi = au_sbr(sb, bindex)->br_xino; ++ file = au_xino_file(xi, calc.idx); ++ if (!file) { ++ hbl = &xi->xi_writing; ++ hlist_bl_lock(hbl); ++ au_hbl_for_each(pos, hbl) { ++ p = container_of(pos, struct au_xi_writing, node); ++ if (p->h_ino == h_ino) { ++ AuDbg("hi%llu, i%llu, found\n", ++ (u64)p->h_ino, (u64)p->ino); ++ *ino = p->ino; ++ break; ++ } ++ } ++ hlist_bl_unlock(hbl); ++ return 0; ++ } else if (vfsub_f_size_read(file) < calc.pos + sizeof(*ino)) ++ return 0; /* no xino */ + -+ pr_err("wkq %d\n", wkq_err); -+ atomic_dec(&br->br_count); ++ sbinfo = au_sbi(sb); ++ sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &calc.pos); ++ if (sz == sizeof(*ino)) ++ return 0; /* success */ + -+out_args: -+ kfree(args); -+out: -+ atomic_dec(&br->br_xino_running); ++ err = sz; ++ if (unlikely(sz >= 0)) { ++ err = -EIO; ++ AuIOErr("xino read error (%zd)\n", sz); ++ } ++ return err; +} + -+/* ---------------------------------------------------------------------- */ -+ +static int au_xino_do_write(vfs_writef_t write, struct file *file, -+ ino_t h_ino, ino_t ino) ++ struct au_xi_calc *calc, ino_t ino) +{ -+ loff_t pos; + ssize_t sz; + -+ pos = h_ino; -+ if (unlikely(au_loff_max / sizeof(ino) - 1 < pos)) { -+ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); -+ return -EFBIG; -+ } -+ pos *= sizeof(ino); -+ sz = xino_fwrite(write, file, &ino, sizeof(ino), &pos); ++ sz = xino_fwrite(write, file, &ino, sizeof(ino), &calc->pos); + if (sz == sizeof(ino)) + return 0; /* success */ + @@ -34221,34 +37749,234 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c +{ + int err; + unsigned int mnt_flags; ++ struct au_xi_calc calc; ++ struct file *file; + struct au_branch *br; ++ struct au_xino *xi; ++ struct au_xi_writing *p; + -+ BUILD_BUG_ON(sizeof(long long) != sizeof(au_loff_max) -+ || ((loff_t)-1) > 0); + SiMustAnyLock(sb); + + mnt_flags = au_mntflags(sb); + if (!au_opt_test(mnt_flags, XINO)) + return 0; + ++ au_xi_calc(sb, h_ino, &calc); + br = au_sbr(sb, bindex); -+ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, -+ h_ino, ino); ++ xi = br->br_xino; ++ file = au_xino_file(xi, calc.idx); ++ if (!file) { ++ /* store the inum pair into the list */ ++ p = kmalloc(sizeof(*p), GFP_NOFS | __GFP_NOFAIL); ++ p->h_ino = h_ino; ++ p->ino = ino; ++ au_hbl_add(&p->node, &xi->xi_writing); ++ ++ /* create and write a new xino file asynchronously */ ++ err = au_xino_new_async(sb, br, &calc, ino); ++ if (!err) ++ return 0; /* success */ ++ goto out; ++ } ++ ++ err = au_xino_do_write(au_sbi(sb)->si_xwrite, file, &calc, ino); + if (!err) { ++ br = au_sbr(sb, bindex); + if (au_opt_test(mnt_flags, TRUNC_XINO) + && au_test_fs_trunc_xino(au_br_sb(br))) + xino_try_trunc(sb, br); + return 0; /* success */ + } + ++out: + AuIOErr("write failed (%d)\n", err); + return -EIO; +} + -+/* ---------------------------------------------------------------------- */ ++static ssize_t xino_fread_wkq(vfs_readf_t func, struct file *file, void *buf, ++ size_t size, loff_t *pos); ++ ++/* todo: unnecessary to support mmap_sem since kernel-space? */ ++ssize_t xino_fread(vfs_readf_t func, struct file *file, void *kbuf, size_t size, ++ loff_t *pos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ char __user *u; ++ } buf; ++ int i; ++ const int prevent_endless = 10; ++ ++ i = 0; ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ do { ++ err = func(file, buf.u, size, pos); ++ if (err == -EINTR ++ && !au_wkq_test() ++ && fatal_signal_pending(current)) { ++ set_fs(oldfs); ++ err = xino_fread_wkq(func, file, kbuf, size, pos); ++ BUG_ON(err == -EINTR); ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ } ++ } while (i++ < prevent_endless ++ && (err == -EAGAIN || err == -EINTR)); ++ set_fs(oldfs); ++ ++#if 0 /* reserved for future use */ ++ if (err > 0) ++ fsnotify_access(file->f_path.dentry); ++#endif ++ ++ return err; ++} ++ ++struct xino_fread_args { ++ ssize_t *errp; ++ vfs_readf_t func; ++ struct file *file; ++ void *buf; ++ size_t size; ++ loff_t *pos; ++}; ++ ++static void call_xino_fread(void *args) ++{ ++ struct xino_fread_args *a = args; ++ *a->errp = xino_fread(a->func, a->file, a->buf, a->size, a->pos); ++} ++ ++static ssize_t xino_fread_wkq(vfs_readf_t func, struct file *file, void *buf, ++ size_t size, loff_t *pos) ++{ ++ ssize_t err; ++ int wkq_err; ++ struct xino_fread_args args = { ++ .errp = &err, ++ .func = func, ++ .file = file, ++ .buf = buf, ++ .size = size, ++ .pos = pos ++ }; ++ ++ wkq_err = au_wkq_wait(call_xino_fread, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ ++ return err; ++} ++ ++static ssize_t xino_fwrite_wkq(vfs_writef_t func, struct file *file, void *buf, ++ size_t size, loff_t *pos); ++ ++static ssize_t do_xino_fwrite(vfs_writef_t func, struct file *file, void *kbuf, ++ size_t size, loff_t *pos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ const char __user *u; ++ } buf; ++ int i; ++ const int prevent_endless = 10; ++ ++ i = 0; ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ do { ++ err = func(file, buf.u, size, pos); ++ if (err == -EINTR ++ && !au_wkq_test() ++ && fatal_signal_pending(current)) { ++ set_fs(oldfs); ++ err = xino_fwrite_wkq(func, file, kbuf, size, pos); ++ BUG_ON(err == -EINTR); ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ } ++ } while (i++ < prevent_endless ++ && (err == -EAGAIN || err == -EINTR)); ++ set_fs(oldfs); ++ ++#if 0 /* reserved for future use */ ++ if (err > 0) ++ fsnotify_modify(file->f_path.dentry); ++#endif ++ ++ return err; ++} ++ ++struct do_xino_fwrite_args { ++ ssize_t *errp; ++ vfs_writef_t func; ++ struct file *file; ++ void *buf; ++ size_t size; ++ loff_t *pos; ++}; ++ ++static void call_do_xino_fwrite(void *args) ++{ ++ struct do_xino_fwrite_args *a = args; ++ *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); ++} ++ ++static ssize_t xino_fwrite_wkq(vfs_writef_t func, struct file *file, void *buf, ++ size_t size, loff_t *pos) ++{ ++ ssize_t err; ++ int wkq_err; ++ struct do_xino_fwrite_args args = { ++ .errp = &err, ++ .func = func, ++ .file = file, ++ .buf = buf, ++ .size = size, ++ .pos = pos ++ }; ++ ++ /* ++ * it breaks RLIMIT_FSIZE and normal user's limit, ++ * users should care about quota and real 'filesystem full.' ++ */ ++ wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ ++ return err; ++} ++ ++ssize_t xino_fwrite(vfs_writef_t func, struct file *file, void *buf, ++ size_t size, loff_t *pos) ++{ ++ ssize_t err; ++ ++ if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { ++ lockdep_off(); ++ err = do_xino_fwrite(func, file, buf, size, pos); ++ lockdep_on(); ++ } else { ++ lockdep_off(); ++ err = xino_fwrite_wkq(func, file, buf, size, pos); ++ lockdep_on(); ++ } ++ ++ return err; ++} + -+/* aufs inode number bitmap */ ++/* ---------------------------------------------------------------------- */ + ++/* ++ * inode number bitmap ++ */ +static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; +static ino_t xib_calc_ino(unsigned long pindex, int bit) +{ @@ -34313,8 +38041,6 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + return err; +} + -+/* ---------------------------------------------------------------------- */ -+ +static void au_xib_clear_bit(struct inode *inode) +{ + int err, bit; @@ -34337,311 +38063,11 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + mutex_unlock(&sbinfo->si_xib_mtx); +} + -+/* for s_op->delete_inode() */ -+void au_xino_delete_inode(struct inode *inode, const int unlinked) -+{ -+ int err; -+ unsigned int mnt_flags; -+ aufs_bindex_t bindex, bend, bi; -+ unsigned char try_trunc; -+ struct au_iinfo *iinfo; -+ struct super_block *sb; -+ struct au_hinode *hi; -+ struct inode *h_inode; -+ struct au_branch *br; -+ vfs_writef_t xwrite; -+ -+ sb = inode->i_sb; -+ mnt_flags = au_mntflags(sb); -+ if (!au_opt_test(mnt_flags, XINO) -+ || inode->i_ino == AUFS_ROOT_INO) -+ return; -+ -+ if (unlinked) { -+ au_xigen_inc(inode); -+ au_xib_clear_bit(inode); -+ } -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ -+ bindex = iinfo->ii_bstart; -+ if (bindex < 0) -+ return; -+ -+ xwrite = au_sbi(sb)->si_xwrite; -+ try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); -+ hi = iinfo->ii_hinode + bindex; -+ bend = iinfo->ii_bend; -+ for (; bindex <= bend; bindex++, hi++) { -+ h_inode = hi->hi_inode; -+ if (!h_inode -+ || (!unlinked && h_inode->i_nlink)) -+ continue; -+ -+ /* inode may not be revalidated */ -+ bi = au_br_index(sb, hi->hi_id); -+ if (bi < 0) -+ continue; -+ -+ br = au_sbr(sb, bi); -+ err = au_xino_do_write(xwrite, br->br_xino.xi_file, -+ h_inode->i_ino, /*ino*/0); -+ if (!err && try_trunc -+ && au_test_fs_trunc_xino(au_br_sb(br))) -+ xino_try_trunc(sb, br); -+ } -+} -+ -+/* get an unused inode number from bitmap */ -+ino_t au_xino_new_ino(struct super_block *sb) -+{ -+ ino_t ino; -+ unsigned long *p, pindex, ul, pend; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ int free_bit, err; -+ -+ if (!au_opt_test(au_mntflags(sb), XINO)) -+ return iunique(sb, AUFS_FIRST_INO); -+ -+ sbinfo = au_sbi(sb); -+ mutex_lock(&sbinfo->si_xib_mtx); -+ p = sbinfo->si_xib_buf; -+ free_bit = sbinfo->si_xib_next_bit; -+ if (free_bit < page_bits && !test_bit(free_bit, p)) -+ goto out; /* success */ -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ -+ pindex = sbinfo->si_xib_last_pindex; -+ for (ul = pindex - 1; ul < ULONG_MAX; ul--) { -+ err = xib_pindex(sb, ul); -+ if (unlikely(err)) -+ goto out_err; -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ } -+ -+ file = sbinfo->si_xib; -+ pend = vfsub_f_size_read(file) / PAGE_SIZE; -+ for (ul = pindex + 1; ul <= pend; ul++) { -+ err = xib_pindex(sb, ul); -+ if (unlikely(err)) -+ goto out_err; -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ } -+ BUG(); -+ -+out: -+ set_bit(free_bit, p); -+ sbinfo->si_xib_next_bit = free_bit + 1; -+ pindex = sbinfo->si_xib_last_pindex; -+ mutex_unlock(&sbinfo->si_xib_mtx); -+ ino = xib_calc_ino(pindex, free_bit); -+ AuDbg("i%lu\n", (unsigned long)ino); -+ return ino; -+out_err: -+ mutex_unlock(&sbinfo->si_xib_mtx); -+ AuDbg("i0\n"); -+ return 0; -+} -+ -+/* -+ * read @ino from xinofile for the specified branch{@sb, @bindex} -+ * at the position of @h_ino. -+ * if @ino does not exist and @do_new is true, get new one. -+ */ -+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t *ino) -+{ -+ int err; -+ ssize_t sz; -+ loff_t pos; -+ struct file *file; -+ struct au_sbinfo *sbinfo; -+ -+ *ino = 0; -+ if (!au_opt_test(au_mntflags(sb), XINO)) -+ return 0; /* no xino */ -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ pos = h_ino; -+ if (unlikely(au_loff_max / sizeof(*ino) - 1 < pos)) { -+ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); -+ return -EFBIG; -+ } -+ pos *= sizeof(*ino); -+ -+ file = au_sbr(sb, bindex)->br_xino.xi_file; -+ if (vfsub_f_size_read(file) < pos + sizeof(*ino)) -+ return 0; /* no ino */ -+ -+ sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos); -+ if (sz == sizeof(*ino)) -+ return 0; /* success */ -+ -+ err = sz; -+ if (unlikely(sz >= 0)) { -+ err = -EIO; -+ AuIOErr("xino read error (%zd)\n", sz); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* create and set a new xino file */ -+ -+struct file *au_xino_create(struct super_block *sb, char *fname, int silent) -+{ -+ struct file *file; -+ struct dentry *h_parent, *d; -+ struct inode *h_dir, *inode; -+ int err; -+ -+ /* -+ * at mount-time, and the xino file is the default path, -+ * hnotify is disabled so we have no notify events to ignore. -+ * when a user specified the xino, we cannot get au_hdir to be ignored. -+ */ -+ file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE -+ /* | __FMODE_NONOTIFY */, -+ S_IRUGO | S_IWUGO); -+ if (IS_ERR(file)) { -+ if (!silent) -+ pr_err("open %s(%ld)\n", fname, PTR_ERR(file)); -+ return file; -+ } -+ -+ /* keep file count */ -+ err = 0; -+ inode = file_inode(file); -+ h_parent = dget_parent(file->f_path.dentry); -+ h_dir = d_inode(h_parent); -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ /* mnt_want_write() is unnecessary here */ -+ /* no delegation since it is just created */ -+ if (inode->i_nlink) -+ err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, -+ /*force*/0); -+ mutex_unlock(&h_dir->i_mutex); -+ dput(h_parent); -+ if (unlikely(err)) { -+ if (!silent) -+ pr_err("unlink %s(%d)\n", fname, err); -+ goto out; -+ } -+ -+ err = -EINVAL; -+ d = file->f_path.dentry; -+ if (unlikely(sb == d->d_sb)) { -+ if (!silent) -+ pr_err("%s must be outside\n", fname); -+ goto out; -+ } -+ if (unlikely(au_test_fs_bad_xino(d->d_sb))) { -+ if (!silent) -+ pr_err("xino doesn't support %s(%s)\n", -+ fname, au_sbtype(d->d_sb)); -+ goto out; -+ } -+ return file; /* success */ -+ -+out: -+ fput(file); -+ file = ERR_PTR(err); -+ return file; -+} -+ -+/* -+ * find another branch who is on the same filesystem of the specified -+ * branch{@btgt}. search until @bend. -+ */ -+static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, -+ aufs_bindex_t bend) -+{ -+ aufs_bindex_t bindex; -+ struct super_block *tgt_sb = au_sbr_sb(sb, btgt); -+ -+ for (bindex = 0; bindex < btgt; bindex++) -+ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) -+ return bindex; -+ for (bindex++; bindex <= bend; bindex++) -+ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) -+ return bindex; -+ return -1; -+} -+ +/* ---------------------------------------------------------------------- */ + +/* -+ * initialize the xinofile for the specified branch @br -+ * at the place/path where @base_file indicates. -+ * test whether another branch is on the same filesystem or not, -+ * if @do_test is true. ++ * truncate a xino bitmap file + */ -+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, -+ struct file *base_file, int do_test) -+{ -+ int err; -+ ino_t ino; -+ aufs_bindex_t bend, bindex; -+ struct au_branch *shared_br, *b; -+ struct file *file; -+ struct super_block *tgt_sb; -+ -+ shared_br = NULL; -+ bend = au_sbend(sb); -+ if (do_test) { -+ tgt_sb = au_br_sb(br); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ b = au_sbr(sb, bindex); -+ if (tgt_sb == au_br_sb(b)) { -+ shared_br = b; -+ break; -+ } -+ } -+ } -+ -+ if (!shared_br || !shared_br->br_xino.xi_file) { -+ struct au_xino_lock_dir ldir; -+ -+ au_xino_lock_dir(sb, base_file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ file = au_xino_create2(base_file, NULL); -+ au_xino_unlock_dir(&ldir); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ br->br_xino.xi_file = file; -+ } else { -+ br->br_xino.xi_file = shared_br->br_xino.xi_file; -+ get_file(br->br_xino.xi_file); -+ } -+ -+ ino = AUFS_ROOT_INO; -+ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, -+ h_ino, ino); -+ if (unlikely(err)) { -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = NULL; -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* trucate a xino bitmap file */ + +/* todo: slow */ +static int do_xib_restore(struct super_block *sb, struct file *file, void *page) @@ -34689,9 +38115,13 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + +static int xib_restore(struct super_block *sb) +{ -+ int err; -+ aufs_bindex_t bindex, bend; ++ int err, i; ++ unsigned int nfile; ++ aufs_bindex_t bindex, bbot; + void *page; ++ struct au_branch *br; ++ struct au_xino *xi; ++ struct file *file; + + err = -ENOMEM; + page = (void *)__get_free_page(GFP_NOFS); @@ -34699,13 +38129,19 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + goto out; + + err = 0; -+ bend = au_sbend(sb); -+ for (bindex = 0; !err && bindex <= bend; bindex++) -+ if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) -+ err = do_xib_restore -+ (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); -+ else -+ AuDbg("b%d\n", bindex); ++ bbot = au_sbbot(sb); ++ for (bindex = 0; !err && bindex <= bbot; bindex++) ++ if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) { ++ br = au_sbr(sb, bindex); ++ xi = br->br_xino; ++ nfile = xi->xi_nfile; ++ for (i = 0; i < nfile; i++) { ++ file = au_xino_file(xi, i); ++ if (file) ++ err = do_xib_restore(sb, file, page); ++ } ++ } else ++ AuDbg("skip shared b%d\n", bindex); + free_page((unsigned long)page); + +out: @@ -34717,7 +38153,6 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + int err; + ssize_t sz; + loff_t pos; -+ struct au_xino_lock_dir ldir; + struct au_sbinfo *sbinfo; + unsigned long *p; + struct file *file; @@ -34733,10 +38168,7 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + if (vfsub_f_size_read(file) <= PAGE_SIZE) + goto out; + -+ au_xino_lock_dir(sb, file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ file = au_xino_create2(sbinfo->si_xib, NULL); -+ au_xino_unlock_dir(&ldir); ++ file = au_xino_create2(sb, &sbinfo->si_xib->f_path, NULL); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; @@ -34766,6 +38198,112 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + +/* ---------------------------------------------------------------------- */ + ++struct au_xino *au_xino_alloc(unsigned int nfile) ++{ ++ struct au_xino *xi; ++ ++ xi = kzalloc(sizeof(*xi), GFP_NOFS); ++ if (unlikely(!xi)) ++ goto out; ++ xi->xi_nfile = nfile; ++ xi->xi_file = kcalloc(nfile, sizeof(*xi->xi_file), GFP_NOFS); ++ if (unlikely(!xi->xi_file)) ++ goto out_free; ++ ++ xi->xi_nondir.total = 8; /* initial size */ ++ xi->xi_nondir.array = kcalloc(xi->xi_nondir.total, sizeof(ino_t), ++ GFP_NOFS); ++ if (unlikely(!xi->xi_nondir.array)) ++ goto out_file; ++ ++ spin_lock_init(&xi->xi_nondir.spin); ++ init_waitqueue_head(&xi->xi_nondir.wqh); ++ mutex_init(&xi->xi_mtx); ++ INIT_HLIST_BL_HEAD(&xi->xi_writing); ++ atomic_set(&xi->xi_truncating, 0); ++ kref_init(&xi->xi_kref); ++ goto out; /* success */ ++ ++out_file: ++ au_kfree_try_rcu(xi->xi_file); ++out_free: ++ au_kfree_rcu(xi); ++ xi = NULL; ++out: ++ return xi; ++} ++ ++static int au_xino_init(struct au_branch *br, int idx, struct file *file) ++{ ++ int err; ++ struct au_xino *xi; ++ ++ err = 0; ++ xi = au_xino_alloc(idx + 1); ++ if (unlikely(!xi)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ if (file) ++ get_file(file); ++ xi->xi_file[idx] = file; ++ AuDebugOn(br->br_xino); ++ br->br_xino = xi; ++ ++out: ++ return err; ++} ++ ++static void au_xino_release(struct kref *kref) ++{ ++ struct au_xino *xi; ++ int i; ++ unsigned long ul; ++ struct hlist_bl_head *hbl; ++ struct hlist_bl_node *pos, *n; ++ struct au_xi_writing *p; ++ ++ xi = container_of(kref, struct au_xino, xi_kref); ++ for (i = 0; i < xi->xi_nfile; i++) ++ if (xi->xi_file[i]) ++ fput(xi->xi_file[i]); ++ for (i = xi->xi_nondir.total - 1; i >= 0; i--) ++ AuDebugOn(xi->xi_nondir.array[i]); ++ mutex_destroy(&xi->xi_mtx); ++ hbl = &xi->xi_writing; ++ ul = au_hbl_count(hbl); ++ if (unlikely(ul)) { ++ pr_warn("xi_writing %lu\n", ul); ++ hlist_bl_lock(hbl); ++ hlist_bl_for_each_entry_safe (p, pos, n, hbl, node) { ++ hlist_bl_del(&p->node); ++ au_kfree_rcu(p); ++ } ++ hlist_bl_unlock(hbl); ++ } ++ au_kfree_try_rcu(xi->xi_file); ++ au_kfree_try_rcu(xi->xi_nondir.array); ++ au_kfree_rcu(xi); ++} ++ ++int au_xino_put(struct au_branch *br) ++{ ++ int ret; ++ struct au_xino *xi; ++ ++ ret = 0; ++ xi = br->br_xino; ++ if (xi) { ++ br->br_xino = NULL; ++ ret = kref_put(&xi->xi_kref, au_xino_release); ++ } ++ ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ +/* + * xino mount option handlers + */ @@ -34778,26 +38316,27 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); -+ sbinfo->si_xread = NULL; -+ sbinfo->si_xwrite = NULL; ++ /* unnecessary to clear sbinfo->si_xread and ->si_xwrite */ + if (sbinfo->si_xib) + fput(sbinfo->si_xib); + sbinfo->si_xib = NULL; -+ free_page((unsigned long)sbinfo->si_xib_buf); ++ if (sbinfo->si_xib_buf) ++ free_page((unsigned long)sbinfo->si_xib_buf); + sbinfo->si_xib_buf = NULL; +} + -+static int au_xino_set_xib(struct super_block *sb, struct file *base) ++static int au_xino_set_xib(struct super_block *sb, struct path *path) +{ + int err; + loff_t pos; + struct au_sbinfo *sbinfo; + struct file *file; ++ struct super_block *xi_sb; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); -+ file = au_xino_create2(base, sbinfo->si_xib); ++ file = au_xino_create2(sb, path, sbinfo->si_xib); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; @@ -34806,6 +38345,15 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + sbinfo->si_xib = file; + sbinfo->si_xread = vfs_readf(file); + sbinfo->si_xwrite = vfs_writef(file); ++ xi_sb = file_inode(file)->i_sb; ++ sbinfo->si_ximaxent = xi_sb->s_maxbytes; ++ if (unlikely(sbinfo->si_ximaxent < PAGE_SIZE)) { ++ err = -EIO; ++ pr_err("s_maxbytes(%llu) on %s is too small\n", ++ (u64)sbinfo->si_ximaxent, au_sbtype(xi_sb)); ++ goto out_unset; ++ } ++ sbinfo->si_ximaxent /= sizeof(ino_t); + + err = -ENOMEM; + if (!sbinfo->si_xib_buf) @@ -34826,101 +38374,122 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + goto out; /* success */ + +out_free: -+ free_page((unsigned long)sbinfo->si_xib_buf); ++ if (sbinfo->si_xib_buf) ++ free_page((unsigned long)sbinfo->si_xib_buf); + sbinfo->si_xib_buf = NULL; + if (err >= 0) + err = -EIO; +out_unset: + fput(sbinfo->si_xib); + sbinfo->si_xib = NULL; -+ sbinfo->si_xread = NULL; -+ sbinfo->si_xwrite = NULL; +out: ++ AuTraceErr(err); + return err; +} + +/* xino for each branch */ +static void xino_clear_br(struct super_block *sb) +{ -+ aufs_bindex_t bindex, bend; ++ aufs_bindex_t bindex, bbot; + struct au_branch *br; + -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { ++ bbot = au_sbbot(sb); ++ for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); -+ if (!br || !br->br_xino.xi_file) -+ continue; ++ AuDebugOn(!br); ++ au_xino_put(br); ++ } ++} + -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = NULL; ++static void au_xino_set_br_shared(struct super_block *sb, struct au_branch *br, ++ aufs_bindex_t bshared) ++{ ++ struct au_branch *brshared; ++ ++ brshared = au_sbr(sb, bshared); ++ AuDebugOn(!brshared->br_xino); ++ AuDebugOn(!brshared->br_xino->xi_file); ++ if (br->br_xino != brshared->br_xino) { ++ au_xino_get(brshared); ++ au_xino_put(br); ++ br->br_xino = brshared->br_xino; + } +} + -+static int au_xino_set_br(struct super_block *sb, struct file *base) ++struct au_xino_do_set_br { ++ vfs_writef_t writef; ++ struct au_branch *br; ++ ino_t h_ino; ++ aufs_bindex_t bshared; ++}; ++ ++static int au_xino_do_set_br(struct super_block *sb, struct path *path, ++ struct au_xino_do_set_br *args) +{ + int err; -+ ino_t ino; -+ aufs_bindex_t bindex, bend, bshared; -+ struct { -+ struct file *old, *new; -+ } *fpair, *p; ++ struct au_xi_calc calc; ++ struct file *file; + struct au_branch *br; -+ struct inode *inode; -+ vfs_writef_t writef; ++ struct au_xi_new xinew = { ++ .base = path ++ }; + -+ SiMustWriteLock(sb); ++ br = args->br; ++ xinew.xi = br->br_xino; ++ au_xi_calc(sb, args->h_ino, &calc); ++ xinew.copy_src = au_xino_file(xinew.xi, calc.idx); ++ if (args->bshared >= 0) ++ /* shared xino */ ++ au_xino_set_br_shared(sb, br, args->bshared); ++ else if (!xinew.xi) { ++ /* new xino */ ++ err = au_xino_init(br, calc.idx, xinew.copy_src); ++ if (unlikely(err)) ++ goto out; ++ } + -+ err = -ENOMEM; -+ bend = au_sbend(sb); -+ fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS); -+ if (unlikely(!fpair)) ++ /* force re-creating */ ++ xinew.xi = br->br_xino; ++ xinew.idx = calc.idx; ++ mutex_lock(&xinew.xi->xi_mtx); ++ file = au_xi_new(sb, &xinew); ++ mutex_unlock(&xinew.xi->xi_mtx); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) + goto out; ++ AuDebugOn(!file); + -+ inode = d_inode(sb->s_root); -+ ino = AUFS_ROOT_INO; -+ writef = au_sbi(sb)->si_xwrite; -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { -+ br = au_sbr(sb, bindex); -+ bshared = is_sb_shared(sb, bindex, bindex - 1); -+ if (bshared >= 0) { -+ /* shared xino */ -+ *p = fpair[bshared]; -+ get_file(p->new); -+ } -+ -+ if (!p->new) { -+ /* new xino */ -+ p->old = br->br_xino.xi_file; -+ p->new = au_xino_create2(base, br->br_xino.xi_file); -+ err = PTR_ERR(p->new); -+ if (IS_ERR(p->new)) { -+ p->new = NULL; -+ goto out_pair; -+ } -+ } ++ err = au_xino_do_write(args->writef, file, &calc, AUFS_ROOT_INO); ++ if (unlikely(err)) ++ au_xino_put(br); + -+ err = au_xino_do_write(writef, p->new, -+ au_h_iptr(inode, bindex)->i_ino, ino); -+ if (unlikely(err)) -+ goto out_pair; -+ } ++out: ++ AuTraceErr(err); ++ return err; ++} + -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { -+ br = au_sbr(sb, bindex); -+ if (br->br_xino.xi_file) -+ fput(br->br_xino.xi_file); -+ get_file(p->new); -+ br->br_xino.xi_file = p->new; -+ } ++static int au_xino_set_br(struct super_block *sb, struct path *path) ++{ ++ int err; ++ aufs_bindex_t bindex, bbot; ++ struct au_xino_do_set_br args; ++ struct inode *inode; + -+out_pair: -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) -+ if (p->new) -+ fput(p->new); -+ else ++ SiMustWriteLock(sb); ++ ++ bbot = au_sbbot(sb); ++ inode = d_inode(sb->s_root); ++ args.writef = au_sbi(sb)->si_xwrite; ++ for (bindex = 0; bindex <= bbot; bindex++) { ++ args.h_ino = au_h_iptr(inode, bindex)->i_ino; ++ args.br = au_sbr(sb, bindex); ++ args.bshared = is_sb_shared(sb, bindex, bindex - 1); ++ err = au_xino_do_set_br(sb, path, &args); ++ if (unlikely(err)) + break; -+ kfree(fpair); -+out: ++ } ++ ++ AuTraceErr(err); + return err; +} + @@ -34931,32 +38500,37 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + au_xigen_clr(sb); + xino_clear_xib(sb); + xino_clear_br(sb); ++ dbgaufs_brs_del(sb, 0); + sbinfo = au_sbi(sb); + /* lvalue, do not call au_mntflags() */ + au_opt_clr(sbinfo->si_mntflags, XINO); +} + -+int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount) ++int au_xino_set(struct super_block *sb, struct au_opt_xino *xiopt, int remount) +{ + int err, skip; -+ struct dentry *parent, *cur_parent; ++ struct dentry *dentry, *parent, *cur_dentry, *cur_parent; + struct qstr *dname, *cur_name; + struct file *cur_xino; -+ struct inode *dir; + struct au_sbinfo *sbinfo; ++ struct path *path, *cur_path; + + SiMustWriteLock(sb); + + err = 0; + sbinfo = au_sbi(sb); -+ parent = dget_parent(xino->file->f_path.dentry); ++ path = &xiopt->file->f_path; ++ dentry = path->dentry; ++ parent = dget_parent(dentry); + if (remount) { + skip = 0; -+ dname = &xino->file->f_path.dentry->d_name; + cur_xino = sbinfo->si_xib; + if (cur_xino) { -+ cur_parent = dget_parent(cur_xino->f_path.dentry); -+ cur_name = &cur_xino->f_path.dentry->d_name; ++ cur_path = &cur_xino->f_path; ++ cur_dentry = cur_path->dentry; ++ cur_parent = dget_parent(cur_dentry); ++ cur_name = &cur_dentry->d_name; ++ dname = &dentry->d_name; + skip = (cur_parent == parent + && au_qstreq(dname, cur_name)); + dput(cur_parent); @@ -34966,30 +38540,26 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + } + + au_opt_set(sbinfo->si_mntflags, XINO); -+ dir = d_inode(parent); -+ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); -+ /* mnt_want_write() is unnecessary here */ -+ err = au_xino_set_xib(sb, xino->file); -+ if (!err) -+ err = au_xigen_set(sb, xino->file); ++ err = au_xino_set_xib(sb, path); ++ /* si_x{read,write} are set */ + if (!err) -+ err = au_xino_set_br(sb, xino->file); -+ mutex_unlock(&dir->i_mutex); ++ err = au_xigen_set(sb, path); + if (!err) ++ err = au_xino_set_br(sb, path); ++ if (!err) { ++ dbgaufs_brs_add(sb, 0, /*topdown*/1); + goto out; /* success */ ++ } + + /* reset all */ -+ AuIOErr("failed creating xino(%d).\n", err); -+ au_xigen_clr(sb); -+ xino_clear_xib(sb); ++ AuIOErr("failed setting xino(%d).\n", err); ++ au_xino_clr(sb); + +out: + dput(parent); + return err; +} + -+/* ---------------------------------------------------------------------- */ -+ +/* + * create a xinofile at the default place/path. + */ @@ -35000,12 +38570,12 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + struct au_branch *br; + struct super_block *h_sb; + struct path path; -+ aufs_bindex_t bend, bindex, bwr; ++ aufs_bindex_t bbot, bindex, bwr; + + br = NULL; -+ bend = au_sbend(sb); ++ bbot = au_sbbot(sb); + bwr = -1; -+ for (bindex = 0; bindex <= bend; bindex++) { ++ for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_writable(br->br_perm) + && !au_test_fs_bad_xino(au_br_sb(br))) { @@ -35027,8 +38597,6 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + strcat(p, "/" AUFS_XINO_FNAME); + AuDbg("%s\n", p); + file = au_xino_create(sb, p, /*silent*/0); -+ if (!IS_ERR(file)) -+ au_xino_brid_set(sb, br->br_id); + } + free_page((unsigned long)page); + } else { @@ -35042,8 +38610,6 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + fput(file); + file = ERR_PTR(-EINVAL); + } -+ if (!IS_ERR(file)) -+ au_xino_brid_set(sb, -1); + } + +out: @@ -35052,6 +38618,254 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c + +/* ---------------------------------------------------------------------- */ + ++/* ++ * initialize the xinofile for the specified branch @br ++ * at the place/path where @base_file indicates. ++ * test whether another branch is on the same filesystem or not, ++ * if found then share the xinofile with another branch. ++ */ ++int au_xino_init_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, ++ struct path *base) ++{ ++ int err; ++ struct au_xino_do_set_br args = { ++ .h_ino = h_ino, ++ .br = br ++ }; ++ ++ args.writef = au_sbi(sb)->si_xwrite; ++ args.bshared = sbr_find_shared(sb, /*btop*/0, au_sbbot(sb), ++ au_br_sb(br)); ++ err = au_xino_do_set_br(sb, base, &args); ++ if (unlikely(err)) ++ au_xino_put(br); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * get an unused inode number from bitmap ++ */ ++ino_t au_xino_new_ino(struct super_block *sb) ++{ ++ ino_t ino; ++ unsigned long *p, pindex, ul, pend; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ int free_bit, err; ++ ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return iunique(sb, AUFS_FIRST_INO); ++ ++ sbinfo = au_sbi(sb); ++ mutex_lock(&sbinfo->si_xib_mtx); ++ p = sbinfo->si_xib_buf; ++ free_bit = sbinfo->si_xib_next_bit; ++ if (free_bit < page_bits && !test_bit(free_bit, p)) ++ goto out; /* success */ ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ ++ pindex = sbinfo->si_xib_last_pindex; ++ for (ul = pindex - 1; ul < ULONG_MAX; ul--) { ++ err = xib_pindex(sb, ul); ++ if (unlikely(err)) ++ goto out_err; ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ } ++ ++ file = sbinfo->si_xib; ++ pend = vfsub_f_size_read(file) / PAGE_SIZE; ++ for (ul = pindex + 1; ul <= pend; ul++) { ++ err = xib_pindex(sb, ul); ++ if (unlikely(err)) ++ goto out_err; ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ } ++ BUG(); ++ ++out: ++ set_bit(free_bit, p); ++ sbinfo->si_xib_next_bit = free_bit + 1; ++ pindex = sbinfo->si_xib_last_pindex; ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ ino = xib_calc_ino(pindex, free_bit); ++ AuDbg("i%lu\n", (unsigned long)ino); ++ return ino; ++out_err: ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ AuDbg("i0\n"); ++ return 0; ++} ++ ++/* for s_op->delete_inode() */ ++void au_xino_delete_inode(struct inode *inode, const int unlinked) ++{ ++ int err; ++ unsigned int mnt_flags; ++ aufs_bindex_t bindex, bbot, bi; ++ unsigned char try_trunc; ++ struct au_iinfo *iinfo; ++ struct super_block *sb; ++ struct au_hinode *hi; ++ struct inode *h_inode; ++ struct au_branch *br; ++ vfs_writef_t xwrite; ++ struct au_xi_calc calc; ++ struct file *file; ++ ++ AuDebugOn(au_is_bad_inode(inode)); ++ ++ sb = inode->i_sb; ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, XINO) ++ || inode->i_ino == AUFS_ROOT_INO) ++ return; ++ ++ if (unlinked) { ++ au_xigen_inc(inode); ++ au_xib_clear_bit(inode); ++ } ++ ++ iinfo = au_ii(inode); ++ bindex = iinfo->ii_btop; ++ if (bindex < 0) ++ return; ++ ++ xwrite = au_sbi(sb)->si_xwrite; ++ try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); ++ hi = au_hinode(iinfo, bindex); ++ bbot = iinfo->ii_bbot; ++ for (; bindex <= bbot; bindex++, hi++) { ++ h_inode = hi->hi_inode; ++ if (!h_inode ++ || (!unlinked && h_inode->i_nlink)) ++ continue; ++ ++ /* inode may not be revalidated */ ++ bi = au_br_index(sb, hi->hi_id); ++ if (bi < 0) ++ continue; ++ ++ br = au_sbr(sb, bi); ++ au_xi_calc(sb, h_inode->i_ino, &calc); ++ file = au_xino_file(br->br_xino, calc.idx); ++ if (IS_ERR_OR_NULL(file)) ++ continue; ++ ++ err = au_xino_do_write(xwrite, file, &calc, /*ino*/0); ++ if (!err && try_trunc ++ && au_test_fs_trunc_xino(au_br_sb(br))) ++ xino_try_trunc(sb, br); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_xinondir_find(struct au_xino *xi, ino_t h_ino) ++{ ++ int found, total, i; ++ ++ found = -1; ++ total = xi->xi_nondir.total; ++ for (i = 0; i < total; i++) { ++ if (xi->xi_nondir.array[i] != h_ino) ++ continue; ++ found = i; ++ break; ++ } ++ ++ return found; ++} ++ ++static int au_xinondir_expand(struct au_xino *xi) ++{ ++ int err, sz; ++ ino_t *p; ++ ++ BUILD_BUG_ON(KMALLOC_MAX_SIZE > INT_MAX); ++ ++ err = -ENOMEM; ++ sz = xi->xi_nondir.total * sizeof(ino_t); ++ if (unlikely(sz > KMALLOC_MAX_SIZE / 2)) ++ goto out; ++ p = au_kzrealloc(xi->xi_nondir.array, sz, sz << 1, GFP_ATOMIC, ++ /*may_shrink*/0); ++ if (p) { ++ xi->xi_nondir.array = p; ++ xi->xi_nondir.total <<= 1; ++ AuDbg("xi_nondir.total %d\n", xi->xi_nondir.total); ++ err = 0; ++ } ++ ++out: ++ return err; ++} ++ ++void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex, ++ ino_t h_ino, int idx) ++{ ++ struct au_xino *xi; ++ ++ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); ++ xi = au_sbr(sb, bindex)->br_xino; ++ AuDebugOn(idx < 0 || xi->xi_nondir.total <= idx); ++ ++ spin_lock(&xi->xi_nondir.spin); ++ AuDebugOn(xi->xi_nondir.array[idx] != h_ino); ++ xi->xi_nondir.array[idx] = 0; ++ spin_unlock(&xi->xi_nondir.spin); ++ wake_up_all(&xi->xi_nondir.wqh); ++} ++ ++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 *xi; ++ ++ err = 0; ++ *idx = -1; ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ goto out; /* no xino */ ++ ++ xi = au_sbr(sb, bindex)->br_xino; ++ ++again: ++ spin_lock(&xi->xi_nondir.spin); ++ found = au_xinondir_find(xi, h_ino); ++ if (found == -1) { ++ empty = au_xinondir_find(xi, /*h_ino*/0); ++ if (empty == -1) { ++ empty = xi->xi_nondir.total; ++ err = au_xinondir_expand(xi); ++ if (unlikely(err)) ++ goto out_unlock; ++ } ++ xi->xi_nondir.array[empty] = h_ino; ++ *idx = empty; ++ } else { ++ spin_unlock(&xi->xi_nondir.spin); ++ wait_event(xi->xi_nondir.wqh, ++ xi->xi_nondir.array[found] != h_ino); ++ goto again; ++ } ++ ++out_unlock: ++ spin_unlock(&xi->xi_nondir.spin); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ +int au_xino_path(struct seq_file *seq, struct file *file) +{ + int err; @@ -35071,10 +38885,11 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c +} 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-01-13 20:11:11.673093671 +0100 -@@ -0,0 +1,419 @@ ++++ linux/include/uapi/linux/aufs_type.h 2018-12-27 13:19:17.715082917 +0100 +@@ -0,0 +1,448 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ +/* -+ * Copyright (C) 2005-2015 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 @@ -35114,7 +38929,7 @@ diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/lin + +#include + -+#define AUFS_VERSION "4.x-rcN-20160111" ++#define AUFS_VERSION "4.x-rcN-20181217" + +/* todo? move this to linux-2.6.19/include/magic.h */ +#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') @@ -35176,6 +38991,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 + @@ -35394,6 +39216,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; @@ -35492,52 +39335,17 @@ 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__ */ +SPDX-License-Identifier: GPL-2.0 aufs4.x-rcN loopback patch diff --git a/drivers/block/loop.c b/drivers/block/loop.c -index abfdd2b..a2e3c43 100644 +index 470dd02..20dc3ec 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c -@@ -556,7 +556,7 @@ static int do_req_filebacked(struct loop_device *lo, struct request *rq) +@@ -625,6 +625,15 @@ static inline void loop_update_dio(struct loop_device *lo) + lo->use_dio); } - struct switch_request { -- struct file *file; -+ struct file *file, *virt_file; - struct completion wait; - }; - -@@ -582,6 +582,7 @@ static void do_loop_switch(struct loop_device *lo, struct switch_request *p) - mapping = file->f_mapping; - mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask); - lo->lo_backing_file = file; -+ lo->lo_backing_virt_file = p->virt_file; - lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ? - mapping->host->i_bdev->bd_block_size : PAGE_SIZE; - lo->old_gfp_mask = mapping_gfp_mask(mapping); -@@ -594,11 +595,13 @@ static void do_loop_switch(struct loop_device *lo, struct switch_request *p) - * First it needs to flush existing IO, it does this by sending a magic - * BIO down the pipe. The completion of this BIO does the actual switch. - */ --static int loop_switch(struct loop_device *lo, struct file *file) -+static int loop_switch(struct loop_device *lo, struct file *file, -+ struct file *virt_file) - { - struct switch_request w; - - w.file = file; -+ w.virt_file = virt_file; - - /* freeze queue and wait for completion of scheduled requests */ - blk_mq_freeze_queue(lo->lo_queue); -@@ -617,7 +620,16 @@ static int loop_switch(struct loop_device *lo, struct file *file) - */ - static int loop_flush(struct loop_device *lo) - { -- return loop_switch(lo, NULL); -+ return loop_switch(lo, NULL, NULL); -+} -+ +static struct file *loop_real_file(struct file *file) +{ + struct file *f = NULL; @@ -35545,18 +39353,20 @@ index abfdd2b..a2e3c43 100644 + if (file->f_path.dentry->d_sb->s_op->real_loop) + f = file->f_path.dentry->d_sb->s_op->real_loop(file); + return f; - } - ++} ++ static void loop_reread_partitions(struct loop_device *lo, -@@ -654,6 +666,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, + struct block_device *bdev) + { +@@ -689,6 +698,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, unsigned int arg) { struct file *file, *old_file; + struct file *f, *virt_file = NULL, *old_virt_file; - struct inode *inode; int error; -@@ -670,9 +683,16 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, + error = -ENXIO; +@@ -704,12 +714,19 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, file = fget(arg); if (!file) goto out; @@ -35567,20 +39377,25 @@ index abfdd2b..a2e3c43 100644 + get_file(file); + } - inode = file->f_mapping->host; + error = loop_validate_file(file, bdev); + if (error) + goto out_putf; + old_file = lo->lo_backing_file; + old_virt_file = lo->lo_backing_virt_file; error = -EINVAL; -@@ -684,17 +704,21 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, - goto out_putf; - - /* and ... switch */ -- error = loop_switch(lo, file); -+ error = loop_switch(lo, file, virt_file); - if (error) - goto out_putf; +@@ -721,6 +738,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, + blk_mq_freeze_queue(lo->lo_queue); + mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask); + lo->lo_backing_file = file; ++ lo->lo_backing_virt_file = virt_file; + lo->old_gfp_mask = mapping_gfp_mask(file->f_mapping); + mapping_set_gfp_mask(file->f_mapping, + lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); +@@ -728,12 +746,16 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, + blk_mq_unfreeze_queue(lo->lo_queue); fput(old_file); + if (old_virt_file) @@ -35596,16 +39411,16 @@ index abfdd2b..a2e3c43 100644 out: return error; } -@@ -881,7 +905,7 @@ static int loop_prepare_queue(struct loop_device *lo) +@@ -921,7 +943,7 @@ static int loop_prepare_queue(struct loop_device *lo) 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; - unsigned lo_blocksize; -@@ -896,6 +920,12 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, + int lo_flags = 0; +@@ -935,6 +957,12 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, file = fget(arg); if (!file) goto out; @@ -35618,7 +39433,7 @@ index abfdd2b..a2e3c43 100644 error = -EBUSY; if (lo->lo_state != Lo_unbound) -@@ -948,6 +978,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, +@@ -967,6 +995,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, lo->lo_device = bdev; lo->lo_flags = lo_flags; lo->lo_backing_file = file; @@ -35626,7 +39441,7 @@ index abfdd2b..a2e3c43 100644 lo->transfer = NULL; lo->ioctl = NULL; lo->lo_sizelimit = 0; -@@ -980,6 +1011,8 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, +@@ -1000,6 +1029,8 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, out_putf: fput(file); @@ -35635,7 +39450,7 @@ index abfdd2b..a2e3c43 100644 out: /* This is safe: open() is still holding a reference. */ module_put(THIS_MODULE); -@@ -1026,6 +1059,7 @@ loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer, +@@ -1046,6 +1077,7 @@ loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer, static int loop_clr_fd(struct loop_device *lo) { struct file *filp = lo->lo_backing_file; @@ -35643,7 +39458,7 @@ index abfdd2b..a2e3c43 100644 gfp_t gfp = lo->old_gfp_mask; struct block_device *bdev = lo->lo_device; -@@ -1057,6 +1091,7 @@ static int loop_clr_fd(struct loop_device *lo) +@@ -1077,6 +1109,7 @@ static int loop_clr_fd(struct loop_device *lo) spin_lock_irq(&lo->lo_lock); lo->lo_state = Lo_rundown; lo->lo_backing_file = NULL; @@ -35651,7 +39466,7 @@ index abfdd2b..a2e3c43 100644 spin_unlock_irq(&lo->lo_lock); loop_release_xfer(lo); -@@ -1101,6 +1136,8 @@ static int loop_clr_fd(struct loop_device *lo) +@@ -1125,6 +1158,8 @@ static int loop_clr_fd(struct loop_device *lo) * bd_mutex which is usually taken before lo_ctl_mutex. */ fput(filp); @@ -35661,7 +39476,7 @@ index abfdd2b..a2e3c43 100644 } diff --git a/drivers/block/loop.h b/drivers/block/loop.h -index fb2237c..c3888c5 100644 +index 4d42c7a..a4974ee 100644 --- a/drivers/block/loop.h +++ b/drivers/block/loop.h @@ -46,7 +46,7 @@ struct loop_device { @@ -35671,13 +39486,13 @@ index fb2237c..c3888c5 100644 - struct file * lo_backing_file; + struct file * lo_backing_file, *lo_backing_virt_file; struct block_device *lo_device; - unsigned lo_blocksize; void *key_data; + diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c -index 91c2ce7..d4ee5a7 100644 +index 5309874..1a334cf 100644 --- a/fs/aufs/f_op.c +++ b/fs/aufs/f_op.c -@@ -389,7 +389,7 @@ static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, +@@ -359,7 +359,7 @@ static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) if (IS_ERR(h_file)) goto out; @@ -35687,12 +39502,12 @@ index 91c2ce7..d4ee5a7 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 f324758..4555e7b 100644 +index 3f3577d..3b44158 100644 --- a/fs/aufs/loop.c +++ b/fs/aufs/loop.c -@@ -131,3 +131,19 @@ void au_loopback_fin(void) +@@ -133,3 +133,19 @@ void au_loopback_fin(void) symbol_put(loop_backing_file); - kfree(au_warn_loopback_array); + au_kfree_try_rcu(au_warn_loopback_array); } + +/* ---------------------------------------------------------------------- */ @@ -35711,10 +39526,10 @@ index f324758..4555e7b 100644 + return f; +} diff --git a/fs/aufs/loop.h b/fs/aufs/loop.h -index 6d9864d..3322557 100644 +index 05d703d..6bb23c8 100644 --- a/fs/aufs/loop.h +++ b/fs/aufs/loop.h -@@ -25,7 +25,11 @@ void au_warn_loopback(struct super_block *h_sb); +@@ -26,7 +26,11 @@ void au_warn_loopback(struct super_block *h_sb); int au_loopback_init(void); void au_loopback_fin(void); @@ -35726,7 +39541,7 @@ index 6d9864d..3322557 100644 AuStubInt0(au_test_loopback_overlap, struct super_block *sb, struct dentry *h_adding) AuStubInt0(au_test_loopback_kthread, void) -@@ -33,6 +37,8 @@ AuStubVoid(au_warn_loopback, struct super_block *h_sb) +@@ -34,6 +38,8 @@ AuStubVoid(au_warn_loopback, struct super_block *h_sb) AuStubInt0(au_loopback_init, void) AuStubVoid(au_loopback_fin, void) @@ -35736,10 +39551,10 @@ index 6d9864d..3322557 100644 #endif /* __KERNEL__ */ diff --git a/fs/aufs/super.c b/fs/aufs/super.c -index 98cfd64..a705a0e 100644 +index 777503e..7130061 100644 --- a/fs/aufs/super.c +++ b/fs/aufs/super.c -@@ -832,7 +832,10 @@ static const struct super_operations aufs_sop = { +@@ -845,7 +845,10 @@ static const struct super_operations aufs_sop = { .statfs = aufs_statfs, .put_super = aufs_put_super, .sync_fs = aufs_sync_fs, @@ -35752,10 +39567,10 @@ index 98cfd64..a705a0e 100644 /* ---------------------------------------------------------------------- */ diff --git a/include/linux/fs.h b/include/linux/fs.h -index 8d48506..5246785 100644 +index 0e44705..59fb8ae 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h -@@ -1719,6 +1719,10 @@ struct super_operations { +@@ -1931,6 +1931,10 @@ struct super_operations { struct shrink_control *); long (*free_cached_objects)(struct super_block *, struct shrink_control *);