-Index: linux/include/linux/atm.h
-===================================================================
-RCS file: /afs/cmf/project/cvsroot/linux/include/linux/atm.h,v
-retrieving revision 1.2
-diff -u -r1.2 atm.h
---- linux/include/uapi/linux/atm.h 12 Feb 2003 20:56:33 -0000 1.2
-+++ linux/include/uapi/linux/atm.h 9 Apr 2003 12:08:38 -0000
-@@ -72,7 +72,7 @@
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/atm.h linux-4.9/include/uapi/linux/atm.h
+--- linux-4.9/include/uapi/linux/atm.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/uapi/linux/atm.h 2021-02-24 15:38:08.999744543 +0100
+@@ -70,7 +70,7 @@
/* connection identifier range; socket must be
bound or connected */
#define SO_ATMQOS __SO_ENCODE(SOL_ATM,2,struct atm_qos)
#define SO_ATMSAP __SO_ENCODE(SOL_ATM,3,struct atm_sap)
/* Service Access Point */
#define SO_ATMPVC __SO_ENCODE(SOL_ATM,4,struct sockaddr_atmpvc)
-@@ -127,9 +127,11 @@
+@@ -126,9 +126,11 @@
#define ATM_NONE 0 /* no traffic */
#define ATM_UBR 1
#define ATM_CBR 2
#define ATM_MAX_PCR -1 /* maximum available PCR */
-@@ -140,6 +142,11 @@
+@@ -139,6 +141,11 @@ struct atm_trafprm {
int min_pcr; /* minimum PCR in cells per second */
int max_cdv; /* maximum CDV in microseconds */
int max_sdu; /* maximum SDU in bytes */
/* extra params for ABR */
unsigned int icr; /* Initial Cell Rate (24-bit) */
unsigned int tbe; /* Transient Buffer Exposure (24-bit) */
-@@ -243,4 +251,37 @@
- };
+@@ -238,4 +245,37 @@ struct atmif_sioc {
+
typedef unsigned short atm_backend_t;
+struct atm_trafprm_compat {
+#define SO_ATMQOS_COMPAT __SO_ENCODE(SOL_ATM,2,struct atm_qos_compat)
+ /* Quality of Service setting (no vbr support) */
#endif /* _UAPI_LINUX_ATM_H */
-Index: linux/net/atm/common.c
-===================================================================
-RCS file: /afs/cmf/project/cvsroot/linux/net/atm/common.c,v
-retrieving revision 1.13
-diff -u -r1.13 common.c
---- linux/net/atm/common.c 17 Mar 2003 16:13:12 -0000 1.13
-+++ linux/net/atm/common.c 9 Apr 2003 12:10:28 -0000
-@@ -1085,6 +1085,43 @@
+diff -urNp -x '*.orig' linux-4.9/net/atm/common.c linux-4.9/net/atm/common.c
+--- linux-4.9/net/atm/common.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/atm/common.c 2021-02-24 15:38:08.999744543 +0100
+@@ -752,6 +752,43 @@ int vcc_setsockopt(struct socket *sock,
vcc = ATM_SD(sock);
switch (optname) {
case SO_ATMQOS:
{
struct atm_qos qos;
-@@ -1132,6 +1169,31 @@
+@@ -800,6 +837,31 @@ int vcc_getsockopt(struct socket *sock,
vcc = ATM_SD(sock);
switch (optname) {
+ -EFAULT : 0;
+ }
case SO_ATMQOS:
- if (!test_bit(ATM_VF_HASQOS,&vcc->flags))
+ if (!test_bit(ATM_VF_HASQOS, &vcc->flags))
return -EINVAL;
-diff -urN linux-2.4.25/drivers/atm/Makefile linux-2.4.25-atmdd/drivers/atm/Makefile
---- linux-2.4.25/drivers/atm/Makefile 2004-02-23 15:18:29.000000000 +0100
-+++ linux-2.4.25-atmdd/drivers/atm/Makefile 2004-02-29 22:51:26.000000000 +0100
-@@ -31,6 +31,7 @@
- endif
-
- obj-$(CONFIG_ATM_DUMMY) += adummy.o
-+obj-$(CONFIG_ATM_DD) += atmdd.o
- obj-$(CONFIG_ATM_TCP) += atmtcp.o
- obj-$(CONFIG_ATM_FIRESTREAM) += firestream.o
- obj-$(CONFIG_ATM_LANAI) += lanai.o
-diff -urN linux-2.4.25/drivers/atm/Kconfig linux-2.4.25-atmdd/drivers/atm/Kconfig
---- linux-2.4.25/drivers/atm/Kcnfig 2003-08-25 13:44:41.000000000 +0200
-+++ linux-2.4.25-atmdd/drivers/atm/Kconfig 2004-02-29 22:52:59.000000000 +0100
-@@ -4,6 +4,14 @@
- default y
+diff -urNp -x '*.orig' linux-4.9/drivers/atm/Kconfig linux-4.9/drivers/atm/Kconfig
+--- linux-4.9/drivers/atm/Kconfig 2021-02-24 15:42:15.767446799 +0100
++++ linux-4.9/drivers/atm/Kconfig 2021-02-24 15:42:29.767884184 +0100
+@@ -14,6 +14,14 @@ menuconfig ATM_DRIVERS
if ATM_DRIVERS && NETDEVICES && ATM
-+
+
+config ATM_DD
+ tristate "ATM loopback"
+ depends on INET && ATM
+ This is an example atm driver. It does not require any actual ATM
+ hardware. It supports AAL5 and AAL0. Frames are merely looped back
+ to the sender on the same VC they were sent.
-
++
config ATM_DUMMY
tristate "Dummy ATM driver"
-diff -urN linux-2.4.25/drivers/atm/atmdd.c linux-2.4.25-atmdd/drivers/atm/atmdd.c
---- linux-2.4.25/drivers/atm/atmdd.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.4.25-atmdd/drivers/atm/atmdd.c 2004-02-29 22:58:11.000000000 +0100
+ help
+diff -urNp -x '*.orig' linux-4.9/drivers/atm/Makefile linux-4.9/drivers/atm/Makefile
+--- linux-4.9/drivers/atm/Makefile 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/drivers/atm/Makefile 2021-02-24 15:42:29.767884184 +0100
+@@ -25,6 +25,7 @@ ifeq ($(CONFIG_ATM_IDT77252_USE_SUNI),y)
+ endif
+
+ obj-$(CONFIG_ATM_DUMMY) += adummy.o
++obj-$(CONFIG_ATM_DD) += atmdd.o
+ obj-$(CONFIG_ATM_TCP) += atmtcp.o
+ obj-$(CONFIG_ATM_FIRESTREAM) += firestream.o
+ obj-$(CONFIG_ATM_LANAI) += lanai.o
+diff -urNp -x '*.orig' linux-4.9/drivers/atm/atmdd.c linux-4.9/drivers/atm/atmdd.c
+--- linux-4.9/drivers/atm/atmdd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/drivers/atm/atmdd.c 2021-02-24 15:42:29.767884184 +0100
@@ -0,0 +1,921 @@
+/*
+#######################################################################
---- linux.aufs/fs/aufs/cpup.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/cpup.c 2013-08-23 23:59:39.631583456 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/cpup.c linux-4.9/fs/aufs/cpup.c
+--- linux-4.9/fs/aufs/cpup.c 2021-02-24 16:25:34.368384227 +0100
++++ linux-4.9/fs/aufs/cpup.c 2021-02-24 16:25:46.172095883 +0100
@@ -24,9 +24,9 @@
- #include <linux/mm.h>
+ #include <linux/task_work.h>
#include "aufs.h"
-void au_cpup_attr_flags(struct inode *dst, unsigned int iflags)
| S_NOATIME | S_NOCMTIME | S_AUTOMOUNT;
BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags));
-@@ -155,7 +155,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
+@@ -159,7 +159,7 @@ void au_dtime_revert(struct au_dtime *dt
struct au_cpup_reg_attr {
int valid;
struct kstat st;
};
static noinline_for_stack
---- linux.aufs/fs/aufs/cpup.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/cpup.h 2013-08-23 23:59:39.634916914 +0200
-@@ -31,7 +31,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.h linux/fs/aufs/cpup.h
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/cpup.h linux-4.9/fs/aufs/cpup.h
+--- linux-4.9/fs/aufs/cpup.h 2021-02-24 16:25:34.368384227 +0100
++++ linux-4.9/fs/aufs/cpup.h 2021-02-24 16:25:46.172095883 +0100
+@@ -30,7 +30,7 @@ struct inode;
struct file;
struct au_pin;
-aufs4.9 kbuild patch
-
-diff --git a/fs/Kconfig b/fs/Kconfig
-index 4bd03a2..620e01b 100644
---- a/fs/Kconfig
-+++ b/fs/Kconfig
-@@ -249,6 +249,7 @@ source "fs/pstore/Kconfig"
- source "fs/sysv/Kconfig"
- source "fs/ufs/Kconfig"
- source "fs/exofs/Kconfig"
-+source "fs/aufs/Kconfig"
-
- endif # MISC_FILESYSTEMS
-
-diff --git a/fs/Makefile b/fs/Makefile
-index ed2b632..aa6d14b 100644
---- a/fs/Makefile
-+++ b/fs/Makefile
-@@ -129,3 +129,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 cd2be1c..78f3c68 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
-aufs4.9 base patch
-
-diff --git a/MAINTAINERS b/MAINTAINERS
-index 63cefa6..d78b954 100644
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -2293,6 +2293,19 @@ F: include/linux/audit.h
- F: include/uapi/linux/audit.h
- F: kernel/audit*
-
-+AUFS (advanced multi layered unification filesystem) FILESYSTEM
-+M: "J. R. Okajima" <hooanon05g@gmail.com>
-+L: linux-unionfs@vger.kernel.org
-+L: aufs-users@lists.sourceforge.net (members only)
-+W: http://aufs.sourceforge.net
-+T: git://github.com/sfjro/aufs4-linux.git
-+S: Supported
-+F: Documentation/filesystems/aufs/
-+F: Documentation/ABI/testing/debugfs-aufs
-+F: Documentation/ABI/testing/sysfs-aufs
-+F: fs/aufs/
-+F: include/uapi/linux/aufs_type.h
-+
- AUXILIARY DISPLAY DRIVERS
- M: Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>
- W: http://miguelojeda.es/auxdisplay.htm
-diff --git a/drivers/block/loop.c b/drivers/block/loop.c
-index fa1b7a9..6ee9235 100644
---- a/drivers/block/loop.c
-+++ b/drivers/block/loop.c
-@@ -701,6 +701,24 @@ static inline int is_loop_device(struct file *file)
- return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR;
- }
-
-+/*
-+ * for AUFS
-+ * no get/put for file.
-+ */
-+struct file *loop_backing_file(struct super_block *sb)
-+{
-+ struct file *ret;
-+ struct loop_device *l;
-+
-+ ret = NULL;
-+ if (MAJOR(sb->s_dev) == LOOP_MAJOR) {
-+ l = sb->s_bdev->bd_disk->private_data;
-+ ret = l->lo_backing_file;
-+ }
-+ return ret;
-+}
-+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 5c7cc95..df0268c 100644
---- a/fs/dcache.c
-+++ b/fs/dcache.c
-@@ -1164,7 +1164,7 @@ enum d_walk_ret {
- *
- * The @enter() and @finish() 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 *))
- {
-diff --git a/fs/fcntl.c b/fs/fcntl.c
-index 350a2c8..6f42279 100644
---- a/fs/fcntl.c
-+++ b/fs/fcntl.c
-@@ -29,7 +29,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;
-@@ -60,6 +60,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 88110fd..9a9ba3a 100644
---- a/fs/inode.c
-+++ b/fs/inode.c
-@@ -1642,7 +1642,7 @@ EXPORT_SYMBOL(generic_update_time);
- * This does the actual work of updating an inodes time or version. Must have
- * had called mnt_want_write() before calling this.
- */
--static int update_time(struct inode *inode, struct timespec *time, int flags)
-+int update_time(struct inode *inode, struct timespec *time, int flags)
- {
- int (*update_time)(struct inode *, struct timespec *, int);
-
-diff --git a/fs/namespace.c b/fs/namespace.c
-index e6c234b..db0b1ac 100644
---- a/fs/namespace.c
-+++ b/fs/namespace.c
-@@ -787,6 +787,12 @@ static inline int check_mnt(struct mount *mnt)
- return mnt->mnt_ns == current->nsproxy->mnt_ns;
- }
-
-+/* for aufs, CONFIG_AUFS_BR_FUSE */
-+int is_current_mnt_ns(struct vfsmount *mnt)
-+{
-+ return check_mnt(real_mount(mnt));
-+}
-+
- /*
- * vfsmount lock must be held for write
- */
-diff --git a/fs/read_write.c b/fs/read_write.c
-index 190e0d36..4052813 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -515,6 +515,28 @@ ssize_t __vfs_write(struct file *file, const char __user *p, size_t count,
- }
- EXPORT_SYMBOL(__vfs_write);
-
-+vfs_readf_t vfs_readf(struct file *file)
-+{
-+ const struct file_operations *fop = file->f_op;
-+
-+ if (fop->read)
-+ return fop->read;
-+ if (fop->read_iter)
-+ return new_sync_read;
-+ return ERR_PTR(-ENOSYS);
-+}
-+
-+vfs_writef_t vfs_writef(struct file *file)
-+{
-+ const struct file_operations *fop = file->f_op;
-+
-+ if (fop->write)
-+ return fop->write;
-+ if (fop->write_iter)
-+ return new_sync_write;
-+ return ERR_PTR(-ENOSYS);
-+}
-+
- ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos)
- {
- mm_segment_t old_fs;
-diff --git a/fs/splice.c b/fs/splice.c
-index 5a7750b..28160a7 100644
---- a/fs/splice.c
-+++ b/fs/splice.c
-@@ -855,8 +855,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
- /*
- * Attempt to initiate a splice from pipe to file.
- */
--static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
-- loff_t *ppos, size_t len, unsigned int flags)
-+long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
-+ loff_t *ppos, size_t len, unsigned int flags)
- {
- ssize_t (*splice_write)(struct pipe_inode_info *, struct file *,
- loff_t *, size_t, unsigned int);
-@@ -872,9 +872,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
- /*
- * Attempt to initiate a splice from a file to a pipe.
- */
--static long do_splice_to(struct file *in, loff_t *ppos,
-- struct pipe_inode_info *pipe, size_t len,
-- unsigned int flags)
-+long do_splice_to(struct file *in, loff_t *ppos,
-+ struct pipe_inode_info *pipe, size_t len,
-+ unsigned int flags)
- {
- ssize_t (*splice_read)(struct file *, loff_t *,
- struct pipe_inode_info *, size_t, unsigned int);
-diff --git a/fs/sync.c b/fs/sync.c
-index 2a54c1f..7a5fa3f 100644
---- a/fs/sync.c
-+++ b/fs/sync.c
-@@ -27,7 +27,7 @@
- * wait == 1 case since in that case write_inode() functions do
- * sync_dirty_buffer() and thus effectively write one block at a time.
- */
--static int __sync_filesystem(struct super_block *sb, int wait)
-+int __sync_filesystem(struct super_block *sb, int wait)
- {
- if (wait)
- sync_inodes_sb(sb);
-diff --git a/include/linux/file.h b/include/linux/file.h
-index 7444f5f..bdac0be 100644
---- a/include/linux/file.h
-+++ 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/include/linux/fs.h b/include/linux/fs.h
-index dc0478c..a02be40d 100644
---- a/include/linux/fs.h
-+++ b/include/linux/fs.h
-@@ -1291,6 +1291,7 @@ extern void fasync_free(struct fasync_struct *);
- /* can be called from interrupts */
- extern void kill_fasync(struct fasync_struct **, int, int);
-
-+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 void f_setown(struct file *filp, unsigned long arg, int force);
- extern void f_delown(struct file *filp);
-@@ -1715,6 +1716,7 @@ struct file_operations {
- ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
- unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
- int (*check_flags)(int);
-+ 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);
-@@ -1768,6 +1770,12 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
- struct iovec *fast_pointer,
- struct iovec **ret_pointer);
-
-+typedef ssize_t (*vfs_readf_t)(struct file *, char __user *, size_t, loff_t *);
-+typedef ssize_t (*vfs_writef_t)(struct file *, const char __user *, size_t,
-+ loff_t *);
-+vfs_readf_t vfs_readf(struct file *file);
-+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 *);
-@@ -2140,6 +2148,7 @@ extern int current_umask(void);
- extern void ihold(struct inode * inode);
- extern void iput(struct inode *);
- extern int generic_update_time(struct inode *, struct timespec *, int);
-+extern int update_time(struct inode *, struct timespec *, int);
-
- /* /sys/fs */
- extern struct kobject *fs_kobj;
-@@ -2419,6 +2428,7 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb)
- return false;
- }
- #endif
-+extern int __sync_filesystem(struct super_block *, int);
- extern int sync_filesystem(struct super_block *);
- extern const struct file_operations def_blk_fops;
- extern const struct file_operations def_chr_fops;
-diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h
-index 12b2ab5..8b810d1 100644
---- a/include/linux/mnt_namespace.h
-+++ b/include/linux/mnt_namespace.h
-@@ -5,11 +5,14 @@
- struct mnt_namespace;
- struct fs_struct;
- struct user_namespace;
-+struct vfsmount;
-
- extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
- struct user_namespace *, struct fs_struct *);
- extern void put_mnt_ns(struct mnt_namespace *ns);
-
-+extern int is_current_mnt_ns(struct vfsmount *mnt);
-+
- extern const struct file_operations proc_mounts_operations;
- extern const struct file_operations proc_mountinfo_operations;
- extern const struct file_operations proc_mountstats_operations;
-diff --git a/include/linux/splice.h b/include/linux/splice.h
-index 00a2116..1f0a4a2 100644
---- a/include/linux/splice.h
-+++ b/include/linux/splice.h
-@@ -86,4 +86,10 @@ extern void spd_release_page(struct splice_pipe_desc *, unsigned int);
-
- extern const struct pipe_buf_operations page_cache_pipe_buf_ops;
- extern const struct pipe_buf_operations default_pipe_buf_ops;
-+
-+extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
-+ loff_t *ppos, size_t len, unsigned int flags);
-+extern long do_splice_to(struct file *in, loff_t *ppos,
-+ struct pipe_inode_info *pipe, size_t len,
-+ unsigned int flags);
- #endif
-aufs4.9 mmap patch
-
-diff --git a/fs/proc/base.c b/fs/proc/base.c
-index ca651ac..0e8551a 100644
---- a/fs/proc/base.c
-+++ b/fs/proc/base.c
-@@ -1953,7 +1953,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path)
- down_read(&mm->mmap_sem);
- vma = find_exact_vma(mm, vm_start, vm_end);
- if (vma && vma->vm_file) {
-- *path = vma->vm_file->f_path;
-+ *path = vma_pr_or_file(vma)->f_path;
- path_get(path);
- rc = 0;
- }
-diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c
-index f8595e8..cb8eda0 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)
- file = region->vm_file;
-
- if (file) {
-- struct inode *inode = file_inode(region->vm_file);
-+ struct inode *inode;
-+
-+ file = vmr_pr_or_file(region);
-+ inode = file_inode(file);
- dev = inode->i_sb->s_dev;
- ino = inode->i_ino;
- }
-diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
-index 35b92d8..5b981db 100644
---- a/fs/proc/task_mmu.c
-+++ b/fs/proc/task_mmu.c
-@@ -291,7 +291,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
- const char *name = NULL;
-
- if (file) {
-- struct inode *inode = file_inode(vma->vm_file);
-+ struct inode *inode;
-+
-+ file = vma_pr_or_file(vma);
-+ inode = file_inode(file);
- dev = inode->i_sb->s_dev;
- ino = inode->i_ino;
- pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
-@@ -1627,7 +1630,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
- struct proc_maps_private *proc_priv = &numa_priv->proc_maps;
- struct vm_area_struct *vma = v;
- struct numa_maps *md = &numa_priv->md;
-- struct file *file = vma->vm_file;
-+ struct file *file = vma_pr_or_file(vma);
- struct mm_struct *mm = vma->vm_mm;
- struct mm_walk walk = {
- .hugetlb_entry = gather_hugetlb_stats,
-diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
-index 3717562..6a328f1 100644
---- a/fs/proc/task_nommu.c
-+++ b/fs/proc/task_nommu.c
-@@ -155,7 +155,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
- file = vma->vm_file;
-
- if (file) {
-- struct inode *inode = file_inode(vma->vm_file);
-+ struct inode *inode;
-+
-+ file = vma_pr_or_file(vma);
-+ inode = file_inode(file);
- dev = inode->i_sb->s_dev;
- 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 a92c8d7..1d83a2a 100644
---- a/include/linux/mm.h
-+++ b/include/linux/mm.h
-@@ -1266,6 +1266,28 @@ static inline int fixup_user_fault(struct task_struct *tsk,
- }
- #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[],
-+ int);
-+extern void vma_do_get_file(struct vm_area_struct *, const char[], int);
-+extern void vma_do_fput(struct vm_area_struct *, const char[], int);
-+
-+#define vma_file_update_time(vma) vma_do_file_update_time(vma, __func__, \
-+ __LINE__)
-+#define vma_pr_or_file(vma) vma_do_pr_or_file(vma, __func__, \
-+ __LINE__)
-+#define vma_get_file(vma) vma_do_get_file(vma, __func__, __LINE__)
-+#define vma_fput(vma) vma_do_fput(vma, __func__, __LINE__)
-+
-+#ifndef CONFIG_MMU
-+extern struct file *vmr_do_pr_or_file(struct vm_region *, const char[], int);
-+extern void vmr_do_fput(struct vm_region *, const char[], int);
-+
-+#define vmr_pr_or_file(region) vmr_do_pr_or_file(region, __func__, \
-+ __LINE__)
-+#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,
- unsigned int gup_flags);
- extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
-diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
-index 4a8aced..badd16b 100644
---- a/include/linux/mm_types.h
-+++ b/include/linux/mm_types.h
-@@ -275,6 +275,7 @@ struct vm_region {
- unsigned long vm_top; /* region allocated to here */
- unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */
- struct file *vm_file; /* the backing file or NULL */
-+ struct file *vm_prfile; /* the virtual backing file or NULL */
-
- int vm_usage; /* region usage count (access under nommu_region_sem) */
- bool vm_icache_flushed : 1; /* true if the icache has been flushed for
-@@ -349,6 +350,7 @@ struct vm_area_struct {
- unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_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
-diff --git a/kernel/fork.c b/kernel/fork.c
-index 997ac1d..4d0131b 100644
---- a/kernel/fork.c
-+++ b/kernel/fork.c
-@@ -624,7 +624,7 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm,
- struct inode *inode = file_inode(file);
- struct address_space *mapping = file->f_mapping;
-
-- get_file(file);
-+ vma_get_file(tmp);
- if (tmp->vm_flags & VM_DENYWRITE)
- atomic_dec(&inode->i_writecount);
- i_mmap_lock_write(mapping);
-diff --git a/mm/Makefile b/mm/Makefile
-index 295bd7a..14fa1c8 100644
---- a/mm/Makefile
-+++ b/mm/Makefile
-@@ -37,7 +37,7 @@ obj-y := filemap.o mempool.o oom_kill.o \
- mm_init.o mmu_context.o percpu.o slab_common.o \
- compaction.o vmacache.o \
- interval_tree.o list_lru.o workingset.o \
-- debug.o $(mmu-y)
-+ prfile.o debug.o $(mmu-y)
-
- obj-y += init-mm.o
-
-diff --git a/mm/filemap.c b/mm/filemap.c
-index 50b52fe..9e607f9 100644
---- a/mm/filemap.c
-+++ b/mm/filemap.c
-@@ -2304,7 +2304,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
- int ret = VM_FAULT_LOCKED;
-
- sb_start_pagefault(inode->i_sb);
-- file_update_time(vma->vm_file);
-+ vma_file_update_time(vma);
- lock_page(page);
- if (page->mapping != inode->i_mapping) {
- unlock_page(page);
-diff --git a/mm/memory.c b/mm/memory.c
-index e18c57b..7be4a39 100644
---- a/mm/memory.c
-+++ b/mm/memory.c
-@@ -2117,7 +2117,7 @@ static inline int wp_page_reuse(struct fault_env *fe, pte_t orig_pte,
- }
-
- 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 1af87c1..95b0ff4 100644
---- a/mm/mmap.c
-+++ b/mm/mmap.c
-@@ -170,7 +170,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma)
- if (vma->vm_ops && vma->vm_ops->close)
- vma->vm_ops->close(vma);
- if (vma->vm_file)
-- fput(vma->vm_file);
-+ vma_fput(vma);
- mpol_put(vma_policy(vma));
- kmem_cache_free(vm_area_cachep, vma);
- return next;
-@@ -879,7 +879,7 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start,
- if (remove_next) {
- if (file) {
- uprobe_munmap(next, next->vm_start, next->vm_end);
-- fput(file);
-+ vma_fput(vma);
- }
- if (next->anon_vma)
- anon_vma_merge(vma, next);
-@@ -1727,8 +1727,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
- return addr;
-
- unmap_and_free_vma:
-+ vma_fput(vma);
- vma->vm_file = NULL;
-- fput(file);
-
- /* Undo any partial mapping done by a device driver. */
- unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
-@@ -2533,7 +2533,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
- goto out_free_mpol;
-
- if (new->vm_file)
-- get_file(new->vm_file);
-+ vma_get_file(new);
-
- if (new->vm_ops && new->vm_ops->open)
- new->vm_ops->open(new);
-@@ -2552,7 +2552,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
- if (new->vm_ops && new->vm_ops->close)
- new->vm_ops->close(new);
- if (new->vm_file)
-- fput(new->vm_file);
-+ vma_fput(new);
- unlink_anon_vmas(new);
- out_free_mpol:
- mpol_put(vma_policy(new));
-@@ -2703,7 +2703,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
- struct vm_area_struct *vma;
- unsigned long populate = 0;
- unsigned long ret = -EINVAL;
-- 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",
- current->comm, current->pid);
-@@ -2778,10 +2778,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);
-+ 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)
-@@ -3056,7 +3073,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
- if (anon_vma_clone(new_vma, vma))
- goto out_free_mempol;
- if (new_vma->vm_file)
-- get_file(new_vma->vm_file);
-+ vma_get_file(new_vma);
- if (new_vma->vm_ops && new_vma->vm_ops->open)
- 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 8b8faaf..5d26ed94 100644
---- a/mm/nommu.c
-+++ b/mm/nommu.c
-@@ -636,7 +636,7 @@ static void __put_nommu_region(struct vm_region *region)
- up_write(&nommu_region_sem);
-
- if (region->vm_file)
-- fput(region->vm_file);
-+ vmr_fput(region);
-
- /* IO memory and memory shared directly out of the pagecache
- * from ramfs/tmpfs mustn't be released here */
-@@ -794,7 +794,7 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma)
- if (vma->vm_ops && vma->vm_ops->close)
- vma->vm_ops->close(vma);
- if (vma->vm_file)
-- fput(vma->vm_file);
-+ vma_fput(vma);
- put_nommu_region(vma->vm_region);
- kmem_cache_free(vm_area_cachep, vma);
- }
-@@ -1320,7 +1320,7 @@ unsigned long do_mmap(struct file *file,
- goto error_just_free;
- }
- }
-- fput(region->vm_file);
-+ vmr_fput(region);
- kmem_cache_free(vm_region_jar, region);
- region = pregion;
- result = start;
-@@ -1395,10 +1395,10 @@ unsigned long do_mmap(struct file *file,
- up_write(&nommu_region_sem);
- error:
- if (region->vm_file)
-- fput(region->vm_file);
-+ 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;
-
-diff --git a/mm/prfile.c b/mm/prfile.c
-new file mode 100644
-index 0000000..86e01bd
---- /dev/null
-+++ b/mm/prfile.c
-@@ -0,0 +1,85 @@
-+/*
-+ * 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-2018 Junjro R. Okajima
-+ * Copyright (c) 2014 Ian Campbell
-+ */
-+
-+#include <linux/mm.h>
-+#include <linux/file.h>
-+#include <linux/fs.h>
-+
-+/* #define PRFILE_TRACE */
-+static inline void prfile_trace(struct file *f, struct file *pr,
-+ const char func[], int line, const char func2[])
-+{
-+#ifdef PRFILE_TRACE
-+ if (pr)
-+ pr_info("%s:%d: %s, %pD2\n", func, line, func2, f);
-+#endif
-+}
-+
-+void vma_do_file_update_time(struct vm_area_struct *vma, const char func[],
-+ int line)
-+{
-+ struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+
-+ prfile_trace(f, pr, func, line, __func__);
-+ file_update_time(f);
-+ if (f && pr)
-+ file_update_time(pr);
-+}
-+
-+struct file *vma_do_pr_or_file(struct vm_area_struct *vma, const char func[],
-+ int line)
-+{
-+ struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+
-+ prfile_trace(f, pr, func, line, __func__);
-+ return (f && pr) ? pr : f;
-+}
-+
-+void vma_do_get_file(struct vm_area_struct *vma, const char func[], int line)
-+{
-+ struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+
-+ prfile_trace(f, pr, func, line, __func__);
-+ get_file(f);
-+ if (f && pr)
-+ get_file(pr);
-+}
-+
-+void vma_do_fput(struct vm_area_struct *vma, const char func[], int line)
-+{
-+ struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+
-+ prfile_trace(f, pr, func, line, __func__);
-+ fput(f);
-+ if (f && pr)
-+ fput(pr);
-+}
-+
-+#ifndef CONFIG_MMU
-+struct file *vmr_do_pr_or_file(struct vm_region *region, const char func[],
-+ int line)
-+{
-+ struct file *f = region->vm_file, *pr = region->vm_prfile;
-+
-+ prfile_trace(f, pr, func, line, __func__);
-+ return (f && pr) ? pr : f;
-+}
-+
-+void vmr_do_fput(struct vm_region *region, const char func[], int line)
-+{
-+ struct file *f = region->vm_file, *pr = region->vm_prfile;
-+
-+ prfile_trace(f, pr, func, line, __func__);
-+ fput(f);
-+ if (f && pr)
-+ fput(pr);
-+}
-+#endif /* !CONFIG_MMU */
-aufs4.9 standalone patch
-
-diff --git a/fs/dcache.c b/fs/dcache.c
-index df0268c..755fea1 100644
---- a/fs/dcache.c
-+++ b/fs/dcache.c
-@@ -1272,6 +1272,7 @@ void d_walk(struct dentry *parent, void *data,
- seq = 1;
- goto again;
- }
-+EXPORT_SYMBOL_GPL(d_walk);
-
- /*
- * Search for at least 1 mount point in the dentry's subdirs.
-@@ -2855,6 +2856,7 @@ void d_exchange(struct dentry *dentry1, struct dentry *dentry2)
-
- write_sequnlock(&rename_lock);
- }
-+EXPORT_SYMBOL_GPL(d_exchange);
-
- /**
- * d_ancestor - search for an ancestor
-diff --git a/fs/exec.c b/fs/exec.c
-index 4e497b9..e27d323 100644
---- a/fs/exec.c
-+++ b/fs/exec.c
-@@ -104,6 +104,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_GPL(path_noexec);
-
- #ifdef CONFIG_USELIB
- /*
-diff --git a/fs/fcntl.c b/fs/fcntl.c
-index 6f42279..04fd33c 100644
---- a/fs/fcntl.c
-+++ b/fs/fcntl.c
-@@ -82,6 +82,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..ae9f267 100644
---- a/fs/file_table.c
-+++ b/fs/file_table.c
-@@ -147,6 +147,7 @@ struct file *get_empty_filp(void)
- }
- return ERR_PTR(-ENFILE);
- }
-+EXPORT_SYMBOL_GPL(get_empty_filp);
-
- /**
- * alloc_file - allocate and initialize a 'struct file'
-@@ -258,6 +259,7 @@ void flush_delayed_fput(void)
- {
- delayed_fput(NULL);
- }
-+EXPORT_SYMBOL_GPL(flush_delayed_fput);
-
- static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput);
-
-@@ -300,6 +302,7 @@ void __fput_sync(struct file *file)
- }
-
- EXPORT_SYMBOL(fput);
-+EXPORT_SYMBOL_GPL(__fput_sync);
-
- void put_filp(struct file *file)
- {
-@@ -308,6 +311,7 @@ void put_filp(struct file *file)
- file_free(file);
- }
- }
-+EXPORT_SYMBOL_GPL(put_filp);
-
- void __init files_init(void)
- {
-diff --git a/fs/inode.c b/fs/inode.c
-index 9a9ba3a..a3a18d83 100644
---- a/fs/inode.c
-+++ b/fs/inode.c
-@@ -1651,6 +1651,7 @@ int update_time(struct inode *inode, struct timespec *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 db0b1ac..2d1e8ff 100644
---- a/fs/namespace.c
-+++ b/fs/namespace.c
-@@ -466,6 +466,7 @@ void __mnt_drop_write(struct vfsmount *mnt)
- mnt_dec_writers(real_mount(mnt));
- preempt_enable();
- }
-+EXPORT_SYMBOL_GPL(__mnt_drop_write);
-
- /**
- * mnt_drop_write - give up write access to a mount
-@@ -792,6 +793,7 @@ int is_current_mnt_ns(struct vfsmount *mnt)
- {
- return check_mnt(real_mount(mnt));
- }
-+EXPORT_SYMBOL_GPL(is_current_mnt_ns);
-
- /*
- * vfsmount lock must be held for write
-@@ -1829,6 +1831,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
- }
- return 0;
- }
-+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 fbe3cbe..bdfc61e 100644
---- a/fs/notify/group.c
-+++ b/fs/notify/group.c
-@@ -22,6 +22,7 @@
- #include <linux/srcu.h>
- #include <linux/rculist.h>
- #include <linux/wait.h>
-+#include <linux/module.h>
-
- #include <linux/fsnotify_backend.h>
- #include "fsnotify.h"
-@@ -100,6 +101,7 @@ void fsnotify_get_group(struct fsnotify_group *group)
- {
- atomic_inc(&group->refcnt);
- }
-+EXPORT_SYMBOL_GPL(fsnotify_get_group);
-
- /*
- * Drop a reference to a group. Free it if it's through.
-@@ -109,6 +111,7 @@ void fsnotify_put_group(struct fsnotify_group *group)
- if (atomic_dec_and_test(&group->refcnt))
- fsnotify_final_destroy_group(group);
- }
-+EXPORT_SYMBOL_GPL(fsnotify_put_group);
-
- /*
- * Create a new fsnotify_group and hold a reference for the group returned.
-@@ -137,6 +140,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops)
-
- return 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 d3fea0b..5fc06ad 100644
---- a/fs/notify/mark.c
-+++ b/fs/notify/mark.c
-@@ -113,6 +113,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
- mark->free_mark(mark);
- }
- }
-+EXPORT_SYMBOL_GPL(fsnotify_put_mark);
-
- /* Calculate mask of events for a list of marks */
- u32 fsnotify_recalc_mask(struct hlist_head *head)
-@@ -230,6 +231,7 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark,
- mutex_unlock(&group->mark_mutex);
- fsnotify_free_mark(mark);
- }
-+EXPORT_SYMBOL_GPL(fsnotify_destroy_mark);
-
- void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock)
- {
-@@ -415,6 +417,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
-
- return ret;
- }
-+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)
-@@ -533,6 +536,7 @@ void fsnotify_init_mark(struct fsnotify_mark *mark,
- atomic_set(&mark->refcnt, 1);
- mark->free_mark = free_mark;
- }
-+EXPORT_SYMBOL_GPL(fsnotify_init_mark);
-
- /*
- * Destroy all marks in destroy_list, waits for SRCU period to finish before
-diff --git a/fs/open.c b/fs/open.c
-index d3ed817..20d2494 100644
---- a/fs/open.c
-+++ b/fs/open.c
-@@ -64,6 +64,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
- inode_unlock(dentry->d_inode);
- return ret;
- }
-+EXPORT_SYMBOL_GPL(do_truncate);
-
- long vfs_truncate(const struct path *path, loff_t length)
- {
-@@ -695,6 +696,7 @@ int open_check_o_direct(struct file *f)
- }
- return 0;
- }
-+EXPORT_SYMBOL_GPL(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 4052813..7dfd732 100644
---- a/fs/read_write.c
-+++ b/fs/read_write.c
-@@ -525,6 +525,7 @@ vfs_readf_t vfs_readf(struct file *file)
- return new_sync_read;
- return ERR_PTR(-ENOSYS);
- }
-+EXPORT_SYMBOL_GPL(vfs_readf);
-
- vfs_writef_t vfs_writef(struct file *file)
- {
-@@ -536,6 +537,7 @@ vfs_writef_t vfs_writef(struct file *file)
- return new_sync_write;
- return ERR_PTR(-ENOSYS);
- }
-+EXPORT_SYMBOL_GPL(vfs_writef);
-
- ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos)
- {
-diff --git a/fs/splice.c b/fs/splice.c
-index 28160a7..98c1902 100644
---- a/fs/splice.c
-+++ b/fs/splice.c
-@@ -868,6 +868,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
-
- return splice_write(pipe, out, ppos, len, flags);
- }
-+EXPORT_SYMBOL_GPL(do_splice_from);
-
- /*
- * Attempt to initiate a splice from a file to a pipe.
-@@ -897,6 +898,7 @@ long do_splice_to(struct file *in, loff_t *ppos,
-
- return splice_read(in, ppos, pipe, len, flags);
- }
-+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 7a5fa3f..c9b9d46 100644
---- a/fs/sync.c
-+++ b/fs/sync.c
-@@ -38,6 +38,7 @@ int __sync_filesystem(struct super_block *sb, int wait)
- sb->s_op->sync_fs(sb, wait);
- return __sync_blockdev(sb->s_bdev, wait);
- }
-+EXPORT_SYMBOL_GPL(__sync_filesystem);
-
- /*
- * Write out and wait upon all dirty data associated with this
-diff --git a/fs/xattr.c b/fs/xattr.c
-index 2d13b4e..41c2bcd 100644
---- a/fs/xattr.c
-+++ b/fs/xattr.c
-@@ -296,6 +296,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
- *xattr_value = value;
- return error;
- }
-+EXPORT_SYMBOL_GPL(vfs_getxattr_alloc);
-
- ssize_t
- __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name,
-diff --git a/kernel/task_work.c b/kernel/task_work.c
-index d513051..e056d54 100644
---- a/kernel/task_work.c
-+++ b/kernel/task_work.c
-@@ -119,3 +119,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 8df676f..6b5cc07 100644
---- a/security/commoncap.c
-+++ b/security/commoncap.c
-@@ -1061,12 +1061,14 @@ int cap_mmap_addr(unsigned long addr)
- }
- return ret;
- }
-+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_GPL(cap_mmap_file);
-
- #ifdef CONFIG_SECURITY
-
-diff --git a/security/device_cgroup.c b/security/device_cgroup.c
-index 03c1652..f88c84b 100644
---- a/security/device_cgroup.c
-+++ b/security/device_cgroup.c
-@@ -7,6 +7,7 @@
- #include <linux/device_cgroup.h>
- #include <linux/cgroup.h>
- #include <linux/ctype.h>
-+#include <linux/export.h>
- #include <linux/list.h>
- #include <linux/uaccess.h>
- #include <linux/seq_file.h>
-@@ -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_GPL(__devcgroup_inode_permission);
-
- int devcgroup_inode_mknod(int mode, dev_t dev)
- {
-diff --git a/security/security.c b/security/security.c
-index f825304..8dd441d 100644
---- a/security/security.c
-+++ b/security/security.c
-@@ -443,6 +443,7 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry)
- return 0;
- return call_int_hook(path_rmdir, 0, dir, dentry);
- }
-+EXPORT_SYMBOL_GPL(security_path_rmdir);
-
- int security_path_unlink(const struct path *dir, struct dentry *dentry)
- {
-@@ -459,6 +460,7 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry,
- return 0;
- return call_int_hook(path_symlink, 0, dir, dentry, old_name);
- }
-+EXPORT_SYMBOL_GPL(security_path_symlink);
-
- int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
- struct dentry *new_dentry)
-@@ -467,6 +469,7 @@ int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
- return 0;
- return call_int_hook(path_link, 0, old_dentry, new_dir, new_dentry);
- }
-+EXPORT_SYMBOL_GPL(security_path_link);
-
- int security_path_rename(const struct path *old_dir, struct dentry *old_dentry,
- const struct path *new_dir, struct dentry *new_dentry,
-@@ -494,6 +497,7 @@ int security_path_truncate(const struct path *path)
- return 0;
- return call_int_hook(path_truncate, 0, path);
- }
-+EXPORT_SYMBOL_GPL(security_path_truncate);
-
- int security_path_chmod(const struct path *path, umode_t mode)
- {
-@@ -501,6 +505,7 @@ int security_path_chmod(const struct path *path, umode_t mode)
- return 0;
- return call_int_hook(path_chmod, 0, path, mode);
- }
-+EXPORT_SYMBOL_GPL(security_path_chmod);
-
- int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
- {
-@@ -508,6 +513,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
- return 0;
- return call_int_hook(path_chown, 0, path, uid, gid);
- }
-+EXPORT_SYMBOL_GPL(security_path_chown);
-
- int security_path_chroot(const struct path *path)
- {
-@@ -593,6 +599,7 @@ int security_inode_readlink(struct dentry *dentry)
- return 0;
- return call_int_hook(inode_readlink, 0, dentry);
- }
-+EXPORT_SYMBOL_GPL(security_inode_readlink);
-
- int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
- bool rcu)
-@@ -608,6 +615,7 @@ int security_inode_permission(struct inode *inode, int mask)
- return 0;
- return call_int_hook(inode_permission, 0, inode, mask);
- }
-+EXPORT_SYMBOL_GPL(security_inode_permission);
-
- int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
- {
-@@ -779,6 +787,7 @@ int security_file_permission(struct file *file, int mask)
-
- return fsnotify_perm(file, mask);
- }
-+EXPORT_SYMBOL_GPL(security_file_permission);
-
- int security_file_alloc(struct file *file)
- {
-@@ -838,6 +847,7 @@ int security_mmap_file(struct file *file, unsigned long prot,
- return ret;
- return ima_file_mmap(file, prot);
- }
-+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 2017-07-29 12:14:25.893041746 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/ABI/testing/debugfs-aufs linux-4.9/Documentation/ABI/testing/debugfs-aufs
+--- linux-4.9/Documentation/ABI/testing/debugfs-aufs 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/ABI/testing/debugfs-aufs 2021-02-24 16:15:09.518240088 +0100
@@ -0,0 +1,50 @@
+What: /debug/aufs/si_<id>/
+Date: March 2009
+ be created.
+ When the aufs mount option 'noxino' is specified, it
+ 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 2017-07-29 12:14:25.893041746 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/ABI/testing/sysfs-aufs linux-4.9/Documentation/ABI/testing/sysfs-aufs
+--- linux-4.9/Documentation/ABI/testing/sysfs-aufs 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/ABI/testing/sysfs-aufs 2021-02-24 16:15:09.518240088 +0100
@@ -0,0 +1,31 @@
+What: /sys/fs/aufs/si_<id>/
+Date: March 2009
+ even if it is the default path.
+ When the aufs mount option 'noxino' is specified, it
+ 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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/README linux-4.9/Documentation/filesystems/aufs/README
+--- linux-4.9/Documentation/filesystems/aufs/README 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/README 2021-02-24 16:15:09.521573529 +0100
+@@ -0,0 +1,393 @@
++
++Aufs4 -- advanced multi layered unification filesystem version 4.x
++http://aufs.sf.net
++Junjiro R. Okajima
++
++
++0. Introduction
++----------------------------------------
++In the early days, aufs was entirely re-designed and re-implemented
++Unionfs Version 1.x series. Adding many original ideas, approaches,
++improvements and implementations, it becomes totally different from
++Unionfs while keeping the basic features.
++Recently, Unionfs Version 2.x series begin taking some of the same
++approaches to aufs1's.
++Unionfs is being developed by Professor Erez Zadok at Stony Brook
++University and his team.
++
++Aufs4 supports linux-4.0 and later, and for linux-3.x series try aufs3.
++If you want older kernel version support, try aufs2-2.6.git or
++aufs2-standalone.git repository, aufs1 from CVS on SourceForge.
++
++Note: it becomes clear that "Aufs was rejected. Let's give it up."
++ According to Christoph Hellwig, linux rejects all union-type
++ filesystems but UnionMount.
++<http://marc.info/?l=linux-kernel&m=123938533724484&w=2>
++
++PS. Al Viro seems have a plan to merge aufs as well as overlayfs and
++ UnionMount, and he pointed out an issue around a directory mutex
++ lock and aufs addressed it. But it is still unsure whether aufs will
++ be merged (or any other union solution).
++<http://marc.info/?l=linux-kernel&m=136312705029295&w=1>
++
++
++1. Features
++----------------------------------------
++- unite several directories into a single virtual filesystem. The member
++ directory is called as a branch.
++- you can specify the permission flags to the branch, which are 'readonly',
++ 'readwrite' and 'whiteout-able.'
++- by upper writable branch, internal copyup and whiteout, files/dirs on
++ readonly branch are modifiable logically.
++- dynamic branch manipulation, add, del.
++- etc...
++
++Also there are many enhancements in aufs, such as:
++- test only the highest one for the directory permission (dirperm1)
++- copyup on open (coo=)
++- 'move' policy for copy-up between two writable branches, after
++ checking free space.
++- xattr, acl
++- readdir(3) in userspace.
++- keep inode number by external inode number table
++- keep the timestamps of file/dir in internal copyup operation
++- seekable directory, supporting NFS readdir.
++- whiteout is hardlinked in order to reduce the consumption of inodes
++ on branch
++- do not copyup, nor create a whiteout when it is unnecessary
++- revert a single systemcall when an error occurs in aufs
++- remount interface instead of ioctl
++- maintain /etc/mtab by an external command, /sbin/mount.aufs.
++- loopback mounted filesystem as a branch
++- kernel thread for removing the dir who has a plenty of whiteouts
++- support copyup sparse file (a file which has a 'hole' in it)
++- default permission flags for branches
++- selectable permission flags for ro branch, whether whiteout can
++ exist or not
++- export via NFS.
++- support <sysfs>/fs/aufs and <debugfs>/aufs.
++- support multiple writable branches, some policies to select one
++ among multiple writable branches.
++- a new semantics for link(2) and rename(2) to support multiple
++ writable branches.
++- no glibc changes are required.
++- pseudo hardlink (hardlink over branches)
++- allow a direct access manually to a file on branch, e.g. bypassing aufs.
++ including NFS or remote filesystem branch.
++- userspace wrapper for pathconf(3)/fpathconf(3) with _PC_LINK_MAX.
++- and more...
++
++Currently these features are dropped temporary from aufs4.
++See design/08plan.txt in detail.
++- nested mount, i.e. aufs as readonly no-whiteout branch of another aufs
++ (robr)
++- statistics of aufs thread (/sys/fs/aufs/stat)
++
++Features or just an idea in the future (see also design/*.txt),
++- reorder the branch index without del/re-add.
++- permanent xino files for NFSD
++- an option for refreshing the opened files after add/del branches
++- light version, without branch manipulation. (unnecessary?)
++- copyup in userspace
++- inotify in userspace
++- readv/writev
++
++
++2. Download
++----------------------------------------
++There are three GIT trees for aufs4, aufs4-linux.git,
++aufs4-standalone.git, and aufs-util.git. Note that there is no "4" in
++"aufs-util.git."
++While the aufs-util is always necessary, you need either of aufs4-linux
++or aufs4-standalone.
++
++The aufs4-linux tree includes the whole linux mainline GIT tree,
++git://git.kernel.org/.../torvalds/linux.git.
++And you cannot select CONFIG_AUFS_FS=m for this version, eg. you cannot
++build aufs4 as an external kernel module.
++Several extra patches are not included in this tree. Only
++aufs4-standalone tree contains them. They are described in the later
++section "Configuration and Compilation."
++
++On the other hand, the aufs4-standalone tree has only aufs source files
++and necessary patches, and you can select CONFIG_AUFS_FS=m.
++But you need to apply all aufs patches manually.
++
++You will find GIT branches whose name is in form of "aufs4.x" where "x"
++represents the linux kernel version, "linux-4.x". For instance,
++"aufs4.0" is for linux-4.0. For latest "linux-4.x-rcN", use
++"aufs4.x-rcN" branch.
++
++o aufs4-linux tree
++$ git clone --reference /your/linux/git/tree \
++ git://github.com/sfjro/aufs4-linux.git aufs4-linux.git
++- if you don't have linux GIT tree, then remove "--reference ..."
++$ cd aufs4-linux.git
++$ git checkout origin/aufs4.0
++
++Or You may want to directly git-pull aufs into your linux GIT tree, and
++leave the patch-work to GIT.
++$ cd /your/linux/git/tree
++$ git remote add aufs4 git://github.com/sfjro/aufs4-linux.git
++$ git fetch aufs4
++$ git checkout -b my4.0 v4.0
++$ (add your local change...)
++$ git pull aufs4 aufs4.0
++- now you have v4.0 + your_changes + aufs4.0 in you my4.0 branch.
++- you may need to solve some conflicts between your_changes and
++ aufs4.0. in this case, git-rerere is recommended so that you can
++ solve the similar conflicts automatically when you upgrade to 4.1 or
++ later in the future.
++
++o aufs4-standalone tree
++$ git clone git://github.com/sfjro/aufs4-standalone.git aufs4-standalone.git
++$ cd aufs4-standalone.git
++$ git checkout origin/aufs4.0
++
++o aufs-util tree
++$ git clone git://git.code.sf.net/p/aufs/aufs-util aufs-util.git
++- note that the public aufs-util.git is on SourceForge instead of
++ GitHUB.
++$ cd aufs-util.git
++$ git checkout origin/aufs4.0
++
++Note: The 4.x-rcN branch is to be used with `rc' kernel versions ONLY.
++The minor version number, 'x' in '4.x', of aufs may not always
++follow the minor version number of the kernel.
++Because changes in the kernel that cause the use of a new
++minor version number do not always require changes to aufs-util.
++
++Since aufs-util has its own minor version number, you may not be
++able to find a GIT branch in aufs-util for your kernel's
++exact minor version number.
++In this case, you should git-checkout the branch for the
++nearest lower number.
++
++For (an unreleased) example:
++If you are using "linux-4.10" and the "aufs4.10" branch
++does not exist in aufs-util repository, then "aufs4.9", "aufs4.8"
++or something numerically smaller is the branch for your kernel.
++
++Also you can view all branches by
++ $ git branch -a
++
++
++3. Configuration and Compilation
++----------------------------------------
++Make sure you have git-checkout'ed the correct branch.
++
++For aufs4-linux tree,
++- enable CONFIG_AUFS_FS.
++- set other aufs configurations if necessary.
++
++For aufs4-standalone tree,
++There are several ways to build.
++
++1.
++- apply ./aufs4-kbuild.patch to your kernel source files.
++- apply ./aufs4-base.patch too.
++- apply ./aufs4-mmap.patch too.
++- apply ./aufs4-standalone.patch too, if you have a plan to set
++ CONFIG_AUFS_FS=m. otherwise you don't need ./aufs4-standalone.patch.
++- copy ./{Documentation,fs,include/uapi/linux/aufs_type.h} files to your
++ kernel source tree. Never copy $PWD/include/uapi/linux/Kbuild.
++- enable CONFIG_AUFS_FS, you can select either
++ =m or =y.
++- and build your kernel as usual.
++- install the built kernel.
++ Note: Since linux-3.9, every filesystem module requires an alias
++ "fs-<fsname>". You should make sure that "fs-aufs" is listed in your
++ modules.aliases file if you set CONFIG_AUFS_FS=m.
++- install the header files too by "make headers_install" to the
++ directory where you specify. By default, it is $PWD/usr.
++ "make help" shows a brief note for headers_install.
++- and reboot your system.
++
++2.
++- module only (CONFIG_AUFS_FS=m).
++- apply ./aufs4-base.patch to your kernel source files.
++- apply ./aufs4-mmap.patch too.
++- apply ./aufs4-standalone.patch too.
++- build your kernel, don't forget "make headers_install", and reboot.
++- edit ./config.mk and set other aufs configurations if necessary.
++ Note: You should read $PWD/fs/aufs/Kconfig carefully which describes
++ every aufs configurations.
++- build the module by simple "make".
++ Note: Since linux-3.9, every filesystem module requires an alias
++ "fs-<fsname>". You should make sure that "fs-aufs" is listed in your
++ modules.aliases file.
++- you can specify ${KDIR} make variable which points to your kernel
++ source tree.
++- install the files
++ + run "make install" to install the aufs module, or copy the built
++ $PWD/aufs.ko to /lib/modules/... and run depmod -a (or reboot simply).
++ + run "make install_headers" (instead of headers_install) to install
++ the modified aufs header file (you can specify DESTDIR which is
++ available in aufs standalone version's Makefile only), or copy
++ $PWD/usr/include/linux/aufs_type.h to /usr/include/linux or wherever
++ you like manually. By default, the target directory is $PWD/usr.
++- no need to apply aufs4-kbuild.patch, nor copying source files to your
++ kernel source tree.
++
++Note: The header file aufs_type.h is necessary to build aufs-util
++ as well as "make headers_install" in the kernel source tree.
++ headers_install is subject to be forgotten, but it is essentially
++ necessary, not only for building aufs-util.
++ You may not meet problems without headers_install in some older
++ version though.
++
++And then,
++- read README in aufs-util, build and install it
++- note that your distribution may contain an obsoleted version of
++ aufs_type.h in /usr/include/linux or something. When you build aufs
++ utilities, make sure that your compiler refers the correct aufs header
++ file which is built by "make headers_install."
++- if you want to use readdir(3) in userspace or pathconf(3) wrapper,
++ then run "make install_ulib" too. And refer to the aufs manual in
++ detail.
++
++There several other patches in aufs4-standalone.git. They are all
++optional. When you meet some problems, they will help you.
++- aufs4-loopback.patch
++ Supports a nested loopback mount in a branch-fs. This patch is
++ unnecessary until aufs produces a message like "you may want to try
++ another patch for loopback file".
++- vfs-ino.patch
++ Modifies a system global kernel internal function get_next_ino() in
++ order to stop assigning 0 for an inode-number. Not directly related to
++ aufs, but recommended generally.
++- tmpfs-idr.patch
++ Keeps the tmpfs inode number as the lowest value. Effective to reduce
++ the size of aufs XINO files for tmpfs branch. Also it prevents the
++ duplication of inode number, which is important for backup tools and
++ other utilities. When you find aufs XINO files for tmpfs branch
++ growing too much, try this patch.
++- lockdep-debug.patch
++ Because aufs is not only an ordinary filesystem (callee of VFS), but
++ also a caller of VFS functions for branch filesystems, subclassing of
++ the internal locks for LOCKDEP is necessary. LOCKDEP is a debugging
++ feature of linux kernel. If you enable CONFIG_LOCKDEP, then you will
++ need to apply this debug patch to expand several constant values.
++ If don't know what LOCKDEP, then you don't have apply this patch.
++
++
++4. Usage
++----------------------------------------
++At first, make sure aufs-util are installed, and please read the aufs
++manual, aufs.5 in aufs-util.git tree.
++$ man -l aufs.5
++
++And then,
++$ mkdir /tmp/rw /tmp/aufs
++# mount -t aufs -o br=/tmp/rw:${HOME} none /tmp/aufs
++
++Here is another example. The result is equivalent.
++# mount -t aufs -o br=/tmp/rw=rw:${HOME}=ro none /tmp/aufs
++ Or
++# mount -t aufs -o br:/tmp/rw none /tmp/aufs
++# mount -o remount,append:${HOME} /tmp/aufs
++
++Then, you can see whole tree of your home dir through /tmp/aufs. If
++you modify a file under /tmp/aufs, the one on your home directory is
++not affected, instead the same named file will be newly created under
++/tmp/rw. And all of your modification to a file will be applied to
++the one under /tmp/rw. This is called the file based Copy on Write
++(COW) method.
++Aufs mount options are described in aufs.5.
++If you run chroot or something and make your aufs as a root directory,
++then you need to customize the shutdown script. See the aufs manual in
++detail.
++
++Additionally, there are some sample usages of aufs which are a
++diskless system with network booting, and LiveCD over NFS.
++See sample dir in CVS tree on SourceForge.
++
++
++5. Contact
++----------------------------------------
++When you have any problems or strange behaviour in aufs, please let me
++know with:
++- /proc/mounts (instead of the output of mount(8))
++- /sys/module/aufs/*
++- /sys/fs/aufs/* (if you have them)
++- /debug/aufs/* (if you have them)
++- linux kernel version
++ if your kernel is not plain, for example modified by distributor,
++ the url where i can download its source is necessary too.
++- aufs version which was printed at loading the module or booting the
++ system, instead of the date you downloaded.
++- configuration (define/undefine CONFIG_AUFS_xxx)
++- kernel configuration or /proc/config.gz (if you have it)
++- behaviour which you think to be incorrect
++- actual operation, reproducible one is better
++- mailto: aufs-users at lists.sourceforge.net
++
++Usually, I don't watch the Public Areas(Bugs, Support Requests, Patches,
++and Feature Requests) on SourceForge. Please join and write to
++aufs-users ML.
++
++
++6. Acknowledgements
++----------------------------------------
++Thanks to everyone who have tried and are using aufs, whoever
++have reported a bug or any feedback.
++
++Especially donators:
++Tomas Matejicek(slax.org) made a donation (much more than once).
++ Since Apr 2010, Tomas M (the author of Slax and Linux Live
++ scripts) is making "doubling" donations.
++ Unfortunately I cannot list all of the donators, but I really
++ appreciate.
++ It ends Aug 2010, but the ordinary donation URL is still available.
++ <http://sourceforge.net/donate/index.php?group_id=167503>
++Dai Itasaka made a donation (2007/8).
++Chuck Smith made a donation (2008/4, 10 and 12).
++Henk Schoneveld made a donation (2008/9).
++Chih-Wei Huang, ASUS, CTC donated Eee PC 4G (2008/10).
++Francois Dupoux made a donation (2008/11).
++Bruno Cesar Ribas and Luis Carlos Erpen de Bona, C3SL serves public
++ aufs2 GIT tree (2009/2).
++William Grant made a donation (2009/3).
++Patrick Lane made a donation (2009/4).
++The Mail Archive (mail-archive.com) made donations (2009/5).
++Nippy Networks (Ed Wildgoose) made a donation (2009/7).
++New Dream Network, LLC (www.dreamhost.com) made a donation (2009/11).
++Pavel Pronskiy made a donation (2011/2).
++Iridium and Inmarsat satellite phone retailer (www.mailasail.com), Nippy
++ Networks (Ed Wildgoose) made a donation for hardware (2011/3).
++Max Lekomcev (DOM-TV project) made a donation (2011/7, 12, 2012/3, 6 and
++11).
++Sam Liddicott made a donation (2011/9).
++Era Scarecrow made a donation (2013/4).
++Bor Ratajc made a donation (2013/4).
++Alessandro Gorreta made a donation (2013/4).
++POIRETTE Marc made a donation (2013/4).
++Alessandro Gorreta made a donation (2013/4).
++lauri kasvandik made a donation (2013/5).
++"pemasu from Finland" made a donation (2013/7).
++The Parted Magic Project made a donation (2013/9 and 11).
++Pavel Barta made a donation (2013/10).
++Nikolay Pertsev made a donation (2014/5).
++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).
++Tomasz Szewczyk made a donation (2016/4).
++James Burry made a donation (2016/12).
++
++Thank you very much.
++Donations are always, including future donations, very important and
++helpful for me to keep on developing aufs.
++
++
++7.
++----------------------------------------
++If you are an experienced user, no explanation is needed. Aufs is
++just a linux filesystem.
++
++
++Enjoy!
++
++# Local variables: ;
++# mode: text;
++# End: ;
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/01intro.txt linux-4.9/Documentation/filesystems/aufs/design/01intro.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/01intro.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/01intro.txt 2021-02-24 16:15:09.518240088 +0100
@@ -0,0 +1,171 @@
+
+# Copyright (C) 2005-2018 Junjiro R. Okajima
+Some people may think it is better to pass such work to user space
+helper, instead of doing in kernel space. Actually I am still thinking
+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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/02struct.txt linux-4.9/Documentation/filesystems/aufs/design/02struct.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/02struct.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/02struct.txt 2021-02-24 16:15:09.518240088 +0100
@@ -0,0 +1,258 @@
+
+# Copyright (C) 2005-2018 Junjiro R. Okajima
+- etc.
+
+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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/03atomic_open.txt linux-4.9/Documentation/filesystems/aufs/design/03atomic_open.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/03atomic_open.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/03atomic_open.txt 2021-02-24 16:15:09.518240088 +0100
@@ -0,0 +1,85 @@
+
+# Copyright (C) 2015-2018 Junjiro R. Okajima
+ ->atomic_open() are lost. in the ordinary case, the checks are
+ done by VFS:do_last(), lookup_open() and atomic_open(). some can
+ 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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/03lookup.txt linux-4.9/Documentation/filesystems/aufs/design/03lookup.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/03lookup.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/03lookup.txt 2021-02-24 16:15:09.518240088 +0100
@@ -0,0 +1,113 @@
+
+# Copyright (C) 2005-2018 Junjiro R. Okajima
+ test, and skip the revalidation in step 4. It is useful and improves
+ aufs performance when system surely hide the aufs branches from user,
+ 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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/04branch.txt linux-4.9/Documentation/filesystems/aufs/design/04branch.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/04branch.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/04branch.txt 2021-02-24 16:15:09.518240088 +0100
@@ -0,0 +1,74 @@
+
+# Copyright (C) 2005-2018 Junjiro R. Okajima
+ - a file on the branch is mmap-ed.
+ - a regular file on the branch is opened for write and there is no
+ 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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/05wbr_policy.txt linux-4.9/Documentation/filesystems/aufs/design/05wbr_policy.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/05wbr_policy.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/05wbr_policy.txt 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,64 @@
+
+# Copyright (C) 2005-2018 Junjiro R. Okajima
+ 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
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/06dirren.dot linux-4.9/Documentation/filesystems/aufs/design/06dirren.dot
+--- linux-4.9/Documentation/filesystems/aufs/design/06dirren.dot 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/06dirren.dot 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,31 @@
+
+// to view this graph, run dot(1) command in GRAPHVIZ.
+
+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
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/06dirren.txt linux-4.9/Documentation/filesystems/aufs/design/06dirren.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/06dirren.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/06dirren.txt 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,102 @@
+
+# Copyright (C) 2017-2018 Junjiro R. Okajima
+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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/06fhsm.txt linux-4.9/Documentation/filesystems/aufs/design/06fhsm.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/06fhsm.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/06fhsm.txt 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,120 @@
+
+# Copyright (C) 2011-2018 Junjiro R. Okajima
+
+And of course, in every step, an error may happen. So the operation
+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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/06mmap.txt linux-4.9/Documentation/filesystems/aufs/design/06mmap.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/06mmap.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/06mmap.txt 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,72 @@
+
+# Copyright (C) 2005-2018 Junjiro R. Okajima
+ equivalent to vm_prfile described above.
+
+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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/06xattr.txt linux-4.9/Documentation/filesystems/aufs/design/06xattr.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/06xattr.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/06xattr.txt 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,96 @@
+
+# Copyright (C) 2014-2018 Junjiro R. Okajima
+Some contradiction may happen I am afraid.
+Do we need another attribute to stop copying XATTR? I am unsure. For
+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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/07export.txt linux-4.9/Documentation/filesystems/aufs/design/07export.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/07export.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/07export.txt 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,58 @@
+
+# Copyright (C) 2005-2018 Junjiro R. Okajima
+ convert it into ESTALE for NFSD.
+- readdir(): call lockdep_on/off() because filldir in NFSD calls
+ 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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/08shwh.txt linux-4.9/Documentation/filesystems/aufs/design/08shwh.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/08shwh.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/08shwh.txt 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,52 @@
+
+# Copyright (C) 2005-2018 Junjiro R. Okajima
+
+This new squashfs archive can be stored on the boot device and the
+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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/Documentation/filesystems/aufs/design/10dynop.txt linux-4.9/Documentation/filesystems/aufs/design/10dynop.txt
+--- linux-4.9/Documentation/filesystems/aufs/design/10dynop.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/filesystems/aufs/design/10dynop.txt 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,47 @@
+
+# Copyright (C) 2010-2018 Junjiro R. Okajima
+XIP (DAX) mainly.
+Currently this approach is applied to address_space_operations for
+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 2017-07-29 12:14:25.893041746 +0200
-@@ -0,0 +1,393 @@
-+
-+Aufs4 -- advanced multi layered unification filesystem version 4.x
-+http://aufs.sf.net
-+Junjiro R. Okajima
-+
-+
-+0. Introduction
-+----------------------------------------
-+In the early days, aufs was entirely re-designed and re-implemented
-+Unionfs Version 1.x series. Adding many original ideas, approaches,
-+improvements and implementations, it becomes totally different from
-+Unionfs while keeping the basic features.
-+Recently, Unionfs Version 2.x series begin taking some of the same
-+approaches to aufs1's.
-+Unionfs is being developed by Professor Erez Zadok at Stony Brook
-+University and his team.
-+
-+Aufs4 supports linux-4.0 and later, and for linux-3.x series try aufs3.
-+If you want older kernel version support, try aufs2-2.6.git or
-+aufs2-standalone.git repository, aufs1 from CVS on SourceForge.
-+
-+Note: it becomes clear that "Aufs was rejected. Let's give it up."
-+ According to Christoph Hellwig, linux rejects all union-type
-+ filesystems but UnionMount.
-+<http://marc.info/?l=linux-kernel&m=123938533724484&w=2>
-+
-+PS. Al Viro seems have a plan to merge aufs as well as overlayfs and
-+ UnionMount, and he pointed out an issue around a directory mutex
-+ lock and aufs addressed it. But it is still unsure whether aufs will
-+ be merged (or any other union solution).
-+<http://marc.info/?l=linux-kernel&m=136312705029295&w=1>
-+
-+
-+1. Features
-+----------------------------------------
-+- unite several directories into a single virtual filesystem. The member
-+ directory is called as a branch.
-+- you can specify the permission flags to the branch, which are 'readonly',
-+ 'readwrite' and 'whiteout-able.'
-+- by upper writable branch, internal copyup and whiteout, files/dirs on
-+ readonly branch are modifiable logically.
-+- dynamic branch manipulation, add, del.
-+- etc...
-+
-+Also there are many enhancements in aufs, such as:
-+- test only the highest one for the directory permission (dirperm1)
-+- copyup on open (coo=)
-+- 'move' policy for copy-up between two writable branches, after
-+ checking free space.
-+- xattr, acl
-+- readdir(3) in userspace.
-+- keep inode number by external inode number table
-+- keep the timestamps of file/dir in internal copyup operation
-+- seekable directory, supporting NFS readdir.
-+- whiteout is hardlinked in order to reduce the consumption of inodes
-+ on branch
-+- do not copyup, nor create a whiteout when it is unnecessary
-+- revert a single systemcall when an error occurs in aufs
-+- remount interface instead of ioctl
-+- maintain /etc/mtab by an external command, /sbin/mount.aufs.
-+- loopback mounted filesystem as a branch
-+- kernel thread for removing the dir who has a plenty of whiteouts
-+- support copyup sparse file (a file which has a 'hole' in it)
-+- default permission flags for branches
-+- selectable permission flags for ro branch, whether whiteout can
-+ exist or not
-+- export via NFS.
-+- support <sysfs>/fs/aufs and <debugfs>/aufs.
-+- support multiple writable branches, some policies to select one
-+ among multiple writable branches.
-+- a new semantics for link(2) and rename(2) to support multiple
-+ writable branches.
-+- no glibc changes are required.
-+- pseudo hardlink (hardlink over branches)
-+- allow a direct access manually to a file on branch, e.g. bypassing aufs.
-+ including NFS or remote filesystem branch.
-+- userspace wrapper for pathconf(3)/fpathconf(3) with _PC_LINK_MAX.
-+- and more...
-+
-+Currently these features are dropped temporary from aufs4.
-+See design/08plan.txt in detail.
-+- nested mount, i.e. aufs as readonly no-whiteout branch of another aufs
-+ (robr)
-+- statistics of aufs thread (/sys/fs/aufs/stat)
-+
-+Features or just an idea in the future (see also design/*.txt),
-+- reorder the branch index without del/re-add.
-+- permanent xino files for NFSD
-+- an option for refreshing the opened files after add/del branches
-+- light version, without branch manipulation. (unnecessary?)
-+- copyup in userspace
-+- inotify in userspace
-+- readv/writev
-+
-+
-+2. Download
-+----------------------------------------
-+There are three GIT trees for aufs4, aufs4-linux.git,
-+aufs4-standalone.git, and aufs-util.git. Note that there is no "4" in
-+"aufs-util.git."
-+While the aufs-util is always necessary, you need either of aufs4-linux
-+or aufs4-standalone.
-+
-+The aufs4-linux tree includes the whole linux mainline GIT tree,
-+git://git.kernel.org/.../torvalds/linux.git.
-+And you cannot select CONFIG_AUFS_FS=m for this version, eg. you cannot
-+build aufs4 as an external kernel module.
-+Several extra patches are not included in this tree. Only
-+aufs4-standalone tree contains them. They are described in the later
-+section "Configuration and Compilation."
-+
-+On the other hand, the aufs4-standalone tree has only aufs source files
-+and necessary patches, and you can select CONFIG_AUFS_FS=m.
-+But you need to apply all aufs patches manually.
-+
-+You will find GIT branches whose name is in form of "aufs4.x" where "x"
-+represents the linux kernel version, "linux-4.x". For instance,
-+"aufs4.0" is for linux-4.0. For latest "linux-4.x-rcN", use
-+"aufs4.x-rcN" branch.
-+
-+o aufs4-linux tree
-+$ git clone --reference /your/linux/git/tree \
-+ git://github.com/sfjro/aufs4-linux.git aufs4-linux.git
-+- if you don't have linux GIT tree, then remove "--reference ..."
-+$ cd aufs4-linux.git
-+$ git checkout origin/aufs4.0
-+
-+Or You may want to directly git-pull aufs into your linux GIT tree, and
-+leave the patch-work to GIT.
-+$ cd /your/linux/git/tree
-+$ git remote add aufs4 git://github.com/sfjro/aufs4-linux.git
-+$ git fetch aufs4
-+$ git checkout -b my4.0 v4.0
-+$ (add your local change...)
-+$ git pull aufs4 aufs4.0
-+- now you have v4.0 + your_changes + aufs4.0 in you my4.0 branch.
-+- you may need to solve some conflicts between your_changes and
-+ aufs4.0. in this case, git-rerere is recommended so that you can
-+ solve the similar conflicts automatically when you upgrade to 4.1 or
-+ later in the future.
-+
-+o aufs4-standalone tree
-+$ git clone git://github.com/sfjro/aufs4-standalone.git aufs4-standalone.git
-+$ cd aufs4-standalone.git
-+$ git checkout origin/aufs4.0
-+
-+o aufs-util tree
-+$ git clone git://git.code.sf.net/p/aufs/aufs-util aufs-util.git
-+- note that the public aufs-util.git is on SourceForge instead of
-+ GitHUB.
-+$ cd aufs-util.git
-+$ git checkout origin/aufs4.0
-+
-+Note: The 4.x-rcN branch is to be used with `rc' kernel versions ONLY.
-+The minor version number, 'x' in '4.x', of aufs may not always
-+follow the minor version number of the kernel.
-+Because changes in the kernel that cause the use of a new
-+minor version number do not always require changes to aufs-util.
-+
-+Since aufs-util has its own minor version number, you may not be
-+able to find a GIT branch in aufs-util for your kernel's
-+exact minor version number.
-+In this case, you should git-checkout the branch for the
-+nearest lower number.
-+
-+For (an unreleased) example:
-+If you are using "linux-4.10" and the "aufs4.10" branch
-+does not exist in aufs-util repository, then "aufs4.9", "aufs4.8"
-+or something numerically smaller is the branch for your kernel.
-+
-+Also you can view all branches by
-+ $ git branch -a
+diff -urNp -x '*.orig' linux-4.9/MAINTAINERS linux-4.9/MAINTAINERS
+--- linux-4.9/MAINTAINERS 2021-02-24 16:14:54.924432098 +0100
++++ linux-4.9/MAINTAINERS 2021-02-24 16:15:09.504906321 +0100
+@@ -2293,6 +2293,19 @@ F: include/linux/audit.h
+ F: include/uapi/linux/audit.h
+ F: kernel/audit*
+
++AUFS (advanced multi layered unification filesystem) FILESYSTEM
++M: "J. R. Okajima" <hooanon05g@gmail.com>
++L: linux-unionfs@vger.kernel.org
++L: aufs-users@lists.sourceforge.net (members only)
++W: http://aufs.sourceforge.net
++T: git://github.com/sfjro/aufs4-linux.git
++S: Supported
++F: Documentation/filesystems/aufs/
++F: Documentation/ABI/testing/debugfs-aufs
++F: Documentation/ABI/testing/sysfs-aufs
++F: fs/aufs/
++F: include/uapi/linux/aufs_type.h
+
+ AUXILIARY DISPLAY DRIVERS
+ M: Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>
+ W: http://miguelojeda.es/auxdisplay.htm
+diff -urNp -x '*.orig' linux-4.9/drivers/block/loop.c linux-4.9/drivers/block/loop.c
+--- linux-4.9/drivers/block/loop.c 2021-02-24 16:14:57.284508877 +0100
++++ linux-4.9/drivers/block/loop.c 2021-02-24 16:15:09.538240738 +0100
+@@ -552,7 +552,7 @@ static int do_req_filebacked(struct loop
+ }
+
+ struct switch_request {
+- struct file *file;
++ struct file *file, *virt_file;
+ struct completion wait;
+ };
+
+@@ -578,6 +578,7 @@ static void do_loop_switch(struct loop_d
+ 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);
+@@ -590,11 +591,13 @@ static void do_loop_switch(struct loop_d
+ * 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);
+@@ -616,7 +619,16 @@ static int loop_flush(struct loop_device
+ /* loop not yet configured, no running thread, nothing to flush */
+ if (lo->lo_state != Lo_bound)
+ return 0;
+- return loop_switch(lo, NULL);
++ return loop_switch(lo, NULL, NULL);
++}
+
-+3. Configuration and Compilation
-+----------------------------------------
-+Make sure you have git-checkout'ed the correct branch.
++static struct file *loop_real_file(struct file *file)
++{
++ struct file *f = NULL;
+
-+For aufs4-linux tree,
-+- enable CONFIG_AUFS_FS.
-+- set other aufs configurations if necessary.
++ 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,
+@@ -683,6 +695,7 @@ static int loop_change_fd(struct loop_de
+ unsigned int arg)
+ {
+ struct file *file, *old_file;
++ struct file *f, *virt_file = NULL, *old_virt_file;
+ struct inode *inode;
+ int error;
+
+@@ -699,6 +712,12 @@ static int loop_change_fd(struct loop_de
+ file = fget(arg);
+ if (!file)
+ goto out;
++ f = loop_real_file(file);
++ if (f) {
++ virt_file = file;
++ file = f;
++ get_file(file);
++ }
+
+ error = loop_validate_file(file, bdev);
+ if (error)
+@@ -706,6 +725,7 @@ static int loop_change_fd(struct loop_de
+
+ inode = file->f_mapping->host;
+ old_file = lo->lo_backing_file;
++ old_virt_file = lo->lo_backing_virt_file;
+
+ error = -EINVAL;
+
+@@ -714,21 +734,43 @@ static int loop_change_fd(struct loop_de
+ goto out_putf;
+
+ /* and ... switch */
+- error = loop_switch(lo, file);
++ error = loop_switch(lo, file, virt_file);
+ if (error)
+ goto out_putf;
+
+ fput(old_file);
++ if (old_virt_file)
++ fput(old_virt_file);
+ if (lo->lo_flags & LO_FLAGS_PARTSCAN)
+ loop_reread_partitions(lo, bdev);
+ return 0;
+
+ out_putf:
+ fput(file);
++ if (virt_file)
++ fput(virt_file);
+ out:
+ return error;
+ }
+
++/*
++ * for AUFS
++ * no get/put for file.
++ */
++struct file *loop_backing_file(struct super_block *sb)
++{
++ struct file *ret;
++ struct loop_device *l;
+
-+For aufs4-standalone tree,
-+There are several ways to build.
++ ret = NULL;
++ if (MAJOR(sb->s_dev) == LOOP_MAJOR) {
++ l = sb->s_bdev->bd_disk->private_data;
++ ret = l->lo_backing_file;
++ }
++ return ret;
++}
++EXPORT_SYMBOL_GPL(loop_backing_file);
+
-+1.
-+- apply ./aufs4-kbuild.patch to your kernel source files.
-+- apply ./aufs4-base.patch too.
-+- apply ./aufs4-mmap.patch too.
-+- apply ./aufs4-standalone.patch too, if you have a plan to set
-+ CONFIG_AUFS_FS=m. otherwise you don't need ./aufs4-standalone.patch.
-+- copy ./{Documentation,fs,include/uapi/linux/aufs_type.h} files to your
-+ kernel source tree. Never copy $PWD/include/uapi/linux/Kbuild.
-+- enable CONFIG_AUFS_FS, you can select either
-+ =m or =y.
-+- and build your kernel as usual.
-+- install the built kernel.
-+ Note: Since linux-3.9, every filesystem module requires an alias
-+ "fs-<fsname>". You should make sure that "fs-aufs" is listed in your
-+ modules.aliases file if you set CONFIG_AUFS_FS=m.
-+- install the header files too by "make headers_install" to the
-+ directory where you specify. By default, it is $PWD/usr.
-+ "make help" shows a brief note for headers_install.
-+- and reboot your system.
+ /* loop sysfs attributes */
+
+ static ssize_t loop_attr_show(struct device *dev, char *page,
+@@ -887,7 +929,7 @@ static int loop_prepare_queue(struct loo
+ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
+ struct block_device *bdev, unsigned int arg)
+ {
+- struct file *file;
++ struct file *file, *f, *virt_file = NULL;
+ struct inode *inode;
+ struct address_space *mapping;
+ unsigned lo_blocksize;
+@@ -902,6 +944,12 @@ static int loop_set_fd(struct loop_devic
+ file = fget(arg);
+ if (!file)
+ goto out;
++ f = loop_real_file(file);
++ if (f) {
++ virt_file = file;
++ file = f;
++ get_file(file);
++ }
+
+ error = -EBUSY;
+ if (lo->lo_state != Lo_unbound)
+@@ -939,6 +987,7 @@ static int loop_set_fd(struct loop_devic
+ lo->lo_flags = lo_flags;
+ lo->lo_xid = vx_current_xid();
+ lo->lo_backing_file = file;
++ lo->lo_backing_virt_file = virt_file;
+ lo->transfer = NULL;
+ lo->ioctl = NULL;
+ lo->lo_sizelimit = 0;
+@@ -971,6 +1020,8 @@ static int loop_set_fd(struct loop_devic
+
+ out_putf:
+ fput(file);
++ if (virt_file)
++ fput(virt_file);
+ out:
+ /* This is safe: open() is still holding a reference. */
+ module_put(THIS_MODULE);
+@@ -1017,6 +1068,7 @@ loop_init_xfer(struct loop_device *lo, s
+ static int loop_clr_fd(struct loop_device *lo)
+ {
+ struct file *filp = lo->lo_backing_file;
++ struct file *virt_filp = lo->lo_backing_virt_file;
+ gfp_t gfp = lo->old_gfp_mask;
+ struct block_device *bdev = lo->lo_device;
+
+@@ -1048,6 +1100,7 @@ static int loop_clr_fd(struct loop_devic
+ spin_lock_irq(&lo->lo_lock);
+ lo->lo_state = Lo_rundown;
+ lo->lo_backing_file = NULL;
++ lo->lo_backing_virt_file = NULL;
+ spin_unlock_irq(&lo->lo_lock);
+
+ loop_release_xfer(lo);
+@@ -1093,6 +1146,8 @@ static int loop_clr_fd(struct loop_devic
+ * bd_mutex which is usually taken before lo_ctl_mutex.
+ */
+ fput(filp);
++ if (virt_filp)
++ fput(virt_filp);
+ return 0;
+ }
+
+diff -urNp -x '*.orig' linux-4.9/drivers/block/loop.h linux-4.9/drivers/block/loop.h
+--- linux-4.9/drivers/block/loop.h 2021-02-24 16:14:57.284508877 +0100
++++ linux-4.9/drivers/block/loop.h 2021-02-24 16:15:09.538240738 +0100
+@@ -47,7 +47,7 @@ struct loop_device {
+ int (*ioctl)(struct loop_device *, int cmd,
+ unsigned long arg);
+
+- 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 -urNp -x '*.orig' linux-4.9/fs/Kconfig linux-4.9/fs/Kconfig
+--- linux-4.9/fs/Kconfig 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/Kconfig 2021-02-24 16:15:09.501572879 +0100
+@@ -249,6 +249,7 @@ source "fs/pstore/Kconfig"
+ source "fs/sysv/Kconfig"
+ source "fs/ufs/Kconfig"
+ source "fs/exofs/Kconfig"
++source "fs/aufs/Kconfig"
+
+ endif # MISC_FILESYSTEMS
+
+diff -urNp -x '*.orig' linux-4.9/fs/Makefile linux-4.9/fs/Makefile
+--- linux-4.9/fs/Makefile 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/Makefile 2021-02-24 16:15:09.501572879 +0100
+@@ -129,3 +129,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 -urNp -x '*.orig' linux-4.9/fs/aufs/Kconfig linux-4.9/fs/aufs/Kconfig
+--- linux-4.9/fs/aufs/Kconfig 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/Kconfig 2021-02-24 16:15:09.531573855 +0100
+@@ -0,0 +1,198 @@
++config AUFS_FS
++ tristate "Aufs (Advanced multi layered unification filesystem) support"
++ help
++ Aufs is a stackable unification filesystem such as Unionfs,
++ which unifies several directories and provides a merged single
++ directory.
++ In the early days, aufs was entirely re-designed and
++ re-implemented Unionfs Version 1.x series. Introducing many
++ original ideas, approaches and improvements, it becomes totally
++ different from Unionfs while keeping the basic features.
+
-+2.
-+- module only (CONFIG_AUFS_FS=m).
-+- apply ./aufs4-base.patch to your kernel source files.
-+- apply ./aufs4-mmap.patch too.
-+- apply ./aufs4-standalone.patch too.
-+- build your kernel, don't forget "make headers_install", and reboot.
-+- edit ./config.mk and set other aufs configurations if necessary.
-+ Note: You should read $PWD/fs/aufs/Kconfig carefully which describes
-+ every aufs configurations.
-+- build the module by simple "make".
-+ Note: Since linux-3.9, every filesystem module requires an alias
-+ "fs-<fsname>". You should make sure that "fs-aufs" is listed in your
-+ modules.aliases file.
-+- you can specify ${KDIR} make variable which points to your kernel
-+ source tree.
-+- install the files
-+ + run "make install" to install the aufs module, or copy the built
-+ $PWD/aufs.ko to /lib/modules/... and run depmod -a (or reboot simply).
-+ + run "make install_headers" (instead of headers_install) to install
-+ the modified aufs header file (you can specify DESTDIR which is
-+ available in aufs standalone version's Makefile only), or copy
-+ $PWD/usr/include/linux/aufs_type.h to /usr/include/linux or wherever
-+ you like manually. By default, the target directory is $PWD/usr.
-+- no need to apply aufs4-kbuild.patch, nor copying source files to your
-+ kernel source tree.
++if AUFS_FS
++choice
++ prompt "Maximum number of branches"
++ default AUFS_BRANCH_MAX_127
++ help
++ Specifies the maximum number of branches (or member directories)
++ in a single aufs. The larger value consumes more system
++ resources and has a minor impact to performance.
++config AUFS_BRANCH_MAX_127
++ bool "127"
++ help
++ Specifies the maximum number of branches (or member directories)
++ in a single aufs. The larger value consumes more system
++ resources and has a minor impact to performance.
++config AUFS_BRANCH_MAX_511
++ bool "511"
++ help
++ Specifies the maximum number of branches (or member directories)
++ in a single aufs. The larger value consumes more system
++ resources and has a minor impact to performance.
++config AUFS_BRANCH_MAX_1023
++ bool "1023"
++ help
++ Specifies the maximum number of branches (or member directories)
++ in a single aufs. The larger value consumes more system
++ resources and has a minor impact to performance.
++config AUFS_BRANCH_MAX_32767
++ bool "32767"
++ help
++ Specifies the maximum number of branches (or member directories)
++ in a single aufs. The larger value consumes more system
++ resources and has a minor impact to performance.
++endchoice
+
-+Note: The header file aufs_type.h is necessary to build aufs-util
-+ as well as "make headers_install" in the kernel source tree.
-+ headers_install is subject to be forgotten, but it is essentially
-+ necessary, not only for building aufs-util.
-+ You may not meet problems without headers_install in some older
-+ version though.
++config AUFS_SBILIST
++ bool
++ depends on AUFS_MAGIC_SYSRQ || PROC_FS
++ default y
++ help
++ Automatic configuration for internal use.
++ When aufs supports Magic SysRq or /proc, enabled automatically.
+
-+And then,
-+- read README in aufs-util, build and install it
-+- note that your distribution may contain an obsoleted version of
-+ aufs_type.h in /usr/include/linux or something. When you build aufs
-+ utilities, make sure that your compiler refers the correct aufs header
-+ file which is built by "make headers_install."
-+- if you want to use readdir(3) in userspace or pathconf(3) wrapper,
-+ then run "make install_ulib" too. And refer to the aufs manual in
-+ detail.
++config AUFS_HNOTIFY
++ bool "Detect direct branch access (bypassing aufs)"
++ help
++ If you want to modify files on branches directly, eg. bypassing aufs,
++ and want aufs to detect the changes of them fully, then enable this
++ option and use 'udba=notify' mount option.
++ Currently there is only one available configuration, "fsnotify".
++ It will have a negative impact to the performance.
++ See detail in aufs.5.
+
-+There several other patches in aufs4-standalone.git. They are all
-+optional. When you meet some problems, they will help you.
-+- aufs4-loopback.patch
-+ Supports a nested loopback mount in a branch-fs. This patch is
-+ unnecessary until aufs produces a message like "you may want to try
-+ another patch for loopback file".
-+- vfs-ino.patch
-+ Modifies a system global kernel internal function get_next_ino() in
-+ order to stop assigning 0 for an inode-number. Not directly related to
-+ aufs, but recommended generally.
-+- tmpfs-idr.patch
-+ Keeps the tmpfs inode number as the lowest value. Effective to reduce
-+ the size of aufs XINO files for tmpfs branch. Also it prevents the
-+ duplication of inode number, which is important for backup tools and
-+ other utilities. When you find aufs XINO files for tmpfs branch
-+ growing too much, try this patch.
-+- lockdep-debug.patch
-+ Because aufs is not only an ordinary filesystem (callee of VFS), but
-+ also a caller of VFS functions for branch filesystems, subclassing of
-+ the internal locks for LOCKDEP is necessary. LOCKDEP is a debugging
-+ feature of linux kernel. If you enable CONFIG_LOCKDEP, then you will
-+ need to apply this debug patch to expand several constant values.
-+ If don't know what LOCKDEP, then you don't have apply this patch.
++choice
++ prompt "method" if AUFS_HNOTIFY
++ default AUFS_HFSNOTIFY
++config AUFS_HFSNOTIFY
++ bool "fsnotify"
++ select FSNOTIFY
++endchoice
+
++config AUFS_EXPORT
++ bool "NFS-exportable aufs"
++ depends on EXPORTFS
++ help
++ If you want to export your mounted aufs via NFS, then enable this
++ option. There are several requirements for this configuration.
++ See detail in aufs.5.
+
-+4. Usage
-+----------------------------------------
-+At first, make sure aufs-util are installed, and please read the aufs
-+manual, aufs.5 in aufs-util.git tree.
-+$ man -l aufs.5
++config AUFS_INO_T_64
++ bool
++ depends on AUFS_EXPORT
++ depends on 64BIT && !(ALPHA || S390)
++ default y
++ help
++ Automatic configuration for internal use.
++ /* typedef unsigned long/int __kernel_ino_t */
++ /* alpha and s390x are int */
+
-+And then,
-+$ mkdir /tmp/rw /tmp/aufs
-+# mount -t aufs -o br=/tmp/rw:${HOME} none /tmp/aufs
++config AUFS_XATTR
++ bool "support for XATTR/EA (including Security Labels)"
++ help
++ If your branch fs supports XATTR/EA and you want to make them
++ available in aufs too, then enable this opsion and specify the
++ branch attributes for EA.
++ See detail in aufs.5.
+
-+Here is another example. The result is equivalent.
-+# mount -t aufs -o br=/tmp/rw=rw:${HOME}=ro none /tmp/aufs
-+ Or
-+# mount -t aufs -o br:/tmp/rw none /tmp/aufs
-+# mount -o remount,append:${HOME} /tmp/aufs
++config AUFS_FHSM
++ bool "File-based Hierarchical Storage Management"
++ help
++ Hierarchical Storage Management (or HSM) is a well-known feature
++ in the storage world. Aufs provides this feature as file-based.
++ with multiple branches.
++ These multiple branches are prioritized, ie. the topmost one
++ should be the fastest drive and be used heavily.
+
-+Then, you can see whole tree of your home dir through /tmp/aufs. If
-+you modify a file under /tmp/aufs, the one on your home directory is
-+not affected, instead the same named file will be newly created under
-+/tmp/rw. And all of your modification to a file will be applied to
-+the one under /tmp/rw. This is called the file based Copy on Write
-+(COW) method.
-+Aufs mount options are described in aufs.5.
-+If you run chroot or something and make your aufs as a root directory,
-+then you need to customize the shutdown script. See the aufs manual in
-+detail.
++config AUFS_RDU
++ bool "Readdir in userspace"
++ help
++ Aufs has two methods to provide a merged view for a directory,
++ by a user-space library and by kernel-space natively. The latter
++ is always enabled but sometimes large and slow.
++ If you enable this option, install the library in aufs2-util
++ package, and set some environment variables for your readdir(3),
++ then the work will be handled in user-space which generally
++ shows better performance in most cases.
++ See detail in aufs.5.
+
-+Additionally, there are some sample usages of aufs which are a
-+diskless system with network booting, and LiveCD over NFS.
-+See sample dir in CVS tree on SourceForge.
++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
++ If you want to make the whiteouts in aufs visible, then enable
++ this option and specify 'shwh' mount option. Although it may
++ sounds like philosophy or something, but in technically it
++ simply shows the name of whiteout with keeping its behaviour.
+
-+5. Contact
-+----------------------------------------
-+When you have any problems or strange behaviour in aufs, please let me
-+know with:
-+- /proc/mounts (instead of the output of mount(8))
-+- /sys/module/aufs/*
-+- /sys/fs/aufs/* (if you have them)
-+- /debug/aufs/* (if you have them)
-+- linux kernel version
-+ if your kernel is not plain, for example modified by distributor,
-+ the url where i can download its source is necessary too.
-+- aufs version which was printed at loading the module or booting the
-+ system, instead of the date you downloaded.
-+- configuration (define/undefine CONFIG_AUFS_xxx)
-+- kernel configuration or /proc/config.gz (if you have it)
-+- behaviour which you think to be incorrect
-+- actual operation, reproducible one is better
-+- mailto: aufs-users at lists.sourceforge.net
++config AUFS_BR_RAMFS
++ bool "Ramfs (initramfs/rootfs) as an aufs branch"
++ help
++ If you want to use ramfs as an aufs branch fs, then enable this
++ option. Generally tmpfs is recommended.
++ Aufs prohibited them to be a branch fs by default, because
++ initramfs becomes unusable after switch_root or something
++ generally. If you sets initramfs as an aufs branch and boot your
++ system by switch_root, you will meet a problem easily since the
++ files in initramfs may be inaccessible.
++ Unless you are going to use ramfs as an aufs branch fs without
++ switch_root or something, leave it N.
+
-+Usually, I don't watch the Public Areas(Bugs, Support Requests, Patches,
-+and Feature Requests) on SourceForge. Please join and write to
-+aufs-users ML.
++config AUFS_BR_FUSE
++ bool "Fuse fs as an aufs branch"
++ depends on FUSE_FS
++ select AUFS_POLL
++ help
++ If you want to use fuse-based userspace filesystem as an aufs
++ branch fs, then enable this option.
++ It implements the internal poll(2) operation which is
++ implemented by fuse only (curretnly).
+
++config AUFS_POLL
++ bool
++ help
++ Automatic configuration for internal use.
+
-+6. Acknowledgements
-+----------------------------------------
-+Thanks to everyone who have tried and are using aufs, whoever
-+have reported a bug or any feedback.
++config AUFS_BR_HFSPLUS
++ bool "Hfsplus as an aufs branch"
++ depends on HFSPLUS_FS
++ default y
++ help
++ If you want to use hfsplus fs as an aufs branch fs, then enable
++ this option. This option introduces a small overhead at
++ copying-up a file on hfsplus.
+
-+Especially donators:
-+Tomas Matejicek(slax.org) made a donation (much more than once).
-+ Since Apr 2010, Tomas M (the author of Slax and Linux Live
-+ scripts) is making "doubling" donations.
-+ Unfortunately I cannot list all of the donators, but I really
-+ appreciate.
-+ It ends Aug 2010, but the ordinary donation URL is still available.
-+ <http://sourceforge.net/donate/index.php?group_id=167503>
-+Dai Itasaka made a donation (2007/8).
-+Chuck Smith made a donation (2008/4, 10 and 12).
-+Henk Schoneveld made a donation (2008/9).
-+Chih-Wei Huang, ASUS, CTC donated Eee PC 4G (2008/10).
-+Francois Dupoux made a donation (2008/11).
-+Bruno Cesar Ribas and Luis Carlos Erpen de Bona, C3SL serves public
-+ aufs2 GIT tree (2009/2).
-+William Grant made a donation (2009/3).
-+Patrick Lane made a donation (2009/4).
-+The Mail Archive (mail-archive.com) made donations (2009/5).
-+Nippy Networks (Ed Wildgoose) made a donation (2009/7).
-+New Dream Network, LLC (www.dreamhost.com) made a donation (2009/11).
-+Pavel Pronskiy made a donation (2011/2).
-+Iridium and Inmarsat satellite phone retailer (www.mailasail.com), Nippy
-+ Networks (Ed Wildgoose) made a donation for hardware (2011/3).
-+Max Lekomcev (DOM-TV project) made a donation (2011/7, 12, 2012/3, 6 and
-+11).
-+Sam Liddicott made a donation (2011/9).
-+Era Scarecrow made a donation (2013/4).
-+Bor Ratajc made a donation (2013/4).
-+Alessandro Gorreta made a donation (2013/4).
-+POIRETTE Marc made a donation (2013/4).
-+Alessandro Gorreta made a donation (2013/4).
-+lauri kasvandik made a donation (2013/5).
-+"pemasu from Finland" made a donation (2013/7).
-+The Parted Magic Project made a donation (2013/9 and 11).
-+Pavel Barta made a donation (2013/10).
-+Nikolay Pertsev made a donation (2014/5).
-+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).
-+Tomasz Szewczyk made a donation (2016/4).
-+James Burry made a donation (2016/12).
++config AUFS_BDEV_LOOP
++ bool
++ depends on BLK_DEV_LOOP
++ default y
++ help
++ Automatic configuration for internal use.
++ Convert =[ym] into =y.
+
-+Thank you very much.
-+Donations are always, including future donations, very important and
-+helpful for me to keep on developing aufs.
++config AUFS_DEBUG
++ bool "Debug aufs"
++ help
++ Enable this to compile aufs internal debug code.
++ It will have a negative impact to the performance.
+
++config AUFS_MAGIC_SYSRQ
++ bool
++ depends on AUFS_DEBUG && MAGIC_SYSRQ
++ default y
++ help
++ Automatic configuration for internal use.
++ When aufs supports Magic SysRq, enabled automatically.
++endif
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/Makefile linux-4.9/fs/aufs/Makefile
+--- linux-4.9/fs/aufs/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/Makefile 2021-02-24 16:15:09.531573855 +0100
+@@ -0,0 +1,45 @@
+
-+7.
-+----------------------------------------
-+If you are an experienced user, no explanation is needed. Aufs is
-+just a linux filesystem.
++include ${src}/magic.mk
++ifeq (${CONFIG_AUFS_FS},m)
++include ${src}/conf.mk
++endif
++-include ${src}/priv_def.mk
+
++# cf. include/linux/kernel.h
++# enable pr_debug
++ccflags-y += -DDEBUG
++# sparse requires the full pathname
++ifdef M
++ccflags-y += -include ${M}/../../include/uapi/linux/aufs_type.h
++else
++ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h
++endif
+
-+Enjoy!
++obj-$(CONFIG_AUFS_FS) += aufs.o
++aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \
++ wkq.o vfsub.o dcsub.o \
++ cpup.o whout.o wbr_policy.o \
++ dinfo.o dentry.o \
++ dynop.o \
++ finfo.o file.o f_op.o \
++ dir.o vdir.o \
++ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \
++ mvdown.o ioctl.o
+
-+# Local variables: ;
-+# mode: text;
-+# 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 2018-04-15 08:49:13.394483860 +0200
++# all are boolean
++aufs-$(CONFIG_PROC_FS) += procfs.o plink.o
++aufs-$(CONFIG_SYSFS) += sysfs.o
++aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o
++aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o
++aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o
++aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o
++aufs-$(CONFIG_AUFS_EXPORT) += export.o
++aufs-$(CONFIG_AUFS_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
++aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o
++aufs-$(CONFIG_AUFS_DEBUG) += debug.o
++aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/aufs.h linux-4.9/fs/aufs/aufs.h
+--- linux-4.9/fs/aufs/aufs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/aufs.h 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/branch.c linux-4.9/fs/aufs/branch.c
+--- linux-4.9/fs/aufs/branch.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/branch.c 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,1432 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+ return err;
+}
-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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/branch.h linux-4.9/fs/aufs/branch.h
+--- linux-4.9/fs/aufs/branch.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/branch.h 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.394483860 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/conf.mk linux-4.9/fs/aufs/conf.mk
+--- linux-4.9/fs/aufs/conf.mk 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/conf.mk 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,39 @@
+
+AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS}
+${obj}/sysfs.o: ${AuConfName}
+
+-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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/cpup.c linux-4.9/fs/aufs/cpup.c
+--- linux-4.9/fs/aufs/cpup.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/cpup.c 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,1414 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ dput(parent);
+ return err;
+}
-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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/cpup.h linux-4.9/fs/aufs/cpup.h
+--- linux-4.9/fs/aufs/cpup.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/cpup.h 2021-02-24 16:15:09.521573529 +0100
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dbgaufs.c linux-4.9/fs/aufs/dbgaufs.c
+--- linux-4.9/fs/aufs/dbgaufs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dbgaufs.c 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ err = 0;
+ return err;
+}
-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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dbgaufs.h linux-4.9/fs/aufs/dbgaufs.h
+--- linux-4.9/fs/aufs/dbgaufs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dbgaufs.h 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dcsub.c linux-4.9/fs/aufs/dcsub.c
+--- linux-4.9/fs/aufs/dcsub.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dcsub.c 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+ return path_is_under(path + 0, path + 1);
+}
-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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dcsub.h linux-4.9/fs/aufs/dcsub.h
+--- linux-4.9/fs/aufs/dcsub.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dcsub.h 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/debug.c linux-4.9/fs/aufs/debug.c
+--- linux-4.9/fs/aufs/debug.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/debug.c 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+ return 0;
+}
-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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/debug.h linux-4.9/fs/aufs/debug.h
+--- linux-4.9/fs/aufs/debug.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/debug.h 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dentry.c linux-4.9/fs/aufs/dentry.c
+--- linux-4.9/fs/aufs/dentry.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dentry.c 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,1152 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+const struct dentry_operations aufs_dop_noreval = {
+ .d_release = aufs_d_release
+};
-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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dentry.h linux-4.9/fs/aufs/dentry.h
+--- linux-4.9/fs/aufs/dentry.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dentry.h 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dinfo.c linux-4.9/fs/aufs/dinfo.c
+--- linux-4.9/fs/aufs/dinfo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dinfo.c 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,553 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ 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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dir.c linux-4.9/fs/aufs/dir.c
+--- linux-4.9/fs/aufs/dir.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dir.c 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,759 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ .flush = aufs_flush_dir,
+ .fsync = aufs_fsync_dir
+};
-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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dir.h linux-4.9/fs/aufs/dir.h
+--- linux-4.9/fs/aufs/dir.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dir.h 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_DIR_H__ */
-diff -urN /usr/share/empty/fs/aufs/dirren.c linux/fs/aufs/dirren.c
---- /usr/share/empty/fs/aufs/dirren.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dirren.c 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dirren.c linux-4.9/fs/aufs/dirren.c
+--- linux-4.9/fs/aufs/dirren.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dirren.c 2021-02-24 16:15:09.524906971 +0100
@@ -0,0 +1,1314 @@
+/*
+ * Copyright (C) 2017-2018 Junjiro R. Okajima
+ return err;
+}
+
-+int au_dr_opt_flush(struct super_block *sb)
++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 -urNp -x '*.orig' linux-4.9/fs/aufs/dirren.h linux-4.9/fs/aufs/dirren.h
+--- linux-4.9/fs/aufs/dirren.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dirren.h 2021-02-24 16:15:09.524906971 +0100
+@@ -0,0 +1,145 @@
++/*
++ * Copyright (C) 2017-2018 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++/*
++ * renamed dir info
++ */
++
++#ifndef __AUFS_DIRREN_H__
++#define __AUFS_DIRREN_H__
++
++#ifdef __KERNEL__
++
++#include <linux/dcache.h>
++#include <linux/statfs.h>
++#include "hbl.h"
++
++#define AuDirren_NHASH 100
++
++#ifdef CONFIG_AUFS_DIRREN
++/* copied from linux/fs/xfs/uuid.h */
++typedef struct {
++ unsigned char __u_bits[16];
++} uuid_t;
++
++#define __UUID_TMPLT "01234567-0123-4567-0123-456701234567"
++
++enum au_brid_type {
++ AuBrid_Unset,
++ AuBrid_UUID,
++ AuBrid_FSID,
++ AuBrid_DEV
++};
++
++struct au_dr_brid {
++ enum au_brid_type type;
++ union {
++ uuid_t uuid; /* unimplemented yet */
++ fsid_t fsid;
++ dev_t dev;
++ };
++};
++
++/* 20 is the max digits length of ulong 64 */
++/* brid-type "_" uuid "_" inum */
++#define AUFS_DIRREN_FNAME_SZ (1 + 1 + sizeof(__UUID_TMPLT) + 20)
++#define AUFS_DIRREN_ENV_VAL_SZ (AUFS_DIRREN_FNAME_SZ + 1 + 20)
++
++struct au_dr_hino {
++ struct hlist_bl_node dr_hnode;
++ ino_t dr_h_ino;
++};
++
++struct au_dr_br {
++ struct hlist_bl_head dr_h_ino[AuDirren_NHASH];
++ struct au_dr_brid dr_brid;
++};
++
++struct au_dr_lookup {
++ /* dr_name is pointed by struct au_do_lookup_args.name */
++ struct qstr dr_name; /* subset of dr_info */
++ aufs_bindex_t ninfo;
++ struct au_drinfo **drinfo;
++};
++#else
++struct au_dr_hino;
++/* empty */
++struct au_dr_br { };
++struct au_dr_lookup { };
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct au_branch;
++struct au_do_lookup_args;
++struct au_hinode;
++#ifdef CONFIG_AUFS_DIRREN
++int au_dr_hino_test_add(struct au_dr_br *dr, ino_t h_ino,
++ struct au_dr_hino *add_ent);
++void au_dr_hino_free(struct au_dr_br *dr);
++int au_dr_br_init(struct super_block *sb, struct au_branch *br,
++ const struct path *path);
++int au_dr_br_fin(struct super_block *sb, struct au_branch *br);
++int au_dr_rename(struct dentry *src, aufs_bindex_t bindex,
++ struct qstr *dst_name, void *_rev);
++void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *rev);
++void au_dr_rename_rev(struct dentry *src, aufs_bindex_t bindex, void *rev);
++int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry,
++ aufs_bindex_t bindex);
++int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt);
++int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex,
++ ino_t h_ino);
++void au_dr_lkup_fin(struct au_do_lookup_args *lkup);
++int au_dr_opt_set(struct super_block *sb);
++int au_dr_opt_flush(struct super_block *sb);
++int au_dr_opt_clr(struct super_block *sb, int no_flush);
++#else
++AuStubInt0(au_dr_hino_test_add, struct au_dr_br *dr, ino_t h_ino,
++ struct au_dr_hino *add_ent);
++AuStubVoid(au_dr_hino_free, struct au_dr_br *dr);
++AuStubInt0(au_dr_br_init, struct super_block *sb, struct au_branch *br,
++ const struct path *path);
++AuStubInt0(au_dr_br_fin, struct super_block *sb, struct au_branch *br);
++AuStubInt0(au_dr_rename, struct dentry *src, aufs_bindex_t bindex,
++ struct qstr *dst_name, void *_rev);
++AuStubVoid(au_dr_rename_fin, struct dentry *src, aufs_bindex_t btgt, void *rev);
++AuStubVoid(au_dr_rename_rev, struct dentry *src, aufs_bindex_t bindex,
++ void *rev);
++AuStubInt0(au_dr_lkup, struct au_do_lookup_args *lkup, struct dentry *dentry,
++ aufs_bindex_t bindex);
++AuStubInt0(au_dr_lkup_name, struct au_do_lookup_args *lkup, aufs_bindex_t btgt);
++AuStubInt0(au_dr_lkup_h_ino, struct au_do_lookup_args *lkup,
++ aufs_bindex_t bindex, ino_t h_ino);
++AuStubVoid(au_dr_lkup_fin, struct au_do_lookup_args *lkup);
++AuStubInt0(au_dr_opt_set, struct super_block *sb);
++AuStubInt0(au_dr_opt_flush, struct super_block *sb);
++AuStubInt0(au_dr_opt_clr, struct super_block *sb, int no_flush);
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_DIRREN
++static inline int au_dr_ihash(ino_t h_ino)
++{
++ return h_ino % AuDirren_NHASH;
++}
++#else
++AuStubInt0(au_dr_ihash, ino_t h_ino);
++#endif
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DIRREN_H__ */
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dynop.c linux-4.9/fs/aufs/dynop.c
+--- linux-4.9/fs/aufs/dynop.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dynop.c 2021-02-24 16:15:09.524906971 +0100
+@@ -0,0 +1,369 @@
++/*
++ * 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 <http://www.gnu.org/licenses/>.
++ */
++
++/*
++ * 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);
++ kfree(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) {
++ kfree(key);
++ key = old;
++ }
++
++out_add:
++ old = dy_bradd(br, key);
++ if (old)
++ /* its ref-count should never be zero here */
++ kref_put(&key->dk_kref, dy_bug);
++out:
++ return key;
++}
++
++/* ---------------------------------------------------------------------- */
++/*
++ * Aufs prohibits O_DIRECT by defaut even if the branch supports it.
++ * This behaviour is 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))
++ /* unnecessary to call dy_fput() */
++ goto out;
++
++ err = 0;
++ inode->i_mapping->a_ops = &dyaop->da_op;
++
++out:
++ return err;
++}
++
++/*
++ * Is it safe to replace a_ops during the inode/file is in operation?
++ * Yes, I hope so.
++ */
++int au_dy_irefresh(struct inode *inode)
+{
+ int err;
-+ aufs_bindex_t bindex, bbot;
-+ struct au_branch *br;
++ aufs_bindex_t btop;
++ struct inode *h_inode;
+
+ 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);
++ if (S_ISREG(inode->i_mode)) {
++ btop = au_ibtop(inode);
++ h_inode = au_h_iptr(inode, btop);
++ err = au_dy_iaop(inode, btop, h_inode);
+ }
-+
+ return err;
+}
+
-+int au_dr_opt_clr(struct super_block *sb, int no_flush)
++void au_dy_arefresh(int do_dx)
+{
-+ int err;
-+ aufs_bindex_t bindex, bbot;
-+ struct au_branch *br;
++ struct hlist_bl_head *hbl;
++ struct hlist_bl_node *pos;
++ struct au_dykey *key;
+
-+ err = 0;
-+ if (!no_flush) {
-+ err = au_dr_opt_flush(sb);
-+ if (unlikely(err))
-+ goto out;
-+ }
++ hbl = dynop + AuDy_AOP;
++ hlist_bl_lock(hbl);
++ hlist_bl_for_each_entry(key, pos, hbl, dk_hnode)
++ dy_adx((void *)key, do_dx);
++ hlist_bl_unlock(hbl);
++}
+
-+ 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;
++void __init au_dy_init(void)
++{
++ int i;
++
++ /* make sure that 'struct au_dykey *' can be any type */
++ BUILD_BUG_ON(offsetof(struct au_dyaop, da_key));
++
++ for (i = 0; i < AuDyLast; i++)
++ INIT_HLIST_BL_HEAD(dynop + i);
+}
-diff -urN /usr/share/empty/fs/aufs/dirren.h linux/fs/aufs/dirren.h
---- /usr/share/empty/fs/aufs/dirren.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dirren.h 2018-04-15 08:49:13.397817296 +0200
-@@ -0,0 +1,145 @@
++
++void au_dy_fin(void)
++{
++ int i;
++
++ for (i = 0; i < AuDyLast; i++)
++ WARN_ON(!hlist_bl_empty(dynop + i));
++}
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/dynop.h linux-4.9/fs/aufs/dynop.h
+--- linux-4.9/fs/aufs/dynop.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/dynop.h 2021-02-24 16:15:09.524906971 +0100
+@@ -0,0 +1,74 @@
+/*
-+ * Copyright (C) 2017-2018 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
+ */
+
+/*
-+ * renamed dir info
++ * dynamically customizable operations (for regular files only)
+ */
+
-+#ifndef __AUFS_DIRREN_H__
-+#define __AUFS_DIRREN_H__
++#ifndef __AUFS_DYNOP_H__
++#define __AUFS_DYNOP_H__
+
+#ifdef __KERNEL__
+
-+#include <linux/dcache.h>
-+#include <linux/statfs.h>
-+#include "hbl.h"
-+
-+#define AuDirren_NHASH 100
-+
-+#ifdef CONFIG_AUFS_DIRREN
-+/* copied from linux/fs/xfs/uuid.h */
-+typedef struct {
-+ unsigned char __u_bits[16];
-+} uuid_t;
-+
-+#define __UUID_TMPLT "01234567-0123-4567-0123-456701234567"
++#include <linux/fs.h>
++#include <linux/kref.h>
+
-+enum au_brid_type {
-+ AuBrid_Unset,
-+ AuBrid_UUID,
-+ AuBrid_FSID,
-+ AuBrid_DEV
-+};
++enum {AuDy_AOP, AuDyLast};
+
-+struct au_dr_brid {
-+ enum au_brid_type type;
++struct au_dynop {
++ int dy_type;
+ union {
-+ uuid_t uuid; /* unimplemented yet */
-+ fsid_t fsid;
-+ dev_t dev;
++ const void *dy_hop;
++ const struct address_space_operations *dy_haop;
+ };
+};
+
-+/* 20 is the max digits length of ulong 64 */
-+/* brid-type "_" uuid "_" inum */
-+#define AUFS_DIRREN_FNAME_SZ (1 + 1 + sizeof(__UUID_TMPLT) + 20)
-+#define AUFS_DIRREN_ENV_VAL_SZ (AUFS_DIRREN_FNAME_SZ + 1 + 20)
-+
-+struct au_dr_hino {
-+ struct hlist_bl_node dr_hnode;
-+ ino_t dr_h_ino;
-+};
++struct au_dykey {
++ union {
++ struct hlist_bl_node dk_hnode;
++ struct rcu_head dk_rcu;
++ };
++ struct au_dynop dk_op;
+
-+struct au_dr_br {
-+ struct hlist_bl_head dr_h_ino[AuDirren_NHASH];
-+ struct au_dr_brid dr_brid;
++ /*
++ * during I am in the branch local array, kref is gotten. when the
++ * branch is removed, kref is put.
++ */
++ struct kref dk_kref;
+};
+
-+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;
++/* stop unioning since their sizes are very different from each other */
++struct au_dyaop {
++ struct au_dykey da_key;
++ struct address_space_operations da_op; /* not const */
+};
-+#else
-+struct au_dr_hino;
-+/* empty */
-+struct au_dr_br { };
-+struct au_dr_lookup { };
-+#endif
+
+/* ---------------------------------------------------------------------- */
+
++/* dynop.c */
+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
-+
-+/* ---------------------------------------------------------------------- */
++void au_dy_put(struct au_dykey *key);
++int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex,
++ struct inode *h_inode);
++int au_dy_irefresh(struct inode *inode);
++void au_dy_arefresh(int do_dio);
+
-+#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
++void __init au_dy_init(void);
++void au_dy_fin(void);
+
+#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-04-15 08:49:13.397817296 +0200
-@@ -0,0 +1,369 @@
++#endif /* __AUFS_DYNOP_H__ */
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/export.c linux-4.9/fs/aufs/export.c
+--- linux-4.9/fs/aufs/export.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/export.c 2021-02-24 16:15:09.528240413 +0100
+@@ -0,0 +1,836 @@
+/*
-+ * Copyright (C) 2010-2018 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
-+/*
-+ * dynamically customizable operations for regular files
-+ */
++/*
++ * export via nfs
++ */
++
++#include <linux/exportfs.h>
++#include <linux/fs_struct.h>
++#include <linux/namei.h>
++#include <linux/nsproxy.h>
++#include <linux/random.h>
++#include <linux/writeback.h>
++#include "aufs.h"
++
++union conv {
++#ifdef CONFIG_AUFS_INO_T_64
++ __u32 a[2];
++#else
++ __u32 a[1];
++#endif
++ ino_t ino;
++};
++
++static ino_t decode_ino(__u32 *a)
++{
++ union conv u;
++
++ BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a));
++ u.a[0] = a[0];
++#ifdef CONFIG_AUFS_INO_T_64
++ u.a[1] = a[1];
++#endif
++ return u.ino;
++}
++
++static void encode_ino(__u32 *a, ino_t ino)
++{
++ union conv u;
++
++ u.ino = ino;
++ a[0] = u.a[0];
++#ifdef CONFIG_AUFS_INO_T_64
++ a[1] = u.a[1];
++#endif
++}
++
++/* NFS file handle */
++enum {
++ Fh_br_id,
++ Fh_sigen,
++#ifdef CONFIG_AUFS_INO_T_64
++ /* support 64bit inode number */
++ Fh_ino1,
++ Fh_ino2,
++ Fh_dir_ino1,
++ Fh_dir_ino2,
++#else
++ Fh_ino1,
++ Fh_dir_ino1,
++#endif
++ Fh_igen,
++ Fh_h_type,
++ Fh_tail,
++
++ Fh_ino = Fh_ino1,
++ Fh_dir_ino = Fh_dir_ino1
++};
++
++static int au_test_anon(struct dentry *dentry)
++{
++ /* note: read d_flags without d_lock */
++ return !!(dentry->d_flags & DCACHE_DISCONNECTED);
++}
++
++int au_test_nfsd(void)
++{
++ int ret;
++ struct task_struct *tsk = current;
++ char comm[sizeof(tsk->comm)];
++
++ ret = 0;
++ if (tsk->flags & PF_KTHREAD) {
++ get_task_comm(comm, tsk);
++ ret = !strcmp(comm, "nfsd");
++ }
++
++ return ret;
++}
++
++/* ---------------------------------------------------------------------- */
++/* inode generation external table */
++
++void au_xigen_inc(struct inode *inode)
++{
++ loff_t pos;
++ ssize_t sz;
++ __u32 igen;
++ struct super_block *sb;
++ struct au_sbinfo *sbinfo;
++
++ sb = inode->i_sb;
++ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO));
++
++ sbinfo = au_sbi(sb);
++ pos = inode->i_ino;
++ pos *= sizeof(igen);
++ igen = inode->i_generation + 1;
++ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen,
++ sizeof(igen), &pos);
++ if (sz == sizeof(igen))
++ return; /* success */
++
++ if (unlikely(sz >= 0))
++ AuIOErr("xigen error (%zd)\n", sz);
++}
++
++int au_xigen_new(struct inode *inode)
++{
++ int err;
++ loff_t pos;
++ ssize_t sz;
++ struct super_block *sb;
++ struct au_sbinfo *sbinfo;
++ struct file *file;
++
++ err = 0;
++ /* todo: dirty, at mount time */
++ if (inode->i_ino == AUFS_ROOT_INO)
++ goto out;
++ sb = inode->i_sb;
++ SiMustAnyLock(sb);
++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
++ goto out;
++
++ err = -EFBIG;
++ pos = inode->i_ino;
++ if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) {
++ AuIOErr1("too large i%lld\n", pos);
++ goto out;
++ }
++ pos *= sizeof(inode->i_generation);
++
++ err = 0;
++ sbinfo = au_sbi(sb);
++ file = sbinfo->si_xigen;
++ BUG_ON(!file);
++
++ if (vfsub_f_size_read(file)
++ < pos + sizeof(inode->i_generation)) {
++ inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next);
++ sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation,
++ sizeof(inode->i_generation), &pos);
++ } else
++ sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation,
++ sizeof(inode->i_generation), &pos);
++ if (sz == sizeof(inode->i_generation))
++ goto out; /* success */
++
++ err = sz;
++ if (unlikely(sz >= 0)) {
++ err = -EIO;
++ AuIOErr("xigen error (%zd)\n", sz);
++ }
++
++out:
++ return err;
++}
++
++int au_xigen_set(struct super_block *sb, struct file *base)
++{
++ int err;
++ struct au_sbinfo *sbinfo;
++ struct file *file;
++
++ SiMustWriteLock(sb);
++
++ sbinfo = au_sbi(sb);
++ file = au_xino_create2(base, sbinfo->si_xigen);
++ err = PTR_ERR(file);
++ if (IS_ERR(file))
++ goto out;
++ err = 0;
++ if (sbinfo->si_xigen)
++ fput(sbinfo->si_xigen);
++ sbinfo->si_xigen = file;
++
++out:
++ return err;
++}
++
++void au_xigen_clr(struct super_block *sb)
++{
++ struct au_sbinfo *sbinfo;
++
++ SiMustWriteLock(sb);
++
++ sbinfo = au_sbi(sb);
++ if (sbinfo->si_xigen) {
++ fput(sbinfo->si_xigen);
++ sbinfo->si_xigen = NULL;
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
++ ino_t dir_ino)
++{
++ struct dentry *dentry, *d;
++ struct inode *inode;
++ unsigned int sigen;
++
++ dentry = NULL;
++ inode = ilookup(sb, ino);
++ if (!inode)
++ goto out;
++
++ dentry = ERR_PTR(-ESTALE);
++ sigen = au_sigen(sb);
++ if (unlikely(au_is_bad_inode(inode)
++ || IS_DEADDIR(inode)
++ || sigen != au_iigen(inode, NULL)))
++ goto out_iput;
++
++ dentry = NULL;
++ if (!dir_ino || S_ISDIR(inode->i_mode))
++ dentry = d_find_alias(inode);
++ else {
++ spin_lock(&inode->i_lock);
++ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) {
++ spin_lock(&d->d_lock);
++ if (!au_test_anon(d)
++ && d_inode(d->d_parent)->i_ino == dir_ino) {
++ dentry = dget_dlock(d);
++ spin_unlock(&d->d_lock);
++ break;
++ }
++ spin_unlock(&d->d_lock);
++ }
++ spin_unlock(&inode->i_lock);
++ }
++ if (unlikely(dentry && au_digen_test(dentry, sigen))) {
++ /* need to refresh */
++ dput(dentry);
++ dentry = NULL;
++ }
++
++out_iput:
++ iput(inode);
++out:
++ AuTraceErrPtr(dentry);
++ return dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* todo: dirty? */
++/* if exportfs_decode_fh() passed vfsmount*, we could be happy */
++
++struct au_compare_mnt_args {
++ /* input */
++ struct super_block *sb;
++
++ /* output */
++ struct vfsmount *mnt;
++};
++
++static int au_compare_mnt(struct vfsmount *mnt, void *arg)
++{
++ struct au_compare_mnt_args *a = arg;
++
++ if (mnt->mnt_sb != a->sb)
++ return 0;
++ a->mnt = mntget(mnt);
++ return 1;
++}
++
++static struct vfsmount *au_mnt_get(struct super_block *sb)
++{
++ int err;
++ struct path root;
++ struct au_compare_mnt_args args = {
++ .sb = sb
++ };
++
++ get_fs_root(current->fs, &root);
++ rcu_read_lock();
++ err = iterate_mounts(au_compare_mnt, &args, root.mnt);
++ rcu_read_unlock();
++ path_put(&root);
++ AuDebugOn(!err);
++ AuDebugOn(!args.mnt);
++ return args.mnt;
++}
++
++struct au_nfsd_si_lock {
++ unsigned int sigen;
++ aufs_bindex_t bindex, br_id;
++ unsigned char force_lock;
++};
++
++static int si_nfsd_read_lock(struct super_block *sb,
++ struct au_nfsd_si_lock *nsi_lock)
++{
++ int err;
++ aufs_bindex_t bindex;
+
-+#include "aufs.h"
++ si_read_lock(sb, AuLock_FLUSH);
+
-+#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop)
++ /* branch id may be wrapped around */
++ err = 0;
++ bindex = au_br_index(sb, nsi_lock->br_id);
++ if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb))
++ goto out; /* success */
+
-+/*
-+ * 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];
++ err = -ESTALE;
++ bindex = -1;
++ if (!nsi_lock->force_lock)
++ si_read_unlock(sb);
+
-+static struct au_dykey *dy_gfind_get(struct hlist_bl_head *hbl,
-+ const void *h_op)
++out:
++ nsi_lock->bindex = bindex;
++ return err;
++}
++
++struct find_name_by_ino {
++ struct dir_context ctx;
++ int called, found;
++ ino_t ino;
++ char *name;
++ int namelen;
++};
++
++static int
++find_name_by_ino(struct dir_context *ctx, const char *name, int namelen,
++ loff_t offset, u64 ino, unsigned int d_type)
+{
-+ struct au_dykey *key, *tmp;
-+ struct hlist_bl_node *pos;
++ struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino,
++ ctx);
+
-+ 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);
++ a->called++;
++ if (a->ino != ino)
++ return 0;
+
-+ return key;
++ memcpy(a->name, name, namelen);
++ a->namelen = namelen;
++ a->found = 1;
++ return 1;
+}
+
-+static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key)
++static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino,
++ struct au_nfsd_si_lock *nsi_lock)
+{
-+ struct au_dykey **k, *found;
-+ const void *h_op = key->dk_op.dy_hop;
-+ int i;
++ struct dentry *dentry, *parent;
++ struct file *file;
++ struct inode *dir;
++ struct find_name_by_ino arg = {
++ .ctx = {
++ .actor = find_name_by_ino
++ }
++ };
++ int err;
+
-+ 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 */
++ parent = path->dentry;
++ if (nsi_lock)
++ si_read_unlock(parent->d_sb);
++ file = vfsub_dentry_open(path, au_dir_roflags);
++ dentry = (void *)file;
++ if (IS_ERR(file))
++ goto out;
++
++ dentry = ERR_PTR(-ENOMEM);
++ arg.name = (void *)__get_free_page(GFP_NOFS);
++ if (unlikely(!arg.name))
++ goto out_file;
++ arg.ino = ino;
++ arg.found = 0;
++ do {
++ arg.called = 0;
++ /* smp_mb(); */
++ err = vfsub_iterate_dir(file, &arg.ctx);
++ } while (!err && !arg.found && arg.called);
++ dentry = ERR_PTR(err);
++ if (unlikely(err))
++ goto out_name;
++ /* instead of ENOENT */
++ dentry = ERR_PTR(-ESTALE);
++ if (!arg.found)
++ goto out_name;
++
++ /* do not call vfsub_lkup_one() */
++ dir = d_inode(parent);
++ dentry = vfsub_lookup_one_len_unlocked(arg.name, parent, arg.namelen);
++ AuTraceErrPtr(dentry);
++ if (IS_ERR(dentry))
++ goto out_name;
++ AuDebugOn(au_test_anon(dentry));
++ if (unlikely(d_really_is_negative(dentry))) {
++ dput(dentry);
++ dentry = ERR_PTR(-ENOENT);
+ }
+
-+ return found;
++out_name:
++ free_page((unsigned long)arg.name);
++out_file:
++ fput(file);
++out:
++ if (unlikely(nsi_lock
++ && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0))
++ if (!IS_ERR(dentry)) {
++ dput(dentry);
++ dentry = ERR_PTR(-ESTALE);
++ }
++ AuTraceErrPtr(dentry);
++ return dentry;
+}
+
-+/* kref_get() if @key is already added */
-+static struct au_dykey *dy_gadd(struct hlist_bl_head *hbl, struct au_dykey *key)
++static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino,
++ ino_t dir_ino,
++ struct au_nfsd_si_lock *nsi_lock)
+{
-+ struct au_dykey *tmp, *found;
-+ struct hlist_bl_node *pos;
-+ const void *h_op = key->dk_op.dy_hop;
++ struct dentry *dentry;
++ struct path path;
+
-+ 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 (dir_ino != AUFS_ROOT_INO) {
++ path.dentry = decode_by_ino(sb, dir_ino, 0);
++ dentry = path.dentry;
++ if (!path.dentry || IS_ERR(path.dentry))
++ goto out;
++ AuDebugOn(au_test_anon(path.dentry));
++ } else
++ path.dentry = dget(sb->s_root);
+
-+ if (!found)
-+ DyPrSym(key);
-+ return found;
++ path.mnt = au_mnt_get(sb);
++ dentry = au_lkup_by_ino(&path, ino, nsi_lock);
++ path_put(&path);
++
++out:
++ AuTraceErrPtr(dentry);
++ return dentry;
+}
+
-+static void dy_free_rcu(struct rcu_head *rcu)
-+{
-+ struct au_dykey *key;
++/* ---------------------------------------------------------------------- */
+
-+ key = container_of(rcu, struct au_dykey, dk_rcu);
-+ DyPrSym(key);
-+ kfree(key);
++static int h_acceptable(void *expv, struct dentry *dentry)
++{
++ return 1;
+}
+
-+static void dy_free(struct kref *kref)
++static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath,
++ char *buf, int len, struct super_block *sb)
+{
-+ struct au_dykey *key;
-+ struct hlist_bl_head *hbl;
++ char *p;
++ int n;
++ struct path path;
+
-+ 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);
-+}
++ p = d_path(h_rootpath, buf, len);
++ if (IS_ERR(p))
++ goto out;
++ n = strlen(p);
+
-+void au_dy_put(struct au_dykey *key)
-+{
-+ kref_put(&key->dk_kref, dy_free);
-+}
++ path.mnt = h_rootpath->mnt;
++ path.dentry = h_parent;
++ p = d_path(&path, buf, len);
++ if (IS_ERR(p))
++ goto out;
++ if (n != 1)
++ p += n;
+
-+/* ---------------------------------------------------------------------- */
++ path.mnt = au_mnt_get(sb);
++ path.dentry = sb->s_root;
++ p = d_path(&path, buf, len - strlen(p));
++ mntput(path.mnt);
++ if (IS_ERR(p))
++ goto out;
++ if (n != 1)
++ p[strlen(p)] = '/';
+
-+#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *))
++out:
++ AuTraceErrPtr(p);
++ return p;
++}
+
-+#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
++static
++struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh,
++ int fh_len, struct au_nfsd_si_lock *nsi_lock)
++{
++ struct dentry *dentry, *h_parent, *root;
++ struct super_block *h_sb;
++ char *pathname, *p;
++ struct vfsmount *h_mnt;
++ struct au_branch *br;
++ int err;
++ struct path path;
+
-+#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)
++ br = au_sbr(sb, nsi_lock->bindex);
++ 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",
++ au_sbtype(h_sb), PTR_ERR(h_parent));
++ goto out;
++ }
++ dentry = NULL;
++ if (unlikely(au_test_anon(h_parent))) {
++ AuWarn1("%s decode_fh returned a disconnected dentry\n",
++ au_sbtype(h_sb));
++ goto out_h_parent;
++ }
+
-+#define DySetForce(func, dst, src) do { \
-+ AuDebugOn(!src.func); \
-+ DyDbgInc(cnt); \
-+ dst.func = src.func; \
-+} while (0)
++ dentry = ERR_PTR(-ENOMEM);
++ pathname = (void *)__get_free_page(GFP_NOFS);
++ if (unlikely(!pathname))
++ goto out_h_parent;
+
-+#define DySetAop(func) \
-+ DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb)
-+#define DySetAopForce(func) \
-+ DySetForce(func, dyaop->da_op, aufs_aop)
++ root = sb->s_root;
++ path.mnt = h_mnt;
++ di_read_lock_parent(root, !AuLock_IR);
++ path.dentry = au_h_dptr(root, nsi_lock->bindex);
++ di_read_unlock(root, !AuLock_IR);
++ p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb);
++ dentry = (void *)p;
++ if (IS_ERR(p))
++ goto out_pathname;
+
-+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);
++ si_read_unlock(sb);
++ err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
++ dentry = ERR_PTR(err);
++ if (unlikely(err))
++ goto out_relock;
+
-+ AuDbg("%s\n", au_sbtype(h_sb));
++ dentry = ERR_PTR(-ENOENT);
++ AuDebugOn(au_test_anon(path.dentry));
++ if (unlikely(d_really_is_negative(path.dentry)))
++ goto out_path;
+
-+ 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);
++ if (ino != d_inode(path.dentry)->i_ino)
++ dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL);
++ else
++ dentry = dget(path.dentry);
+
-+ DyDbgSize(cnt, *h_aop);
++out_path:
++ path_put(&path);
++out_relock:
++ if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0))
++ if (!IS_ERR(dentry)) {
++ dput(dentry);
++ dentry = ERR_PTR(-ESTALE);
++ }
++out_pathname:
++ free_page((unsigned long)pathname);
++out_h_parent:
++ dput(h_parent);
++out:
++ AuTraceErrPtr(dentry);
++ return dentry;
+}
+
+/* ---------------------------------------------------------------------- */
+
-+static void dy_bug(struct kref *kref)
-+{
-+ BUG();
-+}
-+
-+static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br)
++static struct dentry *
++aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
++ int fh_type)
+{
-+ 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
-+ }
++ struct dentry *dentry;
++ __u32 *fh = fid->raw;
++ struct au_branch *br;
++ ino_t ino, dir_ino;
++ struct au_nfsd_si_lock nsi_lock = {
++ .force_lock = 0
+ };
-+ const struct op *p;
+
-+ hbl = dynop + op->dy_type;
-+ key = dy_gfind_get(hbl, op->dy_hop);
-+ if (key)
-+ goto out_add; /* success */
++ dentry = ERR_PTR(-ESTALE);
++ /* it should never happen, but the file handle is unreliable */
++ if (unlikely(fh_len < Fh_tail))
++ goto out;
++ nsi_lock.sigen = fh[Fh_sigen];
++ nsi_lock.br_id = fh[Fh_br_id];
+
-+ p = a + op->dy_type;
-+ key = kzalloc(p->sz, GFP_NOFS);
-+ if (unlikely(!key)) {
-+ key = ERR_PTR(-ENOMEM);
++ /* branch id may be wrapped around */
++ br = NULL;
++ if (unlikely(si_nfsd_read_lock(sb, &nsi_lock)))
+ goto out;
-+ }
++ nsi_lock.force_lock = 1;
+
-+ 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) {
-+ kfree(key);
-+ key = old;
-+ }
++ /* is this inode still cached? */
++ ino = decode_ino(fh + Fh_ino);
++ /* it should never happen */
++ if (unlikely(ino == AUFS_ROOT_INO))
++ goto out_unlock;
+
-+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;
-+}
++ dir_ino = decode_ino(fh + Fh_dir_ino);
++ dentry = decode_by_ino(sb, ino, dir_ino);
++ if (IS_ERR(dentry))
++ goto out_unlock;
++ if (dentry)
++ goto accept;
+
-+/* ---------------------------------------------------------------------- */
-+/*
-+ * 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;
++ /* is the parent dir cached? */
++ br = au_sbr(sb, nsi_lock.bindex);
++ au_br_get(br);
++ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock);
++ if (IS_ERR(dentry))
++ goto out_unlock;
++ if (dentry)
++ goto accept;
++
++ /* lookup path */
++ dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock);
++ if (IS_ERR(dentry))
++ goto out_unlock;
++ if (unlikely(!dentry))
++ /* todo?: make it ESTALE */
++ goto out_unlock;
++
++accept:
++ if (!au_digen_test(dentry, au_sigen(sb))
++ && d_inode(dentry)->i_generation == fh[Fh_igen])
++ goto out_unlock; /* success */
++
++ dput(dentry);
++ dentry = ERR_PTR(-ESTALE);
++out_unlock:
++ if (br)
++ au_br_put(br);
++ si_read_unlock(sb);
++out:
++ AuTraceErrPtr(dentry);
++ return dentry;
+}
+
-+static struct au_dyaop *dy_aget(struct au_branch *br,
-+ const struct address_space_operations *h_aop,
-+ int do_dx)
++#if 0 /* reserved for future use */
++/* support subtreecheck option */
++static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid,
++ int fh_len, int fh_type)
+{
-+ struct au_dyaop *dyaop;
-+ struct au_dynop op;
++ struct dentry *parent;
++ __u32 *fh = fid->raw;
++ ino_t dir_ino;
+
-+ op.dy_type = AuDy_AOP;
-+ op.dy_haop = h_aop;
-+ dyaop = (void *)dy_get(&op, br);
-+ if (IS_ERR(dyaop))
++ dir_ino = decode_ino(fh + Fh_dir_ino);
++ parent = decode_by_ino(sb, dir_ino, 0);
++ if (IS_ERR(parent))
+ goto out;
-+ dy_adx(dyaop, do_dx);
++ if (!parent)
++ parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]),
++ dir_ino, fh, fh_len);
+
+out:
-+ return dyaop;
++ AuTraceErrPtr(parent);
++ return parent;
+}
++#endif
+
-+int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex,
-+ struct inode *h_inode)
++/* ---------------------------------------------------------------------- */
++
++static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len,
++ struct inode *dir)
+{
-+ int err, do_dx;
-+ struct super_block *sb;
++ int err;
++ aufs_bindex_t bindex;
++ struct super_block *sb, *h_sb;
++ struct dentry *dentry, *parent, *h_parent;
++ struct inode *h_dir;
+ struct au_branch *br;
-+ struct au_dyaop *dyaop;
+
-+ AuDebugOn(!S_ISREG(h_inode->i_mode));
-+ IiMustWriteLock(inode);
++ err = -ENOSPC;
++ if (unlikely(*max_len <= Fh_tail)) {
++ AuWarn1("NFSv2 client (max_len %d)?\n", *max_len);
++ goto out;
++ }
++
++ err = FILEID_ROOT;
++ if (inode->i_ino == AUFS_ROOT_INO) {
++ AuDebugOn(inode->i_ino != AUFS_ROOT_INO);
++ goto out;
++ }
+
++ h_parent = NULL;
+ sb = inode->i_sb;
-+ br = au_sbr(sb, bindex);
-+ do_dx = !!au_opt_test(au_mntflags(sb), DIO);
-+ dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx);
-+ err = PTR_ERR(dyaop);
-+ if (IS_ERR(dyaop))
-+ /* unnecessary to call dy_fput() */
++ err = si_read_lock(sb, AuLock_FLUSH);
++ if (unlikely(err))
+ goto out;
+
-+ err = 0;
-+ inode->i_mapping->a_ops = &dyaop->da_op;
++#ifdef CONFIG_AUFS_DEBUG
++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
++ AuWarn1("NFS-exporting requires xino\n");
++#endif
++ err = -EIO;
++ parent = NULL;
++ ii_read_lock_child(inode);
++ bindex = au_ibtop(inode);
++ if (!dir) {
++ dentry = d_find_any_alias(inode);
++ if (unlikely(!dentry))
++ goto out_unlock;
++ AuDebugOn(au_test_anon(dentry));
++ parent = dget_parent(dentry);
++ dput(dentry);
++ if (unlikely(!parent))
++ goto out_unlock;
++ if (d_really_is_positive(parent))
++ dir = d_inode(parent);
++ }
++
++ ii_read_lock_parent(dir);
++ h_dir = au_h_iptr(dir, bindex);
++ ii_read_unlock(dir);
++ if (unlikely(!h_dir))
++ goto out_parent;
++ h_parent = d_find_any_alias(h_dir);
++ if (unlikely(!h_parent))
++ goto out_hparent;
++
++ err = -EPERM;
++ br = au_sbr(sb, bindex);
++ h_sb = au_br_sb(br);
++ if (unlikely(!h_sb->s_export_op)) {
++ AuErr1("%s branch is not exportable\n", au_sbtype(h_sb));
++ goto out_hparent;
++ }
++
++ fh[Fh_br_id] = br->br_id;
++ fh[Fh_sigen] = au_sigen(sb);
++ encode_ino(fh + Fh_ino, inode->i_ino);
++ encode_ino(fh + Fh_dir_ino, dir->i_ino);
++ fh[Fh_igen] = inode->i_generation;
++
++ *max_len -= Fh_tail;
++ fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail),
++ max_len,
++ /*connectable or subtreecheck*/0);
++ err = fh[Fh_h_type];
++ *max_len += Fh_tail;
++ /* todo: macros? */
++ if (err != FILEID_INVALID)
++ err = 99;
++ else
++ AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb));
+
++out_hparent:
++ dput(h_parent);
++out_parent:
++ dput(parent);
++out_unlock:
++ ii_read_unlock(inode);
++ si_read_unlock(sb);
+out:
++ if (unlikely(err < 0))
++ err = FILEID_INVALID;
+ return err;
+}
+
-+/*
-+ * Is it safe to replace a_ops during the inode/file is in operation?
-+ * Yes, I hope so.
-+ */
-+int au_dy_irefresh(struct inode *inode)
++/* ---------------------------------------------------------------------- */
++
++static int aufs_commit_metadata(struct inode *inode)
+{
+ int err;
-+ aufs_bindex_t btop;
++ aufs_bindex_t bindex;
++ struct super_block *sb;
+ struct inode *h_inode;
++ int (*f)(struct inode *inode);
+
-+ err = 0;
-+ if (S_ISREG(inode->i_mode)) {
-+ btop = au_ibtop(inode);
-+ h_inode = au_h_iptr(inode, btop);
-+ err = au_dy_iaop(inode, btop, h_inode);
-+ }
-+ return err;
-+}
++ sb = inode->i_sb;
++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
++ ii_write_lock_child(inode);
++ bindex = au_ibtop(inode);
++ AuDebugOn(bindex < 0);
++ h_inode = au_h_iptr(inode, bindex);
+
-+void au_dy_arefresh(int do_dx)
-+{
-+ struct hlist_bl_head *hbl;
-+ struct hlist_bl_node *pos;
-+ struct au_dykey *key;
++ f = h_inode->i_sb->s_export_op->commit_metadata;
++ if (f)
++ err = f(h_inode);
++ else {
++ struct writeback_control wbc = {
++ .sync_mode = WB_SYNC_ALL,
++ .nr_to_write = 0 /* metadata only */
++ };
+
-+ hbl = dynop + AuDy_AOP;
-+ hlist_bl_lock(hbl);
-+ hlist_bl_for_each_entry(key, pos, hbl, dk_hnode)
-+ dy_adx((void *)key, do_dx);
-+ hlist_bl_unlock(hbl);
++ err = sync_inode(h_inode, &wbc);
++ }
++
++ au_cpup_attr_timesizes(inode);
++ ii_write_unlock(inode);
++ si_read_unlock(sb);
++ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
-+void __init au_dy_init(void)
-+{
-+ int i;
-+
-+ /* make sure that 'struct au_dykey *' can be any type */
-+ BUILD_BUG_ON(offsetof(struct au_dyaop, da_key));
-+
-+ for (i = 0; i < AuDyLast; i++)
-+ INIT_HLIST_BL_HEAD(dynop + i);
-+}
++static struct export_operations aufs_export_op = {
++ .fh_to_dentry = aufs_fh_to_dentry,
++ /* .fh_to_parent = aufs_fh_to_parent, */
++ .encode_fh = aufs_encode_fh,
++ .commit_metadata = aufs_commit_metadata
++};
+
-+void au_dy_fin(void)
++void au_export_init(struct super_block *sb)
+{
-+ int i;
++ struct au_sbinfo *sbinfo;
++ __u32 u;
+
-+ for (i = 0; i < AuDyLast; i++)
-+ WARN_ON(!hlist_bl_empty(dynop + i));
++ 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;
++ get_random_bytes(&u, sizeof(u));
++ BUILD_BUG_ON(sizeof(u) != sizeof(int));
++ atomic_set(&sbinfo->si_xigen_next, u);
+}
-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 2018-04-15 08:49:13.397817296 +0200
-@@ -0,0 +1,74 @@
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/f_op.c linux-4.9/fs/aufs/f_op.c
+--- linux-4.9/fs/aufs/f_op.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/f_op.c 2021-02-24 16:15:09.541574180 +0100
+@@ -0,0 +1,817 @@
+/*
-+ * Copyright (C) 2010-2018 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
+ */
+
+/*
-+ * dynamically customizable operations (for regular files only)
++ * file and vm operations
+ */
+
-+#ifndef __AUFS_DYNOP_H__
-+#define __AUFS_DYNOP_H__
++#include <linux/aio.h>
++#include <linux/fs_stack.h>
++#include <linux/mman.h>
++#include <linux/security.h>
++#include "aufs.h"
+
-+#ifdef __KERNEL__
++int au_do_open_nondir(struct file *file, int flags, struct file *h_file)
++{
++ int err;
++ aufs_bindex_t bindex;
++ struct dentry *dentry, *h_dentry;
++ struct au_finfo *finfo;
++ struct inode *h_inode;
+
-+#include <linux/fs.h>
-+#include <linux/kref.h>
++ FiMustWriteLock(file);
+
-+enum {AuDy_AOP, AuDyLast};
++ err = 0;
++ dentry = file->f_path.dentry;
++ AuDebugOn(IS_ERR_OR_NULL(dentry));
++ finfo = au_fi(file);
++ memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop));
++ atomic_set(&finfo->fi_mmapped, 0);
++ 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 {
++ h_dentry = h_file->f_path.dentry;
++ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb);
++ if (unlikely(err))
++ goto out;
++ 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_fbtop(file, bindex);
++ au_set_h_fptr(file, bindex, h_file);
++ au_update_figen(file);
++ /* todo: necessary? */
++ /* file->f_ra = h_file->f_ra; */
++ }
+
-+struct au_dynop {
-+ int dy_type;
-+ union {
-+ const void *dy_hop;
-+ const struct address_space_operations *dy_haop;
-+ };
-+};
++out:
++ return err;
++}
+
-+struct au_dykey {
-+ union {
-+ struct hlist_bl_node dk_hnode;
-+ struct rcu_head dk_rcu;
++static int aufs_open_nondir(struct inode *inode __maybe_unused,
++ struct file *file)
++{
++ int err;
++ struct super_block *sb;
++ struct au_do_open_args args = {
++ .open = au_do_open_nondir
+ };
-+ struct au_dynop dk_op;
+
-+ /*
-+ * during I am in the branch local array, kref is gotten. when the
-+ * branch is removed, kref is put.
-+ */
-+ struct kref dk_kref;
-+};
++ AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n",
++ file, vfsub_file_flags(file), file->f_mode);
+
-+/* stop unioning since their sizes are very different from each other */
-+struct au_dyaop {
-+ struct au_dykey da_key;
-+ struct address_space_operations da_op; /* not const */
-+};
++ sb = file->f_path.dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++ err = au_do_open(file, &args);
++ si_read_unlock(sb);
++ return err;
++}
++
++int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file)
++{
++ struct au_finfo *finfo;
++ aufs_bindex_t bindex;
++
++ finfo = au_fi(file);
++ 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);
++
++ au_finfo_fin(file);
++ return 0;
++}
+
+/* ---------------------------------------------------------------------- */
+
-+/* dynop.c */
-+struct au_branch;
-+void au_dy_put(struct au_dykey *key);
-+int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex,
-+ struct inode *h_inode);
-+int au_dy_irefresh(struct inode *inode);
-+void au_dy_arefresh(int do_dio);
++static int au_do_flush_nondir(struct file *file, fl_owner_t id)
++{
++ int err;
++ struct file *h_file;
+
-+void __init au_dy_init(void);
-+void au_dy_fin(void);
++ err = 0;
++ h_file = au_hf_top(file);
++ if (h_file)
++ err = vfsub_flush(h_file, id);
++ return err;
++}
+
-+#endif /* __KERNEL__ */
-+#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 2018-04-15 08:49:13.397817296 +0200
-@@ -0,0 +1,836 @@
-+/*
-+ * 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
-+ * 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 <http://www.gnu.org/licenses/>.
-+ */
++static int aufs_flush_nondir(struct file *file, fl_owner_t id)
++{
++ return au_do_flush(file, id, au_do_flush_nondir);
++}
+
++/* ---------------------------------------------------------------------- */
+/*
-+ * export via nfs
++ * read and write functions acquire [fdi]_rwsem once, but release before
++ * mmap_sem. This is because to stop a race condition between mmap(2).
++ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping
++ * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in
++ * read functions after [fdi]_rwsem are released, but it should be harmless.
+ */
+
-+#include <linux/exportfs.h>
-+#include <linux/fs_struct.h>
-+#include <linux/namei.h>
-+#include <linux/nsproxy.h>
-+#include <linux/random.h>
-+#include <linux/writeback.h>
-+#include "aufs.h"
-+
-+union conv {
-+#ifdef CONFIG_AUFS_INO_T_64
-+ __u32 a[2];
-+#else
-+ __u32 a[1];
-+#endif
-+ ino_t ino;
-+};
-+
-+static ino_t decode_ino(__u32 *a)
++/* Callers should call au_read_post() or fput() in the end */
++struct file *au_read_pre(struct file *file, int keep_fi, unsigned int lsc)
+{
-+ union conv u;
++ struct file *h_file;
++ int err;
+
-+ BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a));
-+ u.a[0] = a[0];
-+#ifdef CONFIG_AUFS_INO_T_64
-+ u.a[1] = a[1];
-+#endif
-+ return u.ino;
++ 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);
++ get_file(h_file);
++ if (!keep_fi)
++ fi_read_unlock(file);
++ } else
++ h_file = ERR_PTR(err);
++
++ return h_file;
+}
+
-+static void encode_ino(__u32 *a, ino_t ino)
++static void au_read_post(struct inode *inode, struct file *h_file)
+{
-+ union conv u;
-+
-+ u.ino = ino;
-+ a[0] = u.a[0];
-+#ifdef CONFIG_AUFS_INO_T_64
-+ a[1] = u.a[1];
-+#endif
++ /* update without lock, I don't think it a problem */
++ fsstack_copy_attr_atime(inode, file_inode(h_file));
++ fput(h_file);
+}
+
-+/* NFS file handle */
-+enum {
-+ Fh_br_id,
-+ Fh_sigen,
-+#ifdef CONFIG_AUFS_INO_T_64
-+ /* support 64bit inode number */
-+ Fh_ino1,
-+ Fh_ino2,
-+ Fh_dir_ino1,
-+ Fh_dir_ino2,
-+#else
-+ Fh_ino1,
-+ Fh_dir_ino1,
-+#endif
-+ Fh_igen,
-+ Fh_h_type,
-+ Fh_tail,
++struct au_write_pre {
++ /* input */
++ unsigned int lsc;
+
-+ Fh_ino = Fh_ino1,
-+ Fh_dir_ino = Fh_dir_ino1
++ /* output */
++ blkcnt_t blks;
++ aufs_bindex_t btop;
+};
+
-+static int au_test_anon(struct dentry *dentry)
++/*
++ * return with iinfo is write-locked
++ * callers should call au_write_post() or iinfo_write_unlock() + fput() in the
++ * end
++ */
++static struct file *au_write_pre(struct file *file, int do_ready,
++ struct au_write_pre *wpre)
+{
-+ /* note: read d_flags without d_lock */
-+ return !!(dentry->d_flags & DCACHE_DISCONNECTED);
-+}
++ struct file *h_file;
++ struct dentry *dentry;
++ int err;
++ unsigned int lsc;
++ struct au_pin pin;
+
-+int au_test_nfsd(void)
-+{
-+ int ret;
-+ struct task_struct *tsk = current;
-+ char comm[sizeof(tsk->comm)];
++ 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;
+
-+ ret = 0;
-+ if (tsk->flags & PF_KTHREAD) {
-+ get_task_comm(comm, tsk);
-+ ret = !strcmp(comm, "nfsd");
++ dentry = file->f_path.dentry;
++ if (do_ready) {
++ err = au_ready_to_write(file, -1, &pin);
++ if (unlikely(err)) {
++ h_file = ERR_PTR(err);
++ di_write_unlock(dentry);
++ goto out_fi;
++ }
+ }
+
-+ return ret;
++ di_downgrade_lock(dentry, /*flags*/0);
++ if (wpre)
++ wpre->btop = au_fbtop(file);
++ h_file = au_hf_top(file);
++ get_file(h_file);
++ if (wpre)
++ wpre->blks = file_inode(h_file)->i_blocks;
++ if (do_ready)
++ au_unpin(&pin);
++ di_read_unlock(dentry, /*flags*/0);
++
++out_fi:
++ fi_write_unlock(file);
++out:
++ return h_file;
+}
+
-+/* ---------------------------------------------------------------------- */
-+/* inode generation external table */
++static void au_write_post(struct inode *inode, struct file *h_file,
++ struct au_write_pre *wpre, ssize_t written)
++{
++ struct inode *h_inode;
++
++ au_cpup_attr_timesizes(inode);
++ AuDebugOn(au_ibtop(inode) != wpre->btop);
++ h_inode = file_inode(h_file);
++ inode->i_mode = h_inode->i_mode;
++ ii_write_unlock(inode);
++ /* AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); */
++ if (written > 0)
++ au_fhsm_wrote(inode->i_sb, wpre->btop,
++ /*force*/h_inode->i_blocks > wpre->blks);
++ fput(h_file);
++}
+
-+void au_xigen_inc(struct inode *inode)
++static ssize_t aufs_read(struct file *file, char __user *buf, size_t count,
++ loff_t *ppos)
+{
-+ loff_t pos;
-+ ssize_t sz;
-+ __u32 igen;
++ ssize_t err;
++ struct inode *inode;
++ struct file *h_file;
+ struct super_block *sb;
-+ struct au_sbinfo *sbinfo;
+
++ inode = file_inode(file);
+ sb = inode->i_sb;
-+ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO));
++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
+
-+ sbinfo = au_sbi(sb);
-+ pos = inode->i_ino;
-+ pos *= sizeof(igen);
-+ igen = inode->i_generation + 1;
-+ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen,
-+ sizeof(igen), &pos);
-+ if (sz == sizeof(igen))
-+ return; /* success */
++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out;
+
-+ if (unlikely(sz >= 0))
-+ AuIOErr("xigen error (%zd)\n", sz);
++ /* filedata may be obsoleted by concurrent copyup, but no problem */
++ err = vfsub_read_u(h_file, buf, count, ppos);
++ /* todo: necessary? */
++ /* file->f_ra = h_file->f_ra; */
++ au_read_post(inode, h_file);
++
++out:
++ si_read_unlock(sb);
++ return err;
+}
+
-+int au_xigen_new(struct inode *inode)
++/*
++ * todo: very ugly
++ * it locks both of i_mutex and si_rwsem for read in safe.
++ * if the plink maintenance mode continues forever (that is the problem),
++ * may loop forever.
++ */
++static void au_mtx_and_read_lock(struct inode *inode)
+{
+ int err;
-+ loff_t pos;
-+ ssize_t sz;
-+ struct super_block *sb;
-+ struct au_sbinfo *sbinfo;
-+ struct file *file;
-+
-+ err = 0;
-+ /* todo: dirty, at mount time */
-+ if (inode->i_ino == AUFS_ROOT_INO)
-+ goto out;
-+ sb = inode->i_sb;
-+ SiMustAnyLock(sb);
-+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
-+ goto out;
++ struct super_block *sb = inode->i_sb;
+
-+ err = -EFBIG;
-+ pos = inode->i_ino;
-+ if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) {
-+ AuIOErr1("too large i%lld\n", pos);
-+ goto out;
++ while (1) {
++ inode_lock(inode);
++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
++ if (!err)
++ break;
++ inode_unlock(inode);
++ si_read_lock(sb, AuLock_NOPLMW);
++ si_read_unlock(sb);
+ }
-+ pos *= sizeof(inode->i_generation);
++}
+
-+ err = 0;
-+ sbinfo = au_sbi(sb);
-+ file = sbinfo->si_xigen;
-+ BUG_ON(!file);
++static ssize_t aufs_write(struct file *file, const char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ ssize_t err;
++ struct au_write_pre wpre;
++ struct inode *inode;
++ struct file *h_file;
++ char __user *buf = (char __user *)ubuf;
+
-+ if (vfsub_f_size_read(file)
-+ < pos + sizeof(inode->i_generation)) {
-+ inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next);
-+ sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation,
-+ sizeof(inode->i_generation), &pos);
-+ } else
-+ sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation,
-+ sizeof(inode->i_generation), &pos);
-+ if (sz == sizeof(inode->i_generation))
-+ goto out; /* success */
++ inode = file_inode(file);
++ au_mtx_and_read_lock(inode);
+
-+ err = sz;
-+ if (unlikely(sz >= 0)) {
-+ err = -EIO;
-+ AuIOErr("xigen error (%zd)\n", sz);
-+ }
++ wpre.lsc = 0;
++ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out;
++
++ err = vfsub_write_u(h_file, buf, count, ppos);
++ au_write_post(inode, h_file, &wpre, err);
+
+out:
++ si_read_unlock(inode->i_sb);
++ inode_unlock(inode);
+ return err;
+}
+
-+int au_xigen_set(struct super_block *sb, struct file *base)
++static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio,
++ struct iov_iter *iov_iter)
+{
-+ int err;
-+ struct au_sbinfo *sbinfo;
++ ssize_t err;
+ struct file *file;
++ ssize_t (*iter)(struct kiocb *, struct iov_iter *);
+
-+ SiMustWriteLock(sb);
-+
-+ sbinfo = au_sbi(sb);
-+ file = au_xino_create2(base, sbinfo->si_xigen);
-+ err = PTR_ERR(file);
-+ if (IS_ERR(file))
++ err = security_file_permission(h_file, rw);
++ if (unlikely(err))
+ goto out;
-+ err = 0;
-+ if (sbinfo->si_xigen)
-+ fput(sbinfo->si_xigen);
-+ sbinfo->si_xigen = file;
++
++ err = -ENOSYS;
++ iter = NULL;
++ if (rw == MAY_READ)
++ iter = h_file->f_op->read_iter;
++ else if (rw == MAY_WRITE)
++ iter = h_file->f_op->write_iter;
++
++ file = kio->ki_filp;
++ kio->ki_filp = h_file;
++ if (iter) {
++ lockdep_off();
++ err = iter(kio, iov_iter);
++ lockdep_on();
++ } else
++ /* currently there is no such fs */
++ WARN_ON_ONCE(1);
++ kio->ki_filp = file;
+
+out:
+ return err;
+}
+
-+void au_xigen_clr(struct super_block *sb)
++static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter)
+{
-+ struct au_sbinfo *sbinfo;
++ ssize_t err;
++ struct file *file, *h_file;
++ struct inode *inode;
++ struct super_block *sb;
+
-+ SiMustWriteLock(sb);
++ file = kio->ki_filp;
++ inode = file_inode(file);
++ sb = inode->i_sb;
++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
+
-+ sbinfo = au_sbi(sb);
-+ if (sbinfo->si_xigen) {
-+ fput(sbinfo->si_xigen);
-+ sbinfo->si_xigen = NULL;
++ h_file = au_read_pre(file, /*keep_fi*/1, /*lsc*/0);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out;
++
++ if (0 && 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; */
++ au_read_post(inode, h_file);
+
-+static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
-+ ino_t dir_ino)
++out:
++ si_read_unlock(sb);
++ return err;
++}
++
++static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter)
+{
-+ struct dentry *dentry, *d;
++ ssize_t err;
++ struct au_write_pre wpre;
+ struct inode *inode;
-+ unsigned int sigen;
++ struct file *file, *h_file;
+
-+ dentry = NULL;
-+ inode = ilookup(sb, ino);
-+ if (!inode)
-+ goto out;
++ file = kio->ki_filp;
++ inode = file_inode(file);
++ au_mtx_and_read_lock(inode);
+
-+ dentry = ERR_PTR(-ESTALE);
-+ sigen = au_sigen(sb);
-+ if (unlikely(au_is_bad_inode(inode)
-+ || IS_DEADDIR(inode)
-+ || sigen != au_iigen(inode, NULL)))
-+ goto out_iput;
++ wpre.lsc = 0;
++ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out;
+
-+ dentry = NULL;
-+ if (!dir_ino || S_ISDIR(inode->i_mode))
-+ dentry = d_find_alias(inode);
-+ else {
-+ spin_lock(&inode->i_lock);
-+ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) {
-+ spin_lock(&d->d_lock);
-+ if (!au_test_anon(d)
-+ && d_inode(d->d_parent)->i_ino == dir_ino) {
-+ dentry = dget_dlock(d);
-+ spin_unlock(&d->d_lock);
-+ break;
-+ }
-+ spin_unlock(&d->d_lock);
-+ }
-+ spin_unlock(&inode->i_lock);
-+ }
-+ if (unlikely(dentry && au_digen_test(dentry, sigen))) {
-+ /* need to refresh */
-+ dput(dentry);
-+ dentry = NULL;
-+ }
++ err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter);
++ au_write_post(inode, h_file, &wpre, err);
+
-+out_iput:
-+ iput(inode);
+out:
-+ AuTraceErrPtr(dentry);
-+ return dentry;
++ si_read_unlock(inode->i_sb);
++ inode_unlock(inode);
++ return err;
+}
+
-+/* ---------------------------------------------------------------------- */
-+
-+/* todo: dirty? */
-+/* if exportfs_decode_fh() passed vfsmount*, we could be happy */
-+
-+struct au_compare_mnt_args {
-+ /* input */
++static ssize_t aufs_splice_read(struct file *file, loff_t *ppos,
++ struct pipe_inode_info *pipe, size_t len,
++ unsigned int flags)
++{
++ ssize_t err;
++ struct file *h_file;
++ struct inode *inode;
+ struct super_block *sb;
+
-+ /* output */
-+ struct vfsmount *mnt;
-+};
-+
-+static int au_compare_mnt(struct vfsmount *mnt, void *arg)
-+{
-+ struct au_compare_mnt_args *a = arg;
++ inode = file_inode(file);
++ sb = inode->i_sb;
++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
+
-+ if (mnt->mnt_sb != a->sb)
-+ return 0;
-+ a->mnt = mntget(mnt);
-+ return 1;
-+}
++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out;
+
-+static struct vfsmount *au_mnt_get(struct super_block *sb)
-+{
-+ int err;
-+ struct path root;
-+ struct au_compare_mnt_args args = {
-+ .sb = sb
-+ };
++ err = vfsub_splice_to(h_file, ppos, pipe, len, flags);
++ /* todo: necessasry? */
++ /* file->f_ra = h_file->f_ra; */
++ au_read_post(inode, h_file);
+
-+ get_fs_root(current->fs, &root);
-+ rcu_read_lock();
-+ err = iterate_mounts(au_compare_mnt, &args, root.mnt);
-+ rcu_read_unlock();
-+ path_put(&root);
-+ AuDebugOn(!err);
-+ AuDebugOn(!args.mnt);
-+ return args.mnt;
++out:
++ si_read_unlock(sb);
++ return err;
+}
+
-+struct au_nfsd_si_lock {
-+ unsigned int sigen;
-+ aufs_bindex_t bindex, br_id;
-+ unsigned char force_lock;
-+};
-+
-+static int si_nfsd_read_lock(struct super_block *sb,
-+ struct au_nfsd_si_lock *nsi_lock)
++static ssize_t
++aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos,
++ size_t len, unsigned int flags)
+{
-+ int err;
-+ aufs_bindex_t bindex;
++ ssize_t err;
++ struct au_write_pre wpre;
++ struct inode *inode;
++ struct file *h_file;
+
-+ si_read_lock(sb, AuLock_FLUSH);
++ inode = file_inode(file);
++ au_mtx_and_read_lock(inode);
+
-+ /* branch id may be wrapped around */
-+ err = 0;
-+ bindex = au_br_index(sb, nsi_lock->br_id);
-+ if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb))
-+ goto out; /* success */
++ wpre.lsc = 0;
++ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out;
+
-+ err = -ESTALE;
-+ bindex = -1;
-+ if (!nsi_lock->force_lock)
-+ si_read_unlock(sb);
++ err = vfsub_splice_from(pipe, h_file, ppos, len, flags);
++ au_write_post(inode, h_file, &wpre, err);
+
+out:
-+ nsi_lock->bindex = bindex;
++ si_read_unlock(inode->i_sb);
++ inode_unlock(inode);
+ return err;
+}
+
-+struct find_name_by_ino {
-+ struct dir_context ctx;
-+ int called, found;
-+ ino_t ino;
-+ char *name;
-+ int namelen;
-+};
-+
-+static int
-+find_name_by_ino(struct dir_context *ctx, const char *name, int namelen,
-+ loff_t offset, u64 ino, unsigned int d_type)
++static long aufs_fallocate(struct file *file, int mode, loff_t offset,
++ loff_t len)
+{
-+ struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino,
-+ ctx);
++ long err;
++ struct au_write_pre wpre;
++ struct inode *inode;
++ struct file *h_file;
+
-+ a->called++;
-+ if (a->ino != ino)
-+ return 0;
++ inode = file_inode(file);
++ au_mtx_and_read_lock(inode);
+
-+ memcpy(a->name, name, namelen);
-+ a->namelen = namelen;
-+ a->found = 1;
-+ return 1;
++ wpre.lsc = 0;
++ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out;
++
++ lockdep_off();
++ err = vfs_fallocate(h_file, mode, offset, len);
++ lockdep_on();
++ au_write_post(inode, h_file, &wpre, /*written*/1);
++
++out:
++ si_read_unlock(inode->i_sb);
++ inode_unlock(inode);
++ return err;
+}
+
-+static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino,
-+ struct au_nfsd_si_lock *nsi_lock)
++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)
+{
-+ struct dentry *dentry, *parent;
-+ struct file *file;
-+ struct inode *dir;
-+ struct find_name_by_ino arg = {
-+ .ctx = {
-+ .actor = find_name_by_ino
-+ }
-+ };
-+ int err;
++ 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]
+
-+ parent = path->dentry;
-+ if (nsi_lock)
-+ si_read_unlock(parent->d_sb);
-+ file = vfsub_dentry_open(path, au_dir_roflags);
-+ dentry = (void *)file;
-+ if (IS_ERR(file))
++ 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;
+
-+ dentry = ERR_PTR(-ENOMEM);
-+ arg.name = (void *)__get_free_page(GFP_NOFS);
-+ if (unlikely(!arg.name))
-+ goto out_file;
-+ arg.ino = ino;
-+ arg.found = 0;
-+ do {
-+ arg.called = 0;
-+ /* smp_mb(); */
-+ err = vfsub_iterate_dir(file, &arg.ctx);
-+ } while (!err && !arg.found && arg.called);
-+ dentry = ERR_PTR(err);
-+ if (unlikely(err))
-+ goto out_name;
-+ /* instead of ENOENT */
-+ dentry = ERR_PTR(-ESTALE);
-+ if (!arg.found)
-+ goto out_name;
-+
-+ /* do not call vfsub_lkup_one() */
-+ dir = d_inode(parent);
-+ dentry = vfsub_lookup_one_len_unlocked(arg.name, parent, arg.namelen);
-+ AuTraceErrPtr(dentry);
-+ if (IS_ERR(dentry))
-+ goto out_name;
-+ AuDebugOn(au_test_anon(dentry));
-+ if (unlikely(d_really_is_negative(dentry))) {
-+ dput(dentry);
-+ dentry = ERR_PTR(-ENOENT);
-+ }
++ au_mtx_and_read_lock(a_dst.inode);
++ /*
++ * in order to match the order in di_write_lock2_{child,parent}(),
++ * use f_path.dentry for this comparision.
++ */
++ if (src->f_path.dentry < dst->f_path.dentry) {
++ a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_1);
++ err = PTR_ERR(a_src.h_file);
++ if (IS_ERR(a_src.h_file))
++ goto out_si;
+
-+out_name:
-+ free_page((unsigned long)arg.name);
-+out_file:
-+ fput(file);
-+out:
-+ if (unlikely(nsi_lock
-+ && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0))
-+ if (!IS_ERR(dentry)) {
-+ dput(dentry);
-+ dentry = ERR_PTR(-ESTALE);
++ 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;
+ }
-+ AuTraceErrPtr(dentry);
-+ return dentry;
-+}
++ } 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;
+
-+static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino,
-+ ino_t dir_ino,
-+ struct au_nfsd_si_lock *nsi_lock)
-+{
-+ struct dentry *dentry;
-+ struct path path;
++ 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;
++ }
++ }
+
-+ if (dir_ino != AUFS_ROOT_INO) {
-+ path.dentry = decode_by_ino(sb, dir_ino, 0);
-+ dentry = path.dentry;
-+ if (!path.dentry || IS_ERR(path.dentry))
-+ goto out;
-+ AuDebugOn(au_test_anon(path.dentry));
-+ } else
-+ path.dentry = dget(sb->s_root);
++ 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;
++ }
+
-+ path.mnt = au_mnt_get(sb);
-+ dentry = au_lkup_by_ino(&path, ino, nsi_lock);
-+ path_put(&path);
++ 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:
-+ AuTraceErrPtr(dentry);
-+ return dentry;
++ return err;
++#undef a_src
++#undef a_dst
+}
+
+/* ---------------------------------------------------------------------- */
+
-+static int h_acceptable(void *expv, struct dentry *dentry)
++/*
++ * The locking order around current->mmap_sem.
++ * - in most and regular cases
++ * file I/O syscall -- aufs_read() or something
++ * -- si_rwsem for read -- mmap_sem
++ * (Note that [fdi]i_rwsem are released before mmap_sem).
++ * - in mmap case
++ * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem
++ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for
++ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in
++ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though.
++ * It means that when aufs acquires si_rwsem for write, the process should never
++ * acquire mmap_sem.
++ *
++ * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a
++ * problem either since any directory is not able to be mmap-ed.
++ * The similar scenario is applied to aufs_readlink() too.
++ */
++
++#if 0 /* stop calling security_file_mmap() */
++/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */
++#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b)
++
++static unsigned long au_arch_prot_conv(unsigned long flags)
+{
-+ return 1;
++ /* currently ppc64 only */
++#ifdef CONFIG_PPC64
++ /* cf. linux/arch/powerpc/include/asm/mman.h */
++ AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO);
++ return AuConv_VM_PROT(flags, SAO);
++#else
++ AuDebugOn(arch_calc_vm_prot_bits(-1));
++ return 0;
++#endif
+}
+
-+static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath,
-+ char *buf, int len, struct super_block *sb)
++static unsigned long au_prot_conv(unsigned long flags)
+{
-+ char *p;
-+ int n;
-+ struct path path;
-+
-+ p = d_path(h_rootpath, buf, len);
-+ if (IS_ERR(p))
-+ goto out;
-+ n = strlen(p);
-+
-+ path.mnt = h_rootpath->mnt;
-+ path.dentry = h_parent;
-+ p = d_path(&path, buf, len);
-+ if (IS_ERR(p))
-+ goto out;
-+ if (n != 1)
-+ p += n;
++ return AuConv_VM_PROT(flags, READ)
++ | AuConv_VM_PROT(flags, WRITE)
++ | AuConv_VM_PROT(flags, EXEC)
++ | au_arch_prot_conv(flags);
++}
+
-+ path.mnt = au_mnt_get(sb);
-+ path.dentry = sb->s_root;
-+ p = d_path(&path, buf, len - strlen(p));
-+ mntput(path.mnt);
-+ if (IS_ERR(p))
-+ goto out;
-+ if (n != 1)
-+ p[strlen(p)] = '/';
++/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */
++#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b)
+
-+out:
-+ AuTraceErrPtr(p);
-+ return p;
++static unsigned long au_flag_conv(unsigned long flags)
++{
++ return AuConv_VM_MAP(flags, GROWSDOWN)
++ | AuConv_VM_MAP(flags, DENYWRITE)
++ | AuConv_VM_MAP(flags, LOCKED);
+}
++#endif
+
-+static
-+struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh,
-+ int fh_len, struct au_nfsd_si_lock *nsi_lock)
++static int aufs_mmap(struct file *file, struct vm_area_struct *vma)
+{
-+ struct dentry *dentry, *h_parent, *root;
-+ struct super_block *h_sb;
-+ char *pathname, *p;
-+ struct vfsmount *h_mnt;
-+ struct au_branch *br;
+ int err;
-+ struct path path;
++ const unsigned char wlock
++ = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED);
++ struct super_block *sb;
++ struct file *h_file;
++ struct inode *inode;
+
-+ br = au_sbr(sb, nsi_lock->bindex);
-+ h_mnt = au_br_mnt(br);
-+ h_sb = h_mnt->mnt_sb;
-+ /* todo: call lower fh_to_dentry()? fh_to_parent()? */
++ AuDbgVmRegion(file, vma);
++
++ inode = file_inode(file);
++ sb = inode->i_sb;
+ 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);
++ si_read_lock(sb, AuLock_NOPLMW);
++
++ h_file = au_write_pre(file, wlock, /*wpre*/NULL);
+ lockdep_on();
-+ dentry = h_parent;
-+ if (unlikely(!h_parent || IS_ERR(h_parent))) {
-+ AuWarn1("%s decode_fh failed, %ld\n",
-+ au_sbtype(h_sb), PTR_ERR(h_parent));
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
+ goto out;
-+ }
-+ dentry = NULL;
-+ if (unlikely(au_test_anon(h_parent))) {
-+ AuWarn1("%s decode_fh returned a disconnected dentry\n",
-+ au_sbtype(h_sb));
-+ goto out_h_parent;
-+ }
-+
-+ dentry = ERR_PTR(-ENOMEM);
-+ pathname = (void *)__get_free_page(GFP_NOFS);
-+ if (unlikely(!pathname))
-+ goto out_h_parent;
-+
-+ root = sb->s_root;
-+ path.mnt = h_mnt;
-+ di_read_lock_parent(root, !AuLock_IR);
-+ path.dentry = au_h_dptr(root, nsi_lock->bindex);
-+ di_read_unlock(root, !AuLock_IR);
-+ p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb);
-+ dentry = (void *)p;
-+ if (IS_ERR(p))
-+ goto out_pathname;
-+
-+ si_read_unlock(sb);
-+ err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
-+ dentry = ERR_PTR(err);
-+ if (unlikely(err))
-+ goto out_relock;
-+
-+ dentry = ERR_PTR(-ENOENT);
-+ AuDebugOn(au_test_anon(path.dentry));
-+ if (unlikely(d_really_is_negative(path.dentry)))
-+ goto out_path;
+
-+ if (ino != d_inode(path.dentry)->i_ino)
-+ dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL);
-+ else
-+ dentry = dget(path.dentry);
++ err = 0;
++ au_set_mmapped(file);
++ au_vm_file_reset(vma, h_file);
++ /*
++ * we cannot call security_mmap_file() here since it may acquire
++ * mmap_sem or i_mutex.
++ *
++ * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags),
++ * au_flag_conv(vma->vm_flags));
++ */
++ if (!err)
++ err = h_file->f_op->mmap(h_file, vma);
++ if (!err) {
++ au_vm_prfile_set(vma, file);
++ fsstack_copy_attr_atime(inode, file_inode(h_file));
++ goto out_fput; /* success */
++ }
++ au_unset_mmapped(file);
++ au_vm_file_reset(vma, file);
+
-+out_path:
-+ path_put(&path);
-+out_relock:
-+ if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0))
-+ if (!IS_ERR(dentry)) {
-+ dput(dentry);
-+ dentry = ERR_PTR(-ESTALE);
-+ }
-+out_pathname:
-+ free_page((unsigned long)pathname);
-+out_h_parent:
-+ dput(h_parent);
++out_fput:
++ lockdep_off();
++ ii_write_unlock(inode);
++ lockdep_on();
++ fput(h_file);
+out:
-+ AuTraceErrPtr(dentry);
-+ return dentry;
++ lockdep_off();
++ si_read_unlock(sb);
++ lockdep_on();
++ AuTraceErr(err);
++ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
-+static struct dentry *
-+aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
-+ int fh_type)
++static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end,
++ int datasync)
+{
-+ struct dentry *dentry;
-+ __u32 *fh = fid->raw;
-+ struct au_branch *br;
-+ ino_t ino, dir_ino;
-+ struct au_nfsd_si_lock nsi_lock = {
-+ .force_lock = 0
-+ };
-+
-+ dentry = ERR_PTR(-ESTALE);
-+ /* it should never happen, but the file handle is unreliable */
-+ if (unlikely(fh_len < Fh_tail))
-+ goto out;
-+ nsi_lock.sigen = fh[Fh_sigen];
-+ nsi_lock.br_id = fh[Fh_br_id];
++ int err;
++ struct au_write_pre wpre;
++ struct inode *inode;
++ struct file *h_file;
+
-+ /* branch id may be wrapped around */
-+ br = NULL;
-+ if (unlikely(si_nfsd_read_lock(sb, &nsi_lock)))
++ err = 0; /* -EBADF; */ /* posix? */
++ if (unlikely(!(file->f_mode & FMODE_WRITE)))
+ goto out;
-+ nsi_lock.force_lock = 1;
-+
-+ /* is this inode still cached? */
-+ ino = decode_ino(fh + Fh_ino);
-+ /* it should never happen */
-+ if (unlikely(ino == AUFS_ROOT_INO))
-+ goto out_unlock;
-+
-+ dir_ino = decode_ino(fh + Fh_dir_ino);
-+ dentry = decode_by_ino(sb, ino, dir_ino);
-+ if (IS_ERR(dentry))
-+ goto out_unlock;
-+ if (dentry)
-+ goto accept;
+
-+ /* is the parent dir cached? */
-+ br = au_sbr(sb, nsi_lock.bindex);
-+ au_br_get(br);
-+ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock);
-+ if (IS_ERR(dentry))
-+ goto out_unlock;
-+ if (dentry)
-+ goto accept;
++ inode = file_inode(file);
++ au_mtx_and_read_lock(inode);
+
-+ /* lookup path */
-+ dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock);
-+ if (IS_ERR(dentry))
-+ goto out_unlock;
-+ if (unlikely(!dentry))
-+ /* todo?: make it ESTALE */
++ wpre.lsc = 0;
++ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
+ goto out_unlock;
+
-+accept:
-+ if (!au_digen_test(dentry, au_sigen(sb))
-+ && d_inode(dentry)->i_generation == fh[Fh_igen])
-+ goto out_unlock; /* success */
++ err = vfsub_fsync(h_file, &h_file->f_path, datasync);
++ au_write_post(inode, h_file, &wpre, /*written*/0);
+
-+ dput(dentry);
-+ dentry = ERR_PTR(-ESTALE);
+out_unlock:
-+ if (br)
-+ au_br_put(br);
-+ si_read_unlock(sb);
-+out:
-+ AuTraceErrPtr(dentry);
-+ return dentry;
-+}
-+
-+#if 0 /* reserved for future use */
-+/* support subtreecheck option */
-+static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid,
-+ int fh_len, int fh_type)
-+{
-+ struct dentry *parent;
-+ __u32 *fh = fid->raw;
-+ ino_t dir_ino;
-+
-+ dir_ino = decode_ino(fh + Fh_dir_ino);
-+ parent = decode_by_ino(sb, dir_ino, 0);
-+ if (IS_ERR(parent))
-+ goto out;
-+ if (!parent)
-+ parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]),
-+ dir_ino, fh, fh_len);
-+
++ si_read_unlock(inode->i_sb);
++ inode_unlock(inode);
+out:
-+ AuTraceErrPtr(parent);
-+ return parent;
++ return err;
+}
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
+
-+static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len,
-+ struct inode *dir)
++static int aufs_fasync(int fd, struct file *file, int flag)
+{
+ int err;
-+ aufs_bindex_t bindex;
-+ struct super_block *sb, *h_sb;
-+ struct dentry *dentry, *parent, *h_parent;
-+ struct inode *h_dir;
-+ struct au_branch *br;
-+
-+ err = -ENOSPC;
-+ if (unlikely(*max_len <= Fh_tail)) {
-+ AuWarn1("NFSv2 client (max_len %d)?\n", *max_len);
-+ goto out;
-+ }
++ struct file *h_file;
++ struct super_block *sb;
+
-+ err = FILEID_ROOT;
-+ if (inode->i_ino == AUFS_ROOT_INO) {
-+ AuDebugOn(inode->i_ino != AUFS_ROOT_INO);
-+ goto out;
-+ }
++ sb = file->f_path.dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
+
-+ h_parent = NULL;
-+ sb = inode->i_sb;
-+ err = si_read_lock(sb, AuLock_FLUSH);
-+ if (unlikely(err))
++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
+ goto out;
+
-+#ifdef CONFIG_AUFS_DEBUG
-+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
-+ AuWarn1("NFS-exporting requires xino\n");
-+#endif
-+ err = -EIO;
-+ parent = NULL;
-+ ii_read_lock_child(inode);
-+ bindex = au_ibtop(inode);
-+ if (!dir) {
-+ dentry = d_find_any_alias(inode);
-+ if (unlikely(!dentry))
-+ goto out_unlock;
-+ AuDebugOn(au_test_anon(dentry));
-+ parent = dget_parent(dentry);
-+ dput(dentry);
-+ if (unlikely(!parent))
-+ goto out_unlock;
-+ if (d_really_is_positive(parent))
-+ dir = d_inode(parent);
-+ }
-+
-+ ii_read_lock_parent(dir);
-+ h_dir = au_h_iptr(dir, bindex);
-+ ii_read_unlock(dir);
-+ if (unlikely(!h_dir))
-+ goto out_parent;
-+ h_parent = d_find_any_alias(h_dir);
-+ if (unlikely(!h_parent))
-+ goto out_hparent;
-+
-+ err = -EPERM;
-+ br = au_sbr(sb, bindex);
-+ h_sb = au_br_sb(br);
-+ if (unlikely(!h_sb->s_export_op)) {
-+ AuErr1("%s branch is not exportable\n", au_sbtype(h_sb));
-+ goto out_hparent;
-+ }
-+
-+ fh[Fh_br_id] = br->br_id;
-+ fh[Fh_sigen] = au_sigen(sb);
-+ encode_ino(fh + Fh_ino, inode->i_ino);
-+ encode_ino(fh + Fh_dir_ino, dir->i_ino);
-+ fh[Fh_igen] = inode->i_generation;
-+
-+ *max_len -= Fh_tail;
-+ fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail),
-+ max_len,
-+ /*connectable or subtreecheck*/0);
-+ err = fh[Fh_h_type];
-+ *max_len += Fh_tail;
-+ /* todo: macros? */
-+ if (err != FILEID_INVALID)
-+ err = 99;
-+ else
-+ AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb));
++ 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_hparent:
-+ dput(h_parent);
-+out_parent:
-+ dput(parent);
-+out_unlock:
-+ ii_read_unlock(inode);
-+ si_read_unlock(sb);
+out:
-+ if (unlikely(err < 0))
-+ err = FILEID_INVALID;
++ si_read_unlock(sb);
+ return err;
+}
+
-+/* ---------------------------------------------------------------------- */
-+
-+static int aufs_commit_metadata(struct inode *inode)
++static int aufs_setfl(struct file *file, unsigned long arg)
+{
+ int err;
-+ aufs_bindex_t bindex;
++ struct file *h_file;
+ struct super_block *sb;
-+ struct inode *h_inode;
-+ int (*f)(struct inode *inode);
+
-+ sb = inode->i_sb;
++ sb = file->f_path.dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
-+ ii_write_lock_child(inode);
-+ bindex = au_ibtop(inode);
-+ AuDebugOn(bindex < 0);
-+ h_inode = au_h_iptr(inode, bindex);
+
-+ f = h_inode->i_sb->s_export_op->commit_metadata;
-+ if (f)
-+ err = f(h_inode);
-+ else {
-+ struct writeback_control wbc = {
-+ .sync_mode = WB_SYNC_ALL,
-+ .nr_to_write = 0 /* metadata only */
-+ };
++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out;
+
-+ err = sync_inode(h_inode, &wbc);
-+ }
++ /* 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() */
+
-+ au_cpup_attr_timesizes(inode);
-+ ii_write_unlock(inode);
++out:
+ si_read_unlock(sb);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
-+static struct export_operations aufs_export_op = {
-+ .fh_to_dentry = aufs_fh_to_dentry,
-+ /* .fh_to_parent = aufs_fh_to_parent, */
-+ .encode_fh = aufs_encode_fh,
-+ .commit_metadata = aufs_commit_metadata
-+};
-+
-+void au_export_init(struct super_block *sb)
++/* no one supports this operation, currently */
++#if 0
++static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset,
++ size_t len, loff_t *pos, int more)
+{
-+ struct au_sbinfo *sbinfo;
-+ __u32 u;
++}
++#endif
+
-+ 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;
-+ get_random_bytes(&u, sizeof(u));
-+ BUILD_BUG_ON(sizeof(u) != sizeof(int));
-+ atomic_set(&sbinfo->si_xigen_next, u);
-+}
-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 2018-04-15 08:49:13.397817296 +0200
++const struct file_operations aufs_file_fop = {
++ .owner = THIS_MODULE,
++
++ .llseek = default_llseek,
++
++ .read = aufs_read,
++ .write = aufs_write,
++ .read_iter = aufs_read_iter,
++ .write_iter = aufs_write_iter,
++
++#ifdef CONFIG_AUFS_POLL
++ .poll = aufs_poll,
++#endif
++ .unlocked_ioctl = aufs_ioctl_nondir,
++#ifdef CONFIG_COMPAT
++ .compat_ioctl = aufs_compat_ioctl_nondir,
++#endif
++ .mmap = aufs_mmap,
++ .open = aufs_open_nondir,
++ .flush = aufs_flush_nondir,
++ .release = aufs_release_nondir,
++ .fsync = aufs_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,
++ .copy_file_range = aufs_copy_file_range
++};
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/fhsm.c linux-4.9/fs/aufs/fhsm.c
+--- linux-4.9/fs/aufs/fhsm.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/fhsm.c 2021-02-24 16:15:09.528240413 +0100
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2011-2018 Junjiro R. Okajima
+ if (u != AUFS_FHSM_CACHE_DEF_SEC)
+ seq_printf(seq, ",fhsm_sec=%u", u);
+}
-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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/file.c linux-4.9/fs/aufs/file.c
+--- linux-4.9/fs/aufs/file.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/file.c 2021-02-24 16:15:09.528240413 +0100
@@ -0,0 +1,848 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ .error_remove_page = aufs_error_remove_page,
+ .swap_activate = aufs_swap_activate,
+ .swap_deactivate = aufs_swap_deactivate
-+#endif /* CONFIG_AUFS_DEBUG */
-+};
-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 2018-04-15 08:49:13.397817296 +0200
-@@ -0,0 +1,330 @@
-+/*
-+ * 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
-+ * 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 <http://www.gnu.org/licenses/>.
-+ */
-+
-+/*
-+ * file operations
-+ */
-+
-+#ifndef __AUFS_FILE_H__
-+#define __AUFS_FILE_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/file.h>
-+#include <linux/fs.h>
-+#include <linux/poll.h>
-+#include "rwsem.h"
-+
-+struct au_branch;
-+struct au_hfile {
-+ struct file *hf_file;
-+ struct au_branch *hf_br;
-+};
-+
-+struct au_vdir;
-+struct au_fidir {
-+ aufs_bindex_t fd_bbot;
-+ aufs_bindex_t fd_nent;
-+ struct au_vdir *fd_vdir_cache;
-+ struct au_hfile fd_hfile[];
-+};
-+
-+static inline int au_fidir_sz(int nent)
-+{
-+ AuDebugOn(nent < 0);
-+ return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent;
-+}
-+
-+struct au_finfo {
-+ atomic_t fi_generation;
-+
-+ struct au_rwsem fi_rwsem;
-+ aufs_bindex_t fi_btop;
-+
-+ /* do not union them */
-+ struct { /* for non-dir */
-+ struct au_hfile fi_htop;
-+ atomic_t fi_mmapped;
-+ };
-+ struct au_fidir *fi_hdir; /* for dir only */
-+
-+ struct hlist_bl_node fi_hlist;
-+ struct file *fi_file; /* very ugly */
-+} ____cacheline_aligned_in_smp;
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* file.c */
-+extern const struct address_space_operations aufs_aop;
-+unsigned int au_file_roflags(unsigned int flags);
-+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
-+ struct file *file, int force_wr);
-+struct au_do_open_args {
-+ int aopen;
-+ int (*open)(struct file *file, int flags,
-+ struct file *h_file);
-+ struct au_fidir *fidir;
-+ struct file *h_file;
-+};
-+int au_do_open(struct file *file, struct au_do_open_args *args);
-+int au_reopen_nondir(struct file *file);
-+struct au_pin;
-+int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin);
-+int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),
-+ int wlock, 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);
-+#endif
-+
-+#ifdef CONFIG_AUFS_BR_HFSPLUS
-+/* hfsplus.c */
-+struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex,
-+ int force_wr);
-+void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex,
-+ struct file *h_file);
-+#else
-+AuStub(struct file *, au_h_open_pre, return NULL, struct dentry *dentry,
-+ aufs_bindex_t bindex, int force_wr)
-+AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex,
-+ struct file *h_file);
-+#endif
-+
-+/* f_op.c */
-+extern const struct file_operations aufs_file_fop;
-+int au_do_open_nondir(struct file *file, int flags, 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, unsigned int lsc);
-+
-+/* finfo.c */
-+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 may_shrink);
-+
-+void au_fi_init_once(void *_fi);
-+void au_finfo_fin(struct file *file);
-+int au_finfo_init(struct file *file, struct au_fidir *fidir);
-+
-+/* ioctl.c */
-+long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg);
-+#ifdef CONFIG_COMPAT
-+long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd,
-+ unsigned long arg);
-+long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd,
-+ unsigned long arg);
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline struct au_finfo *au_fi(struct file *file)
-+{
-+ return file->private_data;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * fi_read_lock, fi_write_lock,
-+ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock
-+ */
-+AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem);
-+
-+/* lock subclass for finfo */
-+enum {
-+ AuLsc_FI_1,
-+ AuLsc_FI_2
-+};
-+
-+static inline void fi_read_lock_nested(struct file *f, unsigned int lsc)
-+{
-+ au_rw_read_lock_nested(&au_fi(f)->fi_rwsem, lsc);
-+}
-+
-+static inline void fi_write_lock_nested(struct file *f, unsigned int lsc)
-+{
-+ au_rw_write_lock_nested(&au_fi(f)->fi_rwsem, lsc);
-+}
-+
-+/*
-+ * fi_read_lock_1, fi_write_lock_1,
-+ * fi_read_lock_2, fi_write_lock_2
-+ */
-+#define AuReadLockFunc(name) \
-+static inline void fi_read_lock_##name(struct file *f) \
-+{ fi_read_lock_nested(f, AuLsc_FI_##name); }
-+
-+#define AuWriteLockFunc(name) \
-+static inline void fi_write_lock_##name(struct file *f) \
-+{ fi_write_lock_nested(f, AuLsc_FI_##name); }
-+
-+#define AuRWLockFuncs(name) \
-+ AuReadLockFunc(name) \
-+ AuWriteLockFunc(name)
-+
-+AuRWLockFuncs(1);
-+AuRWLockFuncs(2);
-+
-+#undef AuReadLockFunc
-+#undef AuWriteLockFunc
-+#undef AuRWLockFuncs
-+
-+#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem)
-+#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem)
-+#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem)
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* todo: hard/soft set? */
-+static inline aufs_bindex_t au_fbtop(struct file *file)
-+{
-+ FiMustAnyLock(file);
-+ return au_fi(file)->fi_btop;
-+}
-+
-+static inline aufs_bindex_t au_fbbot_dir(struct file *file)
-+{
-+ FiMustAnyLock(file);
-+ AuDebugOn(!au_fi(file)->fi_hdir);
-+ return au_fi(file)->fi_hdir->fd_bbot;
-+}
-+
-+static inline struct au_vdir *au_fvdir_cache(struct file *file)
-+{
-+ FiMustAnyLock(file);
-+ AuDebugOn(!au_fi(file)->fi_hdir);
-+ return au_fi(file)->fi_hdir->fd_vdir_cache;
-+}
-+
-+static inline void au_set_fbtop(struct file *file, aufs_bindex_t bindex)
-+{
-+ FiMustWriteLock(file);
-+ au_fi(file)->fi_btop = bindex;
-+}
-+
-+static inline void au_set_fbbot_dir(struct file *file, aufs_bindex_t bindex)
-+{
-+ FiMustWriteLock(file);
-+ AuDebugOn(!au_fi(file)->fi_hdir);
-+ au_fi(file)->fi_hdir->fd_bbot = bindex;
-+}
-+
-+static inline void au_set_fvdir_cache(struct file *file,
-+ struct au_vdir *vdir_cache)
-+{
-+ FiMustWriteLock(file);
-+ AuDebugOn(!au_fi(file)->fi_hdir);
-+ au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache;
-+}
-+
-+static inline struct file *au_hf_top(struct file *file)
-+{
-+ FiMustAnyLock(file);
-+ AuDebugOn(au_fi(file)->fi_hdir);
-+ return au_fi(file)->fi_htop.hf_file;
-+}
-+
-+static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex)
-+{
-+ FiMustAnyLock(file);
-+ AuDebugOn(!au_fi(file)->fi_hdir);
-+ return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file;
-+}
-+
-+/* todo: memory barrier? */
-+static inline unsigned int au_figen(struct file *f)
-+{
-+ return atomic_read(&au_fi(f)->fi_generation);
-+}
-+
-+static inline void au_set_mmapped(struct file *f)
-+{
-+ if (atomic_inc_return(&au_fi(f)->fi_mmapped))
-+ return;
-+ pr_warn("fi_mmapped wrapped around\n");
-+ while (!atomic_inc_return(&au_fi(f)->fi_mmapped))
-+ ;
-+}
-+
-+static inline void au_unset_mmapped(struct file *f)
-+{
-+ atomic_dec(&au_fi(f)->fi_mmapped);
-+}
-+
-+static inline int au_test_mmapped(struct file *f)
-+{
-+ return atomic_read(&au_fi(f)->fi_mmapped);
-+}
-+
-+/* customize vma->vm_file */
-+
-+static inline void au_do_vm_file_reset(struct vm_area_struct *vma,
-+ struct file *file)
-+{
-+ struct file *f;
-+
-+ f = vma->vm_file;
-+ get_file(file);
-+ vma->vm_file = file;
-+ fput(f);
-+}
-+
-+#ifdef CONFIG_MMU
-+#define AuDbgVmRegion(file, vma) do {} while (0)
-+
-+static inline void au_vm_file_reset(struct vm_area_struct *vma,
-+ struct file *file)
-+{
-+ au_do_vm_file_reset(vma, file);
-+}
-+#else
-+#define AuDbgVmRegion(file, vma) \
-+ AuDebugOn((vma)->vm_region && (vma)->vm_region->vm_file != (file))
-+
-+static inline void au_vm_file_reset(struct vm_area_struct *vma,
-+ struct file *file)
-+{
-+ struct file *f;
-+
-+ au_do_vm_file_reset(vma, file);
-+ f = vma->vm_region->vm_file;
-+ get_file(file);
-+ vma->vm_region->vm_file = file;
-+ fput(f);
-+}
-+#endif /* CONFIG_MMU */
-+
-+/* handle vma->vm_prfile */
-+static inline void au_vm_prfile_set(struct vm_area_struct *vma,
-+ struct file *file)
-+{
-+ get_file(file);
-+ vma->vm_prfile = file;
-+#ifndef CONFIG_MMU
-+ get_file(file);
-+ vma->vm_region->vm_prfile = file;
-+#endif
-+}
-+
-+#endif /* __KERNEL__ */
-+#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 2018-04-15 08:49:13.397817296 +0200
-@@ -0,0 +1,148 @@
-+/*
-+ * 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
-+ * 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 <http://www.gnu.org/licenses/>.
-+ */
-+
-+/*
-+ * file private data
-+ */
-+
-+#include "aufs.h"
-+
-+void au_hfput(struct au_hfile *hf, int execed)
-+{
-+ if (execed)
-+ allow_write_access(hf->hf_file);
-+ fput(hf->hf_file);
-+ hf->hf_file = NULL;
-+ au_br_put(hf->hf_br);
-+ hf->hf_br = NULL;
-+}
-+
-+void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val)
-+{
-+ struct au_finfo *finfo = au_fi(file);
-+ struct au_hfile *hf;
-+ struct au_fidir *fidir;
-+
-+ fidir = finfo->fi_hdir;
-+ if (!fidir) {
-+ AuDebugOn(finfo->fi_btop != bindex);
-+ hf = &finfo->fi_htop;
-+ } else
-+ hf = fidir->fd_hfile + bindex;
-+
-+ if (hf && hf->hf_file)
-+ au_hfput(hf, vfsub_file_execed(file));
-+ if (val) {
-+ FiMustWriteLock(file);
-+ AuDebugOn(IS_ERR_OR_NULL(file->f_path.dentry));
-+ hf->hf_file = val;
-+ hf->hf_br = au_sbr(file->f_path.dentry->d_sb, bindex);
-+ }
-+}
-+
-+void au_update_figen(struct file *file)
-+{
-+ atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_path.dentry));
-+ /* smp_mb(); */ /* atomic_set */
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct au_fidir *au_fidir_alloc(struct super_block *sb)
-+{
-+ struct au_fidir *fidir;
-+ int nbr;
-+
-+ nbr = au_sbbot(sb) + 1;
-+ if (nbr < 2)
-+ nbr = 2; /* initial allocate for 2 branches */
-+ fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS);
-+ if (fidir) {
-+ fidir->fd_bbot = -1;
-+ fidir->fd_nent = nbr;
-+ }
-+
-+ return fidir;
-+}
-+
-+int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink)
-+{
-+ int err;
-+ struct au_fidir *fidir, *p;
-+
-+ AuRwMustWriteLock(&finfo->fi_rwsem);
-+ fidir = finfo->fi_hdir;
-+ AuDebugOn(!fidir);
-+
-+ err = -ENOMEM;
-+ p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr),
-+ GFP_NOFS, may_shrink);
-+ if (p) {
-+ p->fd_nent = nbr;
-+ finfo->fi_hdir = p;
-+ err = 0;
-+ }
-+
-+ return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void au_finfo_fin(struct file *file)
-+{
-+ struct au_finfo *finfo;
-+
-+ au_nfiles_dec(file->f_path.dentry->d_sb);
-+
-+ finfo = au_fi(file);
-+ AuDebugOn(finfo->fi_hdir);
-+ AuRwDestroy(&finfo->fi_rwsem);
-+ au_cache_free_finfo(finfo);
-+}
-+
-+void au_fi_init_once(void *_finfo)
-+{
-+ struct au_finfo *finfo = _finfo;
-+
-+ au_rw_init(&finfo->fi_rwsem);
-+}
-+
-+int au_finfo_init(struct file *file, struct au_fidir *fidir)
-+{
-+ int err;
-+ struct au_finfo *finfo;
-+ struct dentry *dentry;
-+
-+ err = -ENOMEM;
-+ dentry = file->f_path.dentry;
-+ finfo = au_cache_alloc_finfo();
-+ if (unlikely(!finfo))
-+ goto out;
-+
-+ err = 0;
-+ au_nfiles_inc(dentry->d_sb);
-+ au_rw_write_lock(&finfo->fi_rwsem);
-+ finfo->fi_btop = -1;
-+ finfo->fi_hdir = fidir;
-+ atomic_set(&finfo->fi_generation, au_digen(dentry));
-+ /* smp_mb(); */ /* atomic_set */
-+
-+ file->private_data = finfo;
-+
-+out:
-+ return err;
-+}
-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 2018-04-15 08:49:13.397817296 +0200
-@@ -0,0 +1,817 @@
++#endif /* CONFIG_AUFS_DEBUG */
++};
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/file.h linux-4.9/fs/aufs/file.h
+--- linux-4.9/fs/aufs/file.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/file.h 2021-02-24 16:15:09.528240413 +0100
+@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ *
+ */
+
+/*
-+ * file and vm operations
++ * file operations
+ */
+
-+#include <linux/aio.h>
-+#include <linux/fs_stack.h>
-+#include <linux/mman.h>
-+#include <linux/security.h>
-+#include "aufs.h"
-+
-+int au_do_open_nondir(struct file *file, int flags, struct file *h_file)
-+{
-+ int err;
-+ aufs_bindex_t bindex;
-+ struct dentry *dentry, *h_dentry;
-+ struct au_finfo *finfo;
-+ struct inode *h_inode;
-+
-+ FiMustWriteLock(file);
-+
-+ err = 0;
-+ dentry = file->f_path.dentry;
-+ AuDebugOn(IS_ERR_OR_NULL(dentry));
-+ finfo = au_fi(file);
-+ memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop));
-+ atomic_set(&finfo->fi_mmapped, 0);
-+ 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 {
-+ h_dentry = h_file->f_path.dentry;
-+ err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb);
-+ if (unlikely(err))
-+ goto out;
-+ 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_fbtop(file, bindex);
-+ au_set_h_fptr(file, bindex, h_file);
-+ au_update_figen(file);
-+ /* todo: necessary? */
-+ /* file->f_ra = h_file->f_ra; */
-+ }
++#ifndef __AUFS_FILE_H__
++#define __AUFS_FILE_H__
+
-+out:
-+ return err;
-+}
++#ifdef __KERNEL__
+
-+static int aufs_open_nondir(struct inode *inode __maybe_unused,
-+ struct file *file)
-+{
-+ int err;
-+ struct super_block *sb;
-+ struct au_do_open_args args = {
-+ .open = au_do_open_nondir
-+ };
++#include <linux/file.h>
++#include <linux/fs.h>
++#include <linux/poll.h>
++#include "rwsem.h"
+
-+ AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n",
-+ file, vfsub_file_flags(file), file->f_mode);
++struct au_branch;
++struct au_hfile {
++ struct file *hf_file;
++ struct au_branch *hf_br;
++};
+
-+ sb = file->f_path.dentry->d_sb;
-+ si_read_lock(sb, AuLock_FLUSH);
-+ err = au_do_open(file, &args);
-+ si_read_unlock(sb);
-+ return err;
-+}
++struct au_vdir;
++struct au_fidir {
++ aufs_bindex_t fd_bbot;
++ aufs_bindex_t fd_nent;
++ struct au_vdir *fd_vdir_cache;
++ struct au_hfile fd_hfile[];
++};
+
-+int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file)
++static inline int au_fidir_sz(int nent)
+{
-+ struct au_finfo *finfo;
-+ aufs_bindex_t bindex;
-+
-+ finfo = au_fi(file);
-+ 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);
-+
-+ au_finfo_fin(file);
-+ return 0;
++ AuDebugOn(nent < 0);
++ return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent;
+}
+
-+/* ---------------------------------------------------------------------- */
++struct au_finfo {
++ atomic_t fi_generation;
+
-+static int au_do_flush_nondir(struct file *file, fl_owner_t id)
-+{
-+ int err;
-+ struct file *h_file;
++ struct au_rwsem fi_rwsem;
++ aufs_bindex_t fi_btop;
+
-+ err = 0;
-+ h_file = au_hf_top(file);
-+ if (h_file)
-+ err = vfsub_flush(h_file, id);
-+ return err;
-+}
++ /* do not union them */
++ struct { /* for non-dir */
++ struct au_hfile fi_htop;
++ atomic_t fi_mmapped;
++ };
++ struct au_fidir *fi_hdir; /* for dir only */
+
-+static int aufs_flush_nondir(struct file *file, fl_owner_t id)
-+{
-+ return au_do_flush(file, id, au_do_flush_nondir);
-+}
++ struct hlist_bl_node fi_hlist;
++ struct file *fi_file; /* very ugly */
++} ____cacheline_aligned_in_smp;
+
+/* ---------------------------------------------------------------------- */
-+/*
-+ * read and write functions acquire [fdi]_rwsem once, but release before
-+ * mmap_sem. This is because to stop a race condition between mmap(2).
-+ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping
-+ * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in
-+ * read functions after [fdi]_rwsem are released, but it should be harmless.
-+ */
-+
-+/* Callers should call au_read_post() or fput() in the end */
-+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, lsc);
-+ if (!err) {
-+ di_read_unlock(file->f_path.dentry, AuLock_IR);
-+ h_file = au_hf_top(file);
-+ get_file(h_file);
-+ if (!keep_fi)
-+ fi_read_unlock(file);
-+ } else
-+ h_file = ERR_PTR(err);
+
-+ return h_file;
-+}
++/* file.c */
++extern const struct address_space_operations aufs_aop;
++unsigned int au_file_roflags(unsigned int flags);
++struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
++ struct file *file, int force_wr);
++struct au_do_open_args {
++ int aopen;
++ int (*open)(struct file *file, int flags,
++ struct file *h_file);
++ struct au_fidir *fidir;
++ struct file *h_file;
++};
++int au_do_open(struct file *file, struct au_do_open_args *args);
++int au_reopen_nondir(struct file *file);
++struct au_pin;
++int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin);
++int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),
++ int wlock, unsigned int fi_lsc);
++int au_do_flush(struct file *file, fl_owner_t id,
++ int (*flush)(struct file *file, fl_owner_t id));
+
-+static void au_read_post(struct inode *inode, struct file *h_file)
-+{
-+ /* update without lock, I don't think it a problem */
-+ fsstack_copy_attr_atime(inode, file_inode(h_file));
-+ fput(h_file);
-+}
++/* poll.c */
++#ifdef CONFIG_AUFS_POLL
++unsigned int aufs_poll(struct file *file, poll_table *wait);
++#endif
+
-+struct au_write_pre {
-+ /* input */
-+ unsigned int lsc;
++#ifdef CONFIG_AUFS_BR_HFSPLUS
++/* hfsplus.c */
++struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex,
++ int force_wr);
++void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex,
++ struct file *h_file);
++#else
++AuStub(struct file *, au_h_open_pre, return NULL, struct dentry *dentry,
++ aufs_bindex_t bindex, int force_wr)
++AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex,
++ struct file *h_file);
++#endif
+
-+ /* output */
-+ blkcnt_t blks;
-+ aufs_bindex_t btop;
-+};
++/* f_op.c */
++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, unsigned int lsc);
+
-+/*
-+ * return with iinfo is write-locked
-+ * callers should call au_write_post() or iinfo_write_unlock() + fput() in the
-+ * end
-+ */
-+static struct file *au_write_pre(struct file *file, int do_ready,
-+ struct au_write_pre *wpre)
-+{
-+ struct file *h_file;
-+ struct dentry *dentry;
-+ int err;
-+ unsigned int lsc;
-+ struct au_pin pin;
++/* finfo.c */
++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);
+
-+ 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;
++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 may_shrink);
+
-+ dentry = file->f_path.dentry;
-+ if (do_ready) {
-+ err = au_ready_to_write(file, -1, &pin);
-+ if (unlikely(err)) {
-+ h_file = ERR_PTR(err);
-+ di_write_unlock(dentry);
-+ goto out_fi;
-+ }
-+ }
++void au_fi_init_once(void *_fi);
++void au_finfo_fin(struct file *file);
++int au_finfo_init(struct file *file, struct au_fidir *fidir);
+
-+ di_downgrade_lock(dentry, /*flags*/0);
-+ if (wpre)
-+ wpre->btop = au_fbtop(file);
-+ h_file = au_hf_top(file);
-+ get_file(h_file);
-+ if (wpre)
-+ wpre->blks = file_inode(h_file)->i_blocks;
-+ if (do_ready)
-+ au_unpin(&pin);
-+ di_read_unlock(dentry, /*flags*/0);
++/* ioctl.c */
++long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg);
++#ifdef CONFIG_COMPAT
++long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd,
++ unsigned long arg);
++long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd,
++ unsigned long arg);
++#endif
+
-+out_fi:
-+ fi_write_unlock(file);
-+out:
-+ return h_file;
-+}
++/* ---------------------------------------------------------------------- */
+
-+static void au_write_post(struct inode *inode, struct file *h_file,
-+ struct au_write_pre *wpre, ssize_t written)
++static inline struct au_finfo *au_fi(struct file *file)
+{
-+ struct inode *h_inode;
-+
-+ au_cpup_attr_timesizes(inode);
-+ AuDebugOn(au_ibtop(inode) != wpre->btop);
-+ h_inode = file_inode(h_file);
-+ inode->i_mode = h_inode->i_mode;
-+ ii_write_unlock(inode);
-+ /* AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); */
-+ if (written > 0)
-+ au_fhsm_wrote(inode->i_sb, wpre->btop,
-+ /*force*/h_inode->i_blocks > wpre->blks);
-+ fput(h_file);
++ return file->private_data;
+}
+
-+static ssize_t aufs_read(struct file *file, char __user *buf, size_t count,
-+ loff_t *ppos)
-+{
-+ ssize_t err;
-+ struct inode *inode;
-+ struct file *h_file;
-+ struct super_block *sb;
-+
-+ inode = file_inode(file);
-+ sb = inode->i_sb;
-+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
-+
-+ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
-+ err = PTR_ERR(h_file);
-+ if (IS_ERR(h_file))
-+ goto out;
-+
-+ /* filedata may be obsoleted by concurrent copyup, but no problem */
-+ err = vfsub_read_u(h_file, buf, count, ppos);
-+ /* todo: necessary? */
-+ /* file->f_ra = h_file->f_ra; */
-+ au_read_post(inode, h_file);
-+
-+out:
-+ si_read_unlock(sb);
-+ return err;
-+}
++/* ---------------------------------------------------------------------- */
+
+/*
-+ * todo: very ugly
-+ * it locks both of i_mutex and si_rwsem for read in safe.
-+ * if the plink maintenance mode continues forever (that is the problem),
-+ * may loop forever.
++ * fi_read_lock, fi_write_lock,
++ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock
+ */
-+static void au_mtx_and_read_lock(struct inode *inode)
-+{
-+ int err;
-+ struct super_block *sb = inode->i_sb;
++AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem);
+
-+ while (1) {
-+ inode_lock(inode);
-+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
-+ if (!err)
-+ break;
-+ inode_unlock(inode);
-+ si_read_lock(sb, AuLock_NOPLMW);
-+ si_read_unlock(sb);
-+ }
-+}
++/* lock subclass for finfo */
++enum {
++ AuLsc_FI_1,
++ AuLsc_FI_2
++};
+
-+static ssize_t aufs_write(struct file *file, const char __user *ubuf,
-+ size_t count, loff_t *ppos)
++static inline void fi_read_lock_nested(struct file *f, unsigned int lsc)
+{
-+ ssize_t err;
-+ struct au_write_pre wpre;
-+ struct inode *inode;
-+ struct file *h_file;
-+ char __user *buf = (char __user *)ubuf;
-+
-+ 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))
-+ goto out;
-+
-+ err = vfsub_write_u(h_file, buf, count, ppos);
-+ au_write_post(inode, h_file, &wpre, err);
-+
-+out:
-+ si_read_unlock(inode->i_sb);
-+ inode_unlock(inode);
-+ return err;
++ au_rw_read_lock_nested(&au_fi(f)->fi_rwsem, lsc);
+}
+
-+static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio,
-+ struct iov_iter *iov_iter)
++static inline void fi_write_lock_nested(struct file *f, unsigned int lsc)
+{
-+ ssize_t err;
-+ struct file *file;
-+ ssize_t (*iter)(struct kiocb *, struct iov_iter *);
-+
-+ err = security_file_permission(h_file, rw);
-+ if (unlikely(err))
-+ goto out;
-+
-+ err = -ENOSYS;
-+ iter = NULL;
-+ if (rw == MAY_READ)
-+ iter = h_file->f_op->read_iter;
-+ else if (rw == MAY_WRITE)
-+ iter = h_file->f_op->write_iter;
-+
-+ file = kio->ki_filp;
-+ kio->ki_filp = h_file;
-+ if (iter) {
-+ lockdep_off();
-+ err = iter(kio, iov_iter);
-+ lockdep_on();
-+ } else
-+ /* currently there is no such fs */
-+ WARN_ON_ONCE(1);
-+ kio->ki_filp = file;
-+
-+out:
-+ return err;
++ au_rw_write_lock_nested(&au_fi(f)->fi_rwsem, lsc);
+}
+
-+static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter)
-+{
-+ ssize_t err;
-+ struct file *file, *h_file;
-+ struct inode *inode;
-+ struct super_block *sb;
-+
-+ file = kio->ki_filp;
-+ inode = file_inode(file);
-+ sb = inode->i_sb;
-+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
-+
-+ 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; */
-+ au_read_post(inode, h_file);
++/*
++ * fi_read_lock_1, fi_write_lock_1,
++ * fi_read_lock_2, fi_write_lock_2
++ */
++#define AuReadLockFunc(name) \
++static inline void fi_read_lock_##name(struct file *f) \
++{ fi_read_lock_nested(f, AuLsc_FI_##name); }
+
-+out:
-+ si_read_unlock(sb);
-+ return err;
-+}
++#define AuWriteLockFunc(name) \
++static inline void fi_write_lock_##name(struct file *f) \
++{ fi_write_lock_nested(f, AuLsc_FI_##name); }
+
-+static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter)
-+{
-+ ssize_t err;
-+ struct au_write_pre wpre;
-+ struct inode *inode;
-+ struct file *file, *h_file;
++#define AuRWLockFuncs(name) \
++ AuReadLockFunc(name) \
++ AuWriteLockFunc(name)
+
-+ file = kio->ki_filp;
-+ inode = file_inode(file);
-+ au_mtx_and_read_lock(inode);
++AuRWLockFuncs(1);
++AuRWLockFuncs(2);
+
-+ wpre.lsc = 0;
-+ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
-+ err = PTR_ERR(h_file);
-+ if (IS_ERR(h_file))
-+ goto out;
++#undef AuReadLockFunc
++#undef AuWriteLockFunc
++#undef AuRWLockFuncs
+
-+ err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter);
-+ au_write_post(inode, h_file, &wpre, err);
++#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem)
++#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem)
++#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem)
+
-+out:
-+ si_read_unlock(inode->i_sb);
-+ inode_unlock(inode);
-+ return err;
-+}
++/* ---------------------------------------------------------------------- */
+
-+static ssize_t aufs_splice_read(struct file *file, loff_t *ppos,
-+ struct pipe_inode_info *pipe, size_t len,
-+ unsigned int flags)
++/* todo: hard/soft set? */
++static inline aufs_bindex_t au_fbtop(struct file *file)
+{
-+ ssize_t err;
-+ struct file *h_file;
-+ struct inode *inode;
-+ struct super_block *sb;
-+
-+ inode = file_inode(file);
-+ sb = inode->i_sb;
-+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
-+
-+ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
-+ err = PTR_ERR(h_file);
-+ if (IS_ERR(h_file))
-+ goto out;
-+
-+ err = vfsub_splice_to(h_file, ppos, pipe, len, flags);
-+ /* todo: necessasry? */
-+ /* file->f_ra = h_file->f_ra; */
-+ au_read_post(inode, h_file);
-+
-+out:
-+ si_read_unlock(sb);
-+ return err;
++ FiMustAnyLock(file);
++ return au_fi(file)->fi_btop;
+}
+
-+static ssize_t
-+aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos,
-+ size_t len, unsigned int flags)
++static inline aufs_bindex_t au_fbbot_dir(struct file *file)
+{
-+ ssize_t err;
-+ struct au_write_pre wpre;
-+ struct inode *inode;
-+ struct file *h_file;
++ FiMustAnyLock(file);
++ AuDebugOn(!au_fi(file)->fi_hdir);
++ return au_fi(file)->fi_hdir->fd_bbot;
++}
+
-+ inode = file_inode(file);
-+ au_mtx_and_read_lock(inode);
++static inline struct au_vdir *au_fvdir_cache(struct file *file)
++{
++ FiMustAnyLock(file);
++ AuDebugOn(!au_fi(file)->fi_hdir);
++ return au_fi(file)->fi_hdir->fd_vdir_cache;
++}
+
-+ wpre.lsc = 0;
-+ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
-+ err = PTR_ERR(h_file);
-+ if (IS_ERR(h_file))
-+ goto out;
++static inline void au_set_fbtop(struct file *file, aufs_bindex_t bindex)
++{
++ FiMustWriteLock(file);
++ au_fi(file)->fi_btop = bindex;
++}
+
-+ err = vfsub_splice_from(pipe, h_file, ppos, len, flags);
-+ au_write_post(inode, h_file, &wpre, err);
++static inline void au_set_fbbot_dir(struct file *file, aufs_bindex_t bindex)
++{
++ FiMustWriteLock(file);
++ AuDebugOn(!au_fi(file)->fi_hdir);
++ au_fi(file)->fi_hdir->fd_bbot = bindex;
++}
+
-+out:
-+ si_read_unlock(inode->i_sb);
-+ inode_unlock(inode);
-+ return err;
++static inline void au_set_fvdir_cache(struct file *file,
++ struct au_vdir *vdir_cache)
++{
++ FiMustWriteLock(file);
++ AuDebugOn(!au_fi(file)->fi_hdir);
++ au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache;
+}
+
-+static long aufs_fallocate(struct file *file, int mode, loff_t offset,
-+ loff_t len)
++static inline struct file *au_hf_top(struct file *file)
+{
-+ long err;
-+ struct au_write_pre wpre;
-+ struct inode *inode;
-+ struct file *h_file;
++ FiMustAnyLock(file);
++ AuDebugOn(au_fi(file)->fi_hdir);
++ return au_fi(file)->fi_htop.hf_file;
++}
+
-+ inode = file_inode(file);
-+ au_mtx_and_read_lock(inode);
++static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex)
++{
++ FiMustAnyLock(file);
++ AuDebugOn(!au_fi(file)->fi_hdir);
++ return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file;
++}
+
-+ wpre.lsc = 0;
-+ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
-+ err = PTR_ERR(h_file);
-+ if (IS_ERR(h_file))
-+ goto out;
++/* todo: memory barrier? */
++static inline unsigned int au_figen(struct file *f)
++{
++ return atomic_read(&au_fi(f)->fi_generation);
++}
+
-+ lockdep_off();
-+ err = vfs_fallocate(h_file, mode, offset, len);
-+ lockdep_on();
-+ au_write_post(inode, h_file, &wpre, /*written*/1);
++static inline void au_set_mmapped(struct file *f)
++{
++ if (atomic_inc_return(&au_fi(f)->fi_mmapped))
++ return;
++ pr_warn("fi_mmapped wrapped around\n");
++ while (!atomic_inc_return(&au_fi(f)->fi_mmapped))
++ ;
++}
+
-+out:
-+ si_read_unlock(inode->i_sb);
-+ inode_unlock(inode);
-+ return err;
++static inline void au_unset_mmapped(struct file *f)
++{
++ atomic_dec(&au_fi(f)->fi_mmapped);
+}
+
-+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)
++static inline int au_test_mmapped(struct file *f)
+{
-+ 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]
++ return atomic_read(&au_fi(f)->fi_mmapped);
++}
+
-+ 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;
++/* customize vma->vm_file */
+
-+ au_mtx_and_read_lock(a_dst.inode);
-+ /*
-+ * in order to match the order in di_write_lock2_{child,parent}(),
-+ * use f_path.dentry for this comparision.
-+ */
-+ if (src->f_path.dentry < dst->f_path.dentry) {
-+ a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_1);
-+ err = PTR_ERR(a_src.h_file);
-+ if (IS_ERR(a_src.h_file))
-+ goto out_si;
++static inline void au_do_vm_file_reset(struct vm_area_struct *vma,
++ struct file *file)
++{
++ struct file *f;
+
-+ 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;
++ f = vma->vm_file;
++ get_file(file);
++ vma->vm_file = file;
++ fput(f);
++}
+
-+ 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;
-+ }
-+ }
++#ifdef CONFIG_MMU
++#define AuDbgVmRegion(file, vma) do {} while (0)
+
-+ 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;
-+ }
++static inline void au_vm_file_reset(struct vm_area_struct *vma,
++ struct file *file)
++{
++ au_do_vm_file_reset(vma, file);
++}
++#else
++#define AuDbgVmRegion(file, vma) \
++ AuDebugOn((vma)->vm_region && (vma)->vm_region->vm_file != (file))
+
-+ err = vfsub_copy_file_range(a_src.h_file, src_pos, a_dst.h_file,
-+ dst_pos, len, flags);
++static inline void au_vm_file_reset(struct vm_area_struct *vma,
++ struct file *file)
++{
++ struct file *f;
+
-+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
++ au_do_vm_file_reset(vma, file);
++ f = vma->vm_region->vm_file;
++ get_file(file);
++ vma->vm_region->vm_file = file;
++ fput(f);
+}
++#endif /* CONFIG_MMU */
+
-+/* ---------------------------------------------------------------------- */
++/* handle vma->vm_prfile */
++static inline void au_vm_prfile_set(struct vm_area_struct *vma,
++ struct file *file)
++{
++ get_file(file);
++ vma->vm_prfile = file;
++#ifndef CONFIG_MMU
++ get_file(file);
++ vma->vm_region->vm_prfile = file;
++#endif
++}
+
++#endif /* __KERNEL__ */
++#endif /* __AUFS_FILE_H__ */
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/finfo.c linux-4.9/fs/aufs/finfo.c
+--- linux-4.9/fs/aufs/finfo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/finfo.c 2021-02-24 16:15:09.528240413 +0100
+@@ -0,0 +1,148 @@
+/*
-+ * The locking order around current->mmap_sem.
-+ * - in most and regular cases
-+ * file I/O syscall -- aufs_read() or something
-+ * -- si_rwsem for read -- mmap_sem
-+ * (Note that [fdi]i_rwsem are released before mmap_sem).
-+ * - in mmap case
-+ * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem
-+ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for
-+ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in
-+ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though.
-+ * It means that when aufs acquires si_rwsem for write, the process should never
-+ * acquire mmap_sem.
++ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ *
-+ * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a
-+ * problem either since any directory is not able to be mmap-ed.
-+ * The similar scenario is applied to aufs_readlink() too.
++ * 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 <http://www.gnu.org/licenses/>.
+ */
+
-+#if 0 /* stop calling security_file_mmap() */
-+/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */
-+#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b)
++/*
++ * file private data
++ */
+
-+static unsigned long au_arch_prot_conv(unsigned long flags)
++#include "aufs.h"
++
++void au_hfput(struct au_hfile *hf, int execed)
+{
-+ /* currently ppc64 only */
-+#ifdef CONFIG_PPC64
-+ /* cf. linux/arch/powerpc/include/asm/mman.h */
-+ AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO);
-+ return AuConv_VM_PROT(flags, SAO);
-+#else
-+ AuDebugOn(arch_calc_vm_prot_bits(-1));
-+ return 0;
-+#endif
++ if (execed)
++ allow_write_access(hf->hf_file);
++ fput(hf->hf_file);
++ hf->hf_file = NULL;
++ au_br_put(hf->hf_br);
++ hf->hf_br = NULL;
+}
+
-+static unsigned long au_prot_conv(unsigned long flags)
++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val)
+{
-+ return AuConv_VM_PROT(flags, READ)
-+ | AuConv_VM_PROT(flags, WRITE)
-+ | AuConv_VM_PROT(flags, EXEC)
-+ | au_arch_prot_conv(flags);
-+}
++ struct au_finfo *finfo = au_fi(file);
++ struct au_hfile *hf;
++ struct au_fidir *fidir;
+
-+/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */
-+#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b)
++ fidir = finfo->fi_hdir;
++ if (!fidir) {
++ AuDebugOn(finfo->fi_btop != bindex);
++ hf = &finfo->fi_htop;
++ } else
++ hf = fidir->fd_hfile + bindex;
+
-+static unsigned long au_flag_conv(unsigned long flags)
-+{
-+ return AuConv_VM_MAP(flags, GROWSDOWN)
-+ | AuConv_VM_MAP(flags, DENYWRITE)
-+ | AuConv_VM_MAP(flags, LOCKED);
++ if (hf && hf->hf_file)
++ au_hfput(hf, vfsub_file_execed(file));
++ if (val) {
++ FiMustWriteLock(file);
++ AuDebugOn(IS_ERR_OR_NULL(file->f_path.dentry));
++ hf->hf_file = val;
++ hf->hf_br = au_sbr(file->f_path.dentry->d_sb, bindex);
++ }
+}
-+#endif
+
-+static int aufs_mmap(struct file *file, struct vm_area_struct *vma)
++void au_update_figen(struct file *file)
+{
-+ int err;
-+ const unsigned char wlock
-+ = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED);
-+ struct super_block *sb;
-+ struct file *h_file;
-+ struct inode *inode;
-+
-+ AuDbgVmRegion(file, vma);
++ atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_path.dentry));
++ /* smp_mb(); */ /* atomic_set */
++}
+
-+ inode = file_inode(file);
-+ sb = inode->i_sb;
-+ lockdep_off();
-+ si_read_lock(sb, AuLock_NOPLMW);
++/* ---------------------------------------------------------------------- */
+
-+ h_file = au_write_pre(file, wlock, /*wpre*/NULL);
-+ lockdep_on();
-+ err = PTR_ERR(h_file);
-+ if (IS_ERR(h_file))
-+ goto out;
++struct au_fidir *au_fidir_alloc(struct super_block *sb)
++{
++ struct au_fidir *fidir;
++ int nbr;
+
-+ err = 0;
-+ au_set_mmapped(file);
-+ au_vm_file_reset(vma, h_file);
-+ /*
-+ * we cannot call security_mmap_file() here since it may acquire
-+ * mmap_sem or i_mutex.
-+ *
-+ * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags),
-+ * au_flag_conv(vma->vm_flags));
-+ */
-+ if (!err)
-+ err = h_file->f_op->mmap(h_file, vma);
-+ if (!err) {
-+ au_vm_prfile_set(vma, file);
-+ fsstack_copy_attr_atime(inode, file_inode(h_file));
-+ goto out_fput; /* success */
++ nbr = au_sbbot(sb) + 1;
++ if (nbr < 2)
++ nbr = 2; /* initial allocate for 2 branches */
++ fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS);
++ if (fidir) {
++ fidir->fd_bbot = -1;
++ fidir->fd_nent = nbr;
+ }
-+ au_unset_mmapped(file);
-+ au_vm_file_reset(vma, file);
+
-+out_fput:
-+ lockdep_off();
-+ ii_write_unlock(inode);
-+ lockdep_on();
-+ fput(h_file);
-+out:
-+ lockdep_off();
-+ si_read_unlock(sb);
-+ lockdep_on();
-+ AuTraceErr(err);
-+ return err;
++ return fidir;
+}
+
-+/* ---------------------------------------------------------------------- */
-+
-+static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end,
-+ int datasync)
++int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink)
+{
+ int err;
-+ struct au_write_pre wpre;
-+ struct inode *inode;
-+ struct file *h_file;
-+
-+ err = 0; /* -EBADF; */ /* posix? */
-+ if (unlikely(!(file->f_mode & FMODE_WRITE)))
-+ goto out;
-+
-+ inode = file_inode(file);
-+ au_mtx_and_read_lock(inode);
++ struct au_fidir *fidir, *p;
+
-+ wpre.lsc = 0;
-+ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
-+ err = PTR_ERR(h_file);
-+ if (IS_ERR(h_file))
-+ goto out_unlock;
++ AuRwMustWriteLock(&finfo->fi_rwsem);
++ fidir = finfo->fi_hdir;
++ AuDebugOn(!fidir);
+
-+ err = vfsub_fsync(h_file, &h_file->f_path, datasync);
-+ au_write_post(inode, h_file, &wpre, /*written*/0);
++ err = -ENOMEM;
++ p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr),
++ GFP_NOFS, may_shrink);
++ if (p) {
++ p->fd_nent = nbr;
++ finfo->fi_hdir = p;
++ err = 0;
++ }
+
-+out_unlock:
-+ si_read_unlock(inode->i_sb);
-+ inode_unlock(inode);
-+out:
+ return err;
+}
+
-+static int aufs_fasync(int fd, struct file *file, int flag)
++/* ---------------------------------------------------------------------- */
++
++void au_finfo_fin(struct file *file)
+{
-+ int err;
-+ struct file *h_file;
-+ struct super_block *sb;
++ struct au_finfo *finfo;
+
-+ sb = file->f_path.dentry->d_sb;
-+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
++ au_nfiles_dec(file->f_path.dentry->d_sb);
+
-+ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
-+ err = PTR_ERR(h_file);
-+ if (IS_ERR(h_file))
-+ goto out;
++ finfo = au_fi(file);
++ AuDebugOn(finfo->fi_hdir);
++ AuRwDestroy(&finfo->fi_rwsem);
++ au_cache_free_finfo(finfo);
++}
+
-+ if (h_file->f_op->fasync)
-+ err = h_file->f_op->fasync(fd, h_file, flag);
-+ fput(h_file); /* instead of au_read_post() */
++void au_fi_init_once(void *_finfo)
++{
++ struct au_finfo *finfo = _finfo;
+
-+out:
-+ si_read_unlock(sb);
-+ return err;
++ au_rw_init(&finfo->fi_rwsem);
+}
+
-+static int aufs_setfl(struct file *file, unsigned long arg)
++int au_finfo_init(struct file *file, struct au_fidir *fidir)
+{
+ int err;
-+ struct file *h_file;
-+ struct super_block *sb;
-+
-+ sb = file->f_path.dentry->d_sb;
-+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
++ struct au_finfo *finfo;
++ struct dentry *dentry;
+
-+ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
-+ err = PTR_ERR(h_file);
-+ if (IS_ERR(h_file))
++ err = -ENOMEM;
++ dentry = file->f_path.dentry;
++ finfo = au_cache_alloc_finfo();
++ if (unlikely(!finfo))
+ goto out;
+
-+ /* 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() */
++ err = 0;
++ au_nfiles_inc(dentry->d_sb);
++ au_rw_write_lock(&finfo->fi_rwsem);
++ finfo->fi_btop = -1;
++ finfo->fi_hdir = fidir;
++ atomic_set(&finfo->fi_generation, au_digen(dentry));
++ /* smp_mb(); */ /* atomic_set */
++
++ file->private_data = finfo;
+
+out:
-+ si_read_unlock(sb);
+ return err;
+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* no one supports this operation, currently */
-+#if 0
-+static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset,
-+ size_t len, loff_t *pos, int more)
-+{
-+}
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+const struct file_operations aufs_file_fop = {
-+ .owner = THIS_MODULE,
-+
-+ .llseek = default_llseek,
-+
-+ .read = aufs_read,
-+ .write = aufs_write,
-+ .read_iter = aufs_read_iter,
-+ .write_iter = aufs_write_iter,
-+
-+#ifdef CONFIG_AUFS_POLL
-+ .poll = aufs_poll,
-+#endif
-+ .unlocked_ioctl = aufs_ioctl_nondir,
-+#ifdef CONFIG_COMPAT
-+ .compat_ioctl = aufs_compat_ioctl_nondir,
-+#endif
-+ .mmap = aufs_mmap,
-+ .open = aufs_open_nondir,
-+ .flush = aufs_flush_nondir,
-+ .release = aufs_release_nondir,
-+ .fsync = aufs_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,
-+ .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 2018-04-15 08:49:13.397817296 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/fstype.h linux-4.9/fs/aufs/fstype.h
+--- linux-4.9/fs/aufs/fstype.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/fstype.h 2021-02-24 16:15:09.528240413 +0100
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_FSTYPE_H__ */
-diff -urN /usr/share/empty/fs/aufs/hbl.h linux/fs/aufs/hbl.h
---- /usr/share/empty/fs/aufs/hbl.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/hbl.h 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/hbl.h linux-4.9/fs/aufs/hbl.h
+--- linux-4.9/fs/aufs/hbl.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/hbl.h 2021-02-24 16:15:09.528240413 +0100
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017-2018 Junjiro R. Okajima
+
+#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 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/hfsnotify.c linux-4.9/fs/aufs/hfsnotify.c
+--- linux-4.9/fs/aufs/hfsnotify.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/hfsnotify.c 2021-02-24 16:15:09.528240413 +0100
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ .fin_br = au_hfsn_fin_br,
+ .init_br = au_hfsn_init_br
+};
-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 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/hfsplus.c linux-4.9/fs/aufs/hfsplus.c
+--- linux-4.9/fs/aufs/hfsplus.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/hfsplus.c 2021-02-24 16:15:09.528240413 +0100
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010-2018 Junjiro R. Okajima
+ au_sbr_put(dentry->d_sb, bindex);
+ }
+}
-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 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/hnotify.c linux-4.9/fs/aufs/hnotify.c
+--- linux-4.9/fs/aufs/hnotify.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/hnotify.c 2021-02-24 16:15:09.528240413 +0100
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ h_child_inode = igrab(h_child_inode); /* can be NULL */
+ args->h_child_inode = h_child_inode;
+ args->h_child_nlen = len;
-+ if (len) {
-+ p = (void *)args;
-+ p += sizeof(*args);
-+ memcpy(p, h_child_name, len);
-+ p[len] = 0;
-+ }
-+
-+ /* NFS fires the event for silly-renamed one from kworker */
-+ f = 0;
-+ if (!dir->i_nlink
-+ || (au_test_nfs(h_dir->i_sb) && (mask & FS_DELETE)))
-+ f = AuWkq_NEST;
-+ err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f);
-+ if (unlikely(err)) {
-+ pr_err("wkq %d\n", err);
-+ iput(args->h_child_inode);
-+ iput(args->h_dir);
-+ iput(args->dir);
-+ kfree(args);
-+ }
-+
-+out:
-+ return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm)
-+{
-+ int err;
-+
-+ AuDebugOn(!(udba & AuOptMask_UDBA));
-+
-+ err = 0;
-+ if (au_hnotify_op.reset_br)
-+ err = au_hnotify_op.reset_br(udba, br, perm);
-+
-+ return err;
-+}
-+
-+int au_hnotify_init_br(struct au_branch *br, int perm)
-+{
-+ int err;
-+
-+ err = 0;
-+ if (au_hnotify_op.init_br)
-+ err = au_hnotify_op.init_br(br, perm);
-+
-+ return err;
-+}
-+
-+void au_hnotify_fin_br(struct au_branch *br)
-+{
-+ if (au_hnotify_op.fin_br)
-+ au_hnotify_op.fin_br(br);
-+}
-+
-+static void au_hn_destroy_cache(void)
-+{
-+ kmem_cache_destroy(au_cache[AuCache_HNOTIFY]);
-+ au_cache[AuCache_HNOTIFY] = NULL;
-+}
-+
-+int __init au_hnotify_init(void)
-+{
-+ int err;
-+
-+ err = -ENOMEM;
-+ au_cache[AuCache_HNOTIFY] = AuCache(au_hnotify);
-+ if (au_cache[AuCache_HNOTIFY]) {
-+ err = 0;
-+ if (au_hnotify_op.init)
-+ err = au_hnotify_op.init();
-+ if (unlikely(err))
-+ au_hn_destroy_cache();
-+ }
-+ AuTraceErr(err);
-+ return err;
-+}
-+
-+void au_hnotify_fin(void)
-+{
-+ if (au_hnotify_op.fin)
-+ au_hnotify_op.fin();
-+
-+ /* cf. au_cache_fin() */
-+ if (au_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 2018-04-15 08:49:13.401150731 +0200
-@@ -0,0 +1,285 @@
-+/*
-+ * 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
-+ * 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 <http://www.gnu.org/licenses/>.
-+ */
-+
-+/*
-+ * inode private data
-+ */
-+
-+#include "aufs.h"
-+
-+struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex)
-+{
-+ struct inode *h_inode;
-+ struct au_hinode *hinode;
-+
-+ IiMustAnyLock(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;
-+}
-+
-+/* todo: hard/soft set? */
-+void au_hiput(struct au_hinode *hinode)
-+{
-+ au_hn_free(hinode);
-+ dput(hinode->hi_whdentry);
-+ iput(hinode->hi_inode);
-+}
-+
-+unsigned int au_hi_flags(struct inode *inode, int isdir)
-+{
-+ unsigned int flags;
-+ const unsigned int mnt_flags = au_mntflags(inode->i_sb);
-+
-+ flags = 0;
-+ if (au_opt_test(mnt_flags, XINO))
-+ au_fset_hi(flags, XINO);
-+ if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY))
-+ au_fset_hi(flags, HNOTIFY);
-+ return flags;
-+}
-+
-+void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
-+ struct inode *h_inode, unsigned int flags)
-+{
-+ struct au_hinode *hinode;
-+ struct inode *hi;
-+ struct au_iinfo *iinfo = au_ii(inode);
-+
-+ IiMustWriteLock(inode);
-+
-+ hinode = au_hinode(iinfo, bindex);
-+ hi = hinode->hi_inode;
-+ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0);
-+
-+ if (hi)
-+ au_hiput(hinode);
-+ hinode->hi_inode = h_inode;
-+ if (h_inode) {
-+ int err;
-+ struct super_block *sb = inode->i_sb;
-+ struct au_branch *br;
-+
-+ AuDebugOn(inode->i_mode
-+ && (h_inode->i_mode & S_IFMT)
-+ != (inode->i_mode & S_IFMT));
-+ if (bindex == iinfo->ii_btop)
-+ au_cpup_igen(inode, h_inode);
-+ br = au_sbr(sb, bindex);
-+ hinode->hi_id = br->br_id;
-+ if (au_ftest_hi(flags, XINO)) {
-+ err = au_xino_write(sb, bindex, h_inode->i_ino,
-+ inode->i_ino);
-+ if (unlikely(err))
-+ AuIOErr1("failed au_xino_write() %d\n", err);
-+ }
-+
-+ if (au_ftest_hi(flags, HNOTIFY)
-+ && au_br_hnotifyable(br->br_perm)) {
-+ err = au_hn_alloc(hinode, inode);
-+ if (unlikely(err))
-+ AuIOErr1("au_hn_alloc() %d\n", err);
-+ }
-+ }
-+}
-+
-+void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
-+ struct dentry *h_wh)
-+{
-+ struct au_hinode *hinode;
-+
-+ IiMustWriteLock(inode);
-+
-+ hinode = au_hinode(au_ii(inode), bindex);
-+ AuDebugOn(hinode->hi_whdentry);
-+ hinode->hi_whdentry = h_wh;
-+}
-+
-+void au_update_iigen(struct inode *inode, int half)
-+{
-+ struct au_iinfo *iinfo;
-+ struct au_iigen *iigen;
-+ unsigned int sigen;
-+
-+ sigen = au_sigen(inode->i_sb);
-+ iinfo = au_ii(inode);
-+ iigen = &iinfo->ii_generation;
-+ spin_lock(&iigen->ig_spin);
-+ iigen->ig_generation = sigen;
-+ if (half)
-+ au_ig_fset(iigen->ig_flags, HALF_REFRESHED);
-+ else
-+ au_ig_fclr(iigen->ig_flags, HALF_REFRESHED);
-+ spin_unlock(&iigen->ig_spin);
-+}
-+
-+/* it may be called at remount time, too */
-+void au_update_ibrange(struct inode *inode, int do_put_zero)
-+{
-+ struct au_iinfo *iinfo;
-+ aufs_bindex_t bindex, bbot;
-+
-+ AuDebugOn(au_is_bad_inode(inode));
-+ IiMustWriteLock(inode);
-+
-+ 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 = au_hinode(iinfo, bindex)->hi_inode;
-+ if (h_i
-+ && !h_i->i_nlink
-+ && !(h_i->i_state & I_LINKABLE))
-+ au_set_h_iptr(inode, bindex, NULL, 0);
-+ }
-+ }
-+
-+ 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_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_btop > iinfo->ii_bbot);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void au_icntnr_init_once(void *_c)
-+{
-+ struct au_icntnr *c = _c;
-+ struct au_iinfo *iinfo = &c->iinfo;
-+
-+ spin_lock_init(&iinfo->ii_generation.ig_spin);
-+ au_rw_init(&iinfo->ii_rwsem);
-+ 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_sbbot(sb) + 1;
-+ if (unlikely(nbr <= 0))
-+ nbr = 1;
-+ hi = kmalloc_array(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS);
-+ if (hi) {
-+ au_ninodes_inc(sb);
-+
-+ 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_btop = -1;
-+ iinfo->ii_bbot = -1;
-+ iinfo->ii_vdir = NULL;
-+ return 0;
-+ }
-+ return -ENOMEM;
-+}
-+
-+int au_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink)
-+{
-+ int err, i;
-+ struct au_hinode *hip;
-+
-+ AuRwMustWriteLock(&iinfo->ii_rwsem);
-+
-+ err = -ENOMEM;
-+ 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;
-+ }
-+
-+ return err;
-+}
-+
-+void au_iinfo_fin(struct inode *inode)
-+{
-+ struct au_iinfo *iinfo;
-+ struct au_hinode *hi;
-+ struct super_block *sb;
-+ aufs_bindex_t bindex, bbot;
-+ const unsigned char unlinked = !inode->i_nlink;
-+
-+ AuDebugOn(au_is_bad_inode(inode));
-+
-+ sb = inode->i_sb;
-+ au_ninodes_dec(sb);
-+ if (si_pid_test(sb))
-+ au_xino_delete_inode(inode, unlinked);
-+ else {
-+ /*
-+ * it is safe to hide the dependency between sbinfo and
-+ * sb->s_umount.
-+ */
-+ lockdep_off();
-+ si_noflush_read_lock(sb);
-+ au_xino_delete_inode(inode, unlinked);
-+ si_read_unlock(sb);
-+ lockdep_on();
-+ }
-+
-+ iinfo = au_ii(inode);
-+ if (iinfo->ii_vdir)
-+ au_vdir_free(iinfo->ii_vdir);
-+
-+ bindex = iinfo->ii_btop;
-+ if (bindex >= 0) {
-+ hi = au_hinode(iinfo, bindex);
-+ bbot = iinfo->ii_bbot;
-+ while (bindex++ <= bbot) {
-+ if (hi->hi_inode)
-+ au_hiput(hi);
-+ hi++;
-+ }
-+ }
-+ kfree(iinfo->ii_hinode);
-+ AuRwDestroy(&iinfo->ii_rwsem);
-+}
-diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c
---- /usr/share/empty/fs/aufs/inode.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/inode.c 2018-04-15 08:49:13.401150731 +0200
-@@ -0,0 +1,527 @@
-+/*
-+ * 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
-+ * 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 <http://www.gnu.org/licenses/>.
-+ */
-+
-+/*
-+ * inode functions
-+ */
-+
-+#include "aufs.h"
-+
-+struct inode *au_igrab(struct inode *inode)
-+{
-+ if (inode) {
-+ AuDebugOn(!atomic_read(&inode->i_count));
-+ ihold(inode);
-+ }
-+ return inode;
-+}
-+
-+static void au_refresh_hinode_attr(struct inode *inode, int do_version)
-+{
-+ au_cpup_attr_all(inode, /*force*/0);
-+ au_update_iigen(inode, /*half*/1);
-+ if (do_version)
-+ inode->i_version++;
-+}
-+
-+static int au_ii_refresh(struct inode *inode, int *update)
-+{
-+ 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_hinode_realloc(iinfo, nbr, /*may_shrink*/0);
-+ if (unlikely(err))
-+ goto out;
-+
-+ 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;
-+
-+ AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT));
-+ new_bindex = au_br_index(sb, p->hi_id);
-+ if (new_bindex == bindex)
-+ continue;
-+
-+ if (new_bindex < 0) {
-+ *update = 1;
-+ au_hiput(p);
-+ p->hi_inode = NULL;
-+ continue;
-+ }
-+
-+ if (new_bindex < iinfo->ii_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 = au_hinode(iinfo, new_bindex);
-+ tmp = *q;
-+ *q = *p;
-+ *p = tmp;
-+ if (tmp.hi_inode) {
-+ bindex--;
-+ p--;
-+ }
++ if (len) {
++ p = (void *)args;
++ p += sizeof(*args);
++ memcpy(p, h_child_name, len);
++ p[len] = 0;
++ }
++
++ /* NFS fires the event for silly-renamed one from kworker */
++ f = 0;
++ if (!dir->i_nlink
++ || (au_test_nfs(h_dir->i_sb) && (mask & FS_DELETE)))
++ f = AuWkq_NEST;
++ err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f);
++ if (unlikely(err)) {
++ pr_err("wkq %d\n", err);
++ iput(args->h_child_inode);
++ iput(args->h_dir);
++ iput(args->dir);
++ kfree(args);
+ }
-+ 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;
+
+out:
-+ AuTraceErr(err);
+ return err;
+}
+
-+void au_refresh_iop(struct inode *inode, int force_getattr)
++/* ---------------------------------------------------------------------- */
++
++int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm)
+{
-+ int type;
-+ struct au_sbinfo *sbi = au_sbi(inode->i_sb);
-+ const struct inode_operations *iop
-+ = force_getattr ? aufs_iop : sbi->si_iop_array;
++ int err;
+
-+ if (inode->i_op == iop)
-+ return;
++ AuDebugOn(!(udba & AuOptMask_UDBA));
+
-+ switch (inode->i_mode & S_IFMT) {
-+ case S_IFDIR:
-+ type = AuIop_DIR;
-+ break;
-+ case S_IFLNK:
-+ type = AuIop_SYMLINK;
-+ break;
-+ default:
-+ type = AuIop_OTHER;
-+ break;
-+ }
++ err = 0;
++ if (au_hnotify_op.reset_br)
++ err = au_hnotify_op.reset_br(udba, br, perm);
+
-+ inode->i_op = iop + type;
-+ /* unnecessary smp_wmb() */
++ return err;
+}
+
-+int au_refresh_hinode_self(struct inode *inode)
++int au_hnotify_init_br(struct au_branch *br, int perm)
+{
-+ int err, update;
++ int err;
+
-+ err = au_ii_refresh(inode, &update);
-+ if (!err)
-+ au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode));
++ err = 0;
++ if (au_hnotify_op.init_br)
++ err = au_hnotify_op.init_br(br, perm);
+
-+ AuTraceErr(err);
+ return err;
+}
+
-+int au_refresh_hinode(struct inode *inode, struct dentry *dentry)
++void au_hnotify_fin_br(struct au_branch *br)
+{
-+ int err, e, update;
-+ unsigned int flags;
-+ umode_t mode;
-+ aufs_bindex_t bindex, bbot;
-+ unsigned char isdir;
-+ struct au_hinode *p;
-+ struct au_iinfo *iinfo;
-+
-+ err = au_ii_refresh(inode, &update);
-+ if (unlikely(err))
-+ goto out;
-+
-+ update = 0;
-+ iinfo = au_ii(inode);
-+ p = au_hinode(iinfo, iinfo->ii_btop);
-+ mode = (inode->i_mode & S_IFMT);
-+ isdir = S_ISDIR(mode);
-+ flags = au_hi_flags(inode, isdir);
-+ bbot = au_dbbot(dentry);
-+ for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) {
-+ struct inode *h_i, *h_inode;
-+ struct dentry *h_d;
-+
-+ h_d = au_h_dptr(dentry, bindex);
-+ if (!h_d || d_is_negative(h_d))
-+ continue;
-+
-+ h_inode = d_inode(h_d);
-+ AuDebugOn(mode != (h_inode->i_mode & S_IFMT));
-+ if (iinfo->ii_btop <= bindex && bindex <= iinfo->ii_bbot) {
-+ h_i = au_h_iptr(inode, bindex);
-+ if (h_i) {
-+ if (h_i == h_inode)
-+ continue;
-+ err = -EIO;
-+ break;
-+ }
-+ }
-+ 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;
-+ }
-+ au_update_ibrange(inode, /*do_put_zero*/0);
-+ e = au_dy_irefresh(inode);
-+ if (unlikely(e && !err))
-+ err = e;
-+ if (!err)
-+ au_refresh_hinode_attr(inode, update && isdir);
++ if (au_hnotify_op.fin_br)
++ au_hnotify_op.fin_br(br);
++}
+
-+out:
-+ AuTraceErr(err);
-+ return err;
++static void au_hn_destroy_cache(void)
++{
++ kmem_cache_destroy(au_cache[AuCache_HNOTIFY]);
++ au_cache[AuCache_HNOTIFY] = NULL;
+}
+
-+static int set_inode(struct inode *inode, struct dentry *dentry)
++int __init au_hnotify_init(void)
+{
+ int err;
-+ unsigned int flags;
-+ umode_t mode;
-+ aufs_bindex_t bindex, btop, btail;
-+ unsigned char isdir;
-+ struct dentry *h_dentry;
-+ struct inode *h_inode;
-+ struct au_iinfo *iinfo;
-+ struct inode_operations *iop;
-+
-+ IiMustWriteLock(inode);
+
-+ err = 0;
-+ isdir = 0;
-+ iop = au_sbi(inode->i_sb)->si_iop_array;
-+ 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) {
-+ case S_IFREG:
-+ btail = au_dbtail(dentry);
-+ inode->i_op = iop + AuIop_OTHER;
-+ inode->i_fop = &aufs_file_fop;
-+ err = au_dy_iaop(inode, btop, h_inode);
++ err = -ENOMEM;
++ au_cache[AuCache_HNOTIFY] = AuCache(au_hnotify);
++ if (au_cache[AuCache_HNOTIFY]) {
++ err = 0;
++ if (au_hnotify_op.init)
++ err = au_hnotify_op.init();
+ if (unlikely(err))
-+ goto out;
-+ break;
-+ case S_IFDIR:
-+ isdir = 1;
-+ btail = au_dbtaildir(dentry);
-+ inode->i_op = iop + AuIop_DIR;
-+ inode->i_fop = &aufs_dir_fop;
-+ break;
-+ case S_IFLNK:
-+ btail = au_dbtail(dentry);
-+ inode->i_op = iop + AuIop_SYMLINK;
-+ break;
-+ case S_IFBLK:
-+ case S_IFCHR:
-+ case S_IFIFO:
-+ case S_IFSOCK:
-+ btail = au_dbtail(dentry);
-+ inode->i_op = iop + AuIop_OTHER;
-+ init_special_inode(inode, mode, h_inode->i_rdev);
-+ break;
-+ default:
-+ AuIOErr("Unknown file type 0%o\n", mode);
-+ err = -EIO;
-+ goto out;
++ au_hn_destroy_cache();
+ }
++ AuTraceErr(err);
++ return err;
++}
+
-+ /* do not set hnotify for whiteouted dirs (SHWH mode) */
-+ flags = au_hi_flags(inode, isdir);
-+ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)
-+ && au_ftest_hi(flags, HNOTIFY)
-+ && dentry->d_name.len > AUFS_WH_PFX_LEN
-+ && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))
-+ au_fclr_hi(flags, HNOTIFY);
-+ iinfo = au_ii(inode);
-+ iinfo->ii_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,
-+ au_igrab(d_inode(h_dentry)), flags);
-+ }
-+ au_cpup_attr_all(inode, /*force*/1);
-+ /*
-+ * to force calling aufs_get_acl() every time,
-+ * do not call cache_no_acl() for aufs inode.
-+ */
++void au_hnotify_fin(void)
++{
++ if (au_hnotify_op.fin)
++ au_hnotify_op.fin();
+
-+out:
-+ return err;
++ /* cf. au_cache_fin() */
++ if (au_cache[AuCache_HNOTIFY])
++ au_hn_destroy_cache();
+}
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/i_op.c linux-4.9/fs/aufs/i_op.c
+--- linux-4.9/fs/aufs/i_op.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/i_op.c 2021-02-24 16:15:09.531573855 +0100
+@@ -0,0 +1,1459 @@
++/*
++ * 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
++ * 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 <http://www.gnu.org/licenses/>.
++ */
+
+/*
-+ * successful returns with iinfo write_locked
-+ * minus: errno
-+ * zero: success, matched
-+ * plus: no error, but unmatched
++ * inode operations (except add/del/rename)
+ */
-+static int reval_inode(struct inode *inode, struct dentry *dentry)
++
++#include <linux/device_cgroup.h>
++#include <linux/fs_stack.h>
++#include <linux/namei.h>
++#include <linux/security.h>
++#include "aufs.h"
++
++static int h_permission(struct inode *h_inode, int mask,
++ struct path *h_path, int brperm)
+{
+ int err;
-+ unsigned int gen, igflags;
-+ aufs_bindex_t bindex, bbot;
-+ struct inode *h_inode, *h_dinode;
-+ struct dentry *h_dentry;
++ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));
+
-+ /*
-+ * before this function, if aufs got any iinfo lock, it must be only
-+ * one, the parent dir.
-+ * it can happen by UDBA and the obsoleted inode number.
-+ */
-+ err = -EIO;
-+ if (unlikely(inode->i_ino == parent_ino(dentry)))
++ err = -EPERM;
++ if (write_mask && IS_IMMUTABLE(h_inode))
+ goto out;
+
-+ err = 1;
-+ ii_write_lock_new_child(inode);
-+ h_dentry = au_h_dptr(dentry, au_dbtop(dentry));
-+ h_dinode = d_inode(h_dentry);
-+ 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, &igflags);
-+ if (gen == au_digen(dentry)
-+ && !au_ig_ftest(igflags, HALF_REFRESHED))
-+ break;
-+
-+ /* fully refresh inode using dentry */
-+ err = au_refresh_hinode(inode, dentry);
-+ if (!err)
-+ au_update_iigen(inode, /*half*/0);
-+ break;
-+ }
-+
-+ if (unlikely(err))
-+ ii_write_unlock(inode);
-+out:
-+ return err;
-+}
-+
-+int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
-+ unsigned int d_type, ino_t *ino)
-+{
-+ int err, idx;
-+ const int isnondir = d_type != DT_DIR;
++ err = -EACCES;
++ if (((mask & MAY_EXEC)
++ && S_ISREG(h_inode->i_mode)
++ && (path_noexec(h_path)
++ || !(h_inode->i_mode & S_IXUGO))))
++ goto out;
+
-+ /* prevent hardlinked inode number from race condition */
-+ if (isnondir) {
-+ err = au_xinondir_enter(sb, bindex, h_ino, &idx);
-+ if (unlikely(err))
-+ 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.'
++ * in this case, generic_permission() returns -EOPNOTSUPP.
++ */
++ if ((write_mask && !au_br_writable(brperm))
++ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode)
++ && write_mask && !(mask & MAY_READ))
++ || !h_inode->i_op->permission) {
++ /* AuLabel(generic_permission); */
++ /* AuDbg("get_acl %pf\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);
++ AuTraceErr(err);
++ } else {
++ /* AuLabel(h_inode->permission); */
++ err = h_inode->i_op->permission(h_inode, mask);
++ AuTraceErr(err);
+ }
+
-+ err = au_xino_read(sb, bindex, h_ino, ino);
-+ if (unlikely(err))
-+ goto out_xinondir;
++ if (!err)
++ err = devcgroup_inode_permission(h_inode, mask);
++ if (!err)
++ err = security_inode_permission(h_inode, mask);
+
-+ if (!*ino) {
-+ err = -EIO;
-+ *ino = au_xino_new_ino(sb);
-+ if (unlikely(!*ino))
-+ goto out_xinondir;
-+ err = au_xino_write(sb, bindex, h_ino, *ino);
-+ if (unlikely(err))
-+ goto out_xinondir;
++#if 0
++ if (!err) {
++ /* todo: do we need to call ima_path_check()? */
++ struct path h_path = {
++ .dentry =
++ .mnt = h_mnt
++ };
++ err = ima_path_check(&h_path,
++ mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
++ IMA_COUNT_LEAVE);
+ }
++#endif
+
-+out_xinondir:
-+ if (isnondir && idx >= 0)
-+ au_xinondir_leave(sb, bindex, h_ino, idx);
+out:
+ return err;
+}
+
-+/* successful returns with iinfo write_locked */
-+/* todo: return with unlocked? */
-+struct inode *au_new_inode(struct dentry *dentry, int must_new)
++static int aufs_permission(struct inode *inode, int mask)
+{
-+ struct inode *inode, *h_inode;
-+ struct dentry *h_dentry;
++ int err;
++ 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;
+ struct super_block *sb;
-+ ino_t h_ino, ino;
-+ int err, idx, hlinked;
-+ aufs_bindex_t btop;
-+
-+ sb = dentry->d_sb;
-+ 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;
++ struct au_branch *br;
+
-+new_ino:
-+ /*
-+ * stop 'race'-ing between hardlinks under different
-+ * parents.
-+ */
-+ if (hlinked) {
-+ err = au_xinondir_enter(sb, btop, h_ino, &idx);
-+ inode = ERR_PTR(err);
-+ if (unlikely(err))
-+ goto out;
-+ }
++ /* todo: support rcu-walk? */
++ if (mask & MAY_NOT_BLOCK)
++ return -ECHILD;
+
-+ err = au_xino_read(sb, btop, h_ino, &ino);
-+ inode = ERR_PTR(err);
++ sb = inode->i_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++ ii_read_lock_child(inode);
++#if 0
++ err = au_iigen_test(inode, au_sigen(sb));
+ if (unlikely(err))
-+ goto out_xinondir;
++ goto out;
++#endif
+
-+ if (!ino) {
-+ ino = au_xino_new_ino(sb);
-+ if (unlikely(!ino)) {
-+ inode = ERR_PTR(-EIO);
-+ goto out_xinondir;
++ if (!isdir
++ || write_mask
++ || au_opt_test(au_mntflags(sb), DIRPERM1)) {
++ err = au_busy_or_stale();
++ 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_ibtop(inode);
++ br = au_sbr(sb, bindex);
++ err = h_permission(h_inode, mask, &br->br_path, br->br_perm);
++ if (write_mask
++ && !err
++ && !special_file(h_inode->i_mode)) {
++ /* test whether the upper writable branch exists */
++ err = -EROFS;
++ for (; bindex >= 0; bindex--)
++ if (!au_br_rdonly(au_sbr(sb, bindex))) {
++ err = 0;
++ break;
++ }
+ }
++ goto out;
+ }
+
-+ AuDbg("i%lu\n", (unsigned long)ino);
-+ inode = au_iget_locked(sb, ino);
-+ err = PTR_ERR(inode);
-+ if (IS_ERR(inode))
-+ goto out_xinondir;
-+
-+ AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
-+ if (inode->i_state & I_NEW) {
-+ ii_write_lock_new_child(inode);
-+ err = set_inode(inode, dentry);
-+ if (!err) {
-+ unlock_new_inode(inode);
-+ goto out_xinondir; /* success */
-+ }
++ /* non-write to dir */
++ err = 0;
++ 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();
++ if (unlikely(!S_ISDIR(h_inode->i_mode)))
++ break;
+
-+ /*
-+ * iget_failed() calls iput(), but we need to call
-+ * ii_write_unlock() after iget_failed(). so dirty hack for
-+ * i_count.
-+ */
-+ atomic_inc(&inode->i_count);
-+ iget_failed(inode);
-+ ii_write_unlock(inode);
-+ au_xino_write(sb, btop, h_ino, /*ino*/0);
-+ /* ignore this error */
-+ goto out_iput;
-+ } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) {
-+ /*
-+ * horrible race condition between lookup, readdir and copyup
-+ * (or something).
-+ */
-+ if (hlinked && idx >= 0)
-+ au_xinondir_leave(sb, btop, h_ino, idx);
-+ err = reval_inode(inode, dentry);
-+ if (unlikely(err < 0)) {
-+ hlinked = 0;
-+ goto out_iput;
-+ }
-+ if (!err)
-+ goto out; /* success */
-+ 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;
-+ }
++ br = au_sbr(sb, bindex);
++ err = h_permission(h_inode, mask, &br->br_path,
++ br->br_perm);
+ }
+ }
+
-+ 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",
-+ btop, au_sbtype(h_dentry->d_sb), dentry,
-+ (unsigned long)h_ino, (unsigned long)ino);
-+ ino = 0;
-+ err = au_xino_write(sb, btop, h_ino, /*ino*/0);
-+ if (!err) {
-+ iput(inode);
-+ 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:
-+ return inode;
++ ii_read_unlock(inode);
++ si_read_unlock(sb);
++ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
-+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
-+ struct inode *inode)
++static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
++ unsigned int flags)
+{
-+ int err;
-+ struct inode *hi;
++ struct dentry *ret, *parent;
++ struct inode *inode;
++ struct super_block *sb;
++ int err, npositive;
+
-+ err = au_br_rdonly(au_sbr(sb, bindex));
++ IMustLock(dir);
+
-+ /* pseudo-link after flushed may happen out of bounds */
-+ if (!err
-+ && inode
-+ && au_ibtop(inode) <= bindex
-+ && bindex <= au_ibbot(inode)) {
-+ /*
-+ * permission check is unnecessary since vfsub routine
-+ * will be called later
-+ */
-+ hi = au_h_iptr(inode, bindex);
-+ if (hi)
-+ err = IS_IMMUTABLE(hi) ? -EROFS : 0;
++ /* todo: support rcu-walk? */
++ ret = ERR_PTR(-ECHILD);
++ if (flags & LOOKUP_RCU)
++ goto out;
++
++ ret = ERR_PTR(-ENAMETOOLONG);
++ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
++ goto out;
++
++ sb = dir->i_sb;
++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
++ ret = ERR_PTR(err);
++ if (unlikely(err))
++ goto out;
++
++ err = au_di_init(dentry);
++ ret = ERR_PTR(err);
++ if (unlikely(err))
++ goto out_si;
++
++ inode = NULL;
++ npositive = 0; /* suppress a warning */
++ parent = dentry->d_parent; /* dir inode is locked */
++ di_read_lock_parent(parent, AuLock_IR);
++ err = au_alive_dir(parent);
++ if (!err)
++ err = au_digen_test(parent, au_sigen(sb));
++ if (!err) {
++ /* 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);
++ ret = ERR_PTR(err);
++ if (unlikely(err < 0))
++ goto out_unlock;
+
-+ return err;
-+}
++ if (npositive) {
++ inode = au_new_inode(dentry, /*must_new*/0);
++ if (IS_ERR(inode)) {
++ ret = (void *)inode;
++ inode = NULL;
++ goto out_unlock;
++ }
++ }
+
-+int au_test_h_perm(struct inode *h_inode, int mask)
-+{
-+ if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID))
-+ return 0;
-+ return inode_permission(h_inode, mask);
-+}
++ if (inode)
++ atomic_inc(&inode->i_count);
++ ret = d_splice_alias(inode, dentry);
++#if 0
++ if (unlikely(d_need_lookup(dentry))) {
++ spin_lock(&dentry->d_lock);
++ dentry->d_flags &= ~DCACHE_NEED_LOOKUP;
++ spin_unlock(&dentry->d_lock);
++ } else
++#endif
++ if (inode) {
++ if (!IS_ERR(ret)) {
++ iput(inode);
++ if (ret && ret != dentry)
++ ii_write_unlock(inode);
++ } else {
++ ii_write_unlock(inode);
++ iput(inode);
++ inode = NULL;
++ }
++ }
+
-+int au_test_h_perm_sio(struct inode *h_inode, int mask)
-+{
-+ if (au_test_nfs(h_inode->i_sb)
-+ && (mask & MAY_WRITE)
-+ && S_ISDIR(h_inode->i_mode))
-+ mask |= MAY_READ; /* force permission check */
-+ return au_test_h_perm(h_inode, mask);
++out_unlock:
++ di_write_unlock(dentry);
++out_si:
++ si_read_unlock(sb);
++out:
++ return ret;
+}
-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 2018-04-15 08:49:13.401150731 +0200
-@@ -0,0 +1,696 @@
-+/*
-+ * 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
-+ * 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 <http://www.gnu.org/licenses/>.
-+ */
-+
-+/*
-+ * inode operations
-+ */
+
-+#ifndef __AUFS_INODE_H__
-+#define __AUFS_INODE_H__
++/* ---------------------------------------------------------------------- */
+
-+#ifdef __KERNEL__
++struct aopen_node {
++ struct hlist_bl_node hblist;
++ struct file *file, *h_file;
++};
+
-+#include <linux/fsnotify.h>
-+#include "rwsem.h"
++static int au_do_aopen(struct inode *inode, struct file *file)
++{
++ struct hlist_bl_head *aopen;
++ struct hlist_bl_node *pos;
++ struct aopen_node *node;
++ struct au_do_open_args args = {
++ .aopen = 1,
++ .open = au_do_open_nondir
++ };
+
-+struct vfsmount;
++ aopen = &au_sbi(inode->i_sb)->si_aopen;
++ hlist_bl_lock(aopen);
++ hlist_bl_for_each_entry(node, pos, aopen, hblist)
++ if (node->file == file) {
++ args.h_file = node->h_file;
++ break;
++ }
++ hlist_bl_unlock(aopen);
++ /* AuDebugOn(!args.h_file); */
+
-+struct au_hnotify {
-+#ifdef CONFIG_AUFS_HNOTIFY
-+#ifdef CONFIG_AUFS_HFSNOTIFY
-+ /* never use fsnotify_add_vfsmount_mark() */
-+ struct fsnotify_mark hn_mark;
-+#endif
-+ struct inode *hn_aufs_inode; /* no get/put */
-+#endif
-+} ____cacheline_aligned_in_smp;
++ return au_do_open(file, &args);
++}
+
-+struct au_hinode {
-+ struct inode *hi_inode;
-+ aufs_bindex_t hi_id;
-+#ifdef CONFIG_AUFS_HNOTIFY
-+ struct au_hnotify *hi_notify;
-+#endif
++static int aufs_atomic_open(struct inode *dir, struct dentry *dentry,
++ struct file *file, unsigned int open_flag,
++ umode_t create_mode, int *opened)
++{
++ int err, unlocked, h_opened = *opened;
++ unsigned int lkup_flags;
++ struct dentry *parent, *d;
++ struct hlist_bl_head *aopen;
++ struct vfsub_aopen_args args = {
++ .open_flag = open_flag,
++ .create_mode = create_mode,
++ .opened = &h_opened
++ };
++ struct aopen_node aopen_node = {
++ .file = file
++ };
+
-+ /* reference to the copied-up whiteout with get/put */
-+ struct dentry *hi_whdentry;
-+};
++ IMustLock(dir);
++ AuDbg("open_flag 0%o\n", open_flag);
++ AuDbgDentry(dentry);
+
-+/* ig_flags */
-+#define AuIG_HALF_REFRESHED 1
-+#define au_ig_ftest(flags, name) ((flags) & AuIG_##name)
-+#define au_ig_fset(flags, name) \
-+ do { (flags) |= AuIG_##name; } while (0)
-+#define au_ig_fclr(flags, name) \
-+ do { (flags) &= ~AuIG_##name; } while (0)
++ err = 0;
++ if (!au_di(dentry)) {
++ 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) {
++ /*
++ * obsoleted dentry found.
++ * another error will be returned later.
++ */
++ d_drop(d);
++ AuDbgDentry(d);
++ dput(d);
++ }
++ AuDbgDentry(dentry);
++ }
+
-+struct au_iigen {
-+ spinlock_t ig_spin;
-+ __u32 ig_generation, ig_flags;
-+};
++ if (d_is_positive(dentry)
++ || d_unhashed(dentry)
++ || d_unlinked(dentry)
++ || !(open_flag & O_CREAT))
++ goto out_no_open;
+
-+struct au_vdir;
-+struct au_iinfo {
-+ struct au_iigen ii_generation;
-+ struct super_block *ii_hsb1; /* no get/put */
++ unlocked = 0;
++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN);
++ if (unlikely(err))
++ goto out;
+
-+ struct au_rwsem ii_rwsem;
-+ aufs_bindex_t ii_btop, ii_bbot;
-+ __u32 ii_higen;
-+ struct au_hinode *ii_hinode;
-+ struct au_vdir *ii_vdir;
-+};
++ parent = dentry->d_parent; /* dir is locked */
++ di_write_lock_parent(parent);
++ err = au_lkup_dentry(dentry, /*btop*/0, AuLkup_ALLOW_NEG);
++ if (unlikely(err))
++ goto out_unlock;
+
-+struct au_icntnr {
-+ struct au_iinfo iinfo;
-+ struct inode vfs_inode;
-+ struct hlist_bl_node plink;
-+} ____cacheline_aligned_in_smp;
++ AuDbgDentry(dentry);
++ if (d_is_positive(dentry))
++ goto out_unlock;
+
-+/* au_pin flags */
-+#define AuPin_DI_LOCKED 1
-+#define AuPin_MNT_WRITE (1 << 1)
-+#define au_ftest_pin(flags, name) ((flags) & AuPin_##name)
-+#define au_fset_pin(flags, name) \
-+ do { (flags) |= AuPin_##name; } while (0)
-+#define au_fclr_pin(flags, name) \
-+ do { (flags) &= ~AuPin_##name; } while (0)
++ args.file = get_empty_filp();
++ err = PTR_ERR(args.file);
++ if (IS_ERR(args.file))
++ goto out_unlock;
+
-+struct au_pin {
-+ /* input */
-+ struct dentry *dentry;
-+ unsigned int udba;
-+ unsigned char lsc_di, lsc_hi, flags;
-+ aufs_bindex_t bindex;
++ args.file->f_flags = file->f_flags;
++ err = au_aopen_or_create(dir, dentry, &args);
++ AuTraceErr(err);
++ AuDbgFile(args.file);
++ if (unlikely(err < 0)) {
++ if (h_opened & FILE_OPENED)
++ fput(args.file);
++ else
++ put_filp(args.file);
++ goto out_unlock;
++ }
++ di_write_unlock(parent);
++ di_write_unlock(dentry);
++ unlocked = 1;
+
-+ /* output */
-+ struct dentry *parent;
-+ struct au_hinode *hdir;
-+ struct vfsmount *h_mnt;
++ /* some filesystems don't set FILE_CREATED while succeeded? */
++ *opened |= FILE_CREATED;
++ if (h_opened & FILE_OPENED)
++ aopen_node.h_file = args.file;
++ else {
++ put_filp(args.file);
++ args.file = NULL;
++ }
++ aopen = &au_sbi(dir->i_sb)->si_aopen;
++ au_hbl_add(&aopen_node.hblist, aopen);
++ err = finish_open(file, dentry, au_do_aopen, opened);
++ au_hbl_del(&aopen_node.hblist, aopen);
++ AuTraceErr(err);
++ AuDbgFile(file);
++ if (aopen_node.h_file)
++ fput(aopen_node.h_file);
+
-+ /* temporary unlock/relock for copyup */
-+ struct dentry *h_dentry, *h_parent;
-+ struct au_branch *br;
-+ struct task_struct *task;
-+};
++out_unlock:
++ if (unlocked)
++ si_read_unlock(dentry->d_sb);
++ else {
++ di_write_unlock(parent);
++ aufs_read_unlock(dentry, AuLock_DW);
++ }
++ AuDbgDentry(dentry);
++ if (unlikely(err < 0))
++ goto out;
++out_no_open:
++ if (err >= 0 && !(*opened & FILE_CREATED)) {
++ AuLabel(out_no_open);
++ dget(dentry);
++ err = finish_no_open(file, dentry);
++ }
++out:
++ AuDbg("%pd%s%s\n", dentry,
++ (*opened & FILE_CREATED) ? " created" : "",
++ (*opened & FILE_OPENED) ? " opened" : "");
++ AuTraceErr(err);
++ return err;
++}
+
-+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_acquire_nest(struct au_pin *p);
-+void au_pin_hdir_release(struct au_pin *p);
+
+/* ---------------------------------------------------------------------- */
+
-+static inline struct au_iinfo *au_ii(struct inode *inode)
++static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent,
++ const unsigned char add_entry, aufs_bindex_t bcpup,
++ aufs_bindex_t btop)
+{
-+ BUG_ON(is_bad_inode(inode));
-+ return &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);
-+}
++ int err;
++ struct dentry *h_parent;
++ struct inode *h_dir;
+
-+/* ---------------------------------------------------------------------- */
++ if (add_entry)
++ IMustLock(d_inode(parent));
++ else
++ di_write_lock_parent(parent);
+
-+/* inode.c */
-+struct inode *au_igrab(struct inode *inode);
-+void au_refresh_iop(struct inode *inode, int force_getattr);
-+int au_refresh_hinode_self(struct inode *inode);
-+int au_refresh_hinode(struct inode *inode, struct dentry *dentry);
-+int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
-+ unsigned int d_type, ino_t *ino);
-+struct inode *au_new_inode(struct dentry *dentry, int must_new);
-+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
-+ struct inode *inode);
-+int au_test_h_perm(struct inode *h_inode, int mask);
-+int au_test_h_perm_sio(struct inode *h_inode, int mask);
++ err = 0;
++ if (!au_h_dptr(parent, bcpup)) {
++ if (btop > bcpup)
++ err = au_cpup_dirs(dentry, bcpup);
++ else if (btop < bcpup)
++ err = au_cpdown_dirs(dentry, bcpup);
++ else
++ BUG();
++ }
++ if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) {
++ h_parent = au_h_dptr(parent, bcpup);
++ h_dir = d_inode(h_parent);
++ vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
++ err = au_lkup_neg(dentry, bcpup, /*wh*/0);
++ /* todo: no unlock here */
++ inode_unlock_shared(h_dir);
+
-+static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex,
-+ ino_t h_ino, unsigned int d_type, ino_t *ino)
-+{
-+#ifdef CONFIG_AUFS_SHWH
-+ return au_ino(sb, bindex, h_ino, d_type, ino);
-+#else
-+ return 0;
-+#endif
-+}
++ AuDbg("bcpup %d\n", bcpup);
++ if (!err) {
++ if (d_really_is_negative(dentry))
++ au_set_h_dptr(dentry, btop, NULL);
++ au_update_dbrange(dentry, /*do_put_zero*/0);
++ }
++ }
+
-+/* i_op.c */
-+enum {
-+ AuIop_SYMLINK,
-+ AuIop_DIR,
-+ AuIop_OTHER,
-+ AuIop_Last
-+};
-+extern struct inode_operations aufs_iop[AuIop_Last],
-+ aufs_iop_nogetattr[AuIop_Last];
++ if (!add_entry)
++ di_write_unlock(parent);
++ if (!err)
++ err = bcpup; /* success */
+
-+/* au_wr_dir flags */
-+#define AuWrDir_ADD_ENTRY 1
-+#define AuWrDir_ISDIR (1 << 1)
-+#define AuWrDir_TMPFILE (1 << 2)
-+#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name)
-+#define au_fset_wrdir(flags, name) \
-+ do { (flags) |= AuWrDir_##name; } while (0)
-+#define au_fclr_wrdir(flags, name) \
-+ do { (flags) &= ~AuWrDir_##name; } while (0)
++ AuTraceErr(err);
++ return err;
++}
+
-+struct au_wr_dir_args {
-+ aufs_bindex_t force_btgt;
-+ unsigned char flags;
-+};
++/*
++ * decide the branch and the parent dir where we will create a new entry.
++ * returns new bindex or an error.
++ * copyup the parent dir if needed.
++ */
+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
-+ struct au_wr_dir_args *args);
-+
-+struct dentry *au_pinned_h_parent(struct au_pin *pin);
-+void au_pin_init(struct au_pin *pin, struct dentry *dentry,
-+ aufs_bindex_t bindex, int lsc_di, int lsc_hi,
-+ unsigned int udba, unsigned char flags);
-+int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
-+ unsigned int udba, unsigned char flags) __must_check;
-+int au_do_pin(struct au_pin *pin) __must_check;
-+void au_unpin(struct au_pin *pin);
-+int au_reval_for_attr(struct dentry *dentry, unsigned int sigen);
++ struct au_wr_dir_args *args)
++{
++ int err;
++ unsigned int flags;
++ 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);
++ struct super_block *sb;
++ struct dentry *parent;
++ struct au_sbinfo *sbinfo;
+
-+#define AuIcpup_DID_CPUP 1
-+#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name)
-+#define au_fset_icpup(flags, name) \
-+ do { (flags) |= AuIcpup_##name; } while (0)
-+#define au_fclr_icpup(flags, name) \
-+ do { (flags) &= ~AuIcpup_##name; } while (0)
++ sb = dentry->d_sb;
++ sbinfo = au_sbi(sb);
++ parent = dget_parent(dentry);
++ btop = au_dbtop(dentry);
++ bcpup = btop;
++ if (args->force_btgt < 0) {
++ if (src_dentry) {
++ 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))
++ au_fset_wbr(flags, DIR);
++ err = AuWbrCreate(sbinfo, dentry, flags);
++ bcpup = err;
++ }
+
-+struct au_icpup_args {
-+ unsigned char flags;
-+ unsigned char pin_flags;
-+ aufs_bindex_t btgt;
-+ unsigned int udba;
-+ struct au_pin pin;
-+ struct path h_path;
-+ struct inode *h_inode;
-+};
++ if (bcpup < 0 || au_test_ro(sb, bcpup, d_inode(dentry))) {
++ if (add_entry)
++ err = AuWbrCopyup(sbinfo, dentry);
++ else {
++ if (!IS_ROOT(dentry)) {
++ di_read_lock_parent(parent, !AuLock_IR);
++ err = AuWbrCopyup(sbinfo, dentry);
++ di_read_unlock(parent, !AuLock_IR);
++ } else
++ err = AuWbrCopyup(sbinfo, dentry);
++ }
++ bcpup = err;
++ if (unlikely(err < 0))
++ goto out;
++ }
++ } else {
++ bcpup = args->force_btgt;
++ AuDebugOn(au_test_ro(sb, bcpup, d_inode(dentry)));
++ }
+
-+int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
-+ struct au_icpup_args *a);
++ AuDbg("btop %d, bcpup %d\n", btop, bcpup);
++ err = bcpup;
++ if (bcpup == btop)
++ goto out; /* success */
+
-+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path,
-+ int locked);
++ /* copyup the new parent into the branch we process */
++ 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, btop, NULL);
++ au_set_dbtop(dentry, bcpup);
++ au_set_dbbot(dentry, bcpup);
++ }
++ AuDebugOn(add_entry
++ && !au_ftest_wrdir(args->flags, TMPFILE)
++ && !au_h_dptr(dentry, bcpup));
++ }
+
-+/* i_op_add.c */
-+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
-+ struct dentry *h_parent, int isdir);
-+int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
-+ dev_t dev);
-+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname);
-+int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
-+ bool want_excl);
-+struct vfsub_aopen_args;
-+int au_aopen_or_create(struct inode *dir, struct dentry *dentry,
-+ struct vfsub_aopen_args *args);
-+int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode);
-+int aufs_link(struct dentry *src_dentry, struct inode *dir,
-+ struct dentry *dentry);
-+int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode);
++out:
++ dput(parent);
++ return err;
++}
+
-+/* i_op_del.c */
-+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup);
-+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
-+ struct dentry *h_parent, int isdir);
-+int aufs_unlink(struct inode *dir, struct dentry *dentry);
-+int aufs_rmdir(struct inode *dir, struct dentry *dentry);
++/* ---------------------------------------------------------------------- */
+
-+/* i_op_ren.c */
-+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt);
-+int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
-+ struct inode *dir, struct dentry *dentry,
-+ unsigned int flags);
++void au_pin_hdir_unlock(struct au_pin *p)
++{
++ if (p->hdir)
++ au_hn_inode_unlock(p->hdir);
++}
+
-+/* iinfo.c */
-+struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
-+void au_hiput(struct au_hinode *hinode);
-+void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
-+ struct dentry *h_wh);
-+unsigned int au_hi_flags(struct inode *inode, int isdir);
++int au_pin_hdir_lock(struct au_pin *p)
++{
++ int err;
+
-+/* hinode flags */
-+#define AuHi_XINO 1
-+#define AuHi_HNOTIFY (1 << 1)
-+#define au_ftest_hi(flags, name) ((flags) & AuHi_##name)
-+#define au_fset_hi(flags, name) \
-+ do { (flags) |= AuHi_##name; } while (0)
-+#define au_fclr_hi(flags, name) \
-+ do { (flags) &= ~AuHi_##name; } while (0)
++ err = 0;
++ if (!p->hdir)
++ goto out;
+
-+#ifndef CONFIG_AUFS_HNOTIFY
-+#undef AuHi_HNOTIFY
-+#define AuHi_HNOTIFY 0
-+#endif
++ /* even if an error happens later, keep this lock */
++ au_hn_inode_lock_nested(p->hdir, p->lsc_hi);
+
-+void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
-+ struct inode *h_inode, unsigned int flags);
++ err = -EBUSY;
++ if (unlikely(p->hdir->hi_inode != d_inode(p->h_parent)))
++ goto out;
+
-+void au_update_iigen(struct inode *inode, int half);
-+void au_update_ibrange(struct inode *inode, int do_put_zero);
++ err = 0;
++ if (p->h_dentry)
++ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode,
++ p->h_parent, p->br);
+
-+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_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink);
++out:
++ return err;
++}
+
-+#ifdef CONFIG_PROC_FS
-+/* plink.c */
-+int au_plink_maint(struct super_block *sb, int flags);
-+struct au_sbinfo;
-+void au_plink_maint_leave(struct au_sbinfo *sbinfo);
-+int au_plink_maint_enter(struct super_block *sb);
-+#ifdef CONFIG_AUFS_DEBUG
-+void au_plink_list(struct super_block *sb);
-+#else
-+AuStubVoid(au_plink_list, struct super_block *sb)
-+#endif
-+int au_plink_test(struct inode *inode);
-+struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex);
-+void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
-+ struct dentry *h_dentry);
-+void au_plink_put(struct super_block *sb, int verbose);
-+void au_plink_clean(struct super_block *sb, int verbose);
-+void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id);
-+#else
-+AuStubInt0(au_plink_maint, struct super_block *sb, int flags);
-+AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo);
-+AuStubInt0(au_plink_maint_enter, struct super_block *sb);
-+AuStubVoid(au_plink_list, struct super_block *sb);
-+AuStubInt0(au_plink_test, struct inode *inode);
-+AuStub(struct dentry *, au_plink_lkup, return NULL,
-+ struct inode *inode, aufs_bindex_t bindex);
-+AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex,
-+ struct dentry *h_dentry);
-+AuStubVoid(au_plink_put, struct super_block *sb, int verbose);
-+AuStubVoid(au_plink_clean, struct super_block *sb, int verbose);
-+AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id);
-+#endif /* CONFIG_PROC_FS */
++int au_pin_hdir_relock(struct au_pin *p)
++{
++ int err, i;
++ struct inode *h_i;
++ struct dentry *h_d[] = {
++ p->h_dentry,
++ p->h_parent
++ };
+
-+#ifdef CONFIG_AUFS_XATTR
-+/* xattr.c */
-+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);
-+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);
-+#endif
++ err = au_pin_hdir_lock(p);
++ if (unlikely(err))
++ goto out;
+
-+#ifdef CONFIG_FS_POSIX_ACL
-+struct posix_acl *aufs_get_acl(struct inode *inode, int type);
-+int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type);
-+#endif
++ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) {
++ if (!h_d[i])
++ continue;
++ if (d_is_positive(h_d[i])) {
++ h_i = d_inode(h_d[i]);
++ err = !h_i->i_nlink;
++ }
++ }
+
-+#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL)
-+enum {
-+ AU_XATTR_SET,
-+ AU_ACL_SET
-+};
++out:
++ return err;
++}
+
-+struct au_sxattr {
-+ int type;
-+ union {
-+ struct {
-+ const char *name;
-+ const void *value;
-+ size_t size;
-+ int flags;
-+ } set;
-+ struct {
-+ struct posix_acl *acl;
-+ int type;
-+ } acl_set;
-+ } u;
-+};
-+ssize_t au_sxattr(struct dentry *dentry, struct inode *inode,
-+ struct au_sxattr *arg);
++static void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *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) {
++ 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);
++ }
++}
+
-+/* lock subclass for iinfo */
-+enum {
-+ AuLsc_II_CHILD, /* child first */
-+ AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */
-+ AuLsc_II_CHILD3, /* copyup dirs */
-+ AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */
-+ AuLsc_II_PARENT2,
-+ AuLsc_II_PARENT3, /* copyup dirs */
-+ AuLsc_II_NEW_CHILD
-+};
++void au_pin_hdir_release(struct au_pin *p)
++{
++ if (p->hdir) {
++ au_pin_hdir_set_owner(p, p->task);
++ rwsem_release(&p->hdir->hi_inode->i_rwsem.dep_map, 1, _RET_IP_);
++ }
++}
+
-+/*
-+ * ii_read_lock_child, ii_write_lock_child,
-+ * ii_read_lock_child2, ii_write_lock_child2,
-+ * ii_read_lock_child3, ii_write_lock_child3,
-+ * ii_read_lock_parent, ii_write_lock_parent,
-+ * ii_read_lock_parent2, ii_write_lock_parent2,
-+ * ii_read_lock_parent3, ii_write_lock_parent3,
-+ * ii_read_lock_new_child, ii_write_lock_new_child,
-+ */
-+#define AuReadLockFunc(name, lsc) \
-+static inline void ii_read_lock_##name(struct inode *i) \
-+{ \
-+ au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \
++struct dentry *au_pinned_h_parent(struct au_pin *pin)
++{
++ if (pin && pin->parent)
++ return au_h_dptr(pin->parent, pin->bindex);
++ return NULL;
+}
+
-+#define AuWriteLockFunc(name, lsc) \
-+static inline void ii_write_lock_##name(struct inode *i) \
-+{ \
-+ au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \
++void au_unpin(struct au_pin *p)
++{
++ if (p->hdir)
++ au_pin_hdir_unlock(p);
++ if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE))
++ vfsub_mnt_drop_write(p->h_mnt);
++ if (!p->hdir)
++ return;
++
++ if (!au_ftest_pin(p->flags, DI_LOCKED))
++ di_read_unlock(p->parent, AuLock_IR);
++ iput(p->hdir->hi_inode);
++ dput(p->parent);
++ p->parent = NULL;
++ p->hdir = NULL;
++ p->h_mnt = NULL;
++ /* do not clear p->task */
+}
+
-+#define AuRWLockFuncs(name, lsc) \
-+ AuReadLockFunc(name, lsc) \
-+ AuWriteLockFunc(name, lsc)
++int au_do_pin(struct au_pin *p)
++{
++ int err;
++ struct super_block *sb;
++ struct inode *h_dir;
+
-+AuRWLockFuncs(child, CHILD);
-+AuRWLockFuncs(child2, CHILD2);
-+AuRWLockFuncs(child3, CHILD3);
-+AuRWLockFuncs(parent, PARENT);
-+AuRWLockFuncs(parent2, PARENT2);
-+AuRWLockFuncs(parent3, PARENT3);
-+AuRWLockFuncs(new_child, NEW_CHILD);
++ err = 0;
++ sb = p->dentry->d_sb;
++ p->br = au_sbr(sb, p->bindex);
++ if (IS_ROOT(p->dentry)) {
++ if (au_ftest_pin(p->flags, MNT_WRITE)) {
++ p->h_mnt = au_br_mnt(p->br);
++ err = vfsub_mnt_want_write(p->h_mnt);
++ if (unlikely(err)) {
++ au_fclr_pin(p->flags, MNT_WRITE);
++ goto out_err;
++ }
++ }
++ goto out;
++ }
+
-+#undef AuReadLockFunc
-+#undef AuWriteLockFunc
-+#undef AuRWLockFuncs
++ p->h_dentry = NULL;
++ if (p->bindex <= au_dbbot(p->dentry))
++ p->h_dentry = au_h_dptr(p->dentry, p->bindex);
+
-+/*
-+ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock
-+ */
-+AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem);
++ p->parent = dget_parent(p->dentry);
++ if (!au_ftest_pin(p->flags, DI_LOCKED))
++ di_read_lock(p->parent, AuLock_IR, p->lsc_di);
+
-+#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem)
-+#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem)
-+#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem)
++ h_dir = NULL;
++ p->h_parent = au_h_dptr(p->parent, p->bindex);
++ p->hdir = au_hi(d_inode(p->parent), p->bindex);
++ if (p->hdir)
++ h_dir = p->hdir->hi_inode;
+
-+/* ---------------------------------------------------------------------- */
++ /*
++ * udba case, or
++ * if DI_LOCKED is not set, then p->parent may be different
++ * and h_parent can be NULL.
++ */
++ if (unlikely(!p->hdir || !h_dir || !p->h_parent)) {
++ err = -EBUSY;
++ if (!au_ftest_pin(p->flags, DI_LOCKED))
++ di_read_unlock(p->parent, AuLock_IR);
++ dput(p->parent);
++ p->parent = NULL;
++ goto out_err;
++ }
+
-+static inline void au_icntnr_init(struct au_icntnr *c)
-+{
-+#ifdef CONFIG_AUFS_DEBUG
-+ c->vfs_inode.i_mode = 0;
-+#endif
++ if (au_ftest_pin(p->flags, MNT_WRITE)) {
++ p->h_mnt = au_br_mnt(p->br);
++ err = vfsub_mnt_want_write(p->h_mnt);
++ if (unlikely(err)) {
++ au_fclr_pin(p->flags, MNT_WRITE);
++ if (!au_ftest_pin(p->flags, DI_LOCKED))
++ di_read_unlock(p->parent, AuLock_IR);
++ dput(p->parent);
++ p->parent = NULL;
++ goto out_err;
++ }
++ }
++
++ au_igrab(h_dir);
++ err = au_pin_hdir_lock(p);
++ if (!err)
++ goto out; /* success */
++
++ au_unpin(p);
++
++out_err:
++ pr_err("err %d\n", err);
++ err = au_busy_or_stale();
++out:
++ return err;
+}
+
-+static inline unsigned int au_iigen(struct inode *inode, unsigned int *igflags)
++void au_pin_init(struct au_pin *p, struct dentry *dentry,
++ aufs_bindex_t bindex, int lsc_di, int lsc_hi,
++ unsigned int udba, unsigned char flags)
+{
-+ unsigned int gen;
-+ struct au_iinfo *iinfo;
-+ struct au_iigen *iigen;
++ p->dentry = dentry;
++ p->udba = udba;
++ p->lsc_di = lsc_di;
++ p->lsc_hi = lsc_hi;
++ p->flags = flags;
++ p->bindex = bindex;
+
-+ iinfo = au_ii(inode);
-+ iigen = &iinfo->ii_generation;
-+ spin_lock(&iigen->ig_spin);
-+ if (igflags)
-+ *igflags = iigen->ig_flags;
-+ gen = iigen->ig_generation;
-+ spin_unlock(&iigen->ig_spin);
++ p->parent = NULL;
++ p->hdir = NULL;
++ p->h_mnt = NULL;
+
-+ return gen;
++ p->h_dentry = NULL;
++ p->h_parent = NULL;
++ p->br = NULL;
++ p->task = current;
+}
+
-+/* tiny test for inode number */
-+/* tmpfs generation is too rough */
-+static inline int au_test_higen(struct inode *inode, struct inode *h_inode)
++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
++ unsigned int udba, unsigned char flags)
+{
-+ struct au_iinfo *iinfo;
-+
-+ iinfo = au_ii(inode);
-+ AuRwMustAnyLock(&iinfo->ii_rwsem);
-+ return !(iinfo->ii_hsb1 == h_inode->i_sb
-+ && iinfo->ii_higen == h_inode->i_generation);
++ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2,
++ udba, flags);
++ return au_do_pin(pin);
+}
+
-+static inline void au_iigen_dec(struct inode *inode)
++/* ---------------------------------------------------------------------- */
++
++/*
++ * ->setattr() and ->getattr() are called in various cases.
++ * chmod, stat: dentry is revalidated.
++ * fchmod, fstat: file and dentry are not revalidated, additionally they may be
++ * unhashed.
++ * for ->setattr(), ia->ia_file is passed from ftruncate only.
++ */
++/* todo: consolidate with do_refresh() and simple_reval_dpath() */
++int au_reval_for_attr(struct dentry *dentry, unsigned int sigen)
+{
-+ struct au_iinfo *iinfo;
-+ struct au_iigen *iigen;
++ int err;
++ struct dentry *parent;
+
-+ iinfo = au_ii(inode);
-+ iigen = &iinfo->ii_generation;
-+ spin_lock(&iigen->ig_spin);
-+ iigen->ig_generation--;
-+ spin_unlock(&iigen->ig_spin);
++ err = 0;
++ if (au_digen_test(dentry, sigen)) {
++ parent = dget_parent(dentry);
++ di_read_lock_parent(parent, AuLock_IR);
++ err = au_refresh_dentry(dentry, parent);
++ di_read_unlock(parent, AuLock_IR);
++ dput(parent);
++ }
++
++ AuTraceErr(err);
++ return err;
+}
+
-+static inline int au_iigen_test(struct inode *inode, unsigned int sigen)
++int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
++ struct au_icpup_args *a)
+{
+ int err;
++ loff_t sz;
++ aufs_bindex_t btop, ibtop;
++ struct dentry *hi_wh, *parent;
++ struct inode *inode;
++ struct au_wr_dir_args wr_dir_args = {
++ .force_btgt = -1,
++ .flags = 0
++ };
++
++ if (d_is_dir(dentry))
++ au_fset_wrdir(wr_dir_args.flags, ISDIR);
++ /* plink or hi_wh() case */
++ btop = au_dbtop(dentry);
++ inode = d_inode(dentry);
++ 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 != btop)
++ au_fset_icpup(a->flags, DID_CPUP);
++
++ err = 0;
++ a->pin_flags = AuPin_MNT_WRITE;
++ parent = NULL;
++ if (!IS_ROOT(dentry)) {
++ au_fset_pin(a->pin_flags, DI_LOCKED);
++ parent = dget_parent(dentry);
++ di_write_lock_parent(parent);
++ }
++
++ err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags);
++ if (unlikely(err))
++ goto out_parent;
++
++ 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)) {
++ vfsub_inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD);
++ if (ia->ia_size < i_size_read(a->h_inode))
++ sz = ia->ia_size;
++ inode_unlock_shared(a->h_inode);
++ }
++
++ hi_wh = NULL;
++ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) {
++ hi_wh = au_hi_wh(inode, a->btgt);
++ if (!hi_wh) {
++ struct au_cp_generic cpg = {
++ .dentry = dentry,
++ .bdst = a->btgt,
++ .bsrc = -1,
++ .len = sz,
++ .pin = &a->pin
++ };
++ err = au_sio_cpup_wh(&cpg, /*file*/NULL);
++ if (unlikely(err))
++ goto out_unlock;
++ hi_wh = au_hi_wh(inode, a->btgt);
++ /* todo: revalidate hi_wh? */
++ }
++ }
++
++ if (parent) {
++ au_pin_set_parent_lflag(&a->pin, /*lflag*/0);
++ di_downgrade_lock(parent, AuLock_IR);
++ dput(parent);
++ parent = NULL;
++ }
++ if (!au_ftest_icpup(a->flags, DID_CPUP))
++ goto out; /* success */
+
-+ err = 0;
-+ if (unlikely(inode && au_iigen(inode, NULL) != sigen))
-+ err = -EIO;
++ if (!d_unhashed(dentry)) {
++ struct au_cp_generic cpg = {
++ .dentry = dentry,
++ .bdst = a->btgt,
++ .bsrc = btop,
++ .len = sz,
++ .pin = &a->pin,
++ .flags = AuCpup_DTIME | AuCpup_HOPEN
++ };
++ err = au_sio_cpup_simple(&cpg);
++ if (!err)
++ a->h_path.dentry = au_h_dptr(dentry, a->btgt);
++ } else if (!hi_wh)
++ a->h_path.dentry = au_h_dptr(dentry, a->btgt);
++ else
++ a->h_path.dentry = hi_wh; /* do not dget here */
+
++out_unlock:
++ a->h_inode = d_inode(a->h_path.dentry);
++ if (!err)
++ goto out; /* success */
++ au_unpin(&a->pin);
++out_parent:
++ if (parent) {
++ di_write_unlock(parent);
++ dput(parent);
++ }
++out:
++ if (!err)
++ inode_lock_nested(a->h_inode, AuLsc_I_CHILD);
+ return err;
+}
+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline struct au_hinode *au_hinode(struct au_iinfo *iinfo,
-+ aufs_bindex_t bindex)
++static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
+{
-+ return iinfo->ii_hinode + bindex;
-+}
++ int err;
++ struct inode *inode, *delegated;
++ struct super_block *sb;
++ struct file *file;
++ struct au_icpup_args *a;
+
-+static inline int au_is_bad_inode(struct inode *inode)
-+{
-+ return !!(is_bad_inode(inode) || !au_hinode(au_ii(inode), 0));
-+}
++ inode = d_inode(dentry);
++ IMustLock(inode);
+
-+static inline aufs_bindex_t au_ii_br_id(struct inode *inode,
-+ aufs_bindex_t bindex)
-+{
-+ IiMustAnyLock(inode);
-+ return au_hinode(au_ii(inode), bindex)->hi_id;
-+}
++ err = setattr_prepare(dentry, ia);
++ if (unlikely(err))
++ goto out;
+
-+static inline aufs_bindex_t au_ibtop(struct inode *inode)
-+{
-+ IiMustAnyLock(inode);
-+ return au_ii(inode)->ii_btop;
-+}
++ err = -ENOMEM;
++ a = kzalloc(sizeof(*a), GFP_NOFS);
++ if (unlikely(!a))
++ goto out;
+
-+static inline aufs_bindex_t au_ibbot(struct inode *inode)
-+{
-+ IiMustAnyLock(inode);
-+ return au_ii(inode)->ii_bbot;
-+}
++ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
++ ia->ia_valid &= ~ATTR_MODE;
+
-+static inline struct au_vdir *au_ivdir(struct inode *inode)
-+{
-+ IiMustAnyLock(inode);
-+ return au_ii(inode)->ii_vdir;
-+}
++ file = NULL;
++ sb = dentry->d_sb;
++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
++ if (unlikely(err))
++ goto out_kfree;
+
-+static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex)
-+{
-+ IiMustAnyLock(inode);
-+ return au_hinode(au_ii(inode), bindex)->hi_whdentry;
-+}
++ if (ia->ia_valid & ATTR_FILE) {
++ /* 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,
++ /*fi_lsc*/0);
++ if (unlikely(err))
++ goto out_si;
++ ia->ia_file = au_hf_top(file);
++ a->udba = AuOpt_UDBA_NONE;
++ } else {
++ /* fchmod() doesn't pass ia_file */
++ a->udba = au_opt_udba(sb);
++ di_write_lock_child(dentry);
++ /* no d_unlinked(), to set UDBA_NONE for root */
++ if (d_unhashed(dentry))
++ a->udba = AuOpt_UDBA_NONE;
++ if (a->udba != AuOpt_UDBA_NONE) {
++ AuDebugOn(IS_ROOT(dentry));
++ err = au_reval_for_attr(dentry, au_sigen(sb));
++ if (unlikely(err))
++ goto out_dentry;
++ }
++ }
+
-+static inline void au_set_ibtop(struct inode *inode, aufs_bindex_t bindex)
-+{
-+ IiMustWriteLock(inode);
-+ au_ii(inode)->ii_btop = bindex;
-+}
++ err = au_pin_and_icpup(dentry, ia, a);
++ if (unlikely(err < 0))
++ goto out_dentry;
++ if (au_ftest_icpup(a->flags, DID_CPUP)) {
++ ia->ia_file = NULL;
++ ia->ia_valid &= ~ATTR_FILE;
++ }
+
-+static inline void au_set_ibbot(struct inode *inode, aufs_bindex_t bindex)
-+{
-+ IiMustWriteLock(inode);
-+ au_ii(inode)->ii_bbot = bindex;
-+}
++ a->h_path.mnt = au_sbr_mnt(sb, a->btgt);
++ if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME))
++ == (ATTR_MODE | ATTR_CTIME)) {
++ err = security_path_chmod(&a->h_path, ia->ia_mode);
++ if (unlikely(err))
++ goto out_unlock;
++ } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID))
++ && (ia->ia_valid & ATTR_CTIME)) {
++ err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid);
++ if (unlikely(err))
++ goto out_unlock;
++ }
+
-+static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir)
-+{
-+ IiMustWriteLock(inode);
-+ au_ii(inode)->ii_vdir = vdir;
-+}
++ if (ia->ia_valid & ATTR_SIZE) {
++ struct file *f;
+
-+static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex)
-+{
-+ IiMustAnyLock(inode);
-+ return au_hinode(au_ii(inode), bindex);
-+}
++ if (ia->ia_size < i_size_read(inode))
++ /* unmap only */
++ truncate_setsize(inode, ia->ia_size);
+
-+/* ---------------------------------------------------------------------- */
++ f = NULL;
++ if (ia->ia_valid & ATTR_FILE)
++ f = ia->ia_file;
++ inode_unlock(a->h_inode);
++ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f);
++ inode_lock_nested(a->h_inode, AuLsc_I_CHILD);
++ } else {
++ delegated = NULL;
++ while (1) {
++ err = vfsub_notify_change(&a->h_path, ia, &delegated);
++ if (delegated) {
++ err = break_deleg_wait(&delegated);
++ if (!err)
++ continue;
++ }
++ 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);
+
-+static inline struct dentry *au_pinned_parent(struct au_pin *pin)
-+{
-+ if (pin)
-+ return pin->parent;
-+ return NULL;
++out_unlock:
++ inode_unlock(a->h_inode);
++ au_unpin(&a->pin);
++ if (unlikely(err))
++ au_update_dbtop(dentry);
++out_dentry:
++ di_write_unlock(dentry);
++ if (file) {
++ fi_write_unlock(file);
++ ia->ia_file = file;
++ ia->ia_valid |= ATTR_FILE;
++ }
++out_si:
++ si_read_unlock(sb);
++out_kfree:
++ kfree(a);
++out:
++ AuTraceErr(err);
++ return err;
+}
+
-+static inline struct inode *au_pinned_h_dir(struct au_pin *pin)
++#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL)
++static int au_h_path_to_set_attr(struct dentry *dentry,
++ struct au_icpup_args *a, struct path *h_path)
+{
-+ if (pin && pin->hdir)
-+ return pin->hdir->hi_inode;
-+ return NULL;
-+}
++ int err;
++ struct super_block *sb;
+
-+static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin)
-+{
-+ if (pin)
-+ return pin->hdir;
-+ return NULL;
-+}
++ sb = dentry->d_sb;
++ a->udba = au_opt_udba(sb);
++ /* no d_unlinked(), to set UDBA_NONE for root */
++ if (d_unhashed(dentry))
++ a->udba = AuOpt_UDBA_NONE;
++ if (a->udba != AuOpt_UDBA_NONE) {
++ AuDebugOn(IS_ROOT(dentry));
++ err = au_reval_for_attr(dentry, au_sigen(sb));
++ if (unlikely(err))
++ goto out;
++ }
++ err = au_pin_and_icpup(dentry, /*ia*/NULL, a);
++ if (unlikely(err < 0))
++ goto out;
+
-+static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry)
-+{
-+ if (pin)
-+ pin->dentry = dentry;
-+}
++ h_path->dentry = a->h_path.dentry;
++ h_path->mnt = au_sbr_mnt(sb, a->btgt);
+
-+static inline void au_pin_set_parent_lflag(struct au_pin *pin,
-+ unsigned char lflag)
-+{
-+ if (pin) {
-+ if (lflag)
-+ au_fset_pin(pin->flags, DI_LOCKED);
-+ else
-+ au_fclr_pin(pin->flags, DI_LOCKED);
-+ }
++out:
++ return err;
+}
+
-+#if 0 /* reserved */
-+static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent)
++ssize_t au_sxattr(struct dentry *dentry, struct inode *inode,
++ struct au_sxattr *arg)
+{
-+ if (pin) {
-+ dput(pin->parent);
-+ pin->parent = dget(parent);
-+ }
-+}
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
++ int err;
++ struct path h_path;
++ struct super_block *sb;
++ struct au_icpup_args *a;
++ struct inode *h_inode;
+
-+struct au_branch;
-+#ifdef CONFIG_AUFS_HNOTIFY
-+struct au_hnotify_op {
-+ void (*ctl)(struct au_hinode *hinode, int do_set);
-+ int (*alloc)(struct au_hinode *hinode);
++ IMustLock(inode);
+
-+ /*
-+ * if it returns true, the the caller should free hinode->hi_notify,
-+ * otherwise ->free() frees it.
-+ */
-+ int (*free)(struct au_hinode *hinode,
-+ struct au_hnotify *hn) __must_check;
++ err = -ENOMEM;
++ a = kzalloc(sizeof(*a), GFP_NOFS);
++ if (unlikely(!a))
++ goto out;
+
-+ void (*fin)(void);
-+ int (*init)(void);
++ sb = dentry->d_sb;
++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
++ if (unlikely(err))
++ goto out_kfree;
+
-+ int (*reset_br)(unsigned int udba, struct au_branch *br, int perm);
-+ void (*fin_br)(struct au_branch *br);
-+ int (*init_br)(struct au_branch *br, int perm);
-+};
++ h_path.dentry = NULL; /* silence gcc */
++ di_write_lock_child(dentry);
++ err = au_h_path_to_set_attr(dentry, a, &h_path);
++ if (unlikely(err))
++ goto out_di;
+
-+/* hnotify.c */
-+int au_hn_alloc(struct au_hinode *hinode, struct inode *inode);
-+void au_hn_free(struct au_hinode *hinode);
-+void au_hn_ctl(struct au_hinode *hinode, int do_set);
-+void au_hn_reset(struct inode *inode, unsigned int flags);
-+int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask,
-+ struct qstr *h_child_qstr, struct inode *h_child_inode);
-+int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm);
-+int au_hnotify_init_br(struct au_branch *br, int perm);
-+void au_hnotify_fin_br(struct au_branch *br);
-+int __init au_hnotify_init(void);
-+void au_hnotify_fin(void);
++ 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_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);
++ break;
++ }
++ if (!err)
++ au_cpup_attr_timesizes(inode);
+
-+/* hfsnotify.c */
-+extern const struct au_hnotify_op au_hnotify_op;
++ au_unpin(&a->pin);
++ if (unlikely(err))
++ au_update_dbtop(dentry);
+
-+static inline
-+void au_hn_init(struct au_hinode *hinode)
-+{
-+ hinode->hi_notify = NULL;
++out_di:
++ di_write_unlock(dentry);
++ si_read_unlock(sb);
++out_kfree:
++ kfree(a);
++out:
++ AuTraceErr(err);
++ return err;
+}
++#endif
+
-+static inline struct au_hnotify *au_hn(struct au_hinode *hinode)
++static void au_refresh_iattr(struct inode *inode, struct kstat *st,
++ unsigned int nlink)
+{
-+ return hinode->hi_notify;
-+}
++ unsigned int n;
+
-+#else
-+AuStub(int, au_hn_alloc, return -EOPNOTSUPP,
-+ struct au_hinode *hinode __maybe_unused,
-+ struct inode *inode __maybe_unused)
-+AuStub(struct au_hnotify *, au_hn, return NULL, struct au_hinode *hinode)
-+AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused)
-+AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused,
-+ int do_set __maybe_unused)
-+AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused,
-+ unsigned int flags __maybe_unused)
-+AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused,
-+ struct au_branch *br __maybe_unused,
-+ int perm __maybe_unused)
-+AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused,
-+ int perm __maybe_unused)
-+AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused)
-+AuStubInt0(__init au_hnotify_init, void)
-+AuStubVoid(au_hnotify_fin, void)
-+AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused)
-+#endif /* CONFIG_AUFS_HNOTIFY */
++ inode->i_mode = st->mode;
++ /* don't i_[ug]id_write() here */
++ inode->i_uid = st->uid;
++ inode->i_gid = st->gid;
++ inode->i_atime = st->atime;
++ inode->i_mtime = st->mtime;
++ inode->i_ctime = st->ctime;
+
-+static inline void au_hn_suspend(struct au_hinode *hdir)
-+{
-+ au_hn_ctl(hdir, /*do_set*/0);
-+}
++ au_cpup_attr_nlink(inode, /*force*/0);
++ if (S_ISDIR(inode->i_mode)) {
++ n = inode->i_nlink;
++ n -= nlink;
++ n += st->nlink;
++ smp_mb(); /* for i_nlink */
++ /* 0 can happen */
++ set_nlink(inode, n);
++ }
+
-+static inline void au_hn_resume(struct au_hinode *hdir)
-+{
-+ au_hn_ctl(hdir, /*do_set*/1);
++ spin_lock(&inode->i_lock);
++ inode->i_blocks = st->blocks;
++ i_size_write(inode, st->size);
++ spin_unlock(&inode->i_lock);
+}
+
-+static inline void au_hn_inode_lock(struct au_hinode *hdir)
++/*
++ * 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 locked)
+{
-+ inode_lock(hdir->hi_inode);
-+ au_hn_suspend(hdir);
-+}
++ int err;
++ unsigned int mnt_flags, sigen;
++ unsigned char udba_none;
++ aufs_bindex_t bindex;
++ struct super_block *sb, *h_sb;
++ struct inode *inode;
+
-+static inline void au_hn_inode_lock_nested(struct au_hinode *hdir,
-+ unsigned int sc __maybe_unused)
-+{
-+ inode_lock_nested(hdir->hi_inode, sc);
-+ au_hn_suspend(hdir);
-+}
++ h_path->mnt = NULL;
++ h_path->dentry = NULL;
+
-+#if 0 /* unused */
-+#include "vfsub.h"
-+static inline void au_hn_inode_lock_shared_nested(struct au_hinode *hdir,
-+ unsigned int sc)
-+{
-+ vfsub_inode_lock_shared_nested(hdir->hi_inode, sc);
-+ au_hn_suspend(hdir);
-+}
-+#endif
++ err = 0;
++ sb = dentry->d_sb;
++ mnt_flags = au_mntflags(sb);
++ udba_none = !!au_opt_test(mnt_flags, UDBA_NONE);
+
-+static inline void au_hn_inode_unlock(struct au_hinode *hdir)
-+{
-+ au_hn_resume(hdir);
-+ inode_unlock(hdir->hi_inode);
-+}
++ if (unlikely(locked))
++ goto body; /* skip locking dinfo */
+
-+#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 2018-04-15 08:49:13.401150731 +0200
-@@ -0,0 +1,219 @@
-+/*
-+ * 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
-+ * 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 <http://www.gnu.org/licenses/>.
-+ */
++ /* support fstat(2) */
++ if (!d_unlinked(dentry) && !udba_none) {
++ sigen = au_sigen(sb);
++ err = au_digen_test(dentry, sigen);
++ if (!err) {
++ di_read_lock_child(dentry, AuLock_IR);
++ err = au_dbrange_test(dentry);
++ if (unlikely(err)) {
++ di_read_unlock(dentry, AuLock_IR);
++ goto out;
++ }
++ } else {
++ AuDebugOn(IS_ROOT(dentry));
++ di_write_lock_child(dentry);
++ err = au_dbrange_test(dentry);
++ if (!err)
++ err = au_reval_for_attr(dentry, sigen);
++ if (!err)
++ di_downgrade_lock(dentry, AuLock_IR);
++ else {
++ di_write_unlock(dentry);
++ goto out;
++ }
++ }
++ } else
++ di_read_lock_child(dentry, AuLock_IR);
+
-+/*
-+ * ioctl
-+ * plink-management and readdir in userspace.
-+ * assist the pathconf(3) wrapper library.
-+ * move-down
-+ * File-based Hierarchical Storage Management.
-+ */
++body:
++ inode = d_inode(dentry);
++ bindex = au_ibtop(inode);
++ h_path->mnt = au_sbr_mnt(sb, bindex);
++ h_sb = h_path->mnt->mnt_sb;
++ if (!force
++ && !au_test_fs_bad_iattr(h_sb)
++ && udba_none)
++ goto out; /* success */
+
-+#include <linux/compat.h>
-+#include <linux/file.h>
-+#include "aufs.h"
++ 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);
++ if (IS_ERR(h_path->dentry))
++ /* pretending success */
++ h_path->dentry = NULL;
++ else
++ dput(h_path->dentry);
++ }
+
-+static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg)
++out:
++ return err;
++}
++
++static int aufs_getattr(struct vfsmount *mnt __maybe_unused,
++ struct dentry *dentry, struct kstat *st)
+{
-+ int err, fd;
-+ aufs_bindex_t wbi, bindex, bbot;
-+ struct file *h_file;
++ int err;
++ unsigned char positive;
++ struct path h_path;
++ struct inode *inode;
+ struct super_block *sb;
-+ struct dentry *root;
-+ struct au_branch *br;
-+ struct aufs_wbr_fd wbrfd = {
-+ .oflags = au_dir_roflags,
-+ .brid = -1
-+ };
-+ const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY
-+ | O_NOATIME | O_CLOEXEC;
-+
-+ AuDebugOn(wbrfd.oflags & ~valid);
+
-+ if (arg) {
-+ err = copy_from_user(&wbrfd, arg, sizeof(wbrfd));
-+ if (unlikely(err)) {
-+ err = -EFAULT;
-+ goto out;
-+ }
++ 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, /*locked*/0);
++ if (unlikely(err))
++ goto out_si;
++ if (unlikely(!h_path.dentry))
++ /* illegally overlapped or something */
++ goto out_fill; /* pretending success */
+
-+ err = -EINVAL;
-+ AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid);
-+ wbrfd.oflags |= au_dir_roflags;
-+ AuDbg("0%o\n", wbrfd.oflags);
-+ if (unlikely(wbrfd.oflags & ~valid))
-+ goto out;
++ positive = d_is_positive(h_path.dentry);
++ if (positive)
++ err = vfs_getattr(&h_path, st);
++ if (!err) {
++ if (positive)
++ au_refresh_iattr(inode, st,
++ d_inode(h_path.dentry)->i_nlink);
++ goto out_fill; /* success */
+ }
++ AuTraceErr(err);
++ goto out_di;
+
-+ fd = get_unused_fd_flags(0);
-+ err = fd;
-+ if (unlikely(fd < 0))
++out_fill:
++ generic_fillattr(inode, st);
++out_di:
++ di_read_unlock(dentry, AuLock_IR);
++out_si:
++ si_read_unlock(sb);
++out:
++ AuTraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static const char *aufs_get_link(struct dentry *dentry, struct inode *inode,
++ struct delayed_call *done)
++{
++ const char *ret;
++ struct dentry *h_dentry;
++ struct inode *h_inode;
++ int err;
++ aufs_bindex_t bindex;
++
++ ret = NULL; /* suppress a warning */
++ err = -ECHILD;
++ if (!dentry)
+ goto out;
+
-+ h_file = ERR_PTR(-EINVAL);
-+ wbi = 0;
-+ br = NULL;
-+ sb = path->dentry->d_sb;
-+ root = sb->s_root;
-+ aufs_read_lock(root, AuLock_IR);
-+ bbot = au_sbbot(sb);
-+ if (wbrfd.brid >= 0) {
-+ wbi = au_br_index(sb, wbrfd.brid);
-+ if (unlikely(wbi < 0 || wbi > bbot))
-+ goto out_unlock;
-+ }
++ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN);
++ if (unlikely(err))
++ goto out;
+
-+ h_file = ERR_PTR(-ENOENT);
-+ br = au_sbr(sb, wbi);
-+ if (!au_br_writable(br->br_perm)) {
-+ if (arg)
-+ goto out_unlock;
++ err = au_d_hashed_positive(dentry);
++ if (unlikely(err))
++ goto out_unlock;
+
-+ bindex = wbi + 1;
-+ wbi = -1;
-+ for (; bindex <= bbot; bindex++) {
-+ br = au_sbr(sb, bindex);
-+ if (au_br_writable(br->br_perm)) {
-+ wbi = bindex;
-+ br = au_sbr(sb, wbi);
-+ break;
-+ }
++ err = -EINVAL;
++ inode = d_inode(dentry);
++ bindex = au_ibtop(inode);
++ h_inode = au_h_iptr(inode, bindex);
++ if (unlikely(!h_inode->i_op->get_link))
++ goto out_unlock;
++
++ err = -EBUSY;
++ h_dentry = NULL;
++ if (au_dbtop(dentry) <= bindex) {
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (h_dentry)
++ dget(h_dentry);
++ }
++ if (!h_dentry) {
++ h_dentry = d_find_any_alias(h_inode);
++ if (IS_ERR(h_dentry)) {
++ err = PTR_ERR(h_dentry);
++ goto out_unlock;
+ }
+ }
-+ AuDbg("wbi %d\n", wbi);
-+ if (wbi >= 0)
-+ h_file = au_h_open(root, wbi, wbrfd.oflags, NULL,
-+ /*force_wr*/0);
-+
-+out_unlock:
-+ aufs_read_unlock(root, AuLock_IR);
-+ err = PTR_ERR(h_file);
-+ if (IS_ERR(h_file))
-+ goto out_fd;
++ if (unlikely(!h_dentry))
++ goto out_unlock;
+
-+ au_br_put(br); /* cf. au_h_open() */
-+ fd_install(fd, h_file);
-+ err = fd;
-+ goto out; /* success */
++ err = 0;
++ AuDbg("%pf\n", h_inode->i_op->get_link);
++ AuDbgDentry(h_dentry);
++ ret = vfs_get_link(h_dentry, done);
++ dput(h_dentry);
++ if (IS_ERR(ret))
++ err = PTR_ERR(ret);
+
-+out_fd:
-+ put_unused_fd(fd);
++out_unlock:
++ aufs_read_unlock(dentry, AuLock_IR);
+out:
-+ AuTraceErr(err);
-+ return err;
++ if (unlikely(err))
++ ret = ERR_PTR(err);
++ AuTraceErrPtr(ret);
++ return ret;
+}
+
+/* ---------------------------------------------------------------------- */
+
-+long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg)
++static int au_is_special(struct inode *inode)
+{
-+ long err;
-+ struct dentry *dentry;
++ return (inode->i_mode & (S_IFBLK | S_IFCHR | S_IFIFO | S_IFSOCK));
++}
+
-+ switch (cmd) {
-+ case AUFS_CTL_RDU:
-+ case AUFS_CTL_RDU_INO:
-+ err = au_rdu_ioctl(file, cmd, arg);
-+ break;
++static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags)
++{
++ int err;
++ aufs_bindex_t bindex;
++ struct super_block *sb;
++ struct inode *h_inode;
++ struct vfsmount *h_mnt;
+
-+ case AUFS_CTL_WBR_FD:
-+ err = au_wbr_fd(&file->f_path, (void __user *)arg);
-+ break;
++ sb = inode->i_sb;
++ WARN_ONCE((flags & S_ATIME) && !IS_NOATIME(inode),
++ "unexpected s_flags 0x%lx", sb->s_flags);
+
-+ case AUFS_CTL_IBUSY:
-+ err = au_ibusy_ioctl(file, arg);
-+ break;
++ /* mmap_sem might be acquired already, cf. aufs_mmap() */
++ lockdep_off();
++ si_read_lock(sb, AuLock_FLUSH);
++ ii_write_lock_child(inode);
++ lockdep_on();
+
-+ case AUFS_CTL_BRINFO:
-+ err = au_brinfo_ioctl(file, arg);
-+ break;
++ 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);
++ }
+
-+ case AUFS_CTL_FHSM_FD:
-+ dentry = file->f_path.dentry;
-+ if (IS_ROOT(dentry))
-+ err = au_fhsm_fd(dentry->d_sb, arg);
-+ else
-+ err = -ENOTTY;
-+ break;
++ lockdep_off();
++ if (!err)
++ au_cpup_attr_timesizes(inode);
++ ii_write_unlock(inode);
++ si_read_unlock(sb);
++ lockdep_on();
+
-+ default:
-+ /* do not call the lower */
-+ AuDbg("0x%x\n", cmd);
-+ err = -ENOTTY;
-+ }
++ if (!err && (flags & S_VERSION))
++ inode_inc_iversion(inode);
+
-+ AuTraceErr(err);
+ return err;
+}
+
-+long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg)
-+{
-+ long err;
++/* ---------------------------------------------------------------------- */
+
-+ switch (cmd) {
-+ case AUFS_CTL_MVDOWN:
-+ err = au_mvdown(file->f_path.dentry, (void __user *)arg);
-+ break;
++/* no getattr version will be set by module.c:aufs_init() */
++struct inode_operations aufs_iop_nogetattr[AuIop_Last],
++ aufs_iop[] = {
++ [AuIop_SYMLINK] = {
++ .permission = aufs_permission,
++#ifdef CONFIG_FS_POSIX_ACL
++ .get_acl = aufs_get_acl,
++ .set_acl = aufs_set_acl, /* unsupport for symlink? */
++#endif
+
-+ case AUFS_CTL_WBR_FD:
-+ err = au_wbr_fd(&file->f_path, (void __user *)arg);
-+ break;
++ .setattr = aufs_setattr,
++ .getattr = aufs_getattr,
+
-+ default:
-+ /* do not call the lower */
-+ AuDbg("0x%x\n", cmd);
-+ err = -ENOTTY;
-+ }
++#ifdef CONFIG_AUFS_XATTR
++ .listxattr = aufs_listxattr,
++#endif
+
-+ AuTraceErr(err);
-+ return err;
-+}
++ .readlink = generic_readlink,
++ .get_link = aufs_get_link,
+
-+#ifdef CONFIG_COMPAT
-+long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd,
-+ unsigned long arg)
-+{
-+ long err;
++ /* .update_time = aufs_update_time */
++ },
++ [AuIop_DIR] = {
++ .create = aufs_create,
++ .lookup = aufs_lookup,
++ .link = aufs_link,
++ .unlink = aufs_unlink,
++ .symlink = aufs_symlink,
++ .mkdir = aufs_mkdir,
++ .rmdir = aufs_rmdir,
++ .mknod = aufs_mknod,
++ .rename = aufs_rename,
+
-+ switch (cmd) {
-+ case AUFS_CTL_RDU:
-+ case AUFS_CTL_RDU_INO:
-+ err = au_rdu_compat_ioctl(file, cmd, arg);
-+ break;
++ .permission = aufs_permission,
++#ifdef CONFIG_FS_POSIX_ACL
++ .get_acl = aufs_get_acl,
++ .set_acl = aufs_set_acl,
++#endif
+
-+ case AUFS_CTL_IBUSY:
-+ err = au_ibusy_compat_ioctl(file, arg);
-+ break;
++ .setattr = aufs_setattr,
++ .getattr = aufs_getattr,
+
-+ case AUFS_CTL_BRINFO:
-+ err = au_brinfo_compat_ioctl(file, arg);
-+ break;
++#ifdef CONFIG_AUFS_XATTR
++ .listxattr = aufs_listxattr,
++#endif
+
-+ default:
-+ err = aufs_ioctl_dir(file, cmd, arg);
-+ }
++ .update_time = aufs_update_time,
++ .atomic_open = aufs_atomic_open,
++ .tmpfile = aufs_tmpfile
++ },
++ [AuIop_OTHER] = {
++ .permission = aufs_permission,
++#ifdef CONFIG_FS_POSIX_ACL
++ .get_acl = aufs_get_acl,
++ .set_acl = aufs_set_acl,
++#endif
+
-+ AuTraceErr(err);
-+ return err;
-+}
++ .setattr = aufs_setattr,
++ .getattr = aufs_getattr,
+
-+long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd,
-+ unsigned long arg)
-+{
-+ return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg));
-+}
++#ifdef CONFIG_AUFS_XATTR
++ .listxattr = aufs_listxattr,
+#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 2018-04-15 08:49:13.401150731 +0200
++
++ .update_time = aufs_update_time
++ }
++};
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/i_op_add.c linux-4.9/fs/aufs/i_op_add.c
+--- linux-4.9/fs/aufs/i_op_add.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/i_op_add.c 2021-02-24 16:15:09.531573855 +0100
@@ -0,0 +1,928 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+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 2018-04-15 08:49:13.401150731 +0200
-@@ -0,0 +1,1459 @@
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/i_op_del.c linux-4.9/fs/aufs/i_op_del.c
+--- linux-4.9/fs/aufs/i_op_del.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/i_op_del.c 2021-02-24 16:15:09.531573855 +0100
+@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ *
+ */
+
+/*
-+ * inode operations (except add/del/rename)
++ * inode operations (del entry)
+ */
+
-+#include <linux/device_cgroup.h>
-+#include <linux/fs_stack.h>
-+#include <linux/namei.h>
-+#include <linux/security.h>
+#include "aufs.h"
+
-+static int h_permission(struct inode *h_inode, int mask,
-+ struct path *h_path, int brperm)
++/*
++ * decide if a new whiteout for @dentry is necessary or not.
++ * when it is necessary, prepare the parent dir for the upper branch whose
++ * branch index is @bcpup for creation. the actual creation of the whiteout will
++ * be done by caller.
++ * return value:
++ * 0: wh is unnecessary
++ * plus: wh is necessary
++ * minus: error
++ */
++int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup)
+{
-+ int err;
-+ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));
-+
-+ err = -EPERM;
-+ if (write_mask && IS_IMMUTABLE(h_inode))
-+ goto out;
++ int need_wh, err;
++ aufs_bindex_t btop;
++ struct super_block *sb;
+
-+ err = -EACCES;
-+ if (((mask & MAY_EXEC)
-+ && S_ISREG(h_inode->i_mode)
-+ && (path_noexec(h_path)
-+ || !(h_inode->i_mode & S_IXUGO))))
-+ goto out;
++ sb = dentry->d_sb;
++ btop = au_dbtop(dentry);
++ if (*bcpup < 0) {
++ *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(btop < *bcpup
++ || au_test_ro(sb, *bcpup, d_inode(dentry)));
++ AuDbg("bcpup %d, btop %d\n", *bcpup, btop);
+
-+ /*
-+ * - 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.'
-+ * in this case, generic_permission() returns -EOPNOTSUPP.
-+ */
-+ if ((write_mask && !au_br_writable(brperm))
-+ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode)
-+ && write_mask && !(mask & MAY_READ))
-+ || !h_inode->i_op->permission) {
-+ /* AuLabel(generic_permission); */
-+ /* AuDbg("get_acl %pf\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);
-+ AuTraceErr(err);
++ if (*bcpup != btop) {
++ err = au_cpup_dirs(dentry, *bcpup);
++ if (unlikely(err))
++ goto out;
++ need_wh = 1;
+ } else {
-+ /* AuLabel(h_inode->permission); */
-+ err = h_inode->i_op->permission(h_inode, mask);
-+ AuTraceErr(err);
++ struct au_dinfo *dinfo, *tmp;
++
++ need_wh = -ENOMEM;
++ dinfo = au_di(dentry);
++ tmp = au_di_alloc(sb, AuLsc_DI_TMP);
++ if (tmp) {
++ au_di_cp(tmp, dinfo);
++ au_di_swap(tmp, dinfo);
++ /* returns the number of positive dentries */
++ need_wh = au_lkup_dentry(dentry, btop + 1,
++ /* AuLkup_IGNORE_PERM */ 0);
++ au_di_swap(tmp, dinfo);
++ au_rw_write_unlock(&tmp->di_rwsem);
++ au_di_free(tmp);
++ }
+ }
++ AuDbg("need_wh %d\n", need_wh);
++ err = need_wh;
+
-+ if (!err)
-+ err = devcgroup_inode_permission(h_inode, mask);
-+ if (!err)
-+ err = security_inode_permission(h_inode, mask);
++out:
++ return err;
++}
+
-+#if 0
-+ if (!err) {
-+ /* todo: do we need to call ima_path_check()? */
-+ struct path h_path = {
-+ .dentry =
-+ .mnt = h_mnt
-+ };
-+ err = ima_path_check(&h_path,
-+ mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
-+ IMA_COUNT_LEAVE);
++/*
++ * simple tests for the del-entry operations.
++ * following the checks in vfs, plus the parent-child relationship.
++ */
++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_parent, int isdir)
++{
++ int err;
++ umode_t h_mode;
++ struct dentry *h_dentry, *h_latest;
++ struct inode *h_inode;
++
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (d_really_is_positive(dentry)) {
++ err = -ENOENT;
++ if (unlikely(d_is_negative(h_dentry)))
++ goto out;
++ h_inode = d_inode(h_dentry);
++ if (unlikely(!h_inode->i_nlink))
++ goto out;
++
++ h_mode = h_inode->i_mode;
++ if (!isdir) {
++ err = -EISDIR;
++ if (unlikely(S_ISDIR(h_mode)))
++ goto out;
++ } else if (unlikely(!S_ISDIR(h_mode))) {
++ err = -ENOTDIR;
++ goto out;
++ }
++ } else {
++ /* rename(2) case */
++ err = -EIO;
++ if (unlikely(d_is_positive(h_dentry)))
++ goto out;
+ }
-+#endif
++
++ err = -ENOENT;
++ /* expected parent dir is locked */
++ if (unlikely(h_parent != h_dentry->d_parent))
++ goto out;
++ err = 0;
++
++ /*
++ * rmdir a dir may break the consistency on some filesystem.
++ * let's try heavy test.
++ */
++ err = -EACCES;
++ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)
++ && au_test_h_perm(d_inode(h_parent),
++ MAY_EXEC | MAY_WRITE)))
++ goto out;
++
++ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent);
++ err = -EIO;
++ if (IS_ERR(h_latest))
++ goto out;
++ if (h_latest == h_dentry)
++ err = 0;
++ dput(h_latest);
+
+out:
+ return err;
+}
+
-+static int aufs_permission(struct inode *inode, int mask)
++/*
++ * decide the branch where we operate for @dentry. the branch index will be set
++ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent
++ * dir for reverting.
++ * when a new whiteout is necessary, create it.
++ */
++static struct dentry*
++lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup,
++ struct au_dtime *dt, struct au_pin *pin)
+{
-+ int err;
-+ 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;
++ struct dentry *wh_dentry;
+ struct super_block *sb;
-+ struct au_branch *br;
++ struct path h_path;
++ int err, need_wh;
++ unsigned int udba;
++ aufs_bindex_t bcpup;
+
-+ /* todo: support rcu-walk? */
-+ if (mask & MAY_NOT_BLOCK)
-+ return -ECHILD;
++ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup);
++ wh_dentry = ERR_PTR(need_wh);
++ if (unlikely(need_wh < 0))
++ goto out;
+
-+ sb = inode->i_sb;
-+ si_read_lock(sb, AuLock_FLUSH);
-+ ii_read_lock_child(inode);
-+#if 0
-+ err = au_iigen_test(inode, au_sigen(sb));
++ sb = dentry->d_sb;
++ udba = au_opt_udba(sb);
++ bcpup = *rbcpup;
++ err = au_pin(pin, dentry, bcpup, udba,
++ AuPin_DI_LOCKED | AuPin_MNT_WRITE);
++ wh_dentry = ERR_PTR(err);
+ if (unlikely(err))
+ goto out;
-+#endif
-+
-+ if (!isdir
-+ || write_mask
-+ || au_opt_test(au_mntflags(sb), DIRPERM1)) {
-+ err = au_busy_or_stale();
-+ 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_ibtop(inode);
-+ br = au_sbr(sb, bindex);
-+ err = h_permission(h_inode, mask, &br->br_path, br->br_perm);
-+ if (write_mask
-+ && !err
-+ && !special_file(h_inode->i_mode)) {
-+ /* test whether the upper writable branch exists */
-+ err = -EROFS;
-+ for (; bindex >= 0; bindex--)
-+ if (!au_br_rdonly(au_sbr(sb, bindex))) {
-+ err = 0;
-+ break;
-+ }
-+ }
-+ goto out;
++ h_path.dentry = au_pinned_h_parent(pin);
++ if (udba != AuOpt_UDBA_NONE
++ && au_dbtop(dentry) == bcpup) {
++ err = au_may_del(dentry, bcpup, h_path.dentry, isdir);
++ wh_dentry = ERR_PTR(err);
++ if (unlikely(err))
++ goto out_unpin;
+ }
+
-+ /* non-write to dir */
-+ err = 0;
-+ 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();
-+ if (unlikely(!S_ISDIR(h_inode->i_mode)))
-+ break;
++ h_path.mnt = au_sbr_mnt(sb, bcpup);
++ au_dtime_store(dt, au_pinned_parent(pin), &h_path);
++ wh_dentry = NULL;
++ if (!need_wh)
++ goto out; /* success, no need to create whiteout */
+
-+ br = au_sbr(sb, bindex);
-+ err = h_permission(h_inode, mask, &br->br_path,
-+ br->br_perm);
-+ }
-+ }
++ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry);
++ if (IS_ERR(wh_dentry))
++ goto out_unpin;
+
++ /* returns with the parent is locked and wh_dentry is dget-ed */
++ goto out; /* success */
++
++out_unpin:
++ au_unpin(pin);
+out:
-+ ii_read_unlock(inode);
-+ si_read_unlock(sb);
-+ return err;
++ return wh_dentry;
+}
+
-+/* ---------------------------------------------------------------------- */
-+
-+static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
-+ unsigned int flags)
++/*
++ * when removing a dir, rename it to a unique temporary whiteout-ed name first
++ * in order to be revertible and save time for removing many child whiteouts
++ * under the dir.
++ * returns 1 when there are too many child whiteout and caller should remove
++ * them asynchronously. returns 0 when the number of children is enough small to
++ * remove now or the branch fs is a remote fs.
++ * otherwise return an error.
++ */
++static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,
++ struct au_nhash *whlist, struct inode *dir)
+{
-+ struct dentry *ret, *parent;
-+ struct inode *inode;
++ int rmdir_later, err, dirwh;
++ struct dentry *h_dentry;
+ struct super_block *sb;
-+ int err, npositive;
-+
-+ IMustLock(dir);
-+
-+ /* todo: support rcu-walk? */
-+ ret = ERR_PTR(-ECHILD);
-+ if (flags & LOOKUP_RCU)
-+ goto out;
-+
-+ ret = ERR_PTR(-ENAMETOOLONG);
-+ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
-+ goto out;
++ struct inode *inode;
+
-+ sb = dir->i_sb;
-+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
-+ ret = ERR_PTR(err);
++ sb = dentry->d_sb;
++ SiMustAnyLock(sb);
++ h_dentry = au_h_dptr(dentry, bindex);
++ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex));
+ if (unlikely(err))
+ goto out;
+
-+ err = au_di_init(dentry);
-+ ret = ERR_PTR(err);
-+ if (unlikely(err))
-+ goto out_si;
-+
-+ inode = NULL;
-+ npositive = 0; /* suppress a warning */
-+ parent = dentry->d_parent; /* dir inode is locked */
-+ di_read_lock_parent(parent, AuLock_IR);
-+ err = au_alive_dir(parent);
-+ if (!err)
-+ err = au_digen_test(parent, au_sigen(sb));
-+ if (!err) {
-+ /* 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);
-+ ret = ERR_PTR(err);
-+ if (unlikely(err < 0))
-+ goto out_unlock;
++ /* stop monitoring */
++ inode = d_inode(dentry);
++ au_hn_free(au_hi(inode, bindex));
+
-+ if (npositive) {
-+ inode = au_new_inode(dentry, /*must_new*/0);
-+ if (IS_ERR(inode)) {
-+ ret = (void *)inode;
-+ inode = NULL;
-+ goto out_unlock;
-+ }
++ if (!au_test_fs_remote(h_dentry->d_sb)) {
++ dirwh = au_sbi(sb)->si_dirwh;
++ rmdir_later = (dirwh <= 1);
++ if (!rmdir_later)
++ rmdir_later = au_nhash_test_longer_wh(whlist, bindex,
++ dirwh);
++ if (rmdir_later)
++ return rmdir_later;
+ }
+
-+ if (inode)
-+ atomic_inc(&inode->i_count);
-+ ret = d_splice_alias(inode, dentry);
-+#if 0
-+ if (unlikely(d_need_lookup(dentry))) {
-+ spin_lock(&dentry->d_lock);
-+ dentry->d_flags &= ~DCACHE_NEED_LOOKUP;
-+ spin_unlock(&dentry->d_lock);
-+ } else
-+#endif
-+ if (inode) {
-+ if (!IS_ERR(ret)) {
-+ iput(inode);
-+ if (ret && ret != dentry)
-+ ii_write_unlock(inode);
-+ } else {
-+ ii_write_unlock(inode);
-+ iput(inode);
-+ inode = NULL;
-+ }
++ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist);
++ if (unlikely(err)) {
++ AuIOErr("rmdir %pd, b%d failed, %d. ignored\n",
++ h_dentry, bindex, err);
++ err = 0;
+ }
+
-+out_unlock:
-+ di_write_unlock(dentry);
-+out_si:
-+ si_read_unlock(sb);
+out:
-+ return ret;
++ AuTraceErr(err);
++ return err;
+}
+
-+/* ---------------------------------------------------------------------- */
++/*
++ * final procedure for deleting a entry.
++ * maintain dentry and iattr.
++ */
++static void epilog(struct inode *dir, struct dentry *dentry,
++ aufs_bindex_t bindex)
++{
++ struct inode *inode;
+
-+struct aopen_node {
-+ struct hlist_bl_node hblist;
-+ struct file *file, *h_file;
-+};
++ inode = d_inode(dentry);
++ d_drop(dentry);
++ inode->i_ctime = dir->i_ctime;
+
-+static int au_do_aopen(struct inode *inode, struct file *file)
++ au_dir_ts(dir, bindex);
++ dir->i_version++;
++}
++
++/*
++ * when an error happened, remove the created whiteout and revert everything.
++ */
++static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex,
++ aufs_bindex_t bwh, struct dentry *wh_dentry,
++ struct dentry *dentry, struct au_dtime *dt)
+{
-+ struct hlist_bl_head *aopen;
-+ struct hlist_bl_node *pos;
-+ struct aopen_node *node;
-+ struct au_do_open_args args = {
-+ .aopen = 1,
-+ .open = au_do_open_nondir
++ int rerr;
++ struct path h_path = {
++ .dentry = wh_dentry,
++ .mnt = au_sbr_mnt(dir->i_sb, bindex)
+ };
+
-+ aopen = &au_sbi(inode->i_sb)->si_aopen;
-+ hlist_bl_lock(aopen);
-+ hlist_bl_for_each_entry(node, pos, aopen, hblist)
-+ if (node->file == file) {
-+ args.h_file = node->h_file;
-+ break;
-+ }
-+ hlist_bl_unlock(aopen);
-+ /* AuDebugOn(!args.h_file); */
++ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry);
++ if (!rerr) {
++ au_set_dbwh(dentry, bwh);
++ au_dtime_revert(dt);
++ return 0;
++ }
+
-+ return au_do_open(file, &args);
++ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr);
++ return -EIO;
+}
+
-+static int aufs_atomic_open(struct inode *dir, struct dentry *dentry,
-+ struct file *file, unsigned int open_flag,
-+ umode_t create_mode, int *opened)
++/* ---------------------------------------------------------------------- */
++
++int aufs_unlink(struct inode *dir, struct dentry *dentry)
+{
-+ int err, unlocked, h_opened = *opened;
-+ unsigned int lkup_flags;
-+ struct dentry *parent, *d;
-+ struct hlist_bl_head *aopen;
-+ struct vfsub_aopen_args args = {
-+ .open_flag = open_flag,
-+ .create_mode = create_mode,
-+ .opened = &h_opened
-+ };
-+ struct aopen_node aopen_node = {
-+ .file = file
-+ };
++ int err;
++ aufs_bindex_t bwh, bindex, btop;
++ struct inode *inode, *h_dir, *delegated;
++ struct dentry *parent, *wh_dentry;
++ /* to reuduce stack size */
++ struct {
++ struct au_dtime dt;
++ struct au_pin pin;
++ struct path h_path;
++ } *a;
+
+ IMustLock(dir);
-+ AuDbg("open_flag 0%o\n", open_flag);
-+ AuDbgDentry(dentry);
-+
-+ err = 0;
-+ if (!au_di(dentry)) {
-+ 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) {
-+ /*
-+ * obsoleted dentry found.
-+ * another error will be returned later.
-+ */
-+ d_drop(d);
-+ AuDbgDentry(d);
-+ dput(d);
-+ }
-+ AuDbgDentry(dentry);
-+ }
-+
-+ if (d_is_positive(dentry)
-+ || d_unhashed(dentry)
-+ || d_unlinked(dentry)
-+ || !(open_flag & O_CREAT))
-+ goto out_no_open;
+
-+ unlocked = 0;
-+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN);
-+ if (unlikely(err))
++ err = -ENOMEM;
++ a = kmalloc(sizeof(*a), GFP_NOFS);
++ if (unlikely(!a))
+ goto out;
+
-+ parent = dentry->d_parent; /* dir is locked */
-+ di_write_lock_parent(parent);
-+ err = au_lkup_dentry(dentry, /*btop*/0, AuLkup_ALLOW_NEG);
++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
++ if (unlikely(err))
++ goto out_free;
++ err = au_d_hashed_positive(dentry);
+ if (unlikely(err))
+ goto out_unlock;
++ inode = d_inode(dentry);
++ IMustLock(inode);
++ err = -EISDIR;
++ if (unlikely(d_is_dir(dentry)))
++ goto out_unlock; /* possible? */
+
-+ AuDbgDentry(dentry);
-+ if (d_is_positive(dentry))
-+ goto out_unlock;
-+
-+ args.file = get_empty_filp();
-+ err = PTR_ERR(args.file);
-+ if (IS_ERR(args.file))
-+ goto out_unlock;
++ btop = au_dbtop(dentry);
++ bwh = au_dbwh(dentry);
++ bindex = -1;
++ parent = dentry->d_parent; /* dir inode is locked */
++ di_write_lock_parent(parent);
++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt,
++ &a->pin);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out_parent;
+
-+ args.file->f_flags = file->f_flags;
-+ err = au_aopen_or_create(dir, dentry, &args);
-+ AuTraceErr(err);
-+ AuDbgFile(args.file);
-+ if (unlikely(err < 0)) {
-+ if (h_opened & FILE_OPENED)
-+ fput(args.file);
-+ else
-+ put_filp(args.file);
-+ goto out_unlock;
++ 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 == btop) {
++ h_dir = au_pinned_h_dir(&a->pin);
++ delegated = NULL;
++ err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0);
++ if (unlikely(err == -EWOULDBLOCK)) {
++ pr_warn("cannot retry for NFSv4 delegation"
++ " for an internal unlink\n");
++ iput(delegated);
++ }
++ } else {
++ /* dir inode is locked */
++ h_dir = d_inode(wh_dentry->d_parent);
++ IMustLock(h_dir);
++ err = 0;
+ }
-+ di_write_unlock(parent);
-+ di_write_unlock(dentry);
-+ unlocked = 1;
+
-+ /* some filesystems don't set FILE_CREATED while succeeded? */
-+ *opened |= FILE_CREATED;
-+ if (h_opened & FILE_OPENED)
-+ aopen_node.h_file = args.file;
-+ else {
-+ put_filp(args.file);
-+ args.file = NULL;
-+ }
-+ aopen = &au_sbi(dir->i_sb)->si_aopen;
-+ au_hbl_add(&aopen_node.hblist, aopen);
-+ err = finish_open(file, dentry, au_do_aopen, opened);
-+ au_hbl_del(&aopen_node.hblist, aopen);
-+ AuTraceErr(err);
-+ AuDbgFile(file);
-+ if (aopen_node.h_file)
-+ fput(aopen_node.h_file);
++ if (!err) {
++ vfsub_drop_nlink(inode);
++ epilog(dir, dentry, bindex);
+
-+out_unlock:
-+ if (unlocked)
-+ si_read_unlock(dentry->d_sb);
-+ else {
-+ di_write_unlock(parent);
-+ aufs_read_unlock(dentry, AuLock_DW);
-+ }
-+ AuDbgDentry(dentry);
-+ if (unlikely(err < 0))
-+ goto out;
-+out_no_open:
-+ if (err >= 0 && !(*opened & FILE_CREATED)) {
-+ AuLabel(out_no_open);
-+ dget(dentry);
-+ err = finish_no_open(file, dentry);
++ /* update target timestamps */
++ if (bindex == btop) {
++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL);
++ /*ignore*/
++ inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime;
++ } else
++ /* todo: this timestamp may be reverted later */
++ inode->i_ctime = h_dir->i_ctime;
++ goto out_unpin; /* success */
+ }
-+out:
-+ AuDbg("%pd%s%s\n", dentry,
-+ (*opened & FILE_CREATED) ? " created" : "",
-+ (*opened & FILE_OPENED) ? " opened" : "");
-+ AuTraceErr(err);
-+ return err;
-+}
-+
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent,
-+ const unsigned char add_entry, aufs_bindex_t bcpup,
-+ aufs_bindex_t btop)
-+{
-+ int err;
-+ struct dentry *h_parent;
-+ struct inode *h_dir;
+
-+ if (add_entry)
-+ IMustLock(d_inode(parent));
-+ else
-+ di_write_lock_parent(parent);
-+
-+ err = 0;
-+ if (!au_h_dptr(parent, bcpup)) {
-+ if (btop > bcpup)
-+ err = au_cpup_dirs(dentry, bcpup);
-+ else if (btop < bcpup)
-+ err = au_cpdown_dirs(dentry, bcpup);
-+ else
-+ BUG();
-+ }
-+ if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) {
-+ h_parent = au_h_dptr(parent, bcpup);
-+ h_dir = d_inode(h_parent);
-+ vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
-+ err = au_lkup_neg(dentry, bcpup, /*wh*/0);
-+ /* todo: no unlock here */
-+ inode_unlock_shared(h_dir);
++ /* revert */
++ if (wh_dentry) {
++ int rerr;
+
-+ AuDbg("bcpup %d\n", bcpup);
-+ if (!err) {
-+ if (d_really_is_negative(dentry))
-+ au_set_h_dptr(dentry, btop, NULL);
-+ au_update_dbrange(dentry, /*do_put_zero*/0);
-+ }
++ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry,
++ &a->dt);
++ if (rerr)
++ err = rerr;
+ }
+
-+ if (!add_entry)
-+ di_write_unlock(parent);
-+ if (!err)
-+ err = bcpup; /* success */
-+
-+ AuTraceErr(err);
++out_unpin:
++ au_unpin(&a->pin);
++ dput(wh_dentry);
++ dput(a->h_path.dentry);
++out_parent:
++ di_write_unlock(parent);
++out_unlock:
++ aufs_read_unlock(dentry, AuLock_DW);
++out_free:
++ kfree(a);
++out:
+ return err;
+}
+
-+/*
-+ * decide the branch and the parent dir where we will create a new entry.
-+ * returns new bindex or an error.
-+ * copyup the parent dir if needed.
-+ */
-+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
-+ struct au_wr_dir_args *args)
++int aufs_rmdir(struct inode *dir, struct dentry *dentry)
+{
-+ int err;
-+ unsigned int flags;
-+ 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);
-+ struct super_block *sb;
-+ struct dentry *parent;
-+ struct au_sbinfo *sbinfo;
++ int err, rmdir_later;
++ 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 */
++ struct {
++ struct au_dtime dt;
++ struct au_pin pin;
++ } *a;
++
++ IMustLock(dir);
++
++ err = -ENOMEM;
++ a = kmalloc(sizeof(*a), GFP_NOFS);
++ if (unlikely(!a))
++ goto out;
++
++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN);
++ if (unlikely(err))
++ goto out_free;
++ err = au_alive_dir(dentry);
++ if (unlikely(err))
++ goto out_unlock;
++ inode = d_inode(dentry);
++ IMustLock(inode);
++ err = -ENOTDIR;
++ if (unlikely(!d_is_dir(dentry)))
++ goto out_unlock; /* possible? */
++
++ err = -ENOMEM;
++ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS);
++ if (unlikely(!args))
++ goto out_unlock;
++
++ parent = dentry->d_parent; /* dir inode is locked */
++ di_write_lock_parent(parent);
++ err = au_test_empty(dentry, &args->whlist);
++ if (unlikely(err))
++ goto out_parent;
+
-+ sb = dentry->d_sb;
-+ sbinfo = au_sbi(sb);
-+ parent = dget_parent(dentry);
+ btop = au_dbtop(dentry);
-+ bcpup = btop;
-+ if (args->force_btgt < 0) {
-+ if (src_dentry) {
-+ 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))
-+ au_fset_wbr(flags, DIR);
-+ err = AuWbrCreate(sbinfo, dentry, flags);
-+ bcpup = err;
-+ }
++ bwh = au_dbwh(dentry);
++ bindex = -1;
++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt,
++ &a->pin);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out_parent;
+
-+ if (bcpup < 0 || au_test_ro(sb, bcpup, d_inode(dentry))) {
-+ if (add_entry)
-+ err = AuWbrCopyup(sbinfo, dentry);
-+ else {
-+ if (!IS_ROOT(dentry)) {
-+ di_read_lock_parent(parent, !AuLock_IR);
-+ err = AuWbrCopyup(sbinfo, dentry);
-+ di_read_unlock(parent, !AuLock_IR);
-+ } else
-+ err = AuWbrCopyup(sbinfo, dentry);
-+ }
-+ bcpup = err;
-+ if (unlikely(err < 0))
-+ goto out;
++ h_dentry = au_h_dptr(dentry, btop);
++ dget(h_dentry);
++ rmdir_later = 0;
++ if (bindex == btop) {
++ err = renwh_and_rmdir(dentry, btop, &args->whlist, dir);
++ if (err > 0) {
++ rmdir_later = err;
++ err = 0;
+ }
+ } else {
-+ bcpup = args->force_btgt;
-+ AuDebugOn(au_test_ro(sb, bcpup, d_inode(dentry)));
++ /* stop monitoring */
++ au_hn_free(au_hi(inode, btop));
++
++ /* dir inode is locked */
++ IMustLock(d_inode(wh_dentry->d_parent));
++ err = 0;
+ }
+
-+ AuDbg("btop %d, bcpup %d\n", btop, bcpup);
-+ err = bcpup;
-+ if (bcpup == btop)
-+ goto out; /* success */
++ if (!err) {
++ vfsub_dead_dir(inode);
++ au_set_dbdiropq(dentry, -1);
++ epilog(dir, dentry, bindex);
+
-+ /* copyup the new parent into the branch we process */
-+ 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, btop, NULL);
-+ au_set_dbtop(dentry, bcpup);
-+ au_set_dbbot(dentry, bcpup);
++ if (rmdir_later) {
++ au_whtmp_kick_rmdir(dir, btop, h_dentry, args);
++ args = NULL;
+ }
-+ AuDebugOn(add_entry
-+ && !au_ftest_wrdir(args->flags, TMPFILE)
-+ && !au_h_dptr(dentry, bcpup));
++
++ goto out_unpin; /* success */
++ }
++
++ /* revert */
++ AuLabel(revert);
++ if (wh_dentry) {
++ int rerr;
++
++ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry,
++ &a->dt);
++ if (rerr)
++ err = rerr;
+ }
+
++out_unpin:
++ au_unpin(&a->pin);
++ dput(wh_dentry);
++ dput(h_dentry);
++out_parent:
++ di_write_unlock(parent);
++ if (args)
++ au_whtmp_rmdir_free(args);
++out_unlock:
++ aufs_read_unlock(dentry, AuLock_DW);
++out_free:
++ kfree(a);
+out:
-+ dput(parent);
++ AuTraceErr(err);
+ return err;
+}
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/i_op_ren.c linux-4.9/fs/aufs/i_op_ren.c
+--- linux-4.9/fs/aufs/i_op_ren.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/i_op_ren.c 2021-02-24 16:15:09.531573855 +0100
+@@ -0,0 +1,1246 @@
++/*
++ * 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
++ * 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 <http://www.gnu.org/licenses/>.
++ */
+
-+/* ---------------------------------------------------------------------- */
++/*
++ * inode operation (rename entry)
++ * todo: this is crazy monster
++ */
+
-+void au_pin_hdir_unlock(struct au_pin *p)
-+{
-+ if (p->hdir)
-+ au_hn_inode_unlock(p->hdir);
-+}
++#include "aufs.h"
+
-+int au_pin_hdir_lock(struct au_pin *p)
-+{
-+ int err;
++enum { AuSRC, AuDST, AuSrcDst };
++enum { AuPARENT, AuCHILD, AuParentChild };
+
-+ err = 0;
-+ if (!p->hdir)
-+ goto out;
++#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)
+
-+ /* even if an error happens later, keep this lock */
-+ au_hn_inode_lock_nested(p->hdir, p->lsc_hi);
++#ifndef CONFIG_AUFS_DIRREN
++#undef AuRen_DIRREN
++#define AuRen_DIRREN 0
++#endif
+
-+ err = -EBUSY;
-+ if (unlikely(p->hdir->hi_inode != d_inode(p->h_parent)))
-+ goto out;
++struct au_ren_args {
++ struct {
++ struct dentry *dentry, *h_dentry, *parent, *h_parent,
++ *wh_dentry;
++ struct inode *dir, *inode;
++ struct au_hinode *hdir, *hinode;
++ struct au_dtime dt[AuParentChild];
++ aufs_bindex_t btop, bdiropq;
++ } sd[AuSrcDst];
+
-+ err = 0;
-+ if (p->h_dentry)
-+ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode,
-+ p->h_parent, p->br);
++#define src_dentry sd[AuSRC].dentry
++#define src_dir sd[AuSRC].dir
++#define src_inode sd[AuSRC].inode
++#define src_h_dentry sd[AuSRC].h_dentry
++#define src_parent sd[AuSRC].parent
++#define src_h_parent sd[AuSRC].h_parent
++#define src_wh_dentry sd[AuSRC].wh_dentry
++#define src_hdir sd[AuSRC].hdir
++#define src_hinode sd[AuSRC].hinode
++#define src_h_dir sd[AuSRC].hdir->hi_inode
++#define src_dt sd[AuSRC].dt
++#define src_btop sd[AuSRC].btop
++#define src_bdiropq sd[AuSRC].bdiropq
+
-+out:
-+ return err;
-+}
++#define dst_dentry sd[AuDST].dentry
++#define dst_dir sd[AuDST].dir
++#define dst_inode sd[AuDST].inode
++#define dst_h_dentry sd[AuDST].h_dentry
++#define dst_parent sd[AuDST].parent
++#define dst_h_parent sd[AuDST].h_parent
++#define dst_wh_dentry sd[AuDST].wh_dentry
++#define dst_hdir sd[AuDST].hdir
++#define dst_hinode sd[AuDST].hinode
++#define dst_h_dir sd[AuDST].hdir->hi_inode
++#define dst_dt sd[AuDST].dt
++#define dst_btop sd[AuDST].btop
++#define dst_bdiropq sd[AuDST].bdiropq
+
-+int au_pin_hdir_relock(struct au_pin *p)
-+{
-+ int err, i;
-+ struct inode *h_i;
-+ struct dentry *h_d[] = {
-+ p->h_dentry,
-+ p->h_parent
-+ };
++ struct dentry *h_trap;
++ struct au_branch *br;
++ struct path h_path;
++ struct au_nhash whlist;
++ aufs_bindex_t btgt, src_bwh;
+
-+ err = au_pin_hdir_lock(p);
-+ if (unlikely(err))
-+ goto out;
++ struct {
++ unsigned short auren_flags;
++ unsigned char flags; /* syscall parameter */
++ unsigned char exchange;
++ } __packed;
+
-+ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) {
-+ if (!h_d[i])
-+ continue;
-+ if (d_is_positive(h_d[i])) {
-+ h_i = d_inode(h_d[i]);
-+ err = !h_i->i_nlink;
-+ }
-+ }
++ struct au_whtmp_rmdir *thargs;
++ struct dentry *h_dst;
++ struct au_hinode *h_root;
++};
+
-+out:
-+ return err;
-+}
++/* ---------------------------------------------------------------------- */
+
-+static void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task)
-+{
-+#if !defined(CONFIG_RWSEM_GENERIC_SPINLOCK) && defined(CONFIG_RWSEM_SPIN_ON_OWNER)
-+ p->hdir->hi_inode->i_rwsem.owner = task;
-+#endif
-+}
++/*
++ * functions for reverting.
++ * when an error happened in a single rename systemcall, we should revert
++ * everything as if nothing happened.
++ * we don't need to revert the copied-up/down the parent dir since they are
++ * harmless.
++ */
+
-+void au_pin_hdir_acquire_nest(struct au_pin *p)
-+{
-+ if (p->hdir) {
-+ 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);
-+ }
-+}
++#define RevertFailure(fmt, ...) do { \
++ AuIOErr("revert failure: " fmt " (%d, %d)\n", \
++ ##__VA_ARGS__, err, rerr); \
++ err = -EIO; \
++} while (0)
+
-+void au_pin_hdir_release(struct au_pin *p)
++static void au_ren_do_rev_diropq(int err, struct au_ren_args *a, int idx)
+{
-+ if (p->hdir) {
-+ au_pin_hdir_set_owner(p, p->task);
-+ rwsem_release(&p->hdir->hi_inode->i_rwsem.dep_map, 1, _RET_IP_);
-+ }
-+}
++ int rerr;
++ struct dentry *d;
++#define src_or_dst(member) a->sd[idx].member
+
-+struct dentry *au_pinned_h_parent(struct au_pin *pin)
-+{
-+ if (pin && pin->parent)
-+ return au_h_dptr(pin->parent, pin->bindex);
-+ return NULL;
++ 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", d);
++
++#undef src_or_dst_
+}
+
-+void au_unpin(struct au_pin *p)
++static void au_ren_rev_diropq(int err, struct au_ren_args *a)
+{
-+ if (p->hdir)
-+ au_pin_hdir_unlock(p);
-+ if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE))
-+ vfsub_mnt_drop_write(p->h_mnt);
-+ if (!p->hdir)
-+ return;
-+
-+ if (!au_ftest_pin(p->flags, DI_LOCKED))
-+ di_read_unlock(p->parent, AuLock_IR);
-+ iput(p->hdir->hi_inode);
-+ dput(p->parent);
-+ p->parent = NULL;
-+ p->hdir = NULL;
-+ p->h_mnt = NULL;
-+ /* do not clear p->task */
++ 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);
+}
+
-+int au_do_pin(struct au_pin *p)
++static void au_ren_rev_rename(int err, struct au_ren_args *a)
+{
-+ int err;
-+ struct super_block *sb;
-+ struct inode *h_dir;
-+
-+ err = 0;
-+ sb = p->dentry->d_sb;
-+ p->br = au_sbr(sb, p->bindex);
-+ if (IS_ROOT(p->dentry)) {
-+ if (au_ftest_pin(p->flags, MNT_WRITE)) {
-+ p->h_mnt = au_br_mnt(p->br);
-+ err = vfsub_mnt_want_write(p->h_mnt);
-+ if (unlikely(err)) {
-+ au_fclr_pin(p->flags, MNT_WRITE);
-+ goto out_err;
-+ }
-+ }
-+ goto out;
-+ }
-+
-+ p->h_dentry = NULL;
-+ if (p->bindex <= au_dbbot(p->dentry))
-+ p->h_dentry = au_h_dptr(p->dentry, p->bindex);
-+
-+ p->parent = dget_parent(p->dentry);
-+ if (!au_ftest_pin(p->flags, DI_LOCKED))
-+ di_read_lock(p->parent, AuLock_IR, p->lsc_di);
-+
-+ h_dir = NULL;
-+ p->h_parent = au_h_dptr(p->parent, p->bindex);
-+ p->hdir = au_hi(d_inode(p->parent), p->bindex);
-+ if (p->hdir)
-+ h_dir = p->hdir->hi_inode;
++ int rerr;
++ struct inode *delegated;
+
-+ /*
-+ * udba case, or
-+ * if DI_LOCKED is not set, then p->parent may be different
-+ * and h_parent can be NULL.
-+ */
-+ if (unlikely(!p->hdir || !h_dir || !p->h_parent)) {
-+ err = -EBUSY;
-+ if (!au_ftest_pin(p->flags, DI_LOCKED))
-+ di_read_unlock(p->parent, AuLock_IR);
-+ dput(p->parent);
-+ p->parent = NULL;
-+ goto out_err;
++ a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name,
++ a->src_h_parent);
++ rerr = PTR_ERR(a->h_path.dentry);
++ if (IS_ERR(a->h_path.dentry)) {
++ RevertFailure("lkup one %pd", a->src_dentry);
++ return;
+ }
+
-+ if (au_ftest_pin(p->flags, MNT_WRITE)) {
-+ p->h_mnt = au_br_mnt(p->br);
-+ err = vfsub_mnt_want_write(p->h_mnt);
-+ if (unlikely(err)) {
-+ au_fclr_pin(p->flags, MNT_WRITE);
-+ if (!au_ftest_pin(p->flags, DI_LOCKED))
-+ di_read_unlock(p->parent, AuLock_IR);
-+ dput(p->parent);
-+ p->parent = NULL;
-+ goto out_err;
-+ }
++ 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->flags);
++ if (unlikely(rerr == -EWOULDBLOCK)) {
++ pr_warn("cannot retry for NFSv4 delegation"
++ " for an internal rename\n");
++ iput(delegated);
+ }
-+
-+ au_igrab(h_dir);
-+ err = au_pin_hdir_lock(p);
-+ if (!err)
-+ goto out; /* success */
-+
-+ au_unpin(p);
-+
-+out_err:
-+ pr_err("err %d\n", err);
-+ err = au_busy_or_stale();
-+out:
-+ return err;
++ d_drop(a->h_path.dentry);
++ dput(a->h_path.dentry);
++ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */
++ if (rerr)
++ RevertFailure("rename %pd", a->src_dentry);
+}
+
-+void au_pin_init(struct au_pin *p, struct dentry *dentry,
-+ aufs_bindex_t bindex, int lsc_di, int lsc_hi,
-+ unsigned int udba, unsigned char flags)
++static void au_ren_rev_whtmp(int err, struct au_ren_args *a)
+{
-+ p->dentry = dentry;
-+ p->udba = udba;
-+ p->lsc_di = lsc_di;
-+ p->lsc_hi = lsc_hi;
-+ p->flags = flags;
-+ p->bindex = bindex;
-+
-+ p->parent = NULL;
-+ p->hdir = NULL;
-+ p->h_mnt = NULL;
++ int rerr;
++ struct inode *delegated;
+
-+ p->h_dentry = NULL;
-+ p->h_parent = NULL;
-+ p->br = NULL;
-+ p->task = current;
++ a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name,
++ a->dst_h_parent);
++ rerr = PTR_ERR(a->h_path.dentry);
++ if (IS_ERR(a->h_path.dentry)) {
++ RevertFailure("lkup one %pd", a->dst_dentry);
++ return;
++ }
++ if (d_is_positive(a->h_path.dentry)) {
++ d_drop(a->h_path.dentry);
++ dput(a->h_path.dentry);
++ return;
++ }
++
++ delegated = NULL;
++ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path,
++ &delegated, a->flags);
++ if (unlikely(rerr == -EWOULDBLOCK)) {
++ pr_warn("cannot retry for NFSv4 delegation"
++ " for an internal rename\n");
++ iput(delegated);
++ }
++ d_drop(a->h_path.dentry);
++ dput(a->h_path.dentry);
++ if (!rerr)
++ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst));
++ else
++ RevertFailure("rename %pd", a->h_dst);
+}
+
-+int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
-+ unsigned int udba, unsigned char flags)
++static void au_ren_rev_whsrc(int err, struct au_ren_args *a)
+{
-+ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2,
-+ udba, flags);
-+ return au_do_pin(pin);
++ int rerr;
++
++ a->h_path.dentry = a->src_wh_dentry;
++ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry);
++ au_set_dbwh(a->src_dentry, a->src_bwh);
++ if (rerr)
++ RevertFailure("unlink %pd", a->src_wh_dentry);
+}
++#undef RevertFailure
+
+/* ---------------------------------------------------------------------- */
+
+/*
-+ * ->setattr() and ->getattr() are called in various cases.
-+ * chmod, stat: dentry is revalidated.
-+ * fchmod, fstat: file and dentry are not revalidated, additionally they may be
-+ * unhashed.
-+ * for ->setattr(), ia->ia_file is passed from ftruncate only.
++ * when we have to copyup the renaming entry, do it with the rename-target name
++ * in order to minimize the cost (the later actual rename is unnecessary).
++ * otherwise rename it on the target branch.
+ */
-+/* todo: consolidate with do_refresh() and simple_reval_dpath() */
-+int au_reval_for_attr(struct dentry *dentry, unsigned int sigen)
++static int au_ren_or_cpup(struct au_ren_args *a)
+{
+ int err;
-+ struct dentry *parent;
++ struct dentry *d;
++ struct inode *delegated;
+
-+ err = 0;
-+ if (au_digen_test(dentry, sigen)) {
-+ parent = dget_parent(dentry);
-+ di_read_lock_parent(parent, AuLock_IR);
-+ err = au_refresh_dentry(dentry, parent);
-+ di_read_unlock(parent, AuLock_IR);
-+ dput(parent);
-+ }
++ d = a->src_dentry;
++ if (au_dbtop(d) == a->btgt) {
++ a->h_path.dentry = a->dst_h_dentry;
++ 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->flags);
++ if (unlikely(err == -EWOULDBLOCK)) {
++ pr_warn("cannot retry for NFSv4 delegation"
++ " for an internal rename\n");
++ iput(delegated);
++ }
++ } else
++ BUG();
++
++ if (!err && a->h_dst)
++ /* it will be set to dinfo later */
++ dget(a->h_dst);
+
-+ AuTraceErr(err);
+ return err;
+}
+
-+int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
-+ struct au_icpup_args *a)
++/* cf. aufs_rmdir() */
++static int au_ren_del_whtmp(struct au_ren_args *a)
+{
+ int err;
-+ loff_t sz;
-+ aufs_bindex_t btop, ibtop;
-+ struct dentry *hi_wh, *parent;
-+ struct inode *inode;
-+ struct au_wr_dir_args wr_dir_args = {
-+ .force_btgt = -1,
-+ .flags = 0
-+ };
-+
-+ if (d_is_dir(dentry))
-+ au_fset_wrdir(wr_dir_args.flags, ISDIR);
-+ /* plink or hi_wh() case */
-+ btop = au_dbtop(dentry);
-+ inode = d_inode(dentry);
-+ 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 != btop)
-+ au_fset_icpup(a->flags, DID_CPUP);
++ struct inode *dir;
+
-+ err = 0;
-+ a->pin_flags = AuPin_MNT_WRITE;
-+ parent = NULL;
-+ if (!IS_ROOT(dentry)) {
-+ au_fset_pin(a->pin_flags, DI_LOCKED);
-+ parent = dget_parent(dentry);
-+ di_write_lock_parent(parent);
++ dir = a->dst_dir;
++ SiMustAnyLock(dir->i_sb);
++ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt,
++ au_sbi(dir->i_sb)->si_dirwh)
++ || au_test_fs_remote(a->h_dst->d_sb)) {
++ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist);
++ if (unlikely(err))
++ pr_warn("failed removing whtmp dir %pd (%d), "
++ "ignored.\n", a->h_dst, err);
++ } else {
++ au_nhash_wh_free(&a->thargs->whlist);
++ a->thargs->whlist = a->whlist;
++ a->whlist.nh_num = 0;
++ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs);
++ dput(a->h_dst);
++ a->thargs = NULL;
+ }
+
-+ err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags);
-+ if (unlikely(err))
-+ goto out_parent;
++ return 0;
++}
+
-+ 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)) {
-+ vfsub_inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD);
-+ if (ia->ia_size < i_size_read(a->h_inode))
-+ sz = ia->ia_size;
-+ inode_unlock_shared(a->h_inode);
-+ }
++/* make it 'opaque' dir. */
++static int au_ren_do_diropq(struct au_ren_args *a, int idx)
++{
++ int err;
++ struct dentry *d, *diropq;
++#define src_or_dst(member) a->sd[idx].member
+
-+ hi_wh = NULL;
-+ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) {
-+ hi_wh = au_hi_wh(inode, a->btgt);
-+ if (!hi_wh) {
-+ struct au_cp_generic cpg = {
-+ .dentry = dentry,
-+ .bdst = a->btgt,
-+ .bsrc = -1,
-+ .len = sz,
-+ .pin = &a->pin
-+ };
-+ err = au_sio_cpup_wh(&cpg, /*file*/NULL);
-+ if (unlikely(err))
-+ goto out_unlock;
-+ hi_wh = au_hi_wh(inode, a->btgt);
-+ /* todo: revalidate hi_wh? */
-+ }
-+ }
++ err = 0;
++ 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);
+
-+ if (parent) {
-+ au_pin_set_parent_lflag(&a->pin, /*lflag*/0);
-+ di_downgrade_lock(parent, AuLock_IR);
-+ dput(parent);
-+ parent = NULL;
-+ }
-+ if (!au_ftest_icpup(a->flags, DID_CPUP))
-+ goto out; /* success */
++#undef src_or_dst_
++ return err;
++}
+
-+ if (!d_unhashed(dentry)) {
-+ struct au_cp_generic cpg = {
-+ .dentry = dentry,
-+ .bdst = a->btgt,
-+ .bsrc = btop,
-+ .len = sz,
-+ .pin = &a->pin,
-+ .flags = AuCpup_DTIME | AuCpup_HOPEN
-+ };
-+ err = au_sio_cpup_simple(&cpg);
-+ if (!err)
-+ a->h_path.dentry = au_h_dptr(dentry, a->btgt);
-+ } else if (!hi_wh)
-+ a->h_path.dentry = au_h_dptr(dentry, a->btgt);
-+ else
-+ a->h_path.dentry = hi_wh; /* do not dget here */
++static int au_ren_diropq(struct au_ren_args *a)
++{
++ int err;
++ unsigned char always;
++ struct dentry *d;
+
-+out_unlock:
-+ a->h_inode = d_inode(a->h_path.dentry);
-+ if (!err)
++ 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;
++ au_fset_ren(a->auren_flags, DIROPQ_SRC);
++ }
++ if (!a->exchange)
+ goto out; /* success */
-+ au_unpin(&a->pin);
-+out_parent:
-+ if (parent) {
-+ di_write_unlock(parent);
-+ dput(parent);
++
++ 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_rev_src;
++ au_fset_ren(a->auren_flags, DIROPQ_DST);
+ }
++ goto out; /* success */
++
++out_rev_src:
++ AuDbg("err %d, reverting src\n", err);
++ au_ren_rev_diropq(err, a);
+out:
-+ if (!err)
-+ inode_lock_nested(a->h_inode, AuLsc_I_CHILD);
+ return err;
+}
+
-+static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
++static int do_rename(struct au_ren_args *a)
+{
+ int err;
-+ struct inode *inode, *delegated;
-+ struct super_block *sb;
-+ struct file *file;
-+ struct au_icpup_args *a;
-+
-+ inode = d_inode(dentry);
-+ IMustLock(inode);
++ struct dentry *d, *h_d;
+
-+ err = setattr_prepare(dentry, ia);
-+ if (unlikely(err))
-+ goto out;
++ 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);
++ }
+
-+ err = -ENOMEM;
-+ a = kzalloc(sizeof(*a), GFP_NOFS);
-+ if (unlikely(!a))
-+ goto out;
++ /* 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;
++ }
+
-+ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
-+ ia->ia_valid &= ~ATTR_MODE;
++ /* 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;
++ }
+
-+ file = NULL;
-+ sb = dentry->d_sb;
-+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
-+ if (unlikely(err))
-+ goto out_kfree;
++ /* rename dentry to tmpwh */
++ if (a->thargs) {
++ err = au_whtmp_ren(a->dst_h_dentry, a->br);
++ if (unlikely(err))
++ goto out_whdst;
+
-+ if (ia->ia_valid & ATTR_FILE) {
-+ /* 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,
-+ /*fi_lsc*/0);
-+ if (unlikely(err))
-+ goto out_si;
-+ ia->ia_file = au_hf_top(file);
-+ a->udba = AuOpt_UDBA_NONE;
-+ } else {
-+ /* fchmod() doesn't pass ia_file */
-+ a->udba = au_opt_udba(sb);
-+ di_write_lock_child(dentry);
-+ /* no d_unlinked(), to set UDBA_NONE for root */
-+ if (d_unhashed(dentry))
-+ a->udba = AuOpt_UDBA_NONE;
-+ if (a->udba != AuOpt_UDBA_NONE) {
-+ AuDebugOn(IS_ROOT(dentry));
-+ err = au_reval_for_attr(dentry, au_sigen(sb));
++ 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_dentry;
++ goto out_whtmp;
++ a->dst_h_dentry = au_h_dptr(d, a->btgt);
+ }
+ }
+
-+ err = au_pin_and_icpup(dentry, ia, a);
-+ if (unlikely(err < 0))
-+ goto out_dentry;
-+ if (au_ftest_icpup(a->flags, DID_CPUP)) {
-+ ia->ia_file = NULL;
-+ ia->ia_valid &= ~ATTR_FILE;
-+ }
++ 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
+
-+ a->h_path.mnt = au_sbr_mnt(sb, a->btgt);
-+ if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME))
-+ == (ATTR_MODE | ATTR_CTIME)) {
-+ err = security_path_chmod(&a->h_path, ia->ia_mode);
-+ if (unlikely(err))
-+ goto out_unlock;
-+ } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID))
-+ && (ia->ia_valid & ATTR_CTIME)) {
-+ err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid);
-+ if (unlikely(err))
-+ goto out_unlock;
-+ }
++ /* rename by vfs_rename or cpup */
++ err = au_ren_or_cpup(a);
++ if (unlikely(err))
++ /* leave the copied-up one */
++ goto out_whtmp;
+
-+ if (ia->ia_valid & ATTR_SIZE) {
-+ struct file *f;
++ /* make dir opaque */
++ err = au_ren_diropq(a);
++ if (unlikely(err))
++ goto out_rename;
+
-+ if (ia->ia_size < i_size_read(inode))
-+ /* unmap only */
-+ truncate_setsize(inode, ia->ia_size);
++ /* update target timestamps */
++ if (a->exchange) {
++ AuDebugOn(au_dbtop(a->dst_dentry) != a->btgt);
++ a->h_path.dentry = au_h_dptr(a->dst_dentry, a->btgt);
++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/
++ a->dst_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime;
++ }
++ AuDebugOn(au_dbtop(a->src_dentry) != a->btgt);
++ a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt);
++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/
++ a->src_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime;
+
-+ f = NULL;
-+ if (ia->ia_valid & ATTR_FILE)
-+ f = ia->ia_file;
-+ inode_unlock(a->h_inode);
-+ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f);
-+ inode_lock_nested(a->h_inode, AuLsc_I_CHILD);
-+ } else {
-+ delegated = NULL;
-+ while (1) {
-+ err = vfsub_notify_change(&a->h_path, ia, &delegated);
-+ if (delegated) {
-+ err = break_deleg_wait(&delegated);
-+ if (!err)
-+ continue;
-+ }
-+ break;
++ 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 */
++
++ au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0);
+ }
-+ /*
-+ * 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);
++ err = 0;
++ goto out_success;
+
-+out_unlock:
-+ inode_unlock(a->h_inode);
-+ au_unpin(&a->pin);
-+ if (unlikely(err))
-+ au_update_dbtop(dentry);
-+out_dentry:
-+ di_write_unlock(dentry);
-+ if (file) {
-+ fi_write_unlock(file);
-+ ia->ia_file = file;
-+ ia->ia_valid |= ATTR_FILE;
++out_diropq:
++ au_ren_rev_diropq(err, a);
++out_rename:
++ au_ren_rev_rename(err, a);
++ dput(a->h_dst);
++out_whtmp:
++ if (a->thargs)
++ au_ren_rev_whtmp(err, a);
++out_whdst:
++ dput(a->dst_wh_dentry);
++ a->dst_wh_dentry = NULL;
++out_whsrc:
++ if (a->src_wh_dentry)
++ au_ren_rev_whsrc(err, a);
++out_success:
++ dput(a->src_wh_dentry);
++ dput(a->dst_wh_dentry);
++out_thargs:
++ if (a->thargs) {
++ dput(a->h_dst);
++ au_whtmp_rmdir_free(a->thargs);
++ a->thargs = NULL;
+ }
-+out_si:
-+ si_read_unlock(sb);
-+out_kfree:
-+ kfree(a);
+out:
-+ AuTraceErr(err);
+ return err;
+}
+
-+#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL)
-+static int au_h_path_to_set_attr(struct dentry *dentry,
-+ struct au_icpup_args *a, struct path *h_path)
++/* ---------------------------------------------------------------------- */
++
++/*
++ * test if @dentry dir can be rename destination or not.
++ * success means, it is a logically empty dir.
++ */
++static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist)
++{
++ return au_test_empty(dentry, whlist);
++}
++
++/*
++ * 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 unless DIRREN is on.
++ */
++static int may_rename_srcdir(struct au_ren_args *a)
+{
+ int err;
++ unsigned int rdhash;
++ aufs_bindex_t btop, btgt;
++ struct dentry *dentry;
+ struct super_block *sb;
++ struct au_sbinfo *sbinfo;
+
++ dentry = a->src_dentry;
+ sb = dentry->d_sb;
-+ a->udba = au_opt_udba(sb);
-+ /* no d_unlinked(), to set UDBA_NONE for root */
-+ if (d_unhashed(dentry))
-+ a->udba = AuOpt_UDBA_NONE;
-+ if (a->udba != AuOpt_UDBA_NONE) {
-+ AuDebugOn(IS_ROOT(dentry));
-+ err = au_reval_for_attr(dentry, au_sigen(sb));
++ sbinfo = au_sbi(sb);
++ if (au_opt_test(sbinfo->si_mntflags, DIRREN))
++ au_fset_ren(a->auren_flags, DIRREN);
++
++ btgt = a->btgt;
++ btop = au_dbtop(dentry);
++ if (btop != btgt) {
++ struct au_nhash whlist;
++
++ SiMustAnyLock(sb);
++ rdhash = sbinfo->si_rdhash;
++ if (!rdhash)
++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL,
++ dentry));
++ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS);
+ if (unlikely(err))
+ goto out;
-+ }
-+ err = au_pin_and_icpup(dentry, /*ia*/NULL, a);
-+ if (unlikely(err < 0))
++ err = au_test_empty(dentry, &whlist);
++ au_nhash_wh_free(&whlist);
+ goto out;
++ }
+
-+ h_path->dentry = a->h_path.dentry;
-+ h_path->mnt = au_sbr_mnt(sb, a->btgt);
++ if (btop == au_dbtaildir(dentry))
++ return 0; /* success */
++
++ err = au_test_empty_lower(dentry);
+
+out:
++ if (err == -ENOTEMPTY) {
++ 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;
+}
+
-+ssize_t au_sxattr(struct dentry *dentry, struct inode *inode,
-+ struct au_sxattr *arg)
++/* side effect: sets whlist and h_dentry */
++static int au_ren_may_dir(struct au_ren_args *a)
+{
+ int err;
-+ struct path h_path;
-+ struct super_block *sb;
-+ struct au_icpup_args *a;
-+ struct inode *h_inode;
-+
-+ IMustLock(inode);
-+
-+ err = -ENOMEM;
-+ a = kzalloc(sizeof(*a), GFP_NOFS);
-+ if (unlikely(!a))
-+ goto out;
++ unsigned int rdhash;
++ struct dentry *d;
+
-+ sb = dentry->d_sb;
-+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
-+ if (unlikely(err))
-+ goto out_kfree;
++ d = a->dst_dentry;
++ SiMustAnyLock(d->d_sb);
+
-+ h_path.dentry = NULL; /* silence gcc */
-+ di_write_lock_child(dentry);
-+ err = au_h_path_to_set_attr(dentry, a, &h_path);
-+ if (unlikely(err))
-+ goto out_di;
++ err = 0;
++ 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));
++ err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS);
++ if (unlikely(err))
++ goto out;
+
-+ 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_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);
-+ break;
++ 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);
+ }
-+ if (!err)
-+ au_cpup_attr_timesizes(inode);
-+
-+ au_unpin(&a->pin);
++ a->dst_h_dentry = au_h_dptr(d, au_dbtop(d));
+ if (unlikely(err))
-+ au_update_dbtop(dentry);
++ goto out;
+
-+out_di:
-+ di_write_unlock(dentry);
-+ si_read_unlock(sb);
-+out_kfree:
-+ kfree(a);
++ d = a->src_dentry;
++ 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;
++ }
++ }
+out:
-+ AuTraceErr(err);
+ return err;
+}
-+#endif
-+
-+static void au_refresh_iattr(struct inode *inode, struct kstat *st,
-+ unsigned int nlink)
-+{
-+ unsigned int n;
-+
-+ inode->i_mode = st->mode;
-+ /* don't i_[ug]id_write() here */
-+ inode->i_uid = st->uid;
-+ inode->i_gid = st->gid;
-+ inode->i_atime = st->atime;
-+ inode->i_mtime = st->mtime;
-+ inode->i_ctime = st->ctime;
-+
-+ au_cpup_attr_nlink(inode, /*force*/0);
-+ if (S_ISDIR(inode->i_mode)) {
-+ n = inode->i_nlink;
-+ n -= nlink;
-+ n += st->nlink;
-+ smp_mb(); /* for i_nlink */
-+ /* 0 can happen */
-+ set_nlink(inode, n);
-+ }
+
-+ spin_lock(&inode->i_lock);
-+ inode->i_blocks = st->blocks;
-+ i_size_write(inode, st->size);
-+ spin_unlock(&inode->i_lock);
-+}
++/* ---------------------------------------------------------------------- */
+
+/*
-+ * common routine for aufs_getattr() and au_getxattr().
-+ * returns zero or negative (an error).
-+ * @dentry will be read-locked in success.
++ * simple tests for rename.
++ * following the checks in vfs, plus the parent-child relationship.
+ */
-+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path,
-+ int locked)
++static int au_may_ren(struct au_ren_args *a)
+{
-+ int err;
-+ unsigned int mnt_flags, sigen;
-+ unsigned char udba_none;
-+ aufs_bindex_t bindex;
-+ struct super_block *sb, *h_sb;
-+ struct inode *inode;
-+
-+ h_path->mnt = NULL;
-+ h_path->dentry = NULL;
-+
-+ err = 0;
-+ sb = dentry->d_sb;
-+ 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);
-+ err = au_digen_test(dentry, sigen);
-+ if (!err) {
-+ di_read_lock_child(dentry, AuLock_IR);
-+ err = au_dbrange_test(dentry);
-+ if (unlikely(err)) {
-+ di_read_unlock(dentry, AuLock_IR);
-+ goto out;
-+ }
-+ } else {
-+ AuDebugOn(IS_ROOT(dentry));
-+ di_write_lock_child(dentry);
-+ err = au_dbrange_test(dentry);
-+ if (!err)
-+ err = au_reval_for_attr(dentry, sigen);
-+ if (!err)
-+ di_downgrade_lock(dentry, AuLock_IR);
-+ else {
-+ di_write_unlock(dentry);
-+ goto out;
-+ }
-+ }
-+ } else
-+ di_read_lock_child(dentry, AuLock_IR);
-+
-+body:
-+ inode = d_inode(dentry);
-+ bindex = au_ibtop(inode);
-+ h_path->mnt = au_sbr_mnt(sb, bindex);
-+ h_sb = h_path->mnt->mnt_sb;
-+ if (!force
-+ && !au_test_fs_bad_iattr(h_sb)
-+ && udba_none)
-+ goto out; /* success */
++ int err, isdir;
++ struct inode *h_inode;
+
-+ 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);
-+ if (IS_ERR(h_path->dentry))
-+ /* pretending success */
-+ h_path->dentry = NULL;
-+ else
-+ dput(h_path->dentry);
++ if (a->src_btop == a->btgt) {
++ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent,
++ au_ftest_ren(a->auren_flags, ISDIR_SRC));
++ if (unlikely(err))
++ goto out;
++ err = -EINVAL;
++ if (unlikely(a->src_h_dentry == a->h_trap))
++ goto out;
+ }
+
-+out:
-+ return err;
-+}
-+
-+static int aufs_getattr(struct vfsmount *mnt __maybe_unused,
-+ struct dentry *dentry, struct kstat *st)
-+{
-+ int err;
-+ unsigned char positive;
-+ struct path h_path;
-+ struct inode *inode;
-+ struct super_block *sb;
++ err = 0;
++ if (a->dst_btop != a->btgt)
++ goto out;
+
-+ inode = d_inode(dentry);
-+ sb = dentry->d_sb;
-+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
-+ if (unlikely(err))
++ err = -ENOTEMPTY;
++ if (unlikely(a->dst_h_dentry == a->h_trap))
+ goto out;
-+ err = au_h_path_getattr(dentry, /*force*/0, &h_path, /*locked*/0);
-+ if (unlikely(err))
-+ goto out_si;
-+ if (unlikely(!h_path.dentry))
-+ /* illegally overlapped or something */
-+ goto out_fill; /* pretending success */
+
-+ positive = d_is_positive(h_path.dentry);
-+ if (positive)
-+ err = vfs_getattr(&h_path, st);
-+ if (!err) {
-+ if (positive)
-+ au_refresh_iattr(inode, st,
-+ d_inode(h_path.dentry)->i_nlink);
-+ goto out_fill; /* success */
++ err = -EIO;
++ 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,
++ a->dst_h_parent, isdir);
++ } else {
++ if (unlikely(d_is_negative(a->dst_h_dentry)))
++ goto out;
++ h_inode = d_inode(a->dst_h_dentry);
++ if (h_inode->i_nlink)
++ err = au_may_del(a->dst_dentry, a->btgt,
++ a->dst_h_parent, isdir);
+ }
-+ AuTraceErr(err);
-+ goto out_di;
+
-+out_fill:
-+ generic_fillattr(inode, st);
-+out_di:
-+ di_read_unlock(dentry, AuLock_IR);
-+out_si:
-+ si_read_unlock(sb);
+out:
++ if (unlikely(err == -ENOENT || err == -EEXIST))
++ err = -EIO;
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
-+static const char *aufs_get_link(struct dentry *dentry, struct inode *inode,
-+ struct delayed_call *done)
++/*
++ * locking order
++ * (VFS)
++ * - src_dir and dir by lock_rename()
++ * - inode if exitsts
++ * (aufs)
++ * - lock all
++ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls,
++ * + si_read_lock
++ * + di_write_lock2_child()
++ * + di_write_lock_child()
++ * + ii_write_lock_child()
++ * + di_write_lock_child2()
++ * + ii_write_lock_child2()
++ * + src_parent and parent
++ * + di_write_lock_parent()
++ * + ii_write_lock_parent()
++ * + di_write_lock_parent2()
++ * + ii_write_lock_parent2()
++ * + lower src_dir and dir by vfsub_lock_rename()
++ * + verify the every relationships between child and parent. if any
++ * of them failed, unlock all and return -EBUSY.
++ */
++static void au_ren_unlock(struct au_ren_args *a)
++{
++ vfsub_unlock_rename(a->src_h_parent, a->src_hdir,
++ a->dst_h_parent, a->dst_hdir);
++ 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));
++}
++
++static int au_ren_lock(struct au_ren_args *a)
+{
-+ const char *ret;
-+ struct dentry *h_dentry;
-+ struct inode *h_inode;
+ int err;
-+ aufs_bindex_t bindex;
++ unsigned int udba;
+
-+ ret = NULL; /* suppress a warning */
-+ err = -ECHILD;
-+ if (!dentry)
-+ goto out;
++ err = 0;
++ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt);
++ a->src_hdir = au_hi(a->src_dir, a->btgt);
++ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt);
++ a->dst_hdir = au_hi(a->dst_dir, a->btgt);
+
-+ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN);
++ err = vfsub_mnt_want_write(au_br_mnt(a->br));
+ if (unlikely(err))
+ goto out;
++ au_fset_ren(a->auren_flags, MNT_WRITE);
++ if (au_ftest_ren(a->auren_flags, DIRREN)) {
++ struct dentry *root;
++ struct inode *dir;
+
-+ err = au_d_hashed_positive(dentry);
-+ if (unlikely(err))
-+ goto out_unlock;
-+
-+ err = -EINVAL;
-+ inode = d_inode(dentry);
-+ bindex = au_ibtop(inode);
-+ h_inode = au_h_iptr(inode, bindex);
-+ if (unlikely(!h_inode->i_op->get_link))
-+ goto out_unlock;
-+
-+ err = -EBUSY;
-+ h_dentry = NULL;
-+ if (au_dbtop(dentry) <= bindex) {
-+ h_dentry = au_h_dptr(dentry, bindex);
-+ if (h_dentry)
-+ dget(h_dentry);
-+ }
-+ if (!h_dentry) {
-+ h_dentry = d_find_any_alias(h_inode);
-+ if (IS_ERR(h_dentry)) {
-+ err = PTR_ERR(h_dentry);
-+ goto out_unlock;
++ /*
++ * 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);
+ }
+ }
-+ if (unlikely(!h_dentry))
-+ goto out_unlock;
++ 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_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_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);
++ if (!err)
++ goto out; /* success */
+
-+ err = 0;
-+ AuDbg("%pf\n", h_inode->i_op->get_link);
-+ AuDbgDentry(h_dentry);
-+ ret = vfs_get_link(h_dentry, done);
-+ dput(h_dentry);
-+ if (IS_ERR(ret))
-+ err = PTR_ERR(ret);
++ err = au_busy_or_stale();
++ au_ren_unlock(a);
+
-+out_unlock:
-+ aufs_read_unlock(dentry, AuLock_IR);
+out:
-+ if (unlikely(err))
-+ ret = ERR_PTR(err);
-+ AuTraceErrPtr(ret);
-+ return ret;
++ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
-+static int au_is_special(struct inode *inode)
++static void au_ren_refresh_dir(struct au_ren_args *a)
+{
-+ return (inode->i_mode & (S_IFBLK | S_IFCHR | S_IFIFO | S_IFSOCK));
++ struct inode *dir;
++
++ dir = a->dst_dir;
++ dir->i_version++;
++ 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 (a->exchange) {
++ dir = a->src_dir;
++ dir->i_version++;
++ if (au_ftest_ren(a->auren_flags, ISDIR_DST)) {
++ /* is this updating defined in POSIX? */
++ au_cpup_attr_timesizes(a->dst_inode);
++ au_cpup_attr_nlink(dir, /*force*/1);
++ }
++ au_dir_ts(dir, a->btgt);
++ }
++
++ if (au_ftest_ren(a->auren_flags, ISSAMEDIR))
++ return;
++
++ dir = a->src_dir;
++ dir->i_version++;
++ if (au_ftest_ren(a->auren_flags, ISDIR_SRC))
++ au_cpup_attr_nlink(dir, /*force*/1);
++ au_dir_ts(dir, a->btgt);
+}
+
-+static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags)
++static void au_ren_refresh(struct au_ren_args *a)
+{
-+ int err;
-+ aufs_bindex_t bindex;
++ aufs_bindex_t bbot, bindex;
++ struct dentry *d, *h_d;
++ struct inode *i, *h_i;
+ 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);
++ d = a->dst_dentry;
++ d_drop(d);
++ if (a->h_dst)
++ /* already dget-ed by au_ren_or_cpup() */
++ au_set_h_dptr(d, a->btgt, a->h_dst);
+
-+ /* mmap_sem might be acquired already, cf. aufs_mmap() */
-+ lockdep_off();
-+ si_read_lock(sb, AuLock_FLUSH);
-+ ii_write_lock_child(inode);
-+ lockdep_on();
++ i = a->dst_inode;
++ if (i) {
++ if (!a->exchange) {
++ if (!au_ftest_ren(a->auren_flags, ISDIR_DST))
++ vfsub_drop_nlink(i);
++ else {
++ vfsub_dead_dir(i);
++ au_cpup_attr_timesizes(i);
++ }
++ au_update_dbrange(d, /*do_put_zero*/1);
++ } else
++ au_cpup_attr_nlink(i, /*force*/1);
++ } else {
++ bbot = a->btgt;
++ for (bindex = au_dbtop(d); bindex < bbot; bindex++)
++ au_set_h_dptr(d, bindex, NULL);
++ 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);
++ }
+
-+ 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 (a->exchange
++ || au_ftest_ren(a->auren_flags, DIRREN)) {
++ d_drop(a->src_dentry);
++ if (au_ftest_ren(a->auren_flags, DIRREN))
++ au_set_dbwh(a->src_dentry, -1);
++ return;
++ }
++
++ d = a->src_dentry;
++ au_set_dbwh(d, -1);
++ bbot = au_dbbot(d);
++ 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_dbbot(d, a->btgt);
+
-+ lockdep_off();
-+ if (!err)
-+ au_cpup_attr_timesizes(inode);
-+ ii_write_unlock(inode);
-+ si_read_unlock(sb);
-+ lockdep_on();
-+
-+ if (!err && (flags & S_VERSION))
-+ inode_inc_iversion(inode);
++ sb = d->d_sb;
++ i = a->src_inode;
++ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i))
++ return; /* success */
+
-+ return err;
++ 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);
++ /* ignore this error */
++ au_set_h_iptr(i, bindex, NULL, 0);
++ }
++ }
++ au_set_ibbot(i, a->btgt);
+}
+
+/* ---------------------------------------------------------------------- */
+
-+/* no getattr version will be set by module.c:aufs_init() */
-+struct inode_operations aufs_iop_nogetattr[AuIop_Last],
-+ aufs_iop[] = {
-+ [AuIop_SYMLINK] = {
-+ .permission = aufs_permission,
-+#ifdef CONFIG_FS_POSIX_ACL
-+ .get_acl = aufs_get_acl,
-+ .set_acl = aufs_set_acl, /* unsupport for symlink? */
-+#endif
-+
-+ .setattr = aufs_setattr,
-+ .getattr = aufs_getattr,
-+
-+#ifdef CONFIG_AUFS_XATTR
-+ .listxattr = aufs_listxattr,
-+#endif
-+
-+ .readlink = generic_readlink,
-+ .get_link = aufs_get_link,
-+
-+ /* .update_time = aufs_update_time */
-+ },
-+ [AuIop_DIR] = {
-+ .create = aufs_create,
-+ .lookup = aufs_lookup,
-+ .link = aufs_link,
-+ .unlink = aufs_unlink,
-+ .symlink = aufs_symlink,
-+ .mkdir = aufs_mkdir,
-+ .rmdir = aufs_rmdir,
-+ .mknod = aufs_mknod,
-+ .rename = aufs_rename,
++/* mainly for link(2) and rename(2) */
++int au_wbr(struct dentry *dentry, aufs_bindex_t btgt)
++{
++ aufs_bindex_t bdiropq, bwh;
++ struct dentry *parent;
++ struct au_branch *br;
+
-+ .permission = aufs_permission,
-+#ifdef CONFIG_FS_POSIX_ACL
-+ .get_acl = aufs_get_acl,
-+ .set_acl = aufs_set_acl,
-+#endif
++ parent = dentry->d_parent;
++ IMustLock(d_inode(parent)); /* dir is locked */
+
-+ .setattr = aufs_setattr,
-+ .getattr = aufs_getattr,
++ bdiropq = au_dbdiropq(parent);
++ bwh = au_dbwh(dentry);
++ br = au_sbr(dentry->d_sb, btgt);
++ if (au_br_rdonly(br)
++ || (0 <= bdiropq && bdiropq < btgt)
++ || (0 <= bwh && bwh < btgt))
++ btgt = -1;
+
-+#ifdef CONFIG_AUFS_XATTR
-+ .listxattr = aufs_listxattr,
-+#endif
++ AuDbg("btgt %d\n", btgt);
++ return btgt;
++}
+
-+ .update_time = aufs_update_time,
-+ .atomic_open = aufs_atomic_open,
-+ .tmpfile = aufs_tmpfile
-+ },
-+ [AuIop_OTHER] = {
-+ .permission = aufs_permission,
-+#ifdef CONFIG_FS_POSIX_ACL
-+ .get_acl = aufs_get_acl,
-+ .set_acl = aufs_set_acl,
-+#endif
++/* sets src_btop, dst_btop and btgt */
++static int au_ren_wbr(struct au_ren_args *a)
++{
++ int err;
++ struct au_wr_dir_args wr_dir_args = {
++ /* .force_btgt = -1, */
++ .flags = AuWrDir_ADD_ENTRY
++ };
+
-+ .setattr = aufs_setattr,
-+ .getattr = aufs_getattr,
++ 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_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);
+
-+#ifdef CONFIG_AUFS_XATTR
-+ .listxattr = aufs_listxattr,
-+#endif
++ return err;
++}
+
-+ .update_time = aufs_update_time
++static void au_ren_dt(struct au_ren_args *a)
++{
++ 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->auren_flags, ISSAMEDIR)) {
++ a->h_path.dentry = a->dst_h_parent;
++ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path);
+ }
-+};
-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 2018-04-15 08:49:13.401150731 +0200
-@@ -0,0 +1,511 @@
-+/*
-+ * 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
-+ * 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 <http://www.gnu.org/licenses/>.
-+ */
+
-+/*
-+ * inode operations (del entry)
-+ */
++ au_fclr_ren(a->auren_flags, DT_DSTDIR);
++ if (!au_ftest_ren(a->auren_flags, ISDIR_SRC)
++ && !a->exchange)
++ return;
+
-+#include "aufs.h"
++ 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->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);
++ }
++}
+
-+/*
-+ * decide if a new whiteout for @dentry is necessary or not.
-+ * when it is necessary, prepare the parent dir for the upper branch whose
-+ * branch index is @bcpup for creation. the actual creation of the whiteout will
-+ * be done by caller.
-+ * return value:
-+ * 0: wh is unnecessary
-+ * plus: wh is necessary
-+ * minus: error
-+ */
-+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup)
++static void au_ren_rev_dt(int err, struct au_ren_args *a)
+{
-+ int need_wh, err;
-+ aufs_bindex_t btop;
-+ struct super_block *sb;
++ struct dentry *h_d;
++ struct inode *h_inode;
+
-+ sb = dentry->d_sb;
-+ btop = au_dbtop(dentry);
-+ if (*bcpup < 0) {
-+ *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(btop < *bcpup
-+ || au_test_ro(sb, *bcpup, d_inode(dentry)));
-+ AuDbg("bcpup %d, btop %d\n", *bcpup, btop);
++ au_dtime_revert(a->src_dt + AuPARENT);
++ if (!au_ftest_ren(a->auren_flags, ISSAMEDIR))
++ au_dtime_revert(a->dst_dt + AuPARENT);
+
-+ if (*bcpup != btop) {
-+ err = au_cpup_dirs(dentry, *bcpup);
-+ if (unlikely(err))
-+ goto out;
-+ need_wh = 1;
-+ } else {
-+ struct au_dinfo *dinfo, *tmp;
++ if (au_ftest_ren(a->auren_flags, ISDIR_SRC) && err != -EIO) {
++ h_d = a->src_dt[AuCHILD].dt_h_path.dentry;
++ h_inode = d_inode(h_d);
++ inode_lock_nested(h_inode, AuLsc_I_CHILD);
++ au_dtime_revert(a->src_dt + AuCHILD);
++ inode_unlock(h_inode);
+
-+ need_wh = -ENOMEM;
-+ dinfo = au_di(dentry);
-+ tmp = au_di_alloc(sb, AuLsc_DI_TMP);
-+ if (tmp) {
-+ au_di_cp(tmp, dinfo);
-+ au_di_swap(tmp, dinfo);
-+ /* returns the number of positive dentries */
-+ need_wh = au_lkup_dentry(dentry, btop + 1,
-+ /* AuLkup_IGNORE_PERM */ 0);
-+ au_di_swap(tmp, dinfo);
-+ au_rw_write_unlock(&tmp->di_rwsem);
-+ au_di_free(tmp);
++ if (au_ftest_ren(a->auren_flags, DT_DSTDIR)) {
++ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry;
++ h_inode = d_inode(h_d);
++ inode_lock_nested(h_inode, AuLsc_I_CHILD);
++ au_dtime_revert(a->dst_dt + AuCHILD);
++ inode_unlock(h_inode);
+ }
+ }
-+ AuDbg("need_wh %d\n", need_wh);
-+ err = need_wh;
-+
-+out:
-+ return err;
+}
+
-+/*
-+ * simple tests for the del-entry operations.
-+ * following the checks in vfs, plus the parent-child relationship.
-+ */
-+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
-+ struct dentry *h_parent, int isdir)
++/* ---------------------------------------------------------------------- */
++
++int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
++ struct inode *_dst_dir, struct dentry *_dst_dentry,
++ unsigned int _flags)
+{
-+ int err;
-+ umode_t h_mode;
-+ struct dentry *h_dentry, *h_latest;
-+ struct inode *h_inode;
++ int err, lock_flags;
++ void *rev;
++ /* reduce stack space */
++ struct au_ren_args *a;
++ struct au_pin pin;
+
-+ h_dentry = au_h_dptr(dentry, bindex);
-+ if (d_really_is_positive(dentry)) {
-+ err = -ENOENT;
-+ if (unlikely(d_is_negative(h_dentry)))
-+ goto out;
-+ h_inode = d_inode(h_dentry);
-+ if (unlikely(!h_inode->i_nlink))
-+ goto out;
++ AuDbg("%pd, %pd, 0x%x\n", _src_dentry, _dst_dentry, _flags);
++ IMustLock(_src_dir);
++ IMustLock(_dst_dir);
+
-+ h_mode = h_inode->i_mode;
-+ if (!isdir) {
-+ err = -EISDIR;
-+ if (unlikely(S_ISDIR(h_mode)))
-+ goto out;
-+ } else if (unlikely(!S_ISDIR(h_mode))) {
-+ err = -ENOTDIR;
-+ goto out;
-+ }
-+ } else {
-+ /* rename(2) case */
-+ err = -EIO;
-+ if (unlikely(d_is_positive(h_dentry)))
-+ goto out;
++ err = -EINVAL;
++ if (unlikely(_flags & RENAME_WHITEOUT))
++ goto out;
++
++ err = -ENOMEM;
++ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE);
++ a = kzalloc(sizeof(*a), GFP_NOFS);
++ if (unlikely(!a))
++ goto out;
++
++ a->flags = _flags;
++ a->exchange = _flags & RENAME_EXCHANGE;
++ a->src_dir = _src_dir;
++ a->src_dentry = _src_dentry;
++ a->src_inode = NULL;
++ if (d_really_is_positive(a->src_dentry))
++ a->src_inode = d_inode(a->src_dentry);
++ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */
++ a->dst_dir = _dst_dir;
++ a->dst_dentry = _dst_dentry;
++ a->dst_inode = NULL;
++ if (d_really_is_positive(a->dst_dentry))
++ a->dst_inode = d_inode(a->dst_dentry);
++ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */
++ if (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;
++ lock_flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN;
++ if (d_is_dir(a->src_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;
++ 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,
++ lock_flags);
++ if (unlikely(err))
++ goto out_free;
+
++ err = au_d_hashed_positive(a->src_dentry);
++ if (unlikely(err))
++ goto out_unlock;
+ err = -ENOENT;
-+ /* expected parent dir is locked */
-+ if (unlikely(h_parent != h_dentry->d_parent))
-+ goto out;
-+ err = 0;
++ if (a->dst_inode) {
++ /*
++ * 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 (!au_ftest_ren(a->auren_flags, ISDIR_DST)) {
++ err = au_d_hashed_positive(a->dst_dentry);
++ if (unlikely(err && !a->exchange))
++ goto out_unlock;
++ } else if (unlikely(IS_DEADDIR(a->dst_inode)))
++ goto out_unlock;
++ } else if (unlikely(d_unhashed(a->dst_dentry)))
++ goto out_unlock;
+
+ /*
-+ * rmdir a dir may break the consistency on some filesystem.
-+ * let's try heavy test.
++ * is it possible?
++ * yes, it happened (in linux-3.3-rcN) but I don't know why.
++ * there may exist a problem somewhere else.
+ */
-+ err = -EACCES;
-+ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)
-+ && au_test_h_perm(d_inode(h_parent),
-+ MAY_EXEC | MAY_WRITE)))
-+ goto out;
++ err = -EINVAL;
++ if (unlikely(d_inode(a->dst_parent) == d_inode(a->src_dentry)))
++ goto out_unlock;
+
-+ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent);
-+ err = -EIO;
-+ if (IS_ERR(h_latest))
-+ goto out;
-+ if (h_latest == h_dentry)
-+ err = 0;
-+ dput(h_latest);
++ au_fset_ren(a->auren_flags, ISSAMEDIR); /* temporary */
++ di_write_lock_parent(a->dst_parent);
+
-+out:
-+ return err;
-+}
++ /* which branch we process */
++ err = au_ren_wbr(a);
++ if (unlikely(err < 0))
++ goto out_parent;
++ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt);
++ a->h_path.mnt = au_br_mnt(a->br);
+
-+/*
-+ * decide the branch where we operate for @dentry. the branch index will be set
-+ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent
-+ * dir for reverting.
-+ * when a new whiteout is necessary, create it.
-+ */
-+static struct dentry*
-+lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup,
-+ struct au_dtime *dt, struct au_pin *pin)
-+{
-+ struct dentry *wh_dentry;
-+ struct super_block *sb;
-+ struct path h_path;
-+ int err, need_wh;
-+ unsigned int udba;
-+ aufs_bindex_t bcpup;
++ /* are they available to be renamed */
++ err = au_ren_may_dir(a);
++ if (unlikely(err))
++ goto out_children;
+
-+ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup);
-+ wh_dentry = ERR_PTR(need_wh);
-+ if (unlikely(need_wh < 0))
-+ goto out;
++ /* prepare the writable parent dir on the same branch */
++ 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;
++ }
+
-+ sb = dentry->d_sb;
-+ udba = au_opt_udba(sb);
-+ bcpup = *rbcpup;
-+ err = au_pin(pin, dentry, bcpup, udba,
-+ AuPin_DI_LOCKED | AuPin_MNT_WRITE);
-+ wh_dentry = ERR_PTR(err);
++ 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->auren_flags, WHSRC);
++
++ /* cpup src */
++ 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);
++ if (!err) {
++ struct au_cp_generic cpg = {
++ .dentry = a->src_dentry,
++ .bdst = a->btgt,
++ .bsrc = a->src_btop,
++ .len = -1,
++ .pin = &pin,
++ .flags = AuCpup_DTIME | AuCpup_HOPEN
++ };
++ 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_btop = a->btgt;
++ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt);
++ 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 */
++ err = au_ren_lock(a);
+ if (unlikely(err))
-+ goto out;
++ /* leave the copied-up one */
++ goto out_children;
+
-+ h_path.dentry = au_pinned_h_parent(pin);
-+ if (udba != AuOpt_UDBA_NONE
-+ && au_dbtop(dentry) == bcpup) {
-+ err = au_may_del(dentry, bcpup, h_path.dentry, isdir);
-+ wh_dentry = ERR_PTR(err);
++ 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_unpin;
++ goto out_hdir;
+ }
+
-+ h_path.mnt = au_sbr_mnt(sb, bcpup);
-+ au_dtime_store(dt, au_pinned_parent(pin), &h_path);
-+ wh_dentry = NULL;
-+ if (!need_wh)
-+ goto out; /* success, no need to create whiteout */
++ /* store timestamps to be revertible */
++ au_ren_dt(a);
+
-+ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry);
-+ if (IS_ERR(wh_dentry))
-+ goto out_unpin;
++ /* 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;
++ }
+
-+ /* returns with the parent is locked and wh_dentry is dget-ed */
-+ goto out; /* success */
++ /* here we go */
++ err = do_rename(a);
++ if (unlikely(err))
++ goto out_dirren;
+
-+out_unpin:
-+ au_unpin(pin);
-+out:
-+ return wh_dentry;
-+}
++ if (au_ftest_ren(a->auren_flags, DIRREN))
++ au_dr_rename_fin(a->src_dentry, a->btgt, rev);
+
-+/*
-+ * when removing a dir, rename it to a unique temporary whiteout-ed name first
-+ * in order to be revertible and save time for removing many child whiteouts
-+ * under the dir.
-+ * returns 1 when there are too many child whiteout and caller should remove
-+ * them asynchronously. returns 0 when the number of children is enough small to
-+ * remove now or the branch fs is a remote fs.
-+ * otherwise return an error.
-+ */
-+static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,
-+ struct au_nhash *whlist, struct inode *dir)
-+{
-+ int rmdir_later, err, dirwh;
-+ struct dentry *h_dentry;
-+ struct super_block *sb;
-+ struct inode *inode;
++ /* update dir attributes */
++ au_ren_refresh_dir(a);
+
-+ sb = dentry->d_sb;
-+ SiMustAnyLock(sb);
-+ h_dentry = au_h_dptr(dentry, bindex);
-+ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex));
-+ if (unlikely(err))
-+ goto out;
++ /* dput/iput all lower dentries */
++ au_ren_refresh(a);
+
-+ /* stop monitoring */
-+ inode = d_inode(dentry);
-+ au_hn_free(au_hi(inode, bindex));
++ goto out_hdir; /* success */
+
-+ if (!au_test_fs_remote(h_dentry->d_sb)) {
-+ dirwh = au_sbi(sb)->si_dirwh;
-+ rmdir_later = (dirwh <= 1);
-+ if (!rmdir_later)
-+ rmdir_later = au_nhash_test_longer_wh(whlist, bindex,
-+ dirwh);
-+ if (rmdir_later)
-+ return rmdir_later;
++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_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_dbtop(a->dst_dentry, a->dst_btop);
+ }
-+
-+ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist);
-+ if (unlikely(err)) {
-+ AuIOErr("rmdir %pd, b%d failed, %d. ignored\n",
-+ h_dentry, bindex, err);
-+ err = 0;
++out_parent:
++ 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->auren_flags, ISSAMEDIR))
++ di_write_unlock(a->dst_parent);
++ else
++ di_write_unlock2(a->src_parent, a->dst_parent);
++out_unlock:
++ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry);
++out_free:
++ iput(a->dst_inode);
++ if (a->thargs)
++ au_whtmp_rmdir_free(a->thargs);
++ kfree(a);
+out:
+ AuTraceErr(err);
+ return err;
+}
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/iinfo.c linux-4.9/fs/aufs/iinfo.c
+--- linux-4.9/fs/aufs/iinfo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/iinfo.c 2021-02-24 16:15:09.528240413 +0100
+@@ -0,0 +1,285 @@
++/*
++ * 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
++ * 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 <http://www.gnu.org/licenses/>.
++ */
+
+/*
-+ * final procedure for deleting a entry.
-+ * maintain dentry and iattr.
++ * inode private data
+ */
-+static void epilog(struct inode *dir, struct dentry *dentry,
-+ aufs_bindex_t bindex)
++
++#include "aufs.h"
++
++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex)
+{
-+ struct inode *inode;
++ struct inode *h_inode;
++ struct au_hinode *hinode;
+
-+ inode = d_inode(dentry);
-+ d_drop(dentry);
-+ inode->i_ctime = dir->i_ctime;
++ IiMustAnyLock(inode);
+
-+ au_dir_ts(dir, bindex);
-+ dir->i_version++;
++ 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;
+}
+
-+/*
-+ * when an error happened, remove the created whiteout and revert everything.
-+ */
-+static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex,
-+ aufs_bindex_t bwh, struct dentry *wh_dentry,
-+ struct dentry *dentry, struct au_dtime *dt)
++/* todo: hard/soft set? */
++void au_hiput(struct au_hinode *hinode)
+{
-+ int rerr;
-+ struct path h_path = {
-+ .dentry = wh_dentry,
-+ .mnt = au_sbr_mnt(dir->i_sb, bindex)
-+ };
++ au_hn_free(hinode);
++ dput(hinode->hi_whdentry);
++ iput(hinode->hi_inode);
++}
+
-+ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry);
-+ if (!rerr) {
-+ au_set_dbwh(dentry, bwh);
-+ au_dtime_revert(dt);
-+ return 0;
-+ }
++unsigned int au_hi_flags(struct inode *inode, int isdir)
++{
++ unsigned int flags;
++ const unsigned int mnt_flags = au_mntflags(inode->i_sb);
+
-+ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr);
-+ return -EIO;
++ flags = 0;
++ if (au_opt_test(mnt_flags, XINO))
++ au_fset_hi(flags, XINO);
++ if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY))
++ au_fset_hi(flags, HNOTIFY);
++ return flags;
+}
+
-+/* ---------------------------------------------------------------------- */
-+
-+int aufs_unlink(struct inode *dir, struct dentry *dentry)
++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
++ struct inode *h_inode, unsigned int flags)
+{
-+ int err;
-+ aufs_bindex_t bwh, bindex, btop;
-+ struct inode *inode, *h_dir, *delegated;
-+ struct dentry *parent, *wh_dentry;
-+ /* to reuduce stack size */
-+ struct {
-+ struct au_dtime dt;
-+ struct au_pin pin;
-+ struct path h_path;
-+ } *a;
++ struct au_hinode *hinode;
++ struct inode *hi;
++ struct au_iinfo *iinfo = au_ii(inode);
+
-+ IMustLock(dir);
++ IiMustWriteLock(inode);
+
-+ err = -ENOMEM;
-+ a = kmalloc(sizeof(*a), GFP_NOFS);
-+ if (unlikely(!a))
-+ goto out;
++ hinode = au_hinode(iinfo, bindex);
++ hi = hinode->hi_inode;
++ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0);
+
-+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
-+ if (unlikely(err))
-+ goto out_free;
-+ err = au_d_hashed_positive(dentry);
-+ if (unlikely(err))
-+ goto out_unlock;
-+ inode = d_inode(dentry);
-+ IMustLock(inode);
-+ err = -EISDIR;
-+ if (unlikely(d_is_dir(dentry)))
-+ goto out_unlock; /* possible? */
++ if (hi)
++ au_hiput(hinode);
++ hinode->hi_inode = h_inode;
++ if (h_inode) {
++ int err;
++ struct super_block *sb = inode->i_sb;
++ struct au_branch *br;
+
-+ btop = au_dbtop(dentry);
-+ bwh = au_dbwh(dentry);
-+ bindex = -1;
-+ parent = dentry->d_parent; /* dir inode is locked */
-+ di_write_lock_parent(parent);
-+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt,
-+ &a->pin);
-+ err = PTR_ERR(wh_dentry);
-+ if (IS_ERR(wh_dentry))
-+ goto out_parent;
++ AuDebugOn(inode->i_mode
++ && (h_inode->i_mode & S_IFMT)
++ != (inode->i_mode & S_IFMT));
++ if (bindex == iinfo->ii_btop)
++ au_cpup_igen(inode, h_inode);
++ br = au_sbr(sb, bindex);
++ hinode->hi_id = br->br_id;
++ if (au_ftest_hi(flags, XINO)) {
++ err = au_xino_write(sb, bindex, h_inode->i_ino,
++ inode->i_ino);
++ if (unlikely(err))
++ AuIOErr1("failed au_xino_write() %d\n", err);
++ }
+
-+ 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 == btop) {
-+ h_dir = au_pinned_h_dir(&a->pin);
-+ delegated = NULL;
-+ err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0);
-+ if (unlikely(err == -EWOULDBLOCK)) {
-+ pr_warn("cannot retry for NFSv4 delegation"
-+ " for an internal unlink\n");
-+ iput(delegated);
++ if (au_ftest_hi(flags, HNOTIFY)
++ && au_br_hnotifyable(br->br_perm)) {
++ err = au_hn_alloc(hinode, inode);
++ if (unlikely(err))
++ AuIOErr1("au_hn_alloc() %d\n", err);
+ }
-+ } else {
-+ /* dir inode is locked */
-+ h_dir = d_inode(wh_dentry->d_parent);
-+ IMustLock(h_dir);
-+ err = 0;
+ }
++}
+
-+ if (!err) {
-+ vfsub_drop_nlink(inode);
-+ epilog(dir, dentry, bindex);
++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
++ struct dentry *h_wh)
++{
++ struct au_hinode *hinode;
+
-+ /* update target timestamps */
-+ if (bindex == btop) {
-+ vfsub_update_h_iattr(&a->h_path, /*did*/NULL);
-+ /*ignore*/
-+ inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime;
-+ } else
-+ /* todo: this timestamp may be reverted later */
-+ inode->i_ctime = h_dir->i_ctime;
-+ goto out_unpin; /* success */
-+ }
++ IiMustWriteLock(inode);
+
-+ /* revert */
-+ if (wh_dentry) {
-+ int rerr;
++ hinode = au_hinode(au_ii(inode), bindex);
++ AuDebugOn(hinode->hi_whdentry);
++ hinode->hi_whdentry = h_wh;
++}
+
-+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry,
-+ &a->dt);
-+ if (rerr)
-+ err = rerr;
++void au_update_iigen(struct inode *inode, int half)
++{
++ struct au_iinfo *iinfo;
++ struct au_iigen *iigen;
++ unsigned int sigen;
++
++ sigen = au_sigen(inode->i_sb);
++ iinfo = au_ii(inode);
++ iigen = &iinfo->ii_generation;
++ spin_lock(&iigen->ig_spin);
++ iigen->ig_generation = sigen;
++ if (half)
++ au_ig_fset(iigen->ig_flags, HALF_REFRESHED);
++ else
++ au_ig_fclr(iigen->ig_flags, HALF_REFRESHED);
++ spin_unlock(&iigen->ig_spin);
++}
++
++/* it may be called at remount time, too */
++void au_update_ibrange(struct inode *inode, int do_put_zero)
++{
++ struct au_iinfo *iinfo;
++ aufs_bindex_t bindex, bbot;
++
++ AuDebugOn(au_is_bad_inode(inode));
++ IiMustWriteLock(inode);
++
++ 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 = au_hinode(iinfo, bindex)->hi_inode;
++ if (h_i
++ && !h_i->i_nlink
++ && !(h_i->i_state & I_LINKABLE))
++ au_set_h_iptr(inode, bindex, NULL, 0);
++ }
+ }
+
-+out_unpin:
-+ au_unpin(&a->pin);
-+ dput(wh_dentry);
-+ dput(a->h_path.dentry);
-+out_parent:
-+ di_write_unlock(parent);
-+out_unlock:
-+ aufs_read_unlock(dentry, AuLock_DW);
-+out_free:
-+ kfree(a);
-+out:
-+ return err;
++ 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_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_btop > iinfo->ii_bbot);
+}
+
-+int aufs_rmdir(struct inode *dir, struct dentry *dentry)
++/* ---------------------------------------------------------------------- */
++
++void au_icntnr_init_once(void *_c)
+{
-+ int err, rmdir_later;
-+ 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 */
-+ struct {
-+ struct au_dtime dt;
-+ struct au_pin pin;
-+ } *a;
++ struct au_icntnr *c = _c;
++ struct au_iinfo *iinfo = &c->iinfo;
+
-+ IMustLock(dir);
++ spin_lock_init(&iinfo->ii_generation.ig_spin);
++ au_rw_init(&iinfo->ii_rwsem);
++ inode_init_once(&c->vfs_inode);
++}
+
-+ err = -ENOMEM;
-+ a = kmalloc(sizeof(*a), GFP_NOFS);
-+ if (unlikely(!a))
-+ goto out;
++void au_hinode_init(struct au_hinode *hinode)
++{
++ hinode->hi_inode = NULL;
++ hinode->hi_id = -1;
++ au_hn_init(hinode);
++ hinode->hi_whdentry = NULL;
++}
+
-+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN);
-+ if (unlikely(err))
-+ goto out_free;
-+ err = au_alive_dir(dentry);
-+ if (unlikely(err))
-+ goto out_unlock;
-+ inode = d_inode(dentry);
-+ IMustLock(inode);
-+ err = -ENOTDIR;
-+ if (unlikely(!d_is_dir(dentry)))
-+ goto out_unlock; /* possible? */
++int au_iinfo_init(struct inode *inode)
++{
++ struct au_iinfo *iinfo;
++ struct super_block *sb;
++ struct au_hinode *hi;
++ int nbr, i;
+
-+ err = -ENOMEM;
-+ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS);
-+ if (unlikely(!args))
-+ goto out_unlock;
++ sb = inode->i_sb;
++ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);
++ nbr = au_sbbot(sb) + 1;
++ if (unlikely(nbr <= 0))
++ nbr = 1;
++ hi = kmalloc_array(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS);
++ if (hi) {
++ au_ninodes_inc(sb);
+
-+ parent = dentry->d_parent; /* dir inode is locked */
-+ di_write_lock_parent(parent);
-+ err = au_test_empty(dentry, &args->whlist);
-+ if (unlikely(err))
-+ goto out_parent;
++ iinfo->ii_hinode = hi;
++ for (i = 0; i < nbr; i++, hi++)
++ au_hinode_init(hi);
+
-+ btop = au_dbtop(dentry);
-+ bwh = au_dbwh(dentry);
-+ bindex = -1;
-+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt,
-+ &a->pin);
-+ err = PTR_ERR(wh_dentry);
-+ if (IS_ERR(wh_dentry))
-+ goto out_parent;
++ iinfo->ii_generation.ig_generation = au_sigen(sb);
++ iinfo->ii_btop = -1;
++ iinfo->ii_bbot = -1;
++ iinfo->ii_vdir = NULL;
++ return 0;
++ }
++ return -ENOMEM;
++}
+
-+ h_dentry = au_h_dptr(dentry, btop);
-+ dget(h_dentry);
-+ rmdir_later = 0;
-+ 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, btop));
++int au_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink)
++{
++ int err, i;
++ struct au_hinode *hip;
+
-+ /* dir inode is locked */
-+ IMustLock(d_inode(wh_dentry->d_parent));
++ AuRwMustWriteLock(&iinfo->ii_rwsem);
++
++ err = -ENOMEM;
++ 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;
+ }
+
-+ if (!err) {
-+ vfsub_dead_dir(inode);
-+ au_set_dbdiropq(dentry, -1);
-+ epilog(dir, dentry, bindex);
++ return err;
++}
++
++void au_iinfo_fin(struct inode *inode)
++{
++ struct au_iinfo *iinfo;
++ struct au_hinode *hi;
++ struct super_block *sb;
++ aufs_bindex_t bindex, bbot;
++ const unsigned char unlinked = !inode->i_nlink;
+
-+ if (rmdir_later) {
-+ au_whtmp_kick_rmdir(dir, btop, h_dentry, args);
-+ args = NULL;
-+ }
++ AuDebugOn(au_is_bad_inode(inode));
+
-+ goto out_unpin; /* success */
++ sb = inode->i_sb;
++ au_ninodes_dec(sb);
++ if (si_pid_test(sb))
++ au_xino_delete_inode(inode, unlinked);
++ else {
++ /*
++ * it is safe to hide the dependency between sbinfo and
++ * sb->s_umount.
++ */
++ lockdep_off();
++ si_noflush_read_lock(sb);
++ au_xino_delete_inode(inode, unlinked);
++ si_read_unlock(sb);
++ lockdep_on();
+ }
+
-+ /* revert */
-+ AuLabel(revert);
-+ if (wh_dentry) {
-+ int rerr;
++ iinfo = au_ii(inode);
++ if (iinfo->ii_vdir)
++ au_vdir_free(iinfo->ii_vdir);
+
-+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry,
-+ &a->dt);
-+ if (rerr)
-+ err = rerr;
++ bindex = iinfo->ii_btop;
++ if (bindex >= 0) {
++ hi = au_hinode(iinfo, bindex);
++ bbot = iinfo->ii_bbot;
++ while (bindex++ <= bbot) {
++ if (hi->hi_inode)
++ au_hiput(hi);
++ hi++;
++ }
+ }
-+
-+out_unpin:
-+ au_unpin(&a->pin);
-+ dput(wh_dentry);
-+ dput(h_dentry);
-+out_parent:
-+ di_write_unlock(parent);
-+ if (args)
-+ au_whtmp_rmdir_free(args);
-+out_unlock:
-+ aufs_read_unlock(dentry, AuLock_DW);
-+out_free:
-+ kfree(a);
-+out:
-+ AuTraceErr(err);
-+ return err;
++ kfree(iinfo->ii_hinode);
++ AuRwDestroy(&iinfo->ii_rwsem);
+}
-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 2018-04-15 08:49:13.401150731 +0200
-@@ -0,0 +1,1246 @@
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/inode.c linux-4.9/fs/aufs/inode.c
+--- linux-4.9/fs/aufs/inode.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/inode.c 2021-02-24 16:15:09.528240413 +0100
+@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ *
+ */
+
+/*
-+ * inode operation (rename entry)
-+ * todo: this is crazy monster
++ * inode functions
+ */
+
+#include "aufs.h"
+
-+enum { AuSRC, AuDST, AuSrcDst };
-+enum { AuPARENT, AuCHILD, AuParentChild };
-+
-+#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, *hinode;
-+ struct au_dtime dt[AuParentChild];
-+ aufs_bindex_t btop, bdiropq;
-+ } sd[AuSrcDst];
-+
-+#define src_dentry sd[AuSRC].dentry
-+#define src_dir sd[AuSRC].dir
-+#define src_inode sd[AuSRC].inode
-+#define src_h_dentry sd[AuSRC].h_dentry
-+#define src_parent sd[AuSRC].parent
-+#define src_h_parent sd[AuSRC].h_parent
-+#define src_wh_dentry sd[AuSRC].wh_dentry
-+#define src_hdir sd[AuSRC].hdir
-+#define src_hinode sd[AuSRC].hinode
-+#define src_h_dir sd[AuSRC].hdir->hi_inode
-+#define src_dt sd[AuSRC].dt
-+#define src_btop sd[AuSRC].btop
-+#define src_bdiropq sd[AuSRC].bdiropq
-+
-+#define dst_dentry sd[AuDST].dentry
-+#define dst_dir sd[AuDST].dir
-+#define dst_inode sd[AuDST].inode
-+#define dst_h_dentry sd[AuDST].h_dentry
-+#define dst_parent sd[AuDST].parent
-+#define dst_h_parent sd[AuDST].h_parent
-+#define dst_wh_dentry sd[AuDST].wh_dentry
-+#define dst_hdir sd[AuDST].hdir
-+#define dst_hinode sd[AuDST].hinode
-+#define dst_h_dir sd[AuDST].hdir->hi_inode
-+#define dst_dt sd[AuDST].dt
-+#define dst_btop sd[AuDST].btop
-+#define dst_bdiropq sd[AuDST].bdiropq
-+
-+ struct dentry *h_trap;
-+ struct au_branch *br;
-+ struct path h_path;
-+ struct au_nhash whlist;
-+ aufs_bindex_t btgt, src_bwh;
++struct inode *au_igrab(struct inode *inode)
++{
++ if (inode) {
++ AuDebugOn(!atomic_read(&inode->i_count));
++ ihold(inode);
++ }
++ return inode;
++}
+
-+ struct {
-+ unsigned short auren_flags;
-+ unsigned char flags; /* syscall parameter */
-+ unsigned char exchange;
-+ } __packed;
++static void au_refresh_hinode_attr(struct inode *inode, int do_version)
++{
++ au_cpup_attr_all(inode, /*force*/0);
++ au_update_iigen(inode, /*half*/1);
++ if (do_version)
++ inode->i_version++;
++}
+
-+ struct au_whtmp_rmdir *thargs;
-+ struct dentry *h_dst;
-+ struct au_hinode *h_root;
-+};
++static int au_ii_refresh(struct inode *inode, int *update)
++{
++ 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);
+
-+/*
-+ * functions for reverting.
-+ * when an error happened in a single rename systemcall, we should revert
-+ * everything as if nothing happened.
-+ * we don't need to revert the copied-up/down the parent dir since they are
-+ * harmless.
-+ */
++ *update = 0;
++ sb = inode->i_sb;
++ nbr = au_sbbot(sb) + 1;
++ type = inode->i_mode & S_IFMT;
++ iinfo = au_ii(inode);
++ err = au_hinode_realloc(iinfo, nbr, /*may_shrink*/0);
++ if (unlikely(err))
++ goto out;
+
-+#define RevertFailure(fmt, ...) do { \
-+ AuIOErr("revert failure: " fmt " (%d, %d)\n", \
-+ ##__VA_ARGS__, err, rerr); \
-+ err = -EIO; \
-+} while (0)
++ 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;
+
-+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
++ AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT));
++ new_bindex = au_br_index(sb, p->hi_id);
++ if (new_bindex == bindex)
++ continue;
+
-+ 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", d);
++ if (new_bindex < 0) {
++ *update = 1;
++ au_hiput(p);
++ p->hi_inode = NULL;
++ continue;
++ }
+
-+#undef src_or_dst_
-+}
++ 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 = au_hinode(iinfo, new_bindex);
++ tmp = *q;
++ *q = *p;
++ *p = tmp;
++ if (tmp.hi_inode) {
++ bindex--;
++ p--;
++ }
++ }
++ 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;
+
-+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);
++out:
++ AuTraceErr(err);
++ return err;
+}
+
-+static void au_ren_rev_rename(int err, struct au_ren_args *a)
++void au_refresh_iop(struct inode *inode, int force_getattr)
+{
-+ int rerr;
-+ struct inode *delegated;
++ int type;
++ struct au_sbinfo *sbi = au_sbi(inode->i_sb);
++ const struct inode_operations *iop
++ = force_getattr ? aufs_iop : sbi->si_iop_array;
+
-+ a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name,
-+ a->src_h_parent);
-+ rerr = PTR_ERR(a->h_path.dentry);
-+ if (IS_ERR(a->h_path.dentry)) {
-+ RevertFailure("lkup one %pd", a->src_dentry);
++ if (inode->i_op == iop)
+ return;
-+ }
+
-+ 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->flags);
-+ if (unlikely(rerr == -EWOULDBLOCK)) {
-+ pr_warn("cannot retry for NFSv4 delegation"
-+ " for an internal rename\n");
-+ iput(delegated);
++ switch (inode->i_mode & S_IFMT) {
++ case S_IFDIR:
++ type = AuIop_DIR;
++ break;
++ case S_IFLNK:
++ type = AuIop_SYMLINK;
++ break;
++ default:
++ type = AuIop_OTHER;
++ break;
+ }
-+ d_drop(a->h_path.dentry);
-+ dput(a->h_path.dentry);
-+ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */
-+ if (rerr)
-+ RevertFailure("rename %pd", a->src_dentry);
++
++ inode->i_op = iop + type;
++ /* unnecessary smp_wmb() */
+}
+
-+static void au_ren_rev_whtmp(int err, struct au_ren_args *a)
++int au_refresh_hinode_self(struct inode *inode)
+{
-+ int rerr;
-+ struct inode *delegated;
++ int err, update;
+
-+ a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name,
-+ a->dst_h_parent);
-+ rerr = PTR_ERR(a->h_path.dentry);
-+ if (IS_ERR(a->h_path.dentry)) {
-+ RevertFailure("lkup one %pd", a->dst_dentry);
-+ return;
-+ }
-+ if (d_is_positive(a->h_path.dentry)) {
-+ d_drop(a->h_path.dentry);
-+ dput(a->h_path.dentry);
-+ return;
-+ }
++ err = au_ii_refresh(inode, &update);
++ if (!err)
++ au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode));
+
-+ delegated = NULL;
-+ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path,
-+ &delegated, a->flags);
-+ if (unlikely(rerr == -EWOULDBLOCK)) {
-+ pr_warn("cannot retry for NFSv4 delegation"
-+ " for an internal rename\n");
-+ iput(delegated);
-+ }
-+ d_drop(a->h_path.dentry);
-+ dput(a->h_path.dentry);
-+ if (!rerr)
-+ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst));
-+ else
-+ RevertFailure("rename %pd", a->h_dst);
++ AuTraceErr(err);
++ return err;
+}
+
-+static void au_ren_rev_whsrc(int err, struct au_ren_args *a)
++int au_refresh_hinode(struct inode *inode, struct dentry *dentry)
+{
-+ int rerr;
++ int err, e, update;
++ unsigned int flags;
++ umode_t mode;
++ aufs_bindex_t bindex, bbot;
++ unsigned char isdir;
++ struct au_hinode *p;
++ struct au_iinfo *iinfo;
+
-+ a->h_path.dentry = a->src_wh_dentry;
-+ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry);
-+ au_set_dbwh(a->src_dentry, a->src_bwh);
-+ if (rerr)
-+ RevertFailure("unlink %pd", a->src_wh_dentry);
-+}
-+#undef RevertFailure
++ err = au_ii_refresh(inode, &update);
++ if (unlikely(err))
++ goto out;
+
-+/* ---------------------------------------------------------------------- */
++ update = 0;
++ iinfo = au_ii(inode);
++ p = au_hinode(iinfo, iinfo->ii_btop);
++ mode = (inode->i_mode & S_IFMT);
++ isdir = S_ISDIR(mode);
++ flags = au_hi_flags(inode, isdir);
++ bbot = au_dbbot(dentry);
++ for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) {
++ struct inode *h_i, *h_inode;
++ struct dentry *h_d;
+
-+/*
-+ * when we have to copyup the renaming entry, do it with the rename-target name
-+ * in order to minimize the cost (the later actual rename is unnecessary).
-+ * otherwise rename it on the target branch.
-+ */
-+static int au_ren_or_cpup(struct au_ren_args *a)
-+{
-+ int err;
-+ struct dentry *d;
-+ struct inode *delegated;
++ h_d = au_h_dptr(dentry, bindex);
++ if (!h_d || d_is_negative(h_d))
++ continue;
+
-+ d = a->src_dentry;
-+ if (au_dbtop(d) == a->btgt) {
-+ a->h_path.dentry = a->dst_h_dentry;
-+ 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->flags);
-+ if (unlikely(err == -EWOULDBLOCK)) {
-+ pr_warn("cannot retry for NFSv4 delegation"
-+ " for an internal rename\n");
-+ iput(delegated);
++ h_inode = d_inode(h_d);
++ AuDebugOn(mode != (h_inode->i_mode & S_IFMT));
++ if (iinfo->ii_btop <= bindex && bindex <= iinfo->ii_bbot) {
++ h_i = au_h_iptr(inode, bindex);
++ if (h_i) {
++ if (h_i == h_inode)
++ continue;
++ err = -EIO;
++ break;
++ }
+ }
-+ } else
-+ BUG();
-+
-+ if (!err && a->h_dst)
-+ /* it will be set to dinfo later */
-+ dget(a->h_dst);
++ 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;
++ }
++ au_update_ibrange(inode, /*do_put_zero*/0);
++ e = au_dy_irefresh(inode);
++ if (unlikely(e && !err))
++ err = e;
++ if (!err)
++ au_refresh_hinode_attr(inode, update && isdir);
+
++out:
++ AuTraceErr(err);
+ return err;
+}
+
-+/* cf. aufs_rmdir() */
-+static int au_ren_del_whtmp(struct au_ren_args *a)
++static int set_inode(struct inode *inode, struct dentry *dentry)
+{
+ int err;
-+ struct inode *dir;
++ unsigned int flags;
++ umode_t mode;
++ aufs_bindex_t bindex, btop, btail;
++ unsigned char isdir;
++ struct dentry *h_dentry;
++ struct inode *h_inode;
++ struct au_iinfo *iinfo;
++ struct inode_operations *iop;
+
-+ dir = a->dst_dir;
-+ SiMustAnyLock(dir->i_sb);
-+ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt,
-+ au_sbi(dir->i_sb)->si_dirwh)
-+ || au_test_fs_remote(a->h_dst->d_sb)) {
-+ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist);
++ IiMustWriteLock(inode);
++
++ err = 0;
++ isdir = 0;
++ iop = au_sbi(inode->i_sb)->si_iop_array;
++ 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) {
++ case S_IFREG:
++ btail = au_dbtail(dentry);
++ inode->i_op = iop + AuIop_OTHER;
++ inode->i_fop = &aufs_file_fop;
++ err = au_dy_iaop(inode, btop, h_inode);
+ if (unlikely(err))
-+ pr_warn("failed removing whtmp dir %pd (%d), "
-+ "ignored.\n", a->h_dst, err);
-+ } else {
-+ au_nhash_wh_free(&a->thargs->whlist);
-+ a->thargs->whlist = a->whlist;
-+ a->whlist.nh_num = 0;
-+ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs);
-+ dput(a->h_dst);
-+ a->thargs = NULL;
++ goto out;
++ break;
++ case S_IFDIR:
++ isdir = 1;
++ btail = au_dbtaildir(dentry);
++ inode->i_op = iop + AuIop_DIR;
++ inode->i_fop = &aufs_dir_fop;
++ break;
++ case S_IFLNK:
++ btail = au_dbtail(dentry);
++ inode->i_op = iop + AuIop_SYMLINK;
++ break;
++ case S_IFBLK:
++ case S_IFCHR:
++ case S_IFIFO:
++ case S_IFSOCK:
++ btail = au_dbtail(dentry);
++ inode->i_op = iop + AuIop_OTHER;
++ init_special_inode(inode, mode, h_inode->i_rdev);
++ break;
++ default:
++ AuIOErr("Unknown file type 0%o\n", mode);
++ err = -EIO;
++ goto out;
+ }
+
-+ return 0;
++ /* do not set hnotify for whiteouted dirs (SHWH mode) */
++ flags = au_hi_flags(inode, isdir);
++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)
++ && au_ftest_hi(flags, HNOTIFY)
++ && dentry->d_name.len > AUFS_WH_PFX_LEN
++ && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))
++ au_fclr_hi(flags, HNOTIFY);
++ iinfo = au_ii(inode);
++ iinfo->ii_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,
++ au_igrab(d_inode(h_dentry)), flags);
++ }
++ au_cpup_attr_all(inode, /*force*/1);
++ /*
++ * to force calling aufs_get_acl() every time,
++ * do not call cache_no_acl() for aufs inode.
++ */
++
++out:
++ return err;
+}
+
-+/* make it 'opaque' dir. */
-+static int au_ren_do_diropq(struct au_ren_args *a, int idx)
++/*
++ * successful returns with iinfo write_locked
++ * minus: errno
++ * zero: success, matched
++ * plus: no error, but unmatched
++ */
++static int reval_inode(struct inode *inode, struct dentry *dentry)
+{
+ int err;
-+ struct dentry *d, *diropq;
-+#define src_or_dst(member) a->sd[idx].member
++ unsigned int gen, igflags;
++ aufs_bindex_t bindex, bbot;
++ struct inode *h_inode, *h_dinode;
++ struct dentry *h_dentry;
+
-+ err = 0;
-+ 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);
++ /*
++ * before this function, if aufs got any iinfo lock, it must be only
++ * one, the parent dir.
++ * it can happen by UDBA and the obsoleted inode number.
++ */
++ err = -EIO;
++ if (unlikely(inode->i_ino == parent_ino(dentry)))
++ goto out;
+
-+#undef src_or_dst_
++ err = 1;
++ ii_write_lock_new_child(inode);
++ h_dentry = au_h_dptr(dentry, au_dbtop(dentry));
++ h_dinode = d_inode(h_dentry);
++ 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, &igflags);
++ if (gen == au_digen(dentry)
++ && !au_ig_ftest(igflags, HALF_REFRESHED))
++ break;
++
++ /* fully refresh inode using dentry */
++ err = au_refresh_hinode(inode, dentry);
++ if (!err)
++ au_update_iigen(inode, /*half*/0);
++ break;
++ }
++
++ if (unlikely(err))
++ ii_write_unlock(inode);
++out:
+ return err;
+}
+
-+static int au_ren_diropq(struct au_ren_args *a)
++int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ unsigned int d_type, ino_t *ino)
+{
-+ int err;
-+ unsigned char always;
-+ struct dentry *d;
++ int err, idx;
++ const int isnondir = d_type != DT_DIR;
+
-+ 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);
++ /* prevent hardlinked inode number from race condition */
++ if (isnondir) {
++ err = au_xinondir_enter(sb, bindex, h_ino, &idx);
+ if (unlikely(err))
+ goto out;
-+ au_fset_ren(a->auren_flags, DIROPQ_SRC);
+ }
-+ if (!a->exchange)
-+ goto out; /* success */
+
-+ 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);
++ err = au_xino_read(sb, bindex, h_ino, ino);
++ if (unlikely(err))
++ goto out_xinondir;
++
++ if (!*ino) {
++ err = -EIO;
++ *ino = au_xino_new_ino(sb);
++ if (unlikely(!*ino))
++ goto out_xinondir;
++ err = au_xino_write(sb, bindex, h_ino, *ino);
+ if (unlikely(err))
-+ goto out_rev_src;
-+ au_fset_ren(a->auren_flags, DIROPQ_DST);
++ goto out_xinondir;
+ }
-+ goto out; /* success */
+
-+out_rev_src:
-+ AuDbg("err %d, reverting src\n", err);
-+ au_ren_rev_diropq(err, a);
++out_xinondir:
++ if (isnondir && idx >= 0)
++ au_xinondir_leave(sb, bindex, h_ino, idx);
+out:
+ return err;
+}
+
-+static int do_rename(struct au_ren_args *a)
++/* successful returns with iinfo write_locked */
++/* todo: return with unlocked? */
++struct inode *au_new_inode(struct dentry *dentry, int must_new)
+{
-+ 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);
-+ }
++ struct inode *inode, *h_inode;
++ struct dentry *h_dentry;
++ struct super_block *sb;
++ ino_t h_ino, ino;
++ int err, idx, hlinked;
++ aufs_bindex_t btop;
+
-+ /* 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;
-+ }
++ sb = dentry->d_sb;
++ 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;
+
-+ /* 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;
-+ }
++new_ino:
++ /*
++ * stop 'race'-ing between hardlinks under different
++ * parents.
++ */
++ if (hlinked) {
++ err = au_xinondir_enter(sb, btop, h_ino, &idx);
++ inode = ERR_PTR(err);
++ if (unlikely(err))
++ goto out;
++ }
+
-+ /* rename dentry to tmpwh */
-+ if (a->thargs) {
-+ err = au_whtmp_ren(a->dst_h_dentry, a->br);
-+ if (unlikely(err))
-+ goto out_whdst;
++ err = au_xino_read(sb, btop, h_ino, &ino);
++ inode = ERR_PTR(err);
++ if (unlikely(err))
++ goto out_xinondir;
+
-+ 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);
++ if (!ino) {
++ ino = au_xino_new_ino(sb);
++ if (unlikely(!ino)) {
++ inode = ERR_PTR(-EIO);
++ goto out_xinondir;
+ }
+ }
+
-+ 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
++ AuDbg("i%lu\n", (unsigned long)ino);
++ inode = au_iget_locked(sb, ino);
++ err = PTR_ERR(inode);
++ if (IS_ERR(inode))
++ goto out_xinondir;
+
-+ /* rename by vfs_rename or cpup */
-+ err = au_ren_or_cpup(a);
-+ if (unlikely(err))
-+ /* leave the copied-up one */
-+ goto out_whtmp;
++ AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
++ if (inode->i_state & I_NEW) {
++ ii_write_lock_new_child(inode);
++ err = set_inode(inode, dentry);
++ if (!err) {
++ unlock_new_inode(inode);
++ goto out_xinondir; /* success */
++ }
+
-+ /* make dir opaque */
-+ err = au_ren_diropq(a);
-+ if (unlikely(err))
-+ goto out_rename;
++ /*
++ * iget_failed() calls iput(), but we need to call
++ * ii_write_unlock() after iget_failed(). so dirty hack for
++ * i_count.
++ */
++ atomic_inc(&inode->i_count);
++ iget_failed(inode);
++ ii_write_unlock(inode);
++ au_xino_write(sb, btop, h_ino, /*ino*/0);
++ /* ignore this error */
++ goto out_iput;
++ } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) {
++ /*
++ * horrible race condition between lookup, readdir and copyup
++ * (or something).
++ */
++ if (hlinked && idx >= 0)
++ au_xinondir_leave(sb, btop, h_ino, idx);
++ err = reval_inode(inode, dentry);
++ if (unlikely(err < 0)) {
++ hlinked = 0;
++ goto out_iput;
++ }
++ if (!err)
++ goto out; /* success */
++ 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;
++ }
++ }
++ }
+
-+ /* update target timestamps */
-+ if (a->exchange) {
-+ AuDebugOn(au_dbtop(a->dst_dentry) != a->btgt);
-+ a->h_path.dentry = au_h_dptr(a->dst_dentry, a->btgt);
-+ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/
-+ a->dst_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime;
++ 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",
++ btop, au_sbtype(h_dentry->d_sb), dentry,
++ (unsigned long)h_ino, (unsigned long)ino);
++ ino = 0;
++ err = au_xino_write(sb, btop, h_ino, /*ino*/0);
++ if (!err) {
++ iput(inode);
++ if (hlinked && idx >= 0)
++ au_xinondir_leave(sb, btop, h_ino, idx);
++ goto new_ino;
+ }
-+ 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;
+
-+ 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;
-+ }
++out_iput:
++ iput(inode);
++ inode = ERR_PTR(err);
++out_xinondir:
++ if (hlinked && idx >= 0)
++ au_xinondir_leave(sb, btop, h_ino, idx);
++out:
++ return inode;
++}
+
-+ /* 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);
-+ }
-+ err = 0;
-+ goto out_success;
++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
++ struct inode *inode)
++{
++ int err;
++ struct inode *hi;
+
-+out_diropq:
-+ au_ren_rev_diropq(err, a);
-+out_rename:
-+ au_ren_rev_rename(err, a);
-+ dput(a->h_dst);
-+out_whtmp:
-+ if (a->thargs)
-+ au_ren_rev_whtmp(err, a);
-+out_whdst:
-+ dput(a->dst_wh_dentry);
-+ a->dst_wh_dentry = NULL;
-+out_whsrc:
-+ if (a->src_wh_dentry)
-+ au_ren_rev_whsrc(err, a);
-+out_success:
-+ dput(a->src_wh_dentry);
-+ dput(a->dst_wh_dentry);
-+out_thargs:
-+ if (a->thargs) {
-+ dput(a->h_dst);
-+ au_whtmp_rmdir_free(a->thargs);
-+ a->thargs = NULL;
++ err = au_br_rdonly(au_sbr(sb, bindex));
++
++ /* pseudo-link after flushed may happen out of bounds */
++ if (!err
++ && inode
++ && au_ibtop(inode) <= bindex
++ && bindex <= au_ibbot(inode)) {
++ /*
++ * permission check is unnecessary since vfsub routine
++ * will be called later
++ */
++ hi = au_h_iptr(inode, bindex);
++ if (hi)
++ err = IS_IMMUTABLE(hi) ? -EROFS : 0;
+ }
-+out:
++
+ return err;
+}
+
-+/* ---------------------------------------------------------------------- */
++int au_test_h_perm(struct inode *h_inode, int mask)
++{
++ if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID))
++ return 0;
++ return inode_permission(h_inode, mask);
++}
+
-+/*
-+ * test if @dentry dir can be rename destination or not.
-+ * success means, it is a logically empty dir.
-+ */
-+static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist)
++int au_test_h_perm_sio(struct inode *h_inode, int mask)
+{
-+ return au_test_empty(dentry, whlist);
++ if (au_test_nfs(h_inode->i_sb)
++ && (mask & MAY_WRITE)
++ && S_ISDIR(h_inode->i_mode))
++ mask |= MAY_READ; /* force permission check */
++ return au_test_h_perm(h_inode, mask);
+}
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/inode.h linux-4.9/fs/aufs/inode.h
+--- linux-4.9/fs/aufs/inode.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/inode.h 2021-02-24 16:15:09.528240413 +0100
+@@ -0,0 +1,696 @@
++/*
++ * 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
++ * 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 <http://www.gnu.org/licenses/>.
++ */
+
+/*
-+ * 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 unless DIRREN is on.
++ * inode operations
+ */
-+static int may_rename_srcdir(struct au_ren_args *a)
-+{
-+ int err;
-+ unsigned int rdhash;
-+ 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);
++#ifndef __AUFS_INODE_H__
++#define __AUFS_INODE_H__
+
-+ btgt = a->btgt;
-+ btop = au_dbtop(dentry);
-+ if (btop != btgt) {
-+ struct au_nhash whlist;
++#ifdef __KERNEL__
+
-+ SiMustAnyLock(sb);
-+ rdhash = sbinfo->si_rdhash;
-+ if (!rdhash)
-+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL,
-+ dentry));
-+ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS);
-+ if (unlikely(err))
-+ goto out;
-+ err = au_test_empty(dentry, &whlist);
-+ au_nhash_wh_free(&whlist);
-+ goto out;
-+ }
++#include <linux/fsnotify.h>
++#include "rwsem.h"
+
-+ if (btop == au_dbtaildir(dentry))
-+ return 0; /* success */
++struct vfsmount;
++
++struct au_hnotify {
++#ifdef CONFIG_AUFS_HNOTIFY
++#ifdef CONFIG_AUFS_HFSNOTIFY
++ /* never use fsnotify_add_vfsmount_mark() */
++ struct fsnotify_mark hn_mark;
++#endif
++ struct inode *hn_aufs_inode; /* no get/put */
++#endif
++} ____cacheline_aligned_in_smp;
++
++struct au_hinode {
++ struct inode *hi_inode;
++ aufs_bindex_t hi_id;
++#ifdef CONFIG_AUFS_HNOTIFY
++ struct au_hnotify *hi_notify;
++#endif
++
++ /* reference to the copied-up whiteout with get/put */
++ struct dentry *hi_whdentry;
++};
++
++/* ig_flags */
++#define AuIG_HALF_REFRESHED 1
++#define au_ig_ftest(flags, name) ((flags) & AuIG_##name)
++#define au_ig_fset(flags, name) \
++ do { (flags) |= AuIG_##name; } while (0)
++#define au_ig_fclr(flags, name) \
++ do { (flags) &= ~AuIG_##name; } while (0)
++
++struct au_iigen {
++ spinlock_t ig_spin;
++ __u32 ig_generation, ig_flags;
++};
++
++struct au_vdir;
++struct au_iinfo {
++ struct au_iigen ii_generation;
++ struct super_block *ii_hsb1; /* no get/put */
++
++ struct au_rwsem ii_rwsem;
++ 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 hlist_bl_node plink;
++} ____cacheline_aligned_in_smp;
++
++/* au_pin flags */
++#define AuPin_DI_LOCKED 1
++#define AuPin_MNT_WRITE (1 << 1)
++#define au_ftest_pin(flags, name) ((flags) & AuPin_##name)
++#define au_fset_pin(flags, name) \
++ do { (flags) |= AuPin_##name; } while (0)
++#define au_fclr_pin(flags, name) \
++ do { (flags) &= ~AuPin_##name; } while (0)
++
++struct au_pin {
++ /* input */
++ struct dentry *dentry;
++ unsigned int udba;
++ unsigned char lsc_di, lsc_hi, flags;
++ aufs_bindex_t bindex;
++
++ /* output */
++ struct dentry *parent;
++ struct au_hinode *hdir;
++ struct vfsmount *h_mnt;
++
++ /* temporary unlock/relock for copyup */
++ struct dentry *h_dentry, *h_parent;
++ struct au_branch *br;
++ struct task_struct *task;
++};
++
++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_acquire_nest(struct au_pin *p);
++void au_pin_hdir_release(struct au_pin *p);
+
-+ err = au_test_empty_lower(dentry);
++/* ---------------------------------------------------------------------- */
+
-+out:
-+ if (err == -ENOTEMPTY) {
-+ 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;
++static inline struct au_iinfo *au_ii(struct inode *inode)
++{
++ BUG_ON(is_bad_inode(inode));
++ return &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);
+}
+
-+/* side effect: sets whlist and h_dentry */
-+static int au_ren_may_dir(struct au_ren_args *a)
++/* ---------------------------------------------------------------------- */
++
++/* inode.c */
++struct inode *au_igrab(struct inode *inode);
++void au_refresh_iop(struct inode *inode, int force_getattr);
++int au_refresh_hinode_self(struct inode *inode);
++int au_refresh_hinode(struct inode *inode, struct dentry *dentry);
++int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ unsigned int d_type, ino_t *ino);
++struct inode *au_new_inode(struct dentry *dentry, int must_new);
++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
++ struct inode *inode);
++int au_test_h_perm(struct inode *h_inode, int mask);
++int au_test_h_perm_sio(struct inode *h_inode, int mask);
++
++static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex,
++ ino_t h_ino, unsigned int d_type, ino_t *ino)
+{
-+ int err;
-+ unsigned int rdhash;
-+ struct dentry *d;
++#ifdef CONFIG_AUFS_SHWH
++ return au_ino(sb, bindex, h_ino, d_type, ino);
++#else
++ return 0;
++#endif
++}
+
-+ d = a->dst_dentry;
-+ SiMustAnyLock(d->d_sb);
++/* i_op.c */
++enum {
++ AuIop_SYMLINK,
++ AuIop_DIR,
++ AuIop_OTHER,
++ AuIop_Last
++};
++extern struct inode_operations aufs_iop[AuIop_Last],
++ aufs_iop_nogetattr[AuIop_Last];
+
-+ err = 0;
-+ 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));
-+ err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS);
-+ if (unlikely(err))
-+ goto out;
++/* au_wr_dir flags */
++#define AuWrDir_ADD_ENTRY 1
++#define AuWrDir_ISDIR (1 << 1)
++#define AuWrDir_TMPFILE (1 << 2)
++#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name)
++#define au_fset_wrdir(flags, name) \
++ do { (flags) |= AuWrDir_##name; } while (0)
++#define au_fclr_wrdir(flags, name) \
++ do { (flags) &= ~AuWrDir_##name; } while (0)
+
-+ if (!a->exchange) {
-+ au_set_dbtop(d, a->dst_btop);
-+ err = may_rename_dstdir(d, &a->whlist);
-+ au_set_dbtop(d, a->btgt);
-+ } else
-+ err = may_rename_srcdir(a);
-+ }
-+ a->dst_h_dentry = au_h_dptr(d, au_dbtop(d));
-+ if (unlikely(err))
-+ goto out;
++struct au_wr_dir_args {
++ aufs_bindex_t force_btgt;
++ unsigned char flags;
++};
++int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
++ struct au_wr_dir_args *args);
+
-+ d = a->src_dentry;
-+ 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;
-+ }
-+ }
-+out:
-+ return err;
-+}
++struct dentry *au_pinned_h_parent(struct au_pin *pin);
++void au_pin_init(struct au_pin *pin, struct dentry *dentry,
++ aufs_bindex_t bindex, int lsc_di, int lsc_hi,
++ unsigned int udba, unsigned char flags);
++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
++ unsigned int udba, unsigned char flags) __must_check;
++int au_do_pin(struct au_pin *pin) __must_check;
++void au_unpin(struct au_pin *pin);
++int au_reval_for_attr(struct dentry *dentry, unsigned int sigen);
+
-+/* ---------------------------------------------------------------------- */
++#define AuIcpup_DID_CPUP 1
++#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name)
++#define au_fset_icpup(flags, name) \
++ do { (flags) |= AuIcpup_##name; } while (0)
++#define au_fclr_icpup(flags, name) \
++ do { (flags) &= ~AuIcpup_##name; } while (0)
+
-+/*
-+ * simple tests for rename.
-+ * following the checks in vfs, plus the parent-child relationship.
-+ */
-+static int au_may_ren(struct au_ren_args *a)
-+{
-+ int err, isdir;
++struct au_icpup_args {
++ unsigned char flags;
++ unsigned char pin_flags;
++ aufs_bindex_t btgt;
++ unsigned int udba;
++ struct au_pin pin;
++ struct path h_path;
+ struct inode *h_inode;
++};
+
-+ if (a->src_btop == a->btgt) {
-+ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent,
-+ au_ftest_ren(a->auren_flags, ISDIR_SRC));
-+ if (unlikely(err))
-+ goto out;
-+ err = -EINVAL;
-+ if (unlikely(a->src_h_dentry == a->h_trap))
-+ goto out;
-+ }
++int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia,
++ struct au_icpup_args *a);
+
-+ err = 0;
-+ if (a->dst_btop != a->btgt)
-+ goto out;
++int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path,
++ int locked);
+
-+ err = -ENOTEMPTY;
-+ if (unlikely(a->dst_h_dentry == a->h_trap))
-+ goto out;
++/* i_op_add.c */
++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_parent, int isdir);
++int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
++ dev_t dev);
++int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname);
++int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
++ bool want_excl);
++struct vfsub_aopen_args;
++int au_aopen_or_create(struct inode *dir, struct dentry *dentry,
++ struct vfsub_aopen_args *args);
++int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode);
++int aufs_link(struct dentry *src_dentry, struct inode *dir,
++ struct dentry *dentry);
++int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode);
+
-+ err = -EIO;
-+ 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,
-+ a->dst_h_parent, isdir);
-+ } else {
-+ if (unlikely(d_is_negative(a->dst_h_dentry)))
-+ goto out;
-+ h_inode = d_inode(a->dst_h_dentry);
-+ if (h_inode->i_nlink)
-+ err = au_may_del(a->dst_dentry, a->btgt,
-+ a->dst_h_parent, isdir);
-+ }
++/* i_op_del.c */
++int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup);
++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_parent, int isdir);
++int aufs_unlink(struct inode *dir, struct dentry *dentry);
++int aufs_rmdir(struct inode *dir, struct dentry *dentry);
+
-+out:
-+ if (unlikely(err == -ENOENT || err == -EEXIST))
-+ err = -EIO;
-+ AuTraceErr(err);
-+ return err;
-+}
++/* 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,
++ unsigned int flags);
+
-+/* ---------------------------------------------------------------------- */
++/* iinfo.c */
++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
++void au_hiput(struct au_hinode *hinode);
++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
++ struct dentry *h_wh);
++unsigned int au_hi_flags(struct inode *inode, int isdir);
+
-+/*
-+ * locking order
-+ * (VFS)
-+ * - src_dir and dir by lock_rename()
-+ * - inode if exitsts
-+ * (aufs)
-+ * - lock all
-+ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls,
-+ * + si_read_lock
-+ * + di_write_lock2_child()
-+ * + di_write_lock_child()
-+ * + ii_write_lock_child()
-+ * + di_write_lock_child2()
-+ * + ii_write_lock_child2()
-+ * + src_parent and parent
-+ * + di_write_lock_parent()
-+ * + ii_write_lock_parent()
-+ * + di_write_lock_parent2()
-+ * + ii_write_lock_parent2()
-+ * + lower src_dir and dir by vfsub_lock_rename()
-+ * + verify the every relationships between child and parent. if any
-+ * of them failed, unlock all and return -EBUSY.
-+ */
-+static void au_ren_unlock(struct au_ren_args *a)
-+{
-+ vfsub_unlock_rename(a->src_h_parent, a->src_hdir,
-+ a->dst_h_parent, a->dst_hdir);
-+ 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));
-+}
++/* hinode flags */
++#define AuHi_XINO 1
++#define AuHi_HNOTIFY (1 << 1)
++#define au_ftest_hi(flags, name) ((flags) & AuHi_##name)
++#define au_fset_hi(flags, name) \
++ do { (flags) |= AuHi_##name; } while (0)
++#define au_fclr_hi(flags, name) \
++ do { (flags) &= ~AuHi_##name; } while (0)
+
-+static int au_ren_lock(struct au_ren_args *a)
-+{
-+ int err;
-+ unsigned int udba;
++#ifndef CONFIG_AUFS_HNOTIFY
++#undef AuHi_HNOTIFY
++#define AuHi_HNOTIFY 0
++#endif
+
-+ err = 0;
-+ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt);
-+ a->src_hdir = au_hi(a->src_dir, a->btgt);
-+ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt);
-+ a->dst_hdir = au_hi(a->dst_dir, a->btgt);
++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
++ struct inode *h_inode, unsigned int flags);
+
-+ err = vfsub_mnt_want_write(au_br_mnt(a->br));
-+ if (unlikely(err))
-+ goto out;
-+ au_fset_ren(a->auren_flags, MNT_WRITE);
-+ if (au_ftest_ren(a->auren_flags, DIRREN)) {
-+ struct dentry *root;
-+ struct inode *dir;
++void au_update_iigen(struct inode *inode, int half);
++void au_update_ibrange(struct inode *inode, int do_put_zero);
+
-+ /*
-+ * 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_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_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);
-+ if (!err)
-+ goto out; /* success */
++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_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink);
+
-+ err = au_busy_or_stale();
-+ au_ren_unlock(a);
++#ifdef CONFIG_PROC_FS
++/* plink.c */
++int au_plink_maint(struct super_block *sb, int flags);
++struct au_sbinfo;
++void au_plink_maint_leave(struct au_sbinfo *sbinfo);
++int au_plink_maint_enter(struct super_block *sb);
++#ifdef CONFIG_AUFS_DEBUG
++void au_plink_list(struct super_block *sb);
++#else
++AuStubVoid(au_plink_list, struct super_block *sb)
++#endif
++int au_plink_test(struct inode *inode);
++struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex);
++void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
++ struct dentry *h_dentry);
++void au_plink_put(struct super_block *sb, int verbose);
++void au_plink_clean(struct super_block *sb, int verbose);
++void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id);
++#else
++AuStubInt0(au_plink_maint, struct super_block *sb, int flags);
++AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo);
++AuStubInt0(au_plink_maint_enter, struct super_block *sb);
++AuStubVoid(au_plink_list, struct super_block *sb);
++AuStubInt0(au_plink_test, struct inode *inode);
++AuStub(struct dentry *, au_plink_lkup, return NULL,
++ struct inode *inode, aufs_bindex_t bindex);
++AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex,
++ struct dentry *h_dentry);
++AuStubVoid(au_plink_put, struct super_block *sb, int verbose);
++AuStubVoid(au_plink_clean, struct super_block *sb, int verbose);
++AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id);
++#endif /* CONFIG_PROC_FS */
+
-+out:
-+ return err;
-+}
++#ifdef CONFIG_AUFS_XATTR
++/* xattr.c */
++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);
++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);
++#endif
+
-+/* ---------------------------------------------------------------------- */
++#ifdef CONFIG_FS_POSIX_ACL
++struct posix_acl *aufs_get_acl(struct inode *inode, int type);
++int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type);
++#endif
+
-+static void au_ren_refresh_dir(struct au_ren_args *a)
-+{
-+ struct inode *dir;
++#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL)
++enum {
++ AU_XATTR_SET,
++ AU_ACL_SET
++};
+
-+ dir = a->dst_dir;
-+ dir->i_version++;
-+ 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);
++struct au_sxattr {
++ int type;
++ union {
++ struct {
++ const char *name;
++ const void *value;
++ size_t size;
++ int flags;
++ } set;
++ struct {
++ struct posix_acl *acl;
++ int type;
++ } acl_set;
++ } u;
++};
++ssize_t au_sxattr(struct dentry *dentry, struct inode *inode,
++ struct au_sxattr *arg);
++#endif
+
-+ if (a->exchange) {
-+ dir = a->src_dir;
-+ dir->i_version++;
-+ if (au_ftest_ren(a->auren_flags, ISDIR_DST)) {
-+ /* is this updating defined in POSIX? */
-+ au_cpup_attr_timesizes(a->dst_inode);
-+ au_cpup_attr_nlink(dir, /*force*/1);
-+ }
-+ au_dir_ts(dir, a->btgt);
-+ }
++/* ---------------------------------------------------------------------- */
+
-+ if (au_ftest_ren(a->auren_flags, ISSAMEDIR))
-+ return;
++/* lock subclass for iinfo */
++enum {
++ AuLsc_II_CHILD, /* child first */
++ AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */
++ AuLsc_II_CHILD3, /* copyup dirs */
++ AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */
++ AuLsc_II_PARENT2,
++ AuLsc_II_PARENT3, /* copyup dirs */
++ AuLsc_II_NEW_CHILD
++};
+
-+ dir = a->src_dir;
-+ dir->i_version++;
-+ if (au_ftest_ren(a->auren_flags, ISDIR_SRC))
-+ au_cpup_attr_nlink(dir, /*force*/1);
-+ au_dir_ts(dir, a->btgt);
++/*
++ * ii_read_lock_child, ii_write_lock_child,
++ * ii_read_lock_child2, ii_write_lock_child2,
++ * ii_read_lock_child3, ii_write_lock_child3,
++ * ii_read_lock_parent, ii_write_lock_parent,
++ * ii_read_lock_parent2, ii_write_lock_parent2,
++ * ii_read_lock_parent3, ii_write_lock_parent3,
++ * ii_read_lock_new_child, ii_write_lock_new_child,
++ */
++#define AuReadLockFunc(name, lsc) \
++static inline void ii_read_lock_##name(struct inode *i) \
++{ \
++ au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \
+}
+
-+static void au_ren_refresh(struct au_ren_args *a)
-+{
-+ aufs_bindex_t bbot, bindex;
-+ struct dentry *d, *h_d;
-+ struct inode *i, *h_i;
-+ struct super_block *sb;
++#define AuWriteLockFunc(name, lsc) \
++static inline void ii_write_lock_##name(struct inode *i) \
++{ \
++ au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \
++}
+
-+ d = a->dst_dentry;
-+ d_drop(d);
-+ if (a->h_dst)
-+ /* already dget-ed by au_ren_or_cpup() */
-+ au_set_h_dptr(d, a->btgt, a->h_dst);
++#define AuRWLockFuncs(name, lsc) \
++ AuReadLockFunc(name, lsc) \
++ AuWriteLockFunc(name, lsc)
+
-+ i = a->dst_inode;
-+ if (i) {
-+ if (!a->exchange) {
-+ if (!au_ftest_ren(a->auren_flags, ISDIR_DST))
-+ vfsub_drop_nlink(i);
-+ else {
-+ vfsub_dead_dir(i);
-+ au_cpup_attr_timesizes(i);
-+ }
-+ au_update_dbrange(d, /*do_put_zero*/1);
-+ } else
-+ au_cpup_attr_nlink(i, /*force*/1);
-+ } else {
-+ bbot = a->btgt;
-+ for (bindex = au_dbtop(d); bindex < bbot; bindex++)
-+ au_set_h_dptr(d, bindex, NULL);
-+ 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);
-+ }
++AuRWLockFuncs(child, CHILD);
++AuRWLockFuncs(child2, CHILD2);
++AuRWLockFuncs(child3, CHILD3);
++AuRWLockFuncs(parent, PARENT);
++AuRWLockFuncs(parent2, PARENT2);
++AuRWLockFuncs(parent3, PARENT3);
++AuRWLockFuncs(new_child, NEW_CHILD);
+
-+ 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;
-+ }
++#undef AuReadLockFunc
++#undef AuWriteLockFunc
++#undef AuRWLockFuncs
+
-+ d = a->src_dentry;
-+ au_set_dbwh(d, -1);
-+ 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_dbbot(d, a->btgt);
++/*
++ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock
++ */
++AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem);
+
-+ sb = d->d_sb;
-+ i = a->src_inode;
-+ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i))
-+ return; /* success */
++#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem)
++#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem)
++#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem)
+
-+ 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);
-+ /* ignore this error */
-+ au_set_h_iptr(i, bindex, NULL, 0);
-+ }
-+ }
-+ au_set_ibbot(i, a->btgt);
++/* ---------------------------------------------------------------------- */
++
++static inline void au_icntnr_init(struct au_icntnr *c)
++{
++#ifdef CONFIG_AUFS_DEBUG
++ c->vfs_inode.i_mode = 0;
++#endif
+}
+
-+/* ---------------------------------------------------------------------- */
++static inline unsigned int au_iigen(struct inode *inode, unsigned int *igflags)
++{
++ unsigned int gen;
++ struct au_iinfo *iinfo;
++ struct au_iigen *iigen;
+
-+/* mainly for link(2) and rename(2) */
-+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt)
++ iinfo = au_ii(inode);
++ iigen = &iinfo->ii_generation;
++ spin_lock(&iigen->ig_spin);
++ if (igflags)
++ *igflags = iigen->ig_flags;
++ gen = iigen->ig_generation;
++ spin_unlock(&iigen->ig_spin);
++
++ return gen;
++}
++
++/* tiny test for inode number */
++/* tmpfs generation is too rough */
++static inline int au_test_higen(struct inode *inode, struct inode *h_inode)
+{
-+ aufs_bindex_t bdiropq, bwh;
-+ struct dentry *parent;
-+ struct au_branch *br;
++ struct au_iinfo *iinfo;
+
-+ parent = dentry->d_parent;
-+ IMustLock(d_inode(parent)); /* dir is locked */
++ iinfo = au_ii(inode);
++ AuRwMustAnyLock(&iinfo->ii_rwsem);
++ return !(iinfo->ii_hsb1 == h_inode->i_sb
++ && iinfo->ii_higen == h_inode->i_generation);
++}
+
-+ bdiropq = au_dbdiropq(parent);
-+ bwh = au_dbwh(dentry);
-+ br = au_sbr(dentry->d_sb, btgt);
-+ if (au_br_rdonly(br)
-+ || (0 <= bdiropq && bdiropq < btgt)
-+ || (0 <= bwh && bwh < btgt))
-+ btgt = -1;
++static inline void au_iigen_dec(struct inode *inode)
++{
++ struct au_iinfo *iinfo;
++ struct au_iigen *iigen;
+
-+ AuDbg("btgt %d\n", btgt);
-+ return btgt;
++ iinfo = au_ii(inode);
++ iigen = &iinfo->ii_generation;
++ spin_lock(&iigen->ig_spin);
++ iigen->ig_generation--;
++ spin_unlock(&iigen->ig_spin);
+}
+
-+/* sets src_btop, dst_btop and btgt */
-+static int au_ren_wbr(struct au_ren_args *a)
++static inline int au_iigen_test(struct inode *inode, unsigned int sigen)
+{
+ int err;
-+ struct au_wr_dir_args wr_dir_args = {
-+ /* .force_btgt = -1, */
-+ .flags = AuWrDir_ADD_ENTRY
-+ };
+
-+ 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_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);
++ err = 0;
++ if (unlikely(inode && au_iigen(inode, NULL) != sigen))
++ err = -EIO;
+
+ return err;
+}
+
-+static void au_ren_dt(struct au_ren_args *a)
++/* ---------------------------------------------------------------------- */
++
++static inline struct au_hinode *au_hinode(struct au_iinfo *iinfo,
++ aufs_bindex_t bindex)
+{
-+ 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->auren_flags, ISSAMEDIR)) {
-+ a->h_path.dentry = a->dst_h_parent;
-+ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path);
-+ }
++ return iinfo->ii_hinode + bindex;
++}
+
-+ au_fclr_ren(a->auren_flags, DT_DSTDIR);
-+ if (!au_ftest_ren(a->auren_flags, ISDIR_SRC)
-+ && !a->exchange)
-+ return;
++static inline int au_is_bad_inode(struct inode *inode)
++{
++ return !!(is_bad_inode(inode) || !au_hinode(au_ii(inode), 0));
++}
+
-+ 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->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);
-+ }
++static inline aufs_bindex_t au_ii_br_id(struct inode *inode,
++ aufs_bindex_t bindex)
++{
++ IiMustAnyLock(inode);
++ return au_hinode(au_ii(inode), bindex)->hi_id;
+}
+
-+static void au_ren_rev_dt(int err, struct au_ren_args *a)
++static inline aufs_bindex_t au_ibtop(struct inode *inode)
+{
-+ struct dentry *h_d;
-+ struct inode *h_inode;
++ IiMustAnyLock(inode);
++ return au_ii(inode)->ii_btop;
++}
+
-+ au_dtime_revert(a->src_dt + AuPARENT);
-+ if (!au_ftest_ren(a->auren_flags, ISSAMEDIR))
-+ au_dtime_revert(a->dst_dt + AuPARENT);
++static inline aufs_bindex_t au_ibbot(struct inode *inode)
++{
++ IiMustAnyLock(inode);
++ return au_ii(inode)->ii_bbot;
++}
+
-+ if (au_ftest_ren(a->auren_flags, ISDIR_SRC) && err != -EIO) {
-+ h_d = a->src_dt[AuCHILD].dt_h_path.dentry;
-+ h_inode = d_inode(h_d);
-+ inode_lock_nested(h_inode, AuLsc_I_CHILD);
-+ au_dtime_revert(a->src_dt + AuCHILD);
-+ inode_unlock(h_inode);
++static inline struct au_vdir *au_ivdir(struct inode *inode)
++{
++ IiMustAnyLock(inode);
++ return au_ii(inode)->ii_vdir;
++}
+
-+ if (au_ftest_ren(a->auren_flags, DT_DSTDIR)) {
-+ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry;
-+ h_inode = d_inode(h_d);
-+ inode_lock_nested(h_inode, AuLsc_I_CHILD);
-+ au_dtime_revert(a->dst_dt + AuCHILD);
-+ inode_unlock(h_inode);
-+ }
-+ }
++static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex)
++{
++ IiMustAnyLock(inode);
++ return au_hinode(au_ii(inode), bindex)->hi_whdentry;
++}
++
++static inline void au_set_ibtop(struct inode *inode, aufs_bindex_t bindex)
++{
++ IiMustWriteLock(inode);
++ au_ii(inode)->ii_btop = bindex;
++}
++
++static inline void au_set_ibbot(struct inode *inode, aufs_bindex_t bindex)
++{
++ IiMustWriteLock(inode);
++ au_ii(inode)->ii_bbot = bindex;
++}
++
++static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir)
++{
++ IiMustWriteLock(inode);
++ au_ii(inode)->ii_vdir = vdir;
++}
++
++static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex)
++{
++ IiMustAnyLock(inode);
++ return au_hinode(au_ii(inode), bindex);
+}
+
+/* ---------------------------------------------------------------------- */
+
-+int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
-+ struct inode *_dst_dir, struct dentry *_dst_dentry,
-+ unsigned int _flags)
++static inline struct dentry *au_pinned_parent(struct au_pin *pin)
+{
-+ int err, lock_flags;
-+ void *rev;
-+ /* reduce stack space */
-+ struct au_ren_args *a;
-+ struct au_pin pin;
++ if (pin)
++ return pin->parent;
++ return NULL;
++}
+
-+ AuDbg("%pd, %pd, 0x%x\n", _src_dentry, _dst_dentry, _flags);
-+ IMustLock(_src_dir);
-+ IMustLock(_dst_dir);
++static inline struct inode *au_pinned_h_dir(struct au_pin *pin)
++{
++ if (pin && pin->hdir)
++ return pin->hdir->hi_inode;
++ return NULL;
++}
+
-+ err = -EINVAL;
-+ if (unlikely(_flags & RENAME_WHITEOUT))
-+ goto out;
++static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin)
++{
++ if (pin)
++ return pin->hdir;
++ return NULL;
++}
+
-+ err = -ENOMEM;
-+ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE);
-+ a = kzalloc(sizeof(*a), GFP_NOFS);
-+ if (unlikely(!a))
-+ goto out;
++static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry)
++{
++ if (pin)
++ pin->dentry = dentry;
++}
+
-+ a->flags = _flags;
-+ a->exchange = _flags & RENAME_EXCHANGE;
-+ a->src_dir = _src_dir;
-+ a->src_dentry = _src_dentry;
-+ a->src_inode = NULL;
-+ if (d_really_is_positive(a->src_dentry))
-+ a->src_inode = d_inode(a->src_dentry);
-+ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */
-+ a->dst_dir = _dst_dir;
-+ a->dst_dentry = _dst_dentry;
-+ a->dst_inode = NULL;
-+ if (d_really_is_positive(a->dst_dentry))
-+ a->dst_inode = d_inode(a->dst_dentry);
-+ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */
-+ if (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);
++static inline void au_pin_set_parent_lflag(struct au_pin *pin,
++ unsigned char lflag)
++{
++ if (pin) {
++ if (lflag)
++ au_fset_pin(pin->flags, DI_LOCKED);
++ else
++ au_fclr_pin(pin->flags, DI_LOCKED);
+ }
++}
+
-+ err = -ENOTDIR;
-+ lock_flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN;
-+ if (d_is_dir(a->src_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;
-+ 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;
++#if 0 /* reserved */
++static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent)
++{
++ if (pin) {
++ dput(pin->parent);
++ pin->parent = dget(parent);
+ }
-+ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,
-+ lock_flags);
-+ if (unlikely(err))
-+ goto out_free;
++}
++#endif
+
-+ err = au_d_hashed_positive(a->src_dentry);
-+ if (unlikely(err))
-+ goto out_unlock;
-+ err = -ENOENT;
-+ if (a->dst_inode) {
-+ /*
-+ * 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 (!au_ftest_ren(a->auren_flags, ISDIR_DST)) {
-+ err = au_d_hashed_positive(a->dst_dentry);
-+ if (unlikely(err && !a->exchange))
-+ goto out_unlock;
-+ } else if (unlikely(IS_DEADDIR(a->dst_inode)))
-+ goto out_unlock;
-+ } else if (unlikely(d_unhashed(a->dst_dentry)))
-+ goto out_unlock;
++/* ---------------------------------------------------------------------- */
++
++struct au_branch;
++#ifdef CONFIG_AUFS_HNOTIFY
++struct au_hnotify_op {
++ void (*ctl)(struct au_hinode *hinode, int do_set);
++ int (*alloc)(struct au_hinode *hinode);
+
+ /*
-+ * is it possible?
-+ * yes, it happened (in linux-3.3-rcN) but I don't know why.
-+ * there may exist a problem somewhere else.
++ * if it returns true, the the caller should free hinode->hi_notify,
++ * otherwise ->free() frees it.
+ */
-+ err = -EINVAL;
-+ if (unlikely(d_inode(a->dst_parent) == d_inode(a->src_dentry)))
-+ goto out_unlock;
++ int (*free)(struct au_hinode *hinode,
++ struct au_hnotify *hn) __must_check;
+
-+ au_fset_ren(a->auren_flags, ISSAMEDIR); /* temporary */
-+ di_write_lock_parent(a->dst_parent);
++ void (*fin)(void);
++ int (*init)(void);
+
-+ /* which branch we process */
-+ err = au_ren_wbr(a);
-+ if (unlikely(err < 0))
-+ goto out_parent;
-+ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt);
-+ a->h_path.mnt = au_br_mnt(a->br);
++ int (*reset_br)(unsigned int udba, struct au_branch *br, int perm);
++ void (*fin_br)(struct au_branch *br);
++ int (*init_br)(struct au_branch *br, int perm);
++};
+
-+ /* are they available to be renamed */
-+ err = au_ren_may_dir(a);
-+ if (unlikely(err))
-+ goto out_children;
++/* hnotify.c */
++int au_hn_alloc(struct au_hinode *hinode, struct inode *inode);
++void au_hn_free(struct au_hinode *hinode);
++void au_hn_ctl(struct au_hinode *hinode, int do_set);
++void au_hn_reset(struct inode *inode, unsigned int flags);
++int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask,
++ struct qstr *h_child_qstr, struct inode *h_child_inode);
++int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm);
++int au_hnotify_init_br(struct au_branch *br, int perm);
++void au_hnotify_fin_br(struct au_branch *br);
++int __init au_hnotify_init(void);
++void au_hnotify_fin(void);
+
-+ /* prepare the writable parent dir on the same branch */
-+ 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;
-+ }
++/* hfsnotify.c */
++extern const struct au_hnotify_op au_hnotify_op;
+
-+ 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->auren_flags, WHSRC);
++static inline
++void au_hn_init(struct au_hinode *hinode)
++{
++ hinode->hi_notify = NULL;
++}
+
-+ /* cpup src */
-+ 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);
-+ if (!err) {
-+ struct au_cp_generic cpg = {
-+ .dentry = a->src_dentry,
-+ .bdst = a->btgt,
-+ .bsrc = a->src_btop,
-+ .len = -1,
-+ .pin = &pin,
-+ .flags = AuCpup_DTIME | AuCpup_HOPEN
-+ };
-+ 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_btop = a->btgt;
-+ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt);
-+ if (!a->exchange)
-+ au_fset_ren(a->auren_flags, WHSRC);
-+ }
++static inline struct au_hnotify *au_hn(struct au_hinode *hinode)
++{
++ return hinode->hi_notify;
++}
+
-+ /* 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);
-+ }
++#else
++AuStub(int, au_hn_alloc, return -EOPNOTSUPP,
++ struct au_hinode *hinode __maybe_unused,
++ struct inode *inode __maybe_unused)
++AuStub(struct au_hnotify *, au_hn, return NULL, struct au_hinode *hinode)
++AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused)
++AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused,
++ int do_set __maybe_unused)
++AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused,
++ unsigned int flags __maybe_unused)
++AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused,
++ struct au_branch *br __maybe_unused,
++ int perm __maybe_unused)
++AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused,
++ int perm __maybe_unused)
++AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused)
++AuStubInt0(__init au_hnotify_init, void)
++AuStubVoid(au_hnotify_fin, void)
++AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused)
++#endif /* CONFIG_AUFS_HNOTIFY */
++
++static inline void au_hn_suspend(struct au_hinode *hdir)
++{
++ au_hn_ctl(hdir, /*do_set*/0);
++}
++
++static inline void au_hn_resume(struct au_hinode *hdir)
++{
++ au_hn_ctl(hdir, /*do_set*/1);
++}
++
++static inline void au_hn_inode_lock(struct au_hinode *hdir)
++{
++ inode_lock(hdir->hi_inode);
++ au_hn_suspend(hdir);
++}
++
++static inline void au_hn_inode_lock_nested(struct au_hinode *hdir,
++ unsigned int sc __maybe_unused)
++{
++ inode_lock_nested(hdir->hi_inode, sc);
++ au_hn_suspend(hdir);
++}
++
++#if 0 /* unused */
++#include "vfsub.h"
++static inline void au_hn_inode_lock_shared_nested(struct au_hinode *hdir,
++ unsigned int sc)
++{
++ vfsub_inode_lock_shared_nested(hdir->hi_inode, sc);
++ au_hn_suspend(hdir);
++}
++#endif
++
++static inline void au_hn_inode_unlock(struct au_hinode *hdir)
++{
++ au_hn_resume(hdir);
++ inode_unlock(hdir->hi_inode);
++}
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_INODE_H__ */
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/ioctl.c linux-4.9/fs/aufs/ioctl.c
+--- linux-4.9/fs/aufs/ioctl.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/ioctl.c 2021-02-24 16:15:09.531573855 +0100
+@@ -0,0 +1,219 @@
++/*
++ * 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
++ * 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 <http://www.gnu.org/licenses/>.
++ */
+
-+ /* lock them all */
-+ err = au_ren_lock(a);
-+ if (unlikely(err))
-+ /* leave the copied-up one */
-+ goto out_children;
++/*
++ * ioctl
++ * plink-management and readdir in userspace.
++ * assist the pathconf(3) wrapper library.
++ * move-down
++ * File-based Hierarchical Storage Management.
++ */
+
-+ 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;
-+ }
++#include <linux/compat.h>
++#include <linux/file.h>
++#include "aufs.h"
+
-+ /* store timestamps to be revertible */
-+ au_ren_dt(a);
++static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg)
++{
++ int err, fd;
++ aufs_bindex_t wbi, bindex, bbot;
++ struct file *h_file;
++ struct super_block *sb;
++ struct dentry *root;
++ struct au_branch *br;
++ struct aufs_wbr_fd wbrfd = {
++ .oflags = au_dir_roflags,
++ .brid = -1
++ };
++ const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY
++ | O_NOATIME | O_CLOEXEC;
+
-+ /* 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;
-+ }
++ AuDebugOn(wbrfd.oflags & ~valid);
+
-+ /* here we go */
-+ err = do_rename(a);
-+ if (unlikely(err))
-+ goto out_dirren;
++ if (arg) {
++ err = copy_from_user(&wbrfd, arg, sizeof(wbrfd));
++ if (unlikely(err)) {
++ err = -EFAULT;
++ goto out;
++ }
+
-+ if (au_ftest_ren(a->auren_flags, DIRREN))
-+ au_dr_rename_fin(a->src_dentry, a->btgt, rev);
++ err = -EINVAL;
++ AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid);
++ wbrfd.oflags |= au_dir_roflags;
++ AuDbg("0%o\n", wbrfd.oflags);
++ if (unlikely(wbrfd.oflags & ~valid))
++ goto out;
++ }
+
-+ /* update dir attributes */
-+ au_ren_refresh_dir(a);
++ fd = get_unused_fd_flags(0);
++ err = fd;
++ if (unlikely(fd < 0))
++ goto out;
+
-+ /* dput/iput all lower dentries */
-+ au_ren_refresh(a);
++ h_file = ERR_PTR(-EINVAL);
++ wbi = 0;
++ br = NULL;
++ sb = path->dentry->d_sb;
++ root = sb->s_root;
++ aufs_read_lock(root, AuLock_IR);
++ bbot = au_sbbot(sb);
++ if (wbrfd.brid >= 0) {
++ wbi = au_br_index(sb, wbrfd.brid);
++ if (unlikely(wbi < 0 || wbi > bbot))
++ goto out_unlock;
++ }
+
-+ goto out_hdir; /* success */
++ h_file = ERR_PTR(-ENOENT);
++ br = au_sbr(sb, wbi);
++ if (!au_br_writable(br->br_perm)) {
++ if (arg)
++ goto out_unlock;
+
-+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_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_dbtop(a->dst_dentry, a->dst_btop);
-+ }
-+out_parent:
-+ 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);
++ bindex = wbi + 1;
++ wbi = -1;
++ for (; bindex <= bbot; bindex++) {
++ br = au_sbr(sb, bindex);
++ if (au_br_writable(br->br_perm)) {
++ wbi = bindex;
++ br = au_sbr(sb, wbi);
++ break;
++ }
+ }
-+ 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->auren_flags, ISSAMEDIR))
-+ di_write_unlock(a->dst_parent);
-+ else
-+ di_write_unlock2(a->src_parent, a->dst_parent);
++ AuDbg("wbi %d\n", wbi);
++ if (wbi >= 0)
++ h_file = au_h_open(root, wbi, wbrfd.oflags, NULL,
++ /*force_wr*/0);
++
+out_unlock:
-+ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry);
-+out_free:
-+ iput(a->dst_inode);
-+ if (a->thargs)
-+ au_whtmp_rmdir_free(a->thargs);
-+ kfree(a);
++ aufs_read_unlock(root, AuLock_IR);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out_fd;
++
++ au_br_put(br); /* cf. au_h_open() */
++ fd_install(fd, h_file);
++ err = fd;
++ goto out; /* success */
++
++out_fd:
++ put_unused_fd(fd);
+out:
+ AuTraceErr(err);
+ return err;
+}
-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 2018-04-15 08:49:13.394483860 +0200
-@@ -0,0 +1,198 @@
-+config AUFS_FS
-+ tristate "Aufs (Advanced multi layered unification filesystem) support"
-+ help
-+ Aufs is a stackable unification filesystem such as Unionfs,
-+ which unifies several directories and provides a merged single
-+ directory.
-+ In the early days, aufs was entirely re-designed and
-+ re-implemented Unionfs Version 1.x series. Introducing many
-+ original ideas, approaches and improvements, it becomes totally
-+ different from Unionfs while keeping the basic features.
+
-+if AUFS_FS
-+choice
-+ prompt "Maximum number of branches"
-+ default AUFS_BRANCH_MAX_127
-+ help
-+ Specifies the maximum number of branches (or member directories)
-+ in a single aufs. The larger value consumes more system
-+ resources and has a minor impact to performance.
-+config AUFS_BRANCH_MAX_127
-+ bool "127"
-+ help
-+ Specifies the maximum number of branches (or member directories)
-+ in a single aufs. The larger value consumes more system
-+ resources and has a minor impact to performance.
-+config AUFS_BRANCH_MAX_511
-+ bool "511"
-+ help
-+ Specifies the maximum number of branches (or member directories)
-+ in a single aufs. The larger value consumes more system
-+ resources and has a minor impact to performance.
-+config AUFS_BRANCH_MAX_1023
-+ bool "1023"
-+ help
-+ Specifies the maximum number of branches (or member directories)
-+ in a single aufs. The larger value consumes more system
-+ resources and has a minor impact to performance.
-+config AUFS_BRANCH_MAX_32767
-+ bool "32767"
-+ help
-+ Specifies the maximum number of branches (or member directories)
-+ in a single aufs. The larger value consumes more system
-+ resources and has a minor impact to performance.
-+endchoice
++/* ---------------------------------------------------------------------- */
+
-+config AUFS_SBILIST
-+ bool
-+ depends on AUFS_MAGIC_SYSRQ || PROC_FS
-+ default y
-+ help
-+ Automatic configuration for internal use.
-+ When aufs supports Magic SysRq or /proc, enabled automatically.
++long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ long err;
++ struct dentry *dentry;
+
-+config AUFS_HNOTIFY
-+ bool "Detect direct branch access (bypassing aufs)"
-+ help
-+ If you want to modify files on branches directly, eg. bypassing aufs,
-+ and want aufs to detect the changes of them fully, then enable this
-+ option and use 'udba=notify' mount option.
-+ Currently there is only one available configuration, "fsnotify".
-+ It will have a negative impact to the performance.
-+ See detail in aufs.5.
++ switch (cmd) {
++ case AUFS_CTL_RDU:
++ case AUFS_CTL_RDU_INO:
++ err = au_rdu_ioctl(file, cmd, arg);
++ break;
+
-+choice
-+ prompt "method" if AUFS_HNOTIFY
-+ default AUFS_HFSNOTIFY
-+config AUFS_HFSNOTIFY
-+ bool "fsnotify"
-+ select FSNOTIFY
-+endchoice
++ case AUFS_CTL_WBR_FD:
++ err = au_wbr_fd(&file->f_path, (void __user *)arg);
++ break;
+
-+config AUFS_EXPORT
-+ bool "NFS-exportable aufs"
-+ depends on EXPORTFS
-+ help
-+ If you want to export your mounted aufs via NFS, then enable this
-+ option. There are several requirements for this configuration.
-+ See detail in aufs.5.
++ case AUFS_CTL_IBUSY:
++ err = au_ibusy_ioctl(file, arg);
++ break;
+
-+config AUFS_INO_T_64
-+ bool
-+ depends on AUFS_EXPORT
-+ depends on 64BIT && !(ALPHA || S390)
-+ default y
-+ help
-+ Automatic configuration for internal use.
-+ /* typedef unsigned long/int __kernel_ino_t */
-+ /* alpha and s390x are int */
++ case AUFS_CTL_BRINFO:
++ err = au_brinfo_ioctl(file, arg);
++ break;
+
-+config AUFS_XATTR
-+ bool "support for XATTR/EA (including Security Labels)"
-+ help
-+ If your branch fs supports XATTR/EA and you want to make them
-+ available in aufs too, then enable this opsion and specify the
-+ branch attributes for EA.
-+ See detail in aufs.5.
++ case AUFS_CTL_FHSM_FD:
++ dentry = file->f_path.dentry;
++ if (IS_ROOT(dentry))
++ err = au_fhsm_fd(dentry->d_sb, arg);
++ else
++ err = -ENOTTY;
++ break;
+
-+config AUFS_FHSM
-+ bool "File-based Hierarchical Storage Management"
-+ help
-+ Hierarchical Storage Management (or HSM) is a well-known feature
-+ in the storage world. Aufs provides this feature as file-based.
-+ with multiple branches.
-+ These multiple branches are prioritized, ie. the topmost one
-+ should be the fastest drive and be used heavily.
++ default:
++ /* do not call the lower */
++ AuDbg("0x%x\n", cmd);
++ err = -ENOTTY;
++ }
+
-+config AUFS_RDU
-+ bool "Readdir in userspace"
-+ help
-+ Aufs has two methods to provide a merged view for a directory,
-+ by a user-space library and by kernel-space natively. The latter
-+ is always enabled but sometimes large and slow.
-+ If you enable this option, install the library in aufs2-util
-+ package, and set some environment variables for your readdir(3),
-+ then the work will be handled in user-space which generally
-+ shows better performance in most cases.
-+ See detail in aufs.5.
++ AuTraceErr(err);
++ return err;
++}
+
-+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.
++long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ long err;
+
-+config AUFS_SHWH
-+ bool "Show whiteouts"
-+ help
-+ If you want to make the whiteouts in aufs visible, then enable
-+ this option and specify 'shwh' mount option. Although it may
-+ sounds like philosophy or something, but in technically it
-+ simply shows the name of whiteout with keeping its behaviour.
++ switch (cmd) {
++ case AUFS_CTL_MVDOWN:
++ err = au_mvdown(file->f_path.dentry, (void __user *)arg);
++ break;
+
-+config AUFS_BR_RAMFS
-+ bool "Ramfs (initramfs/rootfs) as an aufs branch"
-+ help
-+ If you want to use ramfs as an aufs branch fs, then enable this
-+ option. Generally tmpfs is recommended.
-+ Aufs prohibited them to be a branch fs by default, because
-+ initramfs becomes unusable after switch_root or something
-+ generally. If you sets initramfs as an aufs branch and boot your
-+ system by switch_root, you will meet a problem easily since the
-+ files in initramfs may be inaccessible.
-+ Unless you are going to use ramfs as an aufs branch fs without
-+ switch_root or something, leave it N.
++ case AUFS_CTL_WBR_FD:
++ err = au_wbr_fd(&file->f_path, (void __user *)arg);
++ break;
+
-+config AUFS_BR_FUSE
-+ bool "Fuse fs as an aufs branch"
-+ depends on FUSE_FS
-+ select AUFS_POLL
-+ help
-+ If you want to use fuse-based userspace filesystem as an aufs
-+ branch fs, then enable this option.
-+ It implements the internal poll(2) operation which is
-+ implemented by fuse only (curretnly).
++ default:
++ /* do not call the lower */
++ AuDbg("0x%x\n", cmd);
++ err = -ENOTTY;
++ }
+
-+config AUFS_POLL
-+ bool
-+ help
-+ Automatic configuration for internal use.
++ AuTraceErr(err);
++ return err;
++}
+
-+config AUFS_BR_HFSPLUS
-+ bool "Hfsplus as an aufs branch"
-+ depends on HFSPLUS_FS
-+ default y
-+ help
-+ If you want to use hfsplus fs as an aufs branch fs, then enable
-+ this option. This option introduces a small overhead at
-+ copying-up a file on hfsplus.
++#ifdef CONFIG_COMPAT
++long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd,
++ unsigned long arg)
++{
++ long err;
+
-+config AUFS_BDEV_LOOP
-+ bool
-+ depends on BLK_DEV_LOOP
-+ default y
-+ help
-+ Automatic configuration for internal use.
-+ Convert =[ym] into =y.
++ switch (cmd) {
++ case AUFS_CTL_RDU:
++ case AUFS_CTL_RDU_INO:
++ err = au_rdu_compat_ioctl(file, cmd, arg);
++ break;
+
-+config AUFS_DEBUG
-+ bool "Debug aufs"
-+ help
-+ Enable this to compile aufs internal debug code.
-+ It will have a negative impact to the performance.
++ case AUFS_CTL_IBUSY:
++ err = au_ibusy_compat_ioctl(file, arg);
++ break;
+
-+config AUFS_MAGIC_SYSRQ
-+ bool
-+ depends on AUFS_DEBUG && MAGIC_SYSRQ
-+ default y
-+ help
-+ Automatic configuration for internal use.
-+ When aufs supports Magic SysRq, enabled automatically.
-+endif
-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 2018-04-15 08:49:13.401150731 +0200
-@@ -0,0 +1,147 @@
++ case AUFS_CTL_BRINFO:
++ err = au_brinfo_compat_ioctl(file, arg);
++ break;
++
++ default:
++ err = aufs_ioctl_dir(file, cmd, arg);
++ }
++
++ AuTraceErr(err);
++ return err;
++}
++
++long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd,
++ unsigned long arg)
++{
++ return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg));
++}
++#endif
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/loop.c linux-4.9/fs/aufs/loop.c
+--- linux-4.9/fs/aufs/loop.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/loop.c 2021-02-24 16:15:09.541574180 +0100
+@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ *
+ symbol_put(loop_backing_file);
+ kfree(au_warn_loopback_array);
+}
-diff -urN /usr/share/empty/fs/aufs/loop.h linux/fs/aufs/loop.h
---- /usr/share/empty/fs/aufs/loop.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/loop.h 2018-04-15 08:49:13.401150731 +0200
-@@ -0,0 +1,52 @@
++
++/* ---------------------------------------------------------------------- */
++
++/* support the loopback block device insude aufs */
++
++struct file *aufs_real_loop(struct file *file)
++{
++ struct file *f;
++
++ BUG_ON(!au_test_aufs(file->f_path.dentry->d_sb));
++ fi_read_lock(file);
++ f = au_hf_top(file);
++ fi_read_unlock(file);
++ AuDebugOn(!f);
++ return f;
++}
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/loop.h linux-4.9/fs/aufs/loop.h
+--- linux-4.9/fs/aufs/loop.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/loop.h 2021-02-24 16:15:09.541574180 +0100
+@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ *
+
+int au_loopback_init(void);
+void au_loopback_fin(void);
++
++struct file *aufs_real_loop(struct file *file);
+#else
++AuStub(struct file *, loop_backing_file, return NULL)
++
+AuStubInt0(au_test_loopback_overlap, struct super_block *sb,
+ struct dentry *h_adding)
+AuStubInt0(au_test_loopback_kthread, void)
+
+AuStubInt0(au_loopback_init, void)
+AuStubVoid(au_loopback_fin, void)
++
++AuStub(struct file *, aufs_real_loop, return NULL, struct file *file)
+#endif /* BLK_DEV_LOOP */
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:11.027744356 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/magic.mk linux-4.9/fs/aufs/magic.mk
+--- linux-4.9/fs/aufs/magic.mk 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/magic.mk 2021-02-24 16:15:09.531573855 +0100
@@ -0,0 +1,30 @@
+
+# defined in ${srctree}/fs/fuse/inode.c
+ifdef CONFIG_HFSPLUS_FS
+ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b
+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 2018-04-15 08:49:13.394483860 +0200
-@@ -0,0 +1,45 @@
-+
-+include ${src}/magic.mk
-+ifeq (${CONFIG_AUFS_FS},m)
-+include ${src}/conf.mk
-+endif
-+-include ${src}/priv_def.mk
-+
-+# cf. include/linux/kernel.h
-+# enable pr_debug
-+ccflags-y += -DDEBUG
-+# sparse requires the full pathname
-+ifdef M
-+ccflags-y += -include ${M}/../../include/uapi/linux/aufs_type.h
-+else
-+ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h
-+endif
-+
-+obj-$(CONFIG_AUFS_FS) += aufs.o
-+aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \
-+ wkq.o vfsub.o dcsub.o \
-+ cpup.o whout.o wbr_policy.o \
-+ dinfo.o dentry.o \
-+ dynop.o \
-+ finfo.o file.o f_op.o \
-+ dir.o vdir.o \
-+ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \
-+ mvdown.o ioctl.o
-+
-+# all are boolean
-+aufs-$(CONFIG_PROC_FS) += procfs.o plink.o
-+aufs-$(CONFIG_SYSFS) += sysfs.o
-+aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o
-+aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o
-+aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o
-+aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o
-+aufs-$(CONFIG_AUFS_EXPORT) += export.o
-+aufs-$(CONFIG_AUFS_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
-+aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o
-+aufs-$(CONFIG_AUFS_DEBUG) += debug.o
-+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 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/module.c linux-4.9/fs/aufs/module.c
+--- linux-4.9/fs/aufs/module.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/module.c 2021-02-24 16:15:09.531573855 +0100
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+module_init(aufs_init);
+module_exit(aufs_exit);
-diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h
---- /usr/share/empty/fs/aufs/module.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/module.h 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/module.h linux-4.9/fs/aufs/module.h
+--- linux-4.9/fs/aufs/module.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/module.h 2021-02-24 16:15:09.531573855 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_MODULE_H__ */
-diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c
---- /usr/share/empty/fs/aufs/mvdown.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/mvdown.c 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/mvdown.c linux-4.9/fs/aufs/mvdown.c
+--- linux-4.9/fs/aufs/mvdown.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/mvdown.c 2021-02-24 16:15:09.531573855 +0100
@@ -0,0 +1,704 @@
+/*
+ * Copyright (C) 2011-2018 Junjiro R. Okajima
+ 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 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/opts.c linux-4.9/fs/aufs/opts.c
+--- linux-4.9/fs/aufs/opts.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/opts.c 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,1913 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+{
+ return au_mntflags(sb) & AuOptMask_UDBA;
+}
-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 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/opts.h linux-4.9/fs/aufs/opts.h
+--- linux-4.9/fs/aufs/opts.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/opts.h 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/plink.c linux-4.9/fs/aufs/plink.c
+--- linux-4.9/fs/aufs/plink.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/plink.c 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ }
+ }
+}
-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 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/poll.c linux-4.9/fs/aufs/poll.c
+--- linux-4.9/fs/aufs/poll.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/poll.c 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ 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 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/posix_acl.c linux-4.9/fs/aufs/posix_acl.c
+--- linux-4.9/fs/aufs/posix_acl.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/posix_acl.c 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014-2018 Junjiro R. Okajima
+out:
+ return err;
+}
-diff -urN /usr/share/empty/fs/aufs/procfs.c linux/fs/aufs/procfs.c
---- /usr/share/empty/fs/aufs/procfs.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/procfs.c 2018-04-15 08:49:13.401150731 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/procfs.c linux-4.9/fs/aufs/procfs.c
+--- linux-4.9/fs/aufs/procfs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/procfs.c 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2010-2018 Junjiro R. Okajima
+out:
+ return err;
+}
-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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/rdu.c linux-4.9/fs/aufs/rdu.c
+--- linux-4.9/fs/aufs/rdu.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/rdu.c 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ return err;
+}
+#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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/rwsem.h linux-4.9/fs/aufs/rwsem.h
+--- linux-4.9/fs/aufs/rwsem.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/rwsem.h 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/sbinfo.c linux-4.9/fs/aufs/sbinfo.c
+--- linux-4.9/fs/aufs/sbinfo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/sbinfo.c 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ di_write_unlock2(d1, d2);
+ si_read_unlock(d1->d_sb);
+}
-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 2018-04-15 08:49:13.404484168 +0200
-@@ -0,0 +1,1046 @@
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/super.c linux-4.9/fs/aufs/super.c
+--- linux-4.9/fs/aufs/super.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/super.c 2021-02-24 16:15:09.541574180 +0100
+@@ -0,0 +1,1049 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ *
+ .statfs = aufs_statfs,
+ .put_super = aufs_put_super,
+ .sync_fs = aufs_sync_fs,
-+ .remount_fs = aufs_remount_fs
++ .remount_fs = aufs_remount_fs,
++#ifdef CONFIG_AUFS_BDEV_LOOP
++ .real_loop = aufs_real_loop
++#endif
+};
+
+/* ---------------------------------------------------------------------- */
+ /* no need to __module_get() and module_put(). */
+ .owner = THIS_MODULE,
+};
-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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/super.h linux-4.9/fs/aufs/super.h
+--- linux-4.9/fs/aufs/super.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/super.h 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,617 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/sysaufs.c linux-4.9/fs/aufs/sysaufs.c
+--- linux-4.9/fs/aufs/sysaufs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/sysaufs.c 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/sysaufs.h linux-4.9/fs/aufs/sysaufs.h
+--- linux-4.9/fs/aufs/sysaufs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/sysaufs.h 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/sysfs.c linux-4.9/fs/aufs/sysfs.c
+--- linux-4.9/fs/aufs/sysfs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/sysfs.c 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ }
+ }
+}
-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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/sysrq.c linux-4.9/fs/aufs/sysrq.c
+--- linux-4.9/fs/aufs/sysrq.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/sysrq.c 2021-02-24 16:15:09.534907296 +0100
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ if (unlikely(err))
+ pr_err("err %d (ignored)\n", err);
+}
-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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/vdir.c linux-4.9/fs/aufs/vdir.c
+--- linux-4.9/fs/aufs/vdir.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/vdir.c 2021-02-24 16:15:09.538240738 +0100
@@ -0,0 +1,893 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ /* smp_mb(); */
+ return 0;
+}
-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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/vfsub.c linux-4.9/fs/aufs/vfsub.c
+--- linux-4.9/fs/aufs/vfsub.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/vfsub.c 2021-02-24 16:15:09.538240738 +0100
@@ -0,0 +1,894 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+ return err;
+}
-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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/vfsub.h linux-4.9/fs/aufs/vfsub.h
+--- linux-4.9/fs/aufs/vfsub.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/vfsub.h 2021-02-24 16:15:09.538240738 +0100
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/wbr_policy.c linux-4.9/fs/aufs/wbr_policy.c
+--- linux-4.9/fs/aufs/wbr_policy.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/wbr_policy.c 2021-02-24 16:15:09.538240738 +0100
@@ -0,0 +1,830 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ .fin = au_wbr_create_fin_mfs
+ }
+};
-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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/whout.c linux-4.9/fs/aufs/whout.c
+--- linux-4.9/fs/aufs/whout.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/whout.c 2021-02-24 16:15:09.538240738 +0100
@@ -0,0 +1,1061 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ au_whtmp_rmdir_free(args);
+ }
+}
-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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/whout.h linux-4.9/fs/aufs/whout.h
+--- linux-4.9/fs/aufs/whout.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/whout.h 2021-02-24 16:15:09.538240738 +0100
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/wkq.c linux-4.9/fs/aufs/wkq.c
+--- linux-4.9/fs/aufs/wkq.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/wkq.c 2021-02-24 16:15:09.538240738 +0100
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+ return err;
+}
-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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/wkq.h linux-4.9/fs/aufs/wkq.h
+--- linux-4.9/fs/aufs/wkq.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/wkq.h 2021-02-24 16:15:09.538240738 +0100
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+
+#endif /* __KERNEL__ */
+#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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/xattr.c linux-4.9/fs/aufs/xattr.c
+--- linux-4.9/fs/aufs/xattr.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/xattr.c 2021-02-24 16:15:09.538240738 +0100
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2014-2018 Junjiro R. Okajima
+{
+ sb->s_xattr = au_xattr_handlers;
+}
-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 2018-04-15 08:49:13.404484168 +0200
+diff -urNp -x '*.orig' linux-4.9/fs/aufs/xino.c linux-4.9/fs/aufs/xino.c
+--- linux-4.9/fs/aufs/xino.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/fs/aufs/xino.c 2021-02-24 16:15:09.538240738 +0100
@@ -0,0 +1,1415 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+ goto again;
+ }
+
-+out_unlock:
-+ spin_unlock(&xino->xi_nondir.spin);
-+out:
-+ return err;
-+}
-diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/linux/aufs_type.h
---- /usr/share/empty/include/uapi/linux/aufs_type.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux/include/uapi/linux/aufs_type.h 2018-04-15 08:49:13.404484168 +0200
++out_unlock:
++ spin_unlock(&xino->xi_nondir.spin);
++out:
++ return err;
++}
+diff -urNp -x '*.orig' linux-4.9/fs/dcache.c linux-4.9/fs/dcache.c
+--- linux-4.9/fs/dcache.c 2021-02-24 16:14:57.291175760 +0100
++++ linux-4.9/fs/dcache.c 2021-02-24 16:15:09.514906646 +0100
+@@ -1206,7 +1206,7 @@ enum d_walk_ret {
+ *
+ * The @enter() and @finish() 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 *))
+ {
+@@ -1314,6 +1314,7 @@ rename_retry:
+ seq = 1;
+ goto again;
+ }
++EXPORT_SYMBOL_GPL(d_walk);
+
+ /*
+ * Search for at least 1 mount point in the dentry's subdirs.
+@@ -2935,6 +2936,7 @@ void d_exchange(struct dentry *dentry1,
+
+ write_sequnlock(&rename_lock);
+ }
++EXPORT_SYMBOL_GPL(d_exchange);
+
+ /**
+ * d_ancestor - search for an ancestor
+diff -urNp -x '*.orig' linux-4.9/fs/exec.c linux-4.9/fs/exec.c
+--- linux-4.9/fs/exec.c 2021-02-24 16:14:56.427814341 +0100
++++ linux-4.9/fs/exec.c 2021-02-24 16:15:09.514906646 +0100
+@@ -104,6 +104,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_GPL(path_noexec);
+
+ #ifdef CONFIG_USELIB
+ /*
+diff -urNp -x '*.orig' linux-4.9/fs/fcntl.c linux-4.9/fs/fcntl.c
+--- linux-4.9/fs/fcntl.c 2021-02-24 16:14:57.297842644 +0100
++++ linux-4.9/fs/fcntl.c 2021-02-24 16:15:09.514906646 +0100
+@@ -30,7 +30,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;
+@@ -61,6 +61,8 @@ static int setfl(int fd, struct file * f
+
+ 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;
+
+@@ -81,6 +83,7 @@ static int setfl(int fd, struct file * f
+ out:
+ return error;
+ }
++EXPORT_SYMBOL_GPL(setfl);
+
+ static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
+ int force)
+diff -urNp -x '*.orig' linux-4.9/fs/file_table.c linux-4.9/fs/file_table.c
+--- linux-4.9/fs/file_table.c 2021-02-24 16:14:57.297842644 +0100
++++ linux-4.9/fs/file_table.c 2021-02-24 16:15:09.514906646 +0100
+@@ -151,6 +151,7 @@ over:
+ }
+ return ERR_PTR(-ENFILE);
+ }
++EXPORT_SYMBOL_GPL(get_empty_filp);
+
+ /**
+ * alloc_file - allocate and initialize a 'struct file'
+@@ -264,6 +265,7 @@ void flush_delayed_fput(void)
+ {
+ delayed_fput(NULL);
+ }
++EXPORT_SYMBOL_GPL(flush_delayed_fput);
+
+ static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput);
+
+@@ -306,6 +308,7 @@ void __fput_sync(struct file *file)
+ }
+
+ EXPORT_SYMBOL(fput);
++EXPORT_SYMBOL_GPL(__fput_sync);
+
+ void put_filp(struct file *file)
+ {
+@@ -316,6 +319,7 @@ void put_filp(struct file *file)
+ file_free(file);
+ }
+ }
++EXPORT_SYMBOL_GPL(put_filp);
+
+ void __init files_init(void)
+ {
+diff -urNp -x '*.orig' linux-4.9/fs/inode.c linux-4.9/fs/inode.c
+--- linux-4.9/fs/inode.c 2021-02-24 16:14:57.301176086 +0100
++++ linux-4.9/fs/inode.c 2021-02-24 16:15:09.514906646 +0100
+@@ -1651,7 +1651,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 timespec *time, int flags)
++int update_time(struct inode *inode, struct timespec *time, int flags)
+ {
+ int (*update_time)(struct inode *, struct timespec *, int);
+
+@@ -1660,6 +1660,7 @@ static int update_time(struct inode *ino
+
+ return update_time(inode, time, flags);
+ }
++EXPORT_SYMBOL_GPL(update_time);
+
+ /**
+ * touch_atime - update the access time
+diff -urNp -x '*.orig' linux-4.9/fs/namespace.c linux-4.9/fs/namespace.c
+--- linux-4.9/fs/namespace.c 2021-02-24 16:14:57.304509527 +0100
++++ linux-4.9/fs/namespace.c 2021-02-24 16:15:09.514906646 +0100
+@@ -472,6 +472,7 @@ void __mnt_drop_write(struct vfsmount *m
+ mnt_dec_writers(real_mount(mnt));
+ preempt_enable();
+ }
++EXPORT_SYMBOL_GPL(__mnt_drop_write);
+
+ /**
+ * mnt_drop_write - give up write access to a mount
+@@ -804,6 +805,13 @@ static inline int check_mnt(struct mount
+ 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));
++}
++EXPORT_SYMBOL_GPL(is_current_mnt_ns);
++
+ /*
+ * vfsmount lock must be held for write
+ */
+@@ -1905,6 +1913,7 @@ int iterate_mounts(int (*f)(struct vfsmo
+ }
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(iterate_mounts);
+
+ static void cleanup_group_ids(struct mount *mnt, struct mount *end)
+ {
+diff -urNp -x '*.orig' linux-4.9/fs/notify/group.c linux-4.9/fs/notify/group.c
+--- linux-4.9/fs/notify/group.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/notify/group.c 2021-02-24 16:15:09.514906646 +0100
+@@ -22,6 +22,7 @@
+ #include <linux/srcu.h>
+ #include <linux/rculist.h>
+ #include <linux/wait.h>
++#include <linux/module.h>
+
+ #include <linux/fsnotify_backend.h>
+ #include "fsnotify.h"
+@@ -100,6 +101,7 @@ void fsnotify_get_group(struct fsnotify_
+ {
+ atomic_inc(&group->refcnt);
+ }
++EXPORT_SYMBOL_GPL(fsnotify_get_group);
+
+ /*
+ * Drop a reference to a group. Free it if it's through.
+@@ -109,6 +111,7 @@ void fsnotify_put_group(struct fsnotify_
+ if (atomic_dec_and_test(&group->refcnt))
+ fsnotify_final_destroy_group(group);
+ }
++EXPORT_SYMBOL_GPL(fsnotify_put_group);
+
+ /*
+ * Create a new fsnotify_group and hold a reference for the group returned.
+@@ -137,6 +140,7 @@ struct fsnotify_group *fsnotify_alloc_gr
+
+ return group;
+ }
++EXPORT_SYMBOL_GPL(fsnotify_alloc_group);
+
+ int fsnotify_fasync(int fd, struct file *file, int on)
+ {
+diff -urNp -x '*.orig' linux-4.9/fs/notify/mark.c linux-4.9/fs/notify/mark.c
+--- linux-4.9/fs/notify/mark.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/notify/mark.c 2021-02-24 16:15:09.514906646 +0100
+@@ -113,6 +113,7 @@ void fsnotify_put_mark(struct fsnotify_m
+ mark->free_mark(mark);
+ }
+ }
++EXPORT_SYMBOL_GPL(fsnotify_put_mark);
+
+ /* Calculate mask of events for a list of marks */
+ u32 fsnotify_recalc_mask(struct hlist_head *head)
+@@ -230,6 +231,7 @@ void fsnotify_destroy_mark(struct fsnoti
+ mutex_unlock(&group->mark_mutex);
+ fsnotify_free_mark(mark);
+ }
++EXPORT_SYMBOL_GPL(fsnotify_destroy_mark);
+
+ void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock)
+ {
+@@ -415,6 +417,7 @@ err:
+
+ return ret;
+ }
++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)
+@@ -533,6 +536,7 @@ void fsnotify_init_mark(struct fsnotify_
+ atomic_set(&mark->refcnt, 1);
+ mark->free_mark = free_mark;
+ }
++EXPORT_SYMBOL_GPL(fsnotify_init_mark);
+
+ /*
+ * Destroy all marks in destroy_list, waits for SRCU period to finish before
+diff -urNp -x '*.orig' linux-4.9/fs/open.c linux-4.9/fs/open.c
+--- linux-4.9/fs/open.c 2021-02-24 16:14:57.311176411 +0100
++++ linux-4.9/fs/open.c 2021-02-24 16:15:09.514906646 +0100
+@@ -69,6 +69,7 @@ int do_truncate(struct dentry *dentry, l
+ inode_unlock(dentry->d_inode);
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(do_truncate);
+
+ long vfs_truncate(struct path *path, loff_t length)
+ {
+@@ -737,6 +738,7 @@ int open_check_o_direct(struct file *f)
+ }
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(open_check_o_direct);
+
+ static int do_dentry_open(struct file *f,
+ struct inode *inode,
+diff -urNp -x '*.orig' linux-4.9/fs/proc/base.c linux-4.9/fs/proc/base.c
+--- linux-4.9/fs/proc/base.c 2021-02-24 16:14:57.374511804 +0100
++++ linux-4.9/fs/proc/base.c 2021-02-24 16:15:09.508239763 +0100
+@@ -2016,7 +2016,7 @@ static int map_files_get_link(struct den
+ down_read(&mm->mmap_sem);
+ vma = find_exact_vma(mm, vm_start, vm_end);
+ if (vma && vma->vm_file) {
+- *path = vma->vm_file->f_path;
++ *path = vma_pr_or_file(vma)->f_path;
+ path_get(path);
+ rc = 0;
+ }
+diff -urNp -x '*.orig' linux-4.9/fs/proc/nommu.c linux-4.9/fs/proc/nommu.c
+--- linux-4.9/fs/proc/nommu.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/proc/nommu.c 2021-02-24 16:15:09.508239763 +0100
+@@ -45,7 +45,10 @@ static int nommu_region_show(struct seq_
+ file = region->vm_file;
+
+ if (file) {
+- struct inode *inode = file_inode(region->vm_file);
++ struct inode *inode;
++
++ file = vmr_pr_or_file(region);
++ inode = file_inode(file);
+ dev = inode->i_sb->s_dev;
+ ino = inode->i_ino;
+ }
+diff -urNp -x '*.orig' linux-4.9/fs/proc/task_mmu.c linux-4.9/fs/proc/task_mmu.c
+--- linux-4.9/fs/proc/task_mmu.c 2021-02-24 16:14:56.501150059 +0100
++++ linux-4.9/fs/proc/task_mmu.c 2021-02-24 16:15:09.508239763 +0100
+@@ -291,7 +291,10 @@ show_map_vma(struct seq_file *m, struct
+ const char *name = NULL;
+
+ if (file) {
+- struct inode *inode = file_inode(vma->vm_file);
++ struct inode *inode;
++
++ file = vma_pr_or_file(vma);
++ inode = file_inode(file);
+ dev = inode->i_sb->s_dev;
+ ino = inode->i_ino;
+ pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
+@@ -1648,7 +1651,7 @@ static int show_numa_map(struct seq_file
+ struct proc_maps_private *proc_priv = &numa_priv->proc_maps;
+ struct vm_area_struct *vma = v;
+ struct numa_maps *md = &numa_priv->md;
+- struct file *file = vma->vm_file;
++ struct file *file = vma_pr_or_file(vma);
+ struct mm_struct *mm = vma->vm_mm;
+ struct mm_walk walk = {
+ .hugetlb_entry = gather_hugetlb_stats,
+diff -urNp -x '*.orig' linux-4.9/fs/proc/task_nommu.c linux-4.9/fs/proc/task_nommu.c
+--- linux-4.9/fs/proc/task_nommu.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/proc/task_nommu.c 2021-02-24 16:15:09.508239763 +0100
+@@ -155,7 +155,10 @@ static int nommu_vma_show(struct seq_fil
+ file = vma->vm_file;
+
+ if (file) {
+- struct inode *inode = file_inode(vma->vm_file);
++ struct inode *inode;
++
++ file = vma_pr_or_file(vma);
++ inode = file_inode(file);
+ dev = inode->i_sb->s_dev;
+ ino = inode->i_ino;
+ pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT;
+diff -urNp -x '*.orig' linux-4.9/fs/read_write.c linux-4.9/fs/read_write.c
+--- linux-4.9/fs/read_write.c 2021-02-24 16:14:56.504483501 +0100
++++ linux-4.9/fs/read_write.c 2021-02-24 16:15:09.514906646 +0100
+@@ -517,6 +517,30 @@ ssize_t __vfs_write(struct file *file, c
+ }
+ EXPORT_SYMBOL(__vfs_write);
+
++vfs_readf_t vfs_readf(struct file *file)
++{
++ const struct file_operations *fop = file->f_op;
++
++ if (fop->read)
++ return fop->read;
++ if (fop->read_iter)
++ return new_sync_read;
++ return ERR_PTR(-ENOSYS);
++}
++EXPORT_SYMBOL_GPL(vfs_readf);
++
++vfs_writef_t vfs_writef(struct file *file)
++{
++ const struct file_operations *fop = file->f_op;
++
++ if (fop->write)
++ return fop->write;
++ if (fop->write_iter)
++ return new_sync_write;
++ return ERR_PTR(-ENOSYS);
++}
++EXPORT_SYMBOL_GPL(vfs_writef);
++
+ ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos)
+ {
+ mm_segment_t old_fs;
+diff -urNp -x '*.orig' linux-4.9/fs/splice.c linux-4.9/fs/splice.c
+--- linux-4.9/fs/splice.c 2021-02-24 16:14:56.507816943 +0100
++++ linux-4.9/fs/splice.c 2021-02-24 16:15:09.518240088 +0100
+@@ -856,8 +856,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
+ /*
+ * Attempt to initiate a splice from pipe to file.
+ */
+-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
+- loff_t *ppos, size_t len, unsigned int flags)
++long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
++ loff_t *ppos, size_t len, unsigned int flags)
+ {
+ ssize_t (*splice_write)(struct pipe_inode_info *, struct file *,
+ loff_t *, size_t, unsigned int);
+@@ -869,13 +869,14 @@ static long do_splice_from(struct pipe_i
+
+ return splice_write(pipe, out, ppos, len, flags);
+ }
++EXPORT_SYMBOL_GPL(do_splice_from);
+
+ /*
+ * Attempt to initiate a splice from a file to a pipe.
+ */
+-static long do_splice_to(struct file *in, loff_t *ppos,
+- struct pipe_inode_info *pipe, size_t len,
+- unsigned int flags)
++long do_splice_to(struct file *in, loff_t *ppos,
++ struct pipe_inode_info *pipe, size_t len,
++ unsigned int flags)
+ {
+ ssize_t (*splice_read)(struct file *, loff_t *,
+ struct pipe_inode_info *, size_t, unsigned int);
+@@ -898,6 +899,7 @@ static long do_splice_to(struct file *in
+
+ return splice_read(in, ppos, pipe, len, flags);
+ }
++EXPORT_SYMBOL_GPL(do_splice_to);
+
+ /**
+ * splice_direct_to_actor - splices data directly between two non-pipes
+diff -urNp -x '*.orig' linux-4.9/fs/sync.c linux-4.9/fs/sync.c
+--- linux-4.9/fs/sync.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/sync.c 2021-02-24 16:15:09.518240088 +0100
+@@ -27,7 +27,7 @@
+ * wait == 1 case since in that case write_inode() functions do
+ * sync_dirty_buffer() and thus effectively write one block at a time.
+ */
+-static int __sync_filesystem(struct super_block *sb, int wait)
++int __sync_filesystem(struct super_block *sb, int wait)
+ {
+ if (wait)
+ sync_inodes_sb(sb);
+@@ -38,6 +38,7 @@ static int __sync_filesystem(struct supe
+ 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 -urNp -x '*.orig' linux-4.9/fs/xattr.c linux-4.9/fs/xattr.c
+--- linux-4.9/fs/xattr.c 2021-02-24 16:14:57.317843295 +0100
++++ linux-4.9/fs/xattr.c 2021-02-24 16:15:09.518240088 +0100
+@@ -334,6 +334,7 @@ vfs_getxattr_alloc(struct dentry *dentry
+ *xattr_value = value;
+ return error;
+ }
++EXPORT_SYMBOL_GPL(vfs_getxattr_alloc);
+
+ ssize_t
+ __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name,
+diff -urNp -x '*.orig' linux-4.9/include/linux/file.h linux-4.9/include/linux/file.h
+--- linux-4.9/include/linux/file.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/file.h 2021-02-24 16:15:09.508239763 +0100
+@@ -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 -urNp -x '*.orig' linux-4.9/include/linux/fs.h linux-4.9/include/linux/fs.h
+--- linux-4.9/include/linux/fs.h 2021-02-24 16:14:57.317843295 +0100
++++ linux-4.9/include/linux/fs.h 2021-02-24 16:15:09.541574180 +0100
+@@ -1315,6 +1315,7 @@ extern void fasync_free(struct fasync_st
+ /* 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 void f_setown(struct file *filp, unsigned long arg, int force);
+ extern void f_delown(struct file *filp);
+@@ -1745,6 +1746,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);
+@@ -1800,6 +1802,12 @@ ssize_t rw_copy_check_uvector(int type,
+ struct iovec **ret_pointer);
+ ssize_t vfs_sendfile(struct file *, struct file *, loff_t *, size_t, loff_t);
+
++typedef ssize_t (*vfs_readf_t)(struct file *, char __user *, size_t, loff_t *);
++typedef ssize_t (*vfs_writef_t)(struct file *, const char __user *, size_t,
++ loff_t *);
++vfs_readf_t vfs_readf(struct file *file);
++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 *);
+@@ -1847,6 +1855,10 @@ struct super_operations {
+ struct shrink_control *);
+ long (*free_cached_objects)(struct super_block *,
+ struct shrink_control *);
++#if defined(CONFIG_BLK_DEV_LOOP) || defined(CONFIG_BLK_DEV_LOOP_MODULE)
++ /* and aufs */
++ struct file *(*real_loop)(struct file *);
++#endif
+ };
+
+ /*
+@@ -2197,6 +2209,7 @@ extern int current_umask(void);
+ extern void ihold(struct inode * inode);
+ extern void iput(struct inode *);
+ extern int generic_update_time(struct inode *, struct timespec *, int);
++extern int update_time(struct inode *, struct timespec *, int);
+
+ /* /sys/fs */
+ extern struct kobject *fs_kobj;
+@@ -2479,6 +2492,7 @@ static inline bool sb_is_blkdev_sb(struc
+ 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 -urNp -x '*.orig' linux-4.9/include/linux/mm.h linux-4.9/include/linux/mm.h
+--- linux-4.9/include/linux/mm.h 2021-02-24 16:14:56.574485779 +0100
++++ linux-4.9/include/linux/mm.h 2021-02-24 16:15:09.508239763 +0100
+@@ -1314,6 +1314,28 @@ static inline int fixup_user_fault(struc
+ }
+ #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[],
++ int);
++extern void vma_do_get_file(struct vm_area_struct *, const char[], int);
++extern void vma_do_fput(struct vm_area_struct *, const char[], int);
++
++#define vma_file_update_time(vma) vma_do_file_update_time(vma, __func__, \
++ __LINE__)
++#define vma_pr_or_file(vma) vma_do_pr_or_file(vma, __func__, \
++ __LINE__)
++#define vma_get_file(vma) vma_do_get_file(vma, __func__, __LINE__)
++#define vma_fput(vma) vma_do_fput(vma, __func__, __LINE__)
++
++#ifndef CONFIG_MMU
++extern struct file *vmr_do_pr_or_file(struct vm_region *, const char[], int);
++extern void vmr_do_fput(struct vm_region *, const char[], int);
++
++#define vmr_pr_or_file(region) vmr_do_pr_or_file(region, __func__, \
++ __LINE__)
++#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,
+ unsigned int gup_flags);
+ extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
+diff -urNp -x '*.orig' linux-4.9/include/linux/mm_types.h linux-4.9/include/linux/mm_types.h
+--- linux-4.9/include/linux/mm_types.h 2021-02-24 16:14:56.574485779 +0100
++++ linux-4.9/include/linux/mm_types.h 2021-02-24 16:15:09.508239763 +0100
+@@ -280,6 +280,7 @@ struct vm_region {
+ unsigned long vm_top; /* region allocated to here */
+ unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */
+ struct file *vm_file; /* the backing file or NULL */
++ struct file *vm_prfile; /* the virtual backing file or NULL */
+
+ int vm_usage; /* region usage count (access under nommu_region_sem) */
+ bool vm_icache_flushed : 1; /* true if the icache has been flushed for
+@@ -354,6 +355,7 @@ struct vm_area_struct {
+ unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_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
+diff -urNp -x '*.orig' linux-4.9/include/linux/mnt_namespace.h linux-4.9/include/linux/mnt_namespace.h
+--- linux-4.9/include/linux/mnt_namespace.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/mnt_namespace.h 2021-02-24 16:15:09.508239763 +0100
+@@ -5,11 +5,14 @@
+ struct mnt_namespace;
+ struct fs_struct;
+ struct user_namespace;
++struct vfsmount;
+
+ extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
+ struct user_namespace *, struct fs_struct *);
+ extern void put_mnt_ns(struct mnt_namespace *ns);
+
++extern int is_current_mnt_ns(struct vfsmount *mnt);
++
+ extern const struct file_operations proc_mounts_operations;
+ extern const struct file_operations proc_mountinfo_operations;
+ extern const struct file_operations proc_mountstats_operations;
+diff -urNp -x '*.orig' linux-4.9/include/linux/splice.h linux-4.9/include/linux/splice.h
+--- linux-4.9/include/linux/splice.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/splice.h 2021-02-24 16:15:09.508239763 +0100
+@@ -86,4 +86,10 @@ extern void spd_release_page(struct spli
+
+ 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);
++extern long do_splice_to(struct file *in, loff_t *ppos,
++ struct pipe_inode_info *pipe, size_t len,
++ unsigned int flags);
+ #endif
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/Kbuild linux-4.9/include/uapi/linux/Kbuild
+--- linux-4.9/include/uapi/linux/Kbuild 2021-02-24 16:14:56.617820522 +0100
++++ linux-4.9/include/uapi/linux/Kbuild 2021-02-24 16:15:09.501572879 +0100
+@@ -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
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/aufs_type.h linux-4.9/include/uapi/linux/aufs_type.h
+--- linux-4.9/include/uapi/linux/aufs_type.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/linux/aufs_type.h 2021-02-24 16:15:09.538240738 +0100
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2005-2018 Junjiro R. Okajima
+#define AUFS_CTL_FHSM_FD _IOW(AuCtlType, AuCtl_FHSM_FD, int)
+
+#endif /* __AUFS_TYPE_H__ */
-aufs4.9 loopback patch
-
-diff --git a/drivers/block/loop.c b/drivers/block/loop.c
-index 6ee9235..f64161f 100644
---- a/drivers/block/loop.c
-+++ b/drivers/block/loop.c
-@@ -551,7 +551,7 @@ static int do_req_filebacked(struct loop_device *lo, struct request *rq)
+diff -urNp -x '*.orig' linux-4.9/kernel/fork.c linux-4.9/kernel/fork.c
+--- linux-4.9/kernel/fork.c 2021-02-24 16:14:57.337843945 +0100
++++ linux-4.9/kernel/fork.c 2021-02-24 16:15:09.508239763 +0100
+@@ -638,7 +638,7 @@ static __latent_entropy int dup_mmap(str
+ struct inode *inode = file_inode(file);
+ struct address_space *mapping = file->f_mapping;
+
+- get_file(file);
++ vma_get_file(tmp);
+ if (tmp->vm_flags & VM_DENYWRITE)
+ atomic_dec(&inode->i_writecount);
+ i_mmap_lock_write(mapping);
+diff -urNp -x '*.orig' linux-4.9/kernel/task_work.c linux-4.9/kernel/task_work.c
+--- linux-4.9/kernel/task_work.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/kernel/task_work.c 2021-02-24 16:15:09.518240088 +0100
+@@ -119,3 +119,4 @@ void task_work_run(void)
+ } while (work);
+ }
}
++EXPORT_SYMBOL_GPL(task_work_run);
+diff -urNp -x '*.orig' linux-4.9/mm/Makefile linux-4.9/mm/Makefile
+--- linux-4.9/mm/Makefile 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/mm/Makefile 2021-02-24 16:15:09.511573204 +0100
+@@ -37,7 +37,7 @@ obj-y := filemap.o mempool.o oom_kill.
+ mm_init.o mmu_context.o percpu.o slab_common.o \
+ compaction.o vmacache.o \
+ interval_tree.o list_lru.o workingset.o \
+- debug.o $(mmu-y)
++ prfile.o debug.o $(mmu-y)
- struct switch_request {
-- struct file *file;
-+ struct file *file, *virt_file;
- struct completion wait;
- };
+ obj-y += init-mm.o
-@@ -577,6 +577,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);
-@@ -589,11 +590,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;
+diff -urNp -x '*.orig' linux-4.9/mm/filemap.c linux-4.9/mm/filemap.c
+--- linux-4.9/mm/filemap.c 2021-02-24 16:14:56.697823124 +0100
++++ linux-4.9/mm/filemap.c 2021-02-24 16:15:09.511573204 +0100
+@@ -2312,7 +2312,7 @@ int filemap_page_mkwrite(struct vm_area_
+ int ret = VM_FAULT_LOCKED;
- w.file = file;
-+ w.virt_file = virt_file;
+ sb_start_pagefault(inode->i_sb);
+- file_update_time(vma->vm_file);
++ vma_file_update_time(vma);
+ lock_page(page);
+ if (page->mapping != inode->i_mapping) {
+ unlock_page(page);
+diff -urNp -x '*.orig' linux-4.9/mm/memory.c linux-4.9/mm/memory.c
+--- linux-4.9/mm/memory.c 2021-02-24 16:14:56.704490008 +0100
++++ linux-4.9/mm/memory.c 2021-02-24 16:15:09.511573204 +0100
+@@ -2124,7 +2124,7 @@ static inline int wp_page_reuse(struct f
+ }
- /* freeze queue and wait for completion of scheduled requests */
- blk_mq_freeze_queue(lo->lo_queue);
-@@ -619,7 +619,16 @@ static int loop_flush(struct loop_device
- /* loop not yet configured, no running thread, nothing to flush */
- if (lo->lo_state != Lo_bound)
- return 0;
-- return loop_switch(lo, NULL);
-+ return loop_switch(lo, NULL, NULL);
-+}
-+
-+static struct file *loop_real_file(struct file *file)
-+{
-+ struct file *f = NULL;
-+
-+ 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;
- }
+ if (!page_mkwrite)
+- file_update_time(vma->vm_file);
++ vma_file_update_time(vma);
+ }
- static void loop_reread_partitions(struct loop_device *lo,
-@@ -649,6 +661,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;
+ return VM_FAULT_WRITE;
+diff -urNp -x '*.orig' linux-4.9/mm/mmap.c linux-4.9/mm/mmap.c
+--- linux-4.9/mm/mmap.c 2021-02-24 16:14:56.707823450 +0100
++++ linux-4.9/mm/mmap.c 2021-02-24 16:15:09.511573204 +0100
+@@ -164,7 +164,7 @@ static struct vm_area_struct *remove_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);
+ return next;
+@@ -887,7 +887,7 @@ again:
+ if (remove_next) {
+ if (file) {
+ uprobe_munmap(next, next->vm_start, next->vm_end);
+- fput(file);
++ vma_fput(vma);
+ }
+ if (next->anon_vma)
+ anon_vma_merge(vma, next);
+@@ -1767,8 +1767,8 @@ out:
+ return addr;
-@@ -665,13 +678,20 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
- file = fget(arg);
- if (!file)
- goto out;
-+ f = loop_real_file(file);
-+ if (f) {
-+ virt_file = file;
-+ file = f;
-+ get_file(file);
-+ }
+ unmap_and_free_vma:
++ vma_fput(vma);
+ vma->vm_file = NULL;
+- fput(file);
- error = loop_validate_file(file, bdev);
- if (error)
- goto out_putf;
+ /* Undo any partial mapping done by a device driver. */
+ unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
+@@ -2602,7 +2602,7 @@ static int __split_vma(struct mm_struct
+ goto out_free_mpol;
- inode = file->f_mapping->host;
- old_file = lo->lo_backing_file;
-+ old_virt_file = lo->lo_backing_virt_file;
+ if (new->vm_file)
+- get_file(new->vm_file);
++ vma_get_file(new);
- error = -EINVAL;
+ if (new->vm_ops && new->vm_ops->open)
+ new->vm_ops->open(new);
+@@ -2621,7 +2621,7 @@ static int __split_vma(struct mm_struct
+ if (new->vm_ops && new->vm_ops->close)
+ new->vm_ops->close(new);
+ if (new->vm_file)
+- fput(new->vm_file);
++ vma_fput(new);
+ unlink_anon_vmas(new);
+ out_free_mpol:
+ mpol_put(vma_policy(new));
+@@ -2772,7 +2772,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsign
+ struct vm_area_struct *vma;
+ unsigned long populate = 0;
+ unsigned long ret = -EINVAL;
+- struct file *file;
++ struct file *file, *prfile;
-@@ -679,17 +699,21 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
- goto out_putf;
+ pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/vm/remap_file_pages.txt.\n",
+ current->comm, current->pid);
+@@ -2847,10 +2847,27 @@ SYSCALL_DEFINE5(remap_file_pages, unsign
+ }
+ }
- /* and ... switch */
-- error = loop_switch(lo, file);
-+ error = loop_switch(lo, file, virt_file);
- if (error)
- goto out_putf;
+- 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);
++ 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)
+@@ -3127,7 +3144,7 @@ struct vm_area_struct *copy_vma(struct v
+ if (anon_vma_clone(new_vma, vma))
+ goto out_free_mempol;
+ if (new_vma->vm_file)
+- get_file(new_vma->vm_file);
++ vma_get_file(new_vma);
+ if (new_vma->vm_ops && new_vma->vm_ops->open)
+ new_vma->vm_ops->open(new_vma);
+ vma_link(mm, new_vma, prev, rb_link, rb_parent);
+diff -urNp -x '*.orig' linux-4.9/mm/nommu.c linux-4.9/mm/nommu.c
+--- linux-4.9/mm/nommu.c 2021-02-24 16:14:56.707823450 +0100
++++ linux-4.9/mm/nommu.c 2021-02-24 16:15:09.511573204 +0100
+@@ -640,7 +640,7 @@ static void __put_nommu_region(struct vm
+ up_write(&nommu_region_sem);
- fput(old_file);
-+ if (old_virt_file)
-+ fput(old_virt_file);
- if (lo->lo_flags & LO_FLAGS_PARTSCAN)
- loop_reread_partitions(lo, bdev);
- return 0;
+ if (region->vm_file)
+- fput(region->vm_file);
++ vmr_fput(region);
- out_putf:
- fput(file);
-+ if (virt_file)
-+ fput(virt_file);
- out:
- return error;
+ /* IO memory and memory shared directly out of the pagecache
+ * from ramfs/tmpfs mustn't be released here */
+@@ -798,7 +798,7 @@ static void delete_vma(struct mm_struct
+ 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);
}
-@@ -876,7 +900,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)
+@@ -1324,7 +1324,7 @@ unsigned long do_mmap(struct file *file,
+ goto error_just_free;
+ }
+ }
+- fput(region->vm_file);
++ vmr_fput(region);
+ kmem_cache_free(vm_region_jar, region);
+ region = pregion;
+ result = start;
+@@ -1399,10 +1399,10 @@ error_just_free:
+ up_write(&nommu_region_sem);
+ error:
+ if (region->vm_file)
+- fput(region->vm_file);
++ 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;
+
+diff -urNp -x '*.orig' linux-4.9/mm/prfile.c linux-4.9/mm/prfile.c
+--- linux-4.9/mm/prfile.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/mm/prfile.c 2021-02-24 16:15:09.511573204 +0100
+@@ -0,0 +1,85 @@
++/*
++ * 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-2018 Junjro R. Okajima
++ * Copyright (c) 2014 Ian Campbell
++ */
++
++#include <linux/mm.h>
++#include <linux/file.h>
++#include <linux/fs.h>
++
++/* #define PRFILE_TRACE */
++static inline void prfile_trace(struct file *f, struct file *pr,
++ const char func[], int line, const char func2[])
++{
++#ifdef PRFILE_TRACE
++ if (pr)
++ pr_info("%s:%d: %s, %pD2\n", func, line, func2, f);
++#endif
++}
++
++void vma_do_file_update_time(struct vm_area_struct *vma, const char func[],
++ int line)
++{
++ struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++ prfile_trace(f, pr, func, line, __func__);
++ file_update_time(f);
++ if (f && pr)
++ file_update_time(pr);
++}
++
++struct file *vma_do_pr_or_file(struct vm_area_struct *vma, const char func[],
++ int line)
++{
++ struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++ prfile_trace(f, pr, func, line, __func__);
++ return (f && pr) ? pr : f;
++}
++
++void vma_do_get_file(struct vm_area_struct *vma, const char func[], int line)
++{
++ struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++ prfile_trace(f, pr, func, line, __func__);
++ get_file(f);
++ if (f && pr)
++ get_file(pr);
++}
++
++void vma_do_fput(struct vm_area_struct *vma, const char func[], int line)
++{
++ struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++ prfile_trace(f, pr, func, line, __func__);
++ fput(f);
++ if (f && pr)
++ fput(pr);
++}
++
++#ifndef CONFIG_MMU
++struct file *vmr_do_pr_or_file(struct vm_region *region, const char func[],
++ int line)
++{
++ struct file *f = region->vm_file, *pr = region->vm_prfile;
++
++ prfile_trace(f, pr, func, line, __func__);
++ return (f && pr) ? pr : f;
++}
++
++void vmr_do_fput(struct vm_region *region, const char func[], int line)
++{
++ struct file *f = region->vm_file, *pr = region->vm_prfile;
++
++ prfile_trace(f, pr, func, line, __func__);
++ fput(f);
++ if (f && pr)
++ fput(pr);
++}
++#endif /* !CONFIG_MMU */
+diff -urNp -x '*.orig' linux-4.9/security/commoncap.c linux-4.9/security/commoncap.c
+--- linux-4.9/security/commoncap.c 2021-02-24 16:14:57.371178363 +0100
++++ linux-4.9/security/commoncap.c 2021-02-24 16:15:09.518240088 +0100
+@@ -1067,12 +1067,14 @@ int cap_mmap_addr(unsigned long addr)
+ }
+ return ret;
+ }
++EXPORT_SYMBOL_GPL(cap_mmap_addr);
+
+ int cap_mmap_file(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags)
{
-- struct file *file;
-+ struct file *file, *f, *virt_file = NULL;
- struct inode *inode;
- struct address_space *mapping;
- unsigned lo_blocksize;
-@@ -891,6 +915,12 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
- file = fget(arg);
- if (!file)
- goto out;
-+ f = loop_real_file(file);
-+ if (f) {
-+ virt_file = file;
-+ file = f;
-+ get_file(file);
-+ }
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(cap_mmap_file);
- error = -EBUSY;
- if (lo->lo_state != Lo_unbound)
-@@ -943,6 +973,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;
-+ lo->lo_backing_virt_file = virt_file;
- lo->transfer = NULL;
- lo->ioctl = NULL;
- lo->lo_sizelimit = 0;
-@@ -975,6 +1006,8 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
+ #ifdef CONFIG_SECURITY
- out_putf:
- fput(file);
-+ if (virt_file)
-+ fput(virt_file);
- out:
- /* This is safe: open() is still holding a reference. */
- module_put(THIS_MODULE);
-@@ -1021,6 +1054,7 @@ loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer,
- static int loop_clr_fd(struct loop_device *lo)
+diff -urNp -x '*.orig' linux-4.9/security/device_cgroup.c linux-4.9/security/device_cgroup.c
+--- linux-4.9/security/device_cgroup.c 2021-02-24 16:14:56.891162747 +0100
++++ linux-4.9/security/device_cgroup.c 2021-02-24 16:15:09.518240088 +0100
+@@ -7,6 +7,7 @@
+ #include <linux/device_cgroup.h>
+ #include <linux/cgroup.h>
+ #include <linux/ctype.h>
++#include <linux/export.h>
+ #include <linux/list.h>
+ #include <linux/uaccess.h>
+ #include <linux/seq_file.h>
+@@ -849,6 +850,7 @@ int __devcgroup_inode_permission(struct
+ return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
+ access);
+ }
++EXPORT_SYMBOL_GPL(__devcgroup_inode_permission);
+
+ int devcgroup_inode_mknod(int mode, dev_t dev)
{
- struct file *filp = lo->lo_backing_file;
-+ struct file *virt_filp = lo->lo_backing_virt_file;
- gfp_t gfp = lo->old_gfp_mask;
- struct block_device *bdev = lo->lo_device;
+diff -urNp -x '*.orig' linux-4.9/security/security.c linux-4.9/security/security.c
+--- linux-4.9/security/security.c 2021-02-24 16:14:56.897829631 +0100
++++ linux-4.9/security/security.c 2021-02-24 16:15:09.518240088 +0100
+@@ -443,6 +443,7 @@ int security_path_rmdir(const struct pat
+ return 0;
+ return call_int_hook(path_rmdir, 0, dir, dentry);
+ }
++EXPORT_SYMBOL_GPL(security_path_rmdir);
-@@ -1052,6 +1086,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;
-+ lo->lo_backing_virt_file = NULL;
- spin_unlock_irq(&lo->lo_lock);
+ int security_path_unlink(const struct path *dir, struct dentry *dentry)
+ {
+@@ -459,6 +460,7 @@ int security_path_symlink(const struct p
+ return 0;
+ return call_int_hook(path_symlink, 0, dir, dentry, old_name);
+ }
++EXPORT_SYMBOL_GPL(security_path_symlink);
- loop_release_xfer(lo);
-@@ -1096,6 +1131,8 @@ static int loop_clr_fd(struct loop_device *lo)
- * bd_mutex which is usually taken before lo_ctl_mutex.
- */
- fput(filp);
-+ if (virt_filp)
-+ fput(virt_filp);
- return 0;
+ int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
+ struct dentry *new_dentry)
+@@ -467,6 +469,7 @@ int security_path_link(struct dentry *ol
+ return 0;
+ return call_int_hook(path_link, 0, old_dentry, new_dir, new_dentry);
}
++EXPORT_SYMBOL_GPL(security_path_link);
-diff --git a/drivers/block/loop.h b/drivers/block/loop.h
-index fb2237c..c3888c5 100644
---- a/drivers/block/loop.h
-+++ b/drivers/block/loop.h
-@@ -46,7 +46,7 @@ struct loop_device {
- int (*ioctl)(struct loop_device *, int cmd,
- unsigned long arg);
+ int security_path_rename(const struct path *old_dir, struct dentry *old_dentry,
+ const struct path *new_dir, struct dentry *new_dentry,
+@@ -494,6 +497,7 @@ int security_path_truncate(const struct
+ return 0;
+ return call_int_hook(path_truncate, 0, path);
+ }
++EXPORT_SYMBOL_GPL(security_path_truncate);
-- 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 22e93ee..4586e8d 100644
---- a/fs/aufs/f_op.c
-+++ b/fs/aufs/f_op.c
-@@ -357,7 +357,7 @@ static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter)
- if (IS_ERR(h_file))
- goto out;
+ int security_path_chmod(const struct path *path, umode_t mode)
+ {
+@@ -501,6 +505,7 @@ int security_path_chmod(const struct pat
+ return 0;
+ return call_int_hook(path_chmod, 0, path, mode);
+ }
++EXPORT_SYMBOL_GPL(security_path_chmod);
-- if (au_test_loopback_kthread()) {
-+ if (0 && 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;
-diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c
-index c1dbe59..1fc4b49 100644
---- a/fs/aufs/loop.c
-+++ b/fs/aufs/loop.c
-@@ -132,3 +132,19 @@ void au_loopback_fin(void)
- symbol_put(loop_backing_file);
- kfree(au_warn_loopback_array);
+ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
+ {
+@@ -508,6 +513,7 @@ int security_path_chown(const struct pat
+ return 0;
+ return call_int_hook(path_chown, 0, path, uid, gid);
}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* support the loopback block device insude aufs */
-+
-+struct file *aufs_real_loop(struct file *file)
-+{
-+ struct file *f;
-+
-+ BUG_ON(!au_test_aufs(file->f_path.dentry->d_sb));
-+ fi_read_lock(file);
-+ f = au_hf_top(file);
-+ fi_read_unlock(file);
-+ AuDebugOn(!f);
-+ return f;
-+}
-diff --git a/fs/aufs/loop.h b/fs/aufs/loop.h
-index c08374e..5e05083 100644
---- a/fs/aufs/loop.h
-+++ b/fs/aufs/loop.h
-@@ -25,7 +25,11 @@ void au_warn_loopback(struct super_block *h_sb);
++EXPORT_SYMBOL_GPL(security_path_chown);
- int au_loopback_init(void);
- void au_loopback_fin(void);
-+
-+struct file *aufs_real_loop(struct file *file);
- #else
-+AuStub(struct file *, loop_backing_file, return NULL)
-+
- 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)
+ int security_path_chroot(const struct path *path)
+ {
+@@ -593,6 +599,7 @@ int security_inode_readlink(struct dentr
+ return 0;
+ return call_int_hook(inode_readlink, 0, dentry);
+ }
++EXPORT_SYMBOL_GPL(security_inode_readlink);
- AuStubInt0(au_loopback_init, void)
- AuStubVoid(au_loopback_fin, void)
-+
-+AuStub(struct file *, aufs_real_loop, return NULL, struct file *file)
- #endif /* BLK_DEV_LOOP */
+ int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
+ bool rcu)
+@@ -608,6 +615,7 @@ int security_inode_permission(struct ino
+ return 0;
+ return call_int_hook(inode_permission, 0, inode, mask);
+ }
++EXPORT_SYMBOL_GPL(security_inode_permission);
- #endif /* __KERNEL__ */
-diff --git a/fs/aufs/super.c b/fs/aufs/super.c
-index dd38e63..2486242 100644
---- a/fs/aufs/super.c
-+++ b/fs/aufs/super.c
-@@ -838,7 +838,10 @@ static const struct super_operations aufs_sop = {
- .statfs = aufs_statfs,
- .put_super = aufs_put_super,
- .sync_fs = aufs_sync_fs,
-- .remount_fs = aufs_remount_fs
-+ .remount_fs = aufs_remount_fs,
-+#ifdef CONFIG_AUFS_BDEV_LOOP
-+ .real_loop = aufs_real_loop
-+#endif
- };
+ int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
+ {
+@@ -779,6 +787,7 @@ int security_file_permission(struct file
- /* ---------------------------------------------------------------------- */
-diff --git a/include/linux/fs.h b/include/linux/fs.h
-index 42c7695..3e4f068 100644
---- a/include/linux/fs.h
-+++ b/include/linux/fs.h
-@@ -1823,6 +1823,10 @@ struct super_operations {
- struct shrink_control *);
- long (*free_cached_objects)(struct super_block *,
- struct shrink_control *);
-+#if defined(CONFIG_BLK_DEV_LOOP) || defined(CONFIG_BLK_DEV_LOOP_MODULE)
-+ /* and aufs */
-+ struct file *(*real_loop)(struct file *);
-+#endif
- };
+ return fsnotify_perm(file, mask);
+ }
++EXPORT_SYMBOL_GPL(security_file_permission);
- /*
+ int security_file_alloc(struct file *file)
+ {
+@@ -838,6 +847,7 @@ int security_mmap_file(struct file *file
+ return ret;
+ return ima_file_mmap(file, prot);
+ }
++EXPORT_SYMBOL_GPL(security_mmap_file);
+
+ int security_mmap_addr(unsigned long addr)
+ {
-diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
-index 95c32f2..93fada5 100644
---- a/drivers/net/Kconfig
-+++ b/drivers/net/Kconfig
+diff -urNp -x '*.orig' linux-4.9/drivers/net/Kconfig linux-4.9/drivers/net/Kconfig
+--- linux-4.9/drivers/net/Kconfig 2021-02-24 15:35:10.207508334 +0100
++++ linux-4.9/drivers/net/Kconfig 2021-02-24 15:35:24.097940603 +0100
@@ -260,6 +260,125 @@ config RIONET_RX_SIZE
depends on RIONET
default "128"
config TUN
tristate "Universal TUN/TAP device driver support"
depends on INET
-diff --git a/drivers/net/Makefile b/drivers/net/Makefile
-index 7336cbd..d6d7ad4 100644
---- a/drivers/net/Makefile
-+++ b/drivers/net/Makefile
+diff -urNp -x '*.orig' linux-4.9/drivers/net/Makefile linux-4.9/drivers/net/Makefile
+--- linux-4.9/drivers/net/Makefile 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/drivers/net/Makefile 2021-02-24 15:35:24.097940603 +0100
@@ -11,6 +11,7 @@ obj-$(CONFIG_DUMMY) += dummy.o
obj-$(CONFIG_EQUALIZER) += eql.o
obj-$(CONFIG_IFB) += ifb.o
obj-$(CONFIG_MACVLAN) += macvlan.o
obj-$(CONFIG_MACVTAP) += macvtap.o
obj-$(CONFIG_MII) += mii.o
-diff --git a/drivers/net/imq.c b/drivers/net/imq.c
-new file mode 100644
-index 0000000..bc3b997
---- /dev/null
-+++ b/drivers/net/imq.c
+diff -urNp -x '*.orig' linux-4.9/drivers/net/imq.c linux-4.9/drivers/net/imq.c
+--- linux-4.9/drivers/net/imq.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/drivers/net/imq.c 2021-02-24 15:35:24.097940603 +0100
@@ -0,0 +1,907 @@
+/*
+ * Pseudo-driver for the intermediate queue device.
+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See https://github.com/imq/linuximq/wiki for more information.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("imq");
-diff --git a/include/linux/imq.h b/include/linux/imq.h
-new file mode 100644
-index 0000000..1babb09
---- /dev/null
-+++ b/include/linux/imq.h
+diff -urNp -x '*.orig' linux-4.9/include/linux/imq.h linux-4.9/include/linux/imq.h
+--- linux-4.9/include/linux/imq.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/imq.h 2021-02-24 15:35:24.097940603 +0100
@@ -0,0 +1,13 @@
+#ifndef _IMQ_H
+#define _IMQ_H
+
+#endif /* _IMQ_H */
+
-diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
-index e16a2a9..4a1090a 100644
---- a/include/linux/netdevice.h
-+++ b/include/linux/netdevice.h
-@@ -3669,6 +3669,19 @@ static inline void netif_tx_unlock_bh(struct net_device *dev)
+diff -urNp -x '*.orig' linux-4.9/include/linux/netdevice.h linux-4.9/include/linux/netdevice.h
+--- linux-4.9/include/linux/netdevice.h 2021-02-24 15:35:11.047534473 +0100
++++ linux-4.9/include/linux/netdevice.h 2021-02-24 15:35:24.101274040 +0100
+@@ -3694,6 +3694,19 @@ static inline void netif_tx_unlock_bh(st
} \
}
static inline void netif_tx_disable(struct net_device *dev)
{
unsigned int i;
-diff --git a/include/linux/netfilter/xt_IMQ.h b/include/linux/netfilter/xt_IMQ.h
-new file mode 100644
-index 0000000..9b07230
---- /dev/null
-+++ b/include/linux/netfilter/xt_IMQ.h
+diff -urNp -x '*.orig' linux-4.9/include/linux/netfilter/xt_IMQ.h linux-4.9/include/linux/netfilter/xt_IMQ.h
+--- linux-4.9/include/linux/netfilter/xt_IMQ.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/netfilter/xt_IMQ.h 2021-02-24 15:35:24.101274040 +0100
@@ -0,0 +1,9 @@
+#ifndef _XT_IMQ_H
+#define _XT_IMQ_H
+
+#endif /* _XT_IMQ_H */
+
-diff --git a/include/linux/netfilter_ipv4/ipt_IMQ.h b/include/linux/netfilter_ipv4/ipt_IMQ.h
-new file mode 100644
-index 0000000..7af320f
---- /dev/null
-+++ b/include/linux/netfilter_ipv4/ipt_IMQ.h
+diff -urNp -x '*.orig' linux-4.9/include/linux/netfilter_ipv4/ipt_IMQ.h linux-4.9/include/linux/netfilter_ipv4/ipt_IMQ.h
+--- linux-4.9/include/linux/netfilter_ipv4/ipt_IMQ.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/netfilter_ipv4/ipt_IMQ.h 2021-02-24 15:35:24.101274040 +0100
@@ -0,0 +1,10 @@
+#ifndef _IPT_IMQ_H
+#define _IPT_IMQ_H
+
+#endif /* _IPT_IMQ_H */
+
-diff --git a/include/linux/netfilter_ipv6/ip6t_IMQ.h b/include/linux/netfilter_ipv6/ip6t_IMQ.h
-new file mode 100644
-index 0000000..198ac01
---- /dev/null
-+++ b/include/linux/netfilter_ipv6/ip6t_IMQ.h
+diff -urNp -x '*.orig' linux-4.9/include/linux/netfilter_ipv6/ip6t_IMQ.h linux-4.9/include/linux/netfilter_ipv6/ip6t_IMQ.h
+--- linux-4.9/include/linux/netfilter_ipv6/ip6t_IMQ.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/netfilter_ipv6/ip6t_IMQ.h 2021-02-24 15:35:24.101274040 +0100
@@ -0,0 +1,10 @@
+#ifndef _IP6T_IMQ_H
+#define _IP6T_IMQ_H
+
+#endif /* _IP6T_IMQ_H */
+
-diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
-index 32810f2..4ce1d0a 100644
---- a/include/linux/skbuff.h
-+++ b/include/linux/skbuff.h
+diff -urNp -x '*.orig' linux-4.9/include/linux/skbuff.h linux-4.9/include/linux/skbuff.h
+--- linux-4.9/include/linux/skbuff.h 2021-02-24 15:35:11.060868221 +0100
++++ linux-4.9/include/linux/skbuff.h 2021-02-24 15:35:24.101274040 +0100
@@ -39,6 +39,10 @@
#include <linux/in6.h>
#include <linux/if_packet.h>
/* The interface for checksum offload between the stack and networking drivers
* is as follows...
-@@ -654,6 +658,9 @@ struct sk_buff {
+@@ -660,6 +664,9 @@ struct sk_buff {
* first. This is owned by whoever has the skb queued ATM.
*/
char cb[48] __aligned(8);
unsigned long _skb_refdst;
void (*destructor)(struct sk_buff *skb);
-@@ -663,6 +670,9 @@ struct sk_buff {
+@@ -669,6 +676,9 @@ struct sk_buff {
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
struct nf_bridge_info *nf_bridge;
#endif
-@@ -743,6 +753,9 @@ struct sk_buff {
+@@ -748,6 +758,9 @@ struct sk_buff {
__u8 offload_fwd_mark:1;
#endif
/* 2, 4 or 5 bit hole */
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
-@@ -903,6 +916,12 @@ void kfree_skb_list(struct sk_buff *segs);
+@@ -908,6 +921,12 @@ void kfree_skb_list(struct sk_buff *segs
void skb_tx_error(struct sk_buff *skb);
void consume_skb(struct sk_buff *skb);
void __kfree_skb(struct sk_buff *skb);
extern struct kmem_cache *skbuff_head_cache;
void kfree_skb_partial(struct sk_buff *skb, bool head_stolen);
-@@ -3594,6 +3613,10 @@ static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src,
+@@ -3640,6 +3659,10 @@ static inline void __nf_copy(struct sk_b
if (copy)
dst->nfctinfo = src->nfctinfo;
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
dst->nf_bridge = src->nf_bridge;
nf_bridge_get(src->nf_bridge);
-diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h
-index 2280cfe..ec8fa51 100644
---- a/include/net/netfilter/nf_queue.h
-+++ b/include/net/netfilter/nf_queue.h
+diff -urNp -x '*.orig' linux-4.9/include/net/netfilter/nf_queue.h linux-4.9/include/net/netfilter/nf_queue.h
+--- linux-4.9/include/net/netfilter/nf_queue.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/net/netfilter/nf_queue.h 2021-02-24 15:35:24.101274040 +0100
@@ -30,6 +30,12 @@ struct nf_queue_handler {
void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh);
void nf_unregister_queue_handler(struct net *net);
void nf_queue_entry_get_refs(struct nf_queue_entry *entry);
void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
-diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
-index cd334c9..6757228 100644
---- a/include/net/pkt_sched.h
-+++ b/include/net/pkt_sched.h
-@@ -105,6 +105,8 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
+diff -urNp -x '*.orig' linux-4.9/include/net/pkt_sched.h linux-4.9/include/net/pkt_sched.h
+--- linux-4.9/include/net/pkt_sched.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/net/pkt_sched.h 2021-02-24 15:35:24.101274040 +0100
+@@ -105,6 +105,8 @@ int sch_direct_xmit(struct sk_buff *skb,
void __qdisc_run(struct Qdisc *q);
static inline void qdisc_run(struct Qdisc *q)
{
if (qdisc_run_begin(q))
-diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
-index e6aa0a2..08b37dc 100644
---- a/include/net/sch_generic.h
-+++ b/include/net/sch_generic.h
-@@ -518,6 +518,13 @@ static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,
+diff -urNp -x '*.orig' linux-4.9/include/net/sch_generic.h linux-4.9/include/net/sch_generic.h
+--- linux-4.9/include/net/sch_generic.h 2021-02-24 15:35:11.077535407 +0100
++++ linux-4.9/include/net/sch_generic.h 2021-02-24 15:35:24.101274040 +0100
+@@ -523,6 +523,13 @@ static inline int qdisc_enqueue(struct s
return sch->enqueue(skb, sch, to_free);
}
static inline bool qdisc_is_percpu_stats(const struct Qdisc *q)
{
return q->flags & TCQ_F_CPUSTATS;
-diff --git a/include/uapi/linux/netfilter.h b/include/uapi/linux/netfilter.h
-index d93f949..23fb6d1 100644
---- a/include/uapi/linux/netfilter.h
-+++ b/include/uapi/linux/netfilter.h
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/netfilter.h linux-4.9/include/uapi/linux/netfilter.h
+--- linux-4.9/include/uapi/linux/netfilter.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/uapi/linux/netfilter.h 2021-02-24 15:35:24.101274040 +0100
@@ -14,7 +14,8 @@
#define NF_QUEUE 3
#define NF_REPEAT 4
/* we overload the higher bits for encoding auxiliary data such as the queue
* number or errno values. Not nice, but better than additional function
-diff --git a/net/core/dev.c b/net/core/dev.c
-index 6666b28..3e12add 100644
---- a/net/core/dev.c
-+++ b/net/core/dev.c
-@@ -141,6 +141,9 @@
+diff -urNp -x '*.orig' linux-4.9/net/core/dev.c linux-4.9/net/core/dev.c
+--- linux-4.9/net/core/dev.c 2021-02-24 15:35:11.220873200 +0100
++++ linux-4.9/net/core/dev.c 2021-02-24 15:35:24.101274040 +0100
+@@ -142,6 +142,9 @@
#include <linux/netfilter_ingress.h>
#include <linux/sctp.h>
#include <linux/crash_dump.h>
#include "net-sysfs.h"
-@@ -2906,7 +2909,12 @@ static int xmit_one(struct sk_buff *skb, struct net_device *dev,
+@@ -2965,7 +2968,12 @@ static int xmit_one(struct sk_buff *skb,
unsigned int len;
int rc;
dev_queue_xmit_nit(skb, dev);
len = skb->len;
-@@ -2945,6 +2953,8 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *de
+@@ -3004,6 +3012,8 @@ out:
return skb;
}
static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb,
netdev_features_t features)
{
-diff --git a/net/core/skbuff.c b/net/core/skbuff.c
-index 1e3e008..379236e 100644
---- a/net/core/skbuff.c
-+++ b/net/core/skbuff.c
-@@ -82,6 +82,87 @@ struct kmem_cache *skbuff_head_cache __read_mostly;
+diff -urNp -x '*.orig' linux-4.9/net/core/skbuff.c linux-4.9/net/core/skbuff.c
+--- linux-4.9/net/core/skbuff.c 2021-02-24 15:35:11.224206637 +0100
++++ linux-4.9/net/core/skbuff.c 2021-02-24 15:35:24.104607477 +0100
+@@ -82,6 +82,87 @@ struct kmem_cache *skbuff_head_cache __r
static struct kmem_cache *skbuff_fclone_cache __read_mostly;
int sysctl_max_skb_frags __read_mostly = MAX_SKB_FRAGS;
EXPORT_SYMBOL(sysctl_max_skb_frags);
/**
* skb_panic - private function for out-of-line support
-@@ -654,6 +735,28 @@ static void skb_release_head_state(struct sk_buff *skb)
+@@ -667,6 +748,28 @@ static void skb_release_head_state(struc
WARN_ON(in_irq());
skb->destructor(skb);
}
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
nf_conntrack_put(skb->nfct);
#endif
-@@ -843,6 +946,10 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
+@@ -856,6 +959,10 @@ static void __copy_skb_header(struct sk_
new->sp = secpath_get(old->sp);
#endif
__nf_copy(new, old, false);
/* Note : this field could be in headers_start/headers_end section
* It is not yet because we do not want to have a 16 bit hole
-@@ -3463,6 +3570,13 @@ void __init skb_init(void)
+@@ -3536,6 +3643,13 @@ void __init skb_init(void)
0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
+#endif
}
- /**
-diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
-index 59eb4ed..8020b07 100644
---- a/net/ipv6/ip6_output.c
-+++ b/net/ipv6/ip6_output.c
-@@ -66,9 +66,6 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
+ static int
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/ip6_output.c linux-4.9/net/ipv6/ip6_output.c
+--- linux-4.9/net/ipv6/ip6_output.c 2021-02-24 15:35:11.257541008 +0100
++++ linux-4.9/net/ipv6/ip6_output.c 2021-02-24 15:35:24.104607477 +0100
+@@ -66,9 +66,6 @@ static int ip6_finish_output2(struct net
struct in6_addr *nexthop;
int ret;
if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) {
struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
-@@ -150,6 +147,13 @@ int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+@@ -150,6 +147,13 @@ int ip6_output(struct net *net, struct s
return 0;
}
return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
net, sk, skb, NULL, dev,
ip6_finish_output,
-diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
-index e8d56d9..1ed3468 100644
---- a/net/netfilter/Kconfig
-+++ b/net/netfilter/Kconfig
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/Kconfig linux-4.9/net/netfilter/Kconfig
+--- linux-4.9/net/netfilter/Kconfig 2021-02-24 15:35:11.727555634 +0100
++++ linux-4.9/net/netfilter/Kconfig 2021-02-24 15:35:24.104607477 +0100
@@ -823,6 +823,18 @@ config NETFILTER_XT_TARGET_LOG
To compile it as a module, choose M here. If unsure, say N.
config NETFILTER_XT_TARGET_MARK
tristate '"MARK" target support'
depends on NETFILTER_ADVANCED
-diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
-index c23c3c8..99911ef 100644
---- a/net/netfilter/Makefile
-+++ b/net/netfilter/Makefile
-@@ -119,6 +119,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/Makefile linux-4.9/net/netfilter/Makefile
+--- linux-4.9/net/netfilter/Makefile 2021-02-24 15:35:11.727555634 +0100
++++ linux-4.9/net/netfilter/Makefile 2021-02-24 15:35:24.104607477 +0100
+@@ -119,6 +119,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) +=
obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o
obj-$(CONFIG_NETFILTER_XT_TARGET_NETMAP) += xt_NETMAP.o
-diff --git a/net/netfilter/core.c b/net/netfilter/core.c
-index 004af03..768a08b 100644
---- a/net/netfilter/core.c
-+++ b/net/netfilter/core.c
-@@ -360,8 +360,11 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state)
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/core.c linux-4.9/net/netfilter/core.c
+--- linux-4.9/net/netfilter/core.c 2021-02-24 15:35:11.287541941 +0100
++++ linux-4.9/net/netfilter/core.c 2021-02-24 15:35:24.104607477 +0100
+@@ -360,8 +360,11 @@ next_hook:
ret = NF_DROP_GETERR(verdict);
if (ret == 0)
ret = -EPERM;
+ goto next_hook;
if (ret == 1 && entry)
goto next_hook;
- }
-diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
-index 8f08d75..8d362c0 100644
---- a/net/netfilter/nf_queue.c
-+++ b/net/netfilter/nf_queue.c
+ } else {
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/nf_queue.c linux-4.9/net/netfilter/nf_queue.c
+--- linux-4.9/net/netfilter/nf_queue.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/netfilter/nf_queue.c 2021-02-24 15:35:24.104607477 +0100
@@ -27,6 +27,23 @@
* receives, no matter what.
*/
/* return EBUSY when somebody else is registered, return EEXIST if the
* same handler is registered, return 0 in case of success. */
void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh)
-@@ -108,16 +125,28 @@ void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_entry *entry)
+@@ -108,16 +125,28 @@ void nf_queue_nf_hook_drop(struct net *n
}
static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
if (!qh) {
status = -ESRCH;
goto err;
-@@ -218,6 +247,7 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
+@@ -218,6 +247,7 @@ okfn:
local_bh_enable();
break;
case NF_QUEUE:
err = nf_queue(skb, &entry->state, &hook_entry, verdict);
if (err == 1) {
if (hook_entry)
-diff --git a/net/netfilter/xt_IMQ.c b/net/netfilter/xt_IMQ.c
-new file mode 100644
-index 0000000..f9c5817
---- /dev/null
-+++ b/net/netfilter/xt_IMQ.c
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/xt_IMQ.c linux-4.9/net/netfilter/xt_IMQ.c
+--- linux-4.9/net/netfilter/xt_IMQ.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/net/netfilter/xt_IMQ.c 2021-02-24 15:35:24.104607477 +0100
@@ -0,0 +1,72 @@
+/*
+ * This target marks packets to be enqueued to an imq device
+MODULE_ALIAS("ipt_IMQ");
+MODULE_ALIAS("ip6t_IMQ");
+
-diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
-index 6cfb6e9..4c675e9 100644
---- a/net/sched/sch_generic.c
-+++ b/net/sched/sch_generic.c
-@@ -154,6 +154,14 @@ static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate,
+diff -urNp -x '*.orig' linux-4.9/net/sched/sch_generic.c linux-4.9/net/sched/sch_generic.c
+--- linux-4.9/net/sched/sch_generic.c 2021-02-24 15:35:11.317542875 +0100
++++ linux-4.9/net/sched/sch_generic.c 2021-02-24 15:35:24.104607477 +0100
+@@ -154,6 +154,14 @@ bulk:
return skb;
}
---- linux-2.6.28-stock/net/netfilter/Kconfig 2009-01-07 16:05:35.000000000 -0600
-+++ linux-2.6.28/net/netfilter/Kconfig 2009-01-07 16:07:31.000000000 -0600
-@@ -795,6 +795,27 @@ config NETFILTER_XT_MATCH_STATE
+diff -urNp -x '*.orig' linux-4.9/include/linux/netfilter/xt_layer7.h linux-4.9/include/linux/netfilter/xt_layer7.h
+--- linux-4.9/include/linux/netfilter/xt_layer7.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/netfilter/xt_layer7.h 2021-02-24 15:33:21.717468479 +0100
+@@ -0,0 +1,13 @@
++#ifndef _XT_LAYER7_H
++#define _XT_LAYER7_H
++
++#define MAX_PATTERN_LEN 8192
++#define MAX_PROTOCOL_LEN 256
++
++struct xt_layer7_info {
++ char protocol[MAX_PROTOCOL_LEN];
++ char pattern[MAX_PATTERN_LEN];
++ u_int8_t invert;
++};
++
++#endif /* _XT_LAYER7_H */
+diff -urNp -x '*.orig' linux-4.9/include/net/netfilter/nf_conntrack.h linux-4.9/include/net/netfilter/nf_conntrack.h
+--- linux-4.9/include/net/netfilter/nf_conntrack.h 2021-02-24 15:33:08.243716623 +0100
++++ linux-4.9/include/net/netfilter/nf_conntrack.h 2021-02-24 15:33:21.717468479 +0100
+@@ -119,6 +119,22 @@ struct nf_conn {
+ /* Extensions */
+ struct nf_ct_ext *ext;
+
++#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || \
++ defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE)
++ struct {
++ /*
++ * e.g. "http". NULL before decision. "unknown" after decision
++ * if no match.
++ */
++ char *app_proto;
++ /*
++ * application layer data so far. NULL after match decision.
++ */
++ char *app_data;
++ unsigned int app_data_len;
++ } layer7;
++#endif
++
+ /* Storage reserved for other modules, must be the last member */
+ union nf_conntrack_proto proto;
+ };
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/Kconfig linux-4.9/net/netfilter/Kconfig
+--- linux-4.9/net/netfilter/Kconfig 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/netfilter/Kconfig 2021-02-24 15:33:21.714135041 +0100
+@@ -1433,6 +1433,27 @@ config NETFILTER_XT_MATCH_STATE
To compile it as a module, choose M here. If unsure, say N.
config NETFILTER_XT_MATCH_STATISTIC
tristate '"statistic" match support'
depends on NETFILTER_ADVANCED
---- linux-2.6.28-stock/net/netfilter/Makefile 2009-01-07 16:05:35.000000000 -0600
-+++ linux-2.6.28/net/netfilter/Makefile 2009-01-07 16:07:31.000000000 -0600
-@@ -84,6 +84,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT)
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/Makefile linux-4.9/net/netfilter/Makefile
+--- linux-4.9/net/netfilter/Makefile 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/netfilter/Makefile 2021-02-24 15:33:21.714135041 +0100
+@@ -174,6 +174,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT)
obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o
obj-$(CONFIG_NETFILTER_XT_MATCH_SOCKET) += xt_socket.o
obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o
obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o
obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o
obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o
---- linux-2.6.28-stock/net/netfilter/xt_layer7.c 1969-12-31 18:00:00.000000000 -0600
-+++ linux-2.6.28/net/netfilter/xt_layer7.c 2009-01-07 20:47:14.000000000 -0600
-@@ -0,0 +1,656 @@
-+/*
-+ Kernel module to match application layer (OSI layer 7) data in connections.
-+
-+ http://l7-filter.sf.net
-+
-+ (C) 2003-2009 Matthew Strait and Ethan Sommer.
-+
-+ 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.
-+ http://www.gnu.org/licenses/gpl.txt
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/nf_conntrack_core.c linux-4.9/net/netfilter/nf_conntrack_core.c
+--- linux-4.9/net/netfilter/nf_conntrack_core.c 2021-02-24 15:33:08.463723455 +0100
++++ linux-4.9/net/netfilter/nf_conntrack_core.c 2021-02-24 15:33:21.717468479 +0100
+@@ -382,6 +382,14 @@ static void nf_ct_del_from_dying_or_unco
+ {
+ struct ct_pcpu *pcpu;
+
++ #if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE)
++ if(ct->layer7.app_proto)
++ kfree(ct->layer7.app_proto);
++ if(ct->layer7.app_data)
++ kfree(ct->layer7.app_data);
++ #endif
+
-+ Based on ipt_string.c (C) 2000 Emmanuel Roger <winfield@freegates.be>,
-+ xt_helper.c (C) 2002 Harald Welte and cls_layer7.c (C) 2003 Matthew Strait,
-+ Ethan Sommer, Justin Levandoski.
-+*/
+
-+#include <linux/spinlock.h>
-+#include <linux/version.h>
-+#include <net/ip.h>
-+#include <net/tcp.h>
-+#include <linux/module.h>
-+#include <linux/skbuff.h>
-+#include <linux/netfilter.h>
-+#include <net/netfilter/nf_conntrack.h>
-+#include <net/netfilter/nf_conntrack_core.h>
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
-+#include <net/netfilter/nf_conntrack_extend.h>
-+#include <net/netfilter/nf_conntrack_acct.h>
+ /* We overload first tuple to link into unconfirmed or dying list.*/
+ pcpu = per_cpu_ptr(nf_ct_net(ct)->ct.pcpu_lists, ct->cpu);
+
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/nf_conntrack_standalone.c linux-4.9/net/netfilter/nf_conntrack_standalone.c
+--- linux-4.9/net/netfilter/nf_conntrack_standalone.c 2021-02-24 15:33:08.467056893 +0100
++++ linux-4.9/net/netfilter/nf_conntrack_standalone.c 2021-02-24 15:33:21.717468479 +0100
+@@ -274,6 +274,12 @@ static int ct_seq_show(struct seq_file *
+ ct_show_zone(s, ct, NF_CT_DEFAULT_ZONE_DIR);
+ ct_show_delta_time(s, ct);
+
++#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE)
++ if(ct->layer7.app_proto &&
++ seq_printf(s, "l7proto=%s ", ct->layer7.app_proto))
++ return -ENOSPC;
+#endif
-+#include <linux/netfilter/x_tables.h>
-+#include <linux/netfilter/xt_layer7.h>
-+#include <linux/ctype.h>
-+#include <linux/proc_fs.h>
+
-+#include "regexp/regexp.c"
+ seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use));
+
+ if (seq_has_overflowed(s))
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/regexp/regexp.c linux-4.9/net/netfilter/regexp/regexp.c
+--- linux-4.9/net/netfilter/regexp/regexp.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/net/netfilter/regexp/regexp.c 2021-02-24 15:33:21.717468479 +0100
+@@ -0,0 +1,1197 @@
++/*
++ * regcomp and regexec -- regsub and regerror are elsewhere
++ * @(#)regexp.c 1.3 of 18 April 87
++ *
++ * Copyright (c) 1986 by University of Toronto.
++ * Written by Henry Spencer. Not derived from licensed software.
++ *
++ * Permission is granted to anyone to use this software for any
++ * purpose on any computer system, and to redistribute it freely,
++ * subject to the following restrictions:
++ *
++ * 1. The author is not responsible for the consequences of use of
++ * this software, no matter how awful, even if they arise
++ * from defects in it.
++ *
++ * 2. The origin of this software must not be misrepresented, either
++ * by explicit claim or by omission.
++ *
++ * 3. Altered versions must be plainly marked as such, and must not
++ * be misrepresented as being the original software.
++ *
++ * Beware that some of this code is subtly aware of the way operator
++ * precedence is structured in regular expressions. Serious changes in
++ * regular-expression syntax might require a total rethink.
++ *
++ * This code was modified by Ethan Sommer to work within the kernel
++ * (it now uses kmalloc etc..)
++ *
++ * Modified slightly by Matthew Strait to use more modern C.
++ */
+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>");
-+MODULE_DESCRIPTION("iptables application layer match module");
-+MODULE_ALIAS("ipt_layer7");
-+MODULE_VERSION("2.21");
++#include "regexp.h"
++#include "regmagic.h"
+
-+static int maxdatalen = 2048; // this is the default
-+module_param(maxdatalen, int, 0444);
-+MODULE_PARM_DESC(maxdatalen, "maximum bytes of data looked at by l7-filter");
-+#ifdef CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG
-+ #define DPRINTK(format,args...) printk(format,##args)
++/* added by ethan and matt. Lets it work in both kernel and user space.
++(So iptables can use it, for instance.) Yea, it goes both ways... */
++#if __KERNEL__
++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC)
+#else
-+ #define DPRINTK(format,args...)
++ #define printk(format,args...) printf(format,##args)
+#endif
+
-+/* Number of packets whose data we look at.
-+This can be modified through /proc/net/layer7_numpackets */
-+static int num_packets = 10;
-+
-+static struct pattern_cache {
-+ char * regex_string;
-+ regexp * pattern;
-+ struct pattern_cache * next;
-+} * first_pattern_cache = NULL;
-+
-+DEFINE_SPINLOCK(l7_lock);
-+
-+static int total_acct_packets(struct nf_conn *ct)
++void regerror(char * s)
+{
-+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26)
-+ BUG_ON(ct == NULL);
-+ return (ct->counters[IP_CT_DIR_ORIGINAL].packets + ct->counters[IP_CT_DIR_REPLY].packets);
-+#else
-+ struct nf_conn_counter *acct;
-+
-+ BUG_ON(ct == NULL);
-+ acct = nf_conn_acct_find(ct);
-+ if (!acct)
-+ return 0;
-+ return (atomic64_read(&acct[IP_CT_DIR_ORIGINAL].packets) + atomic64_read(&acct[IP_CT_DIR_REPLY].packets));
-+#endif
++ printk("<3>Regexp: %s\n", s);
++ /* NOTREACHED */
+}
+
-+#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG
-+/* Converts an unfriendly string into a friendly one by
-+replacing unprintables with periods and all whitespace with " ". */
-+static char * friendly_print(unsigned char * s)
-+{
-+ char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC);
-+ int i;
-+
-+ if(!f) {
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: out of memory in "
-+ "friendly_print, bailing.\n");
-+ return NULL;
-+ }
++/*
++ * The "internal use only" fields in regexp.h are present to pass info from
++ * compile to execute that permits the execute phase to run lots faster on
++ * simple cases. They are:
++ *
++ * regstart char that must begin a match; '\0' if none obvious
++ * reganch is the match anchored (at beginning-of-line only)?
++ * regmust string (pointer into program) that match must include, or NULL
++ * regmlen length of regmust string
++ *
++ * Regstart and reganch permit very fast decisions on suitable starting points
++ * for a match, cutting down the work a lot. Regmust permits fast rejection
++ * of lines that cannot possibly match. The regmust tests are costly enough
++ * that regcomp() supplies a regmust only if the r.e. contains something
++ * potentially expensive (at present, the only such thing detected is * or +
++ * at the start of the r.e., which can involve a lot of backup). Regmlen is
++ * supplied because the test in regexec() needs it and regcomp() is computing
++ * it anyway.
++ */
+
-+ for(i = 0; i < strlen(s); i++){
-+ if(isprint(s[i]) && s[i] < 128) f[i] = s[i];
-+ else if(isspace(s[i])) f[i] = ' ';
-+ else f[i] = '.';
-+ }
-+ f[i] = '\0';
-+ return f;
-+}
++/*
++ * Structure for regexp "program". This is essentially a linear encoding
++ * of a nondeterministic finite-state machine (aka syntax charts or
++ * "railroad normal form" in parsing technology). Each node is an opcode
++ * plus a "next" pointer, possibly plus an operand. "Next" pointers of
++ * all nodes except BRANCH implement concatenation; a "next" pointer with
++ * a BRANCH on both ends of it is connecting two alternatives. (Here we
++ * have one of the subtle syntax dependencies: an individual BRANCH (as
++ * opposed to a collection of them) is never concatenated with anything
++ * because of operator precedence.) The operand of some types of node is
++ * a literal string; for others, it is a node leading into a sub-FSM. In
++ * particular, the operand of a BRANCH node is the first node of the branch.
++ * (NB this is *not* a tree structure: the tail of the branch connects
++ * to the thing following the set of BRANCHes.) The opcodes are:
++ */
+
-+static char dec2hex(int i)
-+{
-+ switch (i) {
-+ case 0 ... 9:
-+ return (i + '0');
-+ break;
-+ case 10 ... 15:
-+ return (i - 10 + 'a');
-+ break;
-+ default:
-+ if (net_ratelimit())
-+ printk("layer7: Problem in dec2hex\n");
-+ return '\0';
-+ }
-+}
++/* definition number opnd? meaning */
++#define END 0 /* no End of program. */
++#define BOL 1 /* no Match "" at beginning of line. */
++#define EOL 2 /* no Match "" at end of line. */
++#define ANY 3 /* no Match any one character. */
++#define ANYOF 4 /* str Match any character in this string. */
++#define ANYBUT 5 /* str Match any character not in this string. */
++#define BRANCH 6 /* node Match this alternative, or the next... */
++#define BACK 7 /* no Match "", "next" ptr points backward. */
++#define EXACTLY 8 /* str Match this string. */
++#define NOTHING 9 /* no Match empty string. */
++#define STAR 10 /* node Match this (simple) thing 0 or more times. */
++#define PLUS 11 /* node Match this (simple) thing 1 or more times. */
++#define OPEN 20 /* no Mark this point in input as start of #n. */
++ /* OPEN+1 is number 1, etc. */
++#define CLOSE 30 /* no Analogous to OPEN. */
+
-+static char * hex_print(unsigned char * s)
-+{
-+ char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC);
-+ int i;
-+
-+ if(!g) {
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: out of memory in hex_print, "
-+ "bailing.\n");
-+ return NULL;
-+ }
-+
-+ for(i = 0; i < strlen(s); i++) {
-+ g[i*3 ] = dec2hex(s[i]/16);
-+ g[i*3 + 1] = dec2hex(s[i]%16);
-+ g[i*3 + 2] = ' ';
-+ }
-+ g[i*3] = '\0';
-+
-+ return g;
-+}
-+#endif // DEBUG
-+
-+/* Use instead of regcomp. As we expect to be seeing the same regexps over and
-+over again, it make sense to cache the results. */
-+static regexp * compile_and_cache(const char * regex_string,
-+ const char * protocol)
-+{
-+ struct pattern_cache * node = first_pattern_cache;
-+ struct pattern_cache * last_pattern_cache = first_pattern_cache;
-+ struct pattern_cache * tmp;
-+ unsigned int len;
-+
-+ while (node != NULL) {
-+ if (!strcmp(node->regex_string, regex_string))
-+ return node->pattern;
++/*
++ * Opcode notes:
++ *
++ * BRANCH The set of branches constituting a single choice are hooked
++ * together with their "next" pointers, since precedence prevents
++ * anything being concatenated to any individual branch. The
++ * "next" pointer of the last BRANCH in a choice points to the
++ * thing following the whole choice. This is also where the
++ * final "next" pointer of each individual branch points; each
++ * branch starts with the operand node of a BRANCH node.
++ *
++ * BACK Normal "next" pointers all implicitly point forward; BACK
++ * exists to make loop structures possible.
++ *
++ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular
++ * BRANCH structures using BACK. Simple cases (one character
++ * per match) are implemented with STAR and PLUS for speed
++ * and to minimize recursive plunges.
++ *
++ * OPEN,CLOSE ...are numbered at compile time.
++ */
+
-+ last_pattern_cache = node;/* points at the last non-NULL node */
-+ node = node->next;
-+ }
++/*
++ * A node is one char of opcode followed by two chars of "next" pointer.
++ * "Next" pointers are stored as two 8-bit pieces, high order first. The
++ * value is a positive offset from the opcode of the node containing it.
++ * An operand, if any, simply follows the node. (Note that much of the
++ * code generation knows about this implicit relationship.)
++ *
++ * Using two bytes for the "next" pointer is vast overkill for most things,
++ * but allows patterns to get big without disasters.
++ */
++#define OP(p) (*(p))
++#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
++#define OPERAND(p) ((p) + 3)
+
-+ /* If we reach the end of the list, then we have not yet cached
-+ the pattern for this regex. Let's do that now.
-+ Be paranoid about running out of memory to avoid list corruption. */
-+ tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC);
++/*
++ * See regmagic.h for one further detail of program structure.
++ */
+
-+ if(!tmp) {
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: out of memory in "
-+ "compile_and_cache, bailing.\n");
-+ return NULL;
-+ }
+
-+ tmp->regex_string = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC);
-+ tmp->pattern = kmalloc(sizeof(struct regexp), GFP_ATOMIC);
-+ tmp->next = NULL;
++/*
++ * Utility definitions.
++ */
++#ifndef CHARBITS
++#define UCHARAT(p) ((int)*(unsigned char *)(p))
++#else
++#define UCHARAT(p) ((int)*(p)&CHARBITS)
++#endif
+
-+ if(!tmp->regex_string || !tmp->pattern) {
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: out of memory in "
-+ "compile_and_cache, bailing.\n");
-+ kfree(tmp->regex_string);
-+ kfree(tmp->pattern);
-+ kfree(tmp);
-+ return NULL;
-+ }
++#define FAIL(m) { regerror(m); return(NULL); }
++#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
++#define META "^$.[()|?+*\\"
+
-+ /* Ok. The new node is all ready now. */
-+ node = tmp;
++/*
++ * Flags to be passed up and down.
++ */
++#define HASWIDTH 01 /* Known never to match null string. */
++#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
++#define SPSTART 04 /* Starts with * or +. */
++#define WORST 0 /* Worst case. */
+
-+ if(first_pattern_cache == NULL) /* list is empty */
-+ first_pattern_cache = node; /* make node the beginning */
-+ else
-+ last_pattern_cache->next = node; /* attach node to the end */
++/*
++ * Global work variables for regcomp().
++ */
++struct match_globals {
++char *reginput; /* String-input pointer. */
++char *regbol; /* Beginning of input, for ^ check. */
++char **regstartp; /* Pointer to startp array. */
++char **regendp; /* Ditto for endp. */
++char *regparse; /* Input-scan pointer. */
++int regnpar; /* () count. */
++char regdummy;
++char *regcode; /* Code-emit pointer; ®dummy = don't. */
++long regsize; /* Code size. */
++};
+
-+ /* copy the string and compile the regex */
-+ len = strlen(regex_string);
-+ DPRINTK("About to compile this: \"%s\"\n", regex_string);
-+ node->pattern = regcomp((char *)regex_string, &len);
-+ if ( !node->pattern ) {
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: Error compiling regexp "
-+ "\"%s\" (%s)\n",
-+ regex_string, protocol);
-+ /* pattern is now cached as NULL, so we won't try again. */
-+ }
++/*
++ * Forward declarations for regcomp()'s friends.
++ */
++#ifndef STATIC
++#define STATIC static
++#endif
++STATIC char *reg(struct match_globals *g, int paren,int *flagp);
++STATIC char *regbranch(struct match_globals *g, int *flagp);
++STATIC char *regpiece(struct match_globals *g, int *flagp);
++STATIC char *regatom(struct match_globals *g, int *flagp);
++STATIC char *regnode(struct match_globals *g, char op);
++STATIC char *regnext(struct match_globals *g, char *p);
++STATIC void regc(struct match_globals *g, char b);
++STATIC void reginsert(struct match_globals *g, char op, char *opnd);
++STATIC void regtail(struct match_globals *g, char *p, char *val);
++STATIC void regoptail(struct match_globals *g, char *p, char *val);
+
-+ strcpy(node->regex_string, regex_string);
-+ return node->pattern;
-+}
+
-+static int can_handle(const struct sk_buff *skb)
++__kernel_size_t my_strcspn(const char *s1,const char *s2)
+{
-+ if(!ip_hdr(skb)) /* not IP */
-+ return 0;
-+ if(ip_hdr(skb)->protocol != IPPROTO_TCP &&
-+ ip_hdr(skb)->protocol != IPPROTO_UDP &&
-+ ip_hdr(skb)->protocol != IPPROTO_ICMP)
-+ return 0;
-+ return 1;
++ char *scan1;
++ char *scan2;
++ int count;
++
++ count = 0;
++ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) {
++ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */
++ if (*scan1 == *scan2++)
++ return(count);
++ count++;
++ }
++ return(count);
+}
+
-+/* Returns offset the into the skb->data that the application data starts */
-+static int app_data_offset(const struct sk_buff *skb)
++/*
++ - regcomp - compile a regular expression into internal code
++ *
++ * We can't allocate space until we know how big the compiled form will be,
++ * but we can't compile it (and thus know how big it is) until we've got a
++ * place to put the code. So we cheat: we compile it twice, once with code
++ * generation turned off and size counting turned on, and once "for real".
++ * This also means that we don't allocate space until we are sure that the
++ * thing really will compile successfully, and we never have to move the
++ * code and thus invalidate pointers into it. (Note that it has to be in
++ * one piece because free() must be able to free it all.)
++ *
++ * Beware that the optimization-preparation code in here knows about some
++ * of the structure of the compiled regexp.
++ */
++regexp *
++regcomp(char *exp,int *patternsize)
+{
-+ /* In case we are ported somewhere (ebtables?) where ip_hdr(skb)
-+ isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */
-+ int ip_hl = 4*ip_hdr(skb)->ihl;
++ register regexp *r;
++ register char *scan;
++ register char *longest;
++ register int len;
++ int flags;
++ struct match_globals g;
++
++ /* commented out by ethan
++ extern char *malloc();
++ */
+
-+ if( ip_hdr(skb)->protocol == IPPROTO_TCP ) {
-+ /* 12 == offset into TCP header for the header length field.
-+ Can't get this with skb->h.th->doff because the tcphdr
-+ struct doesn't get set when routing (this is confirmed to be
-+ true in Netfilter as well as QoS.) */
-+ int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4);
++ if (exp == NULL)
++ FAIL("NULL argument");
+
-+ return ip_hl + tcp_hl;
-+ } else if( ip_hdr(skb)->protocol == IPPROTO_UDP ) {
-+ return ip_hl + 8; /* UDP header is always 8 bytes */
-+ } else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) {
-+ return ip_hl + 8; /* ICMP header is 8 bytes */
-+ } else {
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: tried to handle unknown "
-+ "protocol!\n");
-+ return ip_hl + 8; /* something reasonable */
-+ }
-+}
++ /* First pass: determine size, legality. */
++ g.regparse = exp;
++ g.regnpar = 1;
++ g.regsize = 0L;
++ g.regcode = &g.regdummy;
++ regc(&g, MAGIC);
++ if (reg(&g, 0, &flags) == NULL)
++ return(NULL);
+
-+/* handles whether there's a match when we aren't appending data anymore */
-+static int match_no_append(struct nf_conn * conntrack,
-+ struct nf_conn * master_conntrack,
-+ enum ip_conntrack_info ctinfo,
-+ enum ip_conntrack_info master_ctinfo,
-+ const struct xt_layer7_info * info)
-+{
-+ /* If we're in here, throw the app data away */
-+ if(master_conntrack->layer7.app_data != NULL) {
++ /* Small enough for pointer-storage convention? */
++ if (g.regsize >= 32767L) /* Probably could be 65535L. */
++ FAIL("regexp too big");
+
-+ #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG
-+ if(!master_conntrack->layer7.app_proto) {
-+ char * f =
-+ friendly_print(master_conntrack->layer7.app_data);
-+ char * g =
-+ hex_print(master_conntrack->layer7.app_data);
-+ DPRINTK("\nl7-filter gave up after %d bytes "
-+ "(%d packets):\n%s\n",
-+ strlen(f), total_acct_packets(master_conntrack), f);
-+ kfree(f);
-+ DPRINTK("In hex: %s\n", g);
-+ kfree(g);
-+ }
-+ #endif
++ /* Allocate space. */
++ *patternsize=sizeof(regexp) + (unsigned)g.regsize;
++ r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize);
++ if (r == NULL)
++ FAIL("out of space");
+
-+ kfree(master_conntrack->layer7.app_data);
-+ master_conntrack->layer7.app_data = NULL; /* don't free again */
-+ }
++ /* Second pass: emit code. */
++ g.regparse = exp;
++ g.regnpar = 1;
++ g.regcode = r->program;
++ regc(&g, MAGIC);
++ if (reg(&g, 0, &flags) == NULL)
++ return(NULL);
+
-+ if(master_conntrack->layer7.app_proto){
-+ /* Here child connections set their .app_proto (for /proc) */
-+ if(!conntrack->layer7.app_proto) {
-+ conntrack->layer7.app_proto =
-+ kmalloc(strlen(master_conntrack->layer7.app_proto)+1,
-+ GFP_ATOMIC);
-+ if(!conntrack->layer7.app_proto){
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: out of memory "
-+ "in match_no_append, "
-+ "bailing.\n");
-+ return 1;
-+ }
-+ strcpy(conntrack->layer7.app_proto,
-+ master_conntrack->layer7.app_proto);
-+ }
++ /* Dig out information for optimizations. */
++ r->regstart = '\0'; /* Worst-case defaults. */
++ r->reganch = 0;
++ r->regmust = NULL;
++ r->regmlen = 0;
++ scan = r->program+1; /* First BRANCH. */
++ if (OP(regnext(&g, scan)) == END) { /* Only one top-level choice. */
++ scan = OPERAND(scan);
+
-+ return (!strcmp(master_conntrack->layer7.app_proto,
-+ info->protocol));
-+ }
-+ else {
-+ /* If not classified, set to "unknown" to distinguish from
-+ connections that are still being tested. */
-+ master_conntrack->layer7.app_proto =
-+ kmalloc(strlen("unknown")+1, GFP_ATOMIC);
-+ if(!master_conntrack->layer7.app_proto){
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: out of memory in "
-+ "match_no_append, bailing.\n");
-+ return 1;
++ /* Starting-point info. */
++ if (OP(scan) == EXACTLY)
++ r->regstart = *OPERAND(scan);
++ else if (OP(scan) == BOL)
++ r->reganch++;
++
++ /*
++ * If there's something expensive in the r.e., find the
++ * longest literal string that must appear and make it the
++ * regmust. Resolve ties in favor of later strings, since
++ * the regstart check works with the beginning of the r.e.
++ * and avoiding duplication strengthens checking. Not a
++ * strong reason, but sufficient in the absence of others.
++ */
++ if (flags&SPSTART) {
++ longest = NULL;
++ len = 0;
++ for (; scan != NULL; scan = regnext(&g, scan))
++ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
++ longest = OPERAND(scan);
++ len = strlen(OPERAND(scan));
++ }
++ r->regmust = longest;
++ r->regmlen = len;
+ }
-+ strcpy(master_conntrack->layer7.app_proto, "unknown");
-+ return 0;
+ }
++
++ return(r);
+}
+
-+/* add the new app data to the conntrack. Return number of bytes added. */
-+static int add_data(struct nf_conn * master_conntrack,
-+ char * app_data, int appdatalen)
++/*
++ - reg - regular expression, i.e. main body or parenthesized thing
++ *
++ * Caller must absorb opening parenthesis.
++ *
++ * Combining parenthesis handling with the base level of regular expression
++ * is a trifle forced, but the need to tie the tails of the branches to what
++ * follows makes it hard to avoid.
++ */
++static char *
++reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ )
+{
-+ int length = 0, i;
-+ int oldlength = master_conntrack->layer7.app_data_len;
++ register char *ret;
++ register char *br;
++ register char *ender;
++ register int parno = 0; /* 0 makes gcc happy */
++ int flags;
+
-+ /* This is a fix for a race condition by Deti Fliegl. However, I'm not
-+ clear on whether the race condition exists or whether this really
-+ fixes it. I might just be being dense... Anyway, if it's not really
-+ a fix, all it does is waste a very small amount of time. */
-+ if(!master_conntrack->layer7.app_data) return 0;
++ *flagp = HASWIDTH; /* Tentatively. */
+
-+ /* Strip nulls. Make everything lower case (our regex lib doesn't
-+ do case insensitivity). Add it to the end of the current data. */
-+ for(i = 0; i < maxdatalen-oldlength-1 &&
-+ i < appdatalen; i++) {
-+ if(app_data[i] != '\0') {
-+ /* the kernel version of tolower mungs 'upper ascii' */
-+ master_conntrack->layer7.app_data[length+oldlength] =
-+ isascii(app_data[i])?
-+ tolower(app_data[i]) : app_data[i];
-+ length++;
-+ }
-+ }
++ /* Make an OPEN node, if parenthesized. */
++ if (paren) {
++ if (g->regnpar >= NSUBEXP)
++ FAIL("too many ()");
++ parno = g->regnpar;
++ g->regnpar++;
++ ret = regnode(g, OPEN+parno);
++ } else
++ ret = NULL;
+
-+ master_conntrack->layer7.app_data[length+oldlength] = '\0';
-+ master_conntrack->layer7.app_data_len = length + oldlength;
++ /* Pick up the branches, linking them together. */
++ br = regbranch(g, &flags);
++ if (br == NULL)
++ return(NULL);
++ if (ret != NULL)
++ regtail(g, ret, br); /* OPEN -> first. */
++ else
++ ret = br;
++ if (!(flags&HASWIDTH))
++ *flagp &= ~HASWIDTH;
++ *flagp |= flags&SPSTART;
++ while (*g->regparse == '|') {
++ g->regparse++;
++ br = regbranch(g, &flags);
++ if (br == NULL)
++ return(NULL);
++ regtail(g, ret, br); /* BRANCH -> BRANCH. */
++ if (!(flags&HASWIDTH))
++ *flagp &= ~HASWIDTH;
++ *flagp |= flags&SPSTART;
++ }
+
-+ return length;
-+}
++ /* Make a closing node, and hook it on the end. */
++ ender = regnode(g, (paren) ? CLOSE+parno : END);
++ regtail(g, ret, ender);
+
-+/* taken from drivers/video/modedb.c */
-+static int my_atoi(const char *s)
-+{
-+ int val = 0;
++ /* Hook the tails of the branches to the closing node. */
++ for (br = ret; br != NULL; br = regnext(g, br))
++ regoptail(g, br, ender);
+
-+ for (;; s++) {
-+ switch (*s) {
-+ case '0'...'9':
-+ val = 10*val+(*s-'0');
-+ break;
-+ default:
-+ return val;
-+ }
++ /* Check for proper termination. */
++ if (paren && *g->regparse++ != ')') {
++ FAIL("unmatched ()");
++ } else if (!paren && *g->regparse != '\0') {
++ if (*g->regparse == ')') {
++ FAIL("unmatched ()");
++ } else
++ FAIL("junk on end"); /* "Can't happen". */
++ /* NOTREACHED */
+ }
++
++ return(ret);
+}
+
-+/* write out num_packets to userland. */
-+static int layer7_read_proc(char* page, char ** start, off_t off, int count,
-+ int* eof, void * data)
++/*
++ - regbranch - one alternative of an | operator
++ *
++ * Implements the concatenation operator.
++ */
++static char *
++regbranch(struct match_globals *g, int *flagp)
+{
-+ if(num_packets > 99 && net_ratelimit())
-+ printk(KERN_ERR "layer7: NOT REACHED. num_packets too big\n");
++ register char *ret;
++ register char *chain;
++ register char *latest;
++ int flags;
+
-+ page[0] = num_packets/10 + '0';
-+ page[1] = num_packets%10 + '0';
-+ page[2] = '\n';
-+ page[3] = '\0';
++ *flagp = WORST; /* Tentatively. */
+
-+ *eof=1;
++ ret = regnode(g, BRANCH);
++ chain = NULL;
++ while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') {
++ latest = regpiece(g, &flags);
++ if (latest == NULL)
++ return(NULL);
++ *flagp |= flags&HASWIDTH;
++ if (chain == NULL) /* First piece. */
++ *flagp |= flags&SPSTART;
++ else
++ regtail(g, chain, latest);
++ chain = latest;
++ }
++ if (chain == NULL) /* Loop ran zero times. */
++ (void) regnode(g, NOTHING);
+
-+ return 3;
++ return(ret);
+}
+
-+/* Read in num_packets from userland */
-+static int layer7_write_proc(struct file* file, const char* buffer,
-+ unsigned long count, void *data)
++/*
++ - regpiece - something followed by possible [*+?]
++ *
++ * Note that the branching code sequences used for ? and the general cases
++ * of * and + are somewhat optimized: they use the same NOTHING node as
++ * both the endmarker for their branch list and the body of the last branch.
++ * It might seem that this node could be dispensed with entirely, but the
++ * endmarker role is not redundant.
++ */
++static char *
++regpiece(struct match_globals *g, int *flagp)
+{
-+ char * foo = kmalloc(count, GFP_ATOMIC);
++ register char *ret;
++ register char op;
++ register char *next;
++ int flags;
+
-+ if(!foo){
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: out of memory, bailing. "
-+ "num_packets unchanged.\n");
-+ return count;
-+ }
++ ret = regatom(g, &flags);
++ if (ret == NULL)
++ return(NULL);
+
-+ if(copy_from_user(foo, buffer, count)) {
-+ return -EFAULT;
++ op = *g->regparse;
++ if (!ISMULT(op)) {
++ *flagp = flags;
++ return(ret);
+ }
+
++ if (!(flags&HASWIDTH) && op != '?')
++ FAIL("*+ operand could be empty");
++ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
+
-+ num_packets = my_atoi(foo);
-+ kfree (foo);
-+
-+ /* This has an arbitrary limit to make the math easier. I'm lazy.
-+ But anyway, 99 is a LOT! If you want more, you're doing it wrong! */
-+ if(num_packets > 99) {
-+ printk(KERN_WARNING "layer7: num_packets can't be > 99.\n");
-+ num_packets = 99;
-+ } else if(num_packets < 1) {
-+ printk(KERN_WARNING "layer7: num_packets can't be < 1.\n");
-+ num_packets = 1;
++ if (op == '*' && (flags&SIMPLE))
++ reginsert(g, STAR, ret);
++ else if (op == '*') {
++ /* Emit x* as (x&|), where & means "self". */
++ reginsert(g, BRANCH, ret); /* Either x */
++ regoptail(g, ret, regnode(g, BACK)); /* and loop */
++ regoptail(g, ret, ret); /* back */
++ regtail(g, ret, regnode(g, BRANCH)); /* or */
++ regtail(g, ret, regnode(g, NOTHING)); /* null. */
++ } else if (op == '+' && (flags&SIMPLE))
++ reginsert(g, PLUS, ret);
++ else if (op == '+') {
++ /* Emit x+ as x(&|), where & means "self". */
++ next = regnode(g, BRANCH); /* Either */
++ regtail(g, ret, next);
++ regtail(g, regnode(g, BACK), ret); /* loop back */
++ regtail(g, next, regnode(g, BRANCH)); /* or */
++ regtail(g, ret, regnode(g, NOTHING)); /* null. */
++ } else if (op == '?') {
++ /* Emit x? as (x|) */
++ reginsert(g, BRANCH, ret); /* Either x */
++ regtail(g, ret, regnode(g, BRANCH)); /* or */
++ next = regnode(g, NOTHING); /* null. */
++ regtail(g, ret, next);
++ regoptail(g, ret, next);
+ }
++ g->regparse++;
++ if (ISMULT(*g->regparse))
++ FAIL("nested *?+");
+
-+ return count;
++ return(ret);
+}
+
-+static bool
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
-+match(const struct sk_buff *skbin, struct xt_action_param *par)
-+#else
-+match(const struct sk_buff *skbin,
-+ const struct net_device *in,
-+ const struct net_device *out,
-+ const struct xt_match *match,
-+ const void *matchinfo,
-+ int offset,
-+ unsigned int protoff,
-+ bool *hotdrop)
-+#endif
++/*
++ - regatom - the lowest level
++ *
++ * Optimization: gobbles an entire sequence of ordinary characters so that
++ * it can turn them into a single node, which is smaller to store and
++ * faster to run. Backslashed characters are exceptions, each becoming a
++ * separate node; the code is simpler that way and it's not worth fixing.
++ */
++static char *
++regatom(struct match_globals *g, int *flagp)
+{
-+ /* sidestep const without getting a compiler warning... */
-+ struct sk_buff * skb = (struct sk_buff *)skbin;
-+
-+ const struct xt_layer7_info * info =
-+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
-+ par->matchinfo;
-+ #else
-+ matchinfo;
-+ #endif
++ register char *ret;
++ int flags;
+
-+ enum ip_conntrack_info master_ctinfo, ctinfo;
-+ struct nf_conn *master_conntrack, *conntrack;
-+ unsigned char * app_data;
-+ unsigned int pattern_result, appdatalen;
-+ regexp * comppattern;
++ *flagp = WORST; /* Tentatively. */
+
-+ /* Be paranoid/incompetent - lock the entire match function. */
-+ spin_lock_bh(&l7_lock);
++ switch (*g->regparse++) {
++ case '^':
++ ret = regnode(g, BOL);
++ break;
++ case '$':
++ ret = regnode(g, EOL);
++ break;
++ case '.':
++ ret = regnode(g, ANY);
++ *flagp |= HASWIDTH|SIMPLE;
++ break;
++ case '[': {
++ register int class;
++ register int classend;
+
-+ if(!can_handle(skb)){
-+ DPRINTK("layer7: This is some protocol I can't handle.\n");
-+ spin_unlock_bh(&l7_lock);
-+ return info->invert;
-+ }
++ if (*g->regparse == '^') { /* Complement of range. */
++ ret = regnode(g, ANYBUT);
++ g->regparse++;
++ } else
++ ret = regnode(g, ANYOF);
++ if (*g->regparse == ']' || *g->regparse == '-')
++ regc(g, *g->regparse++);
++ while (*g->regparse != '\0' && *g->regparse != ']') {
++ if (*g->regparse == '-') {
++ g->regparse++;
++ if (*g->regparse == ']' || *g->regparse == '\0')
++ regc(g, '-');
++ else {
++ class = UCHARAT(g->regparse-2)+1;
++ classend = UCHARAT(g->regparse);
++ if (class > classend+1)
++ FAIL("invalid [] range");
++ for (; class <= classend; class++)
++ regc(g, class);
++ g->regparse++;
++ }
++ } else
++ regc(g, *g->regparse++);
++ }
++ regc(g, '\0');
++ if (*g->regparse != ']')
++ FAIL("unmatched []");
++ g->regparse++;
++ *flagp |= HASWIDTH|SIMPLE;
++ }
++ break;
++ case '(':
++ ret = reg(g, 1, &flags);
++ if (ret == NULL)
++ return(NULL);
++ *flagp |= flags&(HASWIDTH|SPSTART);
++ break;
++ case '\0':
++ case '|':
++ case ')':
++ FAIL("internal urp"); /* Supposed to be caught earlier. */
++ break;
++ case '?':
++ case '+':
++ case '*':
++ FAIL("?+* follows nothing");
++ break;
++ case '\\':
++ if (*g->regparse == '\0')
++ FAIL("trailing \\");
++ ret = regnode(g, EXACTLY);
++ regc(g, *g->regparse++);
++ regc(g, '\0');
++ *flagp |= HASWIDTH|SIMPLE;
++ break;
++ default: {
++ register int len;
++ register char ender;
+
-+ /* Treat parent & all its children together as one connection, except
-+ for the purpose of setting conntrack->layer7.app_proto in the actual
-+ connection. This makes /proc/net/ip_conntrack more satisfying. */
-+ if(!(conntrack = nf_ct_get(skb, &ctinfo)) ||
-+ !(master_conntrack=nf_ct_get(skb,&master_ctinfo))){
-+ DPRINTK("layer7: couldn't get conntrack.\n");
-+ spin_unlock_bh(&l7_lock);
-+ return info->invert;
++ g->regparse--;
++ len = my_strcspn((const char *)g->regparse, (const char *)META);
++ if (len <= 0)
++ FAIL("internal disaster");
++ ender = *(g->regparse+len);
++ if (len > 1 && ISMULT(ender))
++ len--; /* Back off clear of ?+* operand. */
++ *flagp |= HASWIDTH;
++ if (len == 1)
++ *flagp |= SIMPLE;
++ ret = regnode(g, EXACTLY);
++ while (len > 0) {
++ regc(g, *g->regparse++);
++ len--;
++ }
++ regc(g, '\0');
++ }
++ break;
+ }
+
-+ /* Try to get a master conntrack (and its master etc) for FTP, etc. */
-+ while (master_ct(master_conntrack) != NULL)
-+ master_conntrack = master_ct(master_conntrack);
-+
-+ /* if we've classified it or seen too many packets */
-+ if(total_acct_packets(master_conntrack) > num_packets ||
-+ master_conntrack->layer7.app_proto) {
-+
-+ pattern_result = match_no_append(conntrack, master_conntrack,
-+ ctinfo, master_ctinfo, info);
-+
-+ /* skb->cb[0] == seen. Don't do things twice if there are
-+ multiple l7 rules. I'm not sure that using cb for this purpose
-+ is correct, even though it says "put your private variables
-+ there". But it doesn't look like it is being used for anything
-+ else in the skbs that make it here. */
-+ skb->cb[0] = 1; /* marking it seen here's probably irrelevant */
++ return(ret);
++}
+
-+ spin_unlock_bh(&l7_lock);
-+ return (pattern_result ^ info->invert);
-+ }
++/*
++ - regnode - emit a node
++ */
++static char * /* Location. */
++regnode(struct match_globals *g, char op)
++{
++ register char *ret;
++ register char *ptr;
+
-+ if(skb_is_nonlinear(skb)){
-+ if(skb_linearize(skb) != 0){
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: failed to linearize "
-+ "packet, bailing.\n");
-+ spin_unlock_bh(&l7_lock);
-+ return info->invert;
-+ }
++ ret = g->regcode;
++ if (ret == &g->regdummy) {
++ g->regsize += 3;
++ return(ret);
+ }
+
-+ /* now that the skb is linearized, it's safe to set these. */
-+ app_data = skb->data + app_data_offset(skb);
-+ appdatalen = skb_tail_pointer(skb) - app_data;
++ ptr = ret;
++ *ptr++ = op;
++ *ptr++ = '\0'; /* Null "next" pointer. */
++ *ptr++ = '\0';
++ g->regcode = ptr;
+
-+ /* the return value gets checked later, when we're ready to use it */
-+ comppattern = compile_and_cache(info->pattern, info->protocol);
++ return(ret);
++}
+
-+ /* On the first packet of a connection, allocate space for app data */
-+ if(total_acct_packets(master_conntrack) == 1 && !skb->cb[0] &&
-+ !master_conntrack->layer7.app_data){
-+ master_conntrack->layer7.app_data =
-+ kmalloc(maxdatalen, GFP_ATOMIC);
-+ if(!master_conntrack->layer7.app_data){
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: out of memory in "
-+ "match, bailing.\n");
-+ spin_unlock_bh(&l7_lock);
-+ return info->invert;
-+ }
-+
-+ master_conntrack->layer7.app_data[0] = '\0';
-+ }
-+
-+ /* Can be here, but unallocated, if numpackets is increased near
-+ the beginning of a connection */
-+ if(master_conntrack->layer7.app_data == NULL){
-+ spin_unlock_bh(&l7_lock);
-+ return info->invert; /* unmatched */
-+ }
-+
-+ if(!skb->cb[0]){
-+ int newbytes;
-+ newbytes = add_data(master_conntrack, app_data, appdatalen);
-+
-+ if(newbytes == 0) { /* didn't add any data */
-+ skb->cb[0] = 1;
-+ /* Didn't match before, not going to match now */
-+ spin_unlock_bh(&l7_lock);
-+ return info->invert;
-+ }
-+ }
-+
-+ /* If looking for "unknown", then never match. "Unknown" means that
-+ we've given up; we're still trying with these packets. */
-+ if(!strcmp(info->protocol, "unknown")) {
-+ pattern_result = 0;
-+ /* If looking for "unset", then always match. "Unset" means that we
-+ haven't yet classified the connection. */
-+ } else if(!strcmp(info->protocol, "unset")) {
-+ pattern_result = 2;
-+ DPRINTK("layer7: matched unset: not yet classified "
-+ "(%d/%d packets)\n",
-+ total_acct_packets(master_conntrack), num_packets);
-+ /* If the regexp failed to compile, don't bother running it */
-+ } else if(comppattern &&
-+ regexec(comppattern, master_conntrack->layer7.app_data)){
-+ DPRINTK("layer7: matched %s\n", info->protocol);
-+ pattern_result = 1;
-+ } else pattern_result = 0;
-+
-+ if(pattern_result == 1) {
-+ master_conntrack->layer7.app_proto =
-+ kmalloc(strlen(info->protocol)+1, GFP_ATOMIC);
-+ if(!master_conntrack->layer7.app_proto){
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: out of memory in "
-+ "match, bailing.\n");
-+ spin_unlock_bh(&l7_lock);
-+ return (pattern_result ^ info->invert);
-+ }
-+ strcpy(master_conntrack->layer7.app_proto, info->protocol);
-+ } else if(pattern_result > 1) { /* cleanup from "unset" */
-+ pattern_result = 1;
-+ }
-+
-+ /* mark the packet seen */
-+ skb->cb[0] = 1;
-+
-+ spin_unlock_bh(&l7_lock);
-+ return (pattern_result ^ info->invert);
-+}
-+
-+// load nf_conntrack_ipv4
-+static int check(const struct xt_mtchk_param *par)
++/*
++ - regc - emit (if appropriate) a byte of code
++ */
++static void
++regc(struct match_globals *g, char b)
+{
-+ if (nf_ct_l3proto_try_module_get(par->family) < 0) {
-+ pr_info("can't load conntrack support for "
-+ "proto=%d\n", par->family);
-+ return -EINVAL;
-+ }
-+ return 0;
++ if (g->regcode != &g->regdummy)
++ *g->regcode++ = b;
++ else
++ g->regsize++;
+}
+
++/*
++ - reginsert - insert an operator in front of already-emitted operand
++ *
++ * Means relocating the operand.
++ */
++static void
++reginsert(struct match_globals *g, char op, char* opnd)
++{
++ register char *src;
++ register char *dst;
++ register char *place;
+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
-+ static void destroy(const struct xt_mtdtor_param *par)
-+ {
-+ nf_ct_l3proto_module_put(par->family);
-+ }
-+#else
-+ static void destroy(const struct xt_match *match, void *matchinfo)
-+ {
-+ nf_ct_l3proto_module_put(match->family);
++ if (g->regcode == &g->regdummy) {
++ g->regsize += 3;
++ return;
+ }
-+#endif
+
-+static struct xt_match xt_layer7_match[] __read_mostly = {
-+{
-+ .name = "layer7",
-+ .family = AF_INET,
-+ .checkentry = check,
-+ .match = match,
-+ .destroy = destroy,
-+ .matchsize = sizeof(struct xt_layer7_info),
-+ .me = THIS_MODULE
-+}
-+};
++ src = g->regcode;
++ g->regcode += 3;
++ dst = g->regcode;
++ while (src > opnd)
++ *--dst = *--src;
+
-+static void layer7_cleanup_proc(void)
-+{
-+ remove_proc_entry("layer7_numpackets", init_net.proc_net);
++ place = opnd; /* Op node, where operand used to be. */
++ *place++ = op;
++ *place++ = '\0';
++ *place++ = '\0';
+}
+
-+/* register the proc file */
-+static void layer7_init_proc(void)
++/*
++ - regtail - set the next-pointer at the end of a node chain
++ */
++static void
++regtail(struct match_globals *g, char *p, char *val)
+{
-+ struct proc_dir_entry* entry;
-+ entry = create_proc_entry("layer7_numpackets", 0644, init_net.proc_net);
-+ entry->read_proc = layer7_read_proc;
-+ entry->write_proc = layer7_write_proc;
-+}
++ register char *scan;
++ register char *temp;
++ register int offset;
+
-+static int __init xt_layer7_init(void)
-+{
-+ need_conntrack();
++ if (p == &g->regdummy)
++ return;
+
-+ layer7_init_proc();
-+ if(maxdatalen < 1) {
-+ printk(KERN_WARNING "layer7: maxdatalen can't be < 1, "
-+ "using 1\n");
-+ maxdatalen = 1;
-+ }
-+ /* This is not a hard limit. It's just here to prevent people from
-+ bringing their slow machines to a grinding halt. */
-+ else if(maxdatalen > 65536) {
-+ printk(KERN_WARNING "layer7: maxdatalen can't be > 65536, "
-+ "using 65536\n");
-+ maxdatalen = 65536;
++ /* Find last node. */
++ scan = p;
++ for (;;) {
++ temp = regnext(g, scan);
++ if (temp == NULL)
++ break;
++ scan = temp;
+ }
-+ return xt_register_matches(xt_layer7_match,
-+ ARRAY_SIZE(xt_layer7_match));
++
++ if (OP(scan) == BACK)
++ offset = scan - val;
++ else
++ offset = val - scan;
++ *(scan+1) = (offset>>8)&0377;
++ *(scan+2) = offset&0377;
+}
+
-+static void __exit xt_layer7_fini(void)
++/*
++ - regoptail - regtail on operand of first argument; nop if operandless
++ */
++static void
++regoptail(struct match_globals *g, char *p, char *val)
+{
-+ layer7_cleanup_proc();
-+ xt_unregister_matches(xt_layer7_match, ARRAY_SIZE(xt_layer7_match));
++ /* "Operandless" and "op != BRANCH" are synonymous in practice. */
++ if (p == NULL || p == &g->regdummy || OP(p) != BRANCH)
++ return;
++ regtail(g, OPERAND(p), val);
+}
+
-+module_init(xt_layer7_init);
-+module_exit(xt_layer7_fini);
---- linux-2.6.28-stock/net/netfilter/regexp/regexp.c 1969-12-31 18:00:00.000000000 -0600
-+++ linux-2.6.28/net/netfilter/regexp/regexp.c 2009-01-07 16:07:31.000000000 -0600
-@@ -0,0 +1,1197 @@
+/*
-+ * regcomp and regexec -- regsub and regerror are elsewhere
-+ * @(#)regexp.c 1.3 of 18 April 87
-+ *
-+ * Copyright (c) 1986 by University of Toronto.
-+ * Written by Henry Spencer. Not derived from licensed software.
-+ *
-+ * Permission is granted to anyone to use this software for any
-+ * purpose on any computer system, and to redistribute it freely,
-+ * subject to the following restrictions:
-+ *
-+ * 1. The author is not responsible for the consequences of use of
-+ * this software, no matter how awful, even if they arise
-+ * from defects in it.
-+ *
-+ * 2. The origin of this software must not be misrepresented, either
-+ * by explicit claim or by omission.
-+ *
-+ * 3. Altered versions must be plainly marked as such, and must not
-+ * be misrepresented as being the original software.
-+ *
-+ * Beware that some of this code is subtly aware of the way operator
-+ * precedence is structured in regular expressions. Serious changes in
-+ * regular-expression syntax might require a total rethink.
-+ *
-+ * This code was modified by Ethan Sommer to work within the kernel
-+ * (it now uses kmalloc etc..)
-+ *
-+ * Modified slightly by Matthew Strait to use more modern C.
++ * regexec and friends
+ */
+
-+#include "regexp.h"
-+#include "regmagic.h"
+
-+/* added by ethan and matt. Lets it work in both kernel and user space.
-+(So iptables can use it, for instance.) Yea, it goes both ways... */
-+#if __KERNEL__
-+ #define malloc(foo) kmalloc(foo,GFP_ATOMIC)
-+#else
-+ #define printk(format,args...) printf(format,##args)
-+#endif
++/*
++ * Forwards.
++ */
++STATIC int regtry(struct match_globals *g, regexp *prog, char *string);
++STATIC int regmatch(struct match_globals *g, char *prog);
++STATIC int regrepeat(struct match_globals *g, char *p);
+
-+void regerror(char * s)
-+{
-+ printk("<3>Regexp: %s\n", s);
-+ /* NOTREACHED */
-+}
++#ifdef DEBUG
++int regnarrate = 0;
++void regdump();
++STATIC char *regprop(char *op);
++#endif
+
+/*
-+ * The "internal use only" fields in regexp.h are present to pass info from
-+ * compile to execute that permits the execute phase to run lots faster on
-+ * simple cases. They are:
-+ *
-+ * regstart char that must begin a match; '\0' if none obvious
-+ * reganch is the match anchored (at beginning-of-line only)?
-+ * regmust string (pointer into program) that match must include, or NULL
-+ * regmlen length of regmust string
-+ *
-+ * Regstart and reganch permit very fast decisions on suitable starting points
-+ * for a match, cutting down the work a lot. Regmust permits fast rejection
-+ * of lines that cannot possibly match. The regmust tests are costly enough
-+ * that regcomp() supplies a regmust only if the r.e. contains something
-+ * potentially expensive (at present, the only such thing detected is * or +
-+ * at the start of the r.e., which can involve a lot of backup). Regmlen is
-+ * supplied because the test in regexec() needs it and regcomp() is computing
-+ * it anyway.
-+ */
-+
-+/*
-+ * Structure for regexp "program". This is essentially a linear encoding
-+ * of a nondeterministic finite-state machine (aka syntax charts or
-+ * "railroad normal form" in parsing technology). Each node is an opcode
-+ * plus a "next" pointer, possibly plus an operand. "Next" pointers of
-+ * all nodes except BRANCH implement concatenation; a "next" pointer with
-+ * a BRANCH on both ends of it is connecting two alternatives. (Here we
-+ * have one of the subtle syntax dependencies: an individual BRANCH (as
-+ * opposed to a collection of them) is never concatenated with anything
-+ * because of operator precedence.) The operand of some types of node is
-+ * a literal string; for others, it is a node leading into a sub-FSM. In
-+ * particular, the operand of a BRANCH node is the first node of the branch.
-+ * (NB this is *not* a tree structure: the tail of the branch connects
-+ * to the thing following the set of BRANCHes.) The opcodes are:
-+ */
-+
-+/* definition number opnd? meaning */
-+#define END 0 /* no End of program. */
-+#define BOL 1 /* no Match "" at beginning of line. */
-+#define EOL 2 /* no Match "" at end of line. */
-+#define ANY 3 /* no Match any one character. */
-+#define ANYOF 4 /* str Match any character in this string. */
-+#define ANYBUT 5 /* str Match any character not in this string. */
-+#define BRANCH 6 /* node Match this alternative, or the next... */
-+#define BACK 7 /* no Match "", "next" ptr points backward. */
-+#define EXACTLY 8 /* str Match this string. */
-+#define NOTHING 9 /* no Match empty string. */
-+#define STAR 10 /* node Match this (simple) thing 0 or more times. */
-+#define PLUS 11 /* node Match this (simple) thing 1 or more times. */
-+#define OPEN 20 /* no Mark this point in input as start of #n. */
-+ /* OPEN+1 is number 1, etc. */
-+#define CLOSE 30 /* no Analogous to OPEN. */
-+
-+/*
-+ * Opcode notes:
-+ *
-+ * BRANCH The set of branches constituting a single choice are hooked
-+ * together with their "next" pointers, since precedence prevents
-+ * anything being concatenated to any individual branch. The
-+ * "next" pointer of the last BRANCH in a choice points to the
-+ * thing following the whole choice. This is also where the
-+ * final "next" pointer of each individual branch points; each
-+ * branch starts with the operand node of a BRANCH node.
-+ *
-+ * BACK Normal "next" pointers all implicitly point forward; BACK
-+ * exists to make loop structures possible.
-+ *
-+ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular
-+ * BRANCH structures using BACK. Simple cases (one character
-+ * per match) are implemented with STAR and PLUS for speed
-+ * and to minimize recursive plunges.
-+ *
-+ * OPEN,CLOSE ...are numbered at compile time.
++ - regexec - match a regexp against a string
+ */
++int
++regexec(regexp *prog, char *string)
++{
++ register char *s;
++ struct match_globals g;
+
-+/*
-+ * A node is one char of opcode followed by two chars of "next" pointer.
-+ * "Next" pointers are stored as two 8-bit pieces, high order first. The
-+ * value is a positive offset from the opcode of the node containing it.
-+ * An operand, if any, simply follows the node. (Note that much of the
-+ * code generation knows about this implicit relationship.)
-+ *
-+ * Using two bytes for the "next" pointer is vast overkill for most things,
-+ * but allows patterns to get big without disasters.
-+ */
-+#define OP(p) (*(p))
-+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
-+#define OPERAND(p) ((p) + 3)
++ /* Be paranoid... */
++ if (prog == NULL || string == NULL) {
++ printk("<3>Regexp: NULL parameter\n");
++ return(0);
++ }
+
-+/*
-+ * See regmagic.h for one further detail of program structure.
-+ */
++ /* Check validity of program. */
++ if (UCHARAT(prog->program) != MAGIC) {
++ printk("<3>Regexp: corrupted program\n");
++ return(0);
++ }
+
++ /* If there is a "must appear" string, look for it. */
++ if (prog->regmust != NULL) {
++ s = string;
++ while ((s = strchr(s, prog->regmust[0])) != NULL) {
++ if (strncmp(s, prog->regmust, prog->regmlen) == 0)
++ break; /* Found it. */
++ s++;
++ }
++ if (s == NULL) /* Not present. */
++ return(0);
++ }
+
-+/*
-+ * Utility definitions.
-+ */
-+#ifndef CHARBITS
-+#define UCHARAT(p) ((int)*(unsigned char *)(p))
-+#else
-+#define UCHARAT(p) ((int)*(p)&CHARBITS)
-+#endif
++ /* Mark beginning of line for ^ . */
++ g.regbol = string;
+
-+#define FAIL(m) { regerror(m); return(NULL); }
-+#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
-+#define META "^$.[()|?+*\\"
++ /* Simplest case: anchored match need be tried only once. */
++ if (prog->reganch)
++ return(regtry(&g, prog, string));
+
-+/*
-+ * Flags to be passed up and down.
-+ */
-+#define HASWIDTH 01 /* Known never to match null string. */
-+#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
-+#define SPSTART 04 /* Starts with * or +. */
-+#define WORST 0 /* Worst case. */
++ /* Messy cases: unanchored match. */
++ s = string;
++ if (prog->regstart != '\0')
++ /* We know what char it must start with. */
++ while ((s = strchr(s, prog->regstart)) != NULL) {
++ if (regtry(&g, prog, s))
++ return(1);
++ s++;
++ }
++ else
++ /* We don't -- general case. */
++ do {
++ if (regtry(&g, prog, s))
++ return(1);
++ } while (*s++ != '\0');
+
-+/*
-+ * Global work variables for regcomp().
-+ */
-+struct match_globals {
-+char *reginput; /* String-input pointer. */
-+char *regbol; /* Beginning of input, for ^ check. */
-+char **regstartp; /* Pointer to startp array. */
-+char **regendp; /* Ditto for endp. */
-+char *regparse; /* Input-scan pointer. */
-+int regnpar; /* () count. */
-+char regdummy;
-+char *regcode; /* Code-emit pointer; ®dummy = don't. */
-+long regsize; /* Code size. */
-+};
++ /* Failure. */
++ return(0);
++}
+
+/*
-+ * Forward declarations for regcomp()'s friends.
++ - regtry - try match at specific point
+ */
-+#ifndef STATIC
-+#define STATIC static
-+#endif
-+STATIC char *reg(struct match_globals *g, int paren,int *flagp);
-+STATIC char *regbranch(struct match_globals *g, int *flagp);
-+STATIC char *regpiece(struct match_globals *g, int *flagp);
-+STATIC char *regatom(struct match_globals *g, int *flagp);
-+STATIC char *regnode(struct match_globals *g, char op);
-+STATIC char *regnext(struct match_globals *g, char *p);
-+STATIC void regc(struct match_globals *g, char b);
-+STATIC void reginsert(struct match_globals *g, char op, char *opnd);
-+STATIC void regtail(struct match_globals *g, char *p, char *val);
-+STATIC void regoptail(struct match_globals *g, char *p, char *val);
-+
-+
-+__kernel_size_t my_strcspn(const char *s1,const char *s2)
++static int /* 0 failure, 1 success */
++regtry(struct match_globals *g, regexp *prog, char *string)
+{
-+ char *scan1;
-+ char *scan2;
-+ int count;
++ register int i;
++ register char **sp;
++ register char **ep;
+
-+ count = 0;
-+ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) {
-+ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */
-+ if (*scan1 == *scan2++)
-+ return(count);
-+ count++;
-+ }
-+ return(count);
++ g->reginput = string;
++ g->regstartp = prog->startp;
++ g->regendp = prog->endp;
++
++ sp = prog->startp;
++ ep = prog->endp;
++ for (i = NSUBEXP; i > 0; i--) {
++ *sp++ = NULL;
++ *ep++ = NULL;
++ }
++ if (regmatch(g, prog->program + 1)) {
++ prog->startp[0] = string;
++ prog->endp[0] = g->reginput;
++ return(1);
++ } else
++ return(0);
+}
+
+/*
-+ - regcomp - compile a regular expression into internal code
-+ *
-+ * We can't allocate space until we know how big the compiled form will be,
-+ * but we can't compile it (and thus know how big it is) until we've got a
-+ * place to put the code. So we cheat: we compile it twice, once with code
-+ * generation turned off and size counting turned on, and once "for real".
-+ * This also means that we don't allocate space until we are sure that the
-+ * thing really will compile successfully, and we never have to move the
-+ * code and thus invalidate pointers into it. (Note that it has to be in
-+ * one piece because free() must be able to free it all.)
++ - regmatch - main matching routine
+ *
-+ * Beware that the optimization-preparation code in here knows about some
-+ * of the structure of the compiled regexp.
++ * Conceptually the strategy is simple: check to see whether the current
++ * node matches, call self recursively to see whether the rest matches,
++ * and then act accordingly. In practice we make some effort to avoid
++ * recursion, in particular by going through "ordinary" nodes (that don't
++ * need to know whether the rest of the match failed) by a loop instead of
++ * by recursion.
+ */
-+regexp *
-+regcomp(char *exp,int *patternsize)
++static int /* 0 failure, 1 success */
++regmatch(struct match_globals *g, char *prog)
+{
-+ register regexp *r;
-+ register char *scan;
-+ register char *longest;
-+ register int len;
-+ int flags;
-+ struct match_globals g;
-+
-+ /* commented out by ethan
-+ extern char *malloc();
-+ */
++ register char *scan = prog; /* Current node. */
++ char *next; /* Next node. */
+
-+ if (exp == NULL)
-+ FAIL("NULL argument");
++#ifdef DEBUG
++ if (scan != NULL && regnarrate)
++ fprintf(stderr, "%s(\n", regprop(scan));
++#endif
++ while (scan != NULL) {
++#ifdef DEBUG
++ if (regnarrate)
++ fprintf(stderr, "%s...\n", regprop(scan));
++#endif
++ next = regnext(g, scan);
+
-+ /* First pass: determine size, legality. */
-+ g.regparse = exp;
-+ g.regnpar = 1;
-+ g.regsize = 0L;
-+ g.regcode = &g.regdummy;
-+ regc(&g, MAGIC);
-+ if (reg(&g, 0, &flags) == NULL)
-+ return(NULL);
++ switch (OP(scan)) {
++ case BOL:
++ if (g->reginput != g->regbol)
++ return(0);
++ break;
++ case EOL:
++ if (*g->reginput != '\0')
++ return(0);
++ break;
++ case ANY:
++ if (*g->reginput == '\0')
++ return(0);
++ g->reginput++;
++ break;
++ case EXACTLY: {
++ register int len;
++ register char *opnd;
+
-+ /* Small enough for pointer-storage convention? */
-+ if (g.regsize >= 32767L) /* Probably could be 65535L. */
-+ FAIL("regexp too big");
++ opnd = OPERAND(scan);
++ /* Inline the first character, for speed. */
++ if (*opnd != *g->reginput)
++ return(0);
++ len = strlen(opnd);
++ if (len > 1 && strncmp(opnd, g->reginput, len) != 0)
++ return(0);
++ g->reginput += len;
++ }
++ break;
++ case ANYOF:
++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL)
++ return(0);
++ g->reginput++;
++ break;
++ case ANYBUT:
++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL)
++ return(0);
++ g->reginput++;
++ break;
++ case NOTHING:
++ case BACK:
++ break;
++ case OPEN+1:
++ case OPEN+2:
++ case OPEN+3:
++ case OPEN+4:
++ case OPEN+5:
++ case OPEN+6:
++ case OPEN+7:
++ case OPEN+8:
++ case OPEN+9: {
++ register int no;
++ register char *save;
+
-+ /* Allocate space. */
-+ *patternsize=sizeof(regexp) + (unsigned)g.regsize;
-+ r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize);
-+ if (r == NULL)
-+ FAIL("out of space");
++ no = OP(scan) - OPEN;
++ save = g->reginput;
+
-+ /* Second pass: emit code. */
-+ g.regparse = exp;
-+ g.regnpar = 1;
-+ g.regcode = r->program;
-+ regc(&g, MAGIC);
-+ if (reg(&g, 0, &flags) == NULL)
-+ return(NULL);
++ if (regmatch(g, next)) {
++ /*
++ * Don't set startp if some later
++ * invocation of the same parentheses
++ * already has.
++ */
++ if (g->regstartp[no] == NULL)
++ g->regstartp[no] = save;
++ return(1);
++ } else
++ return(0);
++ }
++ break;
++ case CLOSE+1:
++ case CLOSE+2:
++ case CLOSE+3:
++ case CLOSE+4:
++ case CLOSE+5:
++ case CLOSE+6:
++ case CLOSE+7:
++ case CLOSE+8:
++ case CLOSE+9:
++ {
++ register int no;
++ register char *save;
+
-+ /* Dig out information for optimizations. */
-+ r->regstart = '\0'; /* Worst-case defaults. */
-+ r->reganch = 0;
-+ r->regmust = NULL;
-+ r->regmlen = 0;
-+ scan = r->program+1; /* First BRANCH. */
-+ if (OP(regnext(&g, scan)) == END) { /* Only one top-level choice. */
-+ scan = OPERAND(scan);
++ no = OP(scan) - CLOSE;
++ save = g->reginput;
+
-+ /* Starting-point info. */
-+ if (OP(scan) == EXACTLY)
-+ r->regstart = *OPERAND(scan);
-+ else if (OP(scan) == BOL)
-+ r->reganch++;
++ if (regmatch(g, next)) {
++ /*
++ * Don't set endp if some later
++ * invocation of the same parentheses
++ * already has.
++ */
++ if (g->regendp[no] == NULL)
++ g->regendp[no] = save;
++ return(1);
++ } else
++ return(0);
++ }
++ break;
++ case BRANCH: {
++ register char *save;
+
-+ /*
-+ * If there's something expensive in the r.e., find the
-+ * longest literal string that must appear and make it the
-+ * regmust. Resolve ties in favor of later strings, since
-+ * the regstart check works with the beginning of the r.e.
-+ * and avoiding duplication strengthens checking. Not a
-+ * strong reason, but sufficient in the absence of others.
-+ */
-+ if (flags&SPSTART) {
-+ longest = NULL;
-+ len = 0;
-+ for (; scan != NULL; scan = regnext(&g, scan))
-+ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
-+ longest = OPERAND(scan);
-+ len = strlen(OPERAND(scan));
++ if (OP(next) != BRANCH) /* No choice. */
++ next = OPERAND(scan); /* Avoid recursion. */
++ else {
++ do {
++ save = g->reginput;
++ if (regmatch(g, OPERAND(scan)))
++ return(1);
++ g->reginput = save;
++ scan = regnext(g, scan);
++ } while (scan != NULL && OP(scan) == BRANCH);
++ return(0);
++ /* NOTREACHED */
+ }
-+ r->regmust = longest;
-+ r->regmlen = len;
++ }
++ break;
++ case STAR:
++ case PLUS: {
++ register char nextch;
++ register int no;
++ register char *save;
++ register int min;
++
++ /*
++ * Lookahead to avoid useless match attempts
++ * when we know what character comes next.
++ */
++ nextch = '\0';
++ if (OP(next) == EXACTLY)
++ nextch = *OPERAND(next);
++ min = (OP(scan) == STAR) ? 0 : 1;
++ save = g->reginput;
++ no = regrepeat(g, OPERAND(scan));
++ while (no >= min) {
++ /* If it could work, try it. */
++ if (nextch == '\0' || *g->reginput == nextch)
++ if (regmatch(g, next))
++ return(1);
++ /* Couldn't or didn't -- back up. */
++ no--;
++ g->reginput = save + no;
++ }
++ return(0);
++ }
++ break;
++ case END:
++ return(1); /* Success! */
++ break;
++ default:
++ printk("<3>Regexp: memory corruption\n");
++ return(0);
++ break;
+ }
++
++ scan = next;
+ }
+
-+ return(r);
++ /*
++ * We get here only if there's trouble -- normally "case END" is
++ * the terminating point.
++ */
++ printk("<3>Regexp: corrupted pointers\n");
++ return(0);
+}
+
+/*
-+ - reg - regular expression, i.e. main body or parenthesized thing
-+ *
-+ * Caller must absorb opening parenthesis.
-+ *
-+ * Combining parenthesis handling with the base level of regular expression
-+ * is a trifle forced, but the need to tie the tails of the branches to what
-+ * follows makes it hard to avoid.
++ - regrepeat - repeatedly match something simple, report how many
+ */
-+static char *
-+reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ )
++static int
++regrepeat(struct match_globals *g, char *p)
+{
-+ register char *ret;
-+ register char *br;
-+ register char *ender;
-+ register int parno = 0; /* 0 makes gcc happy */
-+ int flags;
-+
-+ *flagp = HASWIDTH; /* Tentatively. */
-+
-+ /* Make an OPEN node, if parenthesized. */
-+ if (paren) {
-+ if (g->regnpar >= NSUBEXP)
-+ FAIL("too many ()");
-+ parno = g->regnpar;
-+ g->regnpar++;
-+ ret = regnode(g, OPEN+parno);
-+ } else
-+ ret = NULL;
++ register int count = 0;
++ register char *scan;
++ register char *opnd;
+
-+ /* Pick up the branches, linking them together. */
-+ br = regbranch(g, &flags);
-+ if (br == NULL)
-+ return(NULL);
-+ if (ret != NULL)
-+ regtail(g, ret, br); /* OPEN -> first. */
-+ else
-+ ret = br;
-+ if (!(flags&HASWIDTH))
-+ *flagp &= ~HASWIDTH;
-+ *flagp |= flags&SPSTART;
-+ while (*g->regparse == '|') {
-+ g->regparse++;
-+ br = regbranch(g, &flags);
-+ if (br == NULL)
-+ return(NULL);
-+ regtail(g, ret, br); /* BRANCH -> BRANCH. */
-+ if (!(flags&HASWIDTH))
-+ *flagp &= ~HASWIDTH;
-+ *flagp |= flags&SPSTART;
-+ }
-+
-+ /* Make a closing node, and hook it on the end. */
-+ ender = regnode(g, (paren) ? CLOSE+parno : END);
-+ regtail(g, ret, ender);
-+
-+ /* Hook the tails of the branches to the closing node. */
-+ for (br = ret; br != NULL; br = regnext(g, br))
-+ regoptail(g, br, ender);
-+
-+ /* Check for proper termination. */
-+ if (paren && *g->regparse++ != ')') {
-+ FAIL("unmatched ()");
-+ } else if (!paren && *g->regparse != '\0') {
-+ if (*g->regparse == ')') {
-+ FAIL("unmatched ()");
-+ } else
-+ FAIL("junk on end"); /* "Can't happen". */
-+ /* NOTREACHED */
++ scan = g->reginput;
++ opnd = OPERAND(p);
++ switch (OP(p)) {
++ case ANY:
++ count = strlen(scan);
++ scan += count;
++ break;
++ case EXACTLY:
++ while (*opnd == *scan) {
++ count++;
++ scan++;
++ }
++ break;
++ case ANYOF:
++ while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
++ count++;
++ scan++;
++ }
++ break;
++ case ANYBUT:
++ while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
++ count++;
++ scan++;
++ }
++ break;
++ default: /* Oh dear. Called inappropriately. */
++ printk("<3>Regexp: internal foulup\n");
++ count = 0; /* Best compromise. */
++ break;
+ }
++ g->reginput = scan;
+
-+ return(ret);
++ return(count);
+}
+
+/*
-+ - regbranch - one alternative of an | operator
-+ *
-+ * Implements the concatenation operator.
++ - regnext - dig the "next" pointer out of a node
+ */
-+static char *
-+regbranch(struct match_globals *g, int *flagp)
++static char*
++regnext(struct match_globals *g, char *p)
+{
-+ register char *ret;
-+ register char *chain;
-+ register char *latest;
-+ int flags;
++ register int offset;
+
-+ *flagp = WORST; /* Tentatively. */
++ if (p == &g->regdummy)
++ return(NULL);
+
-+ ret = regnode(g, BRANCH);
-+ chain = NULL;
-+ while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') {
-+ latest = regpiece(g, &flags);
-+ if (latest == NULL)
-+ return(NULL);
-+ *flagp |= flags&HASWIDTH;
-+ if (chain == NULL) /* First piece. */
-+ *flagp |= flags&SPSTART;
-+ else
-+ regtail(g, chain, latest);
-+ chain = latest;
-+ }
-+ if (chain == NULL) /* Loop ran zero times. */
-+ (void) regnode(g, NOTHING);
++ offset = NEXT(p);
++ if (offset == 0)
++ return(NULL);
+
-+ return(ret);
++ if (OP(p) == BACK)
++ return(p-offset);
++ else
++ return(p+offset);
+}
+
++#ifdef DEBUG
++
++STATIC char *regprop();
++
+/*
-+ - regpiece - something followed by possible [*+?]
-+ *
-+ * Note that the branching code sequences used for ? and the general cases
-+ * of * and + are somewhat optimized: they use the same NOTHING node as
-+ * both the endmarker for their branch list and the body of the last branch.
-+ * It might seem that this node could be dispensed with entirely, but the
-+ * endmarker role is not redundant.
++ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
-+static char *
-+regpiece(struct match_globals *g, int *flagp)
++void
++regdump(regexp *r)
+{
-+ register char *ret;
-+ register char op;
++ register char *s;
++ register char op = EXACTLY; /* Arbitrary non-END op. */
+ register char *next;
-+ int flags;
-+
-+ ret = regatom(g, &flags);
-+ if (ret == NULL)
-+ return(NULL);
-+
-+ op = *g->regparse;
-+ if (!ISMULT(op)) {
-+ *flagp = flags;
-+ return(ret);
-+ }
++ /* extern char *strchr(); */
+
-+ if (!(flags&HASWIDTH) && op != '?')
-+ FAIL("*+ operand could be empty");
-+ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
+
-+ if (op == '*' && (flags&SIMPLE))
-+ reginsert(g, STAR, ret);
-+ else if (op == '*') {
-+ /* Emit x* as (x&|), where & means "self". */
-+ reginsert(g, BRANCH, ret); /* Either x */
-+ regoptail(g, ret, regnode(g, BACK)); /* and loop */
-+ regoptail(g, ret, ret); /* back */
-+ regtail(g, ret, regnode(g, BRANCH)); /* or */
-+ regtail(g, ret, regnode(g, NOTHING)); /* null. */
-+ } else if (op == '+' && (flags&SIMPLE))
-+ reginsert(g, PLUS, ret);
-+ else if (op == '+') {
-+ /* Emit x+ as x(&|), where & means "self". */
-+ next = regnode(g, BRANCH); /* Either */
-+ regtail(g, ret, next);
-+ regtail(g, regnode(g, BACK), ret); /* loop back */
-+ regtail(g, next, regnode(g, BRANCH)); /* or */
-+ regtail(g, ret, regnode(g, NOTHING)); /* null. */
-+ } else if (op == '?') {
-+ /* Emit x? as (x|) */
-+ reginsert(g, BRANCH, ret); /* Either x */
-+ regtail(g, ret, regnode(g, BRANCH)); /* or */
-+ next = regnode(g, NOTHING); /* null. */
-+ regtail(g, ret, next);
-+ regoptail(g, ret, next);
++ s = r->program + 1;
++ while (op != END) { /* While that wasn't END last time... */
++ op = OP(s);
++ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */
++ next = regnext(s);
++ if (next == NULL) /* Next ptr. */
++ printf("(0)");
++ else
++ printf("(%d)", (s-r->program)+(next-s));
++ s += 3;
++ if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
++ /* Literal string, where present. */
++ while (*s != '\0') {
++ putchar(*s);
++ s++;
++ }
++ s++;
++ }
++ putchar('\n');
+ }
-+ g->regparse++;
-+ if (ISMULT(*g->regparse))
-+ FAIL("nested *?+");
+
-+ return(ret);
++ /* Header fields of interest. */
++ if (r->regstart != '\0')
++ printf("start `%c' ", r->regstart);
++ if (r->reganch)
++ printf("anchored ");
++ if (r->regmust != NULL)
++ printf("must have \"%s\"", r->regmust);
++ printf("\n");
+}
+
+/*
-+ - regatom - the lowest level
-+ *
-+ * Optimization: gobbles an entire sequence of ordinary characters so that
-+ * it can turn them into a single node, which is smaller to store and
-+ * faster to run. Backslashed characters are exceptions, each becoming a
-+ * separate node; the code is simpler that way and it's not worth fixing.
++ - regprop - printable representation of opcode
+ */
+static char *
-+regatom(struct match_globals *g, int *flagp)
++regprop(char *op)
+{
-+ register char *ret;
-+ int flags;
++#define BUFLEN 50
++ register char *p;
++ static char buf[BUFLEN];
+
-+ *flagp = WORST; /* Tentatively. */
++ strcpy(buf, ":");
+
-+ switch (*g->regparse++) {
-+ case '^':
-+ ret = regnode(g, BOL);
++ switch (OP(op)) {
++ case BOL:
++ p = "BOL";
+ break;
-+ case '$':
-+ ret = regnode(g, EOL);
++ case EOL:
++ p = "EOL";
+ break;
-+ case '.':
-+ ret = regnode(g, ANY);
-+ *flagp |= HASWIDTH|SIMPLE;
++ case ANY:
++ p = "ANY";
+ break;
-+ case '[': {
-+ register int class;
-+ register int classend;
-+
-+ if (*g->regparse == '^') { /* Complement of range. */
-+ ret = regnode(g, ANYBUT);
-+ g->regparse++;
-+ } else
-+ ret = regnode(g, ANYOF);
-+ if (*g->regparse == ']' || *g->regparse == '-')
-+ regc(g, *g->regparse++);
-+ while (*g->regparse != '\0' && *g->regparse != ']') {
-+ if (*g->regparse == '-') {
-+ g->regparse++;
-+ if (*g->regparse == ']' || *g->regparse == '\0')
-+ regc(g, '-');
-+ else {
-+ class = UCHARAT(g->regparse-2)+1;
-+ classend = UCHARAT(g->regparse);
-+ if (class > classend+1)
-+ FAIL("invalid [] range");
-+ for (; class <= classend; class++)
-+ regc(g, class);
-+ g->regparse++;
-+ }
-+ } else
-+ regc(g, *g->regparse++);
-+ }
-+ regc(g, '\0');
-+ if (*g->regparse != ']')
-+ FAIL("unmatched []");
-+ g->regparse++;
-+ *flagp |= HASWIDTH|SIMPLE;
-+ }
++ case ANYOF:
++ p = "ANYOF";
+ break;
-+ case '(':
-+ ret = reg(g, 1, &flags);
-+ if (ret == NULL)
-+ return(NULL);
-+ *flagp |= flags&(HASWIDTH|SPSTART);
++ case ANYBUT:
++ p = "ANYBUT";
+ break;
-+ case '\0':
-+ case '|':
-+ case ')':
-+ FAIL("internal urp"); /* Supposed to be caught earlier. */
++ case BRANCH:
++ p = "BRANCH";
+ break;
-+ case '?':
-+ case '+':
-+ case '*':
-+ FAIL("?+* follows nothing");
++ case EXACTLY:
++ p = "EXACTLY";
+ break;
-+ case '\\':
-+ if (*g->regparse == '\0')
-+ FAIL("trailing \\");
-+ ret = regnode(g, EXACTLY);
-+ regc(g, *g->regparse++);
-+ regc(g, '\0');
-+ *flagp |= HASWIDTH|SIMPLE;
++ case NOTHING:
++ p = "NOTHING";
+ break;
-+ default: {
-+ register int len;
-+ register char ender;
-+
-+ g->regparse--;
-+ len = my_strcspn((const char *)g->regparse, (const char *)META);
-+ if (len <= 0)
-+ FAIL("internal disaster");
-+ ender = *(g->regparse+len);
-+ if (len > 1 && ISMULT(ender))
-+ len--; /* Back off clear of ?+* operand. */
-+ *flagp |= HASWIDTH;
-+ if (len == 1)
-+ *flagp |= SIMPLE;
-+ ret = regnode(g, EXACTLY);
-+ while (len > 0) {
-+ regc(g, *g->regparse++);
-+ len--;
-+ }
-+ regc(g, '\0');
-+ }
++ case BACK:
++ p = "BACK";
++ break;
++ case END:
++ p = "END";
++ break;
++ case OPEN+1:
++ case OPEN+2:
++ case OPEN+3:
++ case OPEN+4:
++ case OPEN+5:
++ case OPEN+6:
++ case OPEN+7:
++ case OPEN+8:
++ case OPEN+9:
++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN);
++ p = NULL;
++ break;
++ case CLOSE+1:
++ case CLOSE+2:
++ case CLOSE+3:
++ case CLOSE+4:
++ case CLOSE+5:
++ case CLOSE+6:
++ case CLOSE+7:
++ case CLOSE+8:
++ case CLOSE+9:
++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE);
++ p = NULL;
++ break;
++ case STAR:
++ p = "STAR";
++ break;
++ case PLUS:
++ p = "PLUS";
++ break;
++ default:
++ printk("<3>Regexp: corrupted opcode\n");
+ break;
+ }
-+
-+ return(ret);
++ if (p != NULL)
++ strncat(buf, p, BUFLEN-strlen(buf));
++ return(buf);
+}
++#endif
+
++
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/regexp/regexp.h linux-4.9/net/netfilter/regexp/regexp.h
+--- linux-4.9/net/netfilter/regexp/regexp.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/net/netfilter/regexp/regexp.h 2021-02-24 15:33:21.717468479 +0100
+@@ -0,0 +1,41 @@
+/*
-+ - regnode - emit a node
++ * Definitions etc. for regexp(3) routines.
++ *
++ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
++ * not the System V one.
+ */
-+static char * /* Location. */
-+regnode(struct match_globals *g, char op)
-+{
-+ register char *ret;
-+ register char *ptr;
+
-+ ret = g->regcode;
-+ if (ret == &g->regdummy) {
-+ g->regsize += 3;
-+ return(ret);
-+ }
++#ifndef REGEXP_H
++#define REGEXP_H
+
-+ ptr = ret;
-+ *ptr++ = op;
-+ *ptr++ = '\0'; /* Null "next" pointer. */
-+ *ptr++ = '\0';
-+ g->regcode = ptr;
+
-+ return(ret);
-+}
++/*
++http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h ,
++which contains a version of this library, says:
++
++ *
++ * NSUBEXP must be at least 10, and no greater than 117 or the parser
++ * will not work properly.
++ *
++
++However, it looks rather like this library is limited to 10. If you think
++otherwise, let us know.
++*/
++
++#define NSUBEXP 10
++typedef struct regexp {
++ char *startp[NSUBEXP];
++ char *endp[NSUBEXP];
++ char regstart; /* Internal use only. */
++ char reganch; /* Internal use only. */
++ char *regmust; /* Internal use only. */
++ int regmlen; /* Internal use only. */
++ char program[1]; /* Unwarranted chumminess with compiler. */
++} regexp;
++
++regexp * regcomp(char *exp, int *patternsize);
++int regexec(regexp *prog, char *string);
++void regsub(regexp *prog, char *source, char *dest);
++void regerror(char *s);
+
++#endif
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/regexp/regmagic.h linux-4.9/net/netfilter/regexp/regmagic.h
+--- linux-4.9/net/netfilter/regexp/regmagic.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/net/netfilter/regexp/regmagic.h 2021-02-24 15:33:21.717468479 +0100
+@@ -0,0 +1,5 @@
+/*
-+ - regc - emit (if appropriate) a byte of code
++ * The first byte of the regexp internal "program" is actually this magic
++ * number; the start node begins in the second byte.
+ */
-+static void
-+regc(struct match_globals *g, char b)
-+{
-+ if (g->regcode != &g->regdummy)
-+ *g->regcode++ = b;
-+ else
-+ g->regsize++;
-+}
-+
++#define MAGIC 0234
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/regexp/regsub.c linux-4.9/net/netfilter/regexp/regsub.c
+--- linux-4.9/net/netfilter/regexp/regsub.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/net/netfilter/regexp/regsub.c 2021-02-24 15:33:21.717468479 +0100
+@@ -0,0 +1,95 @@
+/*
-+ - reginsert - insert an operator in front of already-emitted operand
++ * regsub
++ * @(#)regsub.c 1.3 of 2 April 86
++ *
++ * Copyright (c) 1986 by University of Toronto.
++ * Written by Henry Spencer. Not derived from licensed software.
++ *
++ * Permission is granted to anyone to use this software for any
++ * purpose on any computer system, and to redistribute it freely,
++ * subject to the following restrictions:
++ *
++ * 1. The author is not responsible for the consequences of use of
++ * this software, no matter how awful, even if they arise
++ * from defects in it.
++ *
++ * 2. The origin of this software must not be misrepresented, either
++ * by explicit claim or by omission.
++ *
++ * 3. Altered versions must be plainly marked as such, and must not
++ * be misrepresented as being the original software.
++ *
++ *
++ * This code was modified by Ethan Sommer to work within the kernel
++ * (it now uses kmalloc etc..)
+ *
-+ * Means relocating the operand.
+ */
-+static void
-+reginsert(struct match_globals *g, char op, char* opnd)
-+{
-+ register char *src;
-+ register char *dst;
-+ register char *place;
++#include "regexp.h"
++#include "regmagic.h"
++#include <linux/string.h>
+
-+ if (g->regcode == &g->regdummy) {
-+ g->regsize += 3;
-+ return;
-+ }
+
-+ src = g->regcode;
-+ g->regcode += 3;
-+ dst = g->regcode;
-+ while (src > opnd)
-+ *--dst = *--src;
++#ifndef CHARBITS
++#define UCHARAT(p) ((int)*(unsigned char *)(p))
++#else
++#define UCHARAT(p) ((int)*(p)&CHARBITS)
++#endif
+
-+ place = opnd; /* Op node, where operand used to be. */
-+ *place++ = op;
-+ *place++ = '\0';
-+ *place++ = '\0';
-+}
++#if 0
++//void regerror(char * s)
++//{
++// printk("regexp(3): %s", s);
++// /* NOTREACHED */
++//}
++#endif
+
+/*
-+ - regtail - set the next-pointer at the end of a node chain
++ - regsub - perform substitutions after a regexp match
+ */
-+static void
-+regtail(struct match_globals *g, char *p, char *val)
++void
++regsub(regexp * prog, char * source, char * dest)
+{
-+ register char *scan;
-+ register char *temp;
-+ register int offset;
++ register char *src;
++ register char *dst;
++ register char c;
++ register int no;
++ register int len;
++
++ /* Not necessary and gcc doesn't like it -MLS */
++ /*extern char *strncpy();*/
+
-+ if (p == &g->regdummy)
++ if (prog == NULL || source == NULL || dest == NULL) {
++ regerror("NULL parm to regsub");
++ return;
++ }
++ if (UCHARAT(prog->program) != MAGIC) {
++ regerror("damaged regexp fed to regsub");
+ return;
-+
-+ /* Find last node. */
-+ scan = p;
-+ for (;;) {
-+ temp = regnext(g, scan);
-+ if (temp == NULL)
-+ break;
-+ scan = temp;
+ }
+
-+ if (OP(scan) == BACK)
-+ offset = scan - val;
-+ else
-+ offset = val - scan;
-+ *(scan+1) = (offset>>8)&0377;
-+ *(scan+2) = offset&0377;
-+}
++ src = source;
++ dst = dest;
++ while ((c = *src++) != '\0') {
++ if (c == '&')
++ no = 0;
++ else if (c == '\\' && '0' <= *src && *src <= '9')
++ no = *src++ - '0';
++ else
++ no = -1;
+
-+/*
-+ - regoptail - regtail on operand of first argument; nop if operandless
-+ */
-+static void
-+regoptail(struct match_globals *g, char *p, char *val)
-+{
-+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */
-+ if (p == NULL || p == &g->regdummy || OP(p) != BRANCH)
-+ return;
-+ regtail(g, OPERAND(p), val);
++ if (no < 0) { /* Ordinary character. */
++ if (c == '\\' && (*src == '\\' || *src == '&'))
++ c = *src++;
++ *dst++ = c;
++ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) {
++ len = prog->endp[no] - prog->startp[no];
++ (void) strncpy(dst, prog->startp[no], len);
++ dst += len;
++ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */
++ regerror("damaged match string");
++ return;
++ }
++ }
++ }
++ *dst++ = '\0';
+}
-+
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/xt_layer7.c linux-4.9/net/netfilter/xt_layer7.c
+--- linux-4.9/net/netfilter/xt_layer7.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/net/netfilter/xt_layer7.c 2021-02-24 15:33:21.714135041 +0100
+@@ -0,0 +1,656 @@
+/*
-+ * regexec and friends
-+ */
++ Kernel module to match application layer (OSI layer 7) data in connections.
+
++ http://l7-filter.sf.net
+
-+/*
-+ * Forwards.
-+ */
-+STATIC int regtry(struct match_globals *g, regexp *prog, char *string);
-+STATIC int regmatch(struct match_globals *g, char *prog);
-+STATIC int regrepeat(struct match_globals *g, char *p);
++ (C) 2003-2009 Matthew Strait and Ethan Sommer.
+
-+#ifdef DEBUG
-+int regnarrate = 0;
-+void regdump();
-+STATIC char *regprop(char *op);
++ 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.
++ http://www.gnu.org/licenses/gpl.txt
++
++ Based on ipt_string.c (C) 2000 Emmanuel Roger <winfield@freegates.be>,
++ xt_helper.c (C) 2002 Harald Welte and cls_layer7.c (C) 2003 Matthew Strait,
++ Ethan Sommer, Justin Levandoski.
++*/
++
++#include <linux/spinlock.h>
++#include <linux/version.h>
++#include <net/ip.h>
++#include <net/tcp.h>
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter.h>
++#include <net/netfilter/nf_conntrack.h>
++#include <net/netfilter/nf_conntrack_core.h>
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
++#include <net/netfilter/nf_conntrack_extend.h>
++#include <net/netfilter/nf_conntrack_acct.h>
+#endif
++#include <linux/netfilter/x_tables.h>
++#include <linux/netfilter/xt_layer7.h>
++#include <linux/ctype.h>
++#include <linux/proc_fs.h>
+
-+/*
-+ - regexec - match a regexp against a string
-+ */
-+int
-+regexec(regexp *prog, char *string)
-+{
-+ register char *s;
-+ struct match_globals g;
++#include "regexp/regexp.c"
+
-+ /* Be paranoid... */
-+ if (prog == NULL || string == NULL) {
-+ printk("<3>Regexp: NULL parameter\n");
-+ return(0);
-+ }
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>");
++MODULE_DESCRIPTION("iptables application layer match module");
++MODULE_ALIAS("ipt_layer7");
++MODULE_VERSION("2.21");
+
-+ /* Check validity of program. */
-+ if (UCHARAT(prog->program) != MAGIC) {
-+ printk("<3>Regexp: corrupted program\n");
-+ return(0);
-+ }
++static int maxdatalen = 2048; // this is the default
++module_param(maxdatalen, int, 0444);
++MODULE_PARM_DESC(maxdatalen, "maximum bytes of data looked at by l7-filter");
++#ifdef CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG
++ #define DPRINTK(format,args...) printk(format,##args)
++#else
++ #define DPRINTK(format,args...)
++#endif
+
-+ /* If there is a "must appear" string, look for it. */
-+ if (prog->regmust != NULL) {
-+ s = string;
-+ while ((s = strchr(s, prog->regmust[0])) != NULL) {
-+ if (strncmp(s, prog->regmust, prog->regmlen) == 0)
-+ break; /* Found it. */
-+ s++;
-+ }
-+ if (s == NULL) /* Not present. */
-+ return(0);
-+ }
++/* Number of packets whose data we look at.
++This can be modified through /proc/net/layer7_numpackets */
++static int num_packets = 10;
+
-+ /* Mark beginning of line for ^ . */
-+ g.regbol = string;
++static struct pattern_cache {
++ char * regex_string;
++ regexp * pattern;
++ struct pattern_cache * next;
++} * first_pattern_cache = NULL;
+
-+ /* Simplest case: anchored match need be tried only once. */
-+ if (prog->reganch)
-+ return(regtry(&g, prog, string));
++DEFINE_SPINLOCK(l7_lock);
+
-+ /* Messy cases: unanchored match. */
-+ s = string;
-+ if (prog->regstart != '\0')
-+ /* We know what char it must start with. */
-+ while ((s = strchr(s, prog->regstart)) != NULL) {
-+ if (regtry(&g, prog, s))
-+ return(1);
-+ s++;
-+ }
-+ else
-+ /* We don't -- general case. */
-+ do {
-+ if (regtry(&g, prog, s))
-+ return(1);
-+ } while (*s++ != '\0');
++static int total_acct_packets(struct nf_conn *ct)
++{
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26)
++ BUG_ON(ct == NULL);
++ return (ct->counters[IP_CT_DIR_ORIGINAL].packets + ct->counters[IP_CT_DIR_REPLY].packets);
++#else
++ struct nf_conn_counter *acct;
+
-+ /* Failure. */
-+ return(0);
++ BUG_ON(ct == NULL);
++ acct = nf_conn_acct_find(ct);
++ if (!acct)
++ return 0;
++ return (atomic64_read(&acct[IP_CT_DIR_ORIGINAL].packets) + atomic64_read(&acct[IP_CT_DIR_REPLY].packets));
++#endif
+}
+
-+/*
-+ - regtry - try match at specific point
-+ */
-+static int /* 0 failure, 1 success */
-+regtry(struct match_globals *g, regexp *prog, char *string)
++#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG
++/* Converts an unfriendly string into a friendly one by
++replacing unprintables with periods and all whitespace with " ". */
++static char * friendly_print(unsigned char * s)
+{
-+ register int i;
-+ register char **sp;
-+ register char **ep;
++ char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC);
++ int i;
+
-+ g->reginput = string;
-+ g->regstartp = prog->startp;
-+ g->regendp = prog->endp;
++ if(!f) {
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: out of memory in "
++ "friendly_print, bailing.\n");
++ return NULL;
++ }
+
-+ sp = prog->startp;
-+ ep = prog->endp;
-+ for (i = NSUBEXP; i > 0; i--) {
-+ *sp++ = NULL;
-+ *ep++ = NULL;
++ for(i = 0; i < strlen(s); i++){
++ if(isprint(s[i]) && s[i] < 128) f[i] = s[i];
++ else if(isspace(s[i])) f[i] = ' ';
++ else f[i] = '.';
+ }
-+ if (regmatch(g, prog->program + 1)) {
-+ prog->startp[0] = string;
-+ prog->endp[0] = g->reginput;
-+ return(1);
-+ } else
-+ return(0);
++ f[i] = '\0';
++ return f;
+}
+
-+/*
-+ - regmatch - main matching routine
-+ *
-+ * Conceptually the strategy is simple: check to see whether the current
-+ * node matches, call self recursively to see whether the rest matches,
-+ * and then act accordingly. In practice we make some effort to avoid
-+ * recursion, in particular by going through "ordinary" nodes (that don't
-+ * need to know whether the rest of the match failed) by a loop instead of
-+ * by recursion.
-+ */
-+static int /* 0 failure, 1 success */
-+regmatch(struct match_globals *g, char *prog)
++static char dec2hex(int i)
+{
-+ register char *scan = prog; /* Current node. */
-+ char *next; /* Next node. */
-+
-+#ifdef DEBUG
-+ if (scan != NULL && regnarrate)
-+ fprintf(stderr, "%s(\n", regprop(scan));
-+#endif
-+ while (scan != NULL) {
-+#ifdef DEBUG
-+ if (regnarrate)
-+ fprintf(stderr, "%s...\n", regprop(scan));
-+#endif
-+ next = regnext(g, scan);
-+
-+ switch (OP(scan)) {
-+ case BOL:
-+ if (g->reginput != g->regbol)
-+ return(0);
-+ break;
-+ case EOL:
-+ if (*g->reginput != '\0')
-+ return(0);
++ switch (i) {
++ case 0 ... 9:
++ return (i + '0');
+ break;
-+ case ANY:
-+ if (*g->reginput == '\0')
-+ return(0);
-+ g->reginput++;
++ case 10 ... 15:
++ return (i - 10 + 'a');
+ break;
-+ case EXACTLY: {
-+ register int len;
-+ register char *opnd;
++ default:
++ if (net_ratelimit())
++ printk("layer7: Problem in dec2hex\n");
++ return '\0';
++ }
++}
+
-+ opnd = OPERAND(scan);
-+ /* Inline the first character, for speed. */
-+ if (*opnd != *g->reginput)
-+ return(0);
-+ len = strlen(opnd);
-+ if (len > 1 && strncmp(opnd, g->reginput, len) != 0)
-+ return(0);
-+ g->reginput += len;
-+ }
-+ break;
-+ case ANYOF:
-+ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL)
-+ return(0);
-+ g->reginput++;
-+ break;
-+ case ANYBUT:
-+ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL)
-+ return(0);
-+ g->reginput++;
-+ break;
-+ case NOTHING:
-+ case BACK:
-+ break;
-+ case OPEN+1:
-+ case OPEN+2:
-+ case OPEN+3:
-+ case OPEN+4:
-+ case OPEN+5:
-+ case OPEN+6:
-+ case OPEN+7:
-+ case OPEN+8:
-+ case OPEN+9: {
-+ register int no;
-+ register char *save;
++static char * hex_print(unsigned char * s)
++{
++ char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC);
++ int i;
+
-+ no = OP(scan) - OPEN;
-+ save = g->reginput;
++ if(!g) {
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: out of memory in hex_print, "
++ "bailing.\n");
++ return NULL;
++ }
+
-+ if (regmatch(g, next)) {
-+ /*
-+ * Don't set startp if some later
-+ * invocation of the same parentheses
-+ * already has.
-+ */
-+ if (g->regstartp[no] == NULL)
-+ g->regstartp[no] = save;
-+ return(1);
-+ } else
-+ return(0);
-+ }
-+ break;
-+ case CLOSE+1:
-+ case CLOSE+2:
-+ case CLOSE+3:
-+ case CLOSE+4:
-+ case CLOSE+5:
-+ case CLOSE+6:
-+ case CLOSE+7:
-+ case CLOSE+8:
-+ case CLOSE+9:
-+ {
-+ register int no;
-+ register char *save;
++ for(i = 0; i < strlen(s); i++) {
++ g[i*3 ] = dec2hex(s[i]/16);
++ g[i*3 + 1] = dec2hex(s[i]%16);
++ g[i*3 + 2] = ' ';
++ }
++ g[i*3] = '\0';
+
-+ no = OP(scan) - CLOSE;
-+ save = g->reginput;
++ return g;
++}
++#endif // DEBUG
+
-+ if (regmatch(g, next)) {
-+ /*
-+ * Don't set endp if some later
-+ * invocation of the same parentheses
-+ * already has.
-+ */
-+ if (g->regendp[no] == NULL)
-+ g->regendp[no] = save;
-+ return(1);
-+ } else
-+ return(0);
-+ }
-+ break;
-+ case BRANCH: {
-+ register char *save;
++/* Use instead of regcomp. As we expect to be seeing the same regexps over and
++over again, it make sense to cache the results. */
++static regexp * compile_and_cache(const char * regex_string,
++ const char * protocol)
++{
++ struct pattern_cache * node = first_pattern_cache;
++ struct pattern_cache * last_pattern_cache = first_pattern_cache;
++ struct pattern_cache * tmp;
++ unsigned int len;
+
-+ if (OP(next) != BRANCH) /* No choice. */
-+ next = OPERAND(scan); /* Avoid recursion. */
-+ else {
-+ do {
-+ save = g->reginput;
-+ if (regmatch(g, OPERAND(scan)))
-+ return(1);
-+ g->reginput = save;
-+ scan = regnext(g, scan);
-+ } while (scan != NULL && OP(scan) == BRANCH);
-+ return(0);
-+ /* NOTREACHED */
-+ }
-+ }
-+ break;
-+ case STAR:
-+ case PLUS: {
-+ register char nextch;
-+ register int no;
-+ register char *save;
-+ register int min;
++ while (node != NULL) {
++ if (!strcmp(node->regex_string, regex_string))
++ return node->pattern;
+
-+ /*
-+ * Lookahead to avoid useless match attempts
-+ * when we know what character comes next.
-+ */
-+ nextch = '\0';
-+ if (OP(next) == EXACTLY)
-+ nextch = *OPERAND(next);
-+ min = (OP(scan) == STAR) ? 0 : 1;
-+ save = g->reginput;
-+ no = regrepeat(g, OPERAND(scan));
-+ while (no >= min) {
-+ /* If it could work, try it. */
-+ if (nextch == '\0' || *g->reginput == nextch)
-+ if (regmatch(g, next))
-+ return(1);
-+ /* Couldn't or didn't -- back up. */
-+ no--;
-+ g->reginput = save + no;
-+ }
-+ return(0);
-+ }
-+ break;
-+ case END:
-+ return(1); /* Success! */
-+ break;
-+ default:
-+ printk("<3>Regexp: memory corruption\n");
-+ return(0);
-+ break;
-+ }
++ last_pattern_cache = node;/* points at the last non-NULL node */
++ node = node->next;
++ }
++
++ /* If we reach the end of the list, then we have not yet cached
++ the pattern for this regex. Let's do that now.
++ Be paranoid about running out of memory to avoid list corruption. */
++ tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC);
++
++ if(!tmp) {
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: out of memory in "
++ "compile_and_cache, bailing.\n");
++ return NULL;
++ }
++
++ tmp->regex_string = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC);
++ tmp->pattern = kmalloc(sizeof(struct regexp), GFP_ATOMIC);
++ tmp->next = NULL;
+
-+ scan = next;
++ if(!tmp->regex_string || !tmp->pattern) {
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: out of memory in "
++ "compile_and_cache, bailing.\n");
++ kfree(tmp->regex_string);
++ kfree(tmp->pattern);
++ kfree(tmp);
++ return NULL;
+ }
+
-+ /*
-+ * We get here only if there's trouble -- normally "case END" is
-+ * the terminating point.
-+ */
-+ printk("<3>Regexp: corrupted pointers\n");
-+ return(0);
++ /* Ok. The new node is all ready now. */
++ node = tmp;
++
++ if(first_pattern_cache == NULL) /* list is empty */
++ first_pattern_cache = node; /* make node the beginning */
++ else
++ last_pattern_cache->next = node; /* attach node to the end */
++
++ /* copy the string and compile the regex */
++ len = strlen(regex_string);
++ DPRINTK("About to compile this: \"%s\"\n", regex_string);
++ node->pattern = regcomp((char *)regex_string, &len);
++ if ( !node->pattern ) {
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: Error compiling regexp "
++ "\"%s\" (%s)\n",
++ regex_string, protocol);
++ /* pattern is now cached as NULL, so we won't try again. */
++ }
++
++ strcpy(node->regex_string, regex_string);
++ return node->pattern;
+}
+
-+/*
-+ - regrepeat - repeatedly match something simple, report how many
-+ */
-+static int
-+regrepeat(struct match_globals *g, char *p)
++static int can_handle(const struct sk_buff *skb)
+{
-+ register int count = 0;
-+ register char *scan;
-+ register char *opnd;
++ if(!ip_hdr(skb)) /* not IP */
++ return 0;
++ if(ip_hdr(skb)->protocol != IPPROTO_TCP &&
++ ip_hdr(skb)->protocol != IPPROTO_UDP &&
++ ip_hdr(skb)->protocol != IPPROTO_ICMP)
++ return 0;
++ return 1;
++}
+
-+ scan = g->reginput;
-+ opnd = OPERAND(p);
-+ switch (OP(p)) {
-+ case ANY:
-+ count = strlen(scan);
-+ scan += count;
-+ break;
-+ case EXACTLY:
-+ while (*opnd == *scan) {
-+ count++;
-+ scan++;
++/* Returns offset the into the skb->data that the application data starts */
++static int app_data_offset(const struct sk_buff *skb)
++{
++ /* In case we are ported somewhere (ebtables?) where ip_hdr(skb)
++ isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */
++ int ip_hl = 4*ip_hdr(skb)->ihl;
++
++ if( ip_hdr(skb)->protocol == IPPROTO_TCP ) {
++ /* 12 == offset into TCP header for the header length field.
++ Can't get this with skb->h.th->doff because the tcphdr
++ struct doesn't get set when routing (this is confirmed to be
++ true in Netfilter as well as QoS.) */
++ int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4);
++
++ return ip_hl + tcp_hl;
++ } else if( ip_hdr(skb)->protocol == IPPROTO_UDP ) {
++ return ip_hl + 8; /* UDP header is always 8 bytes */
++ } else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) {
++ return ip_hl + 8; /* ICMP header is 8 bytes */
++ } else {
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: tried to handle unknown "
++ "protocol!\n");
++ return ip_hl + 8; /* something reasonable */
++ }
++}
++
++/* handles whether there's a match when we aren't appending data anymore */
++static int match_no_append(struct nf_conn * conntrack,
++ struct nf_conn * master_conntrack,
++ enum ip_conntrack_info ctinfo,
++ enum ip_conntrack_info master_ctinfo,
++ const struct xt_layer7_info * info)
++{
++ /* If we're in here, throw the app data away */
++ if(master_conntrack->layer7.app_data != NULL) {
++
++ #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG
++ if(!master_conntrack->layer7.app_proto) {
++ char * f =
++ friendly_print(master_conntrack->layer7.app_data);
++ char * g =
++ hex_print(master_conntrack->layer7.app_data);
++ DPRINTK("\nl7-filter gave up after %d bytes "
++ "(%d packets):\n%s\n",
++ strlen(f), total_acct_packets(master_conntrack), f);
++ kfree(f);
++ DPRINTK("In hex: %s\n", g);
++ kfree(g);
+ }
-+ break;
-+ case ANYOF:
-+ while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
-+ count++;
-+ scan++;
++ #endif
++
++ kfree(master_conntrack->layer7.app_data);
++ master_conntrack->layer7.app_data = NULL; /* don't free again */
++ }
++
++ if(master_conntrack->layer7.app_proto){
++ /* Here child connections set their .app_proto (for /proc) */
++ if(!conntrack->layer7.app_proto) {
++ conntrack->layer7.app_proto =
++ kmalloc(strlen(master_conntrack->layer7.app_proto)+1,
++ GFP_ATOMIC);
++ if(!conntrack->layer7.app_proto){
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: out of memory "
++ "in match_no_append, "
++ "bailing.\n");
++ return 1;
++ }
++ strcpy(conntrack->layer7.app_proto,
++ master_conntrack->layer7.app_proto);
+ }
-+ break;
-+ case ANYBUT:
-+ while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
-+ count++;
-+ scan++;
++
++ return (!strcmp(master_conntrack->layer7.app_proto,
++ info->protocol));
++ }
++ else {
++ /* If not classified, set to "unknown" to distinguish from
++ connections that are still being tested. */
++ master_conntrack->layer7.app_proto =
++ kmalloc(strlen("unknown")+1, GFP_ATOMIC);
++ if(!master_conntrack->layer7.app_proto){
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: out of memory in "
++ "match_no_append, bailing.\n");
++ return 1;
+ }
-+ break;
-+ default: /* Oh dear. Called inappropriately. */
-+ printk("<3>Regexp: internal foulup\n");
-+ count = 0; /* Best compromise. */
-+ break;
++ strcpy(master_conntrack->layer7.app_proto, "unknown");
++ return 0;
+ }
-+ g->reginput = scan;
-+
-+ return(count);
+}
+
-+/*
-+ - regnext - dig the "next" pointer out of a node
-+ */
-+static char*
-+regnext(struct match_globals *g, char *p)
++/* add the new app data to the conntrack. Return number of bytes added. */
++static int add_data(struct nf_conn * master_conntrack,
++ char * app_data, int appdatalen)
+{
-+ register int offset;
-+
-+ if (p == &g->regdummy)
-+ return(NULL);
++ int length = 0, i;
++ int oldlength = master_conntrack->layer7.app_data_len;
+
-+ offset = NEXT(p);
-+ if (offset == 0)
-+ return(NULL);
++ /* This is a fix for a race condition by Deti Fliegl. However, I'm not
++ clear on whether the race condition exists or whether this really
++ fixes it. I might just be being dense... Anyway, if it's not really
++ a fix, all it does is waste a very small amount of time. */
++ if(!master_conntrack->layer7.app_data) return 0;
+
-+ if (OP(p) == BACK)
-+ return(p-offset);
-+ else
-+ return(p+offset);
-+}
++ /* Strip nulls. Make everything lower case (our regex lib doesn't
++ do case insensitivity). Add it to the end of the current data. */
++ for(i = 0; i < maxdatalen-oldlength-1 &&
++ i < appdatalen; i++) {
++ if(app_data[i] != '\0') {
++ /* the kernel version of tolower mungs 'upper ascii' */
++ master_conntrack->layer7.app_data[length+oldlength] =
++ isascii(app_data[i])?
++ tolower(app_data[i]) : app_data[i];
++ length++;
++ }
++ }
+
-+#ifdef DEBUG
++ master_conntrack->layer7.app_data[length+oldlength] = '\0';
++ master_conntrack->layer7.app_data_len = length + oldlength;
+
-+STATIC char *regprop();
++ return length;
++}
+
-+/*
-+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
-+ */
-+void
-+regdump(regexp *r)
++/* taken from drivers/video/modedb.c */
++static int my_atoi(const char *s)
+{
-+ register char *s;
-+ register char op = EXACTLY; /* Arbitrary non-END op. */
-+ register char *next;
-+ /* extern char *strchr(); */
-+
++ int val = 0;
+
-+ s = r->program + 1;
-+ while (op != END) { /* While that wasn't END last time... */
-+ op = OP(s);
-+ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */
-+ next = regnext(s);
-+ if (next == NULL) /* Next ptr. */
-+ printf("(0)");
-+ else
-+ printf("(%d)", (s-r->program)+(next-s));
-+ s += 3;
-+ if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
-+ /* Literal string, where present. */
-+ while (*s != '\0') {
-+ putchar(*s);
-+ s++;
-+ }
-+ s++;
++ for (;; s++) {
++ switch (*s) {
++ case '0'...'9':
++ val = 10*val+(*s-'0');
++ break;
++ default:
++ return val;
+ }
-+ putchar('\n');
+ }
++}
++
++/* write out num_packets to userland. */
++static int layer7_read_proc(char* page, char ** start, off_t off, int count,
++ int* eof, void * data)
++{
++ if(num_packets > 99 && net_ratelimit())
++ printk(KERN_ERR "layer7: NOT REACHED. num_packets too big\n");
+
-+ /* Header fields of interest. */
-+ if (r->regstart != '\0')
-+ printf("start `%c' ", r->regstart);
-+ if (r->reganch)
-+ printf("anchored ");
-+ if (r->regmust != NULL)
-+ printf("must have \"%s\"", r->regmust);
-+ printf("\n");
++ page[0] = num_packets/10 + '0';
++ page[1] = num_packets%10 + '0';
++ page[2] = '\n';
++ page[3] = '\0';
++
++ *eof=1;
++
++ return 3;
+}
+
-+/*
-+ - regprop - printable representation of opcode
-+ */
-+static char *
-+regprop(char *op)
++/* Read in num_packets from userland */
++static int layer7_write_proc(struct file* file, const char* buffer,
++ unsigned long count, void *data)
+{
-+#define BUFLEN 50
-+ register char *p;
-+ static char buf[BUFLEN];
++ char * foo = kmalloc(count, GFP_ATOMIC);
+
-+ strcpy(buf, ":");
++ if(!foo){
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: out of memory, bailing. "
++ "num_packets unchanged.\n");
++ return count;
++ }
+
-+ switch (OP(op)) {
-+ case BOL:
-+ p = "BOL";
-+ break;
-+ case EOL:
-+ p = "EOL";
-+ break;
-+ case ANY:
-+ p = "ANY";
-+ break;
-+ case ANYOF:
-+ p = "ANYOF";
-+ break;
-+ case ANYBUT:
-+ p = "ANYBUT";
-+ break;
-+ case BRANCH:
-+ p = "BRANCH";
-+ break;
-+ case EXACTLY:
-+ p = "EXACTLY";
-+ break;
-+ case NOTHING:
-+ p = "NOTHING";
-+ break;
-+ case BACK:
-+ p = "BACK";
-+ break;
-+ case END:
-+ p = "END";
-+ break;
-+ case OPEN+1:
-+ case OPEN+2:
-+ case OPEN+3:
-+ case OPEN+4:
-+ case OPEN+5:
-+ case OPEN+6:
-+ case OPEN+7:
-+ case OPEN+8:
-+ case OPEN+9:
-+ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN);
-+ p = NULL;
-+ break;
-+ case CLOSE+1:
-+ case CLOSE+2:
-+ case CLOSE+3:
-+ case CLOSE+4:
-+ case CLOSE+5:
-+ case CLOSE+6:
-+ case CLOSE+7:
-+ case CLOSE+8:
-+ case CLOSE+9:
-+ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE);
-+ p = NULL;
-+ break;
-+ case STAR:
-+ p = "STAR";
-+ break;
-+ case PLUS:
-+ p = "PLUS";
-+ break;
-+ default:
-+ printk("<3>Regexp: corrupted opcode\n");
-+ break;
++ if(copy_from_user(foo, buffer, count)) {
++ return -EFAULT;
+ }
-+ if (p != NULL)
-+ strncat(buf, p, BUFLEN-strlen(buf));
-+ return(buf);
++
++
++ num_packets = my_atoi(foo);
++ kfree (foo);
++
++ /* This has an arbitrary limit to make the math easier. I'm lazy.
++ But anyway, 99 is a LOT! If you want more, you're doing it wrong! */
++ if(num_packets > 99) {
++ printk(KERN_WARNING "layer7: num_packets can't be > 99.\n");
++ num_packets = 99;
++ } else if(num_packets < 1) {
++ printk(KERN_WARNING "layer7: num_packets can't be < 1.\n");
++ num_packets = 1;
++ }
++
++ return count;
+}
++
++static bool
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
++match(const struct sk_buff *skbin, struct xt_action_param *par)
++#else
++match(const struct sk_buff *skbin,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct xt_match *match,
++ const void *matchinfo,
++ int offset,
++ unsigned int protoff,
++ bool *hotdrop)
+#endif
++{
++ /* sidestep const without getting a compiler warning... */
++ struct sk_buff * skb = (struct sk_buff *)skbin;
+
++ const struct xt_layer7_info * info =
++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
++ par->matchinfo;
++ #else
++ matchinfo;
++ #endif
+
---- linux-2.6.28-stock/net/netfilter/regexp/regexp.h 1969-12-31 18:00:00.000000000 -0600
-+++ linux-2.6.28/net/netfilter/regexp/regexp.h 2009-01-07 16:07:31.000000000 -0600
-@@ -0,0 +1,41 @@
-+/*
-+ * Definitions etc. for regexp(3) routines.
-+ *
-+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
-+ * not the System V one.
-+ */
++ enum ip_conntrack_info master_ctinfo, ctinfo;
++ struct nf_conn *master_conntrack, *conntrack;
++ unsigned char * app_data;
++ unsigned int pattern_result, appdatalen;
++ regexp * comppattern;
+
-+#ifndef REGEXP_H
-+#define REGEXP_H
++ /* Be paranoid/incompetent - lock the entire match function. */
++ spin_lock_bh(&l7_lock);
+
++ if(!can_handle(skb)){
++ DPRINTK("layer7: This is some protocol I can't handle.\n");
++ spin_unlock_bh(&l7_lock);
++ return info->invert;
++ }
+
-+/*
-+http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h ,
-+which contains a version of this library, says:
++ /* Treat parent & all its children together as one connection, except
++ for the purpose of setting conntrack->layer7.app_proto in the actual
++ connection. This makes /proc/net/ip_conntrack more satisfying. */
++ if(!(conntrack = nf_ct_get(skb, &ctinfo)) ||
++ !(master_conntrack=nf_ct_get(skb,&master_ctinfo))){
++ DPRINTK("layer7: couldn't get conntrack.\n");
++ spin_unlock_bh(&l7_lock);
++ return info->invert;
++ }
+
-+ *
-+ * NSUBEXP must be at least 10, and no greater than 117 or the parser
-+ * will not work properly.
-+ *
++ /* Try to get a master conntrack (and its master etc) for FTP, etc. */
++ while (master_ct(master_conntrack) != NULL)
++ master_conntrack = master_ct(master_conntrack);
+
-+However, it looks rather like this library is limited to 10. If you think
-+otherwise, let us know.
-+*/
++ /* if we've classified it or seen too many packets */
++ if(total_acct_packets(master_conntrack) > num_packets ||
++ master_conntrack->layer7.app_proto) {
+
-+#define NSUBEXP 10
-+typedef struct regexp {
-+ char *startp[NSUBEXP];
-+ char *endp[NSUBEXP];
-+ char regstart; /* Internal use only. */
-+ char reganch; /* Internal use only. */
-+ char *regmust; /* Internal use only. */
-+ int regmlen; /* Internal use only. */
-+ char program[1]; /* Unwarranted chumminess with compiler. */
-+} regexp;
++ pattern_result = match_no_append(conntrack, master_conntrack,
++ ctinfo, master_ctinfo, info);
+
-+regexp * regcomp(char *exp, int *patternsize);
-+int regexec(regexp *prog, char *string);
-+void regsub(regexp *prog, char *source, char *dest);
-+void regerror(char *s);
++ /* skb->cb[0] == seen. Don't do things twice if there are
++ multiple l7 rules. I'm not sure that using cb for this purpose
++ is correct, even though it says "put your private variables
++ there". But it doesn't look like it is being used for anything
++ else in the skbs that make it here. */
++ skb->cb[0] = 1; /* marking it seen here's probably irrelevant */
+
-+#endif
---- linux-2.6.28-stock/net/netfilter/regexp/regmagic.h 1969-12-31 18:00:00.000000000 -0600
-+++ linux-2.6.28/net/netfilter/regexp/regmagic.h 2009-01-07 16:07:31.000000000 -0600
-@@ -0,0 +1,5 @@
-+/*
-+ * The first byte of the regexp internal "program" is actually this magic
-+ * number; the start node begins in the second byte.
-+ */
-+#define MAGIC 0234
---- linux-2.6.28-stock/net/netfilter/regexp/regsub.c 1969-12-31 18:00:00.000000000 -0600
-+++ linux-2.6.28/net/netfilter/regexp/regsub.c 2009-01-07 16:07:31.000000000 -0600
-@@ -0,0 +1,95 @@
-+/*
-+ * regsub
-+ * @(#)regsub.c 1.3 of 2 April 86
-+ *
-+ * Copyright (c) 1986 by University of Toronto.
-+ * Written by Henry Spencer. Not derived from licensed software.
-+ *
-+ * Permission is granted to anyone to use this software for any
-+ * purpose on any computer system, and to redistribute it freely,
-+ * subject to the following restrictions:
-+ *
-+ * 1. The author is not responsible for the consequences of use of
-+ * this software, no matter how awful, even if they arise
-+ * from defects in it.
-+ *
-+ * 2. The origin of this software must not be misrepresented, either
-+ * by explicit claim or by omission.
-+ *
-+ * 3. Altered versions must be plainly marked as such, and must not
-+ * be misrepresented as being the original software.
-+ *
-+ *
-+ * This code was modified by Ethan Sommer to work within the kernel
-+ * (it now uses kmalloc etc..)
-+ *
-+ */
-+#include "regexp.h"
-+#include "regmagic.h"
-+#include <linux/string.h>
++ spin_unlock_bh(&l7_lock);
++ return (pattern_result ^ info->invert);
++ }
+
++ if(skb_is_nonlinear(skb)){
++ if(skb_linearize(skb) != 0){
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: failed to linearize "
++ "packet, bailing.\n");
++ spin_unlock_bh(&l7_lock);
++ return info->invert;
++ }
++ }
+
-+#ifndef CHARBITS
-+#define UCHARAT(p) ((int)*(unsigned char *)(p))
-+#else
-+#define UCHARAT(p) ((int)*(p)&CHARBITS)
-+#endif
++ /* now that the skb is linearized, it's safe to set these. */
++ app_data = skb->data + app_data_offset(skb);
++ appdatalen = skb_tail_pointer(skb) - app_data;
+
-+#if 0
-+//void regerror(char * s)
-+//{
-+// printk("regexp(3): %s", s);
-+// /* NOTREACHED */
-+//}
-+#endif
++ /* the return value gets checked later, when we're ready to use it */
++ comppattern = compile_and_cache(info->pattern, info->protocol);
+
-+/*
-+ - regsub - perform substitutions after a regexp match
-+ */
-+void
-+regsub(regexp * prog, char * source, char * dest)
-+{
-+ register char *src;
-+ register char *dst;
-+ register char c;
-+ register int no;
-+ register int len;
-+
-+ /* Not necessary and gcc doesn't like it -MLS */
-+ /*extern char *strncpy();*/
++ /* On the first packet of a connection, allocate space for app data */
++ if(total_acct_packets(master_conntrack) == 1 && !skb->cb[0] &&
++ !master_conntrack->layer7.app_data){
++ master_conntrack->layer7.app_data =
++ kmalloc(maxdatalen, GFP_ATOMIC);
++ if(!master_conntrack->layer7.app_data){
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: out of memory in "
++ "match, bailing.\n");
++ spin_unlock_bh(&l7_lock);
++ return info->invert;
++ }
+
-+ if (prog == NULL || source == NULL || dest == NULL) {
-+ regerror("NULL parm to regsub");
-+ return;
++ master_conntrack->layer7.app_data[0] = '\0';
+ }
-+ if (UCHARAT(prog->program) != MAGIC) {
-+ regerror("damaged regexp fed to regsub");
-+ return;
++
++ /* Can be here, but unallocated, if numpackets is increased near
++ the beginning of a connection */
++ if(master_conntrack->layer7.app_data == NULL){
++ spin_unlock_bh(&l7_lock);
++ return info->invert; /* unmatched */
+ }
+
-+ src = source;
-+ dst = dest;
-+ while ((c = *src++) != '\0') {
-+ if (c == '&')
-+ no = 0;
-+ else if (c == '\\' && '0' <= *src && *src <= '9')
-+ no = *src++ - '0';
-+ else
-+ no = -1;
++ if(!skb->cb[0]){
++ int newbytes;
++ newbytes = add_data(master_conntrack, app_data, appdatalen);
+
-+ if (no < 0) { /* Ordinary character. */
-+ if (c == '\\' && (*src == '\\' || *src == '&'))
-+ c = *src++;
-+ *dst++ = c;
-+ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) {
-+ len = prog->endp[no] - prog->startp[no];
-+ (void) strncpy(dst, prog->startp[no], len);
-+ dst += len;
-+ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */
-+ regerror("damaged match string");
-+ return;
-+ }
++ if(newbytes == 0) { /* didn't add any data */
++ skb->cb[0] = 1;
++ /* Didn't match before, not going to match now */
++ spin_unlock_bh(&l7_lock);
++ return info->invert;
+ }
+ }
-+ *dst++ = '\0';
++
++ /* If looking for "unknown", then never match. "Unknown" means that
++ we've given up; we're still trying with these packets. */
++ if(!strcmp(info->protocol, "unknown")) {
++ pattern_result = 0;
++ /* If looking for "unset", then always match. "Unset" means that we
++ haven't yet classified the connection. */
++ } else if(!strcmp(info->protocol, "unset")) {
++ pattern_result = 2;
++ DPRINTK("layer7: matched unset: not yet classified "
++ "(%d/%d packets)\n",
++ total_acct_packets(master_conntrack), num_packets);
++ /* If the regexp failed to compile, don't bother running it */
++ } else if(comppattern &&
++ regexec(comppattern, master_conntrack->layer7.app_data)){
++ DPRINTK("layer7: matched %s\n", info->protocol);
++ pattern_result = 1;
++ } else pattern_result = 0;
++
++ if(pattern_result == 1) {
++ master_conntrack->layer7.app_proto =
++ kmalloc(strlen(info->protocol)+1, GFP_ATOMIC);
++ if(!master_conntrack->layer7.app_proto){
++ if (net_ratelimit())
++ printk(KERN_ERR "layer7: out of memory in "
++ "match, bailing.\n");
++ spin_unlock_bh(&l7_lock);
++ return (pattern_result ^ info->invert);
++ }
++ strcpy(master_conntrack->layer7.app_proto, info->protocol);
++ } else if(pattern_result > 1) { /* cleanup from "unset" */
++ pattern_result = 1;
++ }
++
++ /* mark the packet seen */
++ skb->cb[0] = 1;
++
++ spin_unlock_bh(&l7_lock);
++ return (pattern_result ^ info->invert);
+}
---- linux-2.6.28-stock/net/netfilter/nf_conntrack_core.c 2009-01-07 16:05:35.000000000 -0600
-+++ linux-2.6.28/net/netfilter/nf_conntrack_core.c 2009-01-07 16:07:31.000000000 -0600
-@@ -201,6 +201,14 @@ destroy_conntrack(struct nf_conntrack *n
- * too. */
- nf_ct_remove_expectations(ct);
-
-+ #if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE)
-+ if(ct->layer7.app_proto)
-+ kfree(ct->layer7.app_proto);
-+ if(ct->layer7.app_data)
-+ kfree(ct->layer7.app_data);
-+ #endif
+
++// load nf_conntrack_ipv4
++static int check(const struct xt_mtchk_param *par)
++{
++ if (nf_ct_l3proto_try_module_get(par->family) < 0) {
++ pr_info("can't load conntrack support for "
++ "proto=%d\n", par->family);
++ return -EINVAL;
++ }
++ return 0;
++}
+
- /* We overload first tuple to link into unconfirmed or dying list.*/
- BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode));
- hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
---- linux-2.6.28-stock/net/netfilter/nf_conntrack_standalone.c 2009-01-07 16:05:35.000000000 -0600
-+++ linux-2.6.28/net/netfilter/nf_conntrack_standalone.c 2009-01-07 16:07:31.000000000 -0600
-@@ -165,6 +165,12 @@ static int ct_seq_show(struct seq_file *
-
- ct_show_delta_time(s, ct);
-
-+#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE)
-+ if(ct->layer7.app_proto &&
-+ seq_printf(s, "l7proto=%s ", ct->layer7.app_proto))
-+ return -ENOSPC;
-+#endif
+
- seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use));
-
- if (seq_has_overflowed(s))
---- linux-2.6.28-stock/include/net/netfilter/nf_conntrack.h 2009-01-07 16:05:30.000000000 -0600
-+++ linux-2.6.28/include/net/netfilter/nf_conntrack.h 2009-01-07 16:07:31.000000000 -0600
-@@ -120,6 +120,22 @@ struct nf_conn {
- /* Extensions */
- struct nf_ct_ext *ext;
-
-+#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || \
-+ defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE)
-+ struct {
-+ /*
-+ * e.g. "http". NULL before decision. "unknown" after decision
-+ * if no match.
-+ */
-+ char *app_proto;
-+ /*
-+ * application layer data so far. NULL after match decision.
-+ */
-+ char *app_data;
-+ unsigned int app_data_len;
-+ } layer7;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
++ static void destroy(const struct xt_mtdtor_param *par)
++ {
++ nf_ct_l3proto_module_put(par->family);
++ }
++#else
++ static void destroy(const struct xt_match *match, void *matchinfo)
++ {
++ nf_ct_l3proto_module_put(match->family);
++ }
+#endif
+
- /* Storage reserved for other modules, must be the last member */
- union nf_conntrack_proto proto;
- };
---- linux-2.6.28-stock/include/linux/netfilter/xt_layer7.h 1969-12-31 18:00:00.000000000 -0600
-+++ linux-2.6.28/include/linux/netfilter/xt_layer7.h 2009-01-07 16:07:31.000000000 -0600
-@@ -0,0 +1,13 @@
-+#ifndef _XT_LAYER7_H
-+#define _XT_LAYER7_H
++static struct xt_match xt_layer7_match[] __read_mostly = {
++{
++ .name = "layer7",
++ .family = AF_INET,
++ .checkentry = check,
++ .match = match,
++ .destroy = destroy,
++ .matchsize = sizeof(struct xt_layer7_info),
++ .me = THIS_MODULE
++}
++};
+
-+#define MAX_PATTERN_LEN 8192
-+#define MAX_PROTOCOL_LEN 256
++static void layer7_cleanup_proc(void)
++{
++ remove_proc_entry("layer7_numpackets", init_net.proc_net);
++}
+
-+struct xt_layer7_info {
-+ char protocol[MAX_PROTOCOL_LEN];
-+ char pattern[MAX_PATTERN_LEN];
-+ u_int8_t invert;
-+};
++/* register the proc file */
++static void layer7_init_proc(void)
++{
++ struct proc_dir_entry* entry;
++ entry = create_proc_entry("layer7_numpackets", 0644, init_net.proc_net);
++ entry->read_proc = layer7_read_proc;
++ entry->write_proc = layer7_write_proc;
++}
+
-+#endif /* _XT_LAYER7_H */
++static int __init xt_layer7_init(void)
++{
++ need_conntrack();
++
++ layer7_init_proc();
++ if(maxdatalen < 1) {
++ printk(KERN_WARNING "layer7: maxdatalen can't be < 1, "
++ "using 1\n");
++ maxdatalen = 1;
++ }
++ /* This is not a hard limit. It's just here to prevent people from
++ bringing their slow machines to a grinding halt. */
++ else if(maxdatalen > 65536) {
++ printk(KERN_WARNING "layer7: maxdatalen can't be > 65536, "
++ "using 65536\n");
++ maxdatalen = 65536;
++ }
++ return xt_register_matches(xt_layer7_match,
++ ARRAY_SIZE(xt_layer7_match));
++}
++
++static void __exit xt_layer7_fini(void)
++{
++ layer7_cleanup_proc();
++ xt_unregister_matches(xt_layer7_match, ARRAY_SIZE(xt_layer7_match));
++}
++
++module_init(xt_layer7_init);
++module_exit(xt_layer7_fini);
-From: Matthew Garrett <mjg59@coreos.com>
-Date: Sat, 18 Apr 2015 08:26:34 -0700
-Subject: [PATCH 1/3] libata: Stash initial power management configuration
-
-The initial configuration for power management settings may be a result of
-the firmware using its knowledge of the installed hardware to program
-reasonable defaults. Stash as much of it as possible for later use.
-
-Signed-off-by: Matthew Garrett <mjg59@srcf.ucam.org>
----
- drivers/ata/acard-ahci.c | 3 +++
- drivers/ata/ahci.c | 3 +++
- drivers/ata/ahci.h | 7 +++++++
- drivers/ata/libahci.c | 46 +++++++++++++++++++++++++++++++++++-------
- drivers/ata/libahci_platform.c | 4 ++++
- drivers/ata/libata-core.c | 19 ++++++++++-------
- drivers/ata/libata-pmp.c | 2 +-
- drivers/ata/libata.h | 2 +-
- drivers/ata/sata_highbank.c | 4 ++++
- include/linux/libata.h | 3 +++
- 10 files changed, 77 insertions(+), 16 deletions(-)
-
-diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c
-index 12489ce..0029229 100644
---- a/drivers/ata/acard-ahci.c
-+++ b/drivers/ata/acard-ahci.c
-@@ -476,6 +476,9 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id
+diff -urNp -x '*.orig' linux-4.9/Documentation/scsi/link_power_management_policy.txt linux-4.9/Documentation/scsi/link_power_management_policy.txt
+--- linux-4.9/Documentation/scsi/link_power_management_policy.txt 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/Documentation/scsi/link_power_management_policy.txt 2021-02-24 16:30:09.260516752 +0100
+@@ -1,8 +1,11 @@
+ This parameter allows the user to set the link (interface) power management.
+-There are 3 possible options:
++There are 4 possible options:
+
+ Value Effect
+ ----------------------------------------------------------------------------
++firmware_defaults Inherit configuration from the state programmed by
++ the firmware during system init.
++
+ min_power Tell the controller to try to make the link use the
+ least possible power when possible. This may
+ sacrifice some performance due to increased latency
+diff -urNp -x '*.orig' linux-4.9/drivers/ata/acard-ahci.c linux-4.9/drivers/ata/acard-ahci.c
+--- linux-4.9/drivers/ata/acard-ahci.c 2021-02-24 16:29:54.606714867 +0100
++++ linux-4.9/drivers/ata/acard-ahci.c 2021-02-24 16:30:09.257183312 +0100
+@@ -480,6 +480,9 @@ static int acard_ahci_init_one(struct pc
ata_port_pbar_desc(ap, AHCI_PCI_BAR,
0x100 + ap->port_no * 0x80, "port");
/* set initial link pm policy */
/*
ap->pm_policy = NOT_AVAILABLE;
-diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
-index c7a92a7..0f875e2 100644
---- a/drivers/ata/ahci.c
-+++ b/drivers/ata/ahci.c
-@@ -1436,6 +1436,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+diff -urNp -x '*.orig' linux-4.9/drivers/ata/ahci.c linux-4.9/drivers/ata/ahci.c
+--- linux-4.9/drivers/ata/ahci.c 2021-02-24 16:29:54.606714867 +0100
++++ linux-4.9/drivers/ata/ahci.c 2021-02-24 16:30:09.257183312 +0100
+@@ -1743,6 +1743,9 @@ static int ahci_init_one(struct pci_dev
if (ap->flags & ATA_FLAG_EM)
ap->em_message_type = hpriv->em_msg_type;
/* disabled/not-implemented port */
if (!(hpriv->port_map & (1 << i)))
-diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
-index 71262e0..c1a4b6a 100644
---- a/drivers/ata/ahci.h
-+++ b/drivers/ata/ahci.h
-@@ -313,6 +313,12 @@ struct ahci_port_priv {
+diff -urNp -x '*.orig' linux-4.9/drivers/ata/ahci.h linux-4.9/drivers/ata/ahci.h
+--- linux-4.9/drivers/ata/ahci.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/drivers/ata/ahci.h 2021-02-24 16:30:09.257183312 +0100
+@@ -322,6 +322,12 @@ struct ahci_port_priv {
/* enclosure management info per PM slot */
struct ahci_em_priv em_priv[EM_MAX_SLOTS];
char *irq_desc; /* desc in /proc/interrupts */
};
struct ahci_host_priv {
-@@ -371,6 +377,7 @@ extern struct ata_port_operations ahci_platform_ops;
+@@ -387,6 +393,7 @@ extern struct ata_port_operations ahci_p
extern struct ata_port_operations ahci_pmp_retry_srst_ops;
unsigned int ahci_dev_classify(struct ata_port *ap);
void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
u32 opts);
void ahci_save_initial_config(struct device *dev,
-diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
-index 61a9c07..f0c7120 100644
---- a/drivers/ata/libahci.c
-+++ b/drivers/ata/libahci.c
-@@ -2212,19 +2212,53 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
+diff -urNp -x '*.orig' linux-4.9/drivers/ata/libahci.c linux-4.9/drivers/ata/libahci.c
+--- linux-4.9/drivers/ata/libahci.c 2021-02-24 16:29:54.606714867 +0100
++++ linux-4.9/drivers/ata/libahci.c 2021-02-24 16:30:09.263850192 +0100
+@@ -743,6 +743,7 @@ static int ahci_set_lpm(struct ata_link
+ {
+ struct ata_port *ap = link->ap;
+ struct ahci_host_priv *hpriv = ap->host->private_data;
++ struct ahci_port_priv *ppriv = ap->private_data;
+ struct ahci_port_priv *pp = ap->private_data;
+ void __iomem *port_mmio = ahci_port_base(ap);
+
+@@ -763,10 +764,10 @@ static int ahci_set_lpm(struct ata_link
+
+ if (hpriv->cap & HOST_CAP_ALPM) {
+ u32 cmd = readl(port_mmio + PORT_CMD);
++ if (!(hints & ATA_LPM_WAKE_ONLY))
++ cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
+
+ if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) {
+- if (!(hints & ATA_LPM_WAKE_ONLY))
+- cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
+ cmd |= PORT_CMD_ICC_ACTIVE;
+
+ writel(cmd, port_mmio + PORT_CMD);
+@@ -777,6 +778,13 @@ static int ahci_set_lpm(struct ata_link
+
+ if (hints & ATA_LPM_WAKE_ONLY)
+ return 0;
++ } else if (policy == ATA_LPM_FIRMWARE_DEFAULTS) {
++ if (ppriv->init_alpe)
++ cmd |= PORT_CMD_ALPE;
++ if (ppriv->init_asp)
++ cmd |= PORT_CMD_ASP;
++
++ writel(cmd, port_mmio + PORT_CMD);
+ } else {
+ cmd |= PORT_CMD_ALPE;
+ if (policy == ATA_LPM_MIN_POWER)
+@@ -791,10 +799,18 @@ static int ahci_set_lpm(struct ata_link
+ if ((hpriv->cap2 & HOST_CAP2_SDS) &&
+ (hpriv->cap2 & HOST_CAP2_SADM) &&
+ (link->device->flags & ATA_DFLAG_DEVSLP)) {
+- if (policy == ATA_LPM_MIN_POWER)
++ switch (policy) {
++ case ATA_LPM_MIN_POWER:
+ ahci_set_aggressive_devslp(ap, true);
+- else
++ break;
++ case ATA_LPM_FIRMWARE_DEFAULTS:
++ case ATA_LPM_MED_POWER:
++ ahci_set_aggressive_devslp(ap, ppriv->init_devslp);
++ break;
++ default:
+ ahci_set_aggressive_devslp(ap, false);
++ break;
++ }
+ }
+
+ if (policy == ATA_LPM_MAX_POWER) {
+@@ -2076,6 +2092,7 @@ static void ahci_post_internal_cmd(struc
+ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
+ {
+ struct ahci_host_priv *hpriv = ap->host->private_data;
++ struct ahci_port_priv *ppriv = ap->private_data;
+ void __iomem *port_mmio = ahci_port_base(ap);
+ struct ata_device *dev = ap->link.device;
+ u32 devslp, dm, dito, mdat, deto;
+@@ -2111,26 +2128,32 @@ static void ahci_set_aggressive_devslp(s
+ if (rc)
+ return;
+
+- dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET;
+- dito = devslp_idle_timeout / (dm + 1);
+- if (dito > 0x3ff)
+- dito = 0x3ff;
+-
+- /* Use the nominal value 10 ms if the read MDAT is zero,
+- * the nominal value of DETO is 20 ms.
+- */
+- if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] &
+- ATA_LOG_DEVSLP_VALID_MASK) {
+- mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] &
+- ATA_LOG_DEVSLP_MDAT_MASK;
+- if (!mdat)
++ if (ppriv->init_devslp) {
++ dito = ppriv->init_dito;
++ deto = ppriv->init_deto;
++ mdat = ppriv->init_mdat;
++ } else {
++ dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET;
++ dito = devslp_idle_timeout / (dm + 1);
++ if (dito > 0x3ff)
++ dito = 0x3ff;
++
++ /* Use the nominal value 10 ms if the read MDAT is zero,
++ * the nominal value of DETO is 20 ms.
++ */
++ if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] &
++ ATA_LOG_DEVSLP_VALID_MASK) {
++ mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] &
++ ATA_LOG_DEVSLP_MDAT_MASK;
++ if (!mdat)
++ mdat = 10;
++ deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO];
++ if (!deto)
++ deto = 20;
++ } else {
+ mdat = 10;
+- deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO];
+- if (!deto)
+ deto = 20;
+- } else {
+- mdat = 10;
+- deto = 20;
++ }
+ }
+
+ /* Make dito, mdat, deto bits to 0s */
+@@ -2298,19 +2321,53 @@ static int ahci_port_suspend(struct ata_
}
#endif
if (ap->host->n_ports > 1) {
pp->irq_desc = devm_kzalloc(dev, 8, GFP_KERNEL);
if (!pp->irq_desc) {
-@@ -2303,8 +2337,6 @@ static int ahci_port_start(struct ata_port *ap)
+@@ -2389,8 +2446,6 @@ static int ahci_port_start(struct ata_po
ap->lock = &pp->lock;
}
/* engage engines, captain */
return ahci_port_resume(ap);
}
-diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
-index d89305d..39946d4 100644
---- a/drivers/ata/libahci_platform.c
-+++ b/drivers/ata/libahci_platform.c
-@@ -563,6 +563,10 @@ int ahci_platform_init_host(struct platform_device *pdev,
+diff -urNp -x '*.orig' linux-4.9/drivers/ata/libahci_platform.c linux-4.9/drivers/ata/libahci_platform.c
+--- linux-4.9/drivers/ata/libahci_platform.c 2021-02-24 16:29:54.606714867 +0100
++++ linux-4.9/drivers/ata/libahci_platform.c 2021-02-24 16:30:09.257183312 +0100
+@@ -571,6 +571,10 @@ int ahci_platform_init_host(struct platf
if (ap->flags & ATA_FLAG_EM)
ap->em_message_type = hpriv->em_msg_type;
/* disabled/not-implemented port */
if (!(hpriv->port_map & (1 << i)))
ap->ops = &ata_dummy_port_ops;
-diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
-index f6cb1f1..c037f33 100644
---- a/drivers/ata/libata-core.c
-+++ b/drivers/ata/libata-core.c
-@@ -2024,6 +2024,9 @@ retry:
+diff -urNp -x '*.orig' linux-4.9/drivers/ata/libata-core.c linux-4.9/drivers/ata/libata-core.c
+--- linux-4.9/drivers/ata/libata-core.c 2021-02-24 16:29:54.610048307 +0100
++++ linux-4.9/drivers/ata/libata-core.c 2021-02-24 16:30:09.263850192 +0100
+@@ -2028,6 +2028,9 @@ retry:
}
}
-+ if (id[79] & SATA_DIPM)
++ if (id[79] & (1 << SATA_DIPM))
+ dev->init_dipm = true;
+
*p_class = class;
return 0;
-@@ -5583,11 +5586,11 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp)
+@@ -3835,6 +3838,11 @@ int sata_link_scr_lpm(struct ata_link *l
+ return rc;
+
+ switch (policy) {
++ case ATA_LPM_FIRMWARE_DEFAULTS:
++ /* use the values we read at probe */
++ scontrol &= ~(0x7 << 8);
++ scontrol |= (link->init_lpm << 8);
++ break;
+ case ATA_LPM_MAX_POWER:
+ /* disable all LPM transitions */
+ scontrol |= (0x7 << 8);
+@@ -3845,10 +3853,6 @@ int sata_link_scr_lpm(struct ata_link *l
+ }
+ break;
+ case ATA_LPM_MED_POWER:
+- /* allow LPM to PARTIAL */
+- scontrol &= ~(0x1 << 8);
+- scontrol |= (0x6 << 8);
+- break;
+ case ATA_LPM_MIN_POWER:
+ if (ata_link_nr_enabled(link) > 0)
+ /* no restrictions on LPM transitions */
+@@ -5814,11 +5818,11 @@ void ata_link_init(struct ata_port *ap,
}
/**
*
* LOCKING:
* Kernel thread context (may sleep).
-@@ -5595,7 +5598,7 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp)
+@@ -5826,7 +5830,7 @@ void ata_link_init(struct ata_port *ap,
* RETURNS:
* 0 on success, -errno on failure.
*/
{
u8 spd;
int rc;
-@@ -5612,6 +5615,8 @@ int sata_link_init_spd(struct ata_link *link)
+@@ -5843,6 +5847,8 @@ int sata_link_init_spd(struct ata_link *
link->sata_spd_limit = link->hw_sata_spd_limit;
return 0;
}
-@@ -6161,9 +6166,9 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
+@@ -6392,9 +6398,9 @@ int ata_host_register(struct ata_host *h
ap->cbl = ATA_CBL_SATA;
/* init sata_spd_limit to the current value */
/* print per-port info to dmesg */
xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask,
-diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c
-index 7ccc084..b9f7ce8 100644
---- a/drivers/ata/libata-pmp.c
-+++ b/drivers/ata/libata-pmp.c
-@@ -531,7 +531,7 @@ int sata_pmp_attach(struct ata_device *dev)
+diff -urNp -x '*.orig' linux-4.9/drivers/ata/libata-eh.c linux-4.9/drivers/ata/libata-eh.c
+--- linux-4.9/drivers/ata/libata-eh.c 2021-02-24 16:29:54.610048307 +0100
++++ linux-4.9/drivers/ata/libata-eh.c 2021-02-24 16:30:09.267183632 +0100
+@@ -3534,9 +3534,7 @@ static int ata_eh_set_lpm(struct ata_lin
+ return 0;
+
+ /*
+- * DIPM is enabled only for MIN_POWER as some devices
+- * misbehave when the host NACKs transition to SLUMBER. Order
+- * device and link configurations such that the host always
++ * Order device and link configurations such that the host always
+ * allows DIPM requests.
+ */
+ ata_for_each_dev(dev, link, ENABLED) {
+@@ -3555,7 +3553,7 @@ static int ata_eh_set_lpm(struct ata_lin
+ hints &= ~ATA_LPM_HIPM;
+
+ /* disable DIPM before changing link config */
+- if (policy != ATA_LPM_MIN_POWER && dipm) {
++ if (policy < ATA_LPM_MED_POWER && dipm) {
+ err_mask = ata_dev_set_feature(dev,
+ SETFEATURES_SATA_DISABLE, SATA_DIPM);
+ if (err_mask && err_mask != AC_ERR_DEV) {
+@@ -3596,10 +3594,13 @@ static int ata_eh_set_lpm(struct ata_lin
+ if (ap && ap->slave_link)
+ ap->slave_link->lpm_policy = policy;
+
+- /* host config updated, enable DIPM if transitioning to MIN_POWER */
++ /* host config updated, enable DIPM if transitioning to MED_POWER,
++ * MIN_POWER or FIRMWARE_DEFAULT when enabled by firmware
++ */
+ ata_for_each_dev(dev, link, ENABLED) {
+- if (policy == ATA_LPM_MIN_POWER && !no_dipm &&
+- ata_id_has_dipm(dev->id)) {
++ if ((policy >= ATA_LPM_MED_POWER && !no_dipm &&
++ ata_id_has_dipm(dev->id)) ||
++ (policy == ATA_LPM_FIRMWARE_DEFAULTS && dev->init_dipm)) {
+ err_mask = ata_dev_set_feature(dev,
+ SETFEATURES_SATA_ENABLE, SATA_DIPM);
+ if (err_mask && err_mask != AC_ERR_DEV) {
+diff -urNp -x '*.orig' linux-4.9/drivers/ata/libata-pmp.c linux-4.9/drivers/ata/libata-pmp.c
+--- linux-4.9/drivers/ata/libata-pmp.c 2021-02-24 16:29:54.610048307 +0100
++++ linux-4.9/drivers/ata/libata-pmp.c 2021-02-24 16:30:09.260516752 +0100
+@@ -538,7 +538,7 @@ int sata_pmp_attach(struct ata_device *d
ap->ops->pmp_attach(ap);
ata_for_each_link(tlink, ap, EDGE)
return 0;
-diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
-index a998a17..35016d6 100644
---- a/drivers/ata/libata.h
-+++ b/drivers/ata/libata.h
-@@ -99,7 +99,7 @@ extern bool ata_phys_link_online(struct ata_link *link);
+diff -urNp -x '*.orig' linux-4.9/drivers/ata/libata-scsi.c linux-4.9/drivers/ata/libata-scsi.c
+--- linux-4.9/drivers/ata/libata-scsi.c 2021-02-24 16:29:54.610048307 +0100
++++ linux-4.9/drivers/ata/libata-scsi.c 2021-02-24 16:30:09.263850192 +0100
+@@ -107,6 +107,7 @@ static const u8 def_control_mpage[CONTRO
+ static const char *ata_lpm_policy_names[] = {
+ [ATA_LPM_UNKNOWN] = "max_performance",
+ [ATA_LPM_MAX_POWER] = "max_performance",
++ [ATA_LPM_FIRMWARE_DEFAULTS] = "firmware_defaults",
+ [ATA_LPM_MED_POWER] = "medium_power",
+ [ATA_LPM_MIN_POWER] = "min_power",
+ };
+diff -urNp -x '*.orig' linux-4.9/drivers/ata/libata.h linux-4.9/drivers/ata/libata.h
+--- linux-4.9/drivers/ata/libata.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/drivers/ata/libata.h 2021-02-24 16:30:09.260516752 +0100
+@@ -99,7 +99,7 @@ extern bool ata_phys_link_online(struct
extern bool ata_phys_link_offline(struct ata_link *link);
extern void ata_dev_init(struct ata_device *dev);
extern void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp);
extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg);
extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
extern struct ata_port *ata_port_alloc(struct ata_host *host);
-diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c
-index 24e311f..a2adf3f 100644
---- a/drivers/ata/sata_highbank.c
-+++ b/drivers/ata/sata_highbank.c
-@@ -556,6 +556,10 @@ static int ahci_highbank_probe(struct platform_device *pdev)
+diff -urNp -x '*.orig' linux-4.9/drivers/ata/sata_highbank.c linux-4.9/drivers/ata/sata_highbank.c
+--- linux-4.9/drivers/ata/sata_highbank.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/drivers/ata/sata_highbank.c 2021-02-24 16:30:09.260516752 +0100
+@@ -557,6 +557,10 @@ static int ahci_highbank_probe(struct pl
if (ap->flags & ATA_FLAG_EM)
ap->em_message_type = hpriv->em_msg_type;
/* disabled/not-implemented port */
if (!(hpriv->port_map & (1 << i)))
ap->ops = &ata_dummy_port_ops;
-diff --git a/include/linux/libata.h b/include/linux/libata.h
-index 8dad4a3..31c149b 100644
---- a/include/linux/libata.h
-+++ b/include/linux/libata.h
-@@ -719,6 +719,8 @@ struct ata_device {
+diff -urNp -x '*.orig' linux-4.9/include/linux/libata.h linux-4.9/include/linux/libata.h
+--- linux-4.9/include/linux/libata.h 2021-02-24 16:29:56.030093715 +0100
++++ linux-4.9/include/linux/libata.h 2021-02-24 16:30:09.263850192 +0100
+@@ -520,6 +520,7 @@ enum ata_completion_errors {
+ enum ata_lpm_policy {
+ ATA_LPM_UNKNOWN,
+ ATA_LPM_MAX_POWER,
++ ATA_LPM_FIRMWARE_DEFAULTS,
+ ATA_LPM_MED_POWER,
+ ATA_LPM_MIN_POWER,
+ };
+@@ -740,6 +741,8 @@ struct ata_device {
int spdn_cnt;
/* ering is CLEAR_END, read comment above CLEAR_END */
struct ata_ering ering;
};
/* Fields between ATA_DEVICE_CLEAR_BEGIN and ATA_DEVICE_CLEAR_END are
-@@ -800,6 +800,7 @@ struct ata_link {
+@@ -811,6 +814,7 @@ struct ata_link {
struct ata_device device[ATA_MAX_DEVICES];
unsigned long last_lpm_change; /* when last LPM change happened */
};
#define ATA_LINK_CLEAR_BEGIN offsetof(struct ata_link, active_tag)
#define ATA_LINK_CLEAR_END offsetof(struct ata_link, device[0])
---
-2.3.5
-
---
-To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
-the body of a message to majordomo@vger.kernel.org
-More majordomo info at http://vger.kernel.org/majordomo-info.html
-Please read the FAQ at http://www.tux.org/lkml/
-From: Matthew Garrett <mjg59@coreos.com>
-Date: Sat, 18 Apr 2015 08:26:35 -0700
-Subject: [PATCH 2/3] libata: Add firmware_default LPM policy
-
-System vendors may configure SATA link power management appropriately for
-their systems in firmware, based on their knowledge of the hardware
-installed. Add an additional LPM policy designed to inherit the
-configuration provided by the firmware.
-
-Signed-off-by: Matthew Garrett <mjg59@coreos.com>
----
- .../scsi/link_power_management_policy.txt | 5 +-
- drivers/ata/libahci.c | 62 +++++++++++++++-------
- drivers/ata/libata-core.c | 7 ++-
- drivers/ata/libata-eh.c | 15 +++---
- drivers/ata/libata-scsi.c | 1 +
- include/linux/libata.h | 1 +
- 6 files changed, 63 insertions(+), 28 deletions(-)
-
-diff --git a/Documentation/scsi/link_power_management_policy.txt b/Documentation/scsi/link_power_management_policy.txt
-index d18993d..0285601 100644
---- a/Documentation/scsi/link_power_management_policy.txt
-+++ b/Documentation/scsi/link_power_management_policy.txt
-@@ -1,8 +1,11 @@
- This parameter allows the user to set the link (interface) power management.
--There are 3 possible options:
-+There are 4 possible options:
-
- Value Effect
- ----------------------------------------------------------------------------
-+firmware_defaults Inherit configuration from the state programmed by
-+ the firmware during system init.
-+
- min_power Tell the controller to try to make the link use the
- least possible power when possible. This may
- sacrifice some performance due to increased latency
-diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
-index f0c7120..fabcff4 100644
---- a/drivers/ata/libahci.c
-+++ b/drivers/ata/libahci.c
-@@ -684,6 +684,7 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- {
- struct ata_port *ap = link->ap;
- struct ahci_host_priv *hpriv = ap->host->private_data;
-+ struct ahci_port_priv *ppriv = ap->private_data;
- struct ahci_port_priv *pp = ap->private_data;
- void __iomem *port_mmio = ahci_port_base(ap);
-
-@@ -701,10 +702,10 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
-
- if (hpriv->cap & HOST_CAP_ALPM) {
- u32 cmd = readl(port_mmio + PORT_CMD);
-+ if (!(hints & ATA_LPM_WAKE_ONLY))
-+ cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
-
- if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) {
-- if (!(hints & ATA_LPM_WAKE_ONLY))
-- cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
- cmd |= PORT_CMD_ICC_ACTIVE;
-
- writel(cmd, port_mmio + PORT_CMD);
-@@ -711,6 +712,13 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
-
- if (hints & ATA_LPM_WAKE_ONLY)
- return 0;
-+ } else if (policy == ATA_LPM_FIRMWARE_DEFAULTS) {
-+ if (ppriv->init_alpe)
-+ cmd |= PORT_CMD_ALPE;
-+ if (ppriv->init_asp)
-+ cmd |= PORT_CMD_ASP;
-+
-+ writel(cmd, port_mmio + PORT_CMD);
- } else {
- cmd |= PORT_CMD_ALPE;
- if (policy == ATA_LPM_MIN_POWER)
-@@ -725,10 +733,17 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- if ((hpriv->cap2 & HOST_CAP2_SDS) &&
- (hpriv->cap2 & HOST_CAP2_SADM) &&
- (link->device->flags & ATA_DFLAG_DEVSLP)) {
-- if (policy == ATA_LPM_MIN_POWER)
-+ switch (policy) {
-+ case ATA_LPM_MIN_POWER:
- ahci_set_aggressive_devslp(ap, true);
-- else
-+ break;
-+ case ATA_LPM_FIRMWARE_DEFAULTS:
-+ ahci_set_aggressive_devslp(ap, ppriv->init_devslp);
-+ break;
-+ default:
- ahci_set_aggressive_devslp(ap, false);
-+ break;
-+ }
- }
-
- if (policy == ATA_LPM_MAX_POWER) {
-@@ -1995,6 +2010,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
- static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
- {
- struct ahci_host_priv *hpriv = ap->host->private_data;
-+ struct ahci_port_priv *ppriv = ap->private_data;
- void __iomem *port_mmio = ahci_port_base(ap);
- struct ata_device *dev = ap->link.device;
- u32 devslp, dm, dito, mdat, deto;
-@@ -2030,26 +2046,32 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
- if (rc)
- return;
-
-- dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET;
-- dito = devslp_idle_timeout / (dm + 1);
-- if (dito > 0x3ff)
-- dito = 0x3ff;
-+ if (ppriv->init_devslp) {
-+ dito = ppriv->init_dito;
-+ deto = ppriv->init_deto;
-+ mdat = ppriv->init_mdat;
-+ } else {
-+ dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET;
-+ dito = devslp_idle_timeout / (dm + 1);
-+ if (dito > 0x3ff)
-+ dito = 0x3ff;
-
-- /* Use the nominal value 10 ms if the read MDAT is zero,
-- * the nominal value of DETO is 20 ms.
-- */
-- if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] &
-- ATA_LOG_DEVSLP_VALID_MASK) {
-- mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] &
-- ATA_LOG_DEVSLP_MDAT_MASK;
-- if (!mdat)
-+ /* Use the nominal value 10 ms if the read MDAT is zero,
-+ * the nominal value of DETO is 20 ms.
-+ */
-+ if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] &
-+ ATA_LOG_DEVSLP_VALID_MASK) {
-+ mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] &
-+ ATA_LOG_DEVSLP_MDAT_MASK;
-+ if (!mdat)
-+ mdat = 10;
-+ deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO];
-+ if (!deto)
-+ deto = 20;
-+ } else {
- mdat = 10;
-- deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO];
-- if (!deto)
- deto = 20;
-- } else {
-- mdat = 10;
-- deto = 20;
-+ }
- }
-
- devslp |= ((dito << PORT_DEVSLP_DITO_OFFSET) |
-diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
-index c037f33..0a78f01 100644
---- a/drivers/ata/libata-core.c
-+++ b/drivers/ata/libata-core.c
-@@ -2024,7 +2024,7 @@ retry:
- }
- }
-
-- if (id[79] & SATA_DIPM)
-+ if (id[79] & (1 << SATA_DIPM))
- dev->init_dipm = true;
-
- *p_class = class;
-@@ -3672,6 +3672,11 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- return rc;
-
- switch (policy) {
-+ case ATA_LPM_FIRMWARE_DEFAULTS:
-+ /* use the values we read at probe */
-+ scontrol &= ~(0x7 << 8);
-+ scontrol |= (link->init_lpm << 8);
-+ break;
- case ATA_LPM_MAX_POWER:
- /* disable all LPM transitions */
- scontrol |= (0x7 << 8);
-diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
-index 07f41be..c36fa56 100644
---- a/drivers/ata/libata-eh.c
-+++ b/drivers/ata/libata-eh.c
-@@ -3519,9 +3519,9 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- return 0;
-
- /*
-- * DIPM is enabled only for MIN_POWER as some devices
-- * misbehave when the host NACKs transition to SLUMBER. Order
-- * device and link configurations such that the host always
-+ * DIPM is enabled only for MIN_POWER and FIRMWARE_DEFAULT as some
-+ * devices misbehave when the host NACKs transition to SLUMBER.
-+ * Order device and link configurations such that the host always
- * allows DIPM requests.
- */
- ata_for_each_dev(dev, link, ENABLED) {
-@@ -3581,10 +3581,13 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- if (ap && ap->slave_link)
- ap->slave_link->lpm_policy = policy;
-
-- /* host config updated, enable DIPM if transitioning to MIN_POWER */
-+ /* host config updated, enable DIPM if transitioning to MIN_POWER or
-+ * FIRMWARE_DEFAULT when enabled by firmware
-+ */
- ata_for_each_dev(dev, link, ENABLED) {
-- if (policy == ATA_LPM_MIN_POWER && !no_dipm &&
-- ata_id_has_dipm(dev->id)) {
-+ if ((policy == ATA_LPM_MIN_POWER && !no_dipm &&
-+ ata_id_has_dipm(dev->id)) ||
-+ (policy == ATA_LPM_FIRMWARE_DEFAULTS && dev->init_dipm)) {
- err_mask = ata_dev_set_feature(dev,
- SETFEATURES_SATA_ENABLE, SATA_DIPM);
- if (err_mask && err_mask != AC_ERR_DEV) {
-diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
-index 3131adc..f1ea052 100644
---- a/drivers/ata/libata-scsi.c
-+++ b/drivers/ata/libata-scsi.c
-@@ -107,6 +107,7 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = {
- static const char *ata_lpm_policy_names[] = {
- [ATA_LPM_UNKNOWN] = "max_performance",
- [ATA_LPM_MAX_POWER] = "max_performance",
-+ [ATA_LPM_FIRMWARE_DEFAULTS] = "firmware_defaults",
- [ATA_LPM_MED_POWER] = "medium_power",
- [ATA_LPM_MIN_POWER] = "min_power",
- };
-diff --git a/include/linux/libata.h b/include/linux/libata.h
-index 31c149b..57b465d 100644
---- a/include/linux/libata.h
-+++ b/include/linux/libata.h
-@@ -507,6 +507,7 @@ enum ata_completion_errors {
- enum ata_lpm_policy {
- ATA_LPM_UNKNOWN,
- ATA_LPM_MAX_POWER,
-+ ATA_LPM_FIRMWARE_DEFAULTS,
- ATA_LPM_MED_POWER,
- ATA_LPM_MIN_POWER,
- };
---
-2.3.5
-
---
-To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
-the body of a message to majordomo@vger.kernel.org
-More majordomo info at http://vger.kernel.org/majordomo-info.html
-Please read the FAQ at http://www.tux.org/lkml/
-From: Matthew Garrett <mjg59@coreos.com>
-Date: Sat, 18 Apr 2015 08:26:36 -0700
-Subject: [PATCH 3/3] libata: Change medium_power LPM policy to match Intel recommendations
-
-Intel publish a document on designing energy efficient SATA devices at
-http://www.intel.com/content/dam/doc/reference-guide/sata-devices-implementation-recommendations.pdf
-which recommends that ALPE be set, ASPE be cleared and that DIPM be enabled
-on the device. Right now we have no policy that matches that - medium_power
-does not enable DIPM and min_power sets ASPE. Change medium_power to
-implement these recommendations, with the addition of devslp state being
-inherited from the initial configuration. With luck this will provide
-reasonable power savings without causing the device breakages we
-occasionally see with the min_power policy.
-
-Signed-off-by: Matthew Garrett <mjg59@coreos.com>
----
- drivers/ata/libahci.c | 1 +
- drivers/ata/libata-core.c | 4 ----
- drivers/ata/libata-eh.c | 10 ++++------
- 3 files changed, 5 insertions(+), 10 deletions(-)
-
-diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
-index fabcff4..8efacb9 100644
---- a/drivers/ata/libahci.c
-+++ b/drivers/ata/libahci.c
-@@ -738,6 +738,7 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- ahci_set_aggressive_devslp(ap, true);
- break;
- case ATA_LPM_FIRMWARE_DEFAULTS:
-+ case ATA_LPM_MED_POWER:
- ahci_set_aggressive_devslp(ap, ppriv->init_devslp);
- break;
- default:
-diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
-index 0a78f01..99a7b8f 100644
---- a/drivers/ata/libata-core.c
-+++ b/drivers/ata/libata-core.c
-@@ -3687,10 +3687,6 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- }
- break;
- case ATA_LPM_MED_POWER:
-- /* allow LPM to PARTIAL */
-- scontrol &= ~(0x1 << 8);
-- scontrol |= (0x6 << 8);
-- break;
- case ATA_LPM_MIN_POWER:
- if (ata_link_nr_enabled(link) > 0)
- /* no restrictions on LPM transitions */
-diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
-index c36fa56..25d5f37 100644
---- a/drivers/ata/libata-eh.c
-+++ b/drivers/ata/libata-eh.c
-@@ -3519,8 +3519,6 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- return 0;
-
- /*
-- * DIPM is enabled only for MIN_POWER and FIRMWARE_DEFAULT as some
-- * devices misbehave when the host NACKs transition to SLUMBER.
- * Order device and link configurations such that the host always
- * allows DIPM requests.
- */
-@@ -3540,7 +3538,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- hints &= ~ATA_LPM_HIPM;
-
- /* disable DIPM before changing link config */
-- if (policy != ATA_LPM_MIN_POWER && dipm) {
-+ if (policy < ATA_LPM_MED_POWER && dipm) {
- err_mask = ata_dev_set_feature(dev,
- SETFEATURES_SATA_DISABLE, SATA_DIPM);
- if (err_mask && err_mask != AC_ERR_DEV) {
-@@ -3581,11 +3579,11 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- if (ap && ap->slave_link)
- ap->slave_link->lpm_policy = policy;
-
-- /* host config updated, enable DIPM if transitioning to MIN_POWER or
-- * FIRMWARE_DEFAULT when enabled by firmware
-+ /* host config updated, enable DIPM if transitioning to MED_POWER,
-+ * MIN_POWER or FIRMWARE_DEFAULT when enabled by firmware
- */
- ata_for_each_dev(dev, link, ENABLED) {
-- if ((policy == ATA_LPM_MIN_POWER && !no_dipm &&
-+ if ((policy >= ATA_LPM_MED_POWER && !no_dipm &&
- ata_id_has_dipm(dev->id)) ||
- (policy == ATA_LPM_FIRMWARE_DEFAULTS && dev->init_dipm)) {
- err_mask = ata_dev_set_feature(dev,
---
-2.3.5
-
---
-To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
-the body of a message to majordomo@vger.kernel.org
-More majordomo info at http://vger.kernel.org/majordomo-info.html
-Please read the FAQ at http://www.tux.org/lkml/
-diff -upr linux-2.6.25/include/uapi/linux/netfilter/xt_owner.h linux-2.6.25-owner-xid/include/uapi/linux/netfilter/xt_owner.h
---- linux-2.6.25/include/uapi/linux/netfilter/xt_owner.h 2008-04-17 02:49:44.000000000 +0000
-+++ linux-2.6.25-owner-xid/include/uapi/linux/netfilter/xt_owner.h 2008-05-20 18:36:38.074950561 +0000
-@@ -5,12 +5,16 @@ enum {
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/netfilter/xt_owner.h linux-4.9/include/uapi/linux/netfilter/xt_owner.h
+--- linux-4.9/include/uapi/linux/netfilter/xt_owner.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/uapi/linux/netfilter/xt_owner.h 2021-02-24 15:31:31.354043397 +0100
+@@ -7,12 +7,16 @@ enum {
XT_OWNER_UID = 1 << 0,
XT_OWNER_GID = 1 << 1,
XT_OWNER_SOCKET = 1 << 2,
};
#endif /* _XT_OWNER_MATCH_H */
-diff -upr linux-2.6.25/net/netfilter/xt_owner.c linux-2.6.25-owner-xid/net/netfilter/xt_owner.c
---- linux-2.6.25/net/netfilter/xt_owner.c 2008-05-20 17:15:02.411418369 +0000
-+++ linux-2.6.25-owner-xid/net/netfilter/xt_owner.c 2008-05-20 17:48:15.774419069 +0000
-@@ -113,6 +133,16 @@ owner_mt(const struct sk_buff *skb, cons
- !(info->invert & XT_OWNER_GID))
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/xt_owner.c linux-4.9/net/netfilter/xt_owner.c
+--- linux-4.9/net/netfilter/xt_owner.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/netfilter/xt_owner.c 2021-02-24 15:31:31.354043397 +0100
+@@ -97,6 +97,16 @@ owner_mt(const struct sk_buff *skb, stru
return false;
+ }
+ if (info->match & XT_OWNER_NID)
+ if ((skb->sk->sk_nid != info->nid) ^
-diff -NurpP --minimal linux-2.6.21.b/net/ipv4/netfilter/Kconfig linux-2.6.21.a/net/ipv4/netfilter/Kconfig
---- linux-2.6.21.b/net/ipv4/netfilter/Kconfig 2007-05-30 11:11:52.000000000 +0200
-+++ linux-2.6.21.a/net/ipv4/netfilter/Kconfig 2007-05-30 11:18:08.000000000 +0200
-@@ -668,5 +668,15 @@ config IP_NF_ARP_MANGLE
- Allows altering the ARP packet payload: source and destination
- hardware and network addresses.
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/netfilter/Kconfig linux-4.9/net/ipv4/netfilter/Kconfig
+--- linux-4.9/net/ipv4/netfilter/Kconfig 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/ipv4/netfilter/Kconfig 2021-02-24 15:26:50.085354929 +0100
+@@ -406,5 +406,15 @@ config IP_NF_ARP_MANGLE
+
+ endif # IP_NF_ARPTABLES
+config IP_NF_TARGET_IPV4OPTSSTRIP
+ tristate 'IPV4OPTSSTRIP target support'
+
endmenu
---- linux-3.4/net/ipv4/netfilter/Makefile~ 2012-05-21 08:42:02.000000000 +0200
-+++ linux-3.4/net/ipv4/netfilter/Makefile 2012-05-21 08:45:09.247956356 +0200
-@@ -54,6 +54,7 @@
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/netfilter/Makefile linux-4.9/net/ipv4/netfilter/Makefile
+--- linux-4.9/net/ipv4/netfilter/Makefile 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/ipv4/netfilter/Makefile 2021-02-24 15:26:50.085354929 +0100
+@@ -56,6 +56,7 @@ obj-$(CONFIG_IP_NF_MATCH_RPFILTER) += ip
# targets
obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o
+obj-$(CONFIG_IP_NF_TARGET_IPV4OPTSSTRIP) += ipt_IPV4OPTSSTRIP.o
obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
- obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
- obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
-diff -NurpP --minimal linux-2.6.21.b/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c linux-2.6.21.a/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c
---- linux-2.6.21.b/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.21.a/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c 2007-05-30 11:18:08.000000000 +0200
+ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+ obj-$(CONFIG_IP_NF_TARGET_SYNPROXY) += ipt_SYNPROXY.o
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c linux-4.9/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c
+--- linux-4.9/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c 2021-02-24 15:26:50.085354929 +0100
@@ -0,0 +1,75 @@
+/**
+ * Strip all IP options in the IP packet header.
---- linux-2.6.22-rc3-orig/drivers/net/usb/rndis_host.c 2007-05-25 22:55:14.000000000 -0400
-+++ linux-2.6.22-rc3/drivers/net/usb/rndis_host.c 2007-05-27 17:06:16.000000000 -0400
-@@ -499,8 +499,7 @@
- net->hard_header_len += sizeof (struct rndis_data_hdr);
- dev->hard_mtu = net->mtu + net->hard_header_len;
+diff -urNp -x '*.orig' linux-4.9/drivers/net/usb/rndis_host.c linux-4.9/drivers/net/usb/rndis_host.c
+--- linux-4.9/drivers/net/usb/rndis_host.c 2021-02-24 15:44:20.021329751 +0100
++++ linux-4.9/drivers/net/usb/rndis_host.c 2021-02-24 15:44:33.395081159 +0100
+@@ -352,8 +352,7 @@ generic_rndis_bind(struct usbnet *dev, s
+ goto fail_and_release;
+ }
- dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1);
- dev->rx_urb_size &= ~(dev->maxpacket - 1);
+ dev->rx_urb_size = (dev->udev->speed == USB_SPEED_FULL) ? 16384 : 8192;
u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size);
- net->change_mtu = NULL;
+ net->netdev_ops = &rndis_netdev_ops;
---- linux-2.6.33/scripts/mod/modpost.c~ 2010-02-24 19:52:17.000000000 +0100
-+++ linux-2.6.33/scripts/mod/modpost.c 2010-03-07 14:26:47.242168558 +0100
-@@ -15,7 +15,8 @@
- #include <stdio.h>
- #include <ctype.h>
- #include "modpost.h"
--#include "../../include/generated/autoconf.h"
-+// PLD architectures don't use CONFIG_SYMBOL_PREFIX
-+//#include "../../include/generated/autoconf.h"
- #include "../../include/linux/license.h"
-
- /* Some toolchains use a `_' prefix for all user symbols. */
-
---- linux-3.0/scripts/kconfig/lxdialog/check-lxdialog.sh~ 2011-07-22 04:17:23.000000000 +0200
-+++ linux-3.0/scripts/kconfig/lxdialog/check-lxdialog.sh 2011-08-25 21:26:04.799150642 +0200
-@@ -9,6 +9,12 @@
- $cc -print-file-name=lib${lib}.${ext} | grep -q /
- if [ $? -eq 0 ]; then
- echo "-l${lib}"
-+ for libt in tinfow tinfo ; do
-+ $cc -print-file-name=lib${libt}.${ext} | grep -q /
-+ if [ $? -eq 0 ]; then
-+ echo "-l${libt}"
-+ fi
-+ done
- exit
- fi
- done
-
-From: Shaohua Li <shli@fb.com>
-
-Basically this is a copy of commit 001e4a8775f6(ext4: implement cgroup
-writeback support). Tested with a fio test, verified writeback is
-throttled against cgroup io.max write bandwidth, also verified moving
-the fio test to another cgroup and the writeback is throttled against
-new cgroup setting.
-
-Cc: Tejun Heo <tj@kernel.org>
-Signed-off-by: Shaohua Li <shli@fb.com>
----
- fs/xfs/xfs_aops.c | 2 ++
- fs/xfs/xfs_super.c | 1 +
- 2 files changed, 3 insertions(+)
-
-diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
-index f18e593..6535054 100644
---- a/fs/xfs/xfs_aops.c
-+++ b/fs/xfs/xfs_aops.c
-@@ -630,8 +630,10 @@ xfs_add_to_ioend(
+diff -urNp -x '*.orig' linux-4.9/fs/xfs/xfs_aops.c linux-4.9/fs/xfs/xfs_aops.c
+--- linux-4.9/fs/xfs/xfs_aops.c 2021-02-24 16:28:24.013817047 +0100
++++ linux-4.9/fs/xfs/xfs_aops.c 2021-02-24 16:28:36.820893504 +0100
+@@ -634,8 +634,10 @@ xfs_add_to_ioend(
if (wpc->ioend)
list_add(&wpc->ioend->io_list, iolist);
wpc->ioend = xfs_alloc_ioend(inode, wpc->io_type, offset, bh);
/*
* If the buffer doesn't fit into the bio we need to allocate a new
* one. This shouldn't happen more than once for a given buffer.
-diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
-index 584cf2d..aea3bc2 100644
---- a/fs/xfs/xfs_super.c
-+++ b/fs/xfs/xfs_super.c
-@@ -1634,6 +1634,7 @@ xfs_fs_fill_super(
+diff -urNp -x '*.orig' linux-4.9/fs/xfs/xfs_super.c linux-4.9/fs/xfs/xfs_super.c
+--- linux-4.9/fs/xfs/xfs_super.c 2021-02-24 16:28:24.027150807 +0100
++++ linux-4.9/fs/xfs/xfs_super.c 2021-02-24 16:28:36.824226943 +0100
+@@ -1609,6 +1609,7 @@ xfs_fs_fill_super(
sb->s_max_links = XFS_MAXLINK;
sb->s_time_gran = 1;
set_posix_acl_flag(sb);
/* version 5 superblocks support inode version counters. */
if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5)
+diff -urNp -x '*.orig' linux-4.9/scripts/kconfig/lxdialog/check-lxdialog.sh linux-4.9/scripts/kconfig/lxdialog/check-lxdialog.sh
+--- linux-4.9/scripts/kconfig/lxdialog/check-lxdialog.sh 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/scripts/kconfig/lxdialog/check-lxdialog.sh 2021-02-24 16:28:36.820893504 +0100
+@@ -11,6 +11,12 @@ ldflags()
+ $cc -print-file-name=lib${lib}.${ext} | grep -q /
+ if [ $? -eq 0 ]; then
+ echo "-l${lib}"
++ for libt in tinfow tinfo ; do
++ $cc -print-file-name=lib${libt}.${ext} | grep -q /
++ if [ $? -eq 0 ]; then
++ echo "-l${libt}"
++ fi
++ done
+ exit
+ fi
+ done
+diff -urNp -x '*.orig' linux-4.9/scripts/mod/modpost.c linux-4.9/scripts/mod/modpost.c
+--- linux-4.9/scripts/mod/modpost.c 2021-02-24 16:28:24.380495447 +0100
++++ linux-4.9/scripts/mod/modpost.c 2021-02-24 16:28:36.820893504 +0100
+@@ -19,7 +19,8 @@
+ #include <stdbool.h>
+ #include <errno.h>
+ #include "modpost.h"
+-#include "../../include/generated/autoconf.h"
++// PLD architectures don't use CONFIG_SYMBOL_PREFIX
++//#include "../../include/generated/autoconf.h"
+ #include "../../include/linux/license.h"
+ #include "../../include/linux/export.h"
+
-diff -NurpP --minimal linux-4.9.217/arch/alpha/Kconfig linux-4.9.217-vs2.3.9.12/arch/alpha/Kconfig
---- linux-4.9.217/arch/alpha/Kconfig 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/alpha/Kconfig 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/Documentation/vserver/debug.txt linux-4.9/Documentation/vserver/debug.txt
+--- linux-4.9/Documentation/vserver/debug.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/Documentation/vserver/debug.txt 2021-02-24 15:47:45.047741532 +0100
+@@ -0,0 +1,154 @@
++
++debug_cvirt:
++
++ 2 4 "vx_map_tgid: %p/%llx: %d -> %d"
++ "vx_rmap_tgid: %p/%llx: %d -> %d"
++
++debug_dlim:
++
++ 0 1 "ALLOC (%p,#%d)%c inode (%d)"
++ "FREE (%p,#%d)%c inode"
++ 1 2 "ALLOC (%p,#%d)%c %lld bytes (%d)"
++ "FREE (%p,#%d)%c %lld bytes"
++ 2 4 "ADJUST: %lld,%lld on %ld,%ld [mult=%d]"
++ 3 8 "ext3_has_free_blocks(%p): %lu<%lu+1, %c, %u!=%u r=%d"
++ "ext3_has_free_blocks(%p): free=%lu, root=%lu"
++ "rcu_free_dl_info(%p)"
++ 4 10 "alloc_dl_info(%p,%d) = %p"
++ "dealloc_dl_info(%p)"
++ "get_dl_info(%p[#%d.%d])"
++ "put_dl_info(%p[#%d.%d])"
++ 5 20 "alloc_dl_info(%p,%d)*"
++ 6 40 "__hash_dl_info: %p[#%d]"
++ "__unhash_dl_info: %p[#%d]"
++ 7 80 "locate_dl_info(%p,#%d) = %p"
++
++debug_misc:
++
++ 0 1 "destroy_dqhash: %p [#0x%08x] c=%d"
++ "new_dqhash: %p [#0x%08x]"
++ "vroot[%d]_clr_dev: dev=%p[%lu,%d:%d]"
++ "vroot[%d]_get_real_bdev: dev=%p[%lu,%d:%d]"
++ "vroot[%d]_set_dev: dev=%p[%lu,%d:%d]"
++ "vroot_get_real_bdev not set"
++ 1 2 "cow_break_link(?%s?)"
++ "temp copy ?%s?"
++ 2 4 "dentry_open(new): %p"
++ "dentry_open(old): %p"
++ "lookup_create(new): %p"
++ "old path ?%s?"
++ "path_lookup(old): %d"
++ "vfs_create(new): %d"
++ "vfs_rename: %d"
++ "vfs_sendfile: %d"
++ 3 8 "fput(new_file=%p[#%d])"
++ "fput(old_file=%p[#%d])"
++ 4 10 "vx_info_kill(%p[#%d],%d,%d) = %d"
++ "vx_info_kill(%p[#%d],%d,%d)*"
++ 5 20 "vs_reboot(%p[#%d],%d)"
++ 6 40 "dropping task %p[#%u,%u] for %p[#%u,%u]"
++
++debug_net:
++
++ 2 4 "nx_addr_conflict(%p,%p) %d.%d,%d.%d"
++ 3 8 "inet_bind(%p) %d.%d.%d.%d, %d.%d.%d.%d, %d.%d.%d.%d"
++ "inet_bind(%p)* %p,%p;%lx %d.%d.%d.%d"
++ 4 10 "ip_route_connect(%p) %p,%p;%lx"
++ 5 20 "__addr_in_socket(%p,%d.%d.%d.%d) %p:%d.%d.%d.%d %p;%lx"
++ 6 40 "sk,egf: %p [#%d] (from %d)"
++ "sk,egn: %p [#%d] (from %d)"
++ "sk,req: %p [#%d] (from %d)"
++ "sk: %p [#%d] (from %d)"
++ "tw: %p [#%d] (from %d)"
++ 7 80 "__sock_recvmsg: %p[%p,%p,%p;%d]:%d/%d"
++ "__sock_sendmsg: %p[%p,%p,%p;%d]:%d/%d"
++
++debug_nid:
++
++ 0 1 "__lookup_nx_info(#%u): %p[#%u]"
++ "alloc_nx_info(%d) = %p"
++ "create_nx_info(%d) (dynamic rejected)"
++ "create_nx_info(%d) = %p (already there)"
++ "create_nx_info(%d) = %p (new)"
++ "dealloc_nx_info(%p)"
++ 1 2 "alloc_nx_info(%d)*"
++ "create_nx_info(%d)*"
++ 2 4 "get_nx_info(%p[#%d.%d])"
++ "put_nx_info(%p[#%d.%d])"
++ 3 8 "claim_nx_info(%p[#%d.%d.%d]) %p"
++ "clr_nx_info(%p[#%d.%d])"
++ "init_nx_info(%p[#%d.%d])"
++ "release_nx_info(%p[#%d.%d.%d]) %p"
++ "set_nx_info(%p[#%d.%d])"
++ 4 10 "__hash_nx_info: %p[#%d]"
++ "__nx_dynamic_id: [#%d]"
++ "__unhash_nx_info: %p[#%d.%d.%d]"
++ 5 20 "moved task %p into nxi:%p[#%d]"
++ "nx_migrate_task(%p,%p[#%d.%d.%d])"
++ "task_get_nx_info(%p)"
++ 6 40 "nx_clear_persistent(%p[#%d])"
++
++debug_quota:
++
++ 0 1 "quota_sync_dqh(%p,%d) discard inode %p"
++ 1 2 "quota_sync_dqh(%p,%d)"
++ "sync_dquots(%p,%d)"
++ "sync_dquots_dqh(%p,%d)"
++ 3 8 "do_quotactl(%p,%d,cmd=%d,id=%d,%p)"
++
++debug_switch:
++
++ 0 1 "vc: VCMD_%02d_%d[%d], %d,%p [%d,%d,%x,%x]"
++ 1 2 "vc: VCMD_%02d_%d[%d] = %08lx(%ld) [%d,%d]"
++ 4 10 "%s: (%s %s) returned %s with %d"
++
++debug_tag:
++
++ 7 80 "dx_parse_tag(?%s?): %d:#%d"
++ "dx_propagate_tag(%p[#%lu.%d]): %d,%d"
++
++debug_xid:
++
++ 0 1 "__lookup_vx_info(#%u): %p[#%u]"
++ "alloc_vx_info(%d) = %p"
++ "alloc_vx_info(%d)*"
++ "create_vx_info(%d) (dynamic rejected)"
++ "create_vx_info(%d) = %p (already there)"
++ "create_vx_info(%d) = %p (new)"
++ "dealloc_vx_info(%p)"
++ "loc_vx_info(%d) = %p (found)"
++ "loc_vx_info(%d) = %p (new)"
++ "loc_vx_info(%d) = %p (not available)"
++ 1 2 "create_vx_info(%d)*"
++ "loc_vx_info(%d)*"
++ 2 4 "get_vx_info(%p[#%d.%d])"
++ "put_vx_info(%p[#%d.%d])"
++ 3 8 "claim_vx_info(%p[#%d.%d.%d]) %p"
++ "clr_vx_info(%p[#%d.%d])"
++ "init_vx_info(%p[#%d.%d])"
++ "release_vx_info(%p[#%d.%d.%d]) %p"
++ "set_vx_info(%p[#%d.%d])"
++ 4 10 "__hash_vx_info: %p[#%d]"
++ "__unhash_vx_info: %p[#%d.%d.%d]"
++ "__vx_dynamic_id: [#%d]"
++ 5 20 "enter_vx_info(%p[#%d],%p) %p[#%d,%p]"
++ "leave_vx_info(%p[#%d,%p]) %p[#%d,%p]"
++ "moved task %p into vxi:%p[#%d]"
++ "task_get_vx_info(%p)"
++ "vx_migrate_task(%p,%p[#%d.%d])"
++ 6 40 "vx_clear_persistent(%p[#%d])"
++ "vx_exit_init(%p[#%d],%p[#%d,%d,%d])"
++ "vx_set_init(%p[#%d],%p[#%d,%d,%d])"
++ "vx_set_persistent(%p[#%d])"
++ "vx_set_reaper(%p[#%d],%p[#%d,%d])"
++ 7 80 "vx_child_reaper(%p[#%u,%u]) = %p[#%u,%u]"
++
++
++debug_limit:
++
++ n 2^n "vx_acc_cres[%5d,%s,%2d]: %5d%s"
++ "vx_cres_avail[%5d,%s,%2d]: %5ld > %5d + %5d"
++
++ m 2^m "vx_acc_page[%5d,%s,%2d]: %5d%s"
++ "vx_acc_pages[%5d,%s,%2d]: %5d += %5d"
++ "vx_pages_avail[%5d,%s,%2d]: %5ld > %5d + %5d"
+diff -urNp -x '*.orig' linux-4.9/arch/alpha/Kconfig linux-4.9/arch/alpha/Kconfig
+--- linux-4.9/arch/alpha/Kconfig 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/alpha/Kconfig 2021-02-24 15:47:45.037741219 +0100
@@ -743,6 +743,8 @@ config DUMMY_CONSOLE
depends on VGA_HOSE
default y
source "security/Kconfig"
source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/alpha/kernel/systbls.S linux-4.9.217-vs2.3.9.12/arch/alpha/kernel/systbls.S
---- linux-4.9.217/arch/alpha/kernel/systbls.S 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/alpha/kernel/systbls.S 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/alpha/kernel/systbls.S linux-4.9/arch/alpha/kernel/systbls.S
+--- linux-4.9/arch/alpha/kernel/systbls.S 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/alpha/kernel/systbls.S 2021-02-24 15:47:45.037741219 +0100
@@ -446,7 +446,7 @@ sys_call_table:
.quad sys_stat64 /* 425 */
.quad sys_lstat64
.quad sys_ni_syscall /* sys_mbind */
.quad sys_ni_syscall /* sys_get_mempolicy */
.quad sys_ni_syscall /* sys_set_mempolicy */
-diff -NurpP --minimal linux-4.9.217/arch/alpha/kernel/traps.c linux-4.9.217-vs2.3.9.12/arch/alpha/kernel/traps.c
---- linux-4.9.217/arch/alpha/kernel/traps.c 2020-03-27 00:50:40.331132545 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/alpha/kernel/traps.c 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/alpha/kernel/traps.c linux-4.9/arch/alpha/kernel/traps.c
+--- linux-4.9/arch/alpha/kernel/traps.c 2021-02-24 15:47:30.747294162 +0100
++++ linux-4.9/arch/alpha/kernel/traps.c 2021-02-24 15:47:45.037741219 +0100
@@ -179,7 +179,8 @@ die_if_kernel(char * str, struct pt_regs
#ifdef CONFIG_SMP
printk("CPU %d ", hard_smp_processor_id());
dik_show_regs(regs, r9_15);
add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
dik_show_trace((unsigned long *)(regs+1));
-diff -NurpP --minimal linux-4.9.217/arch/arm/Kconfig linux-4.9.217-vs2.3.9.12/arch/arm/Kconfig
---- linux-4.9.217/arch/arm/Kconfig 2020-03-27 00:50:40.441130813 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/arm/Kconfig 2020-04-01 09:39:57.315984093 +0000
-@@ -2200,6 +2200,8 @@ source "fs/Kconfig"
+diff -urNp -x '*.orig' linux-4.9/arch/arm/Kconfig linux-4.9/arch/arm/Kconfig
+--- linux-4.9/arch/arm/Kconfig 2021-02-24 15:47:30.757294474 +0100
++++ linux-4.9/arch/arm/Kconfig 2021-02-24 15:47:45.041074656 +0100
+@@ -2202,6 +2202,8 @@ source "fs/Kconfig"
source "arch/arm/Kconfig.debug"
source "security/Kconfig"
source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/arm/kernel/calls.S linux-4.9.217-vs2.3.9.12/arch/arm/kernel/calls.S
---- linux-4.9.217/arch/arm/kernel/calls.S 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/arm/kernel/calls.S 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/arm/kernel/calls.S linux-4.9/arch/arm/kernel/calls.S
+--- linux-4.9/arch/arm/kernel/calls.S 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/arm/kernel/calls.S 2021-02-24 15:47:45.041074656 +0100
@@ -322,7 +322,7 @@
/* 310 */ CALL(sys_request_key)
CALL(sys_keyctl)
CALL(sys_ioprio_set)
/* 315 */ CALL(sys_ioprio_get)
CALL(sys_inotify_init)
-diff -NurpP --minimal linux-4.9.217/arch/arm/kernel/traps.c linux-4.9.217-vs2.3.9.12/arch/arm/kernel/traps.c
---- linux-4.9.217/arch/arm/kernel/traps.c 2020-03-27 00:50:40.901123578 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/arm/kernel/traps.c 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/arm/kernel/traps.c linux-4.9/arch/arm/kernel/traps.c
+--- linux-4.9/arch/arm/kernel/traps.c 2021-02-24 15:47:30.797295726 +0100
++++ linux-4.9/arch/arm/kernel/traps.c 2021-02-24 15:47:45.041074656 +0100
@@ -279,8 +279,8 @@ static int __die(const char *str, int er
print_modules();
if (!user_mode(regs) || in_interrupt()) {
dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp,
-diff -NurpP --minimal linux-4.9.217/arch/cris/Kconfig linux-4.9.217-vs2.3.9.12/arch/cris/Kconfig
---- linux-4.9.217/arch/cris/Kconfig 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/cris/Kconfig 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/cris/Kconfig linux-4.9/arch/cris/Kconfig
+--- linux-4.9/arch/cris/Kconfig 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/cris/Kconfig 2021-02-24 15:47:45.041074656 +0100
@@ -583,6 +583,8 @@ source "fs/Kconfig"
source "arch/cris/Kconfig.debug"
source "security/Kconfig"
source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/ia64/Kconfig linux-4.9.217-vs2.3.9.12/arch/ia64/Kconfig
---- linux-4.9.217/arch/ia64/Kconfig 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/ia64/Kconfig 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/ia64/Kconfig linux-4.9/arch/ia64/Kconfig
+--- linux-4.9/arch/ia64/Kconfig 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/ia64/Kconfig 2021-02-24 15:47:45.041074656 +0100
@@ -602,6 +602,8 @@ source "fs/Kconfig"
source "arch/ia64/Kconfig.debug"
source "security/Kconfig"
source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/ia64/kernel/entry.S linux-4.9.217-vs2.3.9.12/arch/ia64/kernel/entry.S
---- linux-4.9.217/arch/ia64/kernel/entry.S 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/ia64/kernel/entry.S 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/ia64/kernel/entry.S linux-4.9/arch/ia64/kernel/entry.S
+--- linux-4.9/arch/ia64/kernel/entry.S 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/ia64/kernel/entry.S 2021-02-24 15:47:45.041074656 +0100
@@ -1697,7 +1697,7 @@ sys_call_table:
data8 sys_mq_notify
data8 sys_mq_getsetattr
data8 sys_waitid // 1270
data8 sys_add_key
data8 sys_request_key
-diff -NurpP --minimal linux-4.9.217/arch/ia64/kernel/ptrace.c linux-4.9.217-vs2.3.9.12/arch/ia64/kernel/ptrace.c
---- linux-4.9.217/arch/ia64/kernel/ptrace.c 2020-03-27 00:50:42.241102503 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/ia64/kernel/ptrace.c 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/ia64/kernel/ptrace.c linux-4.9/arch/ia64/kernel/ptrace.c
+--- linux-4.9/arch/ia64/kernel/ptrace.c 2021-02-24 15:47:30.857297603 +0100
++++ linux-4.9/arch/ia64/kernel/ptrace.c 2021-02-24 15:47:45.041074656 +0100
@@ -21,6 +21,7 @@
#include <linux/regset.h>
#include <linux/elf.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
-diff -NurpP --minimal linux-4.9.217/arch/ia64/kernel/traps.c linux-4.9.217-vs2.3.9.12/arch/ia64/kernel/traps.c
---- linux-4.9.217/arch/ia64/kernel/traps.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/ia64/kernel/traps.c 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/ia64/kernel/traps.c linux-4.9/arch/ia64/kernel/traps.c
+--- linux-4.9/arch/ia64/kernel/traps.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/ia64/kernel/traps.c 2021-02-24 15:47:45.041074656 +0100
@@ -60,8 +60,9 @@ die (const char *str, struct pt_regs *re
put_cpu();
}
}
}
-diff -NurpP --minimal linux-4.9.217/arch/m32r/kernel/traps.c linux-4.9.217-vs2.3.9.12/arch/m32r/kernel/traps.c
---- linux-4.9.217/arch/m32r/kernel/traps.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/m32r/kernel/traps.c 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/m32r/kernel/traps.c linux-4.9/arch/m32r/kernel/traps.c
+--- linux-4.9/arch/m32r/kernel/traps.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/m32r/kernel/traps.c 2021-02-24 15:47:45.041074656 +0100
@@ -184,8 +184,9 @@ static void show_registers(struct pt_reg
} else {
printk("SPI: %08lx\n", sp);
/*
* When in-kernel, we also print out the stack and code at the
-diff -NurpP --minimal linux-4.9.217/arch/m68k/Kconfig linux-4.9.217-vs2.3.9.12/arch/m68k/Kconfig
---- linux-4.9.217/arch/m68k/Kconfig 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/m68k/Kconfig 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/m68k/Kconfig linux-4.9/arch/m68k/Kconfig
+--- linux-4.9/arch/m68k/Kconfig 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/m68k/Kconfig 2021-02-24 15:47:45.041074656 +0100
@@ -163,6 +163,8 @@ source "fs/Kconfig"
source "arch/m68k/Kconfig.debug"
source "security/Kconfig"
source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/mips/Kconfig linux-4.9.217-vs2.3.9.12/arch/mips/Kconfig
---- linux-4.9.217/arch/mips/Kconfig 2020-03-27 00:50:42.451099201 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/mips/Kconfig 2019-12-25 15:37:43.808561393 +0000
-@@ -3193,6 +3193,8 @@ source "fs/Kconfig"
+diff -urNp -x '*.orig' linux-4.9/arch/mips/Kconfig linux-4.9/arch/mips/Kconfig
+--- linux-4.9/arch/mips/Kconfig 2021-02-24 15:47:30.863964478 +0100
++++ linux-4.9/arch/mips/Kconfig 2021-02-24 15:47:45.044408094 +0100
+@@ -3194,6 +3194,8 @@ source "fs/Kconfig"
source "arch/mips/Kconfig.debug"
source "security/Kconfig"
source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/mips/kernel/ptrace.c linux-4.9.217-vs2.3.9.12/arch/mips/kernel/ptrace.c
---- linux-4.9.217/arch/mips/kernel/ptrace.c 2020-03-27 00:50:42.811093539 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/mips/kernel/ptrace.c 2018-10-20 05:55:33.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/mips/kernel/ptrace.c linux-4.9/arch/mips/kernel/ptrace.c
+--- linux-4.9/arch/mips/kernel/ptrace.c 2021-02-24 15:47:30.880631666 +0100
++++ linux-4.9/arch/mips/kernel/ptrace.c 2021-02-24 15:47:45.044408094 +0100
@@ -30,6 +30,7 @@
#include <linux/audit.h>
#include <linux/seccomp.h>
switch (request) {
/* when I and D space are separate, these will need to be fixed. */
case PTRACE_PEEKTEXT: /* read word at location addr. */
-diff -NurpP --minimal linux-4.9.217/arch/mips/kernel/scall32-o32.S linux-4.9.217-vs2.3.9.12/arch/mips/kernel/scall32-o32.S
---- linux-4.9.217/arch/mips/kernel/scall32-o32.S 2020-03-27 00:50:42.811093539 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/mips/kernel/scall32-o32.S 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/mips/kernel/scall32-o32.S linux-4.9/arch/mips/kernel/scall32-o32.S
+--- linux-4.9/arch/mips/kernel/scall32-o32.S 2021-02-24 15:47:30.880631666 +0100
++++ linux-4.9/arch/mips/kernel/scall32-o32.S 2021-02-24 15:47:45.044408094 +0100
@@ -511,7 +511,7 @@ EXPORT(sys_call_table)
PTR sys_mq_timedreceive
PTR sys_mq_notify /* 4275 */
PTR sys_waitid
PTR sys_ni_syscall /* available, was setaltroot */
PTR sys_add_key /* 4280 */
-diff -NurpP --minimal linux-4.9.217/arch/mips/kernel/scall64-64.S linux-4.9.217-vs2.3.9.12/arch/mips/kernel/scall64-64.S
---- linux-4.9.217/arch/mips/kernel/scall64-64.S 2020-03-27 00:50:42.811093539 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/mips/kernel/scall64-64.S 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/mips/kernel/scall64-64.S linux-4.9/arch/mips/kernel/scall64-64.S
+--- linux-4.9/arch/mips/kernel/scall64-64.S 2021-02-24 15:47:30.880631666 +0100
++++ linux-4.9/arch/mips/kernel/scall64-64.S 2021-02-24 15:47:45.044408094 +0100
@@ -348,7 +348,7 @@ EXPORT(sys_call_table)
PTR sys_mq_timedreceive
PTR sys_mq_notify
PTR sys_waitid
PTR sys_ni_syscall /* available, was setaltroot */
PTR sys_add_key
-diff -NurpP --minimal linux-4.9.217/arch/mips/kernel/scall64-n32.S linux-4.9.217-vs2.3.9.12/arch/mips/kernel/scall64-n32.S
---- linux-4.9.217/arch/mips/kernel/scall64-n32.S 2020-03-27 00:50:42.811093539 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/mips/kernel/scall64-n32.S 2018-10-20 04:58:12.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/mips/kernel/scall64-n32.S linux-4.9/arch/mips/kernel/scall64-n32.S
+--- linux-4.9/arch/mips/kernel/scall64-n32.S 2021-02-24 15:47:30.880631666 +0100
++++ linux-4.9/arch/mips/kernel/scall64-n32.S 2021-02-24 15:47:45.044408094 +0100
@@ -343,7 +343,7 @@ EXPORT(sysn32_call_table)
PTR compat_sys_mq_timedreceive
PTR compat_sys_mq_notify
PTR compat_sys_waitid
PTR sys_ni_syscall /* available, was setaltroot */
PTR sys_add_key
-diff -NurpP --minimal linux-4.9.217/arch/mips/kernel/scall64-o32.S linux-4.9.217-vs2.3.9.12/arch/mips/kernel/scall64-o32.S
---- linux-4.9.217/arch/mips/kernel/scall64-o32.S 2020-03-27 00:50:42.811093539 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/mips/kernel/scall64-o32.S 2019-10-05 14:58:36.920447392 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/mips/kernel/scall64-o32.S linux-4.9/arch/mips/kernel/scall64-o32.S
+--- linux-4.9/arch/mips/kernel/scall64-o32.S 2021-02-24 15:47:30.880631666 +0100
++++ linux-4.9/arch/mips/kernel/scall64-o32.S 2021-02-24 15:47:45.044408094 +0100
@@ -499,7 +499,7 @@ EXPORT(sys32_call_table)
PTR compat_sys_mq_timedreceive
PTR compat_sys_mq_notify /* 4275 */
PTR compat_sys_waitid
PTR sys_ni_syscall /* available, was setaltroot */
PTR sys_add_key /* 4280 */
-diff -NurpP --minimal linux-4.9.217/arch/mips/kernel/traps.c linux-4.9.217-vs2.3.9.12/arch/mips/kernel/traps.c
---- linux-4.9.217/arch/mips/kernel/traps.c 2020-03-27 00:50:42.811093539 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/mips/kernel/traps.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/mips/kernel/traps.c linux-4.9/arch/mips/kernel/traps.c
+--- linux-4.9/arch/mips/kernel/traps.c 2021-02-24 15:47:30.880631666 +0100
++++ linux-4.9/arch/mips/kernel/traps.c 2021-02-24 15:47:45.044408094 +0100
@@ -361,9 +361,10 @@ void show_registers(struct pt_regs *regs
__show_regs(regs);
if (cpu_has_userlocal) {
unsigned long tls;
-diff -NurpP --minimal linux-4.9.217/arch/parisc/Kconfig linux-4.9.217-vs2.3.9.12/arch/parisc/Kconfig
---- linux-4.9.217/arch/parisc/Kconfig 2020-03-27 00:50:43.061089606 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/parisc/Kconfig 2018-10-20 05:55:33.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/parisc/Kconfig linux-4.9/arch/parisc/Kconfig
+--- linux-4.9/arch/parisc/Kconfig 2021-02-24 15:47:30.893965417 +0100
++++ linux-4.9/arch/parisc/Kconfig 2021-02-24 15:47:45.044408094 +0100
@@ -348,6 +348,8 @@ config SECCOMP
If unsure, say Y. Only embedded should say N here.
source "security/Kconfig"
source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/parisc/kernel/syscall_table.S linux-4.9.217-vs2.3.9.12/arch/parisc/kernel/syscall_table.S
---- linux-4.9.217/arch/parisc/kernel/syscall_table.S 2020-03-27 00:50:43.151088192 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/parisc/kernel/syscall_table.S 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/parisc/kernel/syscall_table.S linux-4.9/arch/parisc/kernel/syscall_table.S
+--- linux-4.9/arch/parisc/kernel/syscall_table.S 2021-02-24 15:47:30.897298854 +0100
++++ linux-4.9/arch/parisc/kernel/syscall_table.S 2021-02-24 15:47:45.044408094 +0100
@@ -358,7 +358,7 @@
ENTRY_COMP(mbind) /* 260 */
ENTRY_COMP(get_mempolicy)
ENTRY_SAME(add_key)
ENTRY_SAME(request_key) /* 265 */
ENTRY_COMP(keyctl)
-diff -NurpP --minimal linux-4.9.217/arch/parisc/kernel/traps.c linux-4.9.217-vs2.3.9.12/arch/parisc/kernel/traps.c
---- linux-4.9.217/arch/parisc/kernel/traps.c 2020-03-27 00:50:43.161088037 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/parisc/kernel/traps.c 2019-02-22 08:37:49.463155825 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/parisc/kernel/traps.c linux-4.9/arch/parisc/kernel/traps.c
+--- linux-4.9/arch/parisc/kernel/traps.c 2021-02-24 15:47:30.897298854 +0100
++++ linux-4.9/arch/parisc/kernel/traps.c 2021-02-24 15:47:45.044408094 +0100
@@ -235,8 +235,9 @@ void die_if_kernel(char *str, struct pt_
return; /* STFU */
/* Wot's wrong wif bein' racy? */
if (current->thread.flags & PARISC_KERNEL_DEATH) {
-diff -NurpP --minimal linux-4.9.217/arch/powerpc/include/uapi/asm/unistd.h linux-4.9.217-vs2.3.9.12/arch/powerpc/include/uapi/asm/unistd.h
---- linux-4.9.217/arch/powerpc/include/uapi/asm/unistd.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/powerpc/include/uapi/asm/unistd.h 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/powerpc/Kconfig linux-4.9/arch/powerpc/Kconfig
+--- linux-4.9/arch/powerpc/Kconfig 2021-02-24 15:47:30.900632291 +0100
++++ linux-4.9/arch/powerpc/Kconfig 2021-02-24 15:47:45.044408094 +0100
+@@ -1092,6 +1092,8 @@ source "lib/Kconfig"
+
+ source "arch/powerpc/Kconfig.debug"
+
++source "kernel/vserver/Kconfig"
++
+ source "security/Kconfig"
+
+ source "crypto/Kconfig"
+diff -urNp -x '*.orig' linux-4.9/arch/powerpc/include/uapi/asm/unistd.h linux-4.9/arch/powerpc/include/uapi/asm/unistd.h
+--- linux-4.9/arch/powerpc/include/uapi/asm/unistd.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/powerpc/include/uapi/asm/unistd.h 2021-02-24 15:47:45.044408094 +0100
@@ -275,7 +275,7 @@
#endif
#define __NR_rtas 255
#define __NR_migrate_pages 258
#define __NR_mbind 259
#define __NR_get_mempolicy 260
-diff -NurpP --minimal linux-4.9.217/arch/powerpc/Kconfig linux-4.9.217-vs2.3.9.12/arch/powerpc/Kconfig
---- linux-4.9.217/arch/powerpc/Kconfig 2020-03-27 00:50:43.171087876 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/powerpc/Kconfig 2020-04-01 09:40:17.695646404 +0000
-@@ -1092,6 +1092,8 @@ source "lib/Kconfig"
+diff -urNp -x '*.orig' linux-4.9/arch/s390/Kconfig linux-4.9/arch/s390/Kconfig
+--- linux-4.9/arch/s390/Kconfig 2021-02-24 15:47:30.947300418 +0100
++++ linux-4.9/arch/s390/Kconfig 2021-02-24 15:47:45.044408094 +0100
+@@ -775,6 +775,8 @@ source "fs/Kconfig"
- source "arch/powerpc/Kconfig.debug"
+ source "arch/s390/Kconfig.debug"
+source "kernel/vserver/Kconfig"
+
source "security/Kconfig"
source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/s390/include/asm/tlb.h linux-4.9.217-vs2.3.9.12/arch/s390/include/asm/tlb.h
---- linux-4.9.217/arch/s390/include/asm/tlb.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/s390/include/asm/tlb.h 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/s390/include/asm/tlb.h linux-4.9/arch/s390/include/asm/tlb.h
+--- linux-4.9/arch/s390/include/asm/tlb.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/s390/include/asm/tlb.h 2021-02-24 15:47:45.044408094 +0100
@@ -24,6 +24,7 @@
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <asm/processor.h>
#include <asm/pgalloc.h>
#include <asm/tlbflush.h>
-diff -NurpP --minimal linux-4.9.217/arch/s390/include/uapi/asm/unistd.h linux-4.9.217-vs2.3.9.12/arch/s390/include/uapi/asm/unistd.h
---- linux-4.9.217/arch/s390/include/uapi/asm/unistd.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/s390/include/uapi/asm/unistd.h 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/s390/include/uapi/asm/unistd.h linux-4.9/arch/s390/include/uapi/asm/unistd.h
+--- linux-4.9/arch/s390/include/uapi/asm/unistd.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/s390/include/uapi/asm/unistd.h 2021-02-24 15:47:45.044408094 +0100
@@ -200,7 +200,7 @@
#define __NR_clock_gettime 260
#define __NR_clock_getres 261
#define __NR_statfs64 265
#define __NR_fstatfs64 266
#define __NR_remap_file_pages 267
-diff -NurpP --minimal linux-4.9.217/arch/s390/Kconfig linux-4.9.217-vs2.3.9.12/arch/s390/Kconfig
---- linux-4.9.217/arch/s390/Kconfig 2020-03-27 00:50:44.061073883 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/s390/Kconfig 2018-10-20 04:58:13.000000000 +0000
-@@ -775,6 +775,8 @@ source "fs/Kconfig"
-
- source "arch/s390/Kconfig.debug"
-
-+source "kernel/vserver/Kconfig"
-+
- source "security/Kconfig"
-
- source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/s390/kernel/ptrace.c linux-4.9.217-vs2.3.9.12/arch/s390/kernel/ptrace.c
---- linux-4.9.217/arch/s390/kernel/ptrace.c 2020-03-27 00:50:44.171072152 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/s390/kernel/ptrace.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/s390/kernel/ptrace.c linux-4.9/arch/s390/kernel/ptrace.c
+--- linux-4.9/arch/s390/kernel/ptrace.c 2021-02-24 15:47:30.957300731 +0100
++++ linux-4.9/arch/s390/kernel/ptrace.c 2021-02-24 15:47:45.047741532 +0100
@@ -21,6 +21,7 @@
#include <linux/tracehook.h>
#include <linux/seccomp.h>
#include <trace/syscall.h>
#include <asm/segment.h>
#include <asm/page.h>
-diff -NurpP --minimal linux-4.9.217/arch/s390/kernel/syscalls.S linux-4.9.217-vs2.3.9.12/arch/s390/kernel/syscalls.S
---- linux-4.9.217/arch/s390/kernel/syscalls.S 2020-03-27 00:50:44.171072152 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/s390/kernel/syscalls.S 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/s390/kernel/syscalls.S linux-4.9/arch/s390/kernel/syscalls.S
+--- linux-4.9/arch/s390/kernel/syscalls.S 2021-02-24 15:47:30.960634169 +0100
++++ linux-4.9/arch/s390/kernel/syscalls.S 2021-02-24 15:47:45.047741532 +0100
@@ -271,7 +271,7 @@ SYSCALL(sys_clock_settime,compat_sys_clo
SYSCALL(sys_clock_gettime,compat_sys_clock_gettime) /* 260 */
SYSCALL(sys_clock_getres,compat_sys_clock_getres)
SYSCALL(sys_ni_syscall,compat_sys_s390_fadvise64_64)
SYSCALL(sys_statfs64,compat_sys_statfs64)
SYSCALL(sys_fstatfs64,compat_sys_fstatfs64)
-diff -NurpP --minimal linux-4.9.217/arch/sh/Kconfig linux-4.9.217-vs2.3.9.12/arch/sh/Kconfig
---- linux-4.9.217/arch/sh/Kconfig 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/sh/Kconfig 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/sh/Kconfig linux-4.9/arch/sh/Kconfig
+--- linux-4.9/arch/sh/Kconfig 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/sh/Kconfig 2021-02-24 15:47:45.047741532 +0100
@@ -904,6 +904,8 @@ source "fs/Kconfig"
source "arch/sh/Kconfig.debug"
source "security/Kconfig"
source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/sh/kernel/irq.c linux-4.9.217-vs2.3.9.12/arch/sh/kernel/irq.c
---- linux-4.9.217/arch/sh/kernel/irq.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/sh/kernel/irq.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/sh/kernel/irq.c linux-4.9/arch/sh/kernel/irq.c
+--- linux-4.9/arch/sh/kernel/irq.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/sh/kernel/irq.c 2021-02-24 15:47:45.047741532 +0100
@@ -14,6 +14,7 @@
#include <linux/ftrace.h>
#include <linux/delay.h>
#include <asm/processor.h>
#include <asm/machvec.h>
#include <asm/uaccess.h>
-diff -NurpP --minimal linux-4.9.217/arch/sparc/include/uapi/asm/unistd.h linux-4.9.217-vs2.3.9.12/arch/sparc/include/uapi/asm/unistd.h
---- linux-4.9.217/arch/sparc/include/uapi/asm/unistd.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/sparc/include/uapi/asm/unistd.h 2018-10-20 04:58:13.000000000 +0000
-@@ -332,7 +332,7 @@
- #define __NR_timer_getoverrun 264
- #define __NR_timer_delete 265
- #define __NR_timer_create 266
--/* #define __NR_vserver 267 Reserved for VSERVER */
-+#define __NR_vserver 267
- #define __NR_io_setup 268
- #define __NR_io_destroy 269
- #define __NR_io_submit 270
-diff -NurpP --minimal linux-4.9.217/arch/sparc/Kconfig linux-4.9.217-vs2.3.9.12/arch/sparc/Kconfig
---- linux-4.9.217/arch/sparc/Kconfig 2020-03-27 00:50:44.741063185 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/sparc/Kconfig 2019-02-22 08:37:49.523154806 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/sparc/Kconfig linux-4.9/arch/sparc/Kconfig
+--- linux-4.9/arch/sparc/Kconfig 2021-02-24 15:47:30.967301044 +0100
++++ linux-4.9/arch/sparc/Kconfig 2021-02-24 15:47:45.047741532 +0100
@@ -584,6 +584,8 @@ source "fs/Kconfig"
source "arch/sparc/Kconfig.debug"
source "security/Kconfig"
source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/sparc/kernel/systbls_32.S linux-4.9.217-vs2.3.9.12/arch/sparc/kernel/systbls_32.S
---- linux-4.9.217/arch/sparc/kernel/systbls_32.S 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/sparc/kernel/systbls_32.S 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/sparc/include/uapi/asm/unistd.h linux-4.9/arch/sparc/include/uapi/asm/unistd.h
+--- linux-4.9/arch/sparc/include/uapi/asm/unistd.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/sparc/include/uapi/asm/unistd.h 2021-02-24 15:47:45.047741532 +0100
+@@ -332,7 +332,7 @@
+ #define __NR_timer_getoverrun 264
+ #define __NR_timer_delete 265
+ #define __NR_timer_create 266
+-/* #define __NR_vserver 267 Reserved for VSERVER */
++#define __NR_vserver 267
+ #define __NR_io_setup 268
+ #define __NR_io_destroy 269
+ #define __NR_io_submit 270
+diff -urNp -x '*.orig' linux-4.9/arch/sparc/kernel/systbls_32.S linux-4.9/arch/sparc/kernel/systbls_32.S
+--- linux-4.9/arch/sparc/kernel/systbls_32.S 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/sparc/kernel/systbls_32.S 2021-02-24 15:47:45.047741532 +0100
@@ -70,7 +70,7 @@ sys_call_table:
/*250*/ .long sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_ni_syscall
/*255*/ .long sys_sync_file_range, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep
/*270*/ .long sys_io_submit, sys_io_cancel, sys_io_getevents, sys_mq_open, sys_mq_unlink
/*275*/ .long sys_mq_timedsend, sys_mq_timedreceive, sys_mq_notify, sys_mq_getsetattr, sys_waitid
/*280*/ .long sys_tee, sys_add_key, sys_request_key, sys_keyctl, sys_openat
-diff -NurpP --minimal linux-4.9.217/arch/sparc/kernel/systbls_64.S linux-4.9.217-vs2.3.9.12/arch/sparc/kernel/systbls_64.S
---- linux-4.9.217/arch/sparc/kernel/systbls_64.S 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/sparc/kernel/systbls_64.S 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/sparc/kernel/systbls_64.S linux-4.9/arch/sparc/kernel/systbls_64.S
+--- linux-4.9/arch/sparc/kernel/systbls_64.S 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/sparc/kernel/systbls_64.S 2021-02-24 15:47:45.047741532 +0100
@@ -71,7 +71,7 @@ sys_call_table32:
/*250*/ .word sys_mremap, compat_sys_sysctl, sys_getsid, sys_fdatasync, sys_nis_syscall
.word sys32_sync_file_range, compat_sys_clock_settime, compat_sys_clock_gettime, compat_sys_clock_getres, sys32_clock_nanosleep
/*270*/ .word sys_io_submit, sys_io_cancel, sys_io_getevents, sys_mq_open, sys_mq_unlink
.word sys_mq_timedsend, sys_mq_timedreceive, sys_mq_notify, sys_mq_getsetattr, sys_waitid
/*280*/ .word sys_tee, sys_add_key, sys_request_key, sys_keyctl, sys_openat
-diff -NurpP --minimal linux-4.9.217/arch/um/Kconfig.rest linux-4.9.217-vs2.3.9.12/arch/um/Kconfig.rest
---- linux-4.9.217/arch/um/Kconfig.rest 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/um/Kconfig.rest 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/um/Kconfig.rest linux-4.9/arch/um/Kconfig.rest
+--- linux-4.9/arch/um/Kconfig.rest 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/um/Kconfig.rest 2021-02-24 15:47:45.047741532 +0100
@@ -12,6 +12,8 @@ source "arch/um/Kconfig.net"
source "fs/Kconfig"
source "security/Kconfig"
source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/arch/x86/entry/syscalls/syscall_32.tbl linux-4.9.217-vs2.3.9.12/arch/x86/entry/syscalls/syscall_32.tbl
---- linux-4.9.217/arch/x86/entry/syscalls/syscall_32.tbl 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/x86/entry/syscalls/syscall_32.tbl 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/x86/Kconfig linux-4.9/arch/x86/Kconfig
+--- linux-4.9/arch/x86/Kconfig 2021-02-24 15:47:30.980634795 +0100
++++ linux-4.9/arch/x86/Kconfig 2021-02-24 15:47:45.047741532 +0100
+@@ -2810,6 +2810,8 @@ source "fs/Kconfig"
+
+ source "arch/x86/Kconfig.debug"
+
++source "kernel/vserver/Kconfig"
++
+ source "security/Kconfig"
+
+ source "crypto/Kconfig"
+diff -urNp -x '*.orig' linux-4.9/arch/x86/entry/syscalls/syscall_32.tbl linux-4.9/arch/x86/entry/syscalls/syscall_32.tbl
+--- linux-4.9/arch/x86/entry/syscalls/syscall_32.tbl 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/x86/entry/syscalls/syscall_32.tbl 2021-02-24 15:47:45.047741532 +0100
@@ -279,7 +279,7 @@
270 i386 tgkill sys_tgkill
271 i386 utimes sys_utimes compat_sys_utimes
274 i386 mbind sys_mbind
275 i386 get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy
276 i386 set_mempolicy sys_set_mempolicy
-diff -NurpP --minimal linux-4.9.217/arch/x86/entry/syscalls/syscall_64.tbl linux-4.9.217-vs2.3.9.12/arch/x86/entry/syscalls/syscall_64.tbl
---- linux-4.9.217/arch/x86/entry/syscalls/syscall_64.tbl 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/x86/entry/syscalls/syscall_64.tbl 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/arch/x86/entry/syscalls/syscall_64.tbl linux-4.9/arch/x86/entry/syscalls/syscall_64.tbl
+--- linux-4.9/arch/x86/entry/syscalls/syscall_64.tbl 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/arch/x86/entry/syscalls/syscall_64.tbl 2021-02-24 15:47:45.047741532 +0100
@@ -242,7 +242,7 @@
233 common epoll_ctl sys_epoll_ctl
234 common tgkill sys_tgkill
237 common mbind sys_mbind
238 common set_mempolicy sys_set_mempolicy
239 common get_mempolicy sys_get_mempolicy
-diff -NurpP --minimal linux-4.9.217/arch/x86/Kconfig linux-4.9.217-vs2.3.9.12/arch/x86/Kconfig
---- linux-4.9.217/arch/x86/Kconfig 2020-03-27 00:50:45.651048876 +0000
-+++ linux-4.9.217-vs2.3.9.12/arch/x86/Kconfig 2019-12-25 15:37:44.298553484 +0000
-@@ -2810,6 +2810,8 @@ source "fs/Kconfig"
-
- source "arch/x86/Kconfig.debug"
-
-+source "kernel/vserver/Kconfig"
-+
- source "security/Kconfig"
-
- source "crypto/Kconfig"
-diff -NurpP --minimal linux-4.9.217/block/ioprio.c linux-4.9.217-vs2.3.9.12/block/ioprio.c
---- linux-4.9.217/block/ioprio.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/block/ioprio.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/block/ioprio.c linux-4.9/block/ioprio.c
+--- linux-4.9/block/ioprio.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/block/ioprio.c 2021-02-24 15:47:45.047741532 +0100
@@ -28,6 +28,7 @@
#include <linux/syscalls.h>
#include <linux/security.h>
tmpio = get_task_ioprio(p);
if (tmpio < 0)
continue;
-diff -NurpP --minimal linux-4.9.217/Documentation/vserver/debug.txt linux-4.9.217-vs2.3.9.12/Documentation/vserver/debug.txt
---- linux-4.9.217/Documentation/vserver/debug.txt 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/Documentation/vserver/debug.txt 2018-10-20 04:58:12.000000000 +0000
-@@ -0,0 +1,154 @@
-+
-+debug_cvirt:
-+
-+ 2 4 "vx_map_tgid: %p/%llx: %d -> %d"
-+ "vx_rmap_tgid: %p/%llx: %d -> %d"
-+
-+debug_dlim:
-+
-+ 0 1 "ALLOC (%p,#%d)%c inode (%d)"
-+ "FREE (%p,#%d)%c inode"
-+ 1 2 "ALLOC (%p,#%d)%c %lld bytes (%d)"
-+ "FREE (%p,#%d)%c %lld bytes"
-+ 2 4 "ADJUST: %lld,%lld on %ld,%ld [mult=%d]"
-+ 3 8 "ext3_has_free_blocks(%p): %lu<%lu+1, %c, %u!=%u r=%d"
-+ "ext3_has_free_blocks(%p): free=%lu, root=%lu"
-+ "rcu_free_dl_info(%p)"
-+ 4 10 "alloc_dl_info(%p,%d) = %p"
-+ "dealloc_dl_info(%p)"
-+ "get_dl_info(%p[#%d.%d])"
-+ "put_dl_info(%p[#%d.%d])"
-+ 5 20 "alloc_dl_info(%p,%d)*"
-+ 6 40 "__hash_dl_info: %p[#%d]"
-+ "__unhash_dl_info: %p[#%d]"
-+ 7 80 "locate_dl_info(%p,#%d) = %p"
-+
-+debug_misc:
-+
-+ 0 1 "destroy_dqhash: %p [#0x%08x] c=%d"
-+ "new_dqhash: %p [#0x%08x]"
-+ "vroot[%d]_clr_dev: dev=%p[%lu,%d:%d]"
-+ "vroot[%d]_get_real_bdev: dev=%p[%lu,%d:%d]"
-+ "vroot[%d]_set_dev: dev=%p[%lu,%d:%d]"
-+ "vroot_get_real_bdev not set"
-+ 1 2 "cow_break_link(?%s?)"
-+ "temp copy ?%s?"
-+ 2 4 "dentry_open(new): %p"
-+ "dentry_open(old): %p"
-+ "lookup_create(new): %p"
-+ "old path ?%s?"
-+ "path_lookup(old): %d"
-+ "vfs_create(new): %d"
-+ "vfs_rename: %d"
-+ "vfs_sendfile: %d"
-+ 3 8 "fput(new_file=%p[#%d])"
-+ "fput(old_file=%p[#%d])"
-+ 4 10 "vx_info_kill(%p[#%d],%d,%d) = %d"
-+ "vx_info_kill(%p[#%d],%d,%d)*"
-+ 5 20 "vs_reboot(%p[#%d],%d)"
-+ 6 40 "dropping task %p[#%u,%u] for %p[#%u,%u]"
-+
-+debug_net:
-+
-+ 2 4 "nx_addr_conflict(%p,%p) %d.%d,%d.%d"
-+ 3 8 "inet_bind(%p) %d.%d.%d.%d, %d.%d.%d.%d, %d.%d.%d.%d"
-+ "inet_bind(%p)* %p,%p;%lx %d.%d.%d.%d"
-+ 4 10 "ip_route_connect(%p) %p,%p;%lx"
-+ 5 20 "__addr_in_socket(%p,%d.%d.%d.%d) %p:%d.%d.%d.%d %p;%lx"
-+ 6 40 "sk,egf: %p [#%d] (from %d)"
-+ "sk,egn: %p [#%d] (from %d)"
-+ "sk,req: %p [#%d] (from %d)"
-+ "sk: %p [#%d] (from %d)"
-+ "tw: %p [#%d] (from %d)"
-+ 7 80 "__sock_recvmsg: %p[%p,%p,%p;%d]:%d/%d"
-+ "__sock_sendmsg: %p[%p,%p,%p;%d]:%d/%d"
-+
-+debug_nid:
-+
-+ 0 1 "__lookup_nx_info(#%u): %p[#%u]"
-+ "alloc_nx_info(%d) = %p"
-+ "create_nx_info(%d) (dynamic rejected)"
-+ "create_nx_info(%d) = %p (already there)"
-+ "create_nx_info(%d) = %p (new)"
-+ "dealloc_nx_info(%p)"
-+ 1 2 "alloc_nx_info(%d)*"
-+ "create_nx_info(%d)*"
-+ 2 4 "get_nx_info(%p[#%d.%d])"
-+ "put_nx_info(%p[#%d.%d])"
-+ 3 8 "claim_nx_info(%p[#%d.%d.%d]) %p"
-+ "clr_nx_info(%p[#%d.%d])"
-+ "init_nx_info(%p[#%d.%d])"
-+ "release_nx_info(%p[#%d.%d.%d]) %p"
-+ "set_nx_info(%p[#%d.%d])"
-+ 4 10 "__hash_nx_info: %p[#%d]"
-+ "__nx_dynamic_id: [#%d]"
-+ "__unhash_nx_info: %p[#%d.%d.%d]"
-+ 5 20 "moved task %p into nxi:%p[#%d]"
-+ "nx_migrate_task(%p,%p[#%d.%d.%d])"
-+ "task_get_nx_info(%p)"
-+ 6 40 "nx_clear_persistent(%p[#%d])"
-+
-+debug_quota:
-+
-+ 0 1 "quota_sync_dqh(%p,%d) discard inode %p"
-+ 1 2 "quota_sync_dqh(%p,%d)"
-+ "sync_dquots(%p,%d)"
-+ "sync_dquots_dqh(%p,%d)"
-+ 3 8 "do_quotactl(%p,%d,cmd=%d,id=%d,%p)"
-+
-+debug_switch:
-+
-+ 0 1 "vc: VCMD_%02d_%d[%d], %d,%p [%d,%d,%x,%x]"
-+ 1 2 "vc: VCMD_%02d_%d[%d] = %08lx(%ld) [%d,%d]"
-+ 4 10 "%s: (%s %s) returned %s with %d"
-+
-+debug_tag:
-+
-+ 7 80 "dx_parse_tag(?%s?): %d:#%d"
-+ "dx_propagate_tag(%p[#%lu.%d]): %d,%d"
-+
-+debug_xid:
-+
-+ 0 1 "__lookup_vx_info(#%u): %p[#%u]"
-+ "alloc_vx_info(%d) = %p"
-+ "alloc_vx_info(%d)*"
-+ "create_vx_info(%d) (dynamic rejected)"
-+ "create_vx_info(%d) = %p (already there)"
-+ "create_vx_info(%d) = %p (new)"
-+ "dealloc_vx_info(%p)"
-+ "loc_vx_info(%d) = %p (found)"
-+ "loc_vx_info(%d) = %p (new)"
-+ "loc_vx_info(%d) = %p (not available)"
-+ 1 2 "create_vx_info(%d)*"
-+ "loc_vx_info(%d)*"
-+ 2 4 "get_vx_info(%p[#%d.%d])"
-+ "put_vx_info(%p[#%d.%d])"
-+ 3 8 "claim_vx_info(%p[#%d.%d.%d]) %p"
-+ "clr_vx_info(%p[#%d.%d])"
-+ "init_vx_info(%p[#%d.%d])"
-+ "release_vx_info(%p[#%d.%d.%d]) %p"
-+ "set_vx_info(%p[#%d.%d])"
-+ 4 10 "__hash_vx_info: %p[#%d]"
-+ "__unhash_vx_info: %p[#%d.%d.%d]"
-+ "__vx_dynamic_id: [#%d]"
-+ 5 20 "enter_vx_info(%p[#%d],%p) %p[#%d,%p]"
-+ "leave_vx_info(%p[#%d,%p]) %p[#%d,%p]"
-+ "moved task %p into vxi:%p[#%d]"
-+ "task_get_vx_info(%p)"
-+ "vx_migrate_task(%p,%p[#%d.%d])"
-+ 6 40 "vx_clear_persistent(%p[#%d])"
-+ "vx_exit_init(%p[#%d],%p[#%d,%d,%d])"
-+ "vx_set_init(%p[#%d],%p[#%d,%d,%d])"
-+ "vx_set_persistent(%p[#%d])"
-+ "vx_set_reaper(%p[#%d],%p[#%d,%d])"
-+ 7 80 "vx_child_reaper(%p[#%u,%u]) = %p[#%u,%u]"
-+
-+
-+debug_limit:
-+
-+ n 2^n "vx_acc_cres[%5d,%s,%2d]: %5d%s"
-+ "vx_cres_avail[%5d,%s,%2d]: %5ld > %5d + %5d"
-+
-+ m 2^m "vx_acc_page[%5d,%s,%2d]: %5d%s"
-+ "vx_acc_pages[%5d,%s,%2d]: %5d += %5d"
-+ "vx_pages_avail[%5d,%s,%2d]: %5ld > %5d + %5d"
-diff -NurpP --minimal linux-4.9.217/drivers/block/Kconfig linux-4.9.217-vs2.3.9.12/drivers/block/Kconfig
---- linux-4.9.217/drivers/block/Kconfig 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/block/Kconfig 2018-10-20 04:58:13.000000000 +0000
-@@ -273,6 +273,13 @@ config BLK_DEV_CRYPTOLOOP
-
- source "drivers/block/drbd/Kconfig"
-
-+config BLK_DEV_VROOT
-+ tristate "Virtual Root device support"
-+ depends on QUOTACTL
-+ ---help---
-+ Saying Y here will allow you to use quota/fs ioctls on a shared
-+ partition within a virtual server without compromising security.
+diff -urNp -x '*.orig' linux-4.9/drivers/block/Kconfig linux-4.9/drivers/block/Kconfig
+--- linux-4.9/drivers/block/Kconfig 2021-02-24 15:47:31.113972298 +0100
++++ linux-4.9/drivers/block/Kconfig 2021-02-24 15:47:45.047741532 +0100
+@@ -273,6 +273,13 @@ config BLK_DEV_CRYPTOLOOP
+
+ source "drivers/block/drbd/Kconfig"
+
++config BLK_DEV_VROOT
++ tristate "Virtual Root device support"
++ depends on QUOTACTL
++ ---help---
++ Saying Y here will allow you to use quota/fs ioctls on a shared
++ partition within a virtual server without compromising security.
+
config BLK_DEV_NBD
tristate "Network block device support"
depends on NET
-diff -NurpP --minimal linux-4.9.217/drivers/block/loop.c linux-4.9.217-vs2.3.9.12/drivers/block/loop.c
---- linux-4.9.217/drivers/block/loop.c 2020-03-27 00:50:48.141009707 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/block/loop.c 2019-12-25 15:37:45.418535409 +0000
+diff -urNp -x '*.orig' linux-4.9/drivers/block/Makefile linux-4.9/drivers/block/Makefile
+--- linux-4.9/drivers/block/Makefile 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/drivers/block/Makefile 2021-02-24 15:47:45.051074969 +0100
+@@ -31,6 +31,7 @@ obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
+
+ obj-$(CONFIG_BLK_DEV_SX8) += sx8.o
+ obj-$(CONFIG_BLK_DEV_HD) += hd.o
++obj-$(CONFIG_BLK_DEV_VROOT) += vroot.o
+
+ obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o
+ obj-$(CONFIG_XEN_BLKDEV_BACKEND) += xen-blkback/
+diff -urNp -x '*.orig' linux-4.9/drivers/block/loop.c linux-4.9/drivers/block/loop.c
+--- linux-4.9/drivers/block/loop.c 2021-02-24 15:47:31.117305737 +0100
++++ linux-4.9/drivers/block/loop.c 2021-02-24 15:47:45.051074969 +0100
@@ -76,6 +76,7 @@
#include <linux/miscdevice.h>
#include <linux/falloc.h>
atomic_inc(&lo->lo_refcnt);
out:
mutex_unlock(&loop_index_mutex);
-diff -NurpP --minimal linux-4.9.217/drivers/block/loop.h linux-4.9.217-vs2.3.9.12/drivers/block/loop.h
---- linux-4.9.217/drivers/block/loop.h 2020-03-27 00:50:48.151009551 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/block/loop.h 2019-10-05 14:58:39.110412393 +0000
+diff -urNp -x '*.orig' linux-4.9/drivers/block/loop.h linux-4.9/drivers/block/loop.h
+--- linux-4.9/drivers/block/loop.h 2021-02-24 15:47:31.117305737 +0100
++++ linux-4.9/drivers/block/loop.h 2021-02-24 15:47:45.051074969 +0100
@@ -43,6 +43,7 @@ struct loop_device {
struct loop_func_table *lo_encryption;
__u32 lo_init[2];
int (*ioctl)(struct loop_device *, int cmd,
unsigned long arg);
-diff -NurpP --minimal linux-4.9.217/drivers/block/Makefile linux-4.9.217-vs2.3.9.12/drivers/block/Makefile
---- linux-4.9.217/drivers/block/Makefile 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/block/Makefile 2018-10-20 04:58:13.000000000 +0000
-@@ -31,6 +31,7 @@ obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
-
- obj-$(CONFIG_BLK_DEV_SX8) += sx8.o
- obj-$(CONFIG_BLK_DEV_HD) += hd.o
-+obj-$(CONFIG_BLK_DEV_VROOT) += vroot.o
-
- obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o
- obj-$(CONFIG_XEN_BLKDEV_BACKEND) += xen-blkback/
-diff -NurpP --minimal linux-4.9.217/drivers/block/vroot.c linux-4.9.217-vs2.3.9.12/drivers/block/vroot.c
---- linux-4.9.217/drivers/block/vroot.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/block/vroot.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/drivers/block/vroot.c linux-4.9/drivers/block/vroot.c
+--- linux-4.9/drivers/block/vroot.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/drivers/block/vroot.c 2021-02-24 15:47:45.051074969 +0100
@@ -0,0 +1,291 @@
+/*
+ * linux/drivers/block/vroot.c
+
+#endif
+
-diff -NurpP --minimal linux-4.9.217/drivers/md/dm.c linux-4.9.217-vs2.3.9.12/drivers/md/dm.c
---- linux-4.9.217/drivers/md/dm.c 2020-03-27 00:50:57.810857303 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/md/dm.c 2020-04-01 09:40:21.845577638 +0000
-@@ -22,6 +22,7 @@
- #include <linux/wait.h>
- #include <linux/pr.h>
- #include <linux/vmalloc.h>
-+#include <linux/vs_base.h>
-
- #define DM_MSG_PREFIX "core"
-
-@@ -300,6 +301,7 @@ int dm_deleting_md(struct mapped_device
- static int dm_blk_open(struct block_device *bdev, fmode_t mode)
- {
- struct mapped_device *md;
-+ int ret = -ENXIO;
-
- spin_lock(&_minor_lock);
-
-@@ -308,17 +310,19 @@ static int dm_blk_open(struct block_devi
- goto out;
-
- if (test_bit(DMF_FREEING, &md->flags) ||
-- dm_deleting_md(md)) {
-- md = NULL;
-+ dm_deleting_md(md))
-+ goto out;
-+
-+ ret = -EACCES;
-+ if (!vx_check(md->xid, VS_IDENT|VS_HOSTID))
- goto out;
-- }
-
- dm_get(md);
- atomic_inc(&md->open_count);
-+ ret = 0;
- out:
- spin_unlock(&_minor_lock);
--
-- return md ? 0 : -ENXIO;
-+ return ret;
- }
-
- static void dm_blk_close(struct gendisk *disk, fmode_t mode)
-@@ -744,6 +748,14 @@ int dm_set_geometry(struct mapped_device
- return 0;
- }
-
-+/*
-+ * Get the xid associated with a dm device
-+ */
-+vxid_t dm_get_xid(struct mapped_device *md)
-+{
-+ return md->xid;
-+}
-+
- /*-----------------------------------------------------------------
- * CRUD START:
- * A more elegant soln is in the works that uses the queue
-@@ -1549,6 +1561,7 @@ static struct mapped_device *alloc_dev(i
- INIT_LIST_HEAD(&md->uevent_list);
- INIT_LIST_HEAD(&md->table_devices);
- spin_lock_init(&md->uevent_lock);
-+ md->xid = vx_current_xid();
-
- md->queue = blk_alloc_queue_node(GFP_KERNEL, numa_node_id);
- if (!md->queue)
-diff -NurpP --minimal linux-4.9.217/drivers/md/dm-core.h linux-4.9.217-vs2.3.9.12/drivers/md/dm-core.h
---- linux-4.9.217/drivers/md/dm-core.h 2020-03-27 00:50:57.740858403 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/md/dm-core.h 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/drivers/md/dm-core.h linux-4.9/drivers/md/dm-core.h
+--- linux-4.9/drivers/md/dm-core.h 2021-02-24 15:47:31.450649498 +0100
++++ linux-4.9/drivers/md/dm-core.h 2021-02-24 15:47:45.051074969 +0100
@@ -52,6 +52,7 @@ struct mapped_device {
atomic_t holders;
struct dm_target *immutable_target;
struct target_type *immutable_target_type;
-diff -NurpP --minimal linux-4.9.217/drivers/md/dm.h linux-4.9.217-vs2.3.9.12/drivers/md/dm.h
---- linux-4.9.217/drivers/md/dm.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/md/dm.h 2018-10-20 04:58:13.000000000 +0000
-@@ -45,6 +45,8 @@ struct dm_dev_internal {
- struct dm_table;
- struct dm_md_mempools;
-
-+vxid_t dm_get_xid(struct mapped_device *md);
-+
- /*-----------------------------------------------------------------
- * Internal table functions.
- *---------------------------------------------------------------*/
-diff -NurpP --minimal linux-4.9.217/drivers/md/dm-ioctl.c linux-4.9.217-vs2.3.9.12/drivers/md/dm-ioctl.c
---- linux-4.9.217/drivers/md/dm-ioctl.c 2020-03-27 00:50:57.760858091 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/md/dm-ioctl.c 2019-02-22 08:37:51.173126752 +0000
+diff -urNp -x '*.orig' linux-4.9/drivers/md/dm-ioctl.c linux-4.9/drivers/md/dm-ioctl.c
+--- linux-4.9/drivers/md/dm-ioctl.c 2021-02-24 15:47:31.450649498 +0100
++++ linux-4.9/drivers/md/dm-ioctl.c 2021-02-24 15:47:45.051074969 +0100
@@ -16,6 +16,7 @@
#include <linux/dm-ioctl.h>
#include <linux/hdreg.h>
if (old_nl)
old_nl->next = (uint32_t) ((void *) nl -
(void *) old_nl);
-@@ -1799,8 +1811,8 @@ static int ctl_ioctl(uint command, struc
+@@ -1800,8 +1812,8 @@ static int ctl_ioctl(uint command, struc
size_t input_param_size;
struct dm_ioctl param_kernel;
return -EACCES;
if (_IOC_TYPE(command) != DM_IOCTL)
-diff -NurpP --minimal linux-4.9.217/drivers/net/tun.c linux-4.9.217-vs2.3.9.12/drivers/net/tun.c
---- linux-4.9.217/drivers/net/tun.c 2020-03-27 00:51:04.880745870 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/net/tun.c 2020-04-01 09:40:23.915543339 +0000
-@@ -65,6 +65,7 @@
- #include <linux/nsproxy.h>
- #include <linux/virtio_net.h>
- #include <linux/rcupdate.h>
-+#include <linux/vs_network.h>
- #include <net/net_namespace.h>
- #include <net/netns/generic.h>
- #include <net/rtnetlink.h>
-@@ -194,6 +195,7 @@ struct tun_struct {
- unsigned int flags;
- kuid_t owner;
- kgid_t group;
-+ vnid_t nid;
+diff -urNp -x '*.orig' linux-4.9/drivers/md/dm.c linux-4.9/drivers/md/dm.c
+--- linux-4.9/drivers/md/dm.c 2021-02-24 15:47:31.457316373 +0100
++++ linux-4.9/drivers/md/dm.c 2021-02-24 15:47:45.051074969 +0100
+@@ -22,6 +22,7 @@
+ #include <linux/wait.h>
+ #include <linux/pr.h>
+ #include <linux/vmalloc.h>
++#include <linux/vs_base.h>
- struct net_device *dev;
- netdev_features_t set_features;
-@@ -490,6 +492,7 @@ static inline bool tun_not_capable(struc
- return ((uid_valid(tun->owner) && !uid_eq(cred->euid, tun->owner)) ||
- (gid_valid(tun->group) && !in_egroup_p(tun->group))) &&
- !ns_capable(net->user_ns, CAP_NET_ADMIN);
-+ /* !cap_raised(current_cap(), CAP_NET_ADMIN) */
- }
+ #define DM_MSG_PREFIX "core"
- static void tun_set_real_num_queues(struct tun_struct *tun)
-@@ -1569,6 +1572,7 @@ static void tun_setup(struct net_device
+@@ -300,6 +301,7 @@ int dm_deleting_md(struct mapped_device
+ static int dm_blk_open(struct block_device *bdev, fmode_t mode)
+ {
+ struct mapped_device *md;
++ int ret = -ENXIO;
- tun->owner = INVALID_UID;
+ spin_lock(&_minor_lock);
+
+@@ -308,17 +310,19 @@ static int dm_blk_open(struct block_devi
+ goto out;
+
+ if (test_bit(DMF_FREEING, &md->flags) ||
+- dm_deleting_md(md)) {
+- md = NULL;
++ dm_deleting_md(md))
++ goto out;
++
++ ret = -EACCES;
++ if (!vx_check(md->xid, VS_IDENT|VS_HOSTID))
+ goto out;
+- }
+
+ dm_get(md);
+ atomic_inc(&md->open_count);
++ ret = 0;
+ out:
+ spin_unlock(&_minor_lock);
+-
+- return md ? 0 : -ENXIO;
++ return ret;
+ }
+
+ static void dm_blk_close(struct gendisk *disk, fmode_t mode)
+@@ -744,6 +748,14 @@ int dm_set_geometry(struct mapped_device
+ return 0;
+ }
+
++/*
++ * Get the xid associated with a dm device
++ */
++vxid_t dm_get_xid(struct mapped_device *md)
++{
++ return md->xid;
++}
++
+ /*-----------------------------------------------------------------
+ * CRUD START:
+ * A more elegant soln is in the works that uses the queue
+@@ -1549,6 +1561,7 @@ static struct mapped_device *alloc_dev(i
+ INIT_LIST_HEAD(&md->uevent_list);
+ INIT_LIST_HEAD(&md->table_devices);
+ spin_lock_init(&md->uevent_lock);
++ md->xid = vx_current_xid();
+
+ md->queue = blk_alloc_queue_node(GFP_KERNEL, numa_node_id);
+ if (!md->queue)
+diff -urNp -x '*.orig' linux-4.9/drivers/md/dm.h linux-4.9/drivers/md/dm.h
+--- linux-4.9/drivers/md/dm.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/drivers/md/dm.h 2021-02-24 15:47:45.051074969 +0100
+@@ -45,6 +45,8 @@ struct dm_dev_internal {
+ struct dm_table;
+ struct dm_md_mempools;
+
++vxid_t dm_get_xid(struct mapped_device *md);
++
+ /*-----------------------------------------------------------------
+ * Internal table functions.
+ *---------------------------------------------------------------*/
+diff -urNp -x '*.orig' linux-4.9/drivers/net/tun.c linux-4.9/drivers/net/tun.c
+--- linux-4.9/drivers/net/tun.c 2021-02-24 15:47:31.743992008 +0100
++++ linux-4.9/drivers/net/tun.c 2021-02-24 15:47:45.051074969 +0100
+@@ -65,6 +65,7 @@
+ #include <linux/nsproxy.h>
+ #include <linux/virtio_net.h>
+ #include <linux/rcupdate.h>
++#include <linux/vs_network.h>
+ #include <net/net_namespace.h>
+ #include <net/netns/generic.h>
+ #include <net/rtnetlink.h>
+@@ -194,6 +195,7 @@ struct tun_struct {
+ unsigned int flags;
+ kuid_t owner;
+ kgid_t group;
++ vnid_t nid;
+
+ struct net_device *dev;
+ netdev_features_t set_features;
+@@ -490,6 +492,7 @@ static inline bool tun_not_capable(struc
+ return ((uid_valid(tun->owner) && !uid_eq(cred->euid, tun->owner)) ||
+ (gid_valid(tun->group) && !in_egroup_p(tun->group))) &&
+ !ns_capable(net->user_ns, CAP_NET_ADMIN);
++ /* !cap_raised(current_cap(), CAP_NET_ADMIN) */
+ }
+
+ static void tun_set_real_num_queues(struct tun_struct *tun)
+@@ -1569,6 +1572,7 @@ static void tun_setup(struct net_device
+
+ tun->owner = INVALID_UID;
tun->group = INVALID_GID;
+ tun->nid = nx_current_nid();
case TUNSETLINK:
/* Only allow setting the type when the interface is down */
if (tun->dev->flags & IFF_UP) {
-diff -NurpP --minimal linux-4.9.217/drivers/scsi/cxgbi/libcxgbi.c linux-4.9.217-vs2.3.9.12/drivers/scsi/cxgbi/libcxgbi.c
---- linux-4.9.217/drivers/scsi/cxgbi/libcxgbi.c 2020-03-27 00:51:09.110679202 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/scsi/cxgbi/libcxgbi.c 2020-04-01 09:40:26.325503408 +0000
+diff -urNp -x '*.orig' linux-4.9/drivers/scsi/cxgbi/libcxgbi.c linux-4.9/drivers/scsi/cxgbi/libcxgbi.c
+--- linux-4.9/drivers/scsi/cxgbi/libcxgbi.c 2021-02-24 15:47:31.947331702 +0100
++++ linux-4.9/drivers/scsi/cxgbi/libcxgbi.c 2021-02-24 15:47:45.051074969 +0100
@@ -777,7 +777,8 @@ static struct cxgbi_sock *cxgbi_check_ro
struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt);
if (err) {
pr_info("failed to get source address to reach %pI6\n",
&daddr6->sin6_addr);
-diff -NurpP --minimal linux-4.9.217/drivers/tty/sysrq.c linux-4.9.217-vs2.3.9.12/drivers/tty/sysrq.c
---- linux-4.9.217/drivers/tty/sysrq.c 2020-03-27 00:51:14.160599611 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/tty/sysrq.c 2020-04-01 09:40:27.395485676 +0000
+diff -urNp -x '*.orig' linux-4.9/drivers/tty/sysrq.c linux-4.9/drivers/tty/sysrq.c
+--- linux-4.9/drivers/tty/sysrq.c 2021-02-24 15:47:32.084002644 +0100
++++ linux-4.9/drivers/tty/sysrq.c 2021-02-24 15:47:45.051074969 +0100
@@ -47,6 +47,7 @@
#include <linux/syscalls.h>
#include <linux/of.h>
else
retval = -1;
return retval;
-diff -NurpP --minimal linux-4.9.217/drivers/tty/tty_io.c linux-4.9.217-vs2.3.9.12/drivers/tty/tty_io.c
---- linux-4.9.217/drivers/tty/tty_io.c 2020-03-27 00:51:14.190599135 +0000
-+++ linux-4.9.217-vs2.3.9.12/drivers/tty/tty_io.c 2019-10-05 14:58:43.980334565 +0000
+diff -urNp -x '*.orig' linux-4.9/drivers/tty/tty_io.c linux-4.9/drivers/tty/tty_io.c
+--- linux-4.9/drivers/tty/tty_io.c 2021-02-24 15:47:32.084002644 +0100
++++ linux-4.9/drivers/tty/tty_io.c 2021-02-24 15:47:45.054408406 +0100
@@ -104,6 +104,7 @@
#include <linux/kmod.h>
#undef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP
-@@ -2318,7 +2319,8 @@ static int tiocsti(struct tty_struct *tt
+@@ -2321,7 +2322,8 @@ static int tiocsti(struct tty_struct *tt
char ch, mbz = 0;
struct tty_ldisc *ld;
if (get_user(ch, p))
return -EFAULT;
@@ -2634,6 +2636,7 @@ static int tiocspgrp(struct tty_struct *
- return -ENOTTY;
+
if (get_user(pgrp_nr, p))
return -EFAULT;
+ pgrp_nr = vx_rmap_pid(pgrp_nr);
if (pgrp_nr < 0)
return -EINVAL;
- rcu_read_lock();
-diff -NurpP --minimal linux-4.9.217/fs/attr.c linux-4.9.217-vs2.3.9.12/fs/attr.c
---- linux-4.9.217/fs/attr.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/attr.c 2018-10-20 04:58:13.000000000 +0000
+
+diff -urNp -x '*.orig' linux-4.9/fs/attr.c linux-4.9/fs/attr.c
+--- linux-4.9/fs/attr.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/attr.c 2021-02-24 15:47:45.054408406 +0100
@@ -15,6 +15,9 @@
#include <linux/security.h>
#include <linux/evm.h>
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
return -EPERM;
}
-diff -NurpP --minimal linux-4.9.217/fs/block_dev.c linux-4.9.217-vs2.3.9.12/fs/block_dev.c
---- linux-4.9.217/fs/block_dev.c 2020-03-27 00:51:17.200551698 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/block_dev.c 2019-02-22 08:37:54.463070814 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/block_dev.c linux-4.9/fs/block_dev.c
+--- linux-4.9/fs/block_dev.c 2021-02-24 15:47:32.207339836 +0100
++++ linux-4.9/fs/block_dev.c 2021-02-24 15:47:45.054408406 +0100
@@ -31,6 +31,7 @@
#include <linux/dax.h>
#include <linux/badblocks.h>
if (bdev) {
spin_lock(&bdev_lock);
if (!inode->i_bdev) {
-diff -NurpP --minimal linux-4.9.217/fs/btrfs/ctree.h linux-4.9.217-vs2.3.9.12/fs/btrfs/ctree.h
---- linux-4.9.217/fs/btrfs/ctree.h 2020-03-27 00:51:17.270550595 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/btrfs/ctree.h 2020-04-01 09:40:28.245471591 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/btrfs/ctree.h linux-4.9/fs/btrfs/ctree.h
+--- linux-4.9/fs/btrfs/ctree.h 2021-02-24 15:47:32.210673274 +0100
++++ linux-4.9/fs/btrfs/ctree.h 2021-02-24 15:47:45.054408406 +0100
@@ -1319,6 +1319,8 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(c
#define BTRFS_DEFAULT_COMMIT_INTERVAL (30)
#define BTRFS_DEFAULT_MAX_INLINE (2048)
int btrfs_defrag_file(struct inode *inode, struct file *file,
struct btrfs_ioctl_defrag_range_args *range,
u64 newer_than, unsigned long max_pages);
-diff -NurpP --minimal linux-4.9.217/fs/btrfs/disk-io.c linux-4.9.217-vs2.3.9.12/fs/btrfs/disk-io.c
---- linux-4.9.217/fs/btrfs/disk-io.c 2020-03-27 00:51:17.290550279 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/btrfs/disk-io.c 2020-04-01 09:40:28.245471591 +0000
-@@ -2705,6 +2705,9 @@ int open_ctree(struct super_block *sb,
+diff -urNp -x '*.orig' linux-4.9/fs/btrfs/disk-io.c linux-4.9/fs/btrfs/disk-io.c
+--- linux-4.9/fs/btrfs/disk-io.c 2021-02-24 15:47:32.210673274 +0100
++++ linux-4.9/fs/btrfs/disk-io.c 2021-02-24 15:47:45.054408406 +0100
+@@ -2712,6 +2712,9 @@ int open_ctree(struct super_block *sb,
goto fail_alloc;
}
features = btrfs_super_incompat_flags(disk_super) &
~BTRFS_FEATURE_INCOMPAT_SUPP;
if (features) {
-diff -NurpP --minimal linux-4.9.217/fs/btrfs/inode.c linux-4.9.217-vs2.3.9.12/fs/btrfs/inode.c
---- linux-4.9.217/fs/btrfs/inode.c 2020-03-27 00:51:17.350549330 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/btrfs/inode.c 2020-04-01 09:40:28.265471261 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/btrfs/inode.c linux-4.9/fs/btrfs/inode.c
+--- linux-4.9/fs/btrfs/inode.c 2021-02-24 15:47:32.217340149 +0100
++++ linux-4.9/fs/btrfs/inode.c 2021-02-24 15:47:45.057741845 +0100
@@ -43,6 +43,7 @@
#include <linux/blkdev.h>
#include <linux/posix_acl_xattr.h>
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
-@@ -3701,6 +3702,9 @@ static int btrfs_read_locked_inode(struc
+@@ -3717,6 +3718,9 @@ static int btrfs_read_locked_inode(struc
unsigned long ptr;
int maybe_acls;
u32 rdev;
int ret;
bool filled = false;
int first_xattr_slot;
-@@ -3733,8 +3737,14 @@ static int btrfs_read_locked_inode(struc
+@@ -3749,8 +3753,14 @@ static int btrfs_read_locked_inode(struc
struct btrfs_inode_item);
inode->i_mode = btrfs_inode_mode(leaf, inode_item);
set_nlink(inode, btrfs_inode_nlink(leaf, inode_item));
btrfs_i_size_write(inode, btrfs_inode_size(leaf, inode_item));
inode->i_atime.tv_sec = btrfs_timespec_sec(leaf, &inode_item->atime);
-@@ -3889,11 +3899,18 @@ static void fill_inode_item(struct btrfs
+@@ -3905,11 +3915,18 @@ static void fill_inode_item(struct btrfs
struct inode *inode)
{
struct btrfs_map_token token;
btrfs_set_token_inode_size(leaf, item, BTRFS_I(inode)->disk_i_size,
&token);
btrfs_set_token_inode_mode(leaf, item, inode->i_mode, &token);
-@@ -10646,6 +10663,7 @@ static const struct inode_operations btr
+@@ -10687,6 +10704,7 @@ static const struct inode_operations btr
.mknod = btrfs_mknod,
.listxattr = btrfs_listxattr,
.permission = btrfs_permission,
.get_acl = btrfs_get_acl,
.set_acl = btrfs_set_acl,
.update_time = btrfs_update_time,
-@@ -10654,6 +10672,7 @@ static const struct inode_operations btr
+@@ -10695,6 +10713,7 @@ static const struct inode_operations btr
static const struct inode_operations btrfs_dir_ro_inode_operations = {
.lookup = btrfs_lookup,
.permission = btrfs_permission,
.update_time = btrfs_update_time,
};
-@@ -10719,6 +10738,7 @@ static const struct inode_operations btr
+@@ -10760,6 +10779,7 @@ static const struct inode_operations btr
.listxattr = btrfs_listxattr,
.permission = btrfs_permission,
.fiemap = btrfs_fiemap,
.get_acl = btrfs_get_acl,
.set_acl = btrfs_set_acl,
.update_time = btrfs_update_time,
-diff -NurpP --minimal linux-4.9.217/fs/btrfs/ioctl.c linux-4.9.217-vs2.3.9.12/fs/btrfs/ioctl.c
---- linux-4.9.217/fs/btrfs/ioctl.c 2020-03-27 00:51:17.350549330 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/btrfs/ioctl.c 2020-04-01 09:40:28.265471261 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/btrfs/ioctl.c linux-4.9/fs/btrfs/ioctl.c
+--- linux-4.9/fs/btrfs/ioctl.c 2021-02-24 15:47:32.217340149 +0100
++++ linux-4.9/fs/btrfs/ioctl.c 2021-02-24 15:47:45.057741845 +0100
@@ -110,10 +110,13 @@ static unsigned int btrfs_flags_to_ioctl
{
unsigned int iflags = 0;
if (flags & FS_APPEND_FL)
ip->flags |= BTRFS_INODE_APPEND;
else
-diff -NurpP --minimal linux-4.9.217/fs/btrfs/super.c linux-4.9.217-vs2.3.9.12/fs/btrfs/super.c
---- linux-4.9.217/fs/btrfs/super.c 2020-03-27 00:51:17.390548703 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/btrfs/super.c 2020-04-01 09:40:28.265471261 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/btrfs/super.c linux-4.9/fs/btrfs/super.c
+--- linux-4.9/fs/btrfs/super.c 2021-02-24 15:47:32.224007024 +0100
++++ linux-4.9/fs/btrfs/super.c 2021-02-24 15:47:45.057741845 +0100
@@ -327,7 +327,7 @@ enum {
#ifdef CONFIG_BTRFS_DEBUG
Opt_fragment_data, Opt_fragment_metadata, Opt_fragment_all,
case Opt_err:
btrfs_info(root->fs_info,
"unrecognized mount option '%s'", p);
-@@ -1754,6 +1773,12 @@ static int btrfs_remount(struct super_bl
+@@ -1760,6 +1779,12 @@ static int btrfs_remount(struct super_bl
btrfs_resize_thread_pool(fs_info,
fs_info->thread_pool_size, old_thread_pool_size);
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
goto out;
-diff -NurpP --minimal linux-4.9.217/fs/char_dev.c linux-4.9.217-vs2.3.9.12/fs/char_dev.c
---- linux-4.9.217/fs/char_dev.c 2020-03-27 00:51:17.560546024 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/char_dev.c 2020-04-01 09:40:28.315470432 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/char_dev.c linux-4.9/fs/char_dev.c
+--- linux-4.9/fs/char_dev.c 2021-02-24 15:47:32.230673899 +0100
++++ linux-4.9/fs/char_dev.c 2021-02-24 15:47:45.057741845 +0100
@@ -21,6 +21,8 @@
#include <linux/mutex.h>
#include <linux/backing-dev.h>
if (!kobj)
return -ENXIO;
new = container_of(kobj, struct cdev, kobj);
-diff -NurpP --minimal linux-4.9.217/fs/dcache.c linux-4.9.217-vs2.3.9.12/fs/dcache.c
---- linux-4.9.217/fs/dcache.c 2020-03-27 00:51:17.880540977 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/dcache.c 2019-10-05 14:58:45.150315864 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/dcache.c linux-4.9/fs/dcache.c
+--- linux-4.9/fs/dcache.c 2021-02-24 15:47:32.247341088 +0100
++++ linux-4.9/fs/dcache.c 2021-02-24 15:47:45.061075282 +0100
@@ -39,6 +39,7 @@
#include <linux/ratelimit.h>
#include <linux/list_lru.h>
}
}
return D_WALK_CONTINUE;
-diff -NurpP --minimal linux-4.9.217/fs/devpts/inode.c linux-4.9.217-vs2.3.9.12/fs/devpts/inode.c
---- linux-4.9.217/fs/devpts/inode.c 2020-03-27 00:51:18.080537825 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/devpts/inode.c 2019-10-05 14:58:45.150315864 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/devpts/inode.c linux-4.9/fs/devpts/inode.c
+--- linux-4.9/fs/devpts/inode.c 2021-02-24 15:47:32.247341088 +0100
++++ linux-4.9/fs/devpts/inode.c 2021-02-24 15:47:45.061075282 +0100
@@ -27,6 +27,7 @@
#include <linux/parser.h>
#include <linux/fsnotify.h>
sprintf(s, "%d", index);
-diff -NurpP --minimal linux-4.9.217/fs/ext2/balloc.c linux-4.9.217-vs2.3.9.12/fs/ext2/balloc.c
---- linux-4.9.217/fs/ext2/balloc.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext2/balloc.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext2/balloc.c linux-4.9/fs/ext2/balloc.c
+--- linux-4.9/fs/ext2/balloc.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/ext2/balloc.c 2021-02-24 15:47:45.061075282 +0100
@@ -693,7 +693,6 @@ ext2_try_to_allocate(struct super_block
start = 0;
end = EXT2_BLOCKS_PER_GROUP(sb);
BUG_ON(start > EXT2_BLOCKS_PER_GROUP(sb));
repeat:
-diff -NurpP --minimal linux-4.9.217/fs/ext2/ext2.h linux-4.9.217-vs2.3.9.12/fs/ext2/ext2.h
---- linux-4.9.217/fs/ext2/ext2.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext2/ext2.h 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext2/ext2.h linux-4.9/fs/ext2/ext2.h
+--- linux-4.9/fs/ext2/ext2.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/ext2/ext2.h 2021-02-24 15:47:45.061075282 +0100
@@ -247,8 +247,12 @@ struct ext2_group_desc
#define EXT2_NOTAIL_FL FS_NOTAIL_FL /* file tail should not be merged */
#define EXT2_DIRSYNC_FL FS_DIRSYNC_FL /* dirsync behaviour (directories only) */
/* ioctl.c */
extern long ext2_ioctl(struct file *, unsigned int, unsigned long);
-diff -NurpP --minimal linux-4.9.217/fs/ext2/file.c linux-4.9.217-vs2.3.9.12/fs/ext2/file.c
---- linux-4.9.217/fs/ext2/file.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext2/file.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext2/file.c linux-4.9/fs/ext2/file.c
+--- linux-4.9/fs/ext2/file.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/ext2/file.c 2021-02-24 15:47:45.061075282 +0100
@@ -247,4 +247,5 @@ const struct inode_operations ext2_file_
.get_acl = ext2_get_acl,
.set_acl = ext2_set_acl,
.fiemap = ext2_fiemap,
+ .sync_flags = ext2_sync_flags,
};
-diff -NurpP --minimal linux-4.9.217/fs/ext2/ialloc.c linux-4.9.217-vs2.3.9.12/fs/ext2/ialloc.c
---- linux-4.9.217/fs/ext2/ialloc.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext2/ialloc.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext2/ialloc.c linux-4.9/fs/ext2/ialloc.c
+--- linux-4.9/fs/ext2/ialloc.c 2021-02-24 15:47:32.254007963 +0100
++++ linux-4.9/fs/ext2/ialloc.c 2021-02-24 15:47:45.061075282 +0100
@@ -17,6 +17,7 @@
#include <linux/backing-dev.h>
#include <linux/buffer_head.h>
#include "ext2.h"
#include "xattr.h"
#include "acl.h"
-@@ -551,6 +552,7 @@ got:
+@@ -552,6 +553,7 @@ got:
inode->i_mode = mode;
inode->i_uid = current_fsuid();
inode->i_gid = dir->i_gid;
} else
inode_init_owner(inode, dir, mode);
-diff -NurpP --minimal linux-4.9.217/fs/ext2/inode.c linux-4.9.217-vs2.3.9.12/fs/ext2/inode.c
---- linux-4.9.217/fs/ext2/inode.c 2020-03-27 00:51:18.820526161 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext2/inode.c 2019-12-25 15:37:51.438438255 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext2/inode.c linux-4.9/fs/ext2/inode.c
+--- linux-4.9/fs/ext2/inode.c 2021-02-24 15:47:32.254007963 +0100
++++ linux-4.9/fs/ext2/inode.c 2021-02-24 15:47:45.061075282 +0100
@@ -35,6 +35,7 @@
#include <linux/iomap.h>
#include <linux/namei.h>
error = dquot_transfer(inode, iattr);
if (error)
return error;
-diff -NurpP --minimal linux-4.9.217/fs/ext2/ioctl.c linux-4.9.217-vs2.3.9.12/fs/ext2/ioctl.c
---- linux-4.9.217/fs/ext2/ioctl.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext2/ioctl.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext2/ioctl.c linux-4.9/fs/ext2/ioctl.c
+--- linux-4.9/fs/ext2/ioctl.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/ext2/ioctl.c 2021-02-24 15:47:45.061075282 +0100
@@ -17,6 +17,16 @@
#include <asm/uaccess.h>
flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
ei->i_flags = flags;
-diff -NurpP --minimal linux-4.9.217/fs/ext2/namei.c linux-4.9.217-vs2.3.9.12/fs/ext2/namei.c
---- linux-4.9.217/fs/ext2/namei.c 2020-03-27 00:51:18.830526005 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext2/namei.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext2/namei.c linux-4.9/fs/ext2/namei.c
+--- linux-4.9/fs/ext2/namei.c 2021-02-24 15:47:32.254007963 +0100
++++ linux-4.9/fs/ext2/namei.c 2021-02-24 15:47:45.061075282 +0100
@@ -32,6 +32,7 @@
#include <linux/pagemap.h>
.get_acl = ext2_get_acl,
.set_acl = ext2_set_acl,
};
-diff -NurpP --minimal linux-4.9.217/fs/ext2/super.c linux-4.9.217-vs2.3.9.12/fs/ext2/super.c
---- linux-4.9.217/fs/ext2/super.c 2020-03-27 00:51:18.940524270 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext2/super.c 2020-04-01 09:40:28.405468940 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext2/super.c linux-4.9/fs/ext2/super.c
+--- linux-4.9/fs/ext2/super.c 2021-02-24 15:47:32.254007963 +0100
++++ linux-4.9/fs/ext2/super.c 2021-02-24 15:47:45.061075282 +0100
@@ -411,7 +411,8 @@ enum {
Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug,
Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr,
sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
((sbi->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
-diff -NurpP --minimal linux-4.9.217/fs/ext4/ext4.h linux-4.9.217-vs2.3.9.12/fs/ext4/ext4.h
---- linux-4.9.217/fs/ext4/ext4.h 2020-03-27 00:51:19.290518755 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext4/ext4.h 2020-04-01 09:40:28.435468445 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext4/ext4.h linux-4.9/fs/ext4/ext4.h
+--- linux-4.9/fs/ext4/ext4.h 2021-02-24 15:47:32.254007963 +0100
++++ linux-4.9/fs/ext4/ext4.h 2021-02-24 15:47:45.064408719 +0100
@@ -392,8 +392,11 @@ struct flex_groups {
#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */
#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */
#define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */
#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */
#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */
-@@ -2515,6 +2520,7 @@ extern int ext4_punch_hole(struct inode
+@@ -2527,6 +2532,7 @@ extern int ext4_punch_hole(struct inode
extern int ext4_truncate_restart_trans(handle_t *, struct inode *, int nblocks);
extern void ext4_set_inode_flags(struct inode *);
extern void ext4_get_inode_flags(struct ext4_inode_info *);
extern int ext4_alloc_da_blocks(struct inode *inode);
extern void ext4_set_aops(struct inode *inode);
extern int ext4_writepage_trans_blocks(struct inode *);
-diff -NurpP --minimal linux-4.9.217/fs/ext4/file.c linux-4.9.217-vs2.3.9.12/fs/ext4/file.c
---- linux-4.9.217/fs/ext4/file.c 2020-03-27 00:51:19.360517651 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext4/file.c 2019-10-05 14:58:45.150315864 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext4/file.c linux-4.9/fs/ext4/file.c
+--- linux-4.9/fs/ext4/file.c 2021-02-24 15:47:32.257341400 +0100
++++ linux-4.9/fs/ext4/file.c 2021-02-24 15:47:45.064408719 +0100
@@ -698,5 +698,6 @@ const struct inode_operations ext4_file_
.get_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
+ .sync_flags = ext4_sync_flags,
};
-diff -NurpP --minimal linux-4.9.217/fs/ext4/ialloc.c linux-4.9.217-vs2.3.9.12/fs/ext4/ialloc.c
---- linux-4.9.217/fs/ext4/ialloc.c 2020-03-27 00:51:19.360517651 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext4/ialloc.c 2020-04-01 09:40:28.435468445 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext4/ialloc.c linux-4.9/fs/ext4/ialloc.c
+--- linux-4.9/fs/ext4/ialloc.c 2021-02-24 15:47:32.257341400 +0100
++++ linux-4.9/fs/ext4/ialloc.c 2021-02-24 15:47:45.064408719 +0100
@@ -21,6 +21,7 @@
#include <linux/random.h>
#include <linux/bitops.h>
} else
inode_init_owner(inode, dir, mode);
-diff -NurpP --minimal linux-4.9.217/fs/ext4/inode.c linux-4.9.217-vs2.3.9.12/fs/ext4/inode.c
---- linux-4.9.217/fs/ext4/inode.c 2020-03-27 00:51:19.610513714 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext4/inode.c 2020-04-01 09:40:28.445468277 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext4/inode.c linux-4.9/fs/ext4/inode.c
+--- linux-4.9/fs/ext4/inode.c 2021-02-24 15:47:32.260674838 +0100
++++ linux-4.9/fs/ext4/inode.c 2021-02-24 15:47:45.064408719 +0100
@@ -37,6 +37,7 @@
#include <linux/printk.h>
#include <linux/slab.h>
#include "ext4_jbd2.h"
#include "xattr.h"
-@@ -4390,12 +4391,15 @@ void ext4_set_inode_flags(struct inode *
+@@ -4399,12 +4400,15 @@ void ext4_set_inode_flags(struct inode *
unsigned int flags = EXT4_I(inode)->i_flags;
unsigned int new_fl = 0;
if (flags & EXT4_NOATIME_FL)
new_fl |= S_NOATIME;
if (flags & EXT4_DIRSYNC_FL)
-@@ -4403,31 +4407,52 @@ void ext4_set_inode_flags(struct inode *
+@@ -4412,31 +4416,52 @@ void ext4_set_inode_flags(struct inode *
if (test_opt(inode->i_sb, DAX) && S_ISREG(inode->i_mode))
new_fl |= S_DAX;
inode_set_flags(inode, new_fl,
} while (cmpxchg(&ei->i_flags, old_fl, new_fl) != old_fl);
}
-@@ -4553,8 +4578,10 @@ struct inode *ext4_iget(struct super_blo
+@@ -4584,8 +4609,10 @@ struct inode *__ext4_iget(struct super_b
i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
}
ei->i_projid = make_kprojid(&init_user_ns, i_projid);
set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
-@@ -4882,8 +4909,10 @@ static int ext4_do_update_inode(handle_t
+@@ -4909,8 +4936,10 @@ static int ext4_do_update_inode(handle_t
ext4_get_inode_flags(ei);
raw_inode->i_mode = cpu_to_le16(inode->i_mode);
i_projid = from_kprojid(&init_user_ns, ei->i_projid);
if (!(test_opt(inode->i_sb, NO_UID32))) {
raw_inode->i_uid_low = cpu_to_le16(low_16_bits(i_uid));
-@@ -4907,6 +4936,9 @@ static int ext4_do_update_inode(handle_t
+@@ -4934,6 +4963,9 @@ static int ext4_do_update_inode(handle_t
raw_inode->i_uid_high = 0;
raw_inode->i_gid_high = 0;
}
raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
EXT4_INODE_SET_XTIME(i_ctime, inode, raw_inode);
-@@ -5164,7 +5196,8 @@ int ext4_setattr(struct dentry *dentry,
+@@ -5191,7 +5223,8 @@ int ext4_setattr(struct dentry *dentry,
return error;
}
if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) ||
handle_t *handle;
/* (user+group)*(old+new) structure, inode write (sb,
-@@ -5187,6 +5220,8 @@ int ext4_setattr(struct dentry *dentry,
+@@ -5214,6 +5247,8 @@ int ext4_setattr(struct dentry *dentry,
inode->i_uid = attr->ia_uid;
if (attr->ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
error = ext4_mark_inode_dirty(handle, inode);
ext4_journal_stop(handle);
}
-diff -NurpP --minimal linux-4.9.217/fs/ext4/ioctl.c linux-4.9.217-vs2.3.9.12/fs/ext4/ioctl.c
---- linux-4.9.217/fs/ext4/ioctl.c 2020-03-27 00:51:19.620513553 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext4/ioctl.c 2019-10-05 14:58:45.150315864 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext4/ioctl.c linux-4.9/fs/ext4/ioctl.c
+--- linux-4.9/fs/ext4/ioctl.c 2021-02-24 15:47:32.260674838 +0100
++++ linux-4.9/fs/ext4/ioctl.c 2021-02-24 15:47:45.064408719 +0100
@@ -15,6 +15,7 @@
#include <linux/file.h>
#include <linux/quotaops.h>
inode_lock(inode);
err = ext4_ioctl_setflags(inode, flags);
inode_unlock(inode);
-diff -NurpP --minimal linux-4.9.217/fs/ext4/namei.c linux-4.9.217-vs2.3.9.12/fs/ext4/namei.c
---- linux-4.9.217/fs/ext4/namei.c 2020-03-27 00:51:19.650513081 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext4/namei.c 2020-04-01 09:40:28.445468277 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ext4/namei.c linux-4.9/fs/ext4/namei.c
+--- linux-4.9/fs/ext4/namei.c 2021-02-24 15:47:32.260674838 +0100
++++ linux-4.9/fs/ext4/namei.c 2021-02-24 15:47:45.064408719 +0100
@@ -33,6 +33,7 @@
#include <linux/quotaops.h>
#include <linux/buffer_head.h>
#include "ext4.h"
#include "ext4_jbd2.h"
-@@ -1475,6 +1476,7 @@ restart:
+@@ -1457,6 +1458,7 @@ restart:
REQ_META | REQ_PRIO,
1, &bh);
}
}
if ((bh = bh_use[ra_ptr++]) == NULL)
goto next;
-@@ -3954,6 +3956,7 @@ const struct inode_operations ext4_dir_i
+@@ -3935,6 +3937,7 @@ const struct inode_operations ext4_dir_i
.get_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
.fiemap = ext4_fiemap,
};
const struct inode_operations ext4_special_inode_operations = {
-diff -NurpP --minimal linux-4.9.217/fs/ext4/super.c linux-4.9.217-vs2.3.9.12/fs/ext4/super.c
---- linux-4.9.217/fs/ext4/super.c 2020-03-27 00:51:19.660512926 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ext4/super.c 2020-04-01 09:40:28.445468277 +0000
-@@ -1302,6 +1302,7 @@ enum {
+diff -urNp -x '*.orig' linux-4.9/fs/ext4/super.c linux-4.9/fs/ext4/super.c
+--- linux-4.9/fs/ext4/super.c 2021-02-24 15:47:32.264008276 +0100
++++ linux-4.9/fs/ext4/super.c 2021-02-24 15:47:45.067742158 +0100
+@@ -1294,6 +1294,7 @@ enum {
Opt_dioread_nolock, Opt_dioread_lock,
Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
Opt_max_dir_size_kb, Opt_nojournal_checksum,
};
static const match_table_t tokens = {
-@@ -1388,6 +1389,9 @@ static const match_table_t tokens = {
+@@ -1380,6 +1381,9 @@ static const match_table_t tokens = {
{Opt_removed, "reservation"}, /* mount option from ext2/3 */
{Opt_removed, "noreservation"}, /* mount option from ext2/3 */
{Opt_removed, "journal=%u"}, /* mount option from ext2/3 */
{Opt_err, NULL},
};
-@@ -1633,6 +1637,20 @@ static int handle_mount_opt(struct super
+@@ -1625,6 +1629,20 @@ static int handle_mount_opt(struct super
case Opt_nolazytime:
sb->s_flags &= ~MS_LAZYTIME;
return 1;
}
for (m = ext4_mount_opts; m->token != Opt_err; m++)
-@@ -3652,6 +3670,9 @@ static int ext4_fill_super(struct super_
+@@ -3645,6 +3663,9 @@ static int ext4_fill_super(struct super_
sb->s_iflags |= SB_I_CGROUPWB;
}
sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
(test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
-@@ -5045,6 +5066,14 @@ static int ext4_remount(struct super_blo
+@@ -5032,6 +5053,14 @@ static int ext4_remount(struct super_blo
if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED)
ext4_abort(sb, "Abort forced by user");
sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
(test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
-diff -NurpP --minimal linux-4.9.217/fs/fcntl.c linux-4.9.217-vs2.3.9.12/fs/fcntl.c
---- linux-4.9.217/fs/fcntl.c 2020-03-27 00:51:19.840510086 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/fcntl.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/fcntl.c linux-4.9/fs/fcntl.c
+--- linux-4.9/fs/fcntl.c 2021-02-24 15:47:32.270675151 +0100
++++ linux-4.9/fs/fcntl.c 2021-02-24 15:47:45.067742158 +0100
@@ -22,6 +22,7 @@
#include <linux/pid_namespace.h>
#include <linux/user_namespace.h>
if (unlikely(f.file->f_mode & FMODE_PATH)) {
if (!check_fcntl_cmd(cmd))
-diff -NurpP --minimal linux-4.9.217/fs/file.c linux-4.9.217-vs2.3.9.12/fs/file.c
---- linux-4.9.217/fs/file.c 2020-03-27 00:51:19.860509770 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/file.c 2019-10-05 14:58:45.150315864 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/file.c linux-4.9/fs/file.c
+--- linux-4.9/fs/file.c 2021-02-24 15:47:32.270675151 +0100
++++ linux-4.9/fs/file.c 2021-02-24 15:47:45.067742158 +0100
@@ -22,6 +22,7 @@
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
return fd;
-diff -NurpP --minimal linux-4.9.217/fs/file_table.c linux-4.9.217-vs2.3.9.12/fs/file_table.c
---- linux-4.9.217/fs/file_table.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/file_table.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/file_table.c linux-4.9/fs/file_table.c
+--- linux-4.9/fs/file_table.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/file_table.c 2021-02-24 15:47:45.067742158 +0100
@@ -26,6 +26,8 @@
#include <linux/task_work.h>
#include <linux/ima.h>
file_free(file);
}
}
-diff -NurpP --minimal linux-4.9.217/fs/fs_struct.c linux-4.9.217-vs2.3.9.12/fs/fs_struct.c
---- linux-4.9.217/fs/fs_struct.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/fs_struct.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/fs_struct.c linux-4.9/fs/fs_struct.c
+--- linux-4.9/fs/fs_struct.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/fs_struct.c 2021-02-24 15:47:45.067742158 +0100
@@ -4,6 +4,7 @@
#include <linux/path.h>
#include <linux/slab.h>
}
return fs;
}
-diff -NurpP --minimal linux-4.9.217/fs/gfs2/file.c linux-4.9.217-vs2.3.9.12/fs/gfs2/file.c
---- linux-4.9.217/fs/gfs2/file.c 2020-03-27 00:51:20.390501417 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/gfs2/file.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/gfs2/file.c linux-4.9/fs/gfs2/file.c
+--- linux-4.9/fs/gfs2/file.c 2021-02-24 15:47:32.277342026 +0100
++++ linux-4.9/fs/gfs2/file.c 2021-02-24 15:47:45.067742158 +0100
@@ -137,6 +137,9 @@ static const u32 fsflags_to_gfs2[32] = {
[12] = GFS2_DIF_EXHASH,
[14] = GFS2_DIF_INHERIT_JDATA,
static long gfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd) {
-diff -NurpP --minimal linux-4.9.217/fs/gfs2/inode.h linux-4.9.217-vs2.3.9.12/fs/gfs2/inode.h
---- linux-4.9.217/fs/gfs2/inode.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/gfs2/inode.h 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/gfs2/inode.h linux-4.9/fs/gfs2/inode.h
+--- linux-4.9/fs/gfs2/inode.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/gfs2/inode.h 2021-02-24 15:47:45.067742158 +0100
@@ -117,6 +117,7 @@ extern const struct file_operations gfs2
extern const struct file_operations gfs2_dir_fops_nolock;
#ifdef CONFIG_GFS2_FS_LOCKING_DLM
extern const struct file_operations gfs2_file_fops;
-diff -NurpP --minimal linux-4.9.217/fs/hostfs/hostfs.h linux-4.9.217-vs2.3.9.12/fs/hostfs/hostfs.h
---- linux-4.9.217/fs/hostfs/hostfs.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/hostfs/hostfs.h 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/hostfs/hostfs.h linux-4.9/fs/hostfs/hostfs.h
+--- linux-4.9/fs/hostfs/hostfs.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/hostfs/hostfs.h 2021-02-24 15:47:45.067742158 +0100
@@ -42,6 +42,7 @@ struct hostfs_iattr {
unsigned short ia_mode;
uid_t ia_uid;
loff_t ia_size;
struct timespec ia_atime;
struct timespec ia_mtime;
-diff -NurpP --minimal linux-4.9.217/fs/inode.c linux-4.9.217-vs2.3.9.12/fs/inode.c
---- linux-4.9.217/fs/inode.c 2020-03-27 00:51:20.860494012 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/inode.c 2019-10-05 14:58:45.170315544 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/inode.c linux-4.9/fs/inode.c
+--- linux-4.9/fs/inode.c 2021-02-24 15:47:32.280675463 +0100
++++ linux-4.9/fs/inode.c 2021-02-24 15:47:45.067742158 +0100
@@ -18,6 +18,7 @@
#include <linux/buffer_head.h> /* for inode_has_buffers */
#include <linux/ratelimit.h>
+ /* essential because of inode slab reuse */
inode->i_blkbits = sb->s_blocksize_bits;
inode->i_flags = 0;
- atomic_set(&inode->i_count, 1);
-@@ -144,6 +147,7 @@ int inode_init_always(struct super_block
+ atomic64_set(&inode->i_sequence, 0);
+@@ -145,6 +148,7 @@ int inode_init_always(struct super_block
inode->i_opflags |= IOP_XATTR;
i_uid_write(inode, 0);
i_gid_write(inode, 0);
atomic_set(&inode->i_writecount, 0);
inode->i_size = 0;
inode->i_blocks = 0;
-@@ -155,6 +159,7 @@ int inode_init_always(struct super_block
+@@ -156,6 +160,7 @@ int inode_init_always(struct super_block
inode->i_link = NULL;
inode->i_dir_seq = 0;
inode->i_rdev = 0;
inode->dirtied_when = 0;
#ifdef CONFIG_CGROUP_WRITEBACK
-@@ -479,6 +484,8 @@ void __insert_inode_hash(struct inode *i
+@@ -480,6 +485,8 @@ void __insert_inode_hash(struct inode *i
}
EXPORT_SYMBOL(__insert_inode_hash);
/**
* __remove_inode_hash - remove an inode from the hash
* @inode: inode to unhash
-@@ -1982,9 +1989,11 @@ void init_special_inode(struct inode *in
+@@ -1983,9 +1990,11 @@ void init_special_inode(struct inode *in
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = rdev;
} else if (S_ISFIFO(mode))
inode->i_fop = &pipefifo_fops;
else if (S_ISSOCK(mode))
-@@ -2019,6 +2028,7 @@ void inode_init_owner(struct inode *inod
+@@ -2020,6 +2029,7 @@ void inode_init_owner(struct inode *inod
} else
inode->i_gid = current_fsgid();
inode->i_mode = mode;
}
EXPORT_SYMBOL(inode_init_owner);
-diff -NurpP --minimal linux-4.9.217/fs/ioctl.c linux-4.9.217-vs2.3.9.12/fs/ioctl.c
---- linux-4.9.217/fs/ioctl.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ioctl.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ioctl.c linux-4.9/fs/ioctl.c
+--- linux-4.9/fs/ioctl.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/ioctl.c 2021-02-24 15:47:45.067742158 +0100
@@ -15,6 +15,9 @@
#include <linux/writeback.h>
#include <linux/buffer_head.h>
#include "internal.h"
#include <asm/ioctls.h>
-diff -NurpP --minimal linux-4.9.217/fs/jfs/file.c linux-4.9.217-vs2.3.9.12/fs/jfs/file.c
---- linux-4.9.217/fs/jfs/file.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/jfs/file.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/jfs/file.c linux-4.9/fs/jfs/file.c
+--- linux-4.9/fs/jfs/file.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/jfs/file.c 2021-02-24 15:47:45.067742158 +0100
@@ -113,7 +113,8 @@ int jfs_setattr(struct dentry *dentry, s
return rc;
}
};
const struct file_operations jfs_file_operations = {
-diff -NurpP --minimal linux-4.9.217/fs/jfs/ioctl.c linux-4.9.217-vs2.3.9.12/fs/jfs/ioctl.c
---- linux-4.9.217/fs/jfs/ioctl.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/jfs/ioctl.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/jfs/ioctl.c linux-4.9/fs/jfs/ioctl.c
+--- linux-4.9/fs/jfs/ioctl.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/jfs/ioctl.c 2021-02-24 15:47:45.067742158 +0100
@@ -12,6 +12,7 @@
#include <linux/time.h>
#include <linux/sched.h>
flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
jfs_inode->mode2 = flags;
-diff -NurpP --minimal linux-4.9.217/fs/jfs/jfs_dinode.h linux-4.9.217-vs2.3.9.12/fs/jfs/jfs_dinode.h
---- linux-4.9.217/fs/jfs/jfs_dinode.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/jfs/jfs_dinode.h 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/jfs/jfs_dinode.h linux-4.9/fs/jfs/jfs_dinode.h
+--- linux-4.9/fs/jfs/jfs_dinode.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/jfs/jfs_dinode.h 2021-02-24 15:47:45.067742158 +0100
@@ -161,9 +161,13 @@ struct dinode {
#define JFS_APPEND_FL 0x01000000 /* writes to file may only append */
#define JFS_FL_INHERIT 0x03C80000
/* These are identical to EXT[23]_IOC_GETFLAGS/SETFLAGS */
-diff -NurpP --minimal linux-4.9.217/fs/jfs/jfs_filsys.h linux-4.9.217-vs2.3.9.12/fs/jfs/jfs_filsys.h
---- linux-4.9.217/fs/jfs/jfs_filsys.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/jfs/jfs_filsys.h 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/jfs/jfs_filsys.h linux-4.9/fs/jfs/jfs_filsys.h
+--- linux-4.9/fs/jfs/jfs_filsys.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/jfs/jfs_filsys.h 2021-02-24 15:47:45.067742158 +0100
@@ -266,6 +266,7 @@
#define JFS_NAME_MAX 255
#define JFS_PATH_MAX BPSIZE
/*
* file system state (superblock state)
-diff -NurpP --minimal linux-4.9.217/fs/jfs/jfs_imap.c linux-4.9.217-vs2.3.9.12/fs/jfs/jfs_imap.c
---- linux-4.9.217/fs/jfs/jfs_imap.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/jfs/jfs_imap.c 2018-10-20 04:58:13.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/jfs/jfs_imap.c linux-4.9/fs/jfs/jfs_imap.c
+--- linux-4.9/fs/jfs/jfs_imap.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/jfs/jfs_imap.c 2021-02-24 15:47:45.071075595 +0100
@@ -46,6 +46,7 @@
#include <linux/pagemap.h>
#include <linux/quotaops.h>
jfs_get_inode_flags(jfs_ip);
/*
* mode2 is only needed for storing the higher order bits.
-diff -NurpP --minimal linux-4.9.217/fs/jfs/jfs_inode.c linux-4.9.217-vs2.3.9.12/fs/jfs/jfs_inode.c
---- linux-4.9.217/fs/jfs/jfs_inode.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/jfs/jfs_inode.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/jfs/jfs_inode.c linux-4.9/fs/jfs/jfs_inode.c
+--- linux-4.9/fs/jfs/jfs_inode.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/jfs/jfs_inode.c 2021-02-24 15:47:45.071075595 +0100
@@ -18,6 +18,7 @@
#include <linux/fs.h>
}
/*
-diff -NurpP --minimal linux-4.9.217/fs/jfs/jfs_inode.h linux-4.9.217-vs2.3.9.12/fs/jfs/jfs_inode.h
---- linux-4.9.217/fs/jfs/jfs_inode.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/jfs/jfs_inode.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/jfs/jfs_inode.h linux-4.9/fs/jfs/jfs_inode.h
+--- linux-4.9/fs/jfs/jfs_inode.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/jfs/jfs_inode.h 2021-02-24 15:47:45.071075595 +0100
@@ -39,6 +39,7 @@ extern struct dentry *jfs_fh_to_dentry(s
extern struct dentry *jfs_fh_to_parent(struct super_block *sb, struct fid *fid,
int fh_len, int fh_type);
extern int jfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
extern int jfs_setattr(struct dentry *, struct iattr *);
-diff -NurpP --minimal linux-4.9.217/fs/jfs/namei.c linux-4.9.217-vs2.3.9.12/fs/jfs/namei.c
---- linux-4.9.217/fs/jfs/namei.c 2020-03-27 00:51:21.670481243 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/jfs/namei.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/jfs/namei.c linux-4.9/fs/jfs/namei.c
+--- linux-4.9/fs/jfs/namei.c 2021-02-24 15:47:32.287342339 +0100
++++ linux-4.9/fs/jfs/namei.c 2021-02-24 15:47:45.071075595 +0100
@@ -22,6 +22,7 @@
#include <linux/ctype.h>
#include <linux/quotaops.h>
};
const struct file_operations jfs_dir_operations = {
-diff -NurpP --minimal linux-4.9.217/fs/jfs/super.c linux-4.9.217-vs2.3.9.12/fs/jfs/super.c
---- linux-4.9.217/fs/jfs/super.c 2020-03-27 00:51:21.670481243 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/jfs/super.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/jfs/super.c linux-4.9/fs/jfs/super.c
+--- linux-4.9/fs/jfs/super.c 2021-02-24 15:47:32.287342339 +0100
++++ linux-4.9/fs/jfs/super.c 2021-02-24 15:47:45.071075595 +0100
@@ -206,7 +206,8 @@ enum {
Opt_integrity, Opt_nointegrity, Opt_iocharset, Opt_resize,
Opt_resize_nosize, Opt_errors, Opt_ignore, Opt_err, Opt_quota,
if (newLVSize) {
pr_err("resize option for remount only\n");
-diff -NurpP --minimal linux-4.9.217/fs/libfs.c linux-4.9.217-vs2.3.9.12/fs/libfs.c
---- linux-4.9.217/fs/libfs.c 2020-03-27 00:51:21.710480616 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/libfs.c 2019-10-22 13:49:06.407707139 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/libfs.c linux-4.9/fs/libfs.c
+--- linux-4.9/fs/libfs.c 2021-02-24 15:47:32.287342339 +0100
++++ linux-4.9/fs/libfs.c 2021-02-24 15:47:45.071075595 +0100
@@ -180,7 +180,8 @@ static inline unsigned char dt_type(stru
* both impossible due to the lock on directory.
*/
ssize_t generic_read_dir(struct file *filp, char __user *buf, size_t siz, loff_t *ppos)
{
return -EISDIR;
-diff -NurpP --minimal linux-4.9.217/fs/locks.c linux-4.9.217-vs2.3.9.12/fs/locks.c
---- linux-4.9.217/fs/locks.c 2020-03-27 00:51:21.800479197 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/locks.c 2020-04-01 09:40:28.545466623 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/locks.c linux-4.9/fs/locks.c
+--- linux-4.9/fs/locks.c 2021-02-24 15:47:32.287342339 +0100
++++ linux-4.9/fs/locks.c 2021-02-24 15:47:45.071075595 +0100
@@ -127,6 +127,8 @@
#include <linux/pid_namespace.h>
#include <linux/hashtable.h>
return 0;
}
-diff -NurpP --minimal linux-4.9.217/fs/mount.h linux-4.9.217-vs2.3.9.12/fs/mount.h
---- linux-4.9.217/fs/mount.h 2020-03-27 00:51:21.850478409 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/mount.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/mount.h linux-4.9/fs/mount.h
+--- linux-4.9/fs/mount.h 2021-02-24 15:47:32.287342339 +0100
++++ linux-4.9/fs/mount.h 2021-02-24 15:47:45.071075595 +0100
@@ -69,6 +69,7 @@ struct mount {
struct hlist_head mnt_pins;
struct fs_pin mnt_umount;
};
#define MNT_NS_INTERNAL ERR_PTR(-EINVAL) /* distinct from any mnt_namespace */
-diff -NurpP --minimal linux-4.9.217/fs/namei.c linux-4.9.217-vs2.3.9.12/fs/namei.c
---- linux-4.9.217/fs/namei.c 2020-03-27 00:51:21.910477461 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/namei.c 2020-04-01 09:40:28.545466623 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/namei.c linux-4.9/fs/namei.c
+--- linux-4.9/fs/namei.c 2021-02-24 15:47:32.290675776 +0100
++++ linux-4.9/fs/namei.c 2021-02-24 15:47:45.071075595 +0100
@@ -37,9 +37,19 @@
#include <linux/hash.h>
#include <linux/bitops.h>
/* get the link contents into pagecache */
const char *page_get_link(struct dentry *dentry, struct inode *inode,
struct delayed_call *callback)
-diff -NurpP --minimal linux-4.9.217/fs/namespace.c linux-4.9.217-vs2.3.9.12/fs/namespace.c
---- linux-4.9.217/fs/namespace.c 2020-03-27 00:51:21.950476834 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/namespace.c 2019-02-22 08:37:55.023061290 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/namespace.c linux-4.9/fs/namespace.c
+--- linux-4.9/fs/namespace.c 2021-02-24 15:47:32.290675776 +0100
++++ linux-4.9/fs/namespace.c 2021-02-24 15:47:45.074409033 +0100
@@ -24,6 +24,11 @@
#include <linux/magic.h>
#include <linux/bootmem.h>
free_mnt_ns(ns);
}
-diff -NurpP --minimal linux-4.9.217/fs/nfs/client.c linux-4.9.217-vs2.3.9.12/fs/nfs/client.c
---- linux-4.9.217/fs/nfs/client.c 2020-03-27 00:51:22.630466112 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/nfs/client.c 2019-10-05 14:58:45.410311711 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/nfs/client.c linux-4.9/fs/nfs/client.c
+--- linux-4.9/fs/nfs/client.c 2021-02-24 15:47:32.290675776 +0100
++++ linux-4.9/fs/nfs/client.c 2021-02-24 15:47:45.074409033 +0100
@@ -586,6 +586,9 @@ int nfs_init_server_rpcclient(struct nfs
if (server->flags & NFS_MOUNT_SOFT)
server->client->cl_softrtry = 1;
server->maxfilesize = fsinfo->maxfilesize;
server->time_delta = fsinfo->time_delta;
-diff -NurpP --minimal linux-4.9.217/fs/nfs/dir.c linux-4.9.217-vs2.3.9.12/fs/nfs/dir.c
---- linux-4.9.217/fs/nfs/dir.c 2020-03-27 00:51:22.830462961 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/nfs/dir.c 2020-04-01 09:40:28.555466454 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/nfs/dir.c linux-4.9/fs/nfs/dir.c
+--- linux-4.9/fs/nfs/dir.c 2021-02-24 15:47:32.294009214 +0100
++++ linux-4.9/fs/nfs/dir.c 2021-02-24 15:47:45.074409033 +0100
@@ -37,6 +37,7 @@
#include <linux/sched.h>
#include <linux/kmemleak.h>
#include "delegation.h"
#include "iostat.h"
-@@ -1428,6 +1429,7 @@ struct dentry *nfs_lookup(struct inode *
+@@ -1431,6 +1432,7 @@ struct dentry *nfs_lookup(struct inode *
/* Success: notify readdir to use READDIRPLUS */
nfs_advise_use_readdirplus(dir);
no_entry:
res = d_splice_alias(inode, dentry);
if (res != NULL) {
-diff -NurpP --minimal linux-4.9.217/fs/nfs/inode.c linux-4.9.217-vs2.3.9.12/fs/nfs/inode.c
---- linux-4.9.217/fs/nfs/inode.c 2020-03-27 00:51:22.940461230 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/nfs/inode.c 2019-10-05 14:58:45.430311390 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/nfs/inode.c linux-4.9/fs/nfs/inode.c
+--- linux-4.9/fs/nfs/inode.c 2021-02-24 15:47:32.294009214 +0100
++++ linux-4.9/fs/nfs/inode.c 2021-02-24 15:47:45.074409033 +0100
@@ -38,6 +38,7 @@
#include <linux/slab.h>
#include <linux/compat.h>
if (fattr->valid & NFS_ATTR_FATTR_NLINK) {
if (inode->i_nlink != fattr->nlink) {
invalid |= NFS_INO_INVALID_ATTR;
-diff -NurpP --minimal linux-4.9.217/fs/nfs/nfs3xdr.c linux-4.9.217-vs2.3.9.12/fs/nfs/nfs3xdr.c
---- linux-4.9.217/fs/nfs/nfs3xdr.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/nfs/nfs3xdr.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/nfs/nfs3xdr.c linux-4.9/fs/nfs/nfs3xdr.c
+--- linux-4.9/fs/nfs/nfs3xdr.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/nfs/nfs3xdr.c 2021-02-24 15:47:45.074409033 +0100
@@ -20,6 +20,7 @@
#include <linux/nfs3.h>
#include <linux/nfs_fs.h>
}
/*
-diff -NurpP --minimal linux-4.9.217/fs/nfs/super.c linux-4.9.217-vs2.3.9.12/fs/nfs/super.c
---- linux-4.9.217/fs/nfs/super.c 2020-03-27 00:51:23.470452877 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/nfs/super.c 2020-04-01 09:40:28.555466454 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/nfs/super.c linux-4.9/fs/nfs/super.c
+--- linux-4.9/fs/nfs/super.c 2021-02-24 15:47:32.300676089 +0100
++++ linux-4.9/fs/nfs/super.c 2021-02-24 15:47:45.074409033 +0100
@@ -54,6 +54,7 @@
#include <linux/parser.h>
#include <linux/nsproxy.h>
/*
* options that take text values
-diff -NurpP --minimal linux-4.9.217/fs/nfsd/auth.c linux-4.9.217-vs2.3.9.12/fs/nfsd/auth.c
---- linux-4.9.217/fs/nfsd/auth.c 2020-03-27 00:51:23.680449564 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/nfsd/auth.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/nfsd/auth.c linux-4.9/fs/nfsd/auth.c
+--- linux-4.9/fs/nfsd/auth.c 2021-02-24 15:47:32.300676089 +0100
++++ linux-4.9/fs/nfsd/auth.c 2021-02-24 15:47:45.074409033 +0100
@@ -1,6 +1,7 @@
/* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */
rqgi = rqstp->rq_cred.cr_group_info;
-diff -NurpP --minimal linux-4.9.217/fs/nfsd/nfs3xdr.c linux-4.9.217-vs2.3.9.12/fs/nfsd/nfs3xdr.c
---- linux-4.9.217/fs/nfsd/nfs3xdr.c 2020-03-27 00:51:23.700449249 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/nfsd/nfs3xdr.c 2019-10-05 14:58:45.440311232 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/nfsd/nfs3xdr.c linux-4.9/fs/nfsd/nfs3xdr.c
+--- linux-4.9/fs/nfsd/nfs3xdr.c 2021-02-24 15:47:32.300676089 +0100
++++ linux-4.9/fs/nfsd/nfs3xdr.c 2021-02-24 15:47:45.077742470 +0100
@@ -8,6 +8,7 @@
#include <linux/namei.h>
if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) {
p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
} else {
-diff -NurpP --minimal linux-4.9.217/fs/nfsd/nfs4xdr.c linux-4.9.217-vs2.3.9.12/fs/nfsd/nfs4xdr.c
---- linux-4.9.217/fs/nfsd/nfs4xdr.c 2020-03-27 00:51:23.950445311 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/nfsd/nfs4xdr.c 2018-10-20 05:55:43.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/nfsd/nfs4xdr.c linux-4.9/fs/nfsd/nfs4xdr.c
+--- linux-4.9/fs/nfsd/nfs4xdr.c 2021-02-24 15:47:32.304009527 +0100
++++ linux-4.9/fs/nfsd/nfs4xdr.c 2021-02-24 15:47:45.077742470 +0100
@@ -40,6 +40,7 @@
#include <linux/utsname.h>
#include <linux/pagemap.h>
if (status)
goto out;
}
-diff -NurpP --minimal linux-4.9.217/fs/nfsd/nfsxdr.c linux-4.9.217-vs2.3.9.12/fs/nfsd/nfsxdr.c
---- linux-4.9.217/fs/nfsd/nfsxdr.c 2020-03-27 00:51:24.030444047 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/nfsd/nfsxdr.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/nfsd/nfsxdr.c linux-4.9/fs/nfsd/nfsxdr.c
+--- linux-4.9/fs/nfsd/nfsxdr.c 2021-02-24 15:47:32.307342964 +0100
++++ linux-4.9/fs/nfsd/nfsxdr.c 2021-02-24 15:47:45.077742470 +0100
@@ -7,6 +7,7 @@
#include "vfs.h"
#include "xdr.h"
if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) {
*p++ = htonl(NFS_MAXPATHLEN);
-diff -NurpP --minimal linux-4.9.217/fs/ocfs2/dlmglue.c linux-4.9.217-vs2.3.9.12/fs/ocfs2/dlmglue.c
---- linux-4.9.217/fs/ocfs2/dlmglue.c 2020-03-27 00:51:25.080427501 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ocfs2/dlmglue.c 2019-12-25 15:37:51.918430506 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ocfs2/dlmglue.c linux-4.9/fs/ocfs2/dlmglue.c
+--- linux-4.9/fs/ocfs2/dlmglue.c 2021-02-24 15:47:32.314009840 +0100
++++ linux-4.9/fs/ocfs2/dlmglue.c 2021-02-24 15:47:45.077742470 +0100
@@ -2120,6 +2120,7 @@ static void __ocfs2_stuff_meta_lvb(struc
lvb->lvb_iclusters = cpu_to_be32(oi->ip_clusters);
lvb->lvb_iuid = cpu_to_be32(i_uid_read(inode));
inode->i_mode = be16_to_cpu(lvb->lvb_imode);
set_nlink(inode, be16_to_cpu(lvb->lvb_inlink));
ocfs2_unpack_timespec(&inode->i_atime,
-diff -NurpP --minimal linux-4.9.217/fs/ocfs2/dlmglue.h linux-4.9.217-vs2.3.9.12/fs/ocfs2/dlmglue.h
---- linux-4.9.217/fs/ocfs2/dlmglue.h 2020-03-27 00:51:25.080427501 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ocfs2/dlmglue.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ocfs2/dlmglue.h linux-4.9/fs/ocfs2/dlmglue.h
+--- linux-4.9/fs/ocfs2/dlmglue.h 2021-02-24 15:47:32.314009840 +0100
++++ linux-4.9/fs/ocfs2/dlmglue.h 2021-02-24 15:47:45.077742470 +0100
@@ -46,7 +46,8 @@ struct ocfs2_meta_lvb {
__be16 lvb_inlink;
__be32 lvb_iattr;
};
#define OCFS2_QINFO_LVB_VERSION 1
-diff -NurpP --minimal linux-4.9.217/fs/ocfs2/file.c linux-4.9.217-vs2.3.9.12/fs/ocfs2/file.c
---- linux-4.9.217/fs/ocfs2/file.c 2020-03-27 00:51:25.110427029 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ocfs2/file.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ocfs2/file.c linux-4.9/fs/ocfs2/file.c
+--- linux-4.9/fs/ocfs2/file.c 2021-02-24 15:47:32.314009840 +0100
++++ linux-4.9/fs/ocfs2/file.c 2021-02-24 15:47:45.077742470 +0100
@@ -1151,7 +1151,7 @@ int ocfs2_setattr(struct dentry *dentry,
attr->ia_valid &= ~ATTR_SIZE;
if (!(attr->ia_valid & OCFS2_VALID_ATTRS))
return 0;
-diff -NurpP --minimal linux-4.9.217/fs/ocfs2/inode.c linux-4.9.217-vs2.3.9.12/fs/ocfs2/inode.c
---- linux-4.9.217/fs/ocfs2/inode.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ocfs2/inode.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ocfs2/inode.c linux-4.9/fs/ocfs2/inode.c
+--- linux-4.9/fs/ocfs2/inode.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/ocfs2/inode.c 2021-02-24 15:47:45.077742470 +0100
@@ -28,6 +28,7 @@
#include <linux/highmem.h>
#include <linux/pagemap.h>
/* Fast symlinks will have i_size but no allocated clusters. */
if (S_ISLNK(inode->i_mode) && !fe->i_clusters) {
-diff -NurpP --minimal linux-4.9.217/fs/ocfs2/inode.h linux-4.9.217-vs2.3.9.12/fs/ocfs2/inode.h
---- linux-4.9.217/fs/ocfs2/inode.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ocfs2/inode.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ocfs2/inode.h linux-4.9/fs/ocfs2/inode.h
+--- linux-4.9/fs/ocfs2/inode.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/ocfs2/inode.h 2021-02-24 15:47:45.077742470 +0100
@@ -155,6 +155,7 @@ int ocfs2_mark_inode_dirty(handle_t *han
void ocfs2_set_inode_flags(struct inode *inode);
static inline blkcnt_t ocfs2_inode_sector_count(struct inode *inode)
{
-diff -NurpP --minimal linux-4.9.217/fs/ocfs2/ioctl.c linux-4.9.217-vs2.3.9.12/fs/ocfs2/ioctl.c
---- linux-4.9.217/fs/ocfs2/ioctl.c 2020-03-27 00:51:25.160426241 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ocfs2/ioctl.c 2019-12-25 15:37:51.918430506 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ocfs2/ioctl.c linux-4.9/fs/ocfs2/ioctl.c
+--- linux-4.9/fs/ocfs2/ioctl.c 2021-02-24 15:47:32.314009840 +0100
++++ linux-4.9/fs/ocfs2/ioctl.c 2021-02-24 15:47:45.077742470 +0100
@@ -76,7 +76,41 @@ static int ocfs2_get_inode_attr(struct i
return status;
}
long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file_inode(filp);
-diff -NurpP --minimal linux-4.9.217/fs/ocfs2/namei.c linux-4.9.217-vs2.3.9.12/fs/ocfs2/namei.c
---- linux-4.9.217/fs/ocfs2/namei.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ocfs2/namei.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ocfs2/namei.c linux-4.9/fs/ocfs2/namei.c
+--- linux-4.9/fs/ocfs2/namei.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/ocfs2/namei.c 2021-02-24 15:47:45.081075908 +0100
@@ -41,6 +41,7 @@
#include <linux/slab.h>
#include <linux/highmem.h>
fe->i_mode = cpu_to_le16(inode->i_mode);
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
fe->id1.dev1.i_rdev = cpu_to_le64(huge_encode_dev(dev));
-diff -NurpP --minimal linux-4.9.217/fs/ocfs2/ocfs2_fs.h linux-4.9.217-vs2.3.9.12/fs/ocfs2/ocfs2_fs.h
---- linux-4.9.217/fs/ocfs2/ocfs2_fs.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ocfs2/ocfs2_fs.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ocfs2/ocfs2.h linux-4.9/fs/ocfs2/ocfs2.h
+--- linux-4.9/fs/ocfs2/ocfs2.h 2021-02-24 15:47:32.317343277 +0100
++++ linux-4.9/fs/ocfs2/ocfs2.h 2021-02-24 15:47:45.081075908 +0100
+@@ -289,6 +289,7 @@ enum ocfs2_mount_options
+ OCFS2_MOUNT_JOURNAL_ASYNC_COMMIT = 1 << 15, /* Journal Async Commit */
+ OCFS2_MOUNT_ERRORS_CONT = 1 << 16, /* Return EIO to the calling process on error */
+ OCFS2_MOUNT_ERRORS_ROFS = 1 << 17, /* Change filesystem to read-only on error */
++ OCFS2_MOUNT_TAGGED = 1 << 18, /* use tagging */
+ };
+
+ #define OCFS2_OSB_SOFT_RO 0x0001
+diff -urNp -x '*.orig' linux-4.9/fs/ocfs2/ocfs2_fs.h linux-4.9/fs/ocfs2/ocfs2_fs.h
+--- linux-4.9/fs/ocfs2/ocfs2_fs.h 2021-02-24 15:47:32.317343277 +0100
++++ linux-4.9/fs/ocfs2/ocfs2_fs.h 2021-02-24 15:47:45.081075908 +0100
@@ -275,6 +275,11 @@
#define OCFS2_TOPDIR_FL FS_TOPDIR_FL /* Top of directory hierarchies*/
#define OCFS2_RESERVED_FL FS_RESERVED_FL /* reserved for ext2 lib */
#define OCFS2_FL_VISIBLE FS_FL_USER_VISIBLE /* User visible flags */
#define OCFS2_FL_MODIFIABLE FS_FL_USER_MODIFIABLE /* User modifiable flags */
-diff -NurpP --minimal linux-4.9.217/fs/ocfs2/ocfs2.h linux-4.9.217-vs2.3.9.12/fs/ocfs2/ocfs2.h
---- linux-4.9.217/fs/ocfs2/ocfs2.h 2020-03-27 00:51:25.320423716 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ocfs2/ocfs2.h 2018-10-20 04:58:14.000000000 +0000
-@@ -289,6 +289,7 @@ enum ocfs2_mount_options
- OCFS2_MOUNT_JOURNAL_ASYNC_COMMIT = 1 << 15, /* Journal Async Commit */
- OCFS2_MOUNT_ERRORS_CONT = 1 << 16, /* Return EIO to the calling process on error */
- OCFS2_MOUNT_ERRORS_ROFS = 1 << 17, /* Change filesystem to read-only on error */
-+ OCFS2_MOUNT_TAGGED = 1 << 18, /* use tagging */
- };
-
- #define OCFS2_OSB_SOFT_RO 0x0001
-diff -NurpP --minimal linux-4.9.217/fs/ocfs2/super.c linux-4.9.217-vs2.3.9.12/fs/ocfs2/super.c
---- linux-4.9.217/fs/ocfs2/super.c 2020-03-27 00:51:25.350423246 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/ocfs2/super.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/ocfs2/super.c linux-4.9/fs/ocfs2/super.c
+--- linux-4.9/fs/ocfs2/super.c 2021-02-24 15:47:32.317343277 +0100
++++ linux-4.9/fs/ocfs2/super.c 2021-02-24 15:47:45.081075908 +0100
@@ -188,6 +188,7 @@ enum {
Opt_dir_resv_level,
Opt_journal_async_commit,
default:
mlog(ML_ERROR,
"Unrecognized mount option \"%s\" "
-diff -NurpP --minimal linux-4.9.217/fs/open.c linux-4.9.217-vs2.3.9.12/fs/open.c
---- linux-4.9.217/fs/open.c 2020-03-27 00:51:25.400422458 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/open.c 2020-04-01 09:40:28.635465130 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/open.c linux-4.9/fs/open.c
+--- linux-4.9/fs/open.c 2021-02-24 15:47:32.320676715 +0100
++++ linux-4.9/fs/open.c 2021-02-24 15:47:45.081075908 +0100
@@ -31,6 +31,11 @@
#include <linux/ima.h>
#include <linux/dnotify.h>
error = chown_common(&path, user, group);
mnt_drop_write(path.mnt);
out_release:
-diff -NurpP --minimal linux-4.9.217/fs/proc/array.c linux-4.9.217-vs2.3.9.12/fs/proc/array.c
---- linux-4.9.217/fs/proc/array.c 2020-03-27 00:51:25.650418515 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc/array.c 2019-10-05 14:58:45.660307716 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc/array.c linux-4.9/fs/proc/array.c
+--- linux-4.9/fs/proc/array.c 2021-02-24 15:47:32.324010153 +0100
++++ linux-4.9/fs/proc/array.c 2021-02-24 15:47:45.081075908 +0100
@@ -86,6 +86,8 @@
#include <linux/string_helpers.h>
#include <linux/user_namespace.h>
seq_printf(m, "%d (%s) %c", pid_nr_ns(pid, ns), tcomm, state);
seq_put_decimal_ll(m, " ", ppid);
seq_put_decimal_ll(m, " ", pgid);
-diff -NurpP --minimal linux-4.9.217/fs/proc/base.c linux-4.9.217-vs2.3.9.12/fs/proc/base.c
---- linux-4.9.217/fs/proc/base.c 2020-03-27 00:51:25.650418515 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc/base.c 2019-10-05 14:58:45.660307716 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc/base.c linux-4.9/fs/proc/base.c
+--- linux-4.9/fs/proc/base.c 2021-02-24 15:47:32.324010153 +0100
++++ linux-4.9/fs/proc/base.c 2021-02-24 15:47:45.081075908 +0100
@@ -87,6 +87,8 @@
#include <linux/slab.h>
#include <linux/flex_array.h>
ns = dentry->d_sb->s_fs_info;
rcu_read_lock();
-diff -NurpP --minimal linux-4.9.217/fs/proc/generic.c linux-4.9.217-vs2.3.9.12/fs/proc/generic.c
---- linux-4.9.217/fs/proc/generic.c 2020-03-27 00:51:25.650418515 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc/generic.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc/generic.c linux-4.9/fs/proc/generic.c
+--- linux-4.9/fs/proc/generic.c 2021-02-24 15:47:32.324010153 +0100
++++ linux-4.9/fs/proc/generic.c 2021-02-24 15:47:45.081075908 +0100
@@ -22,6 +22,7 @@
#include <linux/bitops.h>
#include <linux/spinlock.h>
} else {
kfree(ent);
ent = NULL;
-diff -NurpP --minimal linux-4.9.217/fs/proc/inode.c linux-4.9.217-vs2.3.9.12/fs/proc/inode.c
---- linux-4.9.217/fs/proc/inode.c 2020-03-27 00:51:25.670418199 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc/inode.c 2018-10-20 05:55:43.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc/inode.c linux-4.9/fs/proc/inode.c
+--- linux-4.9/fs/proc/inode.c 2021-02-24 15:47:32.324010153 +0100
++++ linux-4.9/fs/proc/inode.c 2021-02-24 15:47:45.081075908 +0100
@@ -433,6 +433,8 @@ struct inode *proc_get_inode(struct supe
inode->i_uid = de->uid;
inode->i_gid = de->gid;
if (de->size)
inode->i_size = de->size;
if (de->nlink)
-diff -NurpP --minimal linux-4.9.217/fs/proc/internal.h linux-4.9.217-vs2.3.9.12/fs/proc/internal.h
---- linux-4.9.217/fs/proc/internal.h 2020-03-27 00:51:25.680418042 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc/internal.h 2018-10-20 05:55:43.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc/internal.h linux-4.9/fs/proc/internal.h
+--- linux-4.9/fs/proc/internal.h 2021-02-24 15:47:32.324010153 +0100
++++ linux-4.9/fs/proc/internal.h 2021-02-24 15:47:45.084409345 +0100
@@ -14,6 +14,7 @@
#include <linux/spinlock.h>
#include <linux/atomic.h>
/*
* base.c
-diff -NurpP --minimal linux-4.9.217/fs/proc/loadavg.c linux-4.9.217-vs2.3.9.12/fs/proc/loadavg.c
---- linux-4.9.217/fs/proc/loadavg.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc/loadavg.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc/loadavg.c linux-4.9/fs/proc/loadavg.c
+--- linux-4.9/fs/proc/loadavg.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/proc/loadavg.c 2021-02-24 15:47:45.084409345 +0100
@@ -12,15 +12,27 @@
static int loadavg_proc_show(struct seq_file *m, void *v)
task_active_pid_ns(current)->last_pid);
return 0;
}
-diff -NurpP --minimal linux-4.9.217/fs/proc/meminfo.c linux-4.9.217-vs2.3.9.12/fs/proc/meminfo.c
---- linux-4.9.217/fs/proc/meminfo.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc/meminfo.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc/meminfo.c linux-4.9/fs/proc/meminfo.c
+--- linux-4.9/fs/proc/meminfo.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/proc/meminfo.c 2021-02-24 15:47:45.084409345 +0100
@@ -55,7 +55,8 @@ static int meminfo_proc_show(struct seq_
si_swapinfo(&i);
committed = percpu_counter_read_positive(&vm_committed_as);
total_swapcache_pages() - i.bufferram;
if (cached < 0)
cached = 0;
-diff -NurpP --minimal linux-4.9.217/fs/proc/root.c linux-4.9.217-vs2.3.9.12/fs/proc/root.c
---- linux-4.9.217/fs/proc/root.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc/root.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc/root.c linux-4.9/fs/proc/root.c
+--- linux-4.9/fs/proc/root.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/proc/root.c 2021-02-24 15:47:45.084409345 +0100
@@ -20,9 +20,14 @@
#include <linux/mount.h>
#include <linux/pid_namespace.h>
.subdir = RB_ROOT,
.name = "/proc",
};
-diff -NurpP --minimal linux-4.9.217/fs/proc/self.c linux-4.9.217-vs2.3.9.12/fs/proc/self.c
---- linux-4.9.217/fs/proc/self.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc/self.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc/self.c linux-4.9/fs/proc/self.c
+--- linux-4.9/fs/proc/self.c 2021-02-24 15:47:32.324010153 +0100
++++ linux-4.9/fs/proc/self.c 2021-02-24 15:47:45.084409345 +0100
@@ -1,6 +1,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include "internal.h"
/*
-@@ -54,6 +55,8 @@ int proc_setup_self(struct super_block *
+@@ -61,6 +62,8 @@ int proc_setup_self(struct super_block *
self = d_alloc_name(s->s_root, "self");
if (self) {
struct inode *inode = new_inode(s);
if (inode) {
inode->i_ino = self_inum;
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
-diff -NurpP --minimal linux-4.9.217/fs/proc/stat.c linux-4.9.217-vs2.3.9.12/fs/proc/stat.c
---- linux-4.9.217/fs/proc/stat.c 2020-03-27 00:51:25.690417888 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc/stat.c 2019-10-13 15:58:54.758080005 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc/stat.c linux-4.9/fs/proc/stat.c
+--- linux-4.9/fs/proc/stat.c 2021-02-24 15:47:32.324010153 +0100
++++ linux-4.9/fs/proc/stat.c 2021-02-24 15:47:45.084409345 +0100
@@ -9,8 +9,10 @@
#include <linux/slab.h>
#include <linux/time.h>
/* Copy values here to work around gcc-2.95.3, gcc-2.96 */
user = kcpustat_cpu(i).cpustat[CPUTIME_USER];
nice = kcpustat_cpu(i).cpustat[CPUTIME_NICE];
-diff -NurpP --minimal linux-4.9.217/fs/proc/uptime.c linux-4.9.217-vs2.3.9.12/fs/proc/uptime.c
---- linux-4.9.217/fs/proc/uptime.c 2020-03-27 00:51:25.690417888 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc/uptime.c 2019-10-13 16:02:19.324763467 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc/uptime.c linux-4.9/fs/proc/uptime.c
+--- linux-4.9/fs/proc/uptime.c 2021-02-24 15:47:32.327343590 +0100
++++ linux-4.9/fs/proc/uptime.c 2021-02-24 15:47:45.084409345 +0100
@@ -5,6 +5,7 @@
#include <linux/seq_file.h>
#include <linux/time.h>
seq_printf(m, "%lu.%02lu %lu.%02lu\n",
(unsigned long) uptime.tv_sec,
(uptime.tv_nsec / (NSEC_PER_SEC / 100)),
-diff -NurpP --minimal linux-4.9.217/fs/proc_namespace.c linux-4.9.217-vs2.3.9.12/fs/proc_namespace.c
---- linux-4.9.217/fs/proc_namespace.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/proc_namespace.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/proc_namespace.c linux-4.9/fs/proc_namespace.c
+--- linux-4.9/fs/proc_namespace.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/proc_namespace.c 2021-02-24 15:47:45.084409345 +0100
@@ -46,6 +46,8 @@ static int show_sb_opts(struct seq_file
{ MS_DIRSYNC, ",dirsync" },
{ MS_MANDLOCK, ",mand" },
/* file system type */
seq_puts(m, "with fstype ");
show_type(m, sb);
-diff -NurpP --minimal linux-4.9.217/fs/quota/dquot.c linux-4.9.217-vs2.3.9.12/fs/quota/dquot.c
---- linux-4.9.217/fs/quota/dquot.c 2020-03-27 00:51:25.790416308 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/quota/dquot.c 2020-04-01 09:40:28.705463971 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/quota/dquot.c linux-4.9/fs/quota/dquot.c
+--- linux-4.9/fs/quota/dquot.c 2021-02-24 15:47:32.327343590 +0100
++++ linux-4.9/fs/quota/dquot.c 2021-02-24 15:47:45.084409345 +0100
@@ -1659,6 +1659,9 @@ int __dquot_alloc_space(struct inode *in
int reserve = flags & DQUOT_SPACE_RESERVE;
struct dquot **dquots;
if (!dquot_active(inode))
return;
-diff -NurpP --minimal linux-4.9.217/fs/quota/quota.c linux-4.9.217-vs2.3.9.12/fs/quota/quota.c
---- linux-4.9.217/fs/quota/quota.c 2020-03-27 00:51:25.840415520 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/quota/quota.c 2018-10-20 05:55:43.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/quota/quota.c linux-4.9/fs/quota/quota.c
+--- linux-4.9/fs/quota/quota.c 2021-02-24 15:47:32.327343590 +0100
++++ linux-4.9/fs/quota/quota.c 2021-02-24 15:47:45.084409345 +0100
@@ -8,6 +8,7 @@
#include <linux/fs.h>
#include <linux/namei.h>
if (quotactl_cmd_write(cmd))
sb = get_super_thawed(bdev);
else
-diff -NurpP --minimal linux-4.9.217/fs/stat.c linux-4.9.217-vs2.3.9.12/fs/stat.c
---- linux-4.9.217/fs/stat.c 2020-03-27 00:51:26.590403699 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/stat.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/stat.c linux-4.9/fs/stat.c
+--- linux-4.9/fs/stat.c 2021-02-24 15:47:32.334010466 +0100
++++ linux-4.9/fs/stat.c 2021-02-24 15:47:45.084409345 +0100
@@ -26,6 +26,7 @@ void generic_fillattr(struct inode *inod
stat->nlink = inode->i_nlink;
stat->uid = inode->i_uid;
stat->rdev = inode->i_rdev;
stat->size = i_size_read(inode);
stat->atime = inode->i_atime;
-diff -NurpP --minimal linux-4.9.217/fs/statfs.c linux-4.9.217-vs2.3.9.12/fs/statfs.c
---- linux-4.9.217/fs/statfs.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/statfs.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/statfs.c linux-4.9/fs/statfs.c
+--- linux-4.9/fs/statfs.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/statfs.c 2021-02-24 15:47:45.084409345 +0100
@@ -7,6 +7,8 @@
#include <linux/statfs.h>
#include <linux/security.h>
return retval;
}
-diff -NurpP --minimal linux-4.9.217/fs/super.c linux-4.9.217-vs2.3.9.12/fs/super.c
---- linux-4.9.217/fs/super.c 2020-03-27 00:51:26.590403699 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/super.c 2019-10-13 10:11:07.125382902 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/super.c linux-4.9/fs/super.c
+--- linux-4.9/fs/super.c 2021-02-24 15:47:32.334010466 +0100
++++ linux-4.9/fs/super.c 2021-02-24 15:47:45.084409345 +0100
@@ -34,6 +34,8 @@
#include <linux/fsnotify.h>
#include <linux/lockdep.h>
error = security_sb_kern_mount(sb, flags, secdata);
if (error)
goto out_sb;
-diff -NurpP --minimal linux-4.9.217/fs/utimes.c linux-4.9.217-vs2.3.9.12/fs/utimes.c
---- linux-4.9.217/fs/utimes.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/utimes.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/utimes.c linux-4.9/fs/utimes.c
+--- linux-4.9/fs/utimes.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/fs/utimes.c 2021-02-24 15:47:45.084409345 +0100
@@ -8,6 +8,8 @@
#include <linux/stat.h>
#include <linux/utime.h>
if (times && times[0].tv_nsec == UTIME_NOW &&
times[1].tv_nsec == UTIME_NOW)
times = NULL;
-diff -NurpP --minimal linux-4.9.217/fs/xattr.c linux-4.9.217-vs2.3.9.12/fs/xattr.c
---- linux-4.9.217/fs/xattr.c 2020-03-27 00:51:27.560388412 +0000
-+++ linux-4.9.217-vs2.3.9.12/fs/xattr.c 2018-10-20 05:55:43.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/fs/xattr.c linux-4.9/fs/xattr.c
+--- linux-4.9/fs/xattr.c 2021-02-24 15:47:32.340677341 +0100
++++ linux-4.9/fs/xattr.c 2021-02-24 15:47:45.084409345 +0100
@@ -21,6 +21,7 @@
#include <linux/audit.h>
#include <linux/vmalloc.h>
return (mask & MAY_WRITE) ? -EPERM : -ENODATA;
return 0;
}
-diff -NurpP --minimal linux-4.9.217/include/linux/capability.h linux-4.9.217-vs2.3.9.12/include/linux/capability.h
---- linux-4.9.217/include/linux/capability.h 2020-03-27 00:51:31.040333567 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/capability.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/capability.h linux-4.9/include/linux/capability.h
+--- linux-4.9/include/linux/capability.h 2021-02-24 15:47:32.377345154 +0100
++++ linux-4.9/include/linux/capability.h 2021-02-24 15:47:45.084409345 +0100
@@ -78,7 +78,8 @@ extern const kernel_cap_t __cap_init_eff
#else /* HAND-CODED capability initializers */
# define CAP_EMPTY_SET ((kernel_cap_t){{ 0, 0 }})
# define CAP_FULL_SET ((kernel_cap_t){{ ~0, CAP_LAST_U32_VALID_MASK }})
-diff -NurpP --minimal linux-4.9.217/include/linux/cred.h linux-4.9.217-vs2.3.9.12/include/linux/cred.h
---- linux-4.9.217/include/linux/cred.h 2020-03-27 00:51:31.560325368 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/cred.h 2019-10-05 14:58:45.680307395 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/cred.h linux-4.9/include/linux/cred.h
+--- linux-4.9/include/linux/cred.h 2021-02-24 15:47:32.380678592 +0100
++++ linux-4.9/include/linux/cred.h 2021-02-24 15:47:45.087742783 +0100
@@ -156,6 +156,7 @@ extern void exit_creds(struct task_struc
extern int copy_creds(struct task_struct *, unsigned long);
extern const struct cred *get_task_cred(struct task_struct *);
/**
* get_new_cred - Get a reference on a new set of credentials
* @cred: The new credentials to reference
-diff -NurpP --minimal linux-4.9.217/include/linux/dcache.h linux-4.9.217-vs2.3.9.12/include/linux/dcache.h
---- linux-4.9.217/include/linux/dcache.h 2020-03-27 00:51:31.570325213 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/dcache.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/dcache.h linux-4.9/include/linux/dcache.h
+--- linux-4.9/include/linux/dcache.h 2021-02-24 15:47:32.380678592 +0100
++++ linux-4.9/include/linux/dcache.h 2021-02-24 15:47:45.087742783 +0100
@@ -308,8 +308,10 @@ extern char *dentry_path(struct dentry *
*/
static inline struct dentry *dget_dlock(struct dentry *dentry)
return dentry;
}
-diff -NurpP --minimal linux-4.9.217/include/linux/devpts_fs.h linux-4.9.217-vs2.3.9.12/include/linux/devpts_fs.h
---- linux-4.9.217/include/linux/devpts_fs.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/devpts_fs.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/devpts_fs.h linux-4.9/include/linux/devpts_fs.h
+--- linux-4.9/include/linux/devpts_fs.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/devpts_fs.h 2021-02-24 15:47:45.087742783 +0100
@@ -34,5 +34,4 @@ void devpts_pty_kill(struct dentry *);
#endif
-
#endif /* _LINUX_DEVPTS_FS_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/fs.h linux-4.9.217-vs2.3.9.12/include/linux/fs.h
---- linux-4.9.217/include/linux/fs.h 2020-03-27 00:51:31.760322216 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/fs.h 2019-10-05 14:58:45.690307237 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/fs.h linux-4.9/include/linux/fs.h
+--- linux-4.9/include/linux/fs.h 2021-02-24 15:47:32.384012030 +0100
++++ linux-4.9/include/linux/fs.h 2021-02-24 15:47:45.087742783 +0100
@@ -231,6 +231,7 @@ typedef int (dio_iodone_t)(struct kiocb
#define ATTR_OPEN (1 << 15) /* Truncating from open(O_TRUNC) */
#define ATTR_TIMES_SET (1 << 16)
loff_t i_size;
struct timespec i_atime;
struct timespec i_mtime;
-@@ -845,14 +850,19 @@ static inline void i_size_write(struct i
+@@ -846,14 +851,19 @@ static inline void i_size_write(struct i
#endif
}
}
extern struct block_device *I_BDEV(struct inode *inode);
-@@ -909,6 +919,7 @@ struct file {
+@@ -910,6 +920,7 @@ struct file {
loff_t f_pos;
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
-@@ -1043,6 +1054,7 @@ struct file_lock {
+@@ -1044,6 +1055,7 @@ struct file_lock {
struct file *fl_file;
loff_t fl_start;
loff_t fl_end;
struct fasync_struct * fl_fasync; /* for lease break notifications */
/* for lease breaks: */
-@@ -1476,6 +1488,11 @@ static inline gid_t i_gid_read(const str
+@@ -1477,6 +1489,11 @@ static inline gid_t i_gid_read(const str
return from_kgid(inode->i_sb->s_user_ns, inode->i_gid);
}
static inline void i_uid_write(struct inode *inode, uid_t uid)
{
inode->i_uid = make_kuid(inode->i_sb->s_user_ns, uid);
-@@ -1765,6 +1782,7 @@ struct inode_operations {
+@@ -1766,6 +1783,7 @@ struct inode_operations {
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
int (*update_time)(struct inode *, struct timespec *, int);
-@@ -1779,6 +1797,7 @@ ssize_t rw_copy_check_uvector(int type,
+@@ -1780,6 +1798,7 @@ ssize_t rw_copy_check_uvector(int type,
unsigned long nr_segs, unsigned long fast_segs,
struct iovec *fast_pointer,
struct iovec **ret_pointer);
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 *);
-@@ -1850,6 +1869,14 @@ struct super_operations {
+@@ -1851,6 +1870,14 @@ struct super_operations {
#else
#define S_DAX 0 /* Make all the DAX code disappear */
#endif
/*
* Note that nosuid etc flags are inode-specific: setting some file-system
-@@ -1874,10 +1901,13 @@ struct super_operations {
+@@ -1875,10 +1902,13 @@ struct super_operations {
#define IS_MANDLOCK(inode) __IS_FLG(inode, MS_MANDLOCK)
#define IS_NOATIME(inode) __IS_FLG(inode, MS_RDONLY|MS_NOATIME)
#define IS_I_VERSION(inode) __IS_FLG(inode, MS_I_VERSION)
#define IS_POSIXACL(inode) __IS_FLG(inode, MS_POSIXACL)
#define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD)
-@@ -1897,6 +1927,16 @@ static inline bool HAS_UNMAPPED_ID(struc
+@@ -1898,6 +1928,16 @@ static inline bool HAS_UNMAPPED_ID(struc
return !uid_valid(inode->i_uid) || !gid_valid(inode->i_gid);
}
/*
* Inode state bits. Protected by inode->i_lock
*
-@@ -2162,6 +2202,9 @@ extern struct kobject *fs_kobj;
+@@ -2167,6 +2207,9 @@ extern struct kobject *fs_kobj;
extern int locks_mandatory_locked(struct file *);
extern int locks_mandatory_area(struct inode *, struct file *, loff_t, loff_t, unsigned char);
/*
* Candidates for mandatory locking have the setgid bit set
* but no group execute bit - an otherwise meaningless combination.
-@@ -2342,7 +2385,7 @@ struct filename {
+@@ -2347,7 +2390,7 @@ struct filename {
const char iname[];
};
extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
struct file *filp);
extern int vfs_fallocate(struct file *file, int mode, loff_t offset,
-@@ -2973,6 +3016,7 @@ extern int dcache_dir_open(struct inode
+@@ -2978,6 +3021,7 @@ extern int dcache_dir_open(struct inode
extern int dcache_dir_close(struct inode *, struct file *);
extern loff_t dcache_dir_lseek(struct file *, loff_t, int);
extern int dcache_readdir(struct file *, struct dir_context *);
extern int simple_setattr(struct dentry *, struct iattr *);
extern int simple_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int simple_statfs(struct dentry *, struct kstatfs *);
-diff -NurpP --minimal linux-4.9.217/include/linux/init_task.h linux-4.9.217-vs2.3.9.12/include/linux/init_task.h
---- linux-4.9.217/include/linux/init_task.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/init_task.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/init_task.h linux-4.9/include/linux/init_task.h
+--- linux-4.9/include/linux/init_task.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/init_task.h 2021-02-24 15:47:45.087742783 +0100
@@ -271,6 +271,10 @@ extern struct task_group root_task_group
INIT_VTIME(tsk) \
INIT_NUMA_BALANCING(tsk) \
}
-diff -NurpP --minimal linux-4.9.217/include/linux/ipc.h linux-4.9.217-vs2.3.9.12/include/linux/ipc.h
---- linux-4.9.217/include/linux/ipc.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/ipc.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/ipc.h linux-4.9/include/linux/ipc.h
+--- linux-4.9/include/linux/ipc.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/ipc.h 2021-02-24 15:47:45.087742783 +0100
@@ -16,6 +16,7 @@ struct kern_ipc_perm
key_t key;
kuid_t uid;
kuid_t cuid;
kgid_t cgid;
umode_t mode;
-diff -NurpP --minimal linux-4.9.217/include/linux/memcontrol.h linux-4.9.217-vs2.3.9.12/include/linux/memcontrol.h
---- linux-4.9.217/include/linux/memcontrol.h 2020-03-27 00:51:33.430295898 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/memcontrol.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/memcontrol.h linux-4.9/include/linux/memcontrol.h
+--- linux-4.9/include/linux/memcontrol.h 2021-02-24 15:47:32.397345780 +0100
++++ linux-4.9/include/linux/memcontrol.h 2021-02-24 15:47:45.087742783 +0100
@@ -92,6 +92,7 @@ enum mem_cgroup_events_target {
MEM_CGROUP_NTARGETS,
};
static inline bool mm_match_cgroup(struct mm_struct *mm,
struct mem_cgroup *memcg)
{
-diff -NurpP --minimal linux-4.9.217/include/linux/mount.h linux-4.9.217-vs2.3.9.12/include/linux/mount.h
---- linux-4.9.217/include/linux/mount.h 2020-03-27 00:51:37.020239318 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/mount.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/mount.h linux-4.9/include/linux/mount.h
+--- linux-4.9/include/linux/mount.h 2021-02-24 15:47:32.400679218 +0100
++++ linux-4.9/include/linux/mount.h 2021-02-24 15:47:45.087742783 +0100
@@ -63,6 +63,9 @@ struct mnt_namespace;
#define MNT_MARKED 0x4000000
#define MNT_UMOUNT 0x8000000
struct vfsmount {
struct dentry *mnt_root; /* root of the mounted tree */
struct super_block *mnt_sb; /* pointer to superblock */
-diff -NurpP --minimal linux-4.9.217/include/linux/netdevice.h linux-4.9.217-vs2.3.9.12/include/linux/netdevice.h
---- linux-4.9.217/include/linux/netdevice.h 2020-03-27 00:51:37.160237109 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/netdevice.h 2019-12-25 15:37:52.148426794 +0000
-@@ -2489,6 +2489,7 @@ static inline int dev_recursion_level(vo
-
- struct net_device *dev_get_by_index(struct net *net, int ifindex);
- struct net_device *__dev_get_by_index(struct net *net, int ifindex);
-+struct net_device *dev_get_by_index_real_rcu(struct net *net, int ifindex);
- struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex);
- int netdev_get_name(struct net *net, char *name, int ifindex);
- int dev_restart(struct net_device *dev);
-diff -NurpP --minimal linux-4.9.217/include/linux/net.h linux-4.9.217-vs2.3.9.12/include/linux/net.h
---- linux-4.9.217/include/linux/net.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/net.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/net.h linux-4.9/include/linux/net.h
+--- linux-4.9/include/linux/net.h 2021-02-24 15:47:32.400679218 +0100
++++ linux-4.9/include/linux/net.h 2021-02-24 15:47:45.091076220 +0100
@@ -44,6 +44,7 @@ struct net;
#define SOCK_NOSPACE 2
#define SOCK_PASSCRED 3
#ifndef ARCH_HAS_SOCKET_TYPES
/**
-diff -NurpP --minimal linux-4.9.217/include/linux/nsproxy.h linux-4.9.217-vs2.3.9.12/include/linux/nsproxy.h
---- linux-4.9.217/include/linux/nsproxy.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/nsproxy.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/netdevice.h linux-4.9/include/linux/netdevice.h
+--- linux-4.9/include/linux/netdevice.h 2021-02-24 15:47:33.074033615 +0100
++++ linux-4.9/include/linux/netdevice.h 2021-02-24 15:47:45.087742783 +0100
+@@ -2489,6 +2489,7 @@ static inline int dev_recursion_level(vo
+
+ struct net_device *dev_get_by_index(struct net *net, int ifindex);
+ struct net_device *__dev_get_by_index(struct net *net, int ifindex);
++struct net_device *dev_get_by_index_real_rcu(struct net *net, int ifindex);
+ struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex);
+ int netdev_get_name(struct net *net, char *name, int ifindex);
+ int dev_restart(struct net_device *dev);
+diff -urNp -x '*.orig' linux-4.9/include/linux/nsproxy.h linux-4.9/include/linux/nsproxy.h
+--- linux-4.9/include/linux/nsproxy.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/nsproxy.h 2021-02-24 15:47:45.091076220 +0100
@@ -3,6 +3,7 @@
#include <linux/spinlock.h>
}
#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/pid.h linux-4.9.217-vs2.3.9.12/include/linux/pid.h
---- linux-4.9.217/include/linux/pid.h 2020-03-27 00:51:38.850210473 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/pid.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/pid.h linux-4.9/include/linux/pid.h
+--- linux-4.9/include/linux/pid.h 2021-02-24 15:47:32.407346093 +0100
++++ linux-4.9/include/linux/pid.h 2021-02-24 15:47:45.091076220 +0100
@@ -10,7 +10,8 @@ enum pid_type
PIDTYPE_SID,
PIDTYPE_MAX,
pid_t pid_vnr(struct pid *pid);
#define do_each_pid_task(pid, type, task) \
-diff -NurpP --minimal linux-4.9.217/include/linux/quotaops.h linux-4.9.217-vs2.3.9.12/include/linux/quotaops.h
---- linux-4.9.217/include/linux/quotaops.h 2020-03-27 00:51:40.950177377 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/quotaops.h 2019-12-25 15:37:52.158426633 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/quotaops.h linux-4.9/include/linux/quotaops.h
+--- linux-4.9/include/linux/quotaops.h 2021-02-24 15:47:32.410679531 +0100
++++ linux-4.9/include/linux/quotaops.h 2021-02-24 15:47:45.091076220 +0100
@@ -8,6 +8,7 @@
#define _LINUX_QUOTAOPS_
}
static inline int dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
-diff -NurpP --minimal linux-4.9.217/include/linux/sched.h linux-4.9.217-vs2.3.9.12/include/linux/sched.h
---- linux-4.9.217/include/linux/sched.h 2020-03-27 00:51:41.060175640 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/sched.h 2019-10-05 14:58:45.710306917 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/sched.h linux-4.9/include/linux/sched.h
+--- linux-4.9/include/linux/sched.h 2021-02-24 15:47:32.414012968 +0100
++++ linux-4.9/include/linux/sched.h 2021-02-24 15:47:45.091076220 +0100
@@ -1719,6 +1719,14 @@ struct task_struct {
#endif
struct seccomp seccomp;
+ vtag_t tag;
+
/* Thread group tracking */
- u32 parent_exec_id;
- u32 self_exec_id;
-@@ -2112,6 +2120,11 @@ struct pid_namespace;
+ u64 parent_exec_id;
+ u64 self_exec_id;
+@@ -2114,6 +2122,11 @@ struct pid_namespace;
pid_t __task_pid_nr_ns(struct task_struct *task, enum pid_type type,
struct pid_namespace *ns);
static inline pid_t task_pid_nr(struct task_struct *tsk)
{
return tsk->pid;
-@@ -2125,7 +2138,8 @@ static inline pid_t task_pid_nr_ns(struc
+@@ -2127,7 +2140,8 @@ static inline pid_t task_pid_nr_ns(struc
static inline pid_t task_pid_vnr(struct task_struct *tsk)
{
}
-diff -NurpP --minimal linux-4.9.217/include/linux/shmem_fs.h linux-4.9.217-vs2.3.9.12/include/linux/shmem_fs.h
---- linux-4.9.217/include/linux/shmem_fs.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/shmem_fs.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/shmem_fs.h linux-4.9/include/linux/shmem_fs.h
+--- linux-4.9/include/linux/shmem_fs.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/shmem_fs.h 2021-02-24 15:47:45.091076220 +0100
@@ -10,6 +10,9 @@
/* inode in-kernel data */
struct shmem_inode_info {
spinlock_t lock;
unsigned int seals; /* shmem seals */
-diff -NurpP --minimal linux-4.9.217/include/linux/stat.h linux-4.9.217-vs2.3.9.12/include/linux/stat.h
---- linux-4.9.217/include/linux/stat.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/stat.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/stat.h linux-4.9/include/linux/stat.h
+--- linux-4.9/include/linux/stat.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/stat.h 2021-02-24 15:47:45.091076220 +0100
@@ -25,6 +25,7 @@ struct kstat {
unsigned int nlink;
kuid_t uid;
dev_t rdev;
loff_t size;
struct timespec atime;
-diff -NurpP --minimal linux-4.9.217/include/linux/sunrpc/auth.h linux-4.9.217-vs2.3.9.12/include/linux/sunrpc/auth.h
---- linux-4.9.217/include/linux/sunrpc/auth.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/sunrpc/auth.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/sunrpc/auth.h linux-4.9/include/linux/sunrpc/auth.h
+--- linux-4.9/include/linux/sunrpc/auth.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/sunrpc/auth.h 2021-02-24 15:47:45.091076220 +0100
@@ -46,6 +46,7 @@ enum {
struct auth_cred {
kuid_t uid;
struct group_info *group_info;
const char *principal;
unsigned long ac_flags;
-diff -NurpP --minimal linux-4.9.217/include/linux/sunrpc/clnt.h linux-4.9.217-vs2.3.9.12/include/linux/sunrpc/clnt.h
---- linux-4.9.217/include/linux/sunrpc/clnt.h 2020-03-27 00:51:41.170173910 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/sunrpc/clnt.h 2018-10-20 05:55:43.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/sunrpc/clnt.h linux-4.9/include/linux/sunrpc/clnt.h
+--- linux-4.9/include/linux/sunrpc/clnt.h 2021-02-24 15:47:32.417346405 +0100
++++ linux-4.9/include/linux/sunrpc/clnt.h 2021-02-24 15:47:45.091076220 +0100
@@ -52,7 +52,8 @@ struct rpc_clnt {
cl_discrtry : 1,/* disconnect before retry */
cl_noretranstimeo: 1,/* No retransmit timeouts */
struct rpc_rtt * cl_rtt; /* RTO estimator data */
const struct rpc_timeout *cl_timeout; /* Timeout strategy */
-diff -NurpP --minimal linux-4.9.217/include/linux/types.h linux-4.9.217-vs2.3.9.12/include/linux/types.h
---- linux-4.9.217/include/linux/types.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/types.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/types.h linux-4.9/include/linux/types.h
+--- linux-4.9/include/linux/types.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/types.h 2021-02-24 15:47:45.091076220 +0100
@@ -32,6 +32,9 @@ typedef __kernel_uid32_t uid_t;
typedef __kernel_gid32_t gid_t;
typedef __kernel_uid16_t uid16_t;
typedef unsigned long uintptr_t;
-diff -NurpP --minimal linux-4.9.217/include/linux/uidgid.h linux-4.9.217-vs2.3.9.12/include/linux/uidgid.h
---- linux-4.9.217/include/linux/uidgid.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/uidgid.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/uidgid.h linux-4.9/include/linux/uidgid.h
+--- linux-4.9/include/linux/uidgid.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/linux/uidgid.h 2021-02-24 15:47:45.091076220 +0100
@@ -21,13 +21,17 @@ typedef struct {
uid_t val;
} kuid_t;
static inline uid_t from_kuid_munged(struct user_namespace *to, kuid_t kuid)
{
uid_t uid = from_kuid(to, kuid);
-diff -NurpP --minimal linux-4.9.217/include/linux/vroot.h linux-4.9.217-vs2.3.9.12/include/linux/vroot.h
---- linux-4.9.217/include/linux/vroot.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vroot.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/vroot.h linux-4.9/include/linux/vroot.h
+--- linux-4.9/include/linux/vroot.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vroot.h 2021-02-24 15:47:45.091076220 +0100
@@ -0,0 +1,51 @@
+
+/*
+#define VROOT_CLR_DEV 0x5601
+
+#endif /* _LINUX_VROOT_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_base.h linux-4.9.217-vs2.3.9.12/include/linux/vs_base.h
---- linux-4.9.217/include/linux/vs_base.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_base.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_base.h linux-4.9/include/linux/vs_base.h
+--- linux-4.9/include/linux/vs_base.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_base.h 2021-02-24 15:47:45.091076220 +0100
@@ -0,0 +1,10 @@
+#ifndef _VS_BASE_H
+#define _VS_BASE_H
+#else
+#warning duplicate inclusion
+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_context.h linux-4.9.217-vs2.3.9.12/include/linux/vs_context.h
---- linux-4.9.217/include/linux/vs_context.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_context.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_context.h linux-4.9/include/linux/vs_context.h
+--- linux-4.9/include/linux/vs_context.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_context.h 2021-02-24 15:47:45.091076220 +0100
@@ -0,0 +1,242 @@
+#ifndef _VS_CONTEXT_H
+#define _VS_CONTEXT_H
+#else
+#warning duplicate inclusion
+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_cowbl.h linux-4.9.217-vs2.3.9.12/include/linux/vs_cowbl.h
---- linux-4.9.217/include/linux/vs_cowbl.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_cowbl.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_cowbl.h linux-4.9/include/linux/vs_cowbl.h
+--- linux-4.9/include/linux/vs_cowbl.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_cowbl.h 2021-02-24 15:47:45.094409658 +0100
@@ -0,0 +1,48 @@
+#ifndef _VS_COWBL_H
+#define _VS_COWBL_H
+#else
+#warning duplicate inclusion
+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_cvirt.h linux-4.9.217-vs2.3.9.12/include/linux/vs_cvirt.h
---- linux-4.9.217/include/linux/vs_cvirt.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_cvirt.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_cvirt.h linux-4.9/include/linux/vs_cvirt.h
+--- linux-4.9/include/linux/vs_cvirt.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_cvirt.h 2021-02-24 15:47:45.094409658 +0100
@@ -0,0 +1,50 @@
+#ifndef _VS_CVIRT_H
+#define _VS_CVIRT_H
+#else
+#warning duplicate inclusion
+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_device.h linux-4.9.217-vs2.3.9.12/include/linux/vs_device.h
---- linux-4.9.217/include/linux/vs_device.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_device.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_device.h linux-4.9/include/linux/vs_device.h
+--- linux-4.9/include/linux/vs_device.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_device.h 2021-02-24 15:47:45.094409658 +0100
@@ -0,0 +1,45 @@
+#ifndef _VS_DEVICE_H
+#define _VS_DEVICE_H
+#else
+#warning duplicate inclusion
+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_dlimit.h linux-4.9.217-vs2.3.9.12/include/linux/vs_dlimit.h
---- linux-4.9.217/include/linux/vs_dlimit.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_dlimit.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_dlimit.h linux-4.9/include/linux/vs_dlimit.h
+--- linux-4.9/include/linux/vs_dlimit.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_dlimit.h 2021-02-24 15:47:45.094409658 +0100
@@ -0,0 +1,215 @@
+#ifndef _VS_DLIMIT_H
+#define _VS_DLIMIT_H
+#else
+#warning duplicate inclusion
+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/base.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/base.h
---- linux-4.9.217/include/linux/vserver/base.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/base.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,184 @@
-+#ifndef _VSERVER_BASE_H
-+#define _VSERVER_BASE_H
-+
-+
-+/* context state changes */
-+
-+enum {
-+ VSC_STARTUP = 1,
-+ VSC_SHUTDOWN,
-+
-+ VSC_NETUP,
-+ VSC_NETDOWN,
-+};
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_inet.h linux-4.9/include/linux/vs_inet.h
+--- linux-4.9/include/linux/vs_inet.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_inet.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,364 @@
++#ifndef _VS_INET_H
++#define _VS_INET_H
+
++#include "vserver/base.h"
++#include "vserver/network.h"
++#include "vserver/debug.h"
+
++#define IPI_LOOPBACK htonl(INADDR_LOOPBACK)
+
-+#define vx_task_xid(t) ((t)->xid)
++#define NXAV4(a) NIPQUAD((a)->ip[0]), NIPQUAD((a)->ip[1]), \
++ NIPQUAD((a)->mask), (a)->type
++#define NXAV4_FMT "[" NIPQUAD_FMT "-" NIPQUAD_FMT "/" NIPQUAD_FMT ":%04x]"
+
-+#define vx_current_xid() vx_task_xid(current)
++#define NIPQUAD(addr) \
++ ((unsigned char *)&addr)[0], \
++ ((unsigned char *)&addr)[1], \
++ ((unsigned char *)&addr)[2], \
++ ((unsigned char *)&addr)[3]
+
-+#define current_vx_info() (current->vx_info)
++#define NIPQUAD_FMT "%u.%u.%u.%u"
+
+
-+#define nx_task_nid(t) ((t)->nid)
++static inline
++int v4_addr_match(struct nx_addr_v4 *nxa, __be32 addr, uint16_t tmask)
++{
++ __be32 ip = nxa->ip[0].s_addr;
++ __be32 mask = nxa->mask.s_addr;
++ __be32 bcast = ip | ~mask;
++ int ret = 0;
+
-+#define nx_current_nid() nx_task_nid(current)
++ switch (nxa->type & tmask) {
++ case NXA_TYPE_MASK:
++ ret = (ip == (addr & mask));
++ break;
++ case NXA_TYPE_ADDR:
++ ret = 3;
++ if (addr == ip)
++ break;
++ /* fall through to broadcast */
++ case NXA_MOD_BCAST:
++ ret = ((tmask & NXA_MOD_BCAST) && (addr == bcast));
++ break;
++ case NXA_TYPE_RANGE:
++ ret = ((nxa->ip[0].s_addr <= addr) &&
++ (nxa->ip[1].s_addr > addr));
++ break;
++ case NXA_TYPE_ANY:
++ ret = 2;
++ break;
++ }
+
-+#define current_nx_info() (current->nx_info)
++ vxdprintk(VXD_CBIT(net, 0),
++ "v4_addr_match(%p" NXAV4_FMT "," NIPQUAD_FMT ",%04x) = %d",
++ nxa, NXAV4(nxa), NIPQUAD(addr), tmask, ret);
++ return ret;
++}
+
++static inline
++int v4_addr_in_nx_info(struct nx_info *nxi, __be32 addr, uint16_t tmask)
++{
++ struct nx_addr_v4 *nxa;
++ unsigned long irqflags;
++ int ret = 1;
+
-+/* generic flag merging */
++ if (!nxi)
++ goto out;
+
-+#define vs_check_flags(v, m, f) (((v) & (m)) ^ (f))
++ ret = 2;
++ /* allow 127.0.0.1 when remapping lback */
++ if ((tmask & NXA_LOOPBACK) &&
++ (addr == IPI_LOOPBACK) &&
++ nx_info_flags(nxi, NXF_LBACK_REMAP, 0))
++ goto out;
++ ret = 3;
++ /* check for lback address */
++ if ((tmask & NXA_MOD_LBACK) &&
++ (nxi->v4_lback.s_addr == addr))
++ goto out;
++ ret = 4;
++ /* check for broadcast address */
++ if ((tmask & NXA_MOD_BCAST) &&
++ (nxi->v4_bcast.s_addr == addr))
++ goto out;
++ ret = 5;
+
-+#define vs_mask_flags(v, f, m) (((v) & ~(m)) | ((f) & (m)))
++ /* check for v4 addresses */
++ spin_lock_irqsave(&nxi->addr_lock, irqflags);
++ for (nxa = &nxi->v4; nxa; nxa = nxa->next)
++ if (v4_addr_match(nxa, addr, tmask))
++ goto out_unlock;
++ ret = 0;
++out_unlock:
++ spin_unlock_irqrestore(&nxi->addr_lock, irqflags);
++out:
++ vxdprintk(VXD_CBIT(net, 0),
++ "v4_addr_in_nx_info(%p[#%u]," NIPQUAD_FMT ",%04x) = %d",
++ nxi, nxi ? nxi->nx_id : 0, NIPQUAD(addr), tmask, ret);
++ return ret;
++}
+
-+#define vs_mask_mask(v, f, m) (((v) & ~(m)) | ((v) & (f) & (m)))
++static inline
++int v4_nx_addr_match(struct nx_addr_v4 *nxa, struct nx_addr_v4 *addr, uint16_t mask)
++{
++ /* FIXME: needs full range checks */
++ return v4_addr_match(nxa, addr->ip[0].s_addr, mask);
++}
+
-+#define vs_check_bit(v, n) ((v) & (1LL << (n)))
++static inline
++int v4_nx_addr_in_nx_info(struct nx_info *nxi, struct nx_addr_v4 *nxa, uint16_t mask)
++{
++ struct nx_addr_v4 *ptr;
++ unsigned long irqflags;
++ int ret = 1;
+
++ spin_lock_irqsave(&nxi->addr_lock, irqflags);
++ for (ptr = &nxi->v4; ptr; ptr = ptr->next)
++ if (v4_nx_addr_match(ptr, nxa, mask))
++ goto out_unlock;
++ ret = 0;
++out_unlock:
++ spin_unlock_irqrestore(&nxi->addr_lock, irqflags);
++ return ret;
++}
+
-+/* context flags */
++#include <net/inet_sock.h>
+
-+#define __vx_flags(v) ((v) ? (v)->vx_flags : 0)
-+
-+#define vx_current_flags() __vx_flags(current_vx_info())
-+
-+#define vx_info_flags(v, m, f) \
-+ vs_check_flags(__vx_flags(v), m, f)
++/*
++ * Check if a given address matches for a socket
++ *
++ * nxi: the socket's nx_info if any
++ * addr: to be verified address
++ */
++static inline
++int v4_sock_addr_match (
++ struct nx_info *nxi,
++ struct inet_sock *inet,
++ __be32 addr)
++{
++ __be32 saddr = inet->inet_rcv_saddr;
++ __be32 bcast = nxi ? nxi->v4_bcast.s_addr : INADDR_BROADCAST;
+
-+#define task_vx_flags(t, m, f) \
-+ ((t) && vx_info_flags((t)->vx_info, m, f))
++ if (addr && (saddr == addr || bcast == addr))
++ return 1;
++ if (!saddr)
++ return v4_addr_in_nx_info(nxi, addr, NXA_MASK_BIND);
++ return 0;
++}
+
-+#define vx_flags(m, f) vx_info_flags(current_vx_info(), m, f)
+
++/* inet related checks and helpers */
+
-+/* context caps */
+
-+#define __vx_ccaps(v) ((v) ? (v)->vx_ccaps : 0)
++struct in_ifaddr;
++struct net_device;
++struct sock;
+
-+#define vx_current_ccaps() __vx_ccaps(current_vx_info())
++#ifdef CONFIG_INET
+
-+#define vx_info_ccaps(v, c) (__vx_ccaps(v) & (c))
++#include <linux/netdevice.h>
++#include <linux/inetdevice.h>
++#include <net/inet_sock.h>
++#include <net/inet_timewait_sock.h>
+
-+#define vx_ccaps(c) vx_info_ccaps(current_vx_info(), (c))
+
++int dev_in_nx_info(struct net_device *, struct nx_info *);
++int v4_dev_in_nx_info(struct net_device *, struct nx_info *);
++int nx_v4_addr_conflict(struct nx_info *, struct nx_info *);
+
+
-+/* network flags */
++/*
++ * check if address is covered by socket
++ *
++ * sk: the socket to check against
++ * addr: the address in question (must be != 0)
++ */
+
-+#define __nx_flags(n) ((n) ? (n)->nx_flags : 0)
++static inline
++int __v4_addr_match_socket(const struct sock *sk, struct nx_addr_v4 *nxa)
++{
++ struct nx_info *nxi = sk->sk_nx_info;
++ __be32 saddr = sk->sk_rcv_saddr;
+
-+#define nx_current_flags() __nx_flags(current_nx_info())
++ vxdprintk(VXD_CBIT(net, 5),
++ "__v4_addr_in_socket(%p," NXAV4_FMT ") %p:" NIPQUAD_FMT " %p;%lx",
++ sk, NXAV4(nxa), nxi, NIPQUAD(saddr), sk->sk_socket,
++ (sk->sk_socket?sk->sk_socket->flags:0));
+
-+#define nx_info_flags(n, m, f) \
-+ vs_check_flags(__nx_flags(n), m, f)
++ if (saddr) { /* direct address match */
++ return v4_addr_match(nxa, saddr, -1);
++ } else if (nxi) { /* match against nx_info */
++ return v4_nx_addr_in_nx_info(nxi, nxa, -1);
++ } else { /* unrestricted any socket */
++ return 1;
++ }
++}
+
-+#define task_nx_flags(t, m, f) \
-+ ((t) && nx_info_flags((t)->nx_info, m, f))
+
-+#define nx_flags(m, f) nx_info_flags(current_nx_info(), m, f)
+
++static inline
++int nx_dev_visible(struct nx_info *nxi, struct net_device *dev)
++{
++ vxdprintk(VXD_CBIT(net, 1),
++ "nx_dev_visible(%p[#%u],%p " VS_Q("%s") ") %d",
++ nxi, nxi ? nxi->nx_id : 0, dev, dev->name,
++ nxi ? dev_in_nx_info(dev, nxi) : 0);
+
-+/* network caps */
++ if (!nx_info_flags(nxi, NXF_HIDE_NETIF, 0))
++ return 1;
++ if (dev_in_nx_info(dev, nxi))
++ return 1;
++ return 0;
++}
+
-+#define __nx_ncaps(n) ((n) ? (n)->nx_ncaps : 0)
+
-+#define nx_current_ncaps() __nx_ncaps(current_nx_info())
++static inline
++int v4_ifa_in_nx_info(struct in_ifaddr *ifa, struct nx_info *nxi)
++{
++ if (!nxi)
++ return 1;
++ if (!ifa)
++ return 0;
++ return v4_addr_in_nx_info(nxi, ifa->ifa_local, NXA_MASK_SHOW);
++}
+
-+#define nx_info_ncaps(n, c) (__nx_ncaps(n) & (c))
++static inline
++int nx_v4_ifa_visible(struct nx_info *nxi, struct in_ifaddr *ifa)
++{
++ vxdprintk(VXD_CBIT(net, 1), "nx_v4_ifa_visible(%p[#%u],%p) %d",
++ nxi, nxi ? nxi->nx_id : 0, ifa,
++ nxi ? v4_ifa_in_nx_info(ifa, nxi) : 0);
+
-+#define nx_ncaps(c) nx_info_ncaps(current_nx_info(), c)
++ if (!nx_info_flags(nxi, NXF_HIDE_NETIF, 0))
++ return 1;
++ if (v4_ifa_in_nx_info(ifa, nxi))
++ return 1;
++ return 0;
++}
+
+
-+/* context mask capabilities */
++struct nx_v4_sock_addr {
++ __be32 saddr; /* Address used for validation */
++ __be32 baddr; /* Address used for socket bind */
++};
+
-+#define __vx_mcaps(v) ((v) ? (v)->vx_ccaps >> 32UL : ~0 )
++static inline
++int v4_map_sock_addr(struct inet_sock *inet, struct sockaddr_in *addr,
++ struct nx_v4_sock_addr *nsa)
++{
++ struct sock *sk = &inet->sk;
++ struct nx_info *nxi = sk->sk_nx_info;
++ __be32 saddr = addr->sin_addr.s_addr;
++ __be32 baddr = saddr;
+
-+#define vx_info_mcaps(v, c) (__vx_mcaps(v) & (c))
++ vxdprintk(VXD_CBIT(net, 3),
++ "inet_bind(%p)* %p,%p;%lx " NIPQUAD_FMT,
++ sk, sk->sk_nx_info, sk->sk_socket,
++ (sk->sk_socket ? sk->sk_socket->flags : 0),
++ NIPQUAD(saddr));
+
-+#define vx_mcaps(c) vx_info_mcaps(current_vx_info(), c)
++ if (nxi) {
++ if (saddr == INADDR_ANY) {
++ if (nx_info_flags(nxi, NXF_SINGLE_IP, 0))
++ baddr = nxi->v4.ip[0].s_addr;
++ } else if (saddr == IPI_LOOPBACK) {
++ if (nx_info_flags(nxi, NXF_LBACK_REMAP, 0))
++ baddr = nxi->v4_lback.s_addr;
++ } else if (!ipv4_is_multicast(saddr) ||
++ !nx_info_ncaps(nxi, NXC_MULTICAST)) {
++ /* normal address bind */
++ if (!v4_addr_in_nx_info(nxi, saddr, NXA_MASK_BIND))
++ return -EADDRNOTAVAIL;
++ }
++ }
+
++ vxdprintk(VXD_CBIT(net, 3),
++ "inet_bind(%p) " NIPQUAD_FMT ", " NIPQUAD_FMT,
++ sk, NIPQUAD(saddr), NIPQUAD(baddr));
+
-+/* context bcap mask */
++ nsa->saddr = saddr;
++ nsa->baddr = baddr;
++ return 0;
++}
+
-+#define __vx_bcaps(v) ((v)->vx_bcaps)
++static inline
++void v4_set_sock_addr(struct inet_sock *inet, struct nx_v4_sock_addr *nsa)
++{
++ inet->inet_saddr = nsa->baddr;
++ inet->inet_rcv_saddr = nsa->baddr;
++}
+
-+#define vx_current_bcaps() __vx_bcaps(current_vx_info())
+
++/*
++ * helper to simplify inet_lookup_listener
++ *
++ * nxi: the socket's nx_info if any
++ * addr: to be verified address
++ * saddr: socket address
++ */
++static inline int v4_inet_addr_match (
++ struct nx_info *nxi,
++ __be32 addr,
++ __be32 saddr)
++{
++ if (addr && (saddr == addr))
++ return 1;
++ if (!saddr)
++ return nxi ? v4_addr_in_nx_info(nxi, addr, NXA_MASK_BIND) : 1;
++ return 0;
++}
+
-+/* mask given bcaps */
++static inline __be32 nx_map_sock_lback(struct nx_info *nxi, __be32 addr)
++{
++ if (nx_info_flags(nxi, NXF_HIDE_LBACK, 0) &&
++ (addr == nxi->v4_lback.s_addr))
++ return IPI_LOOPBACK;
++ return addr;
++}
+
-+#define vx_info_mbcaps(v, c) ((v) ? cap_intersect(__vx_bcaps(v), c) : c)
++static inline
++int nx_info_has_v4(struct nx_info *nxi)
++{
++ if (!nxi)
++ return 1;
++ if (NX_IPV4(nxi))
++ return 1;
++ if (nx_info_flags(nxi, NXF_LBACK_REMAP, 0))
++ return 1;
++ return 0;
++}
+
-+#define vx_mbcaps(c) vx_info_mbcaps(current_vx_info(), c)
++#else /* CONFIG_INET */
+
++static inline
++int nx_dev_visible(struct nx_info *n, struct net_device *d)
++{
++ return 1;
++}
+
-+/* masked cap_bset */
++static inline
++int nx_v4_addr_conflict(struct nx_info *n, uint32_t a, const struct sock *s)
++{
++ return 1;
++}
+
-+#define vx_info_cap_bset(v) vx_info_mbcaps(v, current->cap_bset)
++static inline
++int v4_ifa_in_nx_info(struct in_ifaddr *a, struct nx_info *n)
++{
++ return 1;
++}
+
-+#define vx_current_cap_bset() vx_info_cap_bset(current_vx_info())
++static inline
++int nx_info_has_v4(struct nx_info *nxi)
++{
++ return 0;
++}
+
-+#if 0
-+#define vx_info_mbcap(v, b) \
-+ (!vx_info_flags(v, VXF_STATE_SETUP, 0) ? \
-+ vx_info_bcaps(v, b) : (b))
++#endif /* CONFIG_INET */
+
-+#define task_vx_mbcap(t, b) \
-+ vx_info_mbcap((t)->vx_info, (t)->b)
++#define current_nx_info_has_v4() \
++ nx_info_has_v4(current_nx_info())
+
-+#define vx_mbcap(b) task_vx_mbcap(current, b)
++#else
++// #warning duplicate inclusion
+#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_inet6.h linux-4.9/include/linux/vs_inet6.h
+--- linux-4.9/include/linux/vs_inet6.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_inet6.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,264 @@
++#ifndef _VS_INET6_H
++#define _VS_INET6_H
+
-+#define vx_cap_raised(v, c, f) cap_raised(vx_info_mbcaps(v, c), f)
++#include "vserver/base.h"
++#include "vserver/network.h"
++#include "vserver/debug.h"
+
-+#define vx_capable(b, c) (capable(b) || \
-+ (cap_raised(current_cap(), b) && vx_ccaps(c)))
++#include <net/ipv6.h>
+
-+#define vx_ns_capable(n, b, c) (ns_capable(n, b) || \
-+ (cap_raised(current_cap(), b) && vx_ccaps(c)))
++#define NXAV6(a) &(a)->ip, &(a)->mask, (a)->prefix, (a)->type
++#define NXAV6_FMT "[%pI6/%pI6/%d:%04x]"
+
-+#define nx_capable(b, c) (capable(b) || \
-+ (cap_raised(current_cap(), b) && nx_ncaps(c)))
-+
-+#define nx_ns_capable(n, b, c) (ns_capable(n, b) || \
-+ (cap_raised(current_cap(), b) && nx_ncaps(c)))
-+
-+#define vx_task_initpid(t, n) \
-+ ((t)->vx_info && \
-+ ((t)->vx_info->vx_initpid == (n)))
+
-+#define vx_current_initpid(n) vx_task_initpid(current, n)
++#ifdef CONFIG_IPV6
+
++static inline
++int v6_addr_match(struct nx_addr_v6 *nxa,
++ const struct in6_addr *addr, uint16_t mask)
++{
++ int ret = 0;
+
-+/* context unshare mask */
++ switch (nxa->type & mask) {
++ case NXA_TYPE_MASK:
++ ret = ipv6_masked_addr_cmp(&nxa->ip, &nxa->mask, addr);
++ break;
++ case NXA_TYPE_ADDR:
++ ret = ipv6_addr_equal(&nxa->ip, addr);
++ break;
++ case NXA_TYPE_ANY:
++ ret = 1;
++ break;
++ }
++ vxdprintk(VXD_CBIT(net, 0),
++ "v6_addr_match(%p" NXAV6_FMT ",%pI6,%04x) = %d",
++ nxa, NXAV6(nxa), addr, mask, ret);
++ return ret;
++}
+
-+#define __vx_umask(v) ((v)->vx_umask)
++static inline
++int v6_addr_in_nx_info(struct nx_info *nxi,
++ const struct in6_addr *addr, uint16_t mask)
++{
++ struct nx_addr_v6 *nxa;
++ unsigned long irqflags;
++ int ret = 1;
+
-+#define vx_current_umask() __vx_umask(current_vx_info())
++ if (!nxi)
++ goto out;
+
-+#define vx_can_unshare(b, f) (capable(b) || \
-+ (cap_raised(current_cap(), b) && \
-+ !((f) & ~vx_current_umask())))
++ spin_lock_irqsave(&nxi->addr_lock, irqflags);
++ for (nxa = &nxi->v6; nxa; nxa = nxa->next)
++ if (v6_addr_match(nxa, addr, mask))
++ goto out_unlock;
++ ret = 0;
++out_unlock:
++ spin_unlock_irqrestore(&nxi->addr_lock, irqflags);
++out:
++ vxdprintk(VXD_CBIT(net, 0),
++ "v6_addr_in_nx_info(%p[#%u],%pI6,%04x) = %d",
++ nxi, nxi ? nxi->nx_id : 0, addr, mask, ret);
++ return ret;
++}
+
-+#define vx_ns_can_unshare(n, b, f) (ns_capable(n, b) || \
-+ (cap_raised(current_cap(), b) && \
-+ !((f) & ~vx_current_umask())))
++static inline
++int v6_nx_addr_match(struct nx_addr_v6 *nxa, struct nx_addr_v6 *addr, uint16_t mask)
++{
++ /* FIXME: needs full range checks */
++ return v6_addr_match(nxa, &addr->ip, mask);
++}
+
-+#define __vx_wmask(v) ((v)->vx_wmask)
++static inline
++int v6_nx_addr_in_nx_info(struct nx_info *nxi, struct nx_addr_v6 *nxa, uint16_t mask)
++{
++ struct nx_addr_v6 *ptr;
++ unsigned long irqflags;
++ int ret = 1;
+
-+#define vx_current_wmask() __vx_wmask(current_vx_info())
++ spin_lock_irqsave(&nxi->addr_lock, irqflags);
++ for (ptr = &nxi->v6; ptr; ptr = ptr->next)
++ if (v6_nx_addr_match(ptr, nxa, mask))
++ goto out_unlock;
++ ret = 0;
++out_unlock:
++ spin_unlock_irqrestore(&nxi->addr_lock, irqflags);
++ return ret;
++}
+
+
-+#define __vx_state(v) ((v) ? ((v)->vx_state) : 0)
++/*
++ * Check if a given address matches for a socket
++ *
++ * nxi: the socket's nx_info if any
++ * addr: to be verified address
++ */
++static inline
++int v6_sock_addr_match (
++ struct nx_info *nxi,
++ struct inet_sock *inet,
++ struct in6_addr *addr)
++{
++ struct sock *sk = &inet->sk;
++ const struct in6_addr *saddr = inet6_rcv_saddr(sk);
+
-+#define vx_info_state(v, m) (__vx_state(v) & (m))
++ if (!ipv6_addr_any(addr) &&
++ ipv6_addr_equal(saddr, addr))
++ return 1;
++ if (ipv6_addr_any(saddr))
++ return v6_addr_in_nx_info(nxi, addr, -1);
++ return 0;
++}
+
++/*
++ * check if address is covered by socket
++ *
++ * sk: the socket to check against
++ * addr: the address in question (must be != 0)
++ */
+
-+#define __nx_state(n) ((n) ? ((n)->nx_state) : 0)
++static inline
++int __v6_addr_match_socket(const struct sock *sk, struct nx_addr_v6 *nxa)
++{
++ struct nx_info *nxi = sk->sk_nx_info;
++ const struct in6_addr *saddr = inet6_rcv_saddr(sk);
+
-+#define nx_info_state(n, m) (__nx_state(n) & (m))
++ vxdprintk(VXD_CBIT(net, 5),
++ "__v6_addr_in_socket(%p," NXAV6_FMT ") %p:%pI6 %p;%lx",
++ sk, NXAV6(nxa), nxi, saddr, sk->sk_socket,
++ (sk->sk_socket?sk->sk_socket->flags:0));
+
-+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/cacct_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/cacct_cmd.h
---- linux-4.9.217/include/linux/vserver/cacct_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/cacct_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,10 @@
-+#ifndef _VSERVER_CACCT_CMD_H
-+#define _VSERVER_CACCT_CMD_H
++ if (!ipv6_addr_any(saddr)) { /* direct address match */
++ return v6_addr_match(nxa, saddr, -1);
++ } else if (nxi) { /* match against nx_info */
++ return v6_nx_addr_in_nx_info(nxi, nxa, -1);
++ } else { /* unrestricted any socket */
++ return 1;
++ }
++}
+
+
-+#include <linux/compiler.h>
-+#include <uapi/vserver/cacct_cmd.h>
++/* inet related checks and helpers */
+
-+extern int vc_sock_stat(struct vx_info *, void __user *);
+
-+#endif /* _VSERVER_CACCT_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/cacct_def.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/cacct_def.h
---- linux-4.9.217/include/linux/vserver/cacct_def.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/cacct_def.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,43 @@
-+#ifndef _VSERVER_CACCT_DEF_H
-+#define _VSERVER_CACCT_DEF_H
++struct in_ifaddr;
++struct net_device;
++struct sock;
+
-+#include <asm/atomic.h>
-+#include <linux/vserver/cacct.h>
+
++#include <linux/netdevice.h>
++#include <linux/inetdevice.h>
++#include <net/inet_timewait_sock.h>
+
-+struct _vx_sock_acc {
-+ atomic_long_t count;
-+ atomic_long_t total;
-+};
+
-+/* context sub struct */
++int dev_in_nx_info(struct net_device *, struct nx_info *);
++int v6_dev_in_nx_info(struct net_device *, struct nx_info *);
++int nx_v6_addr_conflict(struct nx_info *, struct nx_info *);
+
-+struct _vx_cacct {
-+ struct _vx_sock_acc sock[VXA_SOCK_SIZE][3];
-+ atomic_t slab[8];
-+ atomic_t page[6][8];
-+};
+
-+#ifdef CONFIG_VSERVER_DEBUG
+
-+static inline void __dump_vx_cacct(struct _vx_cacct *cacct)
++static inline
++int v6_ifa_in_nx_info(struct inet6_ifaddr *ifa, struct nx_info *nxi)
+{
-+ int i, j;
++ if (!nxi)
++ return 1;
++ if (!ifa)
++ return 0;
++ return v6_addr_in_nx_info(nxi, &ifa->addr, -1);
++}
+
-+ printk("\t_vx_cacct:");
-+ for (i = 0; i < 6; i++) {
-+ struct _vx_sock_acc *ptr = cacct->sock[i];
++static inline
++int nx_v6_ifa_visible(struct nx_info *nxi, struct inet6_ifaddr *ifa)
++{
++ vxdprintk(VXD_CBIT(net, 1), "nx_v6_ifa_visible(%p[#%u],%p) %d",
++ nxi, nxi ? nxi->nx_id : 0, ifa,
++ nxi ? v6_ifa_in_nx_info(ifa, nxi) : 0);
+
-+ printk("\t [%d] =", i);
-+ for (j = 0; j < 3; j++) {
-+ printk(" [%d] = %8lu, %8lu", j,
-+ atomic_long_read(&ptr[j].count),
-+ atomic_long_read(&ptr[j].total));
-+ }
-+ printk("\n");
-+ }
++ if (!nx_info_flags(nxi, NXF_HIDE_NETIF, 0))
++ return 1;
++ if (v6_ifa_in_nx_info(ifa, nxi))
++ return 1;
++ return 0;
+}
+
-+#endif
+
-+#endif /* _VSERVER_CACCT_DEF_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/cacct.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/cacct.h
---- linux-4.9.217/include/linux/vserver/cacct.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/cacct.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,15 @@
-+#ifndef _VSERVER_CACCT_H
-+#define _VSERVER_CACCT_H
++struct nx_v6_sock_addr {
++ struct in6_addr saddr; /* Address used for validation */
++ struct in6_addr baddr; /* Address used for socket bind */
++};
+
++static inline
++int v6_map_sock_addr(struct inet_sock *inet, struct sockaddr_in6 *addr,
++ struct nx_v6_sock_addr *nsa)
++{
++ // struct sock *sk = &inet->sk;
++ // struct nx_info *nxi = sk->sk_nx_info;
++ struct in6_addr saddr = addr->sin6_addr;
++ struct in6_addr baddr = saddr;
+
-+enum sock_acc_field {
-+ VXA_SOCK_UNSPEC = 0,
-+ VXA_SOCK_UNIX,
-+ VXA_SOCK_INET,
-+ VXA_SOCK_INET6,
-+ VXA_SOCK_PACKET,
-+ VXA_SOCK_OTHER,
-+ VXA_SOCK_SIZE /* array size */
-+};
++ nsa->saddr = saddr;
++ nsa->baddr = baddr;
++ return 0;
++}
+
-+#endif /* _VSERVER_CACCT_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/cacct_int.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/cacct_int.h
---- linux-4.9.217/include/linux/vserver/cacct_int.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/cacct_int.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,17 @@
-+#ifndef _VSERVER_CACCT_INT_H
-+#define _VSERVER_CACCT_INT_H
++static inline
++void v6_set_sock_addr(struct inet_sock *inet, struct nx_v6_sock_addr *nsa)
++{
++ // struct sock *sk = &inet->sk;
++ // struct in6_addr *saddr = inet6_rcv_saddr(sk);
++
++ // *saddr = nsa->baddr;
++ // inet->inet_saddr = nsa->baddr;
++}
+
+static inline
-+unsigned long vx_sock_count(struct _vx_cacct *cacct, int type, int pos)
++int nx_info_has_v6(struct nx_info *nxi)
+{
-+ return atomic_long_read(&cacct->sock[type][pos].count);
++ if (!nxi)
++ return 1;
++ if (NX_IPV6(nxi))
++ return 1;
++ return 0;
+}
+
++#else /* CONFIG_IPV6 */
+
+static inline
-+unsigned long vx_sock_total(struct _vx_cacct *cacct, int type, int pos)
++int nx_v6_dev_visible(struct nx_info *n, struct net_device *d)
+{
-+ return atomic_long_read(&cacct->sock[type][pos].total);
++ return 1;
+}
+
-+#endif /* _VSERVER_CACCT_INT_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/check.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/check.h
---- linux-4.9.217/include/linux/vserver/check.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/check.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,89 @@
-+#ifndef _VSERVER_CHECK_H
-+#define _VSERVER_CHECK_H
-+
-+
-+#define MAX_S_CONTEXT 65535 /* Arbitrary limit */
-+
-+#ifdef CONFIG_VSERVER_DYNAMIC_IDS
-+#define MIN_D_CONTEXT 49152 /* dynamic contexts start here */
-+#else
-+#define MIN_D_CONTEXT 65536
-+#endif
+
-+/* check conditions */
++static inline
++int nx_v6_addr_conflict(struct nx_info *n, uint32_t a, const struct sock *s)
++{
++ return 1;
++}
+
-+#define VS_ADMIN 0x0001
-+#define VS_WATCH 0x0002
-+#define VS_HIDE 0x0004
-+#define VS_HOSTID 0x0008
++static inline
++int v6_ifa_in_nx_info(struct in_ifaddr *a, struct nx_info *n)
++{
++ return 1;
++}
+
-+#define VS_IDENT 0x0010
-+#define VS_EQUIV 0x0020
-+#define VS_PARENT 0x0040
-+#define VS_CHILD 0x0080
++static inline
++int nx_info_has_v6(struct nx_info *nxi)
++{
++ return 0;
++}
+
-+#define VS_ARG_MASK 0x00F0
++static inline
++int v6_addr_in_nx_info(struct nx_info *nxi,
++ const struct in6_addr *addr, uint16_t mask)
++{
++ return 0;
++}
+
-+#define VS_DYNAMIC 0x0100
-+#define VS_STATIC 0x0200
++#endif /* CONFIG_IPV6 */
+
-+#define VS_ATR_MASK 0x0F00
++#define current_nx_info_has_v6() \
++ nx_info_has_v6(current_nx_info())
+
-+#ifdef CONFIG_VSERVER_PRIVACY
-+#define VS_ADMIN_P (0)
-+#define VS_WATCH_P (0)
+#else
-+#define VS_ADMIN_P VS_ADMIN
-+#define VS_WATCH_P VS_WATCH
++#warning duplicate inclusion
+#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_limit.h linux-4.9/include/linux/vs_limit.h
+--- linux-4.9/include/linux/vs_limit.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_limit.h 2021-02-24 15:47:45.101076533 +0100
+@@ -0,0 +1,140 @@
++#ifndef _VS_LIMIT_H
++#define _VS_LIMIT_H
+
-+#define VS_HARDIRQ 0x1000
-+#define VS_SOFTIRQ 0x2000
-+#define VS_IRQ 0x4000
++#include "vserver/limit.h"
++#include "vserver/base.h"
++#include "vserver/context.h"
++#include "vserver/debug.h"
++#include "vserver/context.h"
++#include "vserver/limit_int.h"
+
-+#define VS_IRQ_MASK 0xF000
+
-+#include <linux/hardirq.h>
++#define vx_acc_cres(v, d, p, r) \
++ __vx_acc_cres(v, r, d, p, __FILE__, __LINE__)
+
-+/*
-+ * check current context for ADMIN/WATCH and
-+ * optionally against supplied argument
-+ */
-+static inline int __vs_check(int cid, int id, unsigned int mode)
-+{
-+ if (mode & VS_ARG_MASK) {
-+ if ((mode & VS_IDENT) && (id == cid))
-+ return 1;
-+ }
-+ if (mode & VS_ATR_MASK) {
-+ if ((mode & VS_DYNAMIC) &&
-+ (id >= MIN_D_CONTEXT) &&
-+ (id <= MAX_S_CONTEXT))
-+ return 1;
-+ if ((mode & VS_STATIC) &&
-+ (id > 1) && (id < MIN_D_CONTEXT))
-+ return 1;
-+ }
-+ if (mode & VS_IRQ_MASK) {
-+ if ((mode & VS_IRQ) && unlikely(in_interrupt()))
-+ return 1;
-+ if ((mode & VS_HARDIRQ) && unlikely(in_irq()))
-+ return 1;
-+ if ((mode & VS_SOFTIRQ) && unlikely(in_softirq()))
-+ return 1;
-+ }
-+ return (((mode & VS_ADMIN) && (cid == 0)) ||
-+ ((mode & VS_WATCH) && (cid == 1)) ||
-+ ((mode & VS_HOSTID) && (id == 0)));
-+}
++#define vx_acc_cres_cond(x, d, p, r) \
++ __vx_acc_cres(((x) == vx_current_xid()) ? current_vx_info() : 0, \
++ r, d, p, __FILE__, __LINE__)
+
-+#define vx_check(c, m) __vs_check(vx_current_xid(), c, (m) | VS_IRQ)
+
-+#define vx_weak_check(c, m) ((m) ? vx_check(c, m) : 1)
++#define vx_add_cres(v, a, p, r) \
++ __vx_add_cres(v, r, a, p, __FILE__, __LINE__)
++#define vx_sub_cres(v, a, p, r) vx_add_cres(v, -(a), p, r)
+
++#define vx_add_cres_cond(x, a, p, r) \
++ __vx_add_cres(((x) == vx_current_xid()) ? current_vx_info() : 0, \
++ r, a, p, __FILE__, __LINE__)
++#define vx_sub_cres_cond(x, a, p, r) vx_add_cres_cond(x, -(a), p, r)
+
-+#define nx_check(c, m) __vs_check(nx_current_nid(), c, m)
+
-+#define nx_weak_check(c, m) ((m) ? nx_check(c, m) : 1)
++/* process and file limits */
+
-+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/context_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/context_cmd.h
---- linux-4.9.217/include/linux/vserver/context_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/context_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,33 @@
-+#ifndef _VSERVER_CONTEXT_CMD_H
-+#define _VSERVER_CONTEXT_CMD_H
++#define vx_nproc_inc(p) \
++ vx_acc_cres((p)->vx_info, 1, p, RLIMIT_NPROC)
+
-+#include <uapi/vserver/context_cmd.h>
++#define vx_nproc_dec(p) \
++ vx_acc_cres((p)->vx_info,-1, p, RLIMIT_NPROC)
+
-+extern int vc_task_xid(uint32_t);
++#define vx_files_inc(f) \
++ vx_acc_cres_cond((f)->f_xid, 1, f, RLIMIT_NOFILE)
+
-+extern int vc_vx_info(struct vx_info *, void __user *);
++#define vx_files_dec(f) \
++ vx_acc_cres_cond((f)->f_xid,-1, f, RLIMIT_NOFILE)
+
-+extern int vc_ctx_stat(struct vx_info *, void __user *);
++#define vx_locks_inc(l) \
++ vx_acc_cres_cond((l)->fl_xid, 1, l, RLIMIT_LOCKS)
+
-+extern int vc_ctx_create(uint32_t, void __user *);
-+extern int vc_ctx_migrate(struct vx_info *, void __user *);
++#define vx_locks_dec(l) \
++ vx_acc_cres_cond((l)->fl_xid,-1, l, RLIMIT_LOCKS)
+
-+extern int vc_get_cflags(struct vx_info *, void __user *);
-+extern int vc_set_cflags(struct vx_info *, void __user *);
++#define vx_openfd_inc(f) \
++ vx_acc_cres(current_vx_info(), 1, (void *)(long)(f), VLIMIT_OPENFD)
+
-+extern int vc_get_ccaps(struct vx_info *, void __user *);
-+extern int vc_set_ccaps(struct vx_info *, void __user *);
++#define vx_openfd_dec(f) \
++ vx_acc_cres(current_vx_info(),-1, (void *)(long)(f), VLIMIT_OPENFD)
+
-+extern int vc_get_bcaps(struct vx_info *, void __user *);
-+extern int vc_set_bcaps(struct vx_info *, void __user *);
+
-+extern int vc_get_umask(struct vx_info *, void __user *);
-+extern int vc_set_umask(struct vx_info *, void __user *);
++#define vx_cres_avail(v, n, r) \
++ __vx_cres_avail(v, r, n, __FILE__, __LINE__)
+
-+extern int vc_get_wmask(struct vx_info *, void __user *);
-+extern int vc_set_wmask(struct vx_info *, void __user *);
+
-+extern int vc_get_badness(struct vx_info *, void __user *);
-+extern int vc_set_badness(struct vx_info *, void __user *);
++#define vx_nproc_avail(n) \
++ vx_cres_avail(current_vx_info(), n, RLIMIT_NPROC)
+
-+#endif /* _VSERVER_CONTEXT_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/context.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/context.h
---- linux-4.9.217/include/linux/vserver/context.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/context.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,110 @@
-+#ifndef _VSERVER_CONTEXT_H
-+#define _VSERVER_CONTEXT_H
++#define vx_files_avail(n) \
++ vx_cres_avail(current_vx_info(), n, RLIMIT_NOFILE)
+
++#define vx_locks_avail(n) \
++ vx_cres_avail(current_vx_info(), n, RLIMIT_LOCKS)
+
-+#include <linux/list.h>
-+#include <linux/spinlock.h>
-+#include <linux/rcupdate.h>
-+#include <uapi/vserver/context.h>
++#define vx_openfd_avail(n) \
++ vx_cres_avail(current_vx_info(), n, VLIMIT_OPENFD)
+
-+#include "limit_def.h"
-+#include "sched_def.h"
-+#include "cvirt_def.h"
-+#include "cacct_def.h"
-+#include "device_def.h"
+
-+#define VX_SPACES 2
++/* dentry limits */
+
-+struct _vx_info_pc {
-+ struct _vx_sched_pc sched_pc;
-+ struct _vx_cvirt_pc cvirt_pc;
-+};
++#define vx_dentry_inc(d) do { \
++ if (d_count(d) == 1) \
++ vx_acc_cres(current_vx_info(), 1, d, VLIMIT_DENTRY); \
++ } while (0)
+
-+struct _vx_space {
-+ unsigned long vx_nsmask; /* assignment mask */
-+ struct nsproxy *vx_nsproxy; /* private namespaces */
-+ struct fs_struct *vx_fs; /* private namespace fs */
-+ const struct cred *vx_cred; /* task credentials */
-+};
++#define vx_dentry_dec(d) do { \
++ if (d_count(d) == 0) \
++ vx_acc_cres(current_vx_info(),-1, d, VLIMIT_DENTRY); \
++ } while (0)
+
-+struct vx_info {
-+ struct hlist_node vx_hlist; /* linked list of contexts */
-+ vxid_t vx_id; /* context id */
-+ atomic_t vx_usecnt; /* usage count */
-+ atomic_t vx_tasks; /* tasks count */
-+ struct vx_info *vx_parent; /* parent context */
-+ int vx_state; /* context state */
++#define vx_dentry_avail(n) \
++ vx_cres_avail(current_vx_info(), n, VLIMIT_DENTRY)
+
-+ struct _vx_space space[VX_SPACES]; /* namespace store */
+
-+ uint64_t vx_flags; /* context flags */
-+ uint64_t vx_ccaps; /* context caps (vserver) */
-+ uint64_t vx_umask; /* unshare mask (guest) */
-+ uint64_t vx_wmask; /* warn mask (guest) */
-+ kernel_cap_t vx_bcaps; /* bounding caps (system) */
++/* socket limits */
+
-+ struct task_struct *vx_reaper; /* guest reaper process */
-+ pid_t vx_initpid; /* PID of guest init */
-+ int64_t vx_badness_bias; /* OOM points bias */
++#define vx_sock_inc(s) \
++ vx_acc_cres((s)->sk_vx_info, 1, s, VLIMIT_NSOCK)
+
-+ struct _vx_limit limit; /* vserver limits */
-+ struct _vx_sched sched; /* vserver scheduler */
-+ struct _vx_cvirt cvirt; /* virtual/bias stuff */
-+ struct _vx_cacct cacct; /* context accounting */
++#define vx_sock_dec(s) \
++ vx_acc_cres((s)->sk_vx_info,-1, s, VLIMIT_NSOCK)
+
-+ struct _vx_device dmap; /* default device map targets */
++#define vx_sock_avail(n) \
++ vx_cres_avail(current_vx_info(), n, VLIMIT_NSOCK)
+
-+#ifndef CONFIG_SMP
-+ struct _vx_info_pc info_pc; /* per cpu data */
-+#else
-+ struct _vx_info_pc *ptr_pc; /* per cpu array */
-+#endif
+
-+ wait_queue_head_t vx_wait; /* context exit waitqueue */
-+ int reboot_cmd; /* last sys_reboot() cmd */
-+ int exit_code; /* last process exit code */
++/* ipc resource limits */
+
-+ char vx_name[65]; /* vserver name */
-+};
++#define vx_ipcmsg_add(v, u, a) \
++ vx_add_cres(v, a, u, RLIMIT_MSGQUEUE)
+
-+#ifndef CONFIG_SMP
-+#define vx_ptr_pc(vxi) (&(vxi)->info_pc)
-+#define vx_per_cpu(vxi, v, id) vx_ptr_pc(vxi)->v
-+#else
-+#define vx_ptr_pc(vxi) ((vxi)->ptr_pc)
-+#define vx_per_cpu(vxi, v, id) per_cpu_ptr(vx_ptr_pc(vxi), id)->v
-+#endif
++#define vx_ipcmsg_sub(v, u, a) \
++ vx_sub_cres(v, a, u, RLIMIT_MSGQUEUE)
+
-+#define vx_cpu(vxi, v) vx_per_cpu(vxi, v, smp_processor_id())
++#define vx_ipcmsg_avail(v, a) \
++ vx_cres_avail(v, a, RLIMIT_MSGQUEUE)
+
+
-+struct vx_info_save {
-+ struct vx_info *vxi;
-+ vxid_t xid;
-+};
-+
++#define vx_ipcshm_add(v, k, a) \
++ vx_add_cres(v, a, (void *)(long)(k), VLIMIT_SHMEM)
+
-+/* status flags */
++#define vx_ipcshm_sub(v, k, a) \
++ vx_sub_cres(v, a, (void *)(long)(k), VLIMIT_SHMEM)
+
-+#define VXS_HASHED 0x0001
-+#define VXS_PAUSED 0x0010
-+#define VXS_SHUTDOWN 0x0100
-+#define VXS_HELPER 0x1000
-+#define VXS_RELEASED 0x8000
++#define vx_ipcshm_avail(v, a) \
++ vx_cres_avail(v, a, VLIMIT_SHMEM)
+
+
-+extern void claim_vx_info(struct vx_info *, struct task_struct *);
-+extern void release_vx_info(struct vx_info *, struct task_struct *);
++#define vx_semary_inc(a) \
++ vx_acc_cres(current_vx_info(), 1, a, VLIMIT_SEMARY)
+
-+extern struct vx_info *lookup_vx_info(int);
-+extern struct vx_info *lookup_or_create_vx_info(int);
++#define vx_semary_dec(a) \
++ vx_acc_cres(current_vx_info(), -1, a, VLIMIT_SEMARY)
+
-+extern int get_xid_list(int, unsigned int *, int);
-+extern int xid_is_hashed(vxid_t);
+
-+extern int vx_migrate_task(struct task_struct *, struct vx_info *, int);
++#define vx_nsems_add(a,n) \
++ vx_add_cres(current_vx_info(), n, a, VLIMIT_NSEMS)
+
-+extern long vs_state_change(struct vx_info *, unsigned int);
++#define vx_nsems_sub(a,n) \
++ vx_sub_cres(current_vx_info(), n, a, VLIMIT_NSEMS)
+
+
-+#endif /* _VSERVER_CONTEXT_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/cvirt_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/cvirt_cmd.h
---- linux-4.9.217/include/linux/vserver/cvirt_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/cvirt_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,13 @@
-+#ifndef _VSERVER_CVIRT_CMD_H
-+#define _VSERVER_CVIRT_CMD_H
++#else
++#warning duplicate inclusion
++#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_network.h linux-4.9/include/linux/vs_network.h
+--- linux-4.9/include/linux/vs_network.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_network.h 2021-02-24 15:47:45.101076533 +0100
+@@ -0,0 +1,169 @@
++#ifndef _NX_VS_NETWORK_H
++#define _NX_VS_NETWORK_H
+
++#include "vserver/context.h"
++#include "vserver/network.h"
++#include "vserver/base.h"
++#include "vserver/check.h"
++#include "vserver/debug.h"
+
-+#include <linux/compiler.h>
-+#include <uapi/vserver/cvirt_cmd.h>
++#include <linux/sched.h>
+
-+extern int vc_set_vhi_name(struct vx_info *, void __user *);
-+extern int vc_get_vhi_name(struct vx_info *, void __user *);
+
-+extern int vc_virt_stat(struct vx_info *, void __user *);
++#define get_nx_info(i) __get_nx_info(i, __FILE__, __LINE__)
+
-+#endif /* _VSERVER_CVIRT_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/cvirt_def.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/cvirt_def.h
---- linux-4.9.217/include/linux/vserver/cvirt_def.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/cvirt_def.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,80 @@
-+#ifndef _VSERVER_CVIRT_DEF_H
-+#define _VSERVER_CVIRT_DEF_H
++static inline struct nx_info *__get_nx_info(struct nx_info *nxi,
++ const char *_file, int _line)
++{
++ if (!nxi)
++ return NULL;
+
-+#include <linux/jiffies.h>
-+#include <linux/spinlock.h>
-+#include <linux/wait.h>
-+#include <linux/time.h>
-+#include <asm/atomic.h>
++ vxlprintk(VXD_CBIT(nid, 2), "get_nx_info(%p[#%d.%d])",
++ nxi, nxi ? nxi->nx_id : 0,
++ nxi ? atomic_read(&nxi->nx_usecnt) : 0,
++ _file, _line);
+
++ atomic_inc(&nxi->nx_usecnt);
++ return nxi;
++}
+
-+struct _vx_usage_stat {
-+ uint64_t user;
-+ uint64_t nice;
-+ uint64_t system;
-+ uint64_t softirq;
-+ uint64_t irq;
-+ uint64_t idle;
-+ uint64_t iowait;
-+};
+
-+struct _vx_syslog {
-+ wait_queue_head_t log_wait;
-+ spinlock_t logbuf_lock; /* lock for the log buffer */
++extern void free_nx_info(struct nx_info *);
+
-+ unsigned long log_start; /* next char to be read by syslog() */
-+ unsigned long con_start; /* next char to be sent to consoles */
-+ unsigned long log_end; /* most-recently-written-char + 1 */
-+ unsigned long logged_chars; /* #chars since last read+clear operation */
++#define put_nx_info(i) __put_nx_info(i, __FILE__, __LINE__)
+
-+ char log_buf[1024];
-+};
++static inline void __put_nx_info(struct nx_info *nxi, const char *_file, int _line)
++{
++ if (!nxi)
++ return;
+
++ vxlprintk(VXD_CBIT(nid, 2), "put_nx_info(%p[#%d.%d])",
++ nxi, nxi ? nxi->nx_id : 0,
++ nxi ? atomic_read(&nxi->nx_usecnt) : 0,
++ _file, _line);
+
-+/* context sub struct */
++ if (atomic_dec_and_test(&nxi->nx_usecnt))
++ free_nx_info(nxi);
++}
+
-+struct _vx_cvirt {
-+ atomic_t nr_threads; /* number of current threads */
-+ atomic_t nr_running; /* number of running threads */
-+ atomic_t nr_uninterruptible; /* number of uninterruptible threads */
+
-+ atomic_t nr_onhold; /* processes on hold */
-+ uint32_t onhold_last; /* jiffies when put on hold */
++#define init_nx_info(p, i) __init_nx_info(p, i, __FILE__, __LINE__)
+
-+ struct timespec64 bias_ts; /* time offset to the host */
-+ struct timespec64 bias_idle;
-+ struct timespec64 bias_uptime; /* context creation point */
-+ uint64_t bias_clock; /* offset in clock_t */
++static inline void __init_nx_info(struct nx_info **nxp, struct nx_info *nxi,
++ const char *_file, int _line)
++{
++ if (nxi) {
++ vxlprintk(VXD_CBIT(nid, 3),
++ "init_nx_info(%p[#%d.%d])",
++ nxi, nxi ? nxi->nx_id : 0,
++ nxi ? atomic_read(&nxi->nx_usecnt) : 0,
++ _file, _line);
+
-+ spinlock_t load_lock; /* lock for the load averages */
-+ atomic_t load_updates; /* nr of load updates done so far */
-+ uint32_t load_last; /* last time load was calculated */
-+ uint32_t load[3]; /* load averages 1,5,15 */
++ atomic_inc(&nxi->nx_usecnt);
++ }
++ *nxp = nxi;
++}
+
-+ atomic_t total_forks; /* number of forks so far */
+
-+ struct _vx_syslog syslog;
-+};
++#define set_nx_info(p, i) __set_nx_info(p, i, __FILE__, __LINE__)
+
-+struct _vx_cvirt_pc {
-+ struct _vx_usage_stat cpustat;
-+};
++static inline void __set_nx_info(struct nx_info **nxp, struct nx_info *nxi,
++ const char *_file, int _line)
++{
++ struct nx_info *nxo;
+
++ if (!nxi)
++ return;
+
-+#ifdef CONFIG_VSERVER_DEBUG
++ vxlprintk(VXD_CBIT(nid, 3), "set_nx_info(%p[#%d.%d])",
++ nxi, nxi ? nxi->nx_id : 0,
++ nxi ? atomic_read(&nxi->nx_usecnt) : 0,
++ _file, _line);
+
-+static inline void __dump_vx_cvirt(struct _vx_cvirt *cvirt)
-+{
-+ printk("\t_vx_cvirt:\n");
-+ printk("\t threads: %4d, %4d, %4d, %4d\n",
-+ atomic_read(&cvirt->nr_threads),
-+ atomic_read(&cvirt->nr_running),
-+ atomic_read(&cvirt->nr_uninterruptible),
-+ atomic_read(&cvirt->nr_onhold));
-+ /* add rest here */
-+ printk("\t total_forks = %d\n", atomic_read(&cvirt->total_forks));
++ atomic_inc(&nxi->nx_usecnt);
++ nxo = xchg(nxp, nxi);
++ BUG_ON(nxo);
+}
+
-+#endif
-+
-+#endif /* _VSERVER_CVIRT_DEF_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/cvirt.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/cvirt.h
---- linux-4.9.217/include/linux/vserver/cvirt.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/cvirt.h 2019-10-13 16:04:08.203030205 +0000
-@@ -0,0 +1,11 @@
-+#ifndef _VSERVER_CVIRT_H
-+#define _VSERVER_CVIRT_H
++#define clr_nx_info(p) __clr_nx_info(p, __FILE__, __LINE__)
+
-+struct vx_info;
++static inline void __clr_nx_info(struct nx_info **nxp,
++ const char *_file, int _line)
++{
++ struct nx_info *nxo;
+
-+void vx_update_load(struct vx_info *);
++ nxo = xchg(nxp, NULL);
++ if (!nxo)
++ return;
+
++ vxlprintk(VXD_CBIT(nid, 3), "clr_nx_info(%p[#%d.%d])",
++ nxo, nxo ? nxo->nx_id : 0,
++ nxo ? atomic_read(&nxo->nx_usecnt) : 0,
++ _file, _line);
+
-+int vx_do_syslog(int, char __user *, int);
++ if (atomic_dec_and_test(&nxo->nx_usecnt))
++ free_nx_info(nxo);
++}
+
-+#endif /* _VSERVER_CVIRT_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/debug_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/debug_cmd.h
---- linux-4.9.217/include/linux/vserver/debug_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/debug_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,37 @@
-+#ifndef _VSERVER_DEBUG_CMD_H
-+#define _VSERVER_DEBUG_CMD_H
+
-+#include <uapi/vserver/debug_cmd.h>
++#define claim_nx_info(v, p) __claim_nx_info(v, p, __FILE__, __LINE__)
+
++static inline void __claim_nx_info(struct nx_info *nxi,
++ struct task_struct *task, const char *_file, int _line)
++{
++ vxlprintk(VXD_CBIT(nid, 3), "claim_nx_info(%p[#%d.%d.%d]) %p",
++ nxi, nxi ? nxi->nx_id : 0,
++ nxi?atomic_read(&nxi->nx_usecnt):0,
++ nxi?atomic_read(&nxi->nx_tasks):0,
++ task, _file, _line);
+
-+#ifdef CONFIG_COMPAT
++ atomic_inc(&nxi->nx_tasks);
++}
+
-+#include <asm/compat.h>
+
-+struct vcmd_read_history_v0_x32 {
-+ uint32_t index;
-+ uint32_t count;
-+ compat_uptr_t data_ptr;
-+};
++extern void unhash_nx_info(struct nx_info *);
+
-+struct vcmd_read_monitor_v0_x32 {
-+ uint32_t index;
-+ uint32_t count;
-+ compat_uptr_t data_ptr;
-+};
++#define release_nx_info(v, p) __release_nx_info(v, p, __FILE__, __LINE__)
+
-+#endif /* CONFIG_COMPAT */
++static inline void __release_nx_info(struct nx_info *nxi,
++ struct task_struct *task, const char *_file, int _line)
++{
++ vxlprintk(VXD_CBIT(nid, 3), "release_nx_info(%p[#%d.%d.%d]) %p",
++ nxi, nxi ? nxi->nx_id : 0,
++ nxi ? atomic_read(&nxi->nx_usecnt) : 0,
++ nxi ? atomic_read(&nxi->nx_tasks) : 0,
++ task, _file, _line);
+
-+extern int vc_dump_history(uint32_t);
++ might_sleep();
+
-+extern int vc_read_history(uint32_t, void __user *);
-+extern int vc_read_monitor(uint32_t, void __user *);
++ if (atomic_dec_and_test(&nxi->nx_tasks))
++ unhash_nx_info(nxi);
++}
+
-+#ifdef CONFIG_COMPAT
+
-+extern int vc_read_history_x32(uint32_t, void __user *);
-+extern int vc_read_monitor_x32(uint32_t, void __user *);
++#define task_get_nx_info(i) __task_get_nx_info(i, __FILE__, __LINE__)
+
-+#endif /* CONFIG_COMPAT */
++static __inline__ struct nx_info *__task_get_nx_info(struct task_struct *p,
++ const char *_file, int _line)
++{
++ struct nx_info *nxi;
+
-+#endif /* _VSERVER_DEBUG_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/debug.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/debug.h
---- linux-4.9.217/include/linux/vserver/debug.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/debug.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,146 @@
-+#ifndef _VSERVER_DEBUG_H
-+#define _VSERVER_DEBUG_H
++ task_lock(p);
++ vxlprintk(VXD_CBIT(nid, 5), "task_get_nx_info(%p)",
++ p, _file, _line);
++ nxi = __get_nx_info(p->nx_info, _file, _line);
++ task_unlock(p);
++ return nxi;
++}
+
+
-+#define VXD_CBIT(n, m) (vs_debug_ ## n & (1 << (m)))
-+#define VXD_CMIN(n, m) (vs_debug_ ## n > (m))
-+#define VXD_MASK(n, m) (vs_debug_ ## n & (m))
++static inline void exit_nx_info(struct task_struct *p)
++{
++ if (p->nx_info)
++ release_nx_info(p->nx_info, p);
++}
+
-+#define VXD_DEV(d) (d), (d)->bd_inode->i_ino, \
-+ imajor((d)->bd_inode), iminor((d)->bd_inode)
-+#define VXF_DEV "%p[%lu,%d:%d]"
+
-+#if defined(CONFIG_QUOTES_UTF8)
-+#define VS_Q_LQM "\xc2\xbb"
-+#define VS_Q_RQM "\xc2\xab"
-+#elif defined(CONFIG_QUOTES_ASCII)
-+#define VS_Q_LQM "\x27"
-+#define VS_Q_RQM "\x27"
+#else
-+#define VS_Q_LQM "\xbb"
-+#define VS_Q_RQM "\xab"
++#warning duplicate inclusion
+#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_pid.h linux-4.9/include/linux/vs_pid.h
+--- linux-4.9/include/linux/vs_pid.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_pid.h 2021-02-24 15:47:45.101076533 +0100
+@@ -0,0 +1,50 @@
++#ifndef _VS_PID_H
++#define _VS_PID_H
+
-+#define VS_Q(f) VS_Q_LQM f VS_Q_RQM
++#include "vserver/base.h"
++#include "vserver/check.h"
++#include "vserver/context.h"
++#include "vserver/debug.h"
++#include "vserver/pid.h"
++#include <linux/pid_namespace.h>
+
+
-+#define vxd_path(p) \
-+ ({ static char _buffer[PATH_MAX]; \
-+ d_path(p, _buffer, sizeof(_buffer)); })
++#define VXF_FAKE_INIT (VXF_INFO_INIT | VXF_STATE_INIT)
+
-+#define vxd_cond_path(n) \
-+ ((n) ? vxd_path(&(n)->path) : "<null>" )
++static inline
++int vx_proc_task_visible(struct task_struct *task)
++{
++ if ((task->pid == 1) &&
++ !vx_flags(VXF_FAKE_INIT, VXF_FAKE_INIT))
++ /* show a blend through init */
++ goto visible;
++ if (vx_check(vx_task_xid(task), VS_WATCH | VS_IDENT))
++ goto visible;
++ return 0;
++visible:
++ return 1;
++}
+
++#define find_task_by_real_pid(pid) find_task_by_pid_ns(pid, &init_pid_ns)
+
-+#ifdef CONFIG_VSERVER_DEBUG
+
-+extern unsigned int vs_debug_switch;
-+extern unsigned int vs_debug_xid;
-+extern unsigned int vs_debug_nid;
-+extern unsigned int vs_debug_tag;
-+extern unsigned int vs_debug_net;
-+extern unsigned int vs_debug_limit;
-+extern unsigned int vs_debug_cres;
-+extern unsigned int vs_debug_dlim;
-+extern unsigned int vs_debug_quota;
-+extern unsigned int vs_debug_cvirt;
-+extern unsigned int vs_debug_space;
-+extern unsigned int vs_debug_perm;
-+extern unsigned int vs_debug_misc;
++static inline
++struct task_struct *vx_get_proc_task(struct inode *inode, struct pid *pid)
++{
++ struct task_struct *task = get_pid_task(pid, PIDTYPE_PID);
+
++ if (task && !vx_proc_task_visible(task)) {
++ vxdprintk(VXD_CBIT(misc, 6),
++ "dropping task (get) %p[#%u,%u] for %p[#%u,%u]",
++ task, task->xid, task->pid,
++ current, current->xid, current->pid);
++ put_task_struct(task);
++ task = NULL;
++ }
++ return task;
++}
+
-+#define VX_LOGLEVEL "vxD: "
-+#define VX_PROC_FMT "%p: "
-+#define VX_PROCESS current
+
-+#define vxdprintk(c, f, x...) \
-+ do { \
-+ if (c) \
-+ printk(VX_LOGLEVEL VX_PROC_FMT f "\n", \
-+ VX_PROCESS , ##x); \
-+ } while (0)
++#else
++#warning duplicate inclusion
++#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_sched.h linux-4.9/include/linux/vs_sched.h
+--- linux-4.9/include/linux/vs_sched.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_sched.h 2021-02-24 15:47:45.101076533 +0100
+@@ -0,0 +1,40 @@
++#ifndef _VS_SCHED_H
++#define _VS_SCHED_H
+
-+#define vxlprintk(c, f, x...) \
-+ do { \
-+ if (c) \
-+ printk(VX_LOGLEVEL f " @%s:%d\n", x); \
-+ } while (0)
++#include "vserver/base.h"
++#include "vserver/context.h"
++#include "vserver/sched.h"
+
-+#define vxfprintk(c, f, x...) \
-+ do { \
-+ if (c) \
-+ printk(VX_LOGLEVEL f " %s@%s:%d\n", x); \
-+ } while (0)
+
++#define MAX_PRIO_BIAS 20
++#define MIN_PRIO_BIAS -20
+
-+struct vx_info;
++static inline
++int vx_adjust_prio(struct task_struct *p, int prio, int max_user)
++{
++ struct vx_info *vxi = p->vx_info;
+
-+void dump_vx_info(struct vx_info *, int);
-+void dump_vx_info_inactive(int);
++ if (vxi)
++ prio += vx_cpu(vxi, sched_pc).prio_bias;
++ return prio;
++}
+
-+#else /* CONFIG_VSERVER_DEBUG */
++static inline void vx_account_user(struct vx_info *vxi,
++ cputime_t cputime, int nice)
++{
++ if (!vxi)
++ return;
++ vx_cpu(vxi, sched_pc).user_ticks += cputime;
++}
+
-+#define vs_debug_switch 0
-+#define vs_debug_xid 0
-+#define vs_debug_nid 0
-+#define vs_debug_tag 0
-+#define vs_debug_net 0
-+#define vs_debug_limit 0
-+#define vs_debug_cres 0
-+#define vs_debug_dlim 0
-+#define vs_debug_quota 0
-+#define vs_debug_cvirt 0
-+#define vs_debug_space 0
-+#define vs_debug_perm 0
-+#define vs_debug_misc 0
++static inline void vx_account_system(struct vx_info *vxi,
++ cputime_t cputime, int idle)
++{
++ if (!vxi)
++ return;
++ vx_cpu(vxi, sched_pc).sys_ticks += cputime;
++}
+
-+#define vxdprintk(x...) do { } while (0)
-+#define vxlprintk(x...) do { } while (0)
-+#define vxfprintk(x...) do { } while (0)
++#else
++#warning duplicate inclusion
++#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_socket.h linux-4.9/include/linux/vs_socket.h
+--- linux-4.9/include/linux/vs_socket.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_socket.h 2021-02-24 15:47:45.101076533 +0100
+@@ -0,0 +1,67 @@
++#ifndef _VS_SOCKET_H
++#define _VS_SOCKET_H
+
-+#endif /* CONFIG_VSERVER_DEBUG */
++#include "vserver/debug.h"
++#include "vserver/base.h"
++#include "vserver/cacct.h"
++#include "vserver/context.h"
++#include "vserver/tag.h"
+
+
-+#ifdef CONFIG_VSERVER_WARN
++/* socket accounting */
+
-+#define VX_WARNLEVEL KERN_WARNING "vxW: "
-+#define VX_WARN_TASK "[" VS_Q("%s") ",%u:#%u|%u|%u] "
-+#define VX_WARN_XID "[xid #%u] "
-+#define VX_WARN_NID "[nid #%u] "
-+#define VX_WARN_TAG "[tag #%u] "
++#include <linux/socket.h>
+
-+#define vxwprintk(c, f, x...) \
-+ do { \
-+ if (c) \
-+ printk(VX_WARNLEVEL f "\n", ##x); \
-+ } while (0)
++static inline int vx_sock_type(int family)
++{
++ switch (family) {
++ case PF_UNSPEC:
++ return VXA_SOCK_UNSPEC;
++ case PF_UNIX:
++ return VXA_SOCK_UNIX;
++ case PF_INET:
++ return VXA_SOCK_INET;
++ case PF_INET6:
++ return VXA_SOCK_INET6;
++ case PF_PACKET:
++ return VXA_SOCK_PACKET;
++ default:
++ return VXA_SOCK_OTHER;
++ }
++}
+
-+#else /* CONFIG_VSERVER_WARN */
++#define vx_acc_sock(v, f, p, s) \
++ __vx_acc_sock(v, f, p, s, __FILE__, __LINE__)
+
-+#define vxwprintk(x...) do { } while (0)
++static inline void __vx_acc_sock(struct vx_info *vxi,
++ int family, int pos, int size, char *file, int line)
++{
++ if (vxi) {
++ int type = vx_sock_type(family);
+
-+#endif /* CONFIG_VSERVER_WARN */
++ atomic_long_inc(&vxi->cacct.sock[type][pos].count);
++ atomic_long_add(size, &vxi->cacct.sock[type][pos].total);
++ }
++}
+
-+#define vxwprintk_task(c, f, x...) \
-+ vxwprintk(c, VX_WARN_TASK f, \
-+ current->comm, current->pid, \
-+ current->xid, current->nid, \
-+ current->tag, ##x)
-+#define vxwprintk_xid(c, f, x...) \
-+ vxwprintk(c, VX_WARN_XID f, current->xid, x)
-+#define vxwprintk_nid(c, f, x...) \
-+ vxwprintk(c, VX_WARN_NID f, current->nid, x)
-+#define vxwprintk_tag(c, f, x...) \
-+ vxwprintk(c, VX_WARN_TAG f, current->tag, x)
++#define vx_sock_recv(sk, s) \
++ vx_acc_sock((sk)->sk_vx_info, (sk)->sk_family, 0, s)
++#define vx_sock_send(sk, s) \
++ vx_acc_sock((sk)->sk_vx_info, (sk)->sk_family, 1, s)
++#define vx_sock_fail(sk, s) \
++ vx_acc_sock((sk)->sk_vx_info, (sk)->sk_family, 2, s)
++
++
++#define sock_vx_init(s) do { \
++ (s)->sk_xid = 0; \
++ (s)->sk_vx_info = NULL; \
++ } while (0)
++
++#define sock_nx_init(s) do { \
++ (s)->sk_nid = 0; \
++ (s)->sk_nx_info = NULL; \
++ } while (0)
+
-+#ifdef CONFIG_VSERVER_DEBUG
-+#define vxd_assert_lock(l) assert_spin_locked(l)
-+#define vxd_assert(c, f, x...) vxlprintk(!(c), \
-+ "assertion [" f "] failed.", ##x, __FILE__, __LINE__)
+#else
-+#define vxd_assert_lock(l) do { } while (0)
-+#define vxd_assert(c, f, x...) do { } while (0)
++#warning duplicate inclusion
+#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_tag.h linux-4.9/include/linux/vs_tag.h
+--- linux-4.9/include/linux/vs_tag.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_tag.h 2021-02-24 15:47:45.101076533 +0100
+@@ -0,0 +1,47 @@
++#ifndef _VS_TAG_H
++#define _VS_TAG_H
+
++#include <linux/vserver/tag.h>
+
-+#endif /* _VSERVER_DEBUG_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/device_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/device_cmd.h
---- linux-4.9.217/include/linux/vserver/device_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/device_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,31 @@
-+#ifndef _VSERVER_DEVICE_CMD_H
-+#define _VSERVER_DEVICE_CMD_H
++/* check conditions */
+
-+#include <uapi/vserver/device_cmd.h>
++#define DX_ADMIN 0x0001
++#define DX_WATCH 0x0002
++#define DX_HOSTID 0x0008
+
++#define DX_IDENT 0x0010
+
-+#ifdef CONFIG_COMPAT
++#define DX_ARG_MASK 0x0010
+
-+#include <asm/compat.h>
+
-+struct vcmd_set_mapping_v0_x32 {
-+ compat_uptr_t device_ptr;
-+ compat_uptr_t target_ptr;
-+ uint32_t flags;
-+};
++#define dx_task_tag(t) ((t)->tag)
+
-+#endif /* CONFIG_COMPAT */
++#define dx_current_tag() dx_task_tag(current)
+
-+#include <linux/compiler.h>
++#define dx_check(c, m) __dx_check(dx_current_tag(), c, m)
+
-+extern int vc_set_mapping(struct vx_info *, void __user *);
-+extern int vc_unset_mapping(struct vx_info *, void __user *);
++#define dx_weak_check(c, m) ((m) ? dx_check(c, m) : 1)
+
-+#ifdef CONFIG_COMPAT
+
-+extern int vc_set_mapping_x32(struct vx_info *, void __user *);
-+extern int vc_unset_mapping_x32(struct vx_info *, void __user *);
++/*
++ * check current context for ADMIN/WATCH and
++ * optionally against supplied argument
++ */
++static inline int __dx_check(vtag_t cid, vtag_t id, unsigned int mode)
++{
++ if (mode & DX_ARG_MASK) {
++ if ((mode & DX_IDENT) && (id == cid))
++ return 1;
++ }
++ return (((mode & DX_ADMIN) && (cid == 0)) ||
++ ((mode & DX_WATCH) && (cid == 1)) ||
++ ((mode & DX_HOSTID) && (id == 0)));
++}
+
-+#endif /* CONFIG_COMPAT */
++struct inode;
++int dx_permission(const struct inode *inode, int mask);
+
-+#endif /* _VSERVER_DEVICE_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/device_def.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/device_def.h
---- linux-4.9.217/include/linux/vserver/device_def.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/device_def.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,17 @@
-+#ifndef _VSERVER_DEVICE_DEF_H
-+#define _VSERVER_DEVICE_DEF_H
+
-+#include <linux/types.h>
++#else
++#warning duplicate inclusion
++#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vs_time.h linux-4.9/include/linux/vs_time.h
+--- linux-4.9/include/linux/vs_time.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vs_time.h 2021-02-24 15:47:45.101076533 +0100
+@@ -0,0 +1,21 @@
++#ifndef _VS_TIME_H
++#define _VS_TIME_H
+
-+struct vx_dmap_target {
-+ dev_t target;
-+ uint32_t flags;
-+};
+
-+struct _vx_device {
-+#ifdef CONFIG_VSERVER_DEVICE
-+ struct vx_dmap_target targets[2];
-+#endif
-+};
++/* time faking stuff */
+
-+#endif /* _VSERVER_DEVICE_DEF_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/device.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/device.h
---- linux-4.9.217/include/linux/vserver/device.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/device.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,9 @@
-+#ifndef _VSERVER_DEVICE_H
-+#define _VSERVER_DEVICE_H
++#ifdef CONFIG_VSERVER_VTIME
+
++extern void vx_adjust_timespec(struct timespec *ts);
++extern int vx_settimeofday(const struct timespec *ts);
++extern int vx_settimeofday64(const struct timespec64 *ts);
+
-+#include <uapi/vserver/device.h>
++#else
++#define vx_adjust_timespec(t) do { } while (0)
++#define vx_settimeofday(t) do_settimeofday(t)
++#define vx_settimeofday64(t) do_settimeofday64(t)
++#endif
+
-+#else /* _VSERVER_DEVICE_H */
++#else
+#warning duplicate inclusion
-+#endif /* _VSERVER_DEVICE_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/dlimit_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/dlimit_cmd.h
---- linux-4.9.217/include/linux/vserver/dlimit_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/dlimit_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,46 @@
-+#ifndef _VSERVER_DLIMIT_CMD_H
-+#define _VSERVER_DLIMIT_CMD_H
-+
-+#include <uapi/vserver/dlimit_cmd.h>
++#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/base.h linux-4.9/include/linux/vserver/base.h
+--- linux-4.9/include/linux/vserver/base.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/base.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,184 @@
++#ifndef _VSERVER_BASE_H
++#define _VSERVER_BASE_H
+
+
-+#ifdef CONFIG_COMPAT
++/* context state changes */
+
-+#include <asm/compat.h>
++enum {
++ VSC_STARTUP = 1,
++ VSC_SHUTDOWN,
+
-+struct vcmd_ctx_dlimit_base_v0_x32 {
-+ compat_uptr_t name_ptr;
-+ uint32_t flags;
++ VSC_NETUP,
++ VSC_NETDOWN,
+};
+
-+struct vcmd_ctx_dlimit_v0_x32 {
-+ compat_uptr_t name_ptr;
-+ uint32_t space_used; /* used space in kbytes */
-+ uint32_t space_total; /* maximum space in kbytes */
-+ uint32_t inodes_used; /* used inodes */
-+ uint32_t inodes_total; /* maximum inodes */
-+ uint32_t reserved; /* reserved for root in % */
-+ uint32_t flags;
-+};
+
-+#endif /* CONFIG_COMPAT */
+
-+#include <linux/compiler.h>
++#define vx_task_xid(t) ((t)->xid)
+
-+extern int vc_add_dlimit(uint32_t, void __user *);
-+extern int vc_rem_dlimit(uint32_t, void __user *);
++#define vx_current_xid() vx_task_xid(current)
+
-+extern int vc_set_dlimit(uint32_t, void __user *);
-+extern int vc_get_dlimit(uint32_t, void __user *);
++#define current_vx_info() (current->vx_info)
+
-+#ifdef CONFIG_COMPAT
+
-+extern int vc_add_dlimit_x32(uint32_t, void __user *);
-+extern int vc_rem_dlimit_x32(uint32_t, void __user *);
++#define nx_task_nid(t) ((t)->nid)
+
-+extern int vc_set_dlimit_x32(uint32_t, void __user *);
-+extern int vc_get_dlimit_x32(uint32_t, void __user *);
++#define nx_current_nid() nx_task_nid(current)
+
-+#endif /* CONFIG_COMPAT */
++#define current_nx_info() (current->nx_info)
+
-+#endif /* _VSERVER_DLIMIT_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/dlimit.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/dlimit.h
---- linux-4.9.217/include/linux/vserver/dlimit.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/dlimit.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,54 @@
-+#ifndef _VSERVER_DLIMIT_H
-+#define _VSERVER_DLIMIT_H
+
-+#include "switch.h"
++/* generic flag merging */
+
++#define vs_check_flags(v, m, f) (((v) & (m)) ^ (f))
+
-+#ifdef __KERNEL__
++#define vs_mask_flags(v, f, m) (((v) & ~(m)) | ((f) & (m)))
+
-+/* keep in sync with CDLIM_INFINITY */
++#define vs_mask_mask(v, f, m) (((v) & ~(m)) | ((v) & (f) & (m)))
+
-+#define DLIM_INFINITY (~0ULL)
++#define vs_check_bit(v, n) ((v) & (1LL << (n)))
+
-+#include <linux/spinlock.h>
-+#include <linux/rcupdate.h>
+
-+struct super_block;
++/* context flags */
+
-+struct dl_info {
-+ struct hlist_node dl_hlist; /* linked list of contexts */
-+ struct rcu_head dl_rcu; /* the rcu head */
-+ vtag_t dl_tag; /* context tag */
-+ atomic_t dl_usecnt; /* usage count */
-+ atomic_t dl_refcnt; /* reference count */
++#define __vx_flags(v) ((v) ? (v)->vx_flags : 0)
+
-+ struct super_block *dl_sb; /* associated superblock */
++#define vx_current_flags() __vx_flags(current_vx_info())
+
-+ spinlock_t dl_lock; /* protect the values */
++#define vx_info_flags(v, m, f) \
++ vs_check_flags(__vx_flags(v), m, f)
+
-+ unsigned long long dl_space_used; /* used space in bytes */
-+ unsigned long long dl_space_total; /* maximum space in bytes */
-+ unsigned long dl_inodes_used; /* used inodes */
-+ unsigned long dl_inodes_total; /* maximum inodes */
++#define task_vx_flags(t, m, f) \
++ ((t) && vx_info_flags((t)->vx_info, m, f))
+
-+ unsigned int dl_nrlmult; /* non root limit mult */
-+};
++#define vx_flags(m, f) vx_info_flags(current_vx_info(), m, f)
+
-+struct rcu_head;
+
-+extern void rcu_free_dl_info(struct rcu_head *);
-+extern void unhash_dl_info(struct dl_info *);
++/* context caps */
+
-+extern struct dl_info *locate_dl_info(struct super_block *, vtag_t);
++#define __vx_ccaps(v) ((v) ? (v)->vx_ccaps : 0)
+
++#define vx_current_ccaps() __vx_ccaps(current_vx_info())
+
-+struct kstatfs;
++#define vx_info_ccaps(v, c) (__vx_ccaps(v) & (c))
+
-+extern void vx_vsi_statfs(struct super_block *, struct kstatfs *);
++#define vx_ccaps(c) vx_info_ccaps(current_vx_info(), (c))
+
-+typedef uint64_t dlsize_t;
+
-+#endif /* __KERNEL__ */
-+#else /* _VSERVER_DLIMIT_H */
-+#warning duplicate inclusion
-+#endif /* _VSERVER_DLIMIT_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/global.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/global.h
---- linux-4.9.217/include/linux/vserver/global.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/global.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,20 @@
-+#ifndef _VSERVER_GLOBAL_H
-+#define _VSERVER_GLOBAL_H
+
++/* network flags */
+
-+extern atomic_t vx_global_ctotal;
-+extern atomic_t vx_global_cactive;
++#define __nx_flags(n) ((n) ? (n)->nx_flags : 0)
+
-+extern atomic_t nx_global_ctotal;
-+extern atomic_t nx_global_cactive;
++#define nx_current_flags() __nx_flags(current_nx_info())
+
-+extern atomic_t vs_global_nsproxy;
-+extern atomic_t vs_global_fs;
-+extern atomic_t vs_global_mnt_ns;
-+extern atomic_t vs_global_uts_ns;
-+extern atomic_t vs_global_ipc_ns;
-+extern atomic_t vs_global_user_ns;
-+extern atomic_t vs_global_pid_ns;
++#define nx_info_flags(n, m, f) \
++ vs_check_flags(__nx_flags(n), m, f)
+
++#define task_nx_flags(t, m, f) \
++ ((t) && nx_info_flags((t)->nx_info, m, f))
+
-+#endif /* _VSERVER_GLOBAL_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/history.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/history.h
---- linux-4.9.217/include/linux/vserver/history.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/history.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,197 @@
-+#ifndef _VSERVER_HISTORY_H
-+#define _VSERVER_HISTORY_H
++#define nx_flags(m, f) nx_info_flags(current_nx_info(), m, f)
+
+
-+enum {
-+ VXH_UNUSED = 0,
-+ VXH_THROW_OOPS = 1,
++/* network caps */
+
-+ VXH_GET_VX_INFO,
-+ VXH_PUT_VX_INFO,
-+ VXH_INIT_VX_INFO,
-+ VXH_SET_VX_INFO,
-+ VXH_CLR_VX_INFO,
-+ VXH_CLAIM_VX_INFO,
-+ VXH_RELEASE_VX_INFO,
-+ VXH_ALLOC_VX_INFO,
-+ VXH_DEALLOC_VX_INFO,
-+ VXH_HASH_VX_INFO,
-+ VXH_UNHASH_VX_INFO,
-+ VXH_LOC_VX_INFO,
-+ VXH_LOOKUP_VX_INFO,
-+ VXH_CREATE_VX_INFO,
-+};
++#define __nx_ncaps(n) ((n) ? (n)->nx_ncaps : 0)
+
-+struct _vxhe_vxi {
-+ struct vx_info *ptr;
-+ unsigned xid;
-+ unsigned usecnt;
-+ unsigned tasks;
-+};
++#define nx_current_ncaps() __nx_ncaps(current_nx_info())
+
-+struct _vxhe_set_clr {
-+ void *data;
-+};
++#define nx_info_ncaps(n, c) (__nx_ncaps(n) & (c))
+
-+struct _vxhe_loc_lookup {
-+ unsigned arg;
-+};
++#define nx_ncaps(c) nx_info_ncaps(current_nx_info(), c)
+
-+struct _vx_hist_entry {
-+ void *loc;
-+ unsigned short seq;
-+ unsigned short type;
-+ struct _vxhe_vxi vxi;
-+ union {
-+ struct _vxhe_set_clr sc;
-+ struct _vxhe_loc_lookup ll;
-+ };
-+};
+
-+#ifdef CONFIG_VSERVER_HISTORY
++/* context mask capabilities */
+
-+extern unsigned volatile int vxh_active;
++#define __vx_mcaps(v) ((v) ? (v)->vx_ccaps >> 32UL : ~0 )
+
-+struct _vx_hist_entry *vxh_advance(void *loc);
++#define vx_info_mcaps(v, c) (__vx_mcaps(v) & (c))
+
++#define vx_mcaps(c) vx_info_mcaps(current_vx_info(), c)
+
-+static inline
-+void __vxh_copy_vxi(struct _vx_hist_entry *entry, struct vx_info *vxi)
-+{
-+ entry->vxi.ptr = vxi;
-+ if (vxi) {
-+ entry->vxi.usecnt = atomic_read(&vxi->vx_usecnt);
-+ entry->vxi.tasks = atomic_read(&vxi->vx_tasks);
-+ entry->vxi.xid = vxi->vx_id;
-+ }
-+}
-+
-+
-+#define __HERE__ current_text_addr()
+
-+#define __VXH_BODY(__type, __data, __here) \
-+ struct _vx_hist_entry *entry; \
-+ \
-+ preempt_disable(); \
-+ entry = vxh_advance(__here); \
-+ __data; \
-+ entry->type = __type; \
-+ preempt_enable();
++/* context bcap mask */
+
++#define __vx_bcaps(v) ((v)->vx_bcaps)
+
-+ /* pass vxi only */
++#define vx_current_bcaps() __vx_bcaps(current_vx_info())
+
-+#define __VXH_SMPL \
-+ __vxh_copy_vxi(entry, vxi)
+
-+static inline
-+void __vxh_smpl(struct vx_info *vxi, int __type, void *__here)
-+{
-+ __VXH_BODY(__type, __VXH_SMPL, __here)
-+}
++/* mask given bcaps */
+
-+ /* pass vxi and data (void *) */
++#define vx_info_mbcaps(v, c) ((v) ? cap_intersect(__vx_bcaps(v), c) : c)
+
-+#define __VXH_DATA \
-+ __vxh_copy_vxi(entry, vxi); \
-+ entry->sc.data = data
++#define vx_mbcaps(c) vx_info_mbcaps(current_vx_info(), c)
+
-+static inline
-+void __vxh_data(struct vx_info *vxi, void *data,
-+ int __type, void *__here)
-+{
-+ __VXH_BODY(__type, __VXH_DATA, __here)
-+}
+
-+ /* pass vxi and arg (long) */
++/* masked cap_bset */
+
-+#define __VXH_LONG \
-+ __vxh_copy_vxi(entry, vxi); \
-+ entry->ll.arg = arg
++#define vx_info_cap_bset(v) vx_info_mbcaps(v, current->cap_bset)
+
-+static inline
-+void __vxh_long(struct vx_info *vxi, long arg,
-+ int __type, void *__here)
-+{
-+ __VXH_BODY(__type, __VXH_LONG, __here)
-+}
++#define vx_current_cap_bset() vx_info_cap_bset(current_vx_info())
+
++#if 0
++#define vx_info_mbcap(v, b) \
++ (!vx_info_flags(v, VXF_STATE_SETUP, 0) ? \
++ vx_info_bcaps(v, b) : (b))
+
-+static inline
-+void __vxh_throw_oops(void *__here)
-+{
-+ __VXH_BODY(VXH_THROW_OOPS, {}, __here);
-+ /* prevent further acquisition */
-+ vxh_active = 0;
-+}
++#define task_vx_mbcap(t, b) \
++ vx_info_mbcap((t)->vx_info, (t)->b)
+
++#define vx_mbcap(b) task_vx_mbcap(current, b)
++#endif
+
-+#define vxh_throw_oops() __vxh_throw_oops(__HERE__);
++#define vx_cap_raised(v, c, f) cap_raised(vx_info_mbcaps(v, c), f)
+
-+#define __vxh_get_vx_info(v, h) __vxh_smpl(v, VXH_GET_VX_INFO, h);
-+#define __vxh_put_vx_info(v, h) __vxh_smpl(v, VXH_PUT_VX_INFO, h);
++#define vx_capable(b, c) (capable(b) || \
++ (cap_raised(current_cap(), b) && vx_ccaps(c)))
+
-+#define __vxh_init_vx_info(v, d, h) \
-+ __vxh_data(v, d, VXH_INIT_VX_INFO, h);
-+#define __vxh_set_vx_info(v, d, h) \
-+ __vxh_data(v, d, VXH_SET_VX_INFO, h);
-+#define __vxh_clr_vx_info(v, d, h) \
-+ __vxh_data(v, d, VXH_CLR_VX_INFO, h);
++#define vx_ns_capable(n, b, c) (ns_capable(n, b) || \
++ (cap_raised(current_cap(), b) && vx_ccaps(c)))
+
-+#define __vxh_claim_vx_info(v, d, h) \
-+ __vxh_data(v, d, VXH_CLAIM_VX_INFO, h);
-+#define __vxh_release_vx_info(v, d, h) \
-+ __vxh_data(v, d, VXH_RELEASE_VX_INFO, h);
++#define nx_capable(b, c) (capable(b) || \
++ (cap_raised(current_cap(), b) && nx_ncaps(c)))
+
-+#define vxh_alloc_vx_info(v) \
-+ __vxh_smpl(v, VXH_ALLOC_VX_INFO, __HERE__);
-+#define vxh_dealloc_vx_info(v) \
-+ __vxh_smpl(v, VXH_DEALLOC_VX_INFO, __HERE__);
++#define nx_ns_capable(n, b, c) (ns_capable(n, b) || \
++ (cap_raised(current_cap(), b) && nx_ncaps(c)))
+
-+#define vxh_hash_vx_info(v) \
-+ __vxh_smpl(v, VXH_HASH_VX_INFO, __HERE__);
-+#define vxh_unhash_vx_info(v) \
-+ __vxh_smpl(v, VXH_UNHASH_VX_INFO, __HERE__);
++#define vx_task_initpid(t, n) \
++ ((t)->vx_info && \
++ ((t)->vx_info->vx_initpid == (n)))
+
-+#define vxh_loc_vx_info(v, l) \
-+ __vxh_long(v, l, VXH_LOC_VX_INFO, __HERE__);
-+#define vxh_lookup_vx_info(v, l) \
-+ __vxh_long(v, l, VXH_LOOKUP_VX_INFO, __HERE__);
-+#define vxh_create_vx_info(v, l) \
-+ __vxh_long(v, l, VXH_CREATE_VX_INFO, __HERE__);
++#define vx_current_initpid(n) vx_task_initpid(current, n)
+
-+extern void vxh_dump_history(void);
+
++/* context unshare mask */
+
-+#else /* CONFIG_VSERVER_HISTORY */
++#define __vx_umask(v) ((v)->vx_umask)
+
-+#define __HERE__ 0
++#define vx_current_umask() __vx_umask(current_vx_info())
+
-+#define vxh_throw_oops() do { } while (0)
++#define vx_can_unshare(b, f) (capable(b) || \
++ (cap_raised(current_cap(), b) && \
++ !((f) & ~vx_current_umask())))
+
-+#define __vxh_get_vx_info(v, h) do { } while (0)
-+#define __vxh_put_vx_info(v, h) do { } while (0)
++#define vx_ns_can_unshare(n, b, f) (ns_capable(n, b) || \
++ (cap_raised(current_cap(), b) && \
++ !((f) & ~vx_current_umask())))
+
-+#define __vxh_init_vx_info(v, d, h) do { } while (0)
-+#define __vxh_set_vx_info(v, d, h) do { } while (0)
-+#define __vxh_clr_vx_info(v, d, h) do { } while (0)
++#define __vx_wmask(v) ((v)->vx_wmask)
+
-+#define __vxh_claim_vx_info(v, d, h) do { } while (0)
-+#define __vxh_release_vx_info(v, d, h) do { } while (0)
++#define vx_current_wmask() __vx_wmask(current_vx_info())
+
-+#define vxh_alloc_vx_info(v) do { } while (0)
-+#define vxh_dealloc_vx_info(v) do { } while (0)
+
-+#define vxh_hash_vx_info(v) do { } while (0)
-+#define vxh_unhash_vx_info(v) do { } while (0)
++#define __vx_state(v) ((v) ? ((v)->vx_state) : 0)
+
-+#define vxh_loc_vx_info(v, l) do { } while (0)
-+#define vxh_lookup_vx_info(v, l) do { } while (0)
-+#define vxh_create_vx_info(v, l) do { } while (0)
++#define vx_info_state(v, m) (__vx_state(v) & (m))
+
-+#define vxh_dump_history() do { } while (0)
+
++#define __nx_state(n) ((n) ? ((n)->nx_state) : 0)
+
-+#endif /* CONFIG_VSERVER_HISTORY */
++#define nx_info_state(n, m) (__nx_state(n) & (m))
+
-+#endif /* _VSERVER_HISTORY_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/inode_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/inode_cmd.h
---- linux-4.9.217/include/linux/vserver/inode_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/inode_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,36 @@
-+#ifndef _VSERVER_INODE_CMD_H
-+#define _VSERVER_INODE_CMD_H
++#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/cacct.h linux-4.9/include/linux/vserver/cacct.h
+--- linux-4.9/include/linux/vserver/cacct.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/cacct.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,15 @@
++#ifndef _VSERVER_CACCT_H
++#define _VSERVER_CACCT_H
+
-+#include <uapi/vserver/inode_cmd.h>
+
++enum sock_acc_field {
++ VXA_SOCK_UNSPEC = 0,
++ VXA_SOCK_UNIX,
++ VXA_SOCK_INET,
++ VXA_SOCK_INET6,
++ VXA_SOCK_PACKET,
++ VXA_SOCK_OTHER,
++ VXA_SOCK_SIZE /* array size */
++};
+
++#endif /* _VSERVER_CACCT_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/cacct_cmd.h linux-4.9/include/linux/vserver/cacct_cmd.h
+--- linux-4.9/include/linux/vserver/cacct_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/cacct_cmd.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,10 @@
++#ifndef _VSERVER_CACCT_CMD_H
++#define _VSERVER_CACCT_CMD_H
+
-+#ifdef CONFIG_COMPAT
+
-+#include <asm/compat.h>
++#include <linux/compiler.h>
++#include <uapi/vserver/cacct_cmd.h>
+
-+struct vcmd_ctx_iattr_v1_x32 {
-+ compat_uptr_t name_ptr;
-+ uint32_t tag;
-+ uint32_t flags;
-+ uint32_t mask;
-+};
++extern int vc_sock_stat(struct vx_info *, void __user *);
+
-+#endif /* CONFIG_COMPAT */
++#endif /* _VSERVER_CACCT_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/cacct_def.h linux-4.9/include/linux/vserver/cacct_def.h
+--- linux-4.9/include/linux/vserver/cacct_def.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/cacct_def.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,43 @@
++#ifndef _VSERVER_CACCT_DEF_H
++#define _VSERVER_CACCT_DEF_H
+
-+#include <linux/compiler.h>
++#include <asm/atomic.h>
++#include <linux/vserver/cacct.h>
+
-+extern int vc_get_iattr(void __user *);
-+extern int vc_set_iattr(void __user *);
+
-+extern int vc_fget_iattr(uint32_t, void __user *);
-+extern int vc_fset_iattr(uint32_t, void __user *);
++struct _vx_sock_acc {
++ atomic_long_t count;
++ atomic_long_t total;
++};
+
-+#ifdef CONFIG_COMPAT
++/* context sub struct */
+
-+extern int vc_get_iattr_x32(void __user *);
-+extern int vc_set_iattr_x32(void __user *);
++struct _vx_cacct {
++ struct _vx_sock_acc sock[VXA_SOCK_SIZE][3];
++ atomic_t slab[8];
++ atomic_t page[6][8];
++};
+
-+#endif /* CONFIG_COMPAT */
++#ifdef CONFIG_VSERVER_DEBUG
+
-+#endif /* _VSERVER_INODE_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/inode.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/inode.h
---- linux-4.9.217/include/linux/vserver/inode.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/inode.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,19 @@
-+#ifndef _VSERVER_INODE_H
-+#define _VSERVER_INODE_H
++static inline void __dump_vx_cacct(struct _vx_cacct *cacct)
++{
++ int i, j;
+
-+#include <uapi/vserver/inode.h>
++ printk("\t_vx_cacct:");
++ for (i = 0; i < 6; i++) {
++ struct _vx_sock_acc *ptr = cacct->sock[i];
+
++ printk("\t [%d] =", i);
++ for (j = 0; j < 3; j++) {
++ printk(" [%d] = %8lu, %8lu", j,
++ atomic_long_read(&ptr[j].count),
++ atomic_long_read(&ptr[j].total));
++ }
++ printk("\n");
++ }
++}
+
-+#ifdef CONFIG_VSERVER_PROC_SECURE
-+#define IATTR_PROC_DEFAULT ( IATTR_ADMIN | IATTR_HIDE )
-+#define IATTR_PROC_SYMLINK ( IATTR_ADMIN )
-+#else
-+#define IATTR_PROC_DEFAULT ( IATTR_ADMIN )
-+#define IATTR_PROC_SYMLINK ( IATTR_ADMIN )
+#endif
+
-+#define vx_hide_check(c, m) (((m) & IATTR_HIDE) ? vx_check(c, m) : 1)
++#endif /* _VSERVER_CACCT_DEF_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/cacct_int.h linux-4.9/include/linux/vserver/cacct_int.h
+--- linux-4.9/include/linux/vserver/cacct_int.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/cacct_int.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,17 @@
++#ifndef _VSERVER_CACCT_INT_H
++#define _VSERVER_CACCT_INT_H
+
-+#else /* _VSERVER_INODE_H */
-+#warning duplicate inclusion
-+#endif /* _VSERVER_INODE_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/limit_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/limit_cmd.h
---- linux-4.9.217/include/linux/vserver/limit_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/limit_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,35 @@
-+#ifndef _VSERVER_LIMIT_CMD_H
-+#define _VSERVER_LIMIT_CMD_H
++static inline
++unsigned long vx_sock_count(struct _vx_cacct *cacct, int type, int pos)
++{
++ return atomic_long_read(&cacct->sock[type][pos].count);
++}
+
-+#include <uapi/vserver/limit_cmd.h>
+
++static inline
++unsigned long vx_sock_total(struct _vx_cacct *cacct, int type, int pos)
++{
++ return atomic_long_read(&cacct->sock[type][pos].total);
++}
+
-+#ifdef CONFIG_IA32_EMULATION
++#endif /* _VSERVER_CACCT_INT_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/check.h linux-4.9/include/linux/vserver/check.h
+--- linux-4.9/include/linux/vserver/check.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/check.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,89 @@
++#ifndef _VSERVER_CHECK_H
++#define _VSERVER_CHECK_H
+
-+struct vcmd_ctx_rlimit_v0_x32 {
-+ uint32_t id;
-+ uint64_t minimum;
-+ uint64_t softlimit;
-+ uint64_t maximum;
-+} __attribute__ ((packed));
-+
-+#endif /* CONFIG_IA32_EMULATION */
+
-+#include <linux/compiler.h>
++#define MAX_S_CONTEXT 65535 /* Arbitrary limit */
+
-+extern int vc_get_rlimit_mask(uint32_t, void __user *);
-+extern int vc_get_rlimit(struct vx_info *, void __user *);
-+extern int vc_set_rlimit(struct vx_info *, void __user *);
-+extern int vc_reset_hits(struct vx_info *, void __user *);
-+extern int vc_reset_minmax(struct vx_info *, void __user *);
++#ifdef CONFIG_VSERVER_DYNAMIC_IDS
++#define MIN_D_CONTEXT 49152 /* dynamic contexts start here */
++#else
++#define MIN_D_CONTEXT 65536
++#endif
+
-+extern int vc_rlimit_stat(struct vx_info *, void __user *);
++/* check conditions */
+
-+#ifdef CONFIG_IA32_EMULATION
++#define VS_ADMIN 0x0001
++#define VS_WATCH 0x0002
++#define VS_HIDE 0x0004
++#define VS_HOSTID 0x0008
+
-+extern int vc_get_rlimit_x32(struct vx_info *, void __user *);
-+extern int vc_set_rlimit_x32(struct vx_info *, void __user *);
++#define VS_IDENT 0x0010
++#define VS_EQUIV 0x0020
++#define VS_PARENT 0x0040
++#define VS_CHILD 0x0080
+
-+#endif /* CONFIG_IA32_EMULATION */
++#define VS_ARG_MASK 0x00F0
+
-+#endif /* _VSERVER_LIMIT_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/limit_def.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/limit_def.h
---- linux-4.9.217/include/linux/vserver/limit_def.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/limit_def.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,47 @@
-+#ifndef _VSERVER_LIMIT_DEF_H
-+#define _VSERVER_LIMIT_DEF_H
++#define VS_DYNAMIC 0x0100
++#define VS_STATIC 0x0200
+
-+#include <asm/atomic.h>
-+#include <asm/resource.h>
++#define VS_ATR_MASK 0x0F00
+
-+#include "limit.h"
++#ifdef CONFIG_VSERVER_PRIVACY
++#define VS_ADMIN_P (0)
++#define VS_WATCH_P (0)
++#else
++#define VS_ADMIN_P VS_ADMIN
++#define VS_WATCH_P VS_WATCH
++#endif
+
++#define VS_HARDIRQ 0x1000
++#define VS_SOFTIRQ 0x2000
++#define VS_IRQ 0x4000
+
-+struct _vx_res_limit {
-+ rlim_t soft; /* Context soft limit */
-+ rlim_t hard; /* Context hard limit */
++#define VS_IRQ_MASK 0xF000
+
-+ rlim_atomic_t rcur; /* Current value */
-+ rlim_t rmin; /* Context minimum */
-+ rlim_t rmax; /* Context maximum */
++#include <linux/hardirq.h>
+
-+ atomic_t lhit; /* Limit hits */
-+};
++/*
++ * check current context for ADMIN/WATCH and
++ * optionally against supplied argument
++ */
++static inline int __vs_check(int cid, int id, unsigned int mode)
++{
++ if (mode & VS_ARG_MASK) {
++ if ((mode & VS_IDENT) && (id == cid))
++ return 1;
++ }
++ if (mode & VS_ATR_MASK) {
++ if ((mode & VS_DYNAMIC) &&
++ (id >= MIN_D_CONTEXT) &&
++ (id <= MAX_S_CONTEXT))
++ return 1;
++ if ((mode & VS_STATIC) &&
++ (id > 1) && (id < MIN_D_CONTEXT))
++ return 1;
++ }
++ if (mode & VS_IRQ_MASK) {
++ if ((mode & VS_IRQ) && unlikely(in_interrupt()))
++ return 1;
++ if ((mode & VS_HARDIRQ) && unlikely(in_irq()))
++ return 1;
++ if ((mode & VS_SOFTIRQ) && unlikely(in_softirq()))
++ return 1;
++ }
++ return (((mode & VS_ADMIN) && (cid == 0)) ||
++ ((mode & VS_WATCH) && (cid == 1)) ||
++ ((mode & VS_HOSTID) && (id == 0)));
++}
+
-+/* context sub struct */
++#define vx_check(c, m) __vs_check(vx_current_xid(), c, (m) | VS_IRQ)
+
-+struct _vx_limit {
-+ struct _vx_res_limit res[NUM_LIMITS];
-+};
++#define vx_weak_check(c, m) ((m) ? vx_check(c, m) : 1)
+
-+#ifdef CONFIG_VSERVER_DEBUG
+
-+static inline void __dump_vx_limit(struct _vx_limit *limit)
-+{
-+ int i;
++#define nx_check(c, m) __vs_check(nx_current_nid(), c, m)
+
-+ printk("\t_vx_limit:");
-+ for (i = 0; i < NUM_LIMITS; i++) {
-+ printk("\t [%2d] = %8lu %8lu/%8lu, %8ld/%8ld, %8d\n",
-+ i, (unsigned long)__rlim_get(limit, i),
-+ (unsigned long)__rlim_rmin(limit, i),
-+ (unsigned long)__rlim_rmax(limit, i),
-+ (long)__rlim_soft(limit, i),
-+ (long)__rlim_hard(limit, i),
-+ atomic_read(&__rlim_lhit(limit, i)));
-+ }
-+}
++#define nx_weak_check(c, m) ((m) ? nx_check(c, m) : 1)
+
+#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/context.h linux-4.9/include/linux/vserver/context.h
+--- linux-4.9/include/linux/vserver/context.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/context.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,110 @@
++#ifndef _VSERVER_CONTEXT_H
++#define _VSERVER_CONTEXT_H
+
-+#endif /* _VSERVER_LIMIT_DEF_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/limit.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/limit.h
---- linux-4.9.217/include/linux/vserver/limit.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/limit.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,67 @@
-+#ifndef _VSERVER_LIMIT_H
-+#define _VSERVER_LIMIT_H
+
-+#include <uapi/vserver/limit.h>
++#include <linux/list.h>
++#include <linux/spinlock.h>
++#include <linux/rcupdate.h>
++#include <uapi/vserver/context.h>
+
++#include "limit_def.h"
++#include "sched_def.h"
++#include "cvirt_def.h"
++#include "cacct_def.h"
++#include "device_def.h"
+
-+#define VLIM_NOCHECK ((1L << VLIMIT_DENTRY) | (1L << RLIMIT_RSS))
++#define VX_SPACES 2
+
-+/* keep in sync with CRLIM_INFINITY */
++struct _vx_info_pc {
++ struct _vx_sched_pc sched_pc;
++ struct _vx_cvirt_pc cvirt_pc;
++};
+
-+#define VLIM_INFINITY (~0ULL)
++struct _vx_space {
++ unsigned long vx_nsmask; /* assignment mask */
++ struct nsproxy *vx_nsproxy; /* private namespaces */
++ struct fs_struct *vx_fs; /* private namespace fs */
++ const struct cred *vx_cred; /* task credentials */
++};
+
-+#include <asm/atomic.h>
-+#include <asm/resource.h>
++struct vx_info {
++ struct hlist_node vx_hlist; /* linked list of contexts */
++ vxid_t vx_id; /* context id */
++ atomic_t vx_usecnt; /* usage count */
++ atomic_t vx_tasks; /* tasks count */
++ struct vx_info *vx_parent; /* parent context */
++ int vx_state; /* context state */
+
-+#ifndef RLIM_INFINITY
-+#warning RLIM_INFINITY is undefined
-+#endif
++ struct _vx_space space[VX_SPACES]; /* namespace store */
+
-+#define __rlim_val(l, r, v) ((l)->res[r].v)
++ uint64_t vx_flags; /* context flags */
++ uint64_t vx_ccaps; /* context caps (vserver) */
++ uint64_t vx_umask; /* unshare mask (guest) */
++ uint64_t vx_wmask; /* warn mask (guest) */
++ kernel_cap_t vx_bcaps; /* bounding caps (system) */
+
-+#define __rlim_soft(l, r) __rlim_val(l, r, soft)
-+#define __rlim_hard(l, r) __rlim_val(l, r, hard)
++ struct task_struct *vx_reaper; /* guest reaper process */
++ pid_t vx_initpid; /* PID of guest init */
++ int64_t vx_badness_bias; /* OOM points bias */
+
-+#define __rlim_rcur(l, r) __rlim_val(l, r, rcur)
-+#define __rlim_rmin(l, r) __rlim_val(l, r, rmin)
-+#define __rlim_rmax(l, r) __rlim_val(l, r, rmax)
++ struct _vx_limit limit; /* vserver limits */
++ struct _vx_sched sched; /* vserver scheduler */
++ struct _vx_cvirt cvirt; /* virtual/bias stuff */
++ struct _vx_cacct cacct; /* context accounting */
+
-+#define __rlim_lhit(l, r) __rlim_val(l, r, lhit)
-+#define __rlim_hit(l, r) atomic_inc(&__rlim_lhit(l, r))
++ struct _vx_device dmap; /* default device map targets */
+
-+typedef atomic_long_t rlim_atomic_t;
-+typedef unsigned long rlim_t;
++#ifndef CONFIG_SMP
++ struct _vx_info_pc info_pc; /* per cpu data */
++#else
++ struct _vx_info_pc *ptr_pc; /* per cpu array */
++#endif
+
-+#define __rlim_get(l, r) atomic_long_read(&__rlim_rcur(l, r))
-+#define __rlim_set(l, r, v) atomic_long_set(&__rlim_rcur(l, r), v)
-+#define __rlim_inc(l, r) atomic_long_inc(&__rlim_rcur(l, r))
-+#define __rlim_dec(l, r) atomic_long_dec(&__rlim_rcur(l, r))
-+#define __rlim_add(l, r, v) atomic_long_add(v, &__rlim_rcur(l, r))
-+#define __rlim_sub(l, r, v) atomic_long_sub(v, &__rlim_rcur(l, r))
++ wait_queue_head_t vx_wait; /* context exit waitqueue */
++ int reboot_cmd; /* last sys_reboot() cmd */
++ int exit_code; /* last process exit code */
+
++ char vx_name[65]; /* vserver name */
++};
+
-+#if (RLIM_INFINITY == VLIM_INFINITY)
-+#define VX_VLIM(r) ((long long)(long)(r))
-+#define VX_RLIM(v) ((rlim_t)(v))
++#ifndef CONFIG_SMP
++#define vx_ptr_pc(vxi) (&(vxi)->info_pc)
++#define vx_per_cpu(vxi, v, id) vx_ptr_pc(vxi)->v
+#else
-+#define VX_VLIM(r) (((r) == RLIM_INFINITY) \
-+ ? VLIM_INFINITY : (long long)(r))
-+#define VX_RLIM(v) (((v) == VLIM_INFINITY) \
-+ ? RLIM_INFINITY : (rlim_t)(v))
++#define vx_ptr_pc(vxi) ((vxi)->ptr_pc)
++#define vx_per_cpu(vxi, v, id) per_cpu_ptr(vx_ptr_pc(vxi), id)->v
+#endif
+
-+struct sysinfo;
++#define vx_cpu(vxi, v) vx_per_cpu(vxi, v, smp_processor_id())
+
-+#ifdef CONFIG_MEMCG
-+void vx_vsi_meminfo(struct sysinfo *);
-+void vx_vsi_swapinfo(struct sysinfo *);
-+long vx_vsi_cached(struct sysinfo *);
-+#else /* !CONFIG_MEMCG */
-+#define vx_vsi_meminfo(s) do { } while (0)
-+#define vx_vsi_swapinfo(s) do { } while (0)
-+#define vx_vsi_cached(s) (0L)
-+#endif /* !CONFIG_MEMCG */
+
-+#define NUM_LIMITS 24
++struct vx_info_save {
++ struct vx_info *vxi;
++ vxid_t xid;
++};
+
-+#endif /* _VSERVER_LIMIT_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/limit_int.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/limit_int.h
---- linux-4.9.217/include/linux/vserver/limit_int.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/limit_int.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,193 @@
-+#ifndef _VSERVER_LIMIT_INT_H
-+#define _VSERVER_LIMIT_INT_H
+
-+#define VXD_RCRES_COND(r) VXD_CBIT(cres, r)
-+#define VXD_RLIMIT_COND(r) VXD_CBIT(limit, r)
++/* status flags */
+
-+extern const char *vlimit_name[NUM_LIMITS];
++#define VXS_HASHED 0x0001
++#define VXS_PAUSED 0x0010
++#define VXS_SHUTDOWN 0x0100
++#define VXS_HELPER 0x1000
++#define VXS_RELEASED 0x8000
+
-+static inline void __vx_acc_cres(struct vx_info *vxi,
-+ int res, int dir, void *_data, char *_file, int _line)
-+{
-+ if (VXD_RCRES_COND(res))
-+ vxlprintk(1, "vx_acc_cres[%5d,%s,%2d]: %5ld%s (%p)",
-+ (vxi ? vxi->vx_id : -1), vlimit_name[res], res,
-+ (vxi ? (long)__rlim_get(&vxi->limit, res) : 0),
-+ (dir > 0) ? "++" : "--", _data, _file, _line);
-+ if (!vxi)
-+ return;
+
-+ if (dir > 0)
-+ __rlim_inc(&vxi->limit, res);
-+ else
-+ __rlim_dec(&vxi->limit, res);
-+}
-+
-+static inline void __vx_add_cres(struct vx_info *vxi,
-+ int res, int amount, void *_data, char *_file, int _line)
-+{
-+ if (VXD_RCRES_COND(res))
-+ vxlprintk(1, "vx_add_cres[%5d,%s,%2d]: %5ld += %5d (%p)",
-+ (vxi ? vxi->vx_id : -1), vlimit_name[res], res,
-+ (vxi ? (long)__rlim_get(&vxi->limit, res) : 0),
-+ amount, _data, _file, _line);
-+ if (amount == 0)
-+ return;
-+ if (!vxi)
-+ return;
-+ __rlim_add(&vxi->limit, res, amount);
-+}
-+
-+static inline
-+int __vx_cres_adjust_max(struct _vx_limit *limit, int res, rlim_t value)
-+{
-+ int cond = (value > __rlim_rmax(limit, res));
-+
-+ if (cond)
-+ __rlim_rmax(limit, res) = value;
-+ return cond;
-+}
++extern void claim_vx_info(struct vx_info *, struct task_struct *);
++extern void release_vx_info(struct vx_info *, struct task_struct *);
+
-+static inline
-+int __vx_cres_adjust_min(struct _vx_limit *limit, int res, rlim_t value)
-+{
-+ int cond = (value < __rlim_rmin(limit, res));
++extern struct vx_info *lookup_vx_info(int);
++extern struct vx_info *lookup_or_create_vx_info(int);
+
-+ if (cond)
-+ __rlim_rmin(limit, res) = value;
-+ return cond;
-+}
++extern int get_xid_list(int, unsigned int *, int);
++extern int xid_is_hashed(vxid_t);
+
-+static inline
-+void __vx_cres_fixup(struct _vx_limit *limit, int res, rlim_t value)
-+{
-+ if (!__vx_cres_adjust_max(limit, res, value))
-+ __vx_cres_adjust_min(limit, res, value);
-+}
++extern int vx_migrate_task(struct task_struct *, struct vx_info *, int);
+
++extern long vs_state_change(struct vx_info *, unsigned int);
+
-+/* return values:
-+ +1 ... no limit hit
-+ -1 ... over soft limit
-+ 0 ... over hard limit */
+
-+static inline int __vx_cres_avail(struct vx_info *vxi,
-+ int res, int num, char *_file, int _line)
-+{
-+ struct _vx_limit *limit;
-+ rlim_t value;
++#endif /* _VSERVER_CONTEXT_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/context_cmd.h linux-4.9/include/linux/vserver/context_cmd.h
+--- linux-4.9/include/linux/vserver/context_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/context_cmd.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,33 @@
++#ifndef _VSERVER_CONTEXT_CMD_H
++#define _VSERVER_CONTEXT_CMD_H
+
-+ if (VXD_RLIMIT_COND(res))
-+ vxlprintk(1, "vx_cres_avail[%5d,%s,%2d]: %5ld/%5ld > %5ld + %5d",
-+ (vxi ? vxi->vx_id : -1), vlimit_name[res], res,
-+ (vxi ? (long)__rlim_soft(&vxi->limit, res) : -1),
-+ (vxi ? (long)__rlim_hard(&vxi->limit, res) : -1),
-+ (vxi ? (long)__rlim_get(&vxi->limit, res) : 0),
-+ num, _file, _line);
-+ if (!vxi)
-+ return 1;
++#include <uapi/vserver/context_cmd.h>
+
-+ limit = &vxi->limit;
-+ value = __rlim_get(limit, res);
++extern int vc_task_xid(uint32_t);
+
-+ if (!__vx_cres_adjust_max(limit, res, value))
-+ __vx_cres_adjust_min(limit, res, value);
++extern int vc_vx_info(struct vx_info *, void __user *);
+
-+ if (num == 0)
-+ return 1;
++extern int vc_ctx_stat(struct vx_info *, void __user *);
+
-+ if (__rlim_soft(limit, res) == RLIM_INFINITY)
-+ return -1;
-+ if (value + num <= __rlim_soft(limit, res))
-+ return -1;
++extern int vc_ctx_create(uint32_t, void __user *);
++extern int vc_ctx_migrate(struct vx_info *, void __user *);
+
-+ if (__rlim_hard(limit, res) == RLIM_INFINITY)
-+ return 1;
-+ if (value + num <= __rlim_hard(limit, res))
-+ return 1;
++extern int vc_get_cflags(struct vx_info *, void __user *);
++extern int vc_set_cflags(struct vx_info *, void __user *);
+
-+ __rlim_hit(limit, res);
-+ return 0;
-+}
++extern int vc_get_ccaps(struct vx_info *, void __user *);
++extern int vc_set_ccaps(struct vx_info *, void __user *);
+
++extern int vc_get_bcaps(struct vx_info *, void __user *);
++extern int vc_set_bcaps(struct vx_info *, void __user *);
+
-+static const int VLA_RSS[] = { RLIMIT_RSS, VLIMIT_ANON, VLIMIT_MAPPED, 0 };
++extern int vc_get_umask(struct vx_info *, void __user *);
++extern int vc_set_umask(struct vx_info *, void __user *);
+
-+static inline
-+rlim_t __vx_cres_array_sum(struct _vx_limit *limit, const int *array)
-+{
-+ rlim_t value, sum = 0;
-+ int res;
++extern int vc_get_wmask(struct vx_info *, void __user *);
++extern int vc_set_wmask(struct vx_info *, void __user *);
+
-+ while ((res = *array++)) {
-+ value = __rlim_get(limit, res);
-+ __vx_cres_fixup(limit, res, value);
-+ sum += value;
-+ }
-+ return sum;
-+}
++extern int vc_get_badness(struct vx_info *, void __user *);
++extern int vc_set_badness(struct vx_info *, void __user *);
+
-+static inline
-+rlim_t __vx_cres_array_fixup(struct _vx_limit *limit, const int *array)
-+{
-+ rlim_t value = __vx_cres_array_sum(limit, array + 1);
-+ int res = *array;
++#endif /* _VSERVER_CONTEXT_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/cvirt.h linux-4.9/include/linux/vserver/cvirt.h
+--- linux-4.9/include/linux/vserver/cvirt.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/cvirt.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,11 @@
++#ifndef _VSERVER_CVIRT_H
++#define _VSERVER_CVIRT_H
+
-+ if (value == __rlim_get(limit, res))
-+ return value;
++struct vx_info;
+
-+ __rlim_set(limit, res, value);
-+ /* now adjust min/max */
-+ if (!__vx_cres_adjust_max(limit, res, value))
-+ __vx_cres_adjust_min(limit, res, value);
++void vx_update_load(struct vx_info *);
+
-+ return value;
-+}
+
-+static inline int __vx_cres_array_avail(struct vx_info *vxi,
-+ const int *array, int num, char *_file, int _line)
-+{
-+ struct _vx_limit *limit;
-+ rlim_t value = 0;
-+ int res;
++int vx_do_syslog(int, char __user *, int);
+
-+ if (num == 0)
-+ return 1;
-+ if (!vxi)
-+ return 1;
++#endif /* _VSERVER_CVIRT_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/cvirt_cmd.h linux-4.9/include/linux/vserver/cvirt_cmd.h
+--- linux-4.9/include/linux/vserver/cvirt_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/cvirt_cmd.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,13 @@
++#ifndef _VSERVER_CVIRT_CMD_H
++#define _VSERVER_CVIRT_CMD_H
+
-+ limit = &vxi->limit;
-+ res = *array;
-+ value = __vx_cres_array_sum(limit, array + 1);
+
-+ __rlim_set(limit, res, value);
-+ __vx_cres_fixup(limit, res, value);
++#include <linux/compiler.h>
++#include <uapi/vserver/cvirt_cmd.h>
+
-+ return __vx_cres_avail(vxi, res, num, _file, _line);
-+}
++extern int vc_set_vhi_name(struct vx_info *, void __user *);
++extern int vc_get_vhi_name(struct vx_info *, void __user *);
+
++extern int vc_virt_stat(struct vx_info *, void __user *);
+
-+static inline void vx_limit_fixup(struct _vx_limit *limit, int id)
-+{
-+ rlim_t value;
-+ int res;
++#endif /* _VSERVER_CVIRT_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/cvirt_def.h linux-4.9/include/linux/vserver/cvirt_def.h
+--- linux-4.9/include/linux/vserver/cvirt_def.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/cvirt_def.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,80 @@
++#ifndef _VSERVER_CVIRT_DEF_H
++#define _VSERVER_CVIRT_DEF_H
+
-+ /* complex resources first */
-+ if ((id < 0) || (id == RLIMIT_RSS))
-+ __vx_cres_array_fixup(limit, VLA_RSS);
++#include <linux/jiffies.h>
++#include <linux/spinlock.h>
++#include <linux/wait.h>
++#include <linux/time.h>
++#include <asm/atomic.h>
+
-+ for (res = 0; res < NUM_LIMITS; res++) {
-+ if ((id > 0) && (res != id))
-+ continue;
+
-+ value = __rlim_get(limit, res);
-+ __vx_cres_fixup(limit, res, value);
++struct _vx_usage_stat {
++ uint64_t user;
++ uint64_t nice;
++ uint64_t system;
++ uint64_t softirq;
++ uint64_t irq;
++ uint64_t idle;
++ uint64_t iowait;
++};
+
-+ /* not supposed to happen, maybe warn? */
-+ if (__rlim_rmax(limit, res) > __rlim_hard(limit, res))
-+ __rlim_rmax(limit, res) = __rlim_hard(limit, res);
-+ }
-+}
++struct _vx_syslog {
++ wait_queue_head_t log_wait;
++ spinlock_t logbuf_lock; /* lock for the log buffer */
+
++ unsigned long log_start; /* next char to be read by syslog() */
++ unsigned long con_start; /* next char to be sent to consoles */
++ unsigned long log_end; /* most-recently-written-char + 1 */
++ unsigned long logged_chars; /* #chars since last read+clear operation */
+
-+#endif /* _VSERVER_LIMIT_INT_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/monitor.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/monitor.h
---- linux-4.9.217/include/linux/vserver/monitor.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/monitor.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,6 @@
-+#ifndef _VSERVER_MONITOR_H
-+#define _VSERVER_MONITOR_H
++ char log_buf[1024];
++};
+
-+#include <uapi/vserver/monitor.h>
+
-+#endif /* _VSERVER_MONITOR_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/network_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/network_cmd.h
---- linux-4.9.217/include/linux/vserver/network_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/network_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,37 @@
-+#ifndef _VSERVER_NETWORK_CMD_H
-+#define _VSERVER_NETWORK_CMD_H
++/* context sub struct */
+
-+#include <uapi/vserver/network_cmd.h>
++struct _vx_cvirt {
++ atomic_t nr_threads; /* number of current threads */
++ atomic_t nr_running; /* number of running threads */
++ atomic_t nr_uninterruptible; /* number of uninterruptible threads */
+
-+extern int vc_task_nid(uint32_t);
++ atomic_t nr_onhold; /* processes on hold */
++ uint32_t onhold_last; /* jiffies when put on hold */
+
-+extern int vc_nx_info(struct nx_info *, void __user *);
++ struct timespec64 bias_ts; /* time offset to the host */
++ struct timespec64 bias_idle;
++ struct timespec64 bias_uptime; /* context creation point */
++ uint64_t bias_clock; /* offset in clock_t */
+
-+extern int vc_net_create(uint32_t, void __user *);
-+extern int vc_net_migrate(struct nx_info *, void __user *);
++ spinlock_t load_lock; /* lock for the load averages */
++ atomic_t load_updates; /* nr of load updates done so far */
++ uint32_t load_last; /* last time load was calculated */
++ uint32_t load[3]; /* load averages 1,5,15 */
+
-+extern int vc_net_add(struct nx_info *, void __user *);
-+extern int vc_net_remove(struct nx_info *, void __user *);
++ atomic_t total_forks; /* number of forks so far */
+
-+extern int vc_net_add_ipv4_v1(struct nx_info *, void __user *);
-+extern int vc_net_add_ipv4(struct nx_info *, void __user *);
++ struct _vx_syslog syslog;
++};
+
-+extern int vc_net_rem_ipv4_v1(struct nx_info *, void __user *);
-+extern int vc_net_rem_ipv4(struct nx_info *, void __user *);
++struct _vx_cvirt_pc {
++ struct _vx_usage_stat cpustat;
++};
+
-+extern int vc_net_add_ipv6(struct nx_info *, void __user *);
-+extern int vc_net_remove_ipv6(struct nx_info *, void __user *);
+
-+extern int vc_add_match_ipv4(struct nx_info *, void __user *);
-+extern int vc_get_match_ipv4(struct nx_info *, void __user *);
++#ifdef CONFIG_VSERVER_DEBUG
+
-+extern int vc_add_match_ipv6(struct nx_info *, void __user *);
-+extern int vc_get_match_ipv6(struct nx_info *, void __user *);
-+
-+extern int vc_get_nflags(struct nx_info *, void __user *);
-+extern int vc_set_nflags(struct nx_info *, void __user *);
-+
-+extern int vc_get_ncaps(struct nx_info *, void __user *);
-+extern int vc_set_ncaps(struct nx_info *, void __user *);
-+
-+#endif /* _VSERVER_CONTEXT_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/network.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/network.h
---- linux-4.9.217/include/linux/vserver/network.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/network.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,76 @@
-+#ifndef _VSERVER_NETWORK_H
-+#define _VSERVER_NETWORK_H
-+
-+
-+#include <linux/list.h>
-+#include <linux/spinlock.h>
-+#include <linux/rcupdate.h>
-+#include <linux/in.h>
-+#include <linux/in6.h>
-+#include <asm/atomic.h>
-+#include <uapi/vserver/network.h>
-+
-+struct nx_addr_v4 {
-+ struct nx_addr_v4 *next;
-+ struct in_addr ip[2];
-+ struct in_addr mask;
-+ uint16_t type;
-+ uint16_t flags;
-+};
-+
-+struct nx_addr_v6 {
-+ struct nx_addr_v6 *next;
-+ struct in6_addr ip;
-+ struct in6_addr mask;
-+ uint32_t prefix;
-+ uint16_t type;
-+ uint16_t flags;
-+};
-+
-+struct nx_info {
-+ struct hlist_node nx_hlist; /* linked list of nxinfos */
-+ vnid_t nx_id; /* vnet id */
-+ atomic_t nx_usecnt; /* usage count */
-+ atomic_t nx_tasks; /* tasks count */
-+ int nx_state; /* context state */
-+
-+ uint64_t nx_flags; /* network flag word */
-+ uint64_t nx_ncaps; /* network capabilities */
++static inline void __dump_vx_cvirt(struct _vx_cvirt *cvirt)
++{
++ printk("\t_vx_cvirt:\n");
++ printk("\t threads: %4d, %4d, %4d, %4d\n",
++ atomic_read(&cvirt->nr_threads),
++ atomic_read(&cvirt->nr_running),
++ atomic_read(&cvirt->nr_uninterruptible),
++ atomic_read(&cvirt->nr_onhold));
++ /* add rest here */
++ printk("\t total_forks = %d\n", atomic_read(&cvirt->total_forks));
++}
+
-+ spinlock_t addr_lock; /* protect address changes */
-+ struct in_addr v4_lback; /* Loopback address */
-+ struct in_addr v4_bcast; /* Broadcast address */
-+ struct nx_addr_v4 v4; /* First/Single ipv4 address */
-+#ifdef CONFIG_IPV6
-+ struct nx_addr_v6 v6; /* First/Single ipv6 address */
+#endif
-+ char nx_name[65]; /* network context name */
-+};
-+
-+
-+/* status flags */
-+
-+#define NXS_HASHED 0x0001
-+#define NXS_SHUTDOWN 0x0100
-+#define NXS_RELEASED 0x8000
-+
-+extern struct nx_info *lookup_nx_info(int);
-+
-+extern int get_nid_list(int, unsigned int *, int);
-+extern int nid_is_hashed(vnid_t);
+
-+extern int nx_migrate_task(struct task_struct *, struct nx_info *);
++#endif /* _VSERVER_CVIRT_DEF_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/debug.h linux-4.9/include/linux/vserver/debug.h
+--- linux-4.9/include/linux/vserver/debug.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/debug.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,146 @@
++#ifndef _VSERVER_DEBUG_H
++#define _VSERVER_DEBUG_H
+
-+extern long vs_net_change(struct nx_info *, unsigned int);
+
-+struct sock;
++#define VXD_CBIT(n, m) (vs_debug_ ## n & (1 << (m)))
++#define VXD_CMIN(n, m) (vs_debug_ ## n > (m))
++#define VXD_MASK(n, m) (vs_debug_ ## n & (m))
+
++#define VXD_DEV(d) (d), (d)->bd_inode->i_ino, \
++ imajor((d)->bd_inode), iminor((d)->bd_inode)
++#define VXF_DEV "%p[%lu,%d:%d]"
+
-+#define NX_IPV4(n) ((n)->v4.type != NXA_TYPE_NONE)
-+#ifdef CONFIG_IPV6
-+#define NX_IPV6(n) ((n)->v6.type != NXA_TYPE_NONE)
++#if defined(CONFIG_QUOTES_UTF8)
++#define VS_Q_LQM "\xc2\xbb"
++#define VS_Q_RQM "\xc2\xab"
++#elif defined(CONFIG_QUOTES_ASCII)
++#define VS_Q_LQM "\x27"
++#define VS_Q_RQM "\x27"
+#else
-+#define NX_IPV6(n) (0)
++#define VS_Q_LQM "\xbb"
++#define VS_Q_RQM "\xab"
+#endif
+
-+#endif /* _VSERVER_NETWORK_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/percpu.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/percpu.h
---- linux-4.9.217/include/linux/vserver/percpu.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/percpu.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,14 @@
-+#ifndef _VSERVER_PERCPU_H
-+#define _VSERVER_PERCPU_H
++#define VS_Q(f) VS_Q_LQM f VS_Q_RQM
+
-+#include "cvirt_def.h"
-+#include "sched_def.h"
+
-+struct _vx_percpu {
-+ struct _vx_cvirt_pc cvirt;
-+ struct _vx_sched_pc sched;
-+};
++#define vxd_path(p) \
++ ({ static char _buffer[PATH_MAX]; \
++ d_path(p, _buffer, sizeof(_buffer)); })
+
-+#define PERCPU_PERCTX (sizeof(struct _vx_percpu))
++#define vxd_cond_path(n) \
++ ((n) ? vxd_path(&(n)->path) : "<null>" )
+
-+#endif /* _VSERVER_PERCPU_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/pid.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/pid.h
---- linux-4.9.217/include/linux/vserver/pid.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/pid.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,51 @@
-+#ifndef _VSERVER_PID_H
-+#define _VSERVER_PID_H
+
-+/* pid faking stuff */
++#ifdef CONFIG_VSERVER_DEBUG
+
-+#define vx_info_map_pid(v, p) \
-+ __vx_info_map_pid((v), (p), __func__, __FILE__, __LINE__)
-+#define vx_info_map_tgid(v,p) vx_info_map_pid(v,p)
-+#define vx_map_pid(p) vx_info_map_pid(current_vx_info(), p)
-+#define vx_map_tgid(p) vx_map_pid(p)
++extern unsigned int vs_debug_switch;
++extern unsigned int vs_debug_xid;
++extern unsigned int vs_debug_nid;
++extern unsigned int vs_debug_tag;
++extern unsigned int vs_debug_net;
++extern unsigned int vs_debug_limit;
++extern unsigned int vs_debug_cres;
++extern unsigned int vs_debug_dlim;
++extern unsigned int vs_debug_quota;
++extern unsigned int vs_debug_cvirt;
++extern unsigned int vs_debug_space;
++extern unsigned int vs_debug_perm;
++extern unsigned int vs_debug_misc;
+
-+static inline int __vx_info_map_pid(struct vx_info *vxi, int pid,
-+ const char *func, const char *file, int line)
-+{
-+ if (vx_info_flags(vxi, VXF_INFO_INIT, 0)) {
-+ vxfprintk(VXD_CBIT(cvirt, 2),
-+ "vx_map_tgid: %p/%llx: %d -> %d",
-+ vxi, (long long)vxi->vx_flags, pid,
-+ (pid && pid == vxi->vx_initpid) ? 1 : pid,
-+ func, file, line);
-+ if (pid == 0)
-+ return 0;
-+ if (pid == vxi->vx_initpid)
-+ return 1;
-+ }
-+ return pid;
-+}
+
-+#define vx_info_rmap_pid(v, p) \
-+ __vx_info_rmap_pid((v), (p), __func__, __FILE__, __LINE__)
-+#define vx_rmap_pid(p) vx_info_rmap_pid(current_vx_info(), p)
-+#define vx_rmap_tgid(p) vx_rmap_pid(p)
++#define VX_LOGLEVEL "vxD: "
++#define VX_PROC_FMT "%p: "
++#define VX_PROCESS current
+
-+static inline int __vx_info_rmap_pid(struct vx_info *vxi, int pid,
-+ const char *func, const char *file, int line)
-+{
-+ if (vx_info_flags(vxi, VXF_INFO_INIT, 0)) {
-+ vxfprintk(VXD_CBIT(cvirt, 2),
-+ "vx_rmap_tgid: %p/%llx: %d -> %d",
-+ vxi, (long long)vxi->vx_flags, pid,
-+ (pid == 1) ? vxi->vx_initpid : pid,
-+ func, file, line);
-+ if ((pid == 1) && vxi->vx_initpid)
-+ return vxi->vx_initpid;
-+ if (pid == vxi->vx_initpid)
-+ return ~0U;
-+ }
-+ return pid;
-+}
++#define vxdprintk(c, f, x...) \
++ do { \
++ if (c) \
++ printk(VX_LOGLEVEL VX_PROC_FMT f "\n", \
++ VX_PROCESS , ##x); \
++ } while (0)
+
-+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/sched_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/sched_cmd.h
---- linux-4.9.217/include/linux/vserver/sched_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/sched_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,11 @@
-+#ifndef _VSERVER_SCHED_CMD_H
-+#define _VSERVER_SCHED_CMD_H
++#define vxlprintk(c, f, x...) \
++ do { \
++ if (c) \
++ printk(VX_LOGLEVEL f " @%s:%d\n", x); \
++ } while (0)
+
++#define vxfprintk(c, f, x...) \
++ do { \
++ if (c) \
++ printk(VX_LOGLEVEL f " %s@%s:%d\n", x); \
++ } while (0)
+
-+#include <linux/compiler.h>
-+#include <uapi/vserver/sched_cmd.h>
+
-+extern int vc_set_prio_bias(struct vx_info *, void __user *);
-+extern int vc_get_prio_bias(struct vx_info *, void __user *);
++struct vx_info;
+
-+#endif /* _VSERVER_SCHED_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/sched_def.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/sched_def.h
---- linux-4.9.217/include/linux/vserver/sched_def.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/sched_def.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,38 @@
-+#ifndef _VSERVER_SCHED_DEF_H
-+#define _VSERVER_SCHED_DEF_H
++void dump_vx_info(struct vx_info *, int);
++void dump_vx_info_inactive(int);
+
-+#include <linux/spinlock.h>
-+#include <linux/jiffies.h>
-+#include <linux/cpumask.h>
-+#include <asm/atomic.h>
-+#include <asm/param.h>
++#else /* CONFIG_VSERVER_DEBUG */
+
++#define vs_debug_switch 0
++#define vs_debug_xid 0
++#define vs_debug_nid 0
++#define vs_debug_tag 0
++#define vs_debug_net 0
++#define vs_debug_limit 0
++#define vs_debug_cres 0
++#define vs_debug_dlim 0
++#define vs_debug_quota 0
++#define vs_debug_cvirt 0
++#define vs_debug_space 0
++#define vs_debug_perm 0
++#define vs_debug_misc 0
+
-+/* context sub struct */
++#define vxdprintk(x...) do { } while (0)
++#define vxlprintk(x...) do { } while (0)
++#define vxfprintk(x...) do { } while (0)
+
-+struct _vx_sched {
-+ int prio_bias; /* bias offset for priority */
++#endif /* CONFIG_VSERVER_DEBUG */
+
-+ cpumask_t update; /* CPUs which should update */
-+};
+
-+struct _vx_sched_pc {
-+ int prio_bias; /* bias offset for priority */
++#ifdef CONFIG_VSERVER_WARN
+
-+ uint64_t user_ticks; /* token tick events */
-+ uint64_t sys_ticks; /* token tick events */
-+ uint64_t hold_ticks; /* token ticks paused */
-+};
++#define VX_WARNLEVEL KERN_WARNING "vxW: "
++#define VX_WARN_TASK "[" VS_Q("%s") ",%u:#%u|%u|%u] "
++#define VX_WARN_XID "[xid #%u] "
++#define VX_WARN_NID "[nid #%u] "
++#define VX_WARN_TAG "[tag #%u] "
+
++#define vxwprintk(c, f, x...) \
++ do { \
++ if (c) \
++ printk(VX_WARNLEVEL f "\n", ##x); \
++ } while (0)
+
-+#ifdef CONFIG_VSERVER_DEBUG
++#else /* CONFIG_VSERVER_WARN */
+
-+static inline void __dump_vx_sched(struct _vx_sched *sched)
-+{
-+ printk("\t_vx_sched:\n");
-+ printk("\t priority = %4d\n", sched->prio_bias);
-+}
++#define vxwprintk(x...) do { } while (0)
+
-+#endif
++#endif /* CONFIG_VSERVER_WARN */
+
-+#endif /* _VSERVER_SCHED_DEF_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/sched.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/sched.h
---- linux-4.9.217/include/linux/vserver/sched.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/sched.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,23 @@
-+#ifndef _VSERVER_SCHED_H
-+#define _VSERVER_SCHED_H
++#define vxwprintk_task(c, f, x...) \
++ vxwprintk(c, VX_WARN_TASK f, \
++ current->comm, current->pid, \
++ current->xid, current->nid, \
++ current->tag, ##x)
++#define vxwprintk_xid(c, f, x...) \
++ vxwprintk(c, VX_WARN_XID f, current->xid, x)
++#define vxwprintk_nid(c, f, x...) \
++ vxwprintk(c, VX_WARN_NID f, current->nid, x)
++#define vxwprintk_tag(c, f, x...) \
++ vxwprintk(c, VX_WARN_TAG f, current->tag, x)
+
++#ifdef CONFIG_VSERVER_DEBUG
++#define vxd_assert_lock(l) assert_spin_locked(l)
++#define vxd_assert(c, f, x...) vxlprintk(!(c), \
++ "assertion [" f "] failed.", ##x, __FILE__, __LINE__)
++#else
++#define vxd_assert_lock(l) do { } while (0)
++#define vxd_assert(c, f, x...) do { } while (0)
++#endif
+
-+#ifdef __KERNEL__
+
-+struct timespec;
++#endif /* _VSERVER_DEBUG_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/debug_cmd.h linux-4.9/include/linux/vserver/debug_cmd.h
+--- linux-4.9/include/linux/vserver/debug_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/debug_cmd.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,37 @@
++#ifndef _VSERVER_DEBUG_CMD_H
++#define _VSERVER_DEBUG_CMD_H
+
-+void vx_vsi_uptime(struct timespec *, struct timespec *);
++#include <uapi/vserver/debug_cmd.h>
+
+
-+struct vx_info;
++#ifdef CONFIG_COMPAT
+
-+void vx_update_load(struct vx_info *);
++#include <asm/compat.h>
+
++struct vcmd_read_history_v0_x32 {
++ uint32_t index;
++ uint32_t count;
++ compat_uptr_t data_ptr;
++};
+
-+void vx_update_sched_param(struct _vx_sched *sched,
-+ struct _vx_sched_pc *sched_pc);
++struct vcmd_read_monitor_v0_x32 {
++ uint32_t index;
++ uint32_t count;
++ compat_uptr_t data_ptr;
++};
+
-+#endif /* __KERNEL__ */
-+#else /* _VSERVER_SCHED_H */
-+#warning duplicate inclusion
-+#endif /* _VSERVER_SCHED_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/signal_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/signal_cmd.h
---- linux-4.9.217/include/linux/vserver/signal_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/signal_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,14 @@
-+#ifndef _VSERVER_SIGNAL_CMD_H
-+#define _VSERVER_SIGNAL_CMD_H
++#endif /* CONFIG_COMPAT */
+
-+#include <uapi/vserver/signal_cmd.h>
++extern int vc_dump_history(uint32_t);
+
++extern int vc_read_history(uint32_t, void __user *);
++extern int vc_read_monitor(uint32_t, void __user *);
+
-+extern int vc_ctx_kill(struct vx_info *, void __user *);
-+extern int vc_wait_exit(struct vx_info *, void __user *);
++#ifdef CONFIG_COMPAT
+
++extern int vc_read_history_x32(uint32_t, void __user *);
++extern int vc_read_monitor_x32(uint32_t, void __user *);
+
-+extern int vc_get_pflags(uint32_t pid, void __user *);
-+extern int vc_set_pflags(uint32_t pid, void __user *);
++#endif /* CONFIG_COMPAT */
+
-+#endif /* _VSERVER_SIGNAL_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/signal.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/signal.h
---- linux-4.9.217/include/linux/vserver/signal.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/signal.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,14 @@
-+#ifndef _VSERVER_SIGNAL_H
-+#define _VSERVER_SIGNAL_H
++#endif /* _VSERVER_DEBUG_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/device.h linux-4.9/include/linux/vserver/device.h
+--- linux-4.9/include/linux/vserver/device.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/device.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,9 @@
++#ifndef _VSERVER_DEVICE_H
++#define _VSERVER_DEVICE_H
+
+
-+#ifdef __KERNEL__
++#include <uapi/vserver/device.h>
+
-+struct vx_info;
++#else /* _VSERVER_DEVICE_H */
++#warning duplicate inclusion
++#endif /* _VSERVER_DEVICE_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/device_cmd.h linux-4.9/include/linux/vserver/device_cmd.h
+--- linux-4.9/include/linux/vserver/device_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/device_cmd.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,31 @@
++#ifndef _VSERVER_DEVICE_CMD_H
++#define _VSERVER_DEVICE_CMD_H
+
-+int vx_info_kill(struct vx_info *, int, int);
++#include <uapi/vserver/device_cmd.h>
+
-+#endif /* __KERNEL__ */
-+#else /* _VSERVER_SIGNAL_H */
-+#warning duplicate inclusion
-+#endif /* _VSERVER_SIGNAL_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/space_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/space_cmd.h
---- linux-4.9.217/include/linux/vserver/space_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/space_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,13 @@
-+#ifndef _VSERVER_SPACE_CMD_H
-+#define _VSERVER_SPACE_CMD_H
+
-+#include <uapi/vserver/space_cmd.h>
++#ifdef CONFIG_COMPAT
+
++#include <asm/compat.h>
+
-+extern int vc_enter_space_v1(struct vx_info *, void __user *);
-+extern int vc_set_space_v1(struct vx_info *, void __user *);
-+extern int vc_enter_space(struct vx_info *, void __user *);
-+extern int vc_set_space(struct vx_info *, void __user *);
-+extern int vc_get_space_mask(void __user *, int);
++struct vcmd_set_mapping_v0_x32 {
++ compat_uptr_t device_ptr;
++ compat_uptr_t target_ptr;
++ uint32_t flags;
++};
+
-+#endif /* _VSERVER_SPACE_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/space.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/space.h
---- linux-4.9.217/include/linux/vserver/space.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/space.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,12 @@
-+#ifndef _VSERVER_SPACE_H
-+#define _VSERVER_SPACE_H
++#endif /* CONFIG_COMPAT */
+
-+#include <linux/types.h>
++#include <linux/compiler.h>
+
-+struct vx_info;
++extern int vc_set_mapping(struct vx_info *, void __user *);
++extern int vc_unset_mapping(struct vx_info *, void __user *);
+
-+int vx_set_space(struct vx_info *vxi, unsigned long mask, unsigned index);
++#ifdef CONFIG_COMPAT
+
-+#else /* _VSERVER_SPACE_H */
-+#warning duplicate inclusion
-+#endif /* _VSERVER_SPACE_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/switch.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/switch.h
---- linux-4.9.217/include/linux/vserver/switch.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/switch.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,8 @@
-+#ifndef _VSERVER_SWITCH_H
-+#define _VSERVER_SWITCH_H
++extern int vc_set_mapping_x32(struct vx_info *, void __user *);
++extern int vc_unset_mapping_x32(struct vx_info *, void __user *);
+
++#endif /* CONFIG_COMPAT */
+
-+#include <linux/errno.h>
-+#include <uapi/vserver/switch.h>
++#endif /* _VSERVER_DEVICE_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/device_def.h linux-4.9/include/linux/vserver/device_def.h
+--- linux-4.9/include/linux/vserver/device_def.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/device_def.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,17 @@
++#ifndef _VSERVER_DEVICE_DEF_H
++#define _VSERVER_DEVICE_DEF_H
+
-+#endif /* _VSERVER_SWITCH_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/tag_cmd.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/tag_cmd.h
---- linux-4.9.217/include/linux/vserver/tag_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/tag_cmd.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,10 @@
-+#ifndef _VSERVER_TAG_CMD_H
-+#define _VSERVER_TAG_CMD_H
++#include <linux/types.h>
+
-+#include <uapi/vserver/tag_cmd.h>
++struct vx_dmap_target {
++ dev_t target;
++ uint32_t flags;
++};
+
-+extern int vc_task_tag(uint32_t);
++struct _vx_device {
++#ifdef CONFIG_VSERVER_DEVICE
++ struct vx_dmap_target targets[2];
++#endif
++};
+
-+extern int vc_tag_migrate(uint32_t);
++#endif /* _VSERVER_DEVICE_DEF_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/dlimit.h linux-4.9/include/linux/vserver/dlimit.h
+--- linux-4.9/include/linux/vserver/dlimit.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/dlimit.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,54 @@
++#ifndef _VSERVER_DLIMIT_H
++#define _VSERVER_DLIMIT_H
+
-+#endif /* _VSERVER_TAG_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vserver/tag.h linux-4.9.217-vs2.3.9.12/include/linux/vserver/tag.h
---- linux-4.9.217/include/linux/vserver/tag.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vserver/tag.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,160 @@
-+#ifndef _DX_TAG_H
-+#define _DX_TAG_H
++#include "switch.h"
+
-+#include <linux/types.h>
-+#include <linux/uidgid.h>
+
++#ifdef __KERNEL__
+
-+#define DX_TAG(in) (IS_TAGGED(in))
++/* keep in sync with CDLIM_INFINITY */
+
++#define DLIM_INFINITY (~0ULL)
+
-+#ifdef CONFIG_TAG_NFSD
-+#define DX_TAG_NFSD 1
-+#else
-+#define DX_TAG_NFSD 0
-+#endif
++#include <linux/spinlock.h>
++#include <linux/rcupdate.h>
+
++struct super_block;
+
-+#ifdef CONFIG_TAGGING_NONE
++struct dl_info {
++ struct hlist_node dl_hlist; /* linked list of contexts */
++ struct rcu_head dl_rcu; /* the rcu head */
++ vtag_t dl_tag; /* context tag */
++ atomic_t dl_usecnt; /* usage count */
++ atomic_t dl_refcnt; /* reference count */
+
-+#define MAX_UID 0xFFFFFFFF
-+#define MAX_GID 0xFFFFFFFF
++ struct super_block *dl_sb; /* associated superblock */
+
-+#define INOTAG_TAG(cond, uid, gid, tag) (0)
++ spinlock_t dl_lock; /* protect the values */
+
-+#define TAGINO_UID(cond, uid, tag) (uid)
-+#define TAGINO_GID(cond, gid, tag) (gid)
++ unsigned long long dl_space_used; /* used space in bytes */
++ unsigned long long dl_space_total; /* maximum space in bytes */
++ unsigned long dl_inodes_used; /* used inodes */
++ unsigned long dl_inodes_total; /* maximum inodes */
+
-+#endif
++ unsigned int dl_nrlmult; /* non root limit mult */
++};
+
++struct rcu_head;
+
-+#ifdef CONFIG_TAGGING_GID16
++extern void rcu_free_dl_info(struct rcu_head *);
++extern void unhash_dl_info(struct dl_info *);
+
-+#define MAX_UID 0xFFFFFFFF
-+#define MAX_GID 0x0000FFFF
++extern struct dl_info *locate_dl_info(struct super_block *, vtag_t);
+
-+#define INOTAG_TAG(cond, uid, gid, tag) \
-+ ((cond) ? (((gid) >> 16) & 0xFFFF) : 0)
+
-+#define TAGINO_UID(cond, uid, tag) (uid)
-+#define TAGINO_GID(cond, gid, tag) \
-+ ((cond) ? (((gid) & 0xFFFF) | ((tag) << 16)) : (gid))
++struct kstatfs;
+
-+#endif
++extern void vx_vsi_statfs(struct super_block *, struct kstatfs *);
+
++typedef uint64_t dlsize_t;
+
-+#ifdef CONFIG_TAGGING_ID24
++#endif /* __KERNEL__ */
++#else /* _VSERVER_DLIMIT_H */
++#warning duplicate inclusion
++#endif /* _VSERVER_DLIMIT_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/dlimit_cmd.h linux-4.9/include/linux/vserver/dlimit_cmd.h
+--- linux-4.9/include/linux/vserver/dlimit_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/dlimit_cmd.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,46 @@
++#ifndef _VSERVER_DLIMIT_CMD_H
++#define _VSERVER_DLIMIT_CMD_H
+
-+#define MAX_UID 0x00FFFFFF
-+#define MAX_GID 0x00FFFFFF
++#include <uapi/vserver/dlimit_cmd.h>
+
-+#define INOTAG_TAG(cond, uid, gid, tag) \
-+ ((cond) ? ((((uid) >> 16) & 0xFF00) | (((gid) >> 24) & 0xFF)) : 0)
+
-+#define TAGINO_UID(cond, uid, tag) \
-+ ((cond) ? (((uid) & 0xFFFFFF) | (((tag) & 0xFF00) << 16)) : (uid))
-+#define TAGINO_GID(cond, gid, tag) \
-+ ((cond) ? (((gid) & 0xFFFFFF) | (((tag) & 0x00FF) << 24)) : (gid))
++#ifdef CONFIG_COMPAT
+
-+#endif
++#include <asm/compat.h>
+
++struct vcmd_ctx_dlimit_base_v0_x32 {
++ compat_uptr_t name_ptr;
++ uint32_t flags;
++};
+
-+#ifdef CONFIG_TAGGING_UID16
++struct vcmd_ctx_dlimit_v0_x32 {
++ compat_uptr_t name_ptr;
++ uint32_t space_used; /* used space in kbytes */
++ uint32_t space_total; /* maximum space in kbytes */
++ uint32_t inodes_used; /* used inodes */
++ uint32_t inodes_total; /* maximum inodes */
++ uint32_t reserved; /* reserved for root in % */
++ uint32_t flags;
++};
+
-+#define MAX_UID 0x0000FFFF
-+#define MAX_GID 0xFFFFFFFF
++#endif /* CONFIG_COMPAT */
+
-+#define INOTAG_TAG(cond, uid, gid, tag) \
-+ ((cond) ? (((uid) >> 16) & 0xFFFF) : 0)
++#include <linux/compiler.h>
+
-+#define TAGINO_UID(cond, uid, tag) \
-+ ((cond) ? (((uid) & 0xFFFF) | ((tag) << 16)) : (uid))
-+#define TAGINO_GID(cond, gid, tag) (gid)
++extern int vc_add_dlimit(uint32_t, void __user *);
++extern int vc_rem_dlimit(uint32_t, void __user *);
+
-+#endif
++extern int vc_set_dlimit(uint32_t, void __user *);
++extern int vc_get_dlimit(uint32_t, void __user *);
+
++#ifdef CONFIG_COMPAT
+
-+#ifdef CONFIG_TAGGING_INTERN
++extern int vc_add_dlimit_x32(uint32_t, void __user *);
++extern int vc_rem_dlimit_x32(uint32_t, void __user *);
+
-+#define MAX_UID 0xFFFFFFFF
-+#define MAX_GID 0xFFFFFFFF
++extern int vc_set_dlimit_x32(uint32_t, void __user *);
++extern int vc_get_dlimit_x32(uint32_t, void __user *);
+
-+#define INOTAG_TAG(cond, uid, gid, tag) \
-+ ((cond) ? (tag) : 0)
++#endif /* CONFIG_COMPAT */
+
-+#define TAGINO_UID(cond, uid, tag) (uid)
-+#define TAGINO_GID(cond, gid, tag) (gid)
++#endif /* _VSERVER_DLIMIT_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/global.h linux-4.9/include/linux/vserver/global.h
+--- linux-4.9/include/linux/vserver/global.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/global.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,20 @@
++#ifndef _VSERVER_GLOBAL_H
++#define _VSERVER_GLOBAL_H
+
-+#endif
+
++extern atomic_t vx_global_ctotal;
++extern atomic_t vx_global_cactive;
+
-+#ifndef CONFIG_TAGGING_NONE
-+#define dx_current_fstag(sb) \
-+ ((sb)->s_flags & MS_TAGGED ? dx_current_tag() : 0)
-+#else
-+#define dx_current_fstag(sb) (0)
-+#endif
++extern atomic_t nx_global_ctotal;
++extern atomic_t nx_global_cactive;
+
-+#ifndef CONFIG_TAGGING_INTERN
-+#define TAGINO_TAG(cond, tag) (0)
-+#else
-+#define TAGINO_TAG(cond, tag) ((cond) ? (tag) : 0)
-+#endif
++extern atomic_t vs_global_nsproxy;
++extern atomic_t vs_global_fs;
++extern atomic_t vs_global_mnt_ns;
++extern atomic_t vs_global_uts_ns;
++extern atomic_t vs_global_ipc_ns;
++extern atomic_t vs_global_user_ns;
++extern atomic_t vs_global_pid_ns;
+
-+#define TAGINO_KUID(cond, kuid, ktag) \
-+ KUIDT_INIT(TAGINO_UID(cond, __kuid_val(kuid), __ktag_val(ktag)))
-+#define TAGINO_KGID(cond, kgid, ktag) \
-+ KGIDT_INIT(TAGINO_GID(cond, __kgid_val(kgid), __ktag_val(ktag)))
-+#define TAGINO_KTAG(cond, ktag) \
-+ KTAGT_INIT(TAGINO_TAG(cond, __ktag_val(ktag)))
+
++#endif /* _VSERVER_GLOBAL_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/history.h linux-4.9/include/linux/vserver/history.h
+--- linux-4.9/include/linux/vserver/history.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/history.h 2021-02-24 15:47:45.094409658 +0100
+@@ -0,0 +1,197 @@
++#ifndef _VSERVER_HISTORY_H
++#define _VSERVER_HISTORY_H
+
-+#define INOTAG_UID(cond, uid, gid) \
-+ ((cond) ? ((uid) & MAX_UID) : (uid))
-+#define INOTAG_GID(cond, uid, gid) \
-+ ((cond) ? ((gid) & MAX_GID) : (gid))
+
-+#define INOTAG_KUID(cond, kuid, kgid) \
-+ KUIDT_INIT(INOTAG_UID(cond, __kuid_val(kuid), __kgid_val(kgid)))
-+#define INOTAG_KGID(cond, kuid, kgid) \
-+ KGIDT_INIT(INOTAG_GID(cond, __kuid_val(kuid), __kgid_val(kgid)))
-+#define INOTAG_KTAG(cond, kuid, kgid, ktag) \
-+ KTAGT_INIT(INOTAG_TAG(cond, \
-+ __kuid_val(kuid), __kgid_val(kgid), __ktag_val(ktag)))
++enum {
++ VXH_UNUSED = 0,
++ VXH_THROW_OOPS = 1,
+
++ VXH_GET_VX_INFO,
++ VXH_PUT_VX_INFO,
++ VXH_INIT_VX_INFO,
++ VXH_SET_VX_INFO,
++ VXH_CLR_VX_INFO,
++ VXH_CLAIM_VX_INFO,
++ VXH_RELEASE_VX_INFO,
++ VXH_ALLOC_VX_INFO,
++ VXH_DEALLOC_VX_INFO,
++ VXH_HASH_VX_INFO,
++ VXH_UNHASH_VX_INFO,
++ VXH_LOC_VX_INFO,
++ VXH_LOOKUP_VX_INFO,
++ VXH_CREATE_VX_INFO,
++};
+
-+static inline uid_t dx_map_uid(uid_t uid)
-+{
-+ if ((uid > MAX_UID) && (uid != -1))
-+ uid = -2;
-+ return (uid & MAX_UID);
-+}
++struct _vxhe_vxi {
++ struct vx_info *ptr;
++ unsigned xid;
++ unsigned usecnt;
++ unsigned tasks;
++};
+
-+static inline gid_t dx_map_gid(gid_t gid)
-+{
-+ if ((gid > MAX_GID) && (gid != -1))
-+ gid = -2;
-+ return (gid & MAX_GID);
-+}
++struct _vxhe_set_clr {
++ void *data;
++};
+
-+struct peer_tag {
-+ int32_t xid;
-+ int32_t nid;
++struct _vxhe_loc_lookup {
++ unsigned arg;
+};
+
-+#define dx_notagcheck(sb) ((sb) && ((sb)->s_flags & MS_NOTAGCHECK))
++struct _vx_hist_entry {
++ void *loc;
++ unsigned short seq;
++ unsigned short type;
++ struct _vxhe_vxi vxi;
++ union {
++ struct _vxhe_set_clr sc;
++ struct _vxhe_loc_lookup ll;
++ };
++};
+
-+int dx_parse_tag(char *string, vtag_t *tag, int remove, int *mnt_flags,
-+ unsigned long *flags);
++#ifdef CONFIG_VSERVER_HISTORY
+
-+#ifdef CONFIG_PROPAGATE
++extern unsigned volatile int vxh_active;
+
-+void __dx_propagate_tag(struct nameidata *nd, struct inode *inode);
++struct _vx_hist_entry *vxh_advance(void *loc);
+
-+#define dx_propagate_tag(n, i) __dx_propagate_tag(n, i)
+
-+#else
-+#define dx_propagate_tag(n, i) do { } while (0)
-+#endif
++static inline
++void __vxh_copy_vxi(struct _vx_hist_entry *entry, struct vx_info *vxi)
++{
++ entry->vxi.ptr = vxi;
++ if (vxi) {
++ entry->vxi.usecnt = atomic_read(&vxi->vx_usecnt);
++ entry->vxi.tasks = atomic_read(&vxi->vx_tasks);
++ entry->vxi.xid = vxi->vx_id;
++ }
++}
+
-+#endif /* _DX_TAG_H */
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_inet6.h linux-4.9.217-vs2.3.9.12/include/linux/vs_inet6.h
---- linux-4.9.217/include/linux/vs_inet6.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_inet6.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,264 @@
-+#ifndef _VS_INET6_H
-+#define _VS_INET6_H
+
-+#include "vserver/base.h"
-+#include "vserver/network.h"
-+#include "vserver/debug.h"
++#define __HERE__ current_text_addr()
+
-+#include <net/ipv6.h>
++#define __VXH_BODY(__type, __data, __here) \
++ struct _vx_hist_entry *entry; \
++ \
++ preempt_disable(); \
++ entry = vxh_advance(__here); \
++ __data; \
++ entry->type = __type; \
++ preempt_enable();
+
-+#define NXAV6(a) &(a)->ip, &(a)->mask, (a)->prefix, (a)->type
-+#define NXAV6_FMT "[%pI6/%pI6/%d:%04x]"
+
++ /* pass vxi only */
+
-+#ifdef CONFIG_IPV6
++#define __VXH_SMPL \
++ __vxh_copy_vxi(entry, vxi)
+
+static inline
-+int v6_addr_match(struct nx_addr_v6 *nxa,
-+ const struct in6_addr *addr, uint16_t mask)
++void __vxh_smpl(struct vx_info *vxi, int __type, void *__here)
+{
-+ int ret = 0;
-+
-+ switch (nxa->type & mask) {
-+ case NXA_TYPE_MASK:
-+ ret = ipv6_masked_addr_cmp(&nxa->ip, &nxa->mask, addr);
-+ break;
-+ case NXA_TYPE_ADDR:
-+ ret = ipv6_addr_equal(&nxa->ip, addr);
-+ break;
-+ case NXA_TYPE_ANY:
-+ ret = 1;
-+ break;
-+ }
-+ vxdprintk(VXD_CBIT(net, 0),
-+ "v6_addr_match(%p" NXAV6_FMT ",%pI6,%04x) = %d",
-+ nxa, NXAV6(nxa), addr, mask, ret);
-+ return ret;
++ __VXH_BODY(__type, __VXH_SMPL, __here)
+}
+
++ /* pass vxi and data (void *) */
++
++#define __VXH_DATA \
++ __vxh_copy_vxi(entry, vxi); \
++ entry->sc.data = data
++
+static inline
-+int v6_addr_in_nx_info(struct nx_info *nxi,
-+ const struct in6_addr *addr, uint16_t mask)
++void __vxh_data(struct vx_info *vxi, void *data,
++ int __type, void *__here)
+{
-+ struct nx_addr_v6 *nxa;
-+ unsigned long irqflags;
-+ int ret = 1;
++ __VXH_BODY(__type, __VXH_DATA, __here)
++}
+
-+ if (!nxi)
-+ goto out;
++ /* pass vxi and arg (long) */
+
-+ spin_lock_irqsave(&nxi->addr_lock, irqflags);
-+ for (nxa = &nxi->v6; nxa; nxa = nxa->next)
-+ if (v6_addr_match(nxa, addr, mask))
-+ goto out_unlock;
-+ ret = 0;
-+out_unlock:
-+ spin_unlock_irqrestore(&nxi->addr_lock, irqflags);
-+out:
-+ vxdprintk(VXD_CBIT(net, 0),
-+ "v6_addr_in_nx_info(%p[#%u],%pI6,%04x) = %d",
-+ nxi, nxi ? nxi->nx_id : 0, addr, mask, ret);
-+ return ret;
-+}
++#define __VXH_LONG \
++ __vxh_copy_vxi(entry, vxi); \
++ entry->ll.arg = arg
+
+static inline
-+int v6_nx_addr_match(struct nx_addr_v6 *nxa, struct nx_addr_v6 *addr, uint16_t mask)
++void __vxh_long(struct vx_info *vxi, long arg,
++ int __type, void *__here)
+{
-+ /* FIXME: needs full range checks */
-+ return v6_addr_match(nxa, &addr->ip, mask);
++ __VXH_BODY(__type, __VXH_LONG, __here)
+}
+
++
+static inline
-+int v6_nx_addr_in_nx_info(struct nx_info *nxi, struct nx_addr_v6 *nxa, uint16_t mask)
++void __vxh_throw_oops(void *__here)
+{
-+ struct nx_addr_v6 *ptr;
-+ unsigned long irqflags;
-+ int ret = 1;
-+
-+ spin_lock_irqsave(&nxi->addr_lock, irqflags);
-+ for (ptr = &nxi->v6; ptr; ptr = ptr->next)
-+ if (v6_nx_addr_match(ptr, nxa, mask))
-+ goto out_unlock;
-+ ret = 0;
-+out_unlock:
-+ spin_unlock_irqrestore(&nxi->addr_lock, irqflags);
-+ return ret;
++ __VXH_BODY(VXH_THROW_OOPS, {}, __here);
++ /* prevent further acquisition */
++ vxh_active = 0;
+}
+
+
-+/*
-+ * Check if a given address matches for a socket
-+ *
-+ * nxi: the socket's nx_info if any
-+ * addr: to be verified address
-+ */
-+static inline
-+int v6_sock_addr_match (
-+ struct nx_info *nxi,
-+ struct inet_sock *inet,
-+ struct in6_addr *addr)
-+{
-+ struct sock *sk = &inet->sk;
-+ const struct in6_addr *saddr = inet6_rcv_saddr(sk);
++#define vxh_throw_oops() __vxh_throw_oops(__HERE__);
+
-+ if (!ipv6_addr_any(addr) &&
-+ ipv6_addr_equal(saddr, addr))
-+ return 1;
-+ if (ipv6_addr_any(saddr))
-+ return v6_addr_in_nx_info(nxi, addr, -1);
-+ return 0;
-+}
++#define __vxh_get_vx_info(v, h) __vxh_smpl(v, VXH_GET_VX_INFO, h);
++#define __vxh_put_vx_info(v, h) __vxh_smpl(v, VXH_PUT_VX_INFO, h);
+
-+/*
-+ * check if address is covered by socket
-+ *
-+ * sk: the socket to check against
-+ * addr: the address in question (must be != 0)
-+ */
++#define __vxh_init_vx_info(v, d, h) \
++ __vxh_data(v, d, VXH_INIT_VX_INFO, h);
++#define __vxh_set_vx_info(v, d, h) \
++ __vxh_data(v, d, VXH_SET_VX_INFO, h);
++#define __vxh_clr_vx_info(v, d, h) \
++ __vxh_data(v, d, VXH_CLR_VX_INFO, h);
+
-+static inline
-+int __v6_addr_match_socket(const struct sock *sk, struct nx_addr_v6 *nxa)
-+{
-+ struct nx_info *nxi = sk->sk_nx_info;
-+ const struct in6_addr *saddr = inet6_rcv_saddr(sk);
++#define __vxh_claim_vx_info(v, d, h) \
++ __vxh_data(v, d, VXH_CLAIM_VX_INFO, h);
++#define __vxh_release_vx_info(v, d, h) \
++ __vxh_data(v, d, VXH_RELEASE_VX_INFO, h);
+
-+ vxdprintk(VXD_CBIT(net, 5),
-+ "__v6_addr_in_socket(%p," NXAV6_FMT ") %p:%pI6 %p;%lx",
-+ sk, NXAV6(nxa), nxi, saddr, sk->sk_socket,
-+ (sk->sk_socket?sk->sk_socket->flags:0));
++#define vxh_alloc_vx_info(v) \
++ __vxh_smpl(v, VXH_ALLOC_VX_INFO, __HERE__);
++#define vxh_dealloc_vx_info(v) \
++ __vxh_smpl(v, VXH_DEALLOC_VX_INFO, __HERE__);
+
-+ if (!ipv6_addr_any(saddr)) { /* direct address match */
-+ return v6_addr_match(nxa, saddr, -1);
-+ } else if (nxi) { /* match against nx_info */
-+ return v6_nx_addr_in_nx_info(nxi, nxa, -1);
-+ } else { /* unrestricted any socket */
-+ return 1;
-+ }
-+}
++#define vxh_hash_vx_info(v) \
++ __vxh_smpl(v, VXH_HASH_VX_INFO, __HERE__);
++#define vxh_unhash_vx_info(v) \
++ __vxh_smpl(v, VXH_UNHASH_VX_INFO, __HERE__);
+
++#define vxh_loc_vx_info(v, l) \
++ __vxh_long(v, l, VXH_LOC_VX_INFO, __HERE__);
++#define vxh_lookup_vx_info(v, l) \
++ __vxh_long(v, l, VXH_LOOKUP_VX_INFO, __HERE__);
++#define vxh_create_vx_info(v, l) \
++ __vxh_long(v, l, VXH_CREATE_VX_INFO, __HERE__);
+
-+/* inet related checks and helpers */
++extern void vxh_dump_history(void);
+
+
-+struct in_ifaddr;
-+struct net_device;
-+struct sock;
++#else /* CONFIG_VSERVER_HISTORY */
+
++#define __HERE__ 0
+
-+#include <linux/netdevice.h>
-+#include <linux/inetdevice.h>
-+#include <net/inet_timewait_sock.h>
++#define vxh_throw_oops() do { } while (0)
+
++#define __vxh_get_vx_info(v, h) do { } while (0)
++#define __vxh_put_vx_info(v, h) do { } while (0)
+
-+int dev_in_nx_info(struct net_device *, struct nx_info *);
-+int v6_dev_in_nx_info(struct net_device *, struct nx_info *);
-+int nx_v6_addr_conflict(struct nx_info *, struct nx_info *);
++#define __vxh_init_vx_info(v, d, h) do { } while (0)
++#define __vxh_set_vx_info(v, d, h) do { } while (0)
++#define __vxh_clr_vx_info(v, d, h) do { } while (0)
+
++#define __vxh_claim_vx_info(v, d, h) do { } while (0)
++#define __vxh_release_vx_info(v, d, h) do { } while (0)
+
++#define vxh_alloc_vx_info(v) do { } while (0)
++#define vxh_dealloc_vx_info(v) do { } while (0)
+
-+static inline
-+int v6_ifa_in_nx_info(struct inet6_ifaddr *ifa, struct nx_info *nxi)
-+{
-+ if (!nxi)
-+ return 1;
-+ if (!ifa)
-+ return 0;
-+ return v6_addr_in_nx_info(nxi, &ifa->addr, -1);
-+}
++#define vxh_hash_vx_info(v) do { } while (0)
++#define vxh_unhash_vx_info(v) do { } while (0)
+
-+static inline
-+int nx_v6_ifa_visible(struct nx_info *nxi, struct inet6_ifaddr *ifa)
-+{
-+ vxdprintk(VXD_CBIT(net, 1), "nx_v6_ifa_visible(%p[#%u],%p) %d",
-+ nxi, nxi ? nxi->nx_id : 0, ifa,
-+ nxi ? v6_ifa_in_nx_info(ifa, nxi) : 0);
++#define vxh_loc_vx_info(v, l) do { } while (0)
++#define vxh_lookup_vx_info(v, l) do { } while (0)
++#define vxh_create_vx_info(v, l) do { } while (0)
+
-+ if (!nx_info_flags(nxi, NXF_HIDE_NETIF, 0))
-+ return 1;
-+ if (v6_ifa_in_nx_info(ifa, nxi))
-+ return 1;
-+ return 0;
-+}
++#define vxh_dump_history() do { } while (0)
+
+
-+struct nx_v6_sock_addr {
-+ struct in6_addr saddr; /* Address used for validation */
-+ struct in6_addr baddr; /* Address used for socket bind */
++#endif /* CONFIG_VSERVER_HISTORY */
++
++#endif /* _VSERVER_HISTORY_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/inode.h linux-4.9/include/linux/vserver/inode.h
+--- linux-4.9/include/linux/vserver/inode.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/inode.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,19 @@
++#ifndef _VSERVER_INODE_H
++#define _VSERVER_INODE_H
++
++#include <uapi/vserver/inode.h>
++
++
++#ifdef CONFIG_VSERVER_PROC_SECURE
++#define IATTR_PROC_DEFAULT ( IATTR_ADMIN | IATTR_HIDE )
++#define IATTR_PROC_SYMLINK ( IATTR_ADMIN )
++#else
++#define IATTR_PROC_DEFAULT ( IATTR_ADMIN )
++#define IATTR_PROC_SYMLINK ( IATTR_ADMIN )
++#endif
++
++#define vx_hide_check(c, m) (((m) & IATTR_HIDE) ? vx_check(c, m) : 1)
++
++#else /* _VSERVER_INODE_H */
++#warning duplicate inclusion
++#endif /* _VSERVER_INODE_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/inode_cmd.h linux-4.9/include/linux/vserver/inode_cmd.h
+--- linux-4.9/include/linux/vserver/inode_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/inode_cmd.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,36 @@
++#ifndef _VSERVER_INODE_CMD_H
++#define _VSERVER_INODE_CMD_H
++
++#include <uapi/vserver/inode_cmd.h>
++
++
++
++#ifdef CONFIG_COMPAT
++
++#include <asm/compat.h>
++
++struct vcmd_ctx_iattr_v1_x32 {
++ compat_uptr_t name_ptr;
++ uint32_t tag;
++ uint32_t flags;
++ uint32_t mask;
+};
+
-+static inline
-+int v6_map_sock_addr(struct inet_sock *inet, struct sockaddr_in6 *addr,
-+ struct nx_v6_sock_addr *nsa)
-+{
-+ // struct sock *sk = &inet->sk;
-+ // struct nx_info *nxi = sk->sk_nx_info;
-+ struct in6_addr saddr = addr->sin6_addr;
-+ struct in6_addr baddr = saddr;
++#endif /* CONFIG_COMPAT */
+
-+ nsa->saddr = saddr;
-+ nsa->baddr = baddr;
-+ return 0;
-+}
++#include <linux/compiler.h>
+
-+static inline
-+void v6_set_sock_addr(struct inet_sock *inet, struct nx_v6_sock_addr *nsa)
-+{
-+ // struct sock *sk = &inet->sk;
-+ // struct in6_addr *saddr = inet6_rcv_saddr(sk);
++extern int vc_get_iattr(void __user *);
++extern int vc_set_iattr(void __user *);
+
-+ // *saddr = nsa->baddr;
-+ // inet->inet_saddr = nsa->baddr;
-+}
++extern int vc_fget_iattr(uint32_t, void __user *);
++extern int vc_fset_iattr(uint32_t, void __user *);
+
-+static inline
-+int nx_info_has_v6(struct nx_info *nxi)
-+{
-+ if (!nxi)
-+ return 1;
-+ if (NX_IPV6(nxi))
-+ return 1;
-+ return 0;
-+}
++#ifdef CONFIG_COMPAT
+
-+#else /* CONFIG_IPV6 */
++extern int vc_get_iattr_x32(void __user *);
++extern int vc_set_iattr_x32(void __user *);
+
-+static inline
-+int nx_v6_dev_visible(struct nx_info *n, struct net_device *d)
-+{
-+ return 1;
-+}
++#endif /* CONFIG_COMPAT */
+
++#endif /* _VSERVER_INODE_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/limit.h linux-4.9/include/linux/vserver/limit.h
+--- linux-4.9/include/linux/vserver/limit.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/limit.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,67 @@
++#ifndef _VSERVER_LIMIT_H
++#define _VSERVER_LIMIT_H
+
-+static inline
-+int nx_v6_addr_conflict(struct nx_info *n, uint32_t a, const struct sock *s)
-+{
-+ return 1;
-+}
++#include <uapi/vserver/limit.h>
+
-+static inline
-+int v6_ifa_in_nx_info(struct in_ifaddr *a, struct nx_info *n)
-+{
-+ return 1;
-+}
+
-+static inline
-+int nx_info_has_v6(struct nx_info *nxi)
-+{
-+ return 0;
-+}
++#define VLIM_NOCHECK ((1L << VLIMIT_DENTRY) | (1L << RLIMIT_RSS))
+
-+static inline
-+int v6_addr_in_nx_info(struct nx_info *nxi,
-+ const struct in6_addr *addr, uint16_t mask)
-+{
-+ return 0;
-+}
++/* keep in sync with CRLIM_INFINITY */
+
-+#endif /* CONFIG_IPV6 */
++#define VLIM_INFINITY (~0ULL)
+
-+#define current_nx_info_has_v6() \
-+ nx_info_has_v6(current_nx_info())
++#include <asm/atomic.h>
++#include <asm/resource.h>
+
-+#else
-+#warning duplicate inclusion
++#ifndef RLIM_INFINITY
++#warning RLIM_INFINITY is undefined
+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_inet.h linux-4.9.217-vs2.3.9.12/include/linux/vs_inet.h
---- linux-4.9.217/include/linux/vs_inet.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_inet.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,364 @@
-+#ifndef _VS_INET_H
-+#define _VS_INET_H
+
-+#include "vserver/base.h"
-+#include "vserver/network.h"
-+#include "vserver/debug.h"
++#define __rlim_val(l, r, v) ((l)->res[r].v)
+
-+#define IPI_LOOPBACK htonl(INADDR_LOOPBACK)
++#define __rlim_soft(l, r) __rlim_val(l, r, soft)
++#define __rlim_hard(l, r) __rlim_val(l, r, hard)
+
-+#define NXAV4(a) NIPQUAD((a)->ip[0]), NIPQUAD((a)->ip[1]), \
-+ NIPQUAD((a)->mask), (a)->type
-+#define NXAV4_FMT "[" NIPQUAD_FMT "-" NIPQUAD_FMT "/" NIPQUAD_FMT ":%04x]"
++#define __rlim_rcur(l, r) __rlim_val(l, r, rcur)
++#define __rlim_rmin(l, r) __rlim_val(l, r, rmin)
++#define __rlim_rmax(l, r) __rlim_val(l, r, rmax)
+
-+#define NIPQUAD(addr) \
-+ ((unsigned char *)&addr)[0], \
-+ ((unsigned char *)&addr)[1], \
-+ ((unsigned char *)&addr)[2], \
-+ ((unsigned char *)&addr)[3]
++#define __rlim_lhit(l, r) __rlim_val(l, r, lhit)
++#define __rlim_hit(l, r) atomic_inc(&__rlim_lhit(l, r))
+
-+#define NIPQUAD_FMT "%u.%u.%u.%u"
++typedef atomic_long_t rlim_atomic_t;
++typedef unsigned long rlim_t;
+
++#define __rlim_get(l, r) atomic_long_read(&__rlim_rcur(l, r))
++#define __rlim_set(l, r, v) atomic_long_set(&__rlim_rcur(l, r), v)
++#define __rlim_inc(l, r) atomic_long_inc(&__rlim_rcur(l, r))
++#define __rlim_dec(l, r) atomic_long_dec(&__rlim_rcur(l, r))
++#define __rlim_add(l, r, v) atomic_long_add(v, &__rlim_rcur(l, r))
++#define __rlim_sub(l, r, v) atomic_long_sub(v, &__rlim_rcur(l, r))
+
-+static inline
-+int v4_addr_match(struct nx_addr_v4 *nxa, __be32 addr, uint16_t tmask)
-+{
-+ __be32 ip = nxa->ip[0].s_addr;
-+ __be32 mask = nxa->mask.s_addr;
-+ __be32 bcast = ip | ~mask;
-+ int ret = 0;
+
-+ switch (nxa->type & tmask) {
-+ case NXA_TYPE_MASK:
-+ ret = (ip == (addr & mask));
-+ break;
-+ case NXA_TYPE_ADDR:
-+ ret = 3;
-+ if (addr == ip)
-+ break;
-+ /* fall through to broadcast */
-+ case NXA_MOD_BCAST:
-+ ret = ((tmask & NXA_MOD_BCAST) && (addr == bcast));
-+ break;
-+ case NXA_TYPE_RANGE:
-+ ret = ((nxa->ip[0].s_addr <= addr) &&
-+ (nxa->ip[1].s_addr > addr));
-+ break;
-+ case NXA_TYPE_ANY:
-+ ret = 2;
-+ break;
-+ }
++#if (RLIM_INFINITY == VLIM_INFINITY)
++#define VX_VLIM(r) ((long long)(long)(r))
++#define VX_RLIM(v) ((rlim_t)(v))
++#else
++#define VX_VLIM(r) (((r) == RLIM_INFINITY) \
++ ? VLIM_INFINITY : (long long)(r))
++#define VX_RLIM(v) (((v) == VLIM_INFINITY) \
++ ? RLIM_INFINITY : (rlim_t)(v))
++#endif
+
-+ vxdprintk(VXD_CBIT(net, 0),
-+ "v4_addr_match(%p" NXAV4_FMT "," NIPQUAD_FMT ",%04x) = %d",
-+ nxa, NXAV4(nxa), NIPQUAD(addr), tmask, ret);
-+ return ret;
-+}
++struct sysinfo;
+
-+static inline
-+int v4_addr_in_nx_info(struct nx_info *nxi, __be32 addr, uint16_t tmask)
-+{
-+ struct nx_addr_v4 *nxa;
-+ unsigned long irqflags;
-+ int ret = 1;
++#ifdef CONFIG_MEMCG
++void vx_vsi_meminfo(struct sysinfo *);
++void vx_vsi_swapinfo(struct sysinfo *);
++long vx_vsi_cached(struct sysinfo *);
++#else /* !CONFIG_MEMCG */
++#define vx_vsi_meminfo(s) do { } while (0)
++#define vx_vsi_swapinfo(s) do { } while (0)
++#define vx_vsi_cached(s) (0L)
++#endif /* !CONFIG_MEMCG */
+
-+ if (!nxi)
-+ goto out;
++#define NUM_LIMITS 24
+
-+ ret = 2;
-+ /* allow 127.0.0.1 when remapping lback */
-+ if ((tmask & NXA_LOOPBACK) &&
-+ (addr == IPI_LOOPBACK) &&
-+ nx_info_flags(nxi, NXF_LBACK_REMAP, 0))
-+ goto out;
-+ ret = 3;
-+ /* check for lback address */
-+ if ((tmask & NXA_MOD_LBACK) &&
-+ (nxi->v4_lback.s_addr == addr))
-+ goto out;
-+ ret = 4;
-+ /* check for broadcast address */
-+ if ((tmask & NXA_MOD_BCAST) &&
-+ (nxi->v4_bcast.s_addr == addr))
-+ goto out;
-+ ret = 5;
++#endif /* _VSERVER_LIMIT_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/limit_cmd.h linux-4.9/include/linux/vserver/limit_cmd.h
+--- linux-4.9/include/linux/vserver/limit_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/limit_cmd.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,35 @@
++#ifndef _VSERVER_LIMIT_CMD_H
++#define _VSERVER_LIMIT_CMD_H
+
-+ /* check for v4 addresses */
-+ spin_lock_irqsave(&nxi->addr_lock, irqflags);
-+ for (nxa = &nxi->v4; nxa; nxa = nxa->next)
-+ if (v4_addr_match(nxa, addr, tmask))
-+ goto out_unlock;
-+ ret = 0;
-+out_unlock:
-+ spin_unlock_irqrestore(&nxi->addr_lock, irqflags);
-+out:
-+ vxdprintk(VXD_CBIT(net, 0),
-+ "v4_addr_in_nx_info(%p[#%u]," NIPQUAD_FMT ",%04x) = %d",
-+ nxi, nxi ? nxi->nx_id : 0, NIPQUAD(addr), tmask, ret);
-+ return ret;
-+}
++#include <uapi/vserver/limit_cmd.h>
+
-+static inline
-+int v4_nx_addr_match(struct nx_addr_v4 *nxa, struct nx_addr_v4 *addr, uint16_t mask)
-+{
-+ /* FIXME: needs full range checks */
-+ return v4_addr_match(nxa, addr->ip[0].s_addr, mask);
-+}
+
-+static inline
-+int v4_nx_addr_in_nx_info(struct nx_info *nxi, struct nx_addr_v4 *nxa, uint16_t mask)
-+{
-+ struct nx_addr_v4 *ptr;
-+ unsigned long irqflags;
-+ int ret = 1;
++#ifdef CONFIG_IA32_EMULATION
+
-+ spin_lock_irqsave(&nxi->addr_lock, irqflags);
-+ for (ptr = &nxi->v4; ptr; ptr = ptr->next)
-+ if (v4_nx_addr_match(ptr, nxa, mask))
-+ goto out_unlock;
-+ ret = 0;
-+out_unlock:
-+ spin_unlock_irqrestore(&nxi->addr_lock, irqflags);
-+ return ret;
-+}
++struct vcmd_ctx_rlimit_v0_x32 {
++ uint32_t id;
++ uint64_t minimum;
++ uint64_t softlimit;
++ uint64_t maximum;
++} __attribute__ ((packed));
+
-+#include <net/inet_sock.h>
++#endif /* CONFIG_IA32_EMULATION */
+
-+/*
-+ * Check if a given address matches for a socket
-+ *
-+ * nxi: the socket's nx_info if any
-+ * addr: to be verified address
-+ */
-+static inline
-+int v4_sock_addr_match (
-+ struct nx_info *nxi,
-+ struct inet_sock *inet,
-+ __be32 addr)
-+{
-+ __be32 saddr = inet->inet_rcv_saddr;
-+ __be32 bcast = nxi ? nxi->v4_bcast.s_addr : INADDR_BROADCAST;
++#include <linux/compiler.h>
+
-+ if (addr && (saddr == addr || bcast == addr))
-+ return 1;
-+ if (!saddr)
-+ return v4_addr_in_nx_info(nxi, addr, NXA_MASK_BIND);
-+ return 0;
-+}
++extern int vc_get_rlimit_mask(uint32_t, void __user *);
++extern int vc_get_rlimit(struct vx_info *, void __user *);
++extern int vc_set_rlimit(struct vx_info *, void __user *);
++extern int vc_reset_hits(struct vx_info *, void __user *);
++extern int vc_reset_minmax(struct vx_info *, void __user *);
+
++extern int vc_rlimit_stat(struct vx_info *, void __user *);
+
-+/* inet related checks and helpers */
++#ifdef CONFIG_IA32_EMULATION
+
++extern int vc_get_rlimit_x32(struct vx_info *, void __user *);
++extern int vc_set_rlimit_x32(struct vx_info *, void __user *);
+
-+struct in_ifaddr;
-+struct net_device;
-+struct sock;
++#endif /* CONFIG_IA32_EMULATION */
+
-+#ifdef CONFIG_INET
++#endif /* _VSERVER_LIMIT_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/limit_def.h linux-4.9/include/linux/vserver/limit_def.h
+--- linux-4.9/include/linux/vserver/limit_def.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/limit_def.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,47 @@
++#ifndef _VSERVER_LIMIT_DEF_H
++#define _VSERVER_LIMIT_DEF_H
+
-+#include <linux/netdevice.h>
-+#include <linux/inetdevice.h>
-+#include <net/inet_sock.h>
-+#include <net/inet_timewait_sock.h>
++#include <asm/atomic.h>
++#include <asm/resource.h>
+
++#include "limit.h"
+
-+int dev_in_nx_info(struct net_device *, struct nx_info *);
-+int v4_dev_in_nx_info(struct net_device *, struct nx_info *);
-+int nx_v4_addr_conflict(struct nx_info *, struct nx_info *);
+
++struct _vx_res_limit {
++ rlim_t soft; /* Context soft limit */
++ rlim_t hard; /* Context hard limit */
+
-+/*
-+ * check if address is covered by socket
-+ *
-+ * sk: the socket to check against
-+ * addr: the address in question (must be != 0)
-+ */
++ rlim_atomic_t rcur; /* Current value */
++ rlim_t rmin; /* Context minimum */
++ rlim_t rmax; /* Context maximum */
+
-+static inline
-+int __v4_addr_match_socket(const struct sock *sk, struct nx_addr_v4 *nxa)
-+{
-+ struct nx_info *nxi = sk->sk_nx_info;
-+ __be32 saddr = sk->sk_rcv_saddr;
++ atomic_t lhit; /* Limit hits */
++};
+
-+ vxdprintk(VXD_CBIT(net, 5),
-+ "__v4_addr_in_socket(%p," NXAV4_FMT ") %p:" NIPQUAD_FMT " %p;%lx",
-+ sk, NXAV4(nxa), nxi, NIPQUAD(saddr), sk->sk_socket,
-+ (sk->sk_socket?sk->sk_socket->flags:0));
++/* context sub struct */
+
-+ if (saddr) { /* direct address match */
-+ return v4_addr_match(nxa, saddr, -1);
-+ } else if (nxi) { /* match against nx_info */
-+ return v4_nx_addr_in_nx_info(nxi, nxa, -1);
-+ } else { /* unrestricted any socket */
-+ return 1;
++struct _vx_limit {
++ struct _vx_res_limit res[NUM_LIMITS];
++};
++
++#ifdef CONFIG_VSERVER_DEBUG
++
++static inline void __dump_vx_limit(struct _vx_limit *limit)
++{
++ int i;
++
++ printk("\t_vx_limit:");
++ for (i = 0; i < NUM_LIMITS; i++) {
++ printk("\t [%2d] = %8lu %8lu/%8lu, %8ld/%8ld, %8d\n",
++ i, (unsigned long)__rlim_get(limit, i),
++ (unsigned long)__rlim_rmin(limit, i),
++ (unsigned long)__rlim_rmax(limit, i),
++ (long)__rlim_soft(limit, i),
++ (long)__rlim_hard(limit, i),
++ atomic_read(&__rlim_lhit(limit, i)));
+ }
+}
+
++#endif
++
++#endif /* _VSERVER_LIMIT_DEF_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/limit_int.h linux-4.9/include/linux/vserver/limit_int.h
+--- linux-4.9/include/linux/vserver/limit_int.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/limit_int.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,193 @@
++#ifndef _VSERVER_LIMIT_INT_H
++#define _VSERVER_LIMIT_INT_H
++
++#define VXD_RCRES_COND(r) VXD_CBIT(cres, r)
++#define VXD_RLIMIT_COND(r) VXD_CBIT(limit, r)
+
++extern const char *vlimit_name[NUM_LIMITS];
+
-+static inline
-+int nx_dev_visible(struct nx_info *nxi, struct net_device *dev)
++static inline void __vx_acc_cres(struct vx_info *vxi,
++ int res, int dir, void *_data, char *_file, int _line)
+{
-+ vxdprintk(VXD_CBIT(net, 1),
-+ "nx_dev_visible(%p[#%u],%p " VS_Q("%s") ") %d",
-+ nxi, nxi ? nxi->nx_id : 0, dev, dev->name,
-+ nxi ? dev_in_nx_info(dev, nxi) : 0);
++ if (VXD_RCRES_COND(res))
++ vxlprintk(1, "vx_acc_cres[%5d,%s,%2d]: %5ld%s (%p)",
++ (vxi ? vxi->vx_id : -1), vlimit_name[res], res,
++ (vxi ? (long)__rlim_get(&vxi->limit, res) : 0),
++ (dir > 0) ? "++" : "--", _data, _file, _line);
++ if (!vxi)
++ return;
+
-+ if (!nx_info_flags(nxi, NXF_HIDE_NETIF, 0))
-+ return 1;
-+ if (dev_in_nx_info(dev, nxi))
-+ return 1;
-+ return 0;
++ if (dir > 0)
++ __rlim_inc(&vxi->limit, res);
++ else
++ __rlim_dec(&vxi->limit, res);
+}
+
-+
-+static inline
-+int v4_ifa_in_nx_info(struct in_ifaddr *ifa, struct nx_info *nxi)
++static inline void __vx_add_cres(struct vx_info *vxi,
++ int res, int amount, void *_data, char *_file, int _line)
+{
-+ if (!nxi)
-+ return 1;
-+ if (!ifa)
-+ return 0;
-+ return v4_addr_in_nx_info(nxi, ifa->ifa_local, NXA_MASK_SHOW);
++ if (VXD_RCRES_COND(res))
++ vxlprintk(1, "vx_add_cres[%5d,%s,%2d]: %5ld += %5d (%p)",
++ (vxi ? vxi->vx_id : -1), vlimit_name[res], res,
++ (vxi ? (long)__rlim_get(&vxi->limit, res) : 0),
++ amount, _data, _file, _line);
++ if (amount == 0)
++ return;
++ if (!vxi)
++ return;
++ __rlim_add(&vxi->limit, res, amount);
+}
+
+static inline
-+int nx_v4_ifa_visible(struct nx_info *nxi, struct in_ifaddr *ifa)
++int __vx_cres_adjust_max(struct _vx_limit *limit, int res, rlim_t value)
+{
-+ vxdprintk(VXD_CBIT(net, 1), "nx_v4_ifa_visible(%p[#%u],%p) %d",
-+ nxi, nxi ? nxi->nx_id : 0, ifa,
-+ nxi ? v4_ifa_in_nx_info(ifa, nxi) : 0);
++ int cond = (value > __rlim_rmax(limit, res));
+
-+ if (!nx_info_flags(nxi, NXF_HIDE_NETIF, 0))
-+ return 1;
-+ if (v4_ifa_in_nx_info(ifa, nxi))
-+ return 1;
-+ return 0;
++ if (cond)
++ __rlim_rmax(limit, res) = value;
++ return cond;
+}
+
-+
-+struct nx_v4_sock_addr {
-+ __be32 saddr; /* Address used for validation */
-+ __be32 baddr; /* Address used for socket bind */
-+};
-+
+static inline
-+int v4_map_sock_addr(struct inet_sock *inet, struct sockaddr_in *addr,
-+ struct nx_v4_sock_addr *nsa)
++int __vx_cres_adjust_min(struct _vx_limit *limit, int res, rlim_t value)
+{
-+ struct sock *sk = &inet->sk;
-+ struct nx_info *nxi = sk->sk_nx_info;
-+ __be32 saddr = addr->sin_addr.s_addr;
-+ __be32 baddr = saddr;
-+
-+ vxdprintk(VXD_CBIT(net, 3),
-+ "inet_bind(%p)* %p,%p;%lx " NIPQUAD_FMT,
-+ sk, sk->sk_nx_info, sk->sk_socket,
-+ (sk->sk_socket ? sk->sk_socket->flags : 0),
-+ NIPQUAD(saddr));
-+
-+ if (nxi) {
-+ if (saddr == INADDR_ANY) {
-+ if (nx_info_flags(nxi, NXF_SINGLE_IP, 0))
-+ baddr = nxi->v4.ip[0].s_addr;
-+ } else if (saddr == IPI_LOOPBACK) {
-+ if (nx_info_flags(nxi, NXF_LBACK_REMAP, 0))
-+ baddr = nxi->v4_lback.s_addr;
-+ } else if (!ipv4_is_multicast(saddr) ||
-+ !nx_info_ncaps(nxi, NXC_MULTICAST)) {
-+ /* normal address bind */
-+ if (!v4_addr_in_nx_info(nxi, saddr, NXA_MASK_BIND))
-+ return -EADDRNOTAVAIL;
-+ }
-+ }
-+
-+ vxdprintk(VXD_CBIT(net, 3),
-+ "inet_bind(%p) " NIPQUAD_FMT ", " NIPQUAD_FMT,
-+ sk, NIPQUAD(saddr), NIPQUAD(baddr));
++ int cond = (value < __rlim_rmin(limit, res));
+
-+ nsa->saddr = saddr;
-+ nsa->baddr = baddr;
-+ return 0;
++ if (cond)
++ __rlim_rmin(limit, res) = value;
++ return cond;
+}
+
+static inline
-+void v4_set_sock_addr(struct inet_sock *inet, struct nx_v4_sock_addr *nsa)
++void __vx_cres_fixup(struct _vx_limit *limit, int res, rlim_t value)
+{
-+ inet->inet_saddr = nsa->baddr;
-+ inet->inet_rcv_saddr = nsa->baddr;
++ if (!__vx_cres_adjust_max(limit, res, value))
++ __vx_cres_adjust_min(limit, res, value);
+}
+
+
-+/*
-+ * helper to simplify inet_lookup_listener
-+ *
-+ * nxi: the socket's nx_info if any
-+ * addr: to be verified address
-+ * saddr: socket address
-+ */
-+static inline int v4_inet_addr_match (
-+ struct nx_info *nxi,
-+ __be32 addr,
-+ __be32 saddr)
++/* return values:
++ +1 ... no limit hit
++ -1 ... over soft limit
++ 0 ... over hard limit */
++
++static inline int __vx_cres_avail(struct vx_info *vxi,
++ int res, int num, char *_file, int _line)
+{
-+ if (addr && (saddr == addr))
++ struct _vx_limit *limit;
++ rlim_t value;
++
++ if (VXD_RLIMIT_COND(res))
++ vxlprintk(1, "vx_cres_avail[%5d,%s,%2d]: %5ld/%5ld > %5ld + %5d",
++ (vxi ? vxi->vx_id : -1), vlimit_name[res], res,
++ (vxi ? (long)__rlim_soft(&vxi->limit, res) : -1),
++ (vxi ? (long)__rlim_hard(&vxi->limit, res) : -1),
++ (vxi ? (long)__rlim_get(&vxi->limit, res) : 0),
++ num, _file, _line);
++ if (!vxi)
+ return 1;
-+ if (!saddr)
-+ return nxi ? v4_addr_in_nx_info(nxi, addr, NXA_MASK_BIND) : 1;
-+ return 0;
-+}
+
-+static inline __be32 nx_map_sock_lback(struct nx_info *nxi, __be32 addr)
-+{
-+ if (nx_info_flags(nxi, NXF_HIDE_LBACK, 0) &&
-+ (addr == nxi->v4_lback.s_addr))
-+ return IPI_LOOPBACK;
-+ return addr;
-+}
++ limit = &vxi->limit;
++ value = __rlim_get(limit, res);
+
-+static inline
-+int nx_info_has_v4(struct nx_info *nxi)
-+{
-+ if (!nxi)
++ if (!__vx_cres_adjust_max(limit, res, value))
++ __vx_cres_adjust_min(limit, res, value);
++
++ if (num == 0)
+ return 1;
-+ if (NX_IPV4(nxi))
++
++ if (__rlim_soft(limit, res) == RLIM_INFINITY)
++ return -1;
++ if (value + num <= __rlim_soft(limit, res))
++ return -1;
++
++ if (__rlim_hard(limit, res) == RLIM_INFINITY)
+ return 1;
-+ if (nx_info_flags(nxi, NXF_LBACK_REMAP, 0))
++ if (value + num <= __rlim_hard(limit, res))
+ return 1;
++
++ __rlim_hit(limit, res);
+ return 0;
+}
+
-+#else /* CONFIG_INET */
++
++static const int VLA_RSS[] = { RLIMIT_RSS, VLIMIT_ANON, VLIMIT_MAPPED, 0 };
+
+static inline
-+int nx_dev_visible(struct nx_info *n, struct net_device *d)
++rlim_t __vx_cres_array_sum(struct _vx_limit *limit, const int *array)
+{
-+ return 1;
++ rlim_t value, sum = 0;
++ int res;
++
++ while ((res = *array++)) {
++ value = __rlim_get(limit, res);
++ __vx_cres_fixup(limit, res, value);
++ sum += value;
++ }
++ return sum;
+}
+
+static inline
-+int nx_v4_addr_conflict(struct nx_info *n, uint32_t a, const struct sock *s)
++rlim_t __vx_cres_array_fixup(struct _vx_limit *limit, const int *array)
+{
-+ return 1;
++ rlim_t value = __vx_cres_array_sum(limit, array + 1);
++ int res = *array;
++
++ if (value == __rlim_get(limit, res))
++ return value;
++
++ __rlim_set(limit, res, value);
++ /* now adjust min/max */
++ if (!__vx_cres_adjust_max(limit, res, value))
++ __vx_cres_adjust_min(limit, res, value);
++
++ return value;
+}
+
-+static inline
-+int v4_ifa_in_nx_info(struct in_ifaddr *a, struct nx_info *n)
++static inline int __vx_cres_array_avail(struct vx_info *vxi,
++ const int *array, int num, char *_file, int _line)
+{
-+ return 1;
++ struct _vx_limit *limit;
++ rlim_t value = 0;
++ int res;
++
++ if (num == 0)
++ return 1;
++ if (!vxi)
++ return 1;
++
++ limit = &vxi->limit;
++ res = *array;
++ value = __vx_cres_array_sum(limit, array + 1);
++
++ __rlim_set(limit, res, value);
++ __vx_cres_fixup(limit, res, value);
++
++ return __vx_cres_avail(vxi, res, num, _file, _line);
+}
+
-+static inline
-+int nx_info_has_v4(struct nx_info *nxi)
++
++static inline void vx_limit_fixup(struct _vx_limit *limit, int id)
+{
-+ return 0;
-+}
++ rlim_t value;
++ int res;
+
-+#endif /* CONFIG_INET */
++ /* complex resources first */
++ if ((id < 0) || (id == RLIMIT_RSS))
++ __vx_cres_array_fixup(limit, VLA_RSS);
+
-+#define current_nx_info_has_v4() \
-+ nx_info_has_v4(current_nx_info())
++ for (res = 0; res < NUM_LIMITS; res++) {
++ if ((id > 0) && (res != id))
++ continue;
+
-+#else
-+// #warning duplicate inclusion
-+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_limit.h linux-4.9.217-vs2.3.9.12/include/linux/vs_limit.h
---- linux-4.9.217/include/linux/vs_limit.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_limit.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,140 @@
-+#ifndef _VS_LIMIT_H
-+#define _VS_LIMIT_H
++ value = __rlim_get(limit, res);
++ __vx_cres_fixup(limit, res, value);
+
-+#include "vserver/limit.h"
-+#include "vserver/base.h"
-+#include "vserver/context.h"
-+#include "vserver/debug.h"
-+#include "vserver/context.h"
-+#include "vserver/limit_int.h"
++ /* not supposed to happen, maybe warn? */
++ if (__rlim_rmax(limit, res) > __rlim_hard(limit, res))
++ __rlim_rmax(limit, res) = __rlim_hard(limit, res);
++ }
++}
+
+
-+#define vx_acc_cres(v, d, p, r) \
-+ __vx_acc_cres(v, r, d, p, __FILE__, __LINE__)
++#endif /* _VSERVER_LIMIT_INT_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/monitor.h linux-4.9/include/linux/vserver/monitor.h
+--- linux-4.9/include/linux/vserver/monitor.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/monitor.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,6 @@
++#ifndef _VSERVER_MONITOR_H
++#define _VSERVER_MONITOR_H
+
-+#define vx_acc_cres_cond(x, d, p, r) \
-+ __vx_acc_cres(((x) == vx_current_xid()) ? current_vx_info() : 0, \
-+ r, d, p, __FILE__, __LINE__)
++#include <uapi/vserver/monitor.h>
++
++#endif /* _VSERVER_MONITOR_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/network.h linux-4.9/include/linux/vserver/network.h
+--- linux-4.9/include/linux/vserver/network.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/network.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,76 @@
++#ifndef _VSERVER_NETWORK_H
++#define _VSERVER_NETWORK_H
+
+
-+#define vx_add_cres(v, a, p, r) \
-+ __vx_add_cres(v, r, a, p, __FILE__, __LINE__)
-+#define vx_sub_cres(v, a, p, r) vx_add_cres(v, -(a), p, r)
++#include <linux/list.h>
++#include <linux/spinlock.h>
++#include <linux/rcupdate.h>
++#include <linux/in.h>
++#include <linux/in6.h>
++#include <asm/atomic.h>
++#include <uapi/vserver/network.h>
+
-+#define vx_add_cres_cond(x, a, p, r) \
-+ __vx_add_cres(((x) == vx_current_xid()) ? current_vx_info() : 0, \
-+ r, a, p, __FILE__, __LINE__)
-+#define vx_sub_cres_cond(x, a, p, r) vx_add_cres_cond(x, -(a), p, r)
++struct nx_addr_v4 {
++ struct nx_addr_v4 *next;
++ struct in_addr ip[2];
++ struct in_addr mask;
++ uint16_t type;
++ uint16_t flags;
++};
+
++struct nx_addr_v6 {
++ struct nx_addr_v6 *next;
++ struct in6_addr ip;
++ struct in6_addr mask;
++ uint32_t prefix;
++ uint16_t type;
++ uint16_t flags;
++};
+
-+/* process and file limits */
++struct nx_info {
++ struct hlist_node nx_hlist; /* linked list of nxinfos */
++ vnid_t nx_id; /* vnet id */
++ atomic_t nx_usecnt; /* usage count */
++ atomic_t nx_tasks; /* tasks count */
++ int nx_state; /* context state */
+
-+#define vx_nproc_inc(p) \
-+ vx_acc_cres((p)->vx_info, 1, p, RLIMIT_NPROC)
++ uint64_t nx_flags; /* network flag word */
++ uint64_t nx_ncaps; /* network capabilities */
+
-+#define vx_nproc_dec(p) \
-+ vx_acc_cres((p)->vx_info,-1, p, RLIMIT_NPROC)
++ spinlock_t addr_lock; /* protect address changes */
++ struct in_addr v4_lback; /* Loopback address */
++ struct in_addr v4_bcast; /* Broadcast address */
++ struct nx_addr_v4 v4; /* First/Single ipv4 address */
++#ifdef CONFIG_IPV6
++ struct nx_addr_v6 v6; /* First/Single ipv6 address */
++#endif
++ char nx_name[65]; /* network context name */
++};
+
-+#define vx_files_inc(f) \
-+ vx_acc_cres_cond((f)->f_xid, 1, f, RLIMIT_NOFILE)
+
-+#define vx_files_dec(f) \
-+ vx_acc_cres_cond((f)->f_xid,-1, f, RLIMIT_NOFILE)
++/* status flags */
+
-+#define vx_locks_inc(l) \
-+ vx_acc_cres_cond((l)->fl_xid, 1, l, RLIMIT_LOCKS)
++#define NXS_HASHED 0x0001
++#define NXS_SHUTDOWN 0x0100
++#define NXS_RELEASED 0x8000
+
-+#define vx_locks_dec(l) \
-+ vx_acc_cres_cond((l)->fl_xid,-1, l, RLIMIT_LOCKS)
++extern struct nx_info *lookup_nx_info(int);
+
-+#define vx_openfd_inc(f) \
-+ vx_acc_cres(current_vx_info(), 1, (void *)(long)(f), VLIMIT_OPENFD)
++extern int get_nid_list(int, unsigned int *, int);
++extern int nid_is_hashed(vnid_t);
+
-+#define vx_openfd_dec(f) \
-+ vx_acc_cres(current_vx_info(),-1, (void *)(long)(f), VLIMIT_OPENFD)
++extern int nx_migrate_task(struct task_struct *, struct nx_info *);
+
++extern long vs_net_change(struct nx_info *, unsigned int);
+
-+#define vx_cres_avail(v, n, r) \
-+ __vx_cres_avail(v, r, n, __FILE__, __LINE__)
++struct sock;
+
+
-+#define vx_nproc_avail(n) \
-+ vx_cres_avail(current_vx_info(), n, RLIMIT_NPROC)
++#define NX_IPV4(n) ((n)->v4.type != NXA_TYPE_NONE)
++#ifdef CONFIG_IPV6
++#define NX_IPV6(n) ((n)->v6.type != NXA_TYPE_NONE)
++#else
++#define NX_IPV6(n) (0)
++#endif
+
-+#define vx_files_avail(n) \
-+ vx_cres_avail(current_vx_info(), n, RLIMIT_NOFILE)
++#endif /* _VSERVER_NETWORK_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/network_cmd.h linux-4.9/include/linux/vserver/network_cmd.h
+--- linux-4.9/include/linux/vserver/network_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/network_cmd.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,37 @@
++#ifndef _VSERVER_NETWORK_CMD_H
++#define _VSERVER_NETWORK_CMD_H
+
-+#define vx_locks_avail(n) \
-+ vx_cres_avail(current_vx_info(), n, RLIMIT_LOCKS)
++#include <uapi/vserver/network_cmd.h>
+
-+#define vx_openfd_avail(n) \
-+ vx_cres_avail(current_vx_info(), n, VLIMIT_OPENFD)
++extern int vc_task_nid(uint32_t);
+
++extern int vc_nx_info(struct nx_info *, void __user *);
+
-+/* dentry limits */
++extern int vc_net_create(uint32_t, void __user *);
++extern int vc_net_migrate(struct nx_info *, void __user *);
+
-+#define vx_dentry_inc(d) do { \
-+ if (d_count(d) == 1) \
-+ vx_acc_cres(current_vx_info(), 1, d, VLIMIT_DENTRY); \
-+ } while (0)
++extern int vc_net_add(struct nx_info *, void __user *);
++extern int vc_net_remove(struct nx_info *, void __user *);
+
-+#define vx_dentry_dec(d) do { \
-+ if (d_count(d) == 0) \
-+ vx_acc_cres(current_vx_info(),-1, d, VLIMIT_DENTRY); \
-+ } while (0)
++extern int vc_net_add_ipv4_v1(struct nx_info *, void __user *);
++extern int vc_net_add_ipv4(struct nx_info *, void __user *);
+
-+#define vx_dentry_avail(n) \
-+ vx_cres_avail(current_vx_info(), n, VLIMIT_DENTRY)
++extern int vc_net_rem_ipv4_v1(struct nx_info *, void __user *);
++extern int vc_net_rem_ipv4(struct nx_info *, void __user *);
+
++extern int vc_net_add_ipv6(struct nx_info *, void __user *);
++extern int vc_net_remove_ipv6(struct nx_info *, void __user *);
+
-+/* socket limits */
++extern int vc_add_match_ipv4(struct nx_info *, void __user *);
++extern int vc_get_match_ipv4(struct nx_info *, void __user *);
+
-+#define vx_sock_inc(s) \
-+ vx_acc_cres((s)->sk_vx_info, 1, s, VLIMIT_NSOCK)
++extern int vc_add_match_ipv6(struct nx_info *, void __user *);
++extern int vc_get_match_ipv6(struct nx_info *, void __user *);
+
-+#define vx_sock_dec(s) \
-+ vx_acc_cres((s)->sk_vx_info,-1, s, VLIMIT_NSOCK)
++extern int vc_get_nflags(struct nx_info *, void __user *);
++extern int vc_set_nflags(struct nx_info *, void __user *);
+
-+#define vx_sock_avail(n) \
-+ vx_cres_avail(current_vx_info(), n, VLIMIT_NSOCK)
++extern int vc_get_ncaps(struct nx_info *, void __user *);
++extern int vc_set_ncaps(struct nx_info *, void __user *);
++
++#endif /* _VSERVER_CONTEXT_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/percpu.h linux-4.9/include/linux/vserver/percpu.h
+--- linux-4.9/include/linux/vserver/percpu.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/percpu.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,14 @@
++#ifndef _VSERVER_PERCPU_H
++#define _VSERVER_PERCPU_H
++
++#include "cvirt_def.h"
++#include "sched_def.h"
+
++struct _vx_percpu {
++ struct _vx_cvirt_pc cvirt;
++ struct _vx_sched_pc sched;
++};
+
-+/* ipc resource limits */
++#define PERCPU_PERCTX (sizeof(struct _vx_percpu))
+
-+#define vx_ipcmsg_add(v, u, a) \
-+ vx_add_cres(v, a, u, RLIMIT_MSGQUEUE)
++#endif /* _VSERVER_PERCPU_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/pid.h linux-4.9/include/linux/vserver/pid.h
+--- linux-4.9/include/linux/vserver/pid.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/pid.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,51 @@
++#ifndef _VSERVER_PID_H
++#define _VSERVER_PID_H
+
-+#define vx_ipcmsg_sub(v, u, a) \
-+ vx_sub_cres(v, a, u, RLIMIT_MSGQUEUE)
++/* pid faking stuff */
+
-+#define vx_ipcmsg_avail(v, a) \
-+ vx_cres_avail(v, a, RLIMIT_MSGQUEUE)
++#define vx_info_map_pid(v, p) \
++ __vx_info_map_pid((v), (p), __func__, __FILE__, __LINE__)
++#define vx_info_map_tgid(v,p) vx_info_map_pid(v,p)
++#define vx_map_pid(p) vx_info_map_pid(current_vx_info(), p)
++#define vx_map_tgid(p) vx_map_pid(p)
+
++static inline int __vx_info_map_pid(struct vx_info *vxi, int pid,
++ const char *func, const char *file, int line)
++{
++ if (vx_info_flags(vxi, VXF_INFO_INIT, 0)) {
++ vxfprintk(VXD_CBIT(cvirt, 2),
++ "vx_map_tgid: %p/%llx: %d -> %d",
++ vxi, (long long)vxi->vx_flags, pid,
++ (pid && pid == vxi->vx_initpid) ? 1 : pid,
++ func, file, line);
++ if (pid == 0)
++ return 0;
++ if (pid == vxi->vx_initpid)
++ return 1;
++ }
++ return pid;
++}
+
-+#define vx_ipcshm_add(v, k, a) \
-+ vx_add_cres(v, a, (void *)(long)(k), VLIMIT_SHMEM)
++#define vx_info_rmap_pid(v, p) \
++ __vx_info_rmap_pid((v), (p), __func__, __FILE__, __LINE__)
++#define vx_rmap_pid(p) vx_info_rmap_pid(current_vx_info(), p)
++#define vx_rmap_tgid(p) vx_rmap_pid(p)
+
-+#define vx_ipcshm_sub(v, k, a) \
-+ vx_sub_cres(v, a, (void *)(long)(k), VLIMIT_SHMEM)
++static inline int __vx_info_rmap_pid(struct vx_info *vxi, int pid,
++ const char *func, const char *file, int line)
++{
++ if (vx_info_flags(vxi, VXF_INFO_INIT, 0)) {
++ vxfprintk(VXD_CBIT(cvirt, 2),
++ "vx_rmap_tgid: %p/%llx: %d -> %d",
++ vxi, (long long)vxi->vx_flags, pid,
++ (pid == 1) ? vxi->vx_initpid : pid,
++ func, file, line);
++ if ((pid == 1) && vxi->vx_initpid)
++ return vxi->vx_initpid;
++ if (pid == vxi->vx_initpid)
++ return ~0U;
++ }
++ return pid;
++}
+
-+#define vx_ipcshm_avail(v, a) \
-+ vx_cres_avail(v, a, VLIMIT_SHMEM)
++#endif
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/sched.h linux-4.9/include/linux/vserver/sched.h
+--- linux-4.9/include/linux/vserver/sched.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/sched.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,23 @@
++#ifndef _VSERVER_SCHED_H
++#define _VSERVER_SCHED_H
+
+
-+#define vx_semary_inc(a) \
-+ vx_acc_cres(current_vx_info(), 1, a, VLIMIT_SEMARY)
++#ifdef __KERNEL__
+
-+#define vx_semary_dec(a) \
-+ vx_acc_cres(current_vx_info(), -1, a, VLIMIT_SEMARY)
++struct timespec;
+
++void vx_vsi_uptime(struct timespec *, struct timespec *);
+
-+#define vx_nsems_add(a,n) \
-+ vx_add_cres(current_vx_info(), n, a, VLIMIT_NSEMS)
+
-+#define vx_nsems_sub(a,n) \
-+ vx_sub_cres(current_vx_info(), n, a, VLIMIT_NSEMS)
++struct vx_info;
+
++void vx_update_load(struct vx_info *);
+
-+#else
-+#warning duplicate inclusion
-+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_network.h linux-4.9.217-vs2.3.9.12/include/linux/vs_network.h
---- linux-4.9.217/include/linux/vs_network.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_network.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,169 @@
-+#ifndef _NX_VS_NETWORK_H
-+#define _NX_VS_NETWORK_H
+
-+#include "vserver/context.h"
-+#include "vserver/network.h"
-+#include "vserver/base.h"
-+#include "vserver/check.h"
-+#include "vserver/debug.h"
++void vx_update_sched_param(struct _vx_sched *sched,
++ struct _vx_sched_pc *sched_pc);
+
-+#include <linux/sched.h>
++#endif /* __KERNEL__ */
++#else /* _VSERVER_SCHED_H */
++#warning duplicate inclusion
++#endif /* _VSERVER_SCHED_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/sched_cmd.h linux-4.9/include/linux/vserver/sched_cmd.h
+--- linux-4.9/include/linux/vserver/sched_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/sched_cmd.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,11 @@
++#ifndef _VSERVER_SCHED_CMD_H
++#define _VSERVER_SCHED_CMD_H
+
+
-+#define get_nx_info(i) __get_nx_info(i, __FILE__, __LINE__)
++#include <linux/compiler.h>
++#include <uapi/vserver/sched_cmd.h>
+
-+static inline struct nx_info *__get_nx_info(struct nx_info *nxi,
-+ const char *_file, int _line)
-+{
-+ if (!nxi)
-+ return NULL;
++extern int vc_set_prio_bias(struct vx_info *, void __user *);
++extern int vc_get_prio_bias(struct vx_info *, void __user *);
+
-+ vxlprintk(VXD_CBIT(nid, 2), "get_nx_info(%p[#%d.%d])",
-+ nxi, nxi ? nxi->nx_id : 0,
-+ nxi ? atomic_read(&nxi->nx_usecnt) : 0,
-+ _file, _line);
++#endif /* _VSERVER_SCHED_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/sched_def.h linux-4.9/include/linux/vserver/sched_def.h
+--- linux-4.9/include/linux/vserver/sched_def.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/sched_def.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,38 @@
++#ifndef _VSERVER_SCHED_DEF_H
++#define _VSERVER_SCHED_DEF_H
+
-+ atomic_inc(&nxi->nx_usecnt);
-+ return nxi;
-+}
++#include <linux/spinlock.h>
++#include <linux/jiffies.h>
++#include <linux/cpumask.h>
++#include <asm/atomic.h>
++#include <asm/param.h>
+
+
-+extern void free_nx_info(struct nx_info *);
++/* context sub struct */
+
-+#define put_nx_info(i) __put_nx_info(i, __FILE__, __LINE__)
++struct _vx_sched {
++ int prio_bias; /* bias offset for priority */
+
-+static inline void __put_nx_info(struct nx_info *nxi, const char *_file, int _line)
-+{
-+ if (!nxi)
-+ return;
++ cpumask_t update; /* CPUs which should update */
++};
+
-+ vxlprintk(VXD_CBIT(nid, 2), "put_nx_info(%p[#%d.%d])",
-+ nxi, nxi ? nxi->nx_id : 0,
-+ nxi ? atomic_read(&nxi->nx_usecnt) : 0,
-+ _file, _line);
++struct _vx_sched_pc {
++ int prio_bias; /* bias offset for priority */
+
-+ if (atomic_dec_and_test(&nxi->nx_usecnt))
-+ free_nx_info(nxi);
-+}
++ uint64_t user_ticks; /* token tick events */
++ uint64_t sys_ticks; /* token tick events */
++ uint64_t hold_ticks; /* token ticks paused */
++};
+
+
-+#define init_nx_info(p, i) __init_nx_info(p, i, __FILE__, __LINE__)
++#ifdef CONFIG_VSERVER_DEBUG
+
-+static inline void __init_nx_info(struct nx_info **nxp, struct nx_info *nxi,
-+ const char *_file, int _line)
++static inline void __dump_vx_sched(struct _vx_sched *sched)
+{
-+ if (nxi) {
-+ vxlprintk(VXD_CBIT(nid, 3),
-+ "init_nx_info(%p[#%d.%d])",
-+ nxi, nxi ? nxi->nx_id : 0,
-+ nxi ? atomic_read(&nxi->nx_usecnt) : 0,
-+ _file, _line);
-+
-+ atomic_inc(&nxi->nx_usecnt);
-+ }
-+ *nxp = nxi;
++ printk("\t_vx_sched:\n");
++ printk("\t priority = %4d\n", sched->prio_bias);
+}
+
++#endif
+
-+#define set_nx_info(p, i) __set_nx_info(p, i, __FILE__, __LINE__)
++#endif /* _VSERVER_SCHED_DEF_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/signal.h linux-4.9/include/linux/vserver/signal.h
+--- linux-4.9/include/linux/vserver/signal.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/signal.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,14 @@
++#ifndef _VSERVER_SIGNAL_H
++#define _VSERVER_SIGNAL_H
+
-+static inline void __set_nx_info(struct nx_info **nxp, struct nx_info *nxi,
-+ const char *_file, int _line)
-+{
-+ struct nx_info *nxo;
+
-+ if (!nxi)
-+ return;
++#ifdef __KERNEL__
+
-+ vxlprintk(VXD_CBIT(nid, 3), "set_nx_info(%p[#%d.%d])",
-+ nxi, nxi ? nxi->nx_id : 0,
-+ nxi ? atomic_read(&nxi->nx_usecnt) : 0,
-+ _file, _line);
++struct vx_info;
+
-+ atomic_inc(&nxi->nx_usecnt);
-+ nxo = xchg(nxp, nxi);
-+ BUG_ON(nxo);
-+}
++int vx_info_kill(struct vx_info *, int, int);
+
-+#define clr_nx_info(p) __clr_nx_info(p, __FILE__, __LINE__)
++#endif /* __KERNEL__ */
++#else /* _VSERVER_SIGNAL_H */
++#warning duplicate inclusion
++#endif /* _VSERVER_SIGNAL_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/signal_cmd.h linux-4.9/include/linux/vserver/signal_cmd.h
+--- linux-4.9/include/linux/vserver/signal_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/signal_cmd.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,14 @@
++#ifndef _VSERVER_SIGNAL_CMD_H
++#define _VSERVER_SIGNAL_CMD_H
+
-+static inline void __clr_nx_info(struct nx_info **nxp,
-+ const char *_file, int _line)
-+{
-+ struct nx_info *nxo;
++#include <uapi/vserver/signal_cmd.h>
+
-+ nxo = xchg(nxp, NULL);
-+ if (!nxo)
-+ return;
+
-+ vxlprintk(VXD_CBIT(nid, 3), "clr_nx_info(%p[#%d.%d])",
-+ nxo, nxo ? nxo->nx_id : 0,
-+ nxo ? atomic_read(&nxo->nx_usecnt) : 0,
-+ _file, _line);
++extern int vc_ctx_kill(struct vx_info *, void __user *);
++extern int vc_wait_exit(struct vx_info *, void __user *);
+
-+ if (atomic_dec_and_test(&nxo->nx_usecnt))
-+ free_nx_info(nxo);
-+}
+
++extern int vc_get_pflags(uint32_t pid, void __user *);
++extern int vc_set_pflags(uint32_t pid, void __user *);
+
-+#define claim_nx_info(v, p) __claim_nx_info(v, p, __FILE__, __LINE__)
++#endif /* _VSERVER_SIGNAL_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/space.h linux-4.9/include/linux/vserver/space.h
+--- linux-4.9/include/linux/vserver/space.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/space.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,12 @@
++#ifndef _VSERVER_SPACE_H
++#define _VSERVER_SPACE_H
+
-+static inline void __claim_nx_info(struct nx_info *nxi,
-+ struct task_struct *task, const char *_file, int _line)
-+{
-+ vxlprintk(VXD_CBIT(nid, 3), "claim_nx_info(%p[#%d.%d.%d]) %p",
-+ nxi, nxi ? nxi->nx_id : 0,
-+ nxi?atomic_read(&nxi->nx_usecnt):0,
-+ nxi?atomic_read(&nxi->nx_tasks):0,
-+ task, _file, _line);
++#include <linux/types.h>
+
-+ atomic_inc(&nxi->nx_tasks);
-+}
++struct vx_info;
+
++int vx_set_space(struct vx_info *vxi, unsigned long mask, unsigned index);
+
-+extern void unhash_nx_info(struct nx_info *);
++#else /* _VSERVER_SPACE_H */
++#warning duplicate inclusion
++#endif /* _VSERVER_SPACE_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/space_cmd.h linux-4.9/include/linux/vserver/space_cmd.h
+--- linux-4.9/include/linux/vserver/space_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/space_cmd.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,13 @@
++#ifndef _VSERVER_SPACE_CMD_H
++#define _VSERVER_SPACE_CMD_H
+
-+#define release_nx_info(v, p) __release_nx_info(v, p, __FILE__, __LINE__)
++#include <uapi/vserver/space_cmd.h>
+
-+static inline void __release_nx_info(struct nx_info *nxi,
-+ struct task_struct *task, const char *_file, int _line)
-+{
-+ vxlprintk(VXD_CBIT(nid, 3), "release_nx_info(%p[#%d.%d.%d]) %p",
-+ nxi, nxi ? nxi->nx_id : 0,
-+ nxi ? atomic_read(&nxi->nx_usecnt) : 0,
-+ nxi ? atomic_read(&nxi->nx_tasks) : 0,
-+ task, _file, _line);
+
-+ might_sleep();
++extern int vc_enter_space_v1(struct vx_info *, void __user *);
++extern int vc_set_space_v1(struct vx_info *, void __user *);
++extern int vc_enter_space(struct vx_info *, void __user *);
++extern int vc_set_space(struct vx_info *, void __user *);
++extern int vc_get_space_mask(void __user *, int);
+
-+ if (atomic_dec_and_test(&nxi->nx_tasks))
-+ unhash_nx_info(nxi);
-+}
++#endif /* _VSERVER_SPACE_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/switch.h linux-4.9/include/linux/vserver/switch.h
+--- linux-4.9/include/linux/vserver/switch.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/switch.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,8 @@
++#ifndef _VSERVER_SWITCH_H
++#define _VSERVER_SWITCH_H
+
+
-+#define task_get_nx_info(i) __task_get_nx_info(i, __FILE__, __LINE__)
++#include <linux/errno.h>
++#include <uapi/vserver/switch.h>
+
-+static __inline__ struct nx_info *__task_get_nx_info(struct task_struct *p,
-+ const char *_file, int _line)
-+{
-+ struct nx_info *nxi;
++#endif /* _VSERVER_SWITCH_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/tag.h linux-4.9/include/linux/vserver/tag.h
+--- linux-4.9/include/linux/vserver/tag.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/tag.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,160 @@
++#ifndef _DX_TAG_H
++#define _DX_TAG_H
+
-+ task_lock(p);
-+ vxlprintk(VXD_CBIT(nid, 5), "task_get_nx_info(%p)",
-+ p, _file, _line);
-+ nxi = __get_nx_info(p->nx_info, _file, _line);
-+ task_unlock(p);
-+ return nxi;
-+}
++#include <linux/types.h>
++#include <linux/uidgid.h>
+
+
-+static inline void exit_nx_info(struct task_struct *p)
-+{
-+ if (p->nx_info)
-+ release_nx_info(p->nx_info, p);
-+}
++#define DX_TAG(in) (IS_TAGGED(in))
+
+
++#ifdef CONFIG_TAG_NFSD
++#define DX_TAG_NFSD 1
+#else
-+#warning duplicate inclusion
++#define DX_TAG_NFSD 0
+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_pid.h linux-4.9.217-vs2.3.9.12/include/linux/vs_pid.h
---- linux-4.9.217/include/linux/vs_pid.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_pid.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,50 @@
-+#ifndef _VS_PID_H
-+#define _VS_PID_H
+
-+#include "vserver/base.h"
-+#include "vserver/check.h"
-+#include "vserver/context.h"
-+#include "vserver/debug.h"
-+#include "vserver/pid.h"
-+#include <linux/pid_namespace.h>
+
++#ifdef CONFIG_TAGGING_NONE
+
-+#define VXF_FAKE_INIT (VXF_INFO_INIT | VXF_STATE_INIT)
++#define MAX_UID 0xFFFFFFFF
++#define MAX_GID 0xFFFFFFFF
+
-+static inline
-+int vx_proc_task_visible(struct task_struct *task)
-+{
-+ if ((task->pid == 1) &&
-+ !vx_flags(VXF_FAKE_INIT, VXF_FAKE_INIT))
-+ /* show a blend through init */
-+ goto visible;
-+ if (vx_check(vx_task_xid(task), VS_WATCH | VS_IDENT))
-+ goto visible;
-+ return 0;
-+visible:
-+ return 1;
-+}
++#define INOTAG_TAG(cond, uid, gid, tag) (0)
+
-+#define find_task_by_real_pid(pid) find_task_by_pid_ns(pid, &init_pid_ns)
++#define TAGINO_UID(cond, uid, tag) (uid)
++#define TAGINO_GID(cond, gid, tag) (gid)
+
++#endif
+
-+static inline
-+struct task_struct *vx_get_proc_task(struct inode *inode, struct pid *pid)
-+{
-+ struct task_struct *task = get_pid_task(pid, PIDTYPE_PID);
+
-+ if (task && !vx_proc_task_visible(task)) {
-+ vxdprintk(VXD_CBIT(misc, 6),
-+ "dropping task (get) %p[#%u,%u] for %p[#%u,%u]",
-+ task, task->xid, task->pid,
-+ current, current->xid, current->pid);
-+ put_task_struct(task);
-+ task = NULL;
-+ }
-+ return task;
-+}
++#ifdef CONFIG_TAGGING_GID16
+
++#define MAX_UID 0xFFFFFFFF
++#define MAX_GID 0x0000FFFF
+
-+#else
-+#warning duplicate inclusion
-+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_sched.h linux-4.9.217-vs2.3.9.12/include/linux/vs_sched.h
---- linux-4.9.217/include/linux/vs_sched.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_sched.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,40 @@
-+#ifndef _VS_SCHED_H
-+#define _VS_SCHED_H
++#define INOTAG_TAG(cond, uid, gid, tag) \
++ ((cond) ? (((gid) >> 16) & 0xFFFF) : 0)
+
-+#include "vserver/base.h"
-+#include "vserver/context.h"
-+#include "vserver/sched.h"
++#define TAGINO_UID(cond, uid, tag) (uid)
++#define TAGINO_GID(cond, gid, tag) \
++ ((cond) ? (((gid) & 0xFFFF) | ((tag) << 16)) : (gid))
+
++#endif
+
-+#define MAX_PRIO_BIAS 20
-+#define MIN_PRIO_BIAS -20
+
-+static inline
-+int vx_adjust_prio(struct task_struct *p, int prio, int max_user)
-+{
-+ struct vx_info *vxi = p->vx_info;
++#ifdef CONFIG_TAGGING_ID24
+
-+ if (vxi)
-+ prio += vx_cpu(vxi, sched_pc).prio_bias;
-+ return prio;
-+}
++#define MAX_UID 0x00FFFFFF
++#define MAX_GID 0x00FFFFFF
+
-+static inline void vx_account_user(struct vx_info *vxi,
-+ cputime_t cputime, int nice)
-+{
-+ if (!vxi)
-+ return;
-+ vx_cpu(vxi, sched_pc).user_ticks += cputime;
-+}
++#define INOTAG_TAG(cond, uid, gid, tag) \
++ ((cond) ? ((((uid) >> 16) & 0xFF00) | (((gid) >> 24) & 0xFF)) : 0)
+
-+static inline void vx_account_system(struct vx_info *vxi,
-+ cputime_t cputime, int idle)
-+{
-+ if (!vxi)
-+ return;
-+ vx_cpu(vxi, sched_pc).sys_ticks += cputime;
-+}
++#define TAGINO_UID(cond, uid, tag) \
++ ((cond) ? (((uid) & 0xFFFFFF) | (((tag) & 0xFF00) << 16)) : (uid))
++#define TAGINO_GID(cond, gid, tag) \
++ ((cond) ? (((gid) & 0xFFFFFF) | (((tag) & 0x00FF) << 24)) : (gid))
+
-+#else
-+#warning duplicate inclusion
+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_socket.h linux-4.9.217-vs2.3.9.12/include/linux/vs_socket.h
---- linux-4.9.217/include/linux/vs_socket.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_socket.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,67 @@
-+#ifndef _VS_SOCKET_H
-+#define _VS_SOCKET_H
+
-+#include "vserver/debug.h"
-+#include "vserver/base.h"
-+#include "vserver/cacct.h"
-+#include "vserver/context.h"
-+#include "vserver/tag.h"
+
++#ifdef CONFIG_TAGGING_UID16
+
-+/* socket accounting */
++#define MAX_UID 0x0000FFFF
++#define MAX_GID 0xFFFFFFFF
+
-+#include <linux/socket.h>
++#define INOTAG_TAG(cond, uid, gid, tag) \
++ ((cond) ? (((uid) >> 16) & 0xFFFF) : 0)
+
-+static inline int vx_sock_type(int family)
-+{
-+ switch (family) {
-+ case PF_UNSPEC:
-+ return VXA_SOCK_UNSPEC;
-+ case PF_UNIX:
-+ return VXA_SOCK_UNIX;
-+ case PF_INET:
-+ return VXA_SOCK_INET;
-+ case PF_INET6:
-+ return VXA_SOCK_INET6;
-+ case PF_PACKET:
-+ return VXA_SOCK_PACKET;
-+ default:
-+ return VXA_SOCK_OTHER;
-+ }
-+}
++#define TAGINO_UID(cond, uid, tag) \
++ ((cond) ? (((uid) & 0xFFFF) | ((tag) << 16)) : (uid))
++#define TAGINO_GID(cond, gid, tag) (gid)
+
-+#define vx_acc_sock(v, f, p, s) \
-+ __vx_acc_sock(v, f, p, s, __FILE__, __LINE__)
++#endif
+
-+static inline void __vx_acc_sock(struct vx_info *vxi,
-+ int family, int pos, int size, char *file, int line)
-+{
-+ if (vxi) {
-+ int type = vx_sock_type(family);
+
-+ atomic_long_inc(&vxi->cacct.sock[type][pos].count);
-+ atomic_long_add(size, &vxi->cacct.sock[type][pos].total);
-+ }
-+}
++#ifdef CONFIG_TAGGING_INTERN
+
-+#define vx_sock_recv(sk, s) \
-+ vx_acc_sock((sk)->sk_vx_info, (sk)->sk_family, 0, s)
-+#define vx_sock_send(sk, s) \
-+ vx_acc_sock((sk)->sk_vx_info, (sk)->sk_family, 1, s)
-+#define vx_sock_fail(sk, s) \
-+ vx_acc_sock((sk)->sk_vx_info, (sk)->sk_family, 2, s)
++#define MAX_UID 0xFFFFFFFF
++#define MAX_GID 0xFFFFFFFF
+
++#define INOTAG_TAG(cond, uid, gid, tag) \
++ ((cond) ? (tag) : 0)
+
-+#define sock_vx_init(s) do { \
-+ (s)->sk_xid = 0; \
-+ (s)->sk_vx_info = NULL; \
-+ } while (0)
++#define TAGINO_UID(cond, uid, tag) (uid)
++#define TAGINO_GID(cond, gid, tag) (gid)
++
++#endif
+
-+#define sock_nx_init(s) do { \
-+ (s)->sk_nid = 0; \
-+ (s)->sk_nx_info = NULL; \
-+ } while (0)
+
++#ifndef CONFIG_TAGGING_NONE
++#define dx_current_fstag(sb) \
++ ((sb)->s_flags & MS_TAGGED ? dx_current_tag() : 0)
+#else
-+#warning duplicate inclusion
++#define dx_current_fstag(sb) (0)
+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_tag.h linux-4.9.217-vs2.3.9.12/include/linux/vs_tag.h
---- linux-4.9.217/include/linux/vs_tag.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_tag.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,47 @@
-+#ifndef _VS_TAG_H
-+#define _VS_TAG_H
+
-+#include <linux/vserver/tag.h>
++#ifndef CONFIG_TAGGING_INTERN
++#define TAGINO_TAG(cond, tag) (0)
++#else
++#define TAGINO_TAG(cond, tag) ((cond) ? (tag) : 0)
++#endif
+
-+/* check conditions */
++#define TAGINO_KUID(cond, kuid, ktag) \
++ KUIDT_INIT(TAGINO_UID(cond, __kuid_val(kuid), __ktag_val(ktag)))
++#define TAGINO_KGID(cond, kgid, ktag) \
++ KGIDT_INIT(TAGINO_GID(cond, __kgid_val(kgid), __ktag_val(ktag)))
++#define TAGINO_KTAG(cond, ktag) \
++ KTAGT_INIT(TAGINO_TAG(cond, __ktag_val(ktag)))
+
-+#define DX_ADMIN 0x0001
-+#define DX_WATCH 0x0002
-+#define DX_HOSTID 0x0008
+
-+#define DX_IDENT 0x0010
++#define INOTAG_UID(cond, uid, gid) \
++ ((cond) ? ((uid) & MAX_UID) : (uid))
++#define INOTAG_GID(cond, uid, gid) \
++ ((cond) ? ((gid) & MAX_GID) : (gid))
+
-+#define DX_ARG_MASK 0x0010
++#define INOTAG_KUID(cond, kuid, kgid) \
++ KUIDT_INIT(INOTAG_UID(cond, __kuid_val(kuid), __kgid_val(kgid)))
++#define INOTAG_KGID(cond, kuid, kgid) \
++ KGIDT_INIT(INOTAG_GID(cond, __kuid_val(kuid), __kgid_val(kgid)))
++#define INOTAG_KTAG(cond, kuid, kgid, ktag) \
++ KTAGT_INIT(INOTAG_TAG(cond, \
++ __kuid_val(kuid), __kgid_val(kgid), __ktag_val(ktag)))
+
+
-+#define dx_task_tag(t) ((t)->tag)
++static inline uid_t dx_map_uid(uid_t uid)
++{
++ if ((uid > MAX_UID) && (uid != -1))
++ uid = -2;
++ return (uid & MAX_UID);
++}
+
-+#define dx_current_tag() dx_task_tag(current)
++static inline gid_t dx_map_gid(gid_t gid)
++{
++ if ((gid > MAX_GID) && (gid != -1))
++ gid = -2;
++ return (gid & MAX_GID);
++}
+
-+#define dx_check(c, m) __dx_check(dx_current_tag(), c, m)
++struct peer_tag {
++ int32_t xid;
++ int32_t nid;
++};
+
-+#define dx_weak_check(c, m) ((m) ? dx_check(c, m) : 1)
++#define dx_notagcheck(sb) ((sb) && ((sb)->s_flags & MS_NOTAGCHECK))
+
++int dx_parse_tag(char *string, vtag_t *tag, int remove, int *mnt_flags,
++ unsigned long *flags);
+
-+/*
-+ * check current context for ADMIN/WATCH and
-+ * optionally against supplied argument
-+ */
-+static inline int __dx_check(vtag_t cid, vtag_t id, unsigned int mode)
-+{
-+ if (mode & DX_ARG_MASK) {
-+ if ((mode & DX_IDENT) && (id == cid))
-+ return 1;
-+ }
-+ return (((mode & DX_ADMIN) && (cid == 0)) ||
-+ ((mode & DX_WATCH) && (cid == 1)) ||
-+ ((mode & DX_HOSTID) && (id == 0)));
-+}
++#ifdef CONFIG_PROPAGATE
+
-+struct inode;
-+int dx_permission(const struct inode *inode, int mask);
++void __dx_propagate_tag(struct nameidata *nd, struct inode *inode);
+
++#define dx_propagate_tag(n, i) __dx_propagate_tag(n, i)
+
+#else
-+#warning duplicate inclusion
++#define dx_propagate_tag(n, i) do { } while (0)
+#endif
-diff -NurpP --minimal linux-4.9.217/include/linux/vs_time.h linux-4.9.217-vs2.3.9.12/include/linux/vs_time.h
---- linux-4.9.217/include/linux/vs_time.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/linux/vs_time.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,21 @@
-+#ifndef _VS_TIME_H
-+#define _VS_TIME_H
+
++#endif /* _DX_TAG_H */
+diff -urNp -x '*.orig' linux-4.9/include/linux/vserver/tag_cmd.h linux-4.9/include/linux/vserver/tag_cmd.h
+--- linux-4.9/include/linux/vserver/tag_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/linux/vserver/tag_cmd.h 2021-02-24 15:47:45.097743096 +0100
+@@ -0,0 +1,10 @@
++#ifndef _VSERVER_TAG_CMD_H
++#define _VSERVER_TAG_CMD_H
+
-+/* time faking stuff */
-+
-+#ifdef CONFIG_VSERVER_VTIME
++#include <uapi/vserver/tag_cmd.h>
+
-+extern void vx_adjust_timespec(struct timespec *ts);
-+extern int vx_settimeofday(const struct timespec *ts);
-+extern int vx_settimeofday64(const struct timespec64 *ts);
++extern int vc_task_tag(uint32_t);
+
-+#else
-+#define vx_adjust_timespec(t) do { } while (0)
-+#define vx_settimeofday(t) do_settimeofday(t)
-+#define vx_settimeofday64(t) do_settimeofday64(t)
-+#endif
++extern int vc_tag_migrate(uint32_t);
+
-+#else
-+#warning duplicate inclusion
-+#endif
-diff -NurpP --minimal linux-4.9.217/include/net/addrconf.h linux-4.9.217-vs2.3.9.12/include/net/addrconf.h
---- linux-4.9.217/include/net/addrconf.h 2020-03-27 00:51:41.560167763 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/net/addrconf.h 2018-10-20 04:58:14.000000000 +0000
++#endif /* _VSERVER_TAG_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/net/addrconf.h linux-4.9/include/net/addrconf.h
+--- linux-4.9/include/net/addrconf.h 2021-02-24 15:47:32.424013281 +0100
++++ linux-4.9/include/net/addrconf.h 2021-02-24 15:47:45.101076533 +0100
@@ -85,7 +85,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(str
int ipv6_dev_get_saddr(struct net *net, const struct net_device *dev,
int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
u32 banned_flags);
int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
-diff -NurpP --minimal linux-4.9.217/include/net/af_unix.h linux-4.9.217-vs2.3.9.12/include/net/af_unix.h
---- linux-4.9.217/include/net/af_unix.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/net/af_unix.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/net/af_unix.h linux-4.9/include/net/af_unix.h
+--- linux-4.9/include/net/af_unix.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/net/af_unix.h 2021-02-24 15:47:45.101076533 +0100
@@ -4,6 +4,7 @@
#include <linux/socket.h>
#include <linux/un.h>
#include <net/sock.h>
void unix_inflight(struct user_struct *user, struct file *fp);
-diff -NurpP --minimal linux-4.9.217/include/net/inet_timewait_sock.h linux-4.9.217-vs2.3.9.12/include/net/inet_timewait_sock.h
---- linux-4.9.217/include/net/inet_timewait_sock.h 2020-03-27 00:51:41.750164768 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/net/inet_timewait_sock.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/net/inet_timewait_sock.h linux-4.9/include/net/inet_timewait_sock.h
+--- linux-4.9/include/net/inet_timewait_sock.h 2021-02-24 15:47:32.427346719 +0100
++++ linux-4.9/include/net/inet_timewait_sock.h 2021-02-24 15:47:45.101076533 +0100
@@ -72,6 +72,10 @@ struct inet_timewait_sock {
#define tw_num __tw_common.skc_num
#define tw_cookie __tw_common.skc_cookie
int tw_timeout;
volatile unsigned char tw_substate;
-diff -NurpP --minimal linux-4.9.217/include/net/ip6_route.h linux-4.9.217-vs2.3.9.12/include/net/ip6_route.h
---- linux-4.9.217/include/net/ip6_route.h 2020-03-27 00:51:41.750164768 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/net/ip6_route.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/net/ip6_route.h linux-4.9/include/net/ip6_route.h
+--- linux-4.9/include/net/ip6_route.h 2021-02-24 15:47:32.427346719 +0100
++++ linux-4.9/include/net/ip6_route.h 2021-02-24 15:47:45.101076533 +0100
@@ -26,6 +26,7 @@ struct route_info {
#include <linux/ip.h>
#include <linux/ipv6.h>
return err;
}
-diff -NurpP --minimal linux-4.9.217/include/net/route.h linux-4.9.217-vs2.3.9.12/include/net/route.h
---- linux-4.9.217/include/net/route.h 2020-03-27 00:51:41.900162405 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/net/route.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/net/route.h linux-4.9/include/net/route.h
+--- linux-4.9/include/net/route.h 2021-02-24 15:47:32.434013594 +0100
++++ linux-4.9/include/net/route.h 2021-02-24 15:47:45.101076533 +0100
@@ -226,6 +226,9 @@ static inline void ip_rt_put(struct rtab
dst_release(&rt->dst);
}
rt = __ip_route_output_key(net, fl4);
if (IS_ERR(rt))
return rt;
-diff -NurpP --minimal linux-4.9.217/include/net/sock.h linux-4.9.217-vs2.3.9.12/include/net/sock.h
---- linux-4.9.217/include/net/sock.h 2020-03-27 00:51:41.960161456 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/net/sock.h 2020-04-01 09:40:32.325403988 +0000
+diff -urNp -x '*.orig' linux-4.9/include/net/sock.h linux-4.9/include/net/sock.h
+--- linux-4.9/include/net/sock.h 2021-02-24 15:47:32.434013594 +0100
++++ linux-4.9/include/net/sock.h 2021-02-24 15:47:45.101076533 +0100
@@ -187,6 +187,10 @@ struct sock_common {
struct in6_addr skc_v6_daddr;
struct in6_addr skc_v6_rcv_saddr;
#define sk_incoming_cpu __sk_common.skc_incoming_cpu
#define sk_flags __sk_common.skc_flags
#define sk_rxhash __sk_common.skc_rxhash
-diff -NurpP --minimal linux-4.9.217/include/uapi/Kbuild linux-4.9.217-vs2.3.9.12/include/uapi/Kbuild
---- linux-4.9.217/include/uapi/Kbuild 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/Kbuild 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/Kbuild linux-4.9/include/uapi/Kbuild
+--- linux-4.9/include/uapi/Kbuild 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/uapi/Kbuild 2021-02-24 15:47:45.101076533 +0100
@@ -13,3 +13,4 @@ header-y += drm/
header-y += xen/
header-y += scsi/
header-y += misc/
+header-y += vserver/
-diff -NurpP --minimal linux-4.9.217/include/uapi/linux/btrfs_tree.h linux-4.9.217-vs2.3.9.12/include/uapi/linux/btrfs_tree.h
---- linux-4.9.217/include/uapi/linux/btrfs_tree.h 2020-03-27 00:51:42.430154051 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/linux/btrfs_tree.h 2019-02-22 08:37:55.713049558 +0000
-@@ -564,11 +564,14 @@ struct btrfs_inode_item {
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/btrfs_tree.h linux-4.9/include/uapi/linux/btrfs_tree.h
+--- linux-4.9/include/uapi/linux/btrfs_tree.h 2021-02-24 15:47:32.444013907 +0100
++++ linux-4.9/include/uapi/linux/btrfs_tree.h 2021-02-24 15:47:45.101076533 +0100
+@@ -566,11 +566,14 @@ struct btrfs_inode_item {
/* modification sequence number for NFS */
__le64 sequence;
struct btrfs_timespec atime;
struct btrfs_timespec ctime;
struct btrfs_timespec mtime;
-diff -NurpP --minimal linux-4.9.217/include/uapi/linux/capability.h linux-4.9.217-vs2.3.9.12/include/uapi/linux/capability.h
---- linux-4.9.217/include/uapi/linux/capability.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/linux/capability.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/capability.h linux-4.9/include/uapi/linux/capability.h
+--- linux-4.9/include/uapi/linux/capability.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/uapi/linux/capability.h 2021-02-24 15:47:45.101076533 +0100
@@ -257,6 +257,7 @@ struct vfs_cap_data {
arbitrary SCSI commands */
/* Allow setting encryption key on loopback filesystem */
/*
* Bit location of each capability (used by user-space library and kernel)
-diff -NurpP --minimal linux-4.9.217/include/uapi/linux/fs.h linux-4.9.217-vs2.3.9.12/include/uapi/linux/fs.h
---- linux-4.9.217/include/uapi/linux/fs.h 2020-03-27 00:51:42.450153736 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/linux/fs.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/fs.h linux-4.9/include/uapi/linux/fs.h
+--- linux-4.9/include/uapi/linux/fs.h 2021-02-24 15:47:32.444013907 +0100
++++ linux-4.9/include/uapi/linux/fs.h 2021-02-24 15:47:45.101076533 +0100
@@ -130,6 +130,9 @@ struct inodes_stat_t {
#define MS_I_VERSION (1<<23) /* Update inode I_version field */
#define MS_STRICTATIME (1<<24) /* Always perform atime updates */
#define SYNC_FILE_RANGE_WAIT_BEFORE 1
#define SYNC_FILE_RANGE_WRITE 2
-diff -NurpP --minimal linux-4.9.217/include/uapi/linux/gfs2_ondisk.h linux-4.9.217-vs2.3.9.12/include/uapi/linux/gfs2_ondisk.h
---- linux-4.9.217/include/uapi/linux/gfs2_ondisk.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/linux/gfs2_ondisk.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/gfs2_ondisk.h linux-4.9/include/uapi/linux/gfs2_ondisk.h
+--- linux-4.9/include/uapi/linux/gfs2_ondisk.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/uapi/linux/gfs2_ondisk.h 2021-02-24 15:47:45.104409971 +0100
@@ -225,6 +225,9 @@ enum {
gfs2fl_Sync = 8,
gfs2fl_System = 9,
#define GFS2_DIF_TRUNC_IN_PROG 0x20000000 /* New in gfs2 */
#define GFS2_DIF_INHERIT_DIRECTIO 0x40000000 /* only in gfs1 */
#define GFS2_DIF_INHERIT_JDATA 0x80000000
-diff -NurpP --minimal linux-4.9.217/include/uapi/linux/if_tun.h linux-4.9.217-vs2.3.9.12/include/uapi/linux/if_tun.h
---- linux-4.9.217/include/uapi/linux/if_tun.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/linux/if_tun.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/if_tun.h linux-4.9/include/uapi/linux/if_tun.h
+--- linux-4.9/include/uapi/linux/if_tun.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/uapi/linux/if_tun.h 2021-02-24 15:47:45.104409971 +0100
@@ -56,6 +56,7 @@
*/
#define TUNSETVNETBE _IOW('T', 222, int)
/* TUNSETIFF ifr flags */
#define IFF_TUN 0x0001
-diff -NurpP --minimal linux-4.9.217/include/uapi/linux/major.h linux-4.9.217-vs2.3.9.12/include/uapi/linux/major.h
---- linux-4.9.217/include/uapi/linux/major.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/linux/major.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/major.h linux-4.9/include/uapi/linux/major.h
+--- linux-4.9/include/uapi/linux/major.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/uapi/linux/major.h 2021-02-24 15:47:45.104409971 +0100
@@ -15,6 +15,7 @@
#define HD_MAJOR IDE0_MAJOR
#define PTY_SLAVE_MAJOR 3
#define TTYAUX_MAJOR 5
#define LP_MAJOR 6
#define VCS_MAJOR 7
-diff -NurpP --minimal linux-4.9.217/include/uapi/linux/nfs_mount.h linux-4.9.217-vs2.3.9.12/include/uapi/linux/nfs_mount.h
---- linux-4.9.217/include/uapi/linux/nfs_mount.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/linux/nfs_mount.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/nfs_mount.h linux-4.9/include/uapi/linux/nfs_mount.h
+--- linux-4.9/include/uapi/linux/nfs_mount.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/uapi/linux/nfs_mount.h 2021-02-24 15:47:45.104409971 +0100
@@ -63,7 +63,8 @@ struct nfs_mount_data {
#define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 non-text parsed mount data only */
#define NFS_MOUNT_NORDIRPLUS 0x4000 /* 5 */
/* The following are for internal use only */
#define NFS_MOUNT_LOOKUP_CACHE_NONEG 0x10000
-diff -NurpP --minimal linux-4.9.217/include/uapi/linux/reboot.h linux-4.9.217-vs2.3.9.12/include/uapi/linux/reboot.h
---- linux-4.9.217/include/uapi/linux/reboot.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/linux/reboot.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/reboot.h linux-4.9/include/uapi/linux/reboot.h
+--- linux-4.9/include/uapi/linux/reboot.h 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/include/uapi/linux/reboot.h 2021-02-24 15:47:45.104409971 +0100
@@ -33,7 +33,7 @@
#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4
#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2
#endif /* _UAPI_LINUX_REBOOT_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/linux/sysctl.h linux-4.9.217-vs2.3.9.12/include/uapi/linux/sysctl.h
---- linux-4.9.217/include/uapi/linux/sysctl.h 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/linux/sysctl.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/linux/sysctl.h linux-4.9/include/uapi/linux/sysctl.h
+--- linux-4.9/include/uapi/linux/sysctl.h 2021-02-24 15:47:32.450680782 +0100
++++ linux-4.9/include/uapi/linux/sysctl.h 2021-02-24 15:47:45.104409971 +0100
@@ -58,6 +58,7 @@ enum
CTL_ABI=9, /* Binary emulation */
CTL_CPU=10, /* CPU stuff (speed scaling, etc) */
KERN_SPARC_REBOOT=21, /* reboot command on Sparc */
KERN_CTLALTDEL=22, /* int: allow ctl-alt-del to reboot */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/cacct_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/cacct_cmd.h
---- linux-4.9.217/include/uapi/vserver/cacct_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/cacct_cmd.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/Kbuild linux-4.9/include/uapi/vserver/Kbuild
+--- linux-4.9/include/uapi/vserver/Kbuild 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/Kbuild 2021-02-24 15:47:45.104409971 +0100
+@@ -0,0 +1,9 @@
++
++header-y += context_cmd.h network_cmd.h space_cmd.h \
++ cacct_cmd.h cvirt_cmd.h limit_cmd.h dlimit_cmd.h \
++ inode_cmd.h tag_cmd.h sched_cmd.h signal_cmd.h \
++ debug_cmd.h device_cmd.h
++
++header-y += switch.h context.h network.h monitor.h \
++ limit.h inode.h device.h
++
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/cacct_cmd.h linux-4.9/include/uapi/vserver/cacct_cmd.h
+--- linux-4.9/include/uapi/vserver/cacct_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/cacct_cmd.h 2021-02-24 15:47:45.104409971 +0100
@@ -0,0 +1,15 @@
+#ifndef _UAPI_VS_CACCT_CMD_H
+#define _UAPI_VS_CACCT_CMD_H
+};
+
+#endif /* _UAPI_VS_CACCT_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/context_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/context_cmd.h
---- linux-4.9.217/include/uapi/vserver/context_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/context_cmd.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/context.h linux-4.9/include/uapi/vserver/context.h
+--- linux-4.9/include/uapi/vserver/context.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/context.h 2021-02-24 15:47:45.104409971 +0100
+@@ -0,0 +1,81 @@
++#ifndef _UAPI_VS_CONTEXT_H
++#define _UAPI_VS_CONTEXT_H
++
++#include <linux/types.h>
++#include <linux/capability.h>
++
++
++/* context flags */
++
++#define VXF_INFO_SCHED 0x00000002
++#define VXF_INFO_NPROC 0x00000004
++#define VXF_INFO_PRIVATE 0x00000008
++
++#define VXF_INFO_INIT 0x00000010
++#define VXF_INFO_HIDE 0x00000020
++#define VXF_INFO_ULIMIT 0x00000040
++#define VXF_INFO_NSPACE 0x00000080
++
++#define VXF_SCHED_HARD 0x00000100
++#define VXF_SCHED_PRIO 0x00000200
++#define VXF_SCHED_PAUSE 0x00000400
++
++#define VXF_VIRT_MEM 0x00010000
++#define VXF_VIRT_UPTIME 0x00020000
++#define VXF_VIRT_CPU 0x00040000
++#define VXF_VIRT_LOAD 0x00080000
++#define VXF_VIRT_TIME 0x00100000
++
++#define VXF_HIDE_MOUNT 0x01000000
++/* was VXF_HIDE_NETIF 0x02000000 */
++#define VXF_HIDE_VINFO 0x04000000
++
++#define VXF_STATE_SETUP (1ULL << 32)
++#define VXF_STATE_INIT (1ULL << 33)
++#define VXF_STATE_ADMIN (1ULL << 34)
++
++#define VXF_SC_HELPER (1ULL << 36)
++#define VXF_REBOOT_KILL (1ULL << 37)
++#define VXF_PERSISTENT (1ULL << 38)
++
++#define VXF_FORK_RSS (1ULL << 48)
++#define VXF_PROLIFIC (1ULL << 49)
++
++#define VXF_IGNEG_NICE (1ULL << 52)
++
++#define VXF_ONE_TIME (0x0007ULL << 32)
++
++#define VXF_INIT_SET (VXF_STATE_SETUP | VXF_STATE_INIT | VXF_STATE_ADMIN)
++
++
++/* context migration */
++
++#define VXM_SET_INIT 0x00000001
++#define VXM_SET_REAPER 0x00000002
++
++/* context caps */
++
++#define VXC_SET_UTSNAME 0x00000001
++#define VXC_SET_RLIMIT 0x00000002
++#define VXC_FS_SECURITY 0x00000004
++#define VXC_FS_TRUSTED 0x00000008
++#define VXC_TIOCSTI 0x00000010
++
++/* was VXC_RAW_ICMP 0x00000100 */
++#define VXC_SYSLOG 0x00001000
++#define VXC_OOM_ADJUST 0x00002000
++#define VXC_AUDIT_CONTROL 0x00004000
++
++#define VXC_SECURE_MOUNT 0x00010000
++/* #define VXC_SECURE_REMOUNT 0x00020000 */
++#define VXC_BINARY_MOUNT 0x00040000
++#define VXC_DEV_MOUNT 0x00080000
++
++#define VXC_QUOTA_CTL 0x00100000
++#define VXC_ADMIN_MAPPER 0x00200000
++#define VXC_ADMIN_CLOOP 0x00400000
++
++#define VXC_KTHREAD 0x01000000
++#define VXC_NAMESPACE 0x02000000
++
++#endif /* _UAPI_VS_CONTEXT_H */
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/context_cmd.h linux-4.9/include/uapi/vserver/context_cmd.h
+--- linux-4.9/include/uapi/vserver/context_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/context_cmd.h 2021-02-24 15:47:45.104409971 +0100
@@ -0,0 +1,115 @@
+#ifndef _UAPI_VS_CONTEXT_CMD_H
+#define _UAPI_VS_CONTEXT_CMD_H
+};
+
+#endif /* _UAPI_VS_CONTEXT_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/context.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/context.h
---- linux-4.9.217/include/uapi/vserver/context.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/context.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,81 @@
-+#ifndef _UAPI_VS_CONTEXT_H
-+#define _UAPI_VS_CONTEXT_H
-+
-+#include <linux/types.h>
-+#include <linux/capability.h>
-+
-+
-+/* context flags */
-+
-+#define VXF_INFO_SCHED 0x00000002
-+#define VXF_INFO_NPROC 0x00000004
-+#define VXF_INFO_PRIVATE 0x00000008
-+
-+#define VXF_INFO_INIT 0x00000010
-+#define VXF_INFO_HIDE 0x00000020
-+#define VXF_INFO_ULIMIT 0x00000040
-+#define VXF_INFO_NSPACE 0x00000080
-+
-+#define VXF_SCHED_HARD 0x00000100
-+#define VXF_SCHED_PRIO 0x00000200
-+#define VXF_SCHED_PAUSE 0x00000400
-+
-+#define VXF_VIRT_MEM 0x00010000
-+#define VXF_VIRT_UPTIME 0x00020000
-+#define VXF_VIRT_CPU 0x00040000
-+#define VXF_VIRT_LOAD 0x00080000
-+#define VXF_VIRT_TIME 0x00100000
-+
-+#define VXF_HIDE_MOUNT 0x01000000
-+/* was VXF_HIDE_NETIF 0x02000000 */
-+#define VXF_HIDE_VINFO 0x04000000
-+
-+#define VXF_STATE_SETUP (1ULL << 32)
-+#define VXF_STATE_INIT (1ULL << 33)
-+#define VXF_STATE_ADMIN (1ULL << 34)
-+
-+#define VXF_SC_HELPER (1ULL << 36)
-+#define VXF_REBOOT_KILL (1ULL << 37)
-+#define VXF_PERSISTENT (1ULL << 38)
-+
-+#define VXF_FORK_RSS (1ULL << 48)
-+#define VXF_PROLIFIC (1ULL << 49)
-+
-+#define VXF_IGNEG_NICE (1ULL << 52)
-+
-+#define VXF_ONE_TIME (0x0007ULL << 32)
-+
-+#define VXF_INIT_SET (VXF_STATE_SETUP | VXF_STATE_INIT | VXF_STATE_ADMIN)
-+
-+
-+/* context migration */
-+
-+#define VXM_SET_INIT 0x00000001
-+#define VXM_SET_REAPER 0x00000002
-+
-+/* context caps */
-+
-+#define VXC_SET_UTSNAME 0x00000001
-+#define VXC_SET_RLIMIT 0x00000002
-+#define VXC_FS_SECURITY 0x00000004
-+#define VXC_FS_TRUSTED 0x00000008
-+#define VXC_TIOCSTI 0x00000010
-+
-+/* was VXC_RAW_ICMP 0x00000100 */
-+#define VXC_SYSLOG 0x00001000
-+#define VXC_OOM_ADJUST 0x00002000
-+#define VXC_AUDIT_CONTROL 0x00004000
-+
-+#define VXC_SECURE_MOUNT 0x00010000
-+/* #define VXC_SECURE_REMOUNT 0x00020000 */
-+#define VXC_BINARY_MOUNT 0x00040000
-+#define VXC_DEV_MOUNT 0x00080000
-+
-+#define VXC_QUOTA_CTL 0x00100000
-+#define VXC_ADMIN_MAPPER 0x00200000
-+#define VXC_ADMIN_CLOOP 0x00400000
-+
-+#define VXC_KTHREAD 0x01000000
-+#define VXC_NAMESPACE 0x02000000
-+
-+#endif /* _UAPI_VS_CONTEXT_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/cvirt_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/cvirt_cmd.h
---- linux-4.9.217/include/uapi/vserver/cvirt_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/cvirt_cmd.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/cvirt_cmd.h linux-4.9/include/uapi/vserver/cvirt_cmd.h
+--- linux-4.9/include/uapi/vserver/cvirt_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/cvirt_cmd.h 2021-02-24 15:47:45.104409971 +0100
@@ -0,0 +1,41 @@
+#ifndef _UAPI_VS_CVIRT_CMD_H
+#define _UAPI_VS_CVIRT_CMD_H
+};
+
+#endif /* _UAPI_VS_CVIRT_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/debug_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/debug_cmd.h
---- linux-4.9.217/include/uapi/vserver/debug_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/debug_cmd.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/debug_cmd.h linux-4.9/include/uapi/vserver/debug_cmd.h
+--- linux-4.9/include/uapi/vserver/debug_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/debug_cmd.h 2021-02-24 15:47:45.104409971 +0100
@@ -0,0 +1,24 @@
+#ifndef _UAPI_VS_DEBUG_CMD_H
+#define _UAPI_VS_DEBUG_CMD_H
+};
+
+#endif /* _UAPI_VS_DEBUG_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/device_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/device_cmd.h
---- linux-4.9.217/include/uapi/vserver/device_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/device_cmd.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/device.h linux-4.9/include/uapi/vserver/device.h
+--- linux-4.9/include/uapi/vserver/device.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/device.h 2021-02-24 15:47:45.104409971 +0100
+@@ -0,0 +1,12 @@
++#ifndef _UAPI_VS_DEVICE_H
++#define _UAPI_VS_DEVICE_H
++
++
++#define DATTR_CREATE 0x00000001
++#define DATTR_OPEN 0x00000002
++
++#define DATTR_REMAP 0x00000010
++
++#define DATTR_MASK 0x00000013
++
++#endif /* _UAPI_VS_DEVICE_H */
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/device_cmd.h linux-4.9/include/uapi/vserver/device_cmd.h
+--- linux-4.9/include/uapi/vserver/device_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/device_cmd.h 2021-02-24 15:47:45.104409971 +0100
@@ -0,0 +1,16 @@
+#ifndef _UAPI_VS_DEVICE_CMD_H
+#define _UAPI_VS_DEVICE_CMD_H
+ uint32_t flags;
+};
+
-+#endif /* _UAPI_VS_DEVICE_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/device.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/device.h
---- linux-4.9.217/include/uapi/vserver/device.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/device.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,12 @@
-+#ifndef _UAPI_VS_DEVICE_H
-+#define _UAPI_VS_DEVICE_H
-+
-+
-+#define DATTR_CREATE 0x00000001
-+#define DATTR_OPEN 0x00000002
-+
-+#define DATTR_REMAP 0x00000010
-+
-+#define DATTR_MASK 0x00000013
-+
-+#endif /* _UAPI_VS_DEVICE_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/dlimit_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/dlimit_cmd.h
---- linux-4.9.217/include/uapi/vserver/dlimit_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/dlimit_cmd.h 2018-10-20 04:58:14.000000000 +0000
++#endif /* _UAPI_VS_DEVICE_CMD_H */
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/dlimit_cmd.h linux-4.9/include/uapi/vserver/dlimit_cmd.h
+--- linux-4.9/include/uapi/vserver/dlimit_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/dlimit_cmd.h 2021-02-24 15:47:45.104409971 +0100
@@ -0,0 +1,67 @@
+#ifndef _UAPI_VS_DLIMIT_CMD_H
+#define _UAPI_VS_DLIMIT_CMD_H
+}
+
+#endif /* _UAPI_VS_DLIMIT_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/inode_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/inode_cmd.h
---- linux-4.9.217/include/uapi/vserver/inode_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/inode_cmd.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/inode.h linux-4.9/include/uapi/vserver/inode.h
+--- linux-4.9/include/uapi/vserver/inode.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/inode.h 2021-02-24 15:47:45.104409971 +0100
+@@ -0,0 +1,23 @@
++#ifndef _UAPI_VS_INODE_H
++#define _UAPI_VS_INODE_H
++
++
++#define IATTR_TAG 0x01000000
++
++#define IATTR_ADMIN 0x00000001
++#define IATTR_WATCH 0x00000002
++#define IATTR_HIDE 0x00000004
++#define IATTR_FLAGS 0x00000007
++
++#define IATTR_BARRIER 0x00010000
++#define IATTR_IXUNLINK 0x00020000
++#define IATTR_IMMUTABLE 0x00040000
++#define IATTR_COW 0x00080000
++
++
++/* inode ioctls */
++
++#define FIOC_GETXFLG _IOR('x', 5, long)
++#define FIOC_SETXFLG _IOW('x', 6, long)
++
++#endif /* _UAPI_VS_INODE_H */
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/inode_cmd.h linux-4.9/include/uapi/vserver/inode_cmd.h
+--- linux-4.9/include/uapi/vserver/inode_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/inode_cmd.h 2021-02-24 15:47:45.104409971 +0100
@@ -0,0 +1,26 @@
+#ifndef _UAPI_VS_INODE_CMD_H
+#define _UAPI_VS_INODE_CMD_H
+};
+
+#endif /* _UAPI_VS_INODE_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/inode.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/inode.h
---- linux-4.9.217/include/uapi/vserver/inode.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/inode.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,23 @@
-+#ifndef _UAPI_VS_INODE_H
-+#define _UAPI_VS_INODE_H
-+
-+
-+#define IATTR_TAG 0x01000000
-+
-+#define IATTR_ADMIN 0x00000001
-+#define IATTR_WATCH 0x00000002
-+#define IATTR_HIDE 0x00000004
-+#define IATTR_FLAGS 0x00000007
-+
-+#define IATTR_BARRIER 0x00010000
-+#define IATTR_IXUNLINK 0x00020000
-+#define IATTR_IMMUTABLE 0x00040000
-+#define IATTR_COW 0x00080000
-+
-+
-+/* inode ioctls */
-+
-+#define FIOC_GETXFLG _IOR('x', 5, long)
-+#define FIOC_SETXFLG _IOW('x', 6, long)
-+
-+#endif /* _UAPI_VS_INODE_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/Kbuild linux-4.9.217-vs2.3.9.12/include/uapi/vserver/Kbuild
---- linux-4.9.217/include/uapi/vserver/Kbuild 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/Kbuild 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,9 @@
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/limit.h linux-4.9/include/uapi/vserver/limit.h
+--- linux-4.9/include/uapi/vserver/limit.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/limit.h 2021-02-24 15:47:45.104409971 +0100
+@@ -0,0 +1,14 @@
++#ifndef _UAPI_VS_LIMIT_H
++#define _UAPI_VS_LIMIT_H
+
-+header-y += context_cmd.h network_cmd.h space_cmd.h \
-+ cacct_cmd.h cvirt_cmd.h limit_cmd.h dlimit_cmd.h \
-+ inode_cmd.h tag_cmd.h sched_cmd.h signal_cmd.h \
-+ debug_cmd.h device_cmd.h
+
-+header-y += switch.h context.h network.h monitor.h \
-+ limit.h inode.h device.h
++#define VLIMIT_NSOCK 16
++#define VLIMIT_OPENFD 17
++#define VLIMIT_ANON 18
++#define VLIMIT_SHMEM 19
++#define VLIMIT_SEMARY 20
++#define VLIMIT_NSEMS 21
++#define VLIMIT_DENTRY 22
++#define VLIMIT_MAPPED 23
+
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/limit_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/limit_cmd.h
---- linux-4.9.217/include/uapi/vserver/limit_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/limit_cmd.h 2018-10-20 04:58:14.000000000 +0000
++#endif /* _UAPI_VS_LIMIT_H */
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/limit_cmd.h linux-4.9/include/uapi/vserver/limit_cmd.h
+--- linux-4.9/include/uapi/vserver/limit_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/limit_cmd.h 2021-02-24 15:47:45.104409971 +0100
@@ -0,0 +1,40 @@
+#ifndef _UAPI_VS_LIMIT_CMD_H
+#define _UAPI_VS_LIMIT_CMD_H
+#define CRLIM_KEEP (~1ULL)
+
+#endif /* _UAPI_VS_LIMIT_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/limit.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/limit.h
---- linux-4.9.217/include/uapi/vserver/limit.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/limit.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,14 @@
-+#ifndef _UAPI_VS_LIMIT_H
-+#define _UAPI_VS_LIMIT_H
-+
-+
-+#define VLIMIT_NSOCK 16
-+#define VLIMIT_OPENFD 17
-+#define VLIMIT_ANON 18
-+#define VLIMIT_SHMEM 19
-+#define VLIMIT_SEMARY 20
-+#define VLIMIT_NSEMS 21
-+#define VLIMIT_DENTRY 22
-+#define VLIMIT_MAPPED 23
-+
-+#endif /* _UAPI_VS_LIMIT_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/monitor.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/monitor.h
---- linux-4.9.217/include/uapi/vserver/monitor.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/monitor.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/monitor.h linux-4.9/include/uapi/vserver/monitor.h
+--- linux-4.9/include/uapi/vserver/monitor.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/monitor.h 2021-02-24 15:47:45.104409971 +0100
@@ -0,0 +1,96 @@
+#ifndef _UAPI_VS_MONITOR_H
+#define _UAPI_VS_MONITOR_H
+};
+
+#endif /* _UAPI_VS_MONITOR_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/network_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/network_cmd.h
---- linux-4.9.217/include/uapi/vserver/network_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/network_cmd.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/network.h linux-4.9/include/uapi/vserver/network.h
+--- linux-4.9/include/uapi/vserver/network.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/network.h 2021-02-24 15:47:45.104409971 +0100
+@@ -0,0 +1,76 @@
++#ifndef _UAPI_VS_NETWORK_H
++#define _UAPI_VS_NETWORK_H
++
++#include <linux/types.h>
++
++
++#define MAX_N_CONTEXT 65535 /* Arbitrary limit */
++
++
++/* network flags */
++
++#define NXF_INFO_PRIVATE 0x00000008
++
++#define NXF_SINGLE_IP 0x00000100
++#define NXF_LBACK_REMAP 0x00000200
++#define NXF_LBACK_ALLOW 0x00000400
++
++#define NXF_HIDE_NETIF 0x02000000
++#define NXF_HIDE_LBACK 0x04000000
++
++#define NXF_STATE_SETUP (1ULL << 32)
++#define NXF_STATE_ADMIN (1ULL << 34)
++
++#define NXF_SC_HELPER (1ULL << 36)
++#define NXF_PERSISTENT (1ULL << 38)
++
++#define NXF_ONE_TIME (0x0005ULL << 32)
++
++
++#define NXF_INIT_SET (__nxf_init_set())
++
++static inline uint64_t __nxf_init_set(void) {
++ return NXF_STATE_ADMIN
++#ifdef CONFIG_VSERVER_AUTO_LBACK
++ | NXF_LBACK_REMAP
++ | NXF_HIDE_LBACK
++#endif
++#ifdef CONFIG_VSERVER_AUTO_SINGLE
++ | NXF_SINGLE_IP
++#endif
++ | NXF_HIDE_NETIF;
++}
++
++
++/* network caps */
++
++#define NXC_TUN_CREATE 0x00000001
++
++#define NXC_RAW_ICMP 0x00000100
++
++#define NXC_MULTICAST 0x00001000
++
++
++/* address types */
++
++#define NXA_TYPE_IPV4 0x0001
++#define NXA_TYPE_IPV6 0x0002
++
++#define NXA_TYPE_NONE 0x0000
++#define NXA_TYPE_ANY 0x00FF
++
++#define NXA_TYPE_ADDR 0x0010
++#define NXA_TYPE_MASK 0x0020
++#define NXA_TYPE_RANGE 0x0040
++
++#define NXA_MASK_ALL (NXA_TYPE_ADDR | NXA_TYPE_MASK | NXA_TYPE_RANGE)
++
++#define NXA_MOD_BCAST 0x0100
++#define NXA_MOD_LBACK 0x0200
++
++#define NXA_LOOPBACK 0x1000
++
++#define NXA_MASK_BIND (NXA_MASK_ALL | NXA_MOD_BCAST | NXA_MOD_LBACK)
++#define NXA_MASK_SHOW (NXA_MASK_ALL | NXA_LOOPBACK)
++
++#endif /* _UAPI_VS_NETWORK_H */
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/network_cmd.h linux-4.9/include/uapi/vserver/network_cmd.h
+--- linux-4.9/include/uapi/vserver/network_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/network_cmd.h 2021-02-24 15:47:45.104409971 +0100
@@ -0,0 +1,123 @@
+#ifndef _UAPI_VS_NETWORK_CMD_H
+#define _UAPI_VS_NETWORK_CMD_H
+};
+
+#endif /* _UAPI_VS_NETWORK_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/network.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/network.h
---- linux-4.9.217/include/uapi/vserver/network.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/network.h 2018-10-20 04:58:14.000000000 +0000
-@@ -0,0 +1,76 @@
-+#ifndef _UAPI_VS_NETWORK_H
-+#define _UAPI_VS_NETWORK_H
-+
-+#include <linux/types.h>
-+
-+
-+#define MAX_N_CONTEXT 65535 /* Arbitrary limit */
-+
-+
-+/* network flags */
-+
-+#define NXF_INFO_PRIVATE 0x00000008
-+
-+#define NXF_SINGLE_IP 0x00000100
-+#define NXF_LBACK_REMAP 0x00000200
-+#define NXF_LBACK_ALLOW 0x00000400
-+
-+#define NXF_HIDE_NETIF 0x02000000
-+#define NXF_HIDE_LBACK 0x04000000
-+
-+#define NXF_STATE_SETUP (1ULL << 32)
-+#define NXF_STATE_ADMIN (1ULL << 34)
-+
-+#define NXF_SC_HELPER (1ULL << 36)
-+#define NXF_PERSISTENT (1ULL << 38)
-+
-+#define NXF_ONE_TIME (0x0005ULL << 32)
-+
-+
-+#define NXF_INIT_SET (__nxf_init_set())
-+
-+static inline uint64_t __nxf_init_set(void) {
-+ return NXF_STATE_ADMIN
-+#ifdef CONFIG_VSERVER_AUTO_LBACK
-+ | NXF_LBACK_REMAP
-+ | NXF_HIDE_LBACK
-+#endif
-+#ifdef CONFIG_VSERVER_AUTO_SINGLE
-+ | NXF_SINGLE_IP
-+#endif
-+ | NXF_HIDE_NETIF;
-+}
-+
-+
-+/* network caps */
-+
-+#define NXC_TUN_CREATE 0x00000001
-+
-+#define NXC_RAW_ICMP 0x00000100
-+
-+#define NXC_MULTICAST 0x00001000
-+
-+
-+/* address types */
-+
-+#define NXA_TYPE_IPV4 0x0001
-+#define NXA_TYPE_IPV6 0x0002
-+
-+#define NXA_TYPE_NONE 0x0000
-+#define NXA_TYPE_ANY 0x00FF
-+
-+#define NXA_TYPE_ADDR 0x0010
-+#define NXA_TYPE_MASK 0x0020
-+#define NXA_TYPE_RANGE 0x0040
-+
-+#define NXA_MASK_ALL (NXA_TYPE_ADDR | NXA_TYPE_MASK | NXA_TYPE_RANGE)
-+
-+#define NXA_MOD_BCAST 0x0100
-+#define NXA_MOD_LBACK 0x0200
-+
-+#define NXA_LOOPBACK 0x1000
-+
-+#define NXA_MASK_BIND (NXA_MASK_ALL | NXA_MOD_BCAST | NXA_MOD_LBACK)
-+#define NXA_MASK_SHOW (NXA_MASK_ALL | NXA_LOOPBACK)
-+
-+#endif /* _UAPI_VS_NETWORK_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/sched_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/sched_cmd.h
---- linux-4.9.217/include/uapi/vserver/sched_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/sched_cmd.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/sched_cmd.h linux-4.9/include/uapi/vserver/sched_cmd.h
+--- linux-4.9/include/uapi/vserver/sched_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/sched_cmd.h 2021-02-24 15:47:45.104409971 +0100
@@ -0,0 +1,13 @@
+#ifndef _UAPI_VS_SCHED_CMD_H
+#define _UAPI_VS_SCHED_CMD_H
+#define VCMD_get_prio_bias VC_CMD(SCHED, 5, 0)
+
+#endif /* _UAPI_VS_SCHED_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/signal_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/signal_cmd.h
---- linux-4.9.217/include/uapi/vserver/signal_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/signal_cmd.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/signal_cmd.h linux-4.9/include/uapi/vserver/signal_cmd.h
+--- linux-4.9/include/uapi/vserver/signal_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/signal_cmd.h 2021-02-24 15:47:45.107743409 +0100
@@ -0,0 +1,31 @@
+#ifndef _UAPI_VS_SIGNAL_CMD_H
+#define _UAPI_VS_SIGNAL_CMD_H
+};
+
+#endif /* _UAPI_VS_SIGNAL_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/space_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/space_cmd.h
---- linux-4.9.217/include/uapi/vserver/space_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/space_cmd.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/space_cmd.h linux-4.9/include/uapi/vserver/space_cmd.h
+--- linux-4.9/include/uapi/vserver/space_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/space_cmd.h 2021-02-24 15:47:45.107743409 +0100
@@ -0,0 +1,28 @@
+#ifndef _UAPI_VS_SPACE_CMD_H
+#define _UAPI_VS_SPACE_CMD_H
+};
+
+#endif /* _UAPI_VS_SPACE_CMD_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/switch.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/switch.h
---- linux-4.9.217/include/uapi/vserver/switch.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/switch.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/switch.h linux-4.9/include/uapi/vserver/switch.h
+--- linux-4.9/include/uapi/vserver/switch.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/switch.h 2021-02-24 15:47:45.107743409 +0100
@@ -0,0 +1,90 @@
+#ifndef _UAPI_VS_SWITCH_H
+#define _UAPI_VS_SWITCH_H
+#define VCMD_get_vci VC_CMD(VERSION, 1, 0)
+
+#endif /* _UAPI_VS_SWITCH_H */
-diff -NurpP --minimal linux-4.9.217/include/uapi/vserver/tag_cmd.h linux-4.9.217-vs2.3.9.12/include/uapi/vserver/tag_cmd.h
---- linux-4.9.217/include/uapi/vserver/tag_cmd.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/include/uapi/vserver/tag_cmd.h 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/include/uapi/vserver/tag_cmd.h linux-4.9/include/uapi/vserver/tag_cmd.h
+--- linux-4.9/include/uapi/vserver/tag_cmd.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/include/uapi/vserver/tag_cmd.h 2021-02-24 15:47:45.107743409 +0100
@@ -0,0 +1,14 @@
+#ifndef _UAPI_VS_TAG_CMD_H
+#define _UAPI_VS_TAG_CMD_H
+#define VCMD_tag_migrate VC_CMD(TAGMIG, 1, 0)
+
+#endif /* _UAPI_VS_TAG_CMD_H */
-diff -NurpP --minimal linux-4.9.217/init/Kconfig linux-4.9.217-vs2.3.9.12/init/Kconfig
---- linux-4.9.217/init/Kconfig 2020-03-27 00:51:42.790148377 +0000
-+++ linux-4.9.217-vs2.3.9.12/init/Kconfig 2018-10-20 04:58:14.000000000 +0000
-@@ -958,6 +958,7 @@ config NUMA_BALANCING_DEFAULT_ENABLED
+diff -urNp -x '*.orig' linux-4.9/init/Kconfig linux-4.9/init/Kconfig
+--- linux-4.9/init/Kconfig 2021-02-24 15:47:32.454014219 +0100
++++ linux-4.9/init/Kconfig 2021-02-24 15:47:45.107743409 +0100
+@@ -959,6 +959,7 @@ config NUMA_BALANCING_DEFAULT_ENABLED
menuconfig CGROUPS
bool "Control Group support"
select KERNFS
help
This option adds support for grouping sets of processes together, for
use with process control subsystems such as Cpusets, CFS, memory
-diff -NurpP --minimal linux-4.9.217/init/main.c linux-4.9.217-vs2.3.9.12/init/main.c
---- linux-4.9.217/init/main.c 2020-03-27 00:51:42.800148216 +0000
-+++ linux-4.9.217-vs2.3.9.12/init/main.c 2019-10-05 14:58:45.760306119 +0000
+diff -urNp -x '*.orig' linux-4.9/init/main.c linux-4.9/init/main.c
+--- linux-4.9/init/main.c 2021-02-24 15:47:32.454014219 +0100
++++ linux-4.9/init/main.c 2021-02-24 15:47:45.107743409 +0100
@@ -82,6 +82,7 @@
#include <linux/io.h>
#include <linux/kaiser.h>
#include <asm/io.h>
#include <asm/bugs.h>
-diff -NurpP --minimal linux-4.9.217/ipc/mqueue.c linux-4.9.217-vs2.3.9.12/ipc/mqueue.c
---- linux-4.9.217/ipc/mqueue.c 2020-03-27 00:51:42.820147901 +0000
-+++ linux-4.9.217-vs2.3.9.12/ipc/mqueue.c 2019-10-05 15:01:19.537840241 +0000
+diff -urNp -x '*.orig' linux-4.9/ipc/mqueue.c linux-4.9/ipc/mqueue.c
+--- linux-4.9/ipc/mqueue.c 2021-02-24 15:47:32.454014219 +0100
++++ linux-4.9/ipc/mqueue.c 2021-02-24 15:47:45.107743409 +0100
@@ -35,6 +35,8 @@
#include <linux/ipc_namespace.h>
#include <linux/user_namespace.h>
free_uid(user);
}
if (ipc_ns)
-diff -NurpP --minimal linux-4.9.217/ipc/msg.c linux-4.9.217-vs2.3.9.12/ipc/msg.c
---- linux-4.9.217/ipc/msg.c 2020-03-27 00:51:42.850147429 +0000
-+++ linux-4.9.217-vs2.3.9.12/ipc/msg.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/ipc/msg.c linux-4.9/ipc/msg.c
+--- linux-4.9/ipc/msg.c 2021-02-24 15:47:32.454014219 +0100
++++ linux-4.9/ipc/msg.c 2021-02-24 15:47:45.107743409 +0100
@@ -37,6 +37,7 @@
#include <linux/rwsem.h>
#include <linux/nsproxy.h>
msq->q_perm.security = NULL;
retval = security_msg_queue_alloc(msq);
-diff -NurpP --minimal linux-4.9.217/ipc/namespace.c linux-4.9.217-vs2.3.9.12/ipc/namespace.c
---- linux-4.9.217/ipc/namespace.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/ipc/namespace.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/ipc/namespace.c linux-4.9/ipc/namespace.c
+--- linux-4.9/ipc/namespace.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/ipc/namespace.c 2021-02-24 15:47:45.107743409 +0100
@@ -13,6 +13,7 @@
#include <linux/mount.h>
#include <linux/user_namespace.h>
kfree(ns);
}
-diff -NurpP --minimal linux-4.9.217/ipc/sem.c linux-4.9.217-vs2.3.9.12/ipc/sem.c
---- linux-4.9.217/ipc/sem.c 2020-03-27 00:51:42.870147113 +0000
-+++ linux-4.9.217-vs2.3.9.12/ipc/sem.c 2020-04-01 09:40:32.455401832 +0000
+diff -urNp -x '*.orig' linux-4.9/ipc/sem.c linux-4.9/ipc/sem.c
+--- linux-4.9/ipc/sem.c 2021-02-24 15:47:32.454014219 +0100
++++ linux-4.9/ipc/sem.c 2021-02-24 15:47:45.107743409 +0100
@@ -85,6 +85,8 @@
#include <linux/rwsem.h>
#include <linux/nsproxy.h>
ipc_rcu_putref(sma, sem_rcu_free);
}
-diff -NurpP --minimal linux-4.9.217/ipc/shm.c linux-4.9.217-vs2.3.9.12/ipc/shm.c
---- linux-4.9.217/ipc/shm.c 2020-03-27 00:51:42.870147113 +0000
-+++ linux-4.9.217-vs2.3.9.12/ipc/shm.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/ipc/shm.c linux-4.9/ipc/shm.c
+--- linux-4.9/ipc/shm.c 2021-02-24 15:47:32.454014219 +0100
++++ linux-4.9/ipc/shm.c 2021-02-24 15:47:45.107743409 +0100
@@ -42,6 +42,8 @@
#include <linux/nsproxy.h>
#include <linux/mount.h>
return error;
no_id:
-diff -NurpP --minimal linux-4.9.217/kernel/auditsc.c linux-4.9.217-vs2.3.9.12/kernel/auditsc.c
---- linux-4.9.217/kernel/auditsc.c 2020-03-27 00:51:42.950145853 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/auditsc.c 2019-12-25 15:37:52.348423568 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/Makefile linux-4.9/kernel/Makefile
+--- linux-4.9/kernel/Makefile 2021-02-24 15:47:32.454014219 +0100
++++ linux-4.9/kernel/Makefile 2021-02-24 15:47:45.111076846 +0100
+@@ -40,6 +40,7 @@ obj-y += printk/
+ obj-y += irq/
+ obj-y += rcu/
+ obj-y += livepatch/
++obj-y += vserver/
+
+ obj-$(CONFIG_CHECKPOINT_RESTORE) += kcmp.o
+ obj-$(CONFIG_FREEZER) += freezer.o
+diff -urNp -x '*.orig' linux-4.9/kernel/auditsc.c linux-4.9/kernel/auditsc.c
+--- linux-4.9/kernel/auditsc.c 2021-02-24 15:47:32.457347657 +0100
++++ linux-4.9/kernel/auditsc.c 2021-02-24 15:47:45.107743409 +0100
@@ -1967,7 +1967,7 @@ static int audit_set_loginuid_perm(kuid_
if (is_audit_feature_set(AUDIT_FEATURE_LOGINUID_IMMUTABLE))
return -EPERM;
return -EPERM;
/* reject if this is not an unset and we don't allow that */
if (is_audit_feature_set(AUDIT_FEATURE_ONLY_UNSET_LOGINUID) && uid_valid(loginuid))
-diff -NurpP --minimal linux-4.9.217/kernel/capability.c linux-4.9.217-vs2.3.9.12/kernel/capability.c
---- linux-4.9.217/kernel/capability.c 2020-03-27 00:51:43.080143807 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/capability.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/capability.c linux-4.9/kernel/capability.c
+--- linux-4.9/kernel/capability.c 2021-02-24 15:47:32.460681095 +0100
++++ linux-4.9/kernel/capability.c 2021-02-24 15:47:45.111076846 +0100
@@ -17,6 +17,7 @@
#include <linux/syscalls.h>
#include <linux/pid_namespace.h>
/**
* has_capability_noaudit - Does a task have a capability (unaudited) in the
* initial user ns
-diff -NurpP --minimal linux-4.9.217/kernel/compat.c linux-4.9.217-vs2.3.9.12/kernel/compat.c
---- linux-4.9.217/kernel/compat.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/compat.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/compat.c linux-4.9/kernel/compat.c
+--- linux-4.9/kernel/compat.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/kernel/compat.c 2021-02-24 15:47:45.111076846 +0100
@@ -27,6 +27,7 @@
#include <linux/times.h>
#include <linux/ptrace.h>
return 0;
}
-diff -NurpP --minimal linux-4.9.217/kernel/cred.c linux-4.9.217-vs2.3.9.12/kernel/cred.c
---- linux-4.9.217/kernel/cred.c 2020-03-27 00:51:43.110143330 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/cred.c 2020-04-01 09:40:32.475401502 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/cred.c linux-4.9/kernel/cred.c
+--- linux-4.9/kernel/cred.c 2021-02-24 15:47:32.460681095 +0100
++++ linux-4.9/kernel/cred.c 2021-02-24 15:47:45.111076846 +0100
@@ -64,31 +64,6 @@ struct cred init_cred = {
.group_info = &init_groups,
};
EXPORT_SYMBOL(prepare_creds);
/*
-diff -NurpP --minimal linux-4.9.217/kernel/exit.c linux-4.9.217-vs2.3.9.12/kernel/exit.c
---- linux-4.9.217/kernel/exit.c 2020-03-27 00:51:43.250141127 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/exit.c 2019-02-24 12:44:58.353750946 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/exit.c linux-4.9/kernel/exit.c
+--- linux-4.9/kernel/exit.c 2021-02-24 15:47:32.467347970 +0100
++++ linux-4.9/kernel/exit.c 2021-02-24 15:47:45.111076846 +0100
@@ -48,6 +48,10 @@
#include <linux/fs_struct.h>
#include <linux/init_task.h>
#include <trace/events/sched.h>
#include <linux/hw_breakpoint.h>
#include <linux/oom.h>
-@@ -532,15 +536,25 @@ static struct task_struct *find_child_re
+@@ -535,15 +539,25 @@ static struct task_struct *find_child_re
{
struct pid_namespace *pid_ns = task_active_pid_ns(father);
struct task_struct *reaper = pid_ns->child_reaper;
}
write_unlock_irq(&tasklist_lock);
-@@ -557,7 +571,10 @@ static struct task_struct *find_child_re
+@@ -560,7 +574,10 @@ static struct task_struct *find_child_re
zap_pid_ns_processes(pid_ns);
write_lock_irq(&tasklist_lock);
}
/*
-@@ -645,9 +662,13 @@ static void forget_original_parent(struc
+@@ -648,9 +665,13 @@ static void forget_original_parent(struc
return;
reaper = find_new_reaper(father, reaper);
BUG_ON((!t->ptrace) != (t->parent == father));
if (likely(!t->ptrace))
t->parent = t->real_parent;
-@@ -659,10 +680,13 @@ static void forget_original_parent(struc
+@@ -662,10 +683,13 @@ static void forget_original_parent(struc
* If this is a threaded reparent there is no need to
* notify anyone anything has happened.
*/
}
/*
-@@ -852,6 +876,9 @@ void __noreturn do_exit(long code)
+@@ -843,6 +867,9 @@ void __noreturn do_exit(long code)
*/
flush_ptrace_hw_breakpoint(tsk);
TASKS_RCU(preempt_disable());
TASKS_RCU(tasks_rcu_i = __srcu_read_lock(&tasks_rcu_exit_srcu));
TASKS_RCU(preempt_enable());
-@@ -884,6 +911,10 @@ void __noreturn do_exit(long code)
+@@ -869,6 +896,10 @@ void __noreturn do_exit(long code)
validate_creds_for_do_exit(tsk);
check_stack_usage();
preempt_disable();
if (tsk->nr_dirtied)
-diff -NurpP --minimal linux-4.9.217/kernel/fork.c linux-4.9.217-vs2.3.9.12/kernel/fork.c
---- linux-4.9.217/kernel/fork.c 2020-03-27 00:51:43.250141127 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/fork.c 2019-10-22 13:47:05.619629084 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/fork.c linux-4.9/kernel/fork.c
+--- linux-4.9/kernel/fork.c 2021-02-24 15:47:32.467347970 +0100
++++ linux-4.9/kernel/fork.c 2021-02-24 15:47:45.111076846 +0100
@@ -77,6 +77,9 @@
#include <linux/compiler.h>
#include <linux/sysctl.h>
ftrace_graph_exit_task(tsk);
put_seccomp_filter(tsk);
arch_release_task_struct(tsk);
-@@ -1480,6 +1485,8 @@ static __latent_entropy struct task_stru
+@@ -1476,6 +1481,8 @@ static __latent_entropy struct task_stru
{
int retval;
struct task_struct *p;
if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL);
-@@ -1552,7 +1559,12 @@ static __latent_entropy struct task_stru
+@@ -1548,7 +1555,12 @@ static __latent_entropy struct task_stru
DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
if (atomic_read(&p->real_cred->user->processes) >=
task_rlimit(p, RLIMIT_NPROC)) {
if (p->real_cred->user != INIT_USER &&
-@@ -1853,6 +1865,18 @@ static __latent_entropy struct task_stru
+@@ -1843,6 +1855,18 @@ static __latent_entropy struct task_stru
total_forks++;
spin_unlock(¤t->sighand->siglock);
syscall_tracepoint_update(p);
write_unlock_irq(&tasklist_lock);
proc_fork_connector(p);
-diff -NurpP --minimal linux-4.9.217/kernel/kthread.c linux-4.9.217-vs2.3.9.12/kernel/kthread.c
---- linux-4.9.217/kernel/kthread.c 2020-03-27 00:51:43.440138133 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/kthread.c 2018-10-20 05:55:43.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/kthread.c linux-4.9/kernel/kthread.c
+--- linux-4.9/kernel/kthread.c 2021-02-24 15:47:32.474014845 +0100
++++ linux-4.9/kernel/kthread.c 2021-02-24 15:47:45.111076846 +0100
@@ -19,6 +19,7 @@
#include <linux/ptrace.h>
#include <linux/uaccess.h>
#include <linux/cgroup.h>
+#include <linux/vs_pid.h>
#include <trace/events/sched.h>
-
- static DEFINE_SPINLOCK(kthread_create_lock);
-diff -NurpP --minimal linux-4.9.217/kernel/Makefile linux-4.9.217-vs2.3.9.12/kernel/Makefile
---- linux-4.9.217/kernel/Makefile 2020-03-27 00:51:42.890146797 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/Makefile 2019-10-05 14:58:46.030301805 +0000
-@@ -40,6 +40,7 @@ obj-y += printk/
- obj-y += irq/
- obj-y += rcu/
- obj-y += livepatch/
-+obj-y += vserver/
-
- obj-$(CONFIG_CHECKPOINT_RESTORE) += kcmp.o
- obj-$(CONFIG_FREEZER) += freezer.o
-diff -NurpP --minimal linux-4.9.217/kernel/nsproxy.c linux-4.9.217-vs2.3.9.12/kernel/nsproxy.c
---- linux-4.9.217/kernel/nsproxy.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/nsproxy.c 2018-10-20 04:58:14.000000000 +0000
+
+ static DEFINE_SPINLOCK(kthread_create_lock);
+diff -urNp -x '*.orig' linux-4.9/kernel/nsproxy.c linux-4.9/kernel/nsproxy.c
+--- linux-4.9/kernel/nsproxy.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/kernel/nsproxy.c 2021-02-24 15:47:45.111076846 +0100
@@ -20,12 +20,15 @@
#include <linux/mnt_namespace.h>
#include <linux/utsname.h>
return -EPERM;
*new_nsp = create_new_namespaces(unshare_flags, current, user_ns,
-diff -NurpP --minimal linux-4.9.217/kernel/pid.c linux-4.9.217-vs2.3.9.12/kernel/pid.c
---- linux-4.9.217/kernel/pid.c 2020-03-27 00:51:43.530136712 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/pid.c 2018-10-20 04:58:14.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/pid.c linux-4.9/kernel/pid.c
+--- linux-4.9/kernel/pid.c 2021-02-24 15:47:32.477348283 +0100
++++ linux-4.9/kernel/pid.c 2021-02-24 15:47:45.111076846 +0100
@@ -38,6 +38,7 @@
#include <linux/syscalls.h>
#include <linux/proc_ns.h>
pid_t pid_vnr(struct pid *pid)
{
return pid_nr_ns(pid, task_active_pid_ns(current));
-diff -NurpP --minimal linux-4.9.217/kernel/pid_namespace.c linux-4.9.217-vs2.3.9.12/kernel/pid_namespace.c
---- linux-4.9.217/kernel/pid_namespace.c 2020-03-27 00:51:43.530136712 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/pid_namespace.c 2019-10-05 14:58:46.030301805 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/pid_namespace.c linux-4.9/kernel/pid_namespace.c
+--- linux-4.9/kernel/pid_namespace.c 2021-02-24 15:47:32.477348283 +0100
++++ linux-4.9/kernel/pid_namespace.c 2021-02-24 15:47:45.111076846 +0100
@@ -18,6 +18,7 @@
#include <linux/proc_ns.h>
#include <linux/reboot.h>
kmem_cache_free(pid_ns_cachep, ns);
out_dec:
dec_pid_namespaces(ucounts);
-diff -NurpP --minimal linux-4.9.217/kernel/printk/printk.c linux-4.9.217-vs2.3.9.12/kernel/printk/printk.c
---- linux-4.9.217/kernel/printk/printk.c 2020-03-27 00:51:43.630135138 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/printk/printk.c 2019-12-25 15:37:52.568420017 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/printk/printk.c linux-4.9/kernel/printk/printk.c
+--- linux-4.9/kernel/printk/printk.c 2021-02-24 15:47:32.480681720 +0100
++++ linux-4.9/kernel/printk/printk.c 2021-02-24 15:47:45.114410284 +0100
@@ -45,6 +45,7 @@
#include <linux/utsname.h>
#include <linux/ctype.h>
error = syslog_print_all(buf, len, clear);
break;
/* Clear ring buffer */
-diff -NurpP --minimal linux-4.9.217/kernel/ptrace.c linux-4.9.217-vs2.3.9.12/kernel/ptrace.c
---- linux-4.9.217/kernel/ptrace.c 2020-03-27 00:51:43.630135138 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/ptrace.c 2019-10-05 14:59:56.929168738 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/ptrace.c linux-4.9/kernel/ptrace.c
+--- linux-4.9/kernel/ptrace.c 2021-02-24 15:47:32.480681720 +0100
++++ linux-4.9/kernel/ptrace.c 2021-02-24 15:47:45.114410284 +0100
@@ -23,6 +23,7 @@
#include <linux/syscalls.h>
#include <linux/uaccess.h>
if (mode & PTRACE_MODE_SCHED)
return 0;
return security_ptrace_access_check(task, mode);
-diff -NurpP --minimal linux-4.9.217/kernel/reboot.c linux-4.9.217-vs2.3.9.12/kernel/reboot.c
---- linux-4.9.217/kernel/reboot.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/reboot.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/reboot.c linux-4.9/kernel/reboot.c
+--- linux-4.9/kernel/reboot.c 2021-02-24 15:47:32.484015158 +0100
++++ linux-4.9/kernel/reboot.c 2021-02-24 15:47:45.114410284 +0100
@@ -16,6 +16,7 @@
#include <linux/syscalls.h>
#include <linux/syscore_ops.h>
mutex_lock(&reboot_mutex);
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
-diff -NurpP --minimal linux-4.9.217/kernel/sched/core.c linux-4.9.217-vs2.3.9.12/kernel/sched/core.c
---- linux-4.9.217/kernel/sched/core.c 2020-03-27 00:51:43.660134665 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/sched/core.c 2019-10-22 13:47:05.639628767 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/sched/core.c linux-4.9/kernel/sched/core.c
+--- linux-4.9/kernel/sched/core.c 2021-02-24 15:47:32.484015158 +0100
++++ linux-4.9/kernel/sched/core.c 2021-02-24 15:47:45.114410284 +0100
@@ -75,6 +75,8 @@
#include <linux/compiler.h>
#include <linux/frame.h>
BUG();
/* Avoid "noreturn function does return". */
for (;;)
-@@ -3824,7 +3827,7 @@ SYSCALL_DEFINE1(nice, int, increment)
+@@ -3825,7 +3828,7 @@ SYSCALL_DEFINE1(nice, int, increment)
nice = clamp_val(nice, MIN_NICE, MAX_NICE);
if (increment < 0 && !can_nice(current, nice))
retval = security_task_setnice(current, nice);
if (retval)
-diff -NurpP --minimal linux-4.9.217/kernel/sched/cputime.c linux-4.9.217-vs2.3.9.12/kernel/sched/cputime.c
---- linux-4.9.217/kernel/sched/cputime.c 2020-03-27 00:51:43.690134189 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/sched/cputime.c 2018-10-20 11:46:17.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/sched/cputime.c linux-4.9/kernel/sched/cputime.c
+--- linux-4.9/kernel/sched/cputime.c 2021-02-24 15:47:32.484015158 +0100
++++ linux-4.9/kernel/sched/cputime.c 2021-02-24 15:47:45.114410284 +0100
@@ -4,6 +4,7 @@
#include <linux/kernel_stat.h>
#include <linux/static_key.h>
account_group_system_time(p, cputime);
/* Add system time to cpustat. */
-diff -NurpP --minimal linux-4.9.217/kernel/sched/fair.c linux-4.9.217-vs2.3.9.12/kernel/sched/fair.c
---- linux-4.9.217/kernel/sched/fair.c 2020-03-27 00:51:43.690134189 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/sched/fair.c 2019-12-25 15:37:52.578419856 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/sched/fair.c linux-4.9/kernel/sched/fair.c
+--- linux-4.9/kernel/sched/fair.c 2021-02-24 15:47:32.487348596 +0100
++++ linux-4.9/kernel/sched/fair.c 2021-02-24 15:47:45.117743722 +0100
@@ -30,6 +30,7 @@
#include <linux/mempolicy.h>
#include <linux/migrate.h>
account_entity_dequeue(cfs_rq, se);
/*
-diff -NurpP --minimal linux-4.9.217/kernel/sched/loadavg.c linux-4.9.217-vs2.3.9.12/kernel/sched/loadavg.c
---- linux-4.9.217/kernel/sched/loadavg.c 2020-03-27 00:51:43.690134189 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/sched/loadavg.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/sched/loadavg.c linux-4.9/kernel/sched/loadavg.c
+--- linux-4.9/kernel/sched/loadavg.c 2021-02-24 15:47:32.487348596 +0100
++++ linux-4.9/kernel/sched/loadavg.c 2021-02-24 15:47:45.117743722 +0100
@@ -73,9 +73,16 @@ EXPORT_SYMBOL(avenrun); /* should be rem
*/
void get_avenrun(unsigned long *loads, unsigned long offset, int shift)
}
long calc_load_fold_active(struct rq *this_rq, long adjust)
-diff -NurpP --minimal linux-4.9.217/kernel/signal.c linux-4.9.217-vs2.3.9.12/kernel/signal.c
---- linux-4.9.217/kernel/signal.c 2020-03-27 00:51:43.700134034 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/signal.c 2020-04-01 09:40:32.625399016 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/signal.c linux-4.9/kernel/signal.c
+--- linux-4.9/kernel/signal.c 2021-02-24 15:47:32.490682033 +0100
++++ linux-4.9/kernel/signal.c 2021-02-24 15:47:45.117743722 +0100
@@ -34,6 +34,8 @@
#include <linux/compat.h>
#include <linux/cn_proc.h>
if (sig_kernel_stop(signr)) {
/*
* The default action is to stop all threads in
-diff -NurpP --minimal linux-4.9.217/kernel/softirq.c linux-4.9.217-vs2.3.9.12/kernel/softirq.c
---- linux-4.9.217/kernel/softirq.c 2020-03-27 00:51:43.710133873 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/softirq.c 2018-10-20 05:55:43.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/softirq.c linux-4.9/kernel/softirq.c
+--- linux-4.9/kernel/softirq.c 2021-02-24 15:47:32.490682033 +0100
++++ linux-4.9/kernel/softirq.c 2021-02-24 15:47:45.117743722 +0100
@@ -26,6 +26,7 @@
#include <linux/smpboot.h>
#include <linux/tick.h>
#define CREATE_TRACE_POINTS
#include <trace/events/irq.h>
-diff -NurpP --minimal linux-4.9.217/kernel/sys.c linux-4.9.217-vs2.3.9.12/kernel/sys.c
---- linux-4.9.217/kernel/sys.c 2020-03-27 00:51:43.730133562 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/sys.c 2019-10-13 16:09:18.098106817 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/sys.c linux-4.9/kernel/sys.c
+--- linux-4.9/kernel/sys.c 2021-02-24 15:47:32.490682033 +0100
++++ linux-4.9/kernel/sys.c 2021-02-24 15:47:45.117743722 +0100
@@ -56,6 +56,7 @@
#include <linux/nospec.h>
if (uid_eq(task_uid(p), uid) && task_pid_vnr(p)) {
niceval = nice_to_rlimit(task_nice(p));
if (niceval > retval)
-@@ -1211,7 +1221,8 @@ SYSCALL_DEFINE2(sethostname, char __user
+@@ -1213,7 +1223,8 @@ SYSCALL_DEFINE2(sethostname, char __user
int errno;
char tmp[__NEW_UTS_LEN];
return -EPERM;
if (len < 0 || len > __NEW_UTS_LEN)
-@@ -1264,7 +1275,8 @@ SYSCALL_DEFINE2(setdomainname, char __us
+@@ -1266,7 +1277,8 @@ SYSCALL_DEFINE2(setdomainname, char __us
int errno;
char tmp[__NEW_UTS_LEN];
return -EPERM;
if (len < 0 || len > __NEW_UTS_LEN)
return -EINVAL;
-@@ -1384,7 +1396,7 @@ int do_prlimit(struct task_struct *tsk,
+@@ -1386,7 +1398,7 @@ int do_prlimit(struct task_struct *tsk,
/* Keep the capable check against init_user_ns until
cgroups can contain all limits */
if (new_rlim->rlim_max > rlim->rlim_max &&
retval = -EPERM;
if (!retval)
retval = security_task_setrlimit(tsk->group_leader,
-@@ -1437,7 +1449,8 @@ static int check_prlimit_permission(stru
+@@ -1439,7 +1451,8 @@ static int check_prlimit_permission(stru
gid_eq(cred->gid, tcred->sgid) &&
gid_eq(cred->gid, tcred->gid))
return 0;
return 0;
return -EPERM;
-@@ -2326,7 +2339,12 @@ static int do_sysinfo(struct sysinfo *in
+@@ -2328,7 +2341,12 @@ static int do_sysinfo(struct sysinfo *in
get_avenrun(info->loads, 0, SI_LOAD_SHIFT - FSHIFT);
si_meminfo(info);
si_swapinfo(info);
-diff -NurpP --minimal linux-4.9.217/kernel/sysctl_binary.c linux-4.9.217-vs2.3.9.12/kernel/sysctl_binary.c
---- linux-4.9.217/kernel/sysctl_binary.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/sysctl_binary.c 2018-10-20 04:58:15.000000000 +0000
-@@ -74,6 +74,7 @@ static const struct bin_table bin_kern_t
-
- { CTL_INT, KERN_PANIC, "panic" },
- { CTL_INT, KERN_REALROOTDEV, "real-root-dev" },
-+ { CTL_STR, KERN_VSHELPER, "vshelper" },
-
- { CTL_STR, KERN_SPARC_REBOOT, "reboot-cmd" },
- { CTL_INT, KERN_CTLALTDEL, "ctrl-alt-del" },
-diff -NurpP --minimal linux-4.9.217/kernel/sysctl.c linux-4.9.217-vs2.3.9.12/kernel/sysctl.c
---- linux-4.9.217/kernel/sysctl.c 2020-03-27 00:51:43.730133562 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/sysctl.c 2020-04-01 09:40:32.625399016 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/sysctl.c linux-4.9/kernel/sysctl.c
+--- linux-4.9/kernel/sysctl.c 2021-02-24 15:47:32.490682033 +0100
++++ linux-4.9/kernel/sysctl.c 2021-02-24 15:47:45.117743722 +0100
@@ -87,6 +87,7 @@
#if defined(CONFIG_PROVE_LOCKING) || defined(CONFIG_LOCK_STAT)
#include <linux/lockdep.h>
#endif /* CONFIG_COMPACTION */
{
.procname = "min_free_kbytes",
-diff -NurpP --minimal linux-4.9.217/kernel/time/posix-timers.c linux-4.9.217-vs2.3.9.12/kernel/time/posix-timers.c
---- linux-4.9.217/kernel/time/posix-timers.c 2020-03-27 00:51:43.770132929 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/time/posix-timers.c 2019-02-22 08:37:55.983044969 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/sysctl_binary.c linux-4.9/kernel/sysctl_binary.c
+--- linux-4.9/kernel/sysctl_binary.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/kernel/sysctl_binary.c 2021-02-24 15:47:45.117743722 +0100
+@@ -74,6 +74,7 @@ static const struct bin_table bin_kern_t
+
+ { CTL_INT, KERN_PANIC, "panic" },
+ { CTL_INT, KERN_REALROOTDEV, "real-root-dev" },
++ { CTL_STR, KERN_VSHELPER, "vshelper" },
+
+ { CTL_STR, KERN_SPARC_REBOOT, "reboot-cmd" },
+ { CTL_INT, KERN_CTLALTDEL, "ctrl-alt-del" },
+diff -urNp -x '*.orig' linux-4.9/kernel/time/posix-timers.c linux-4.9/kernel/time/posix-timers.c
+--- linux-4.9/kernel/time/posix-timers.c 2021-02-24 15:47:32.494015471 +0100
++++ linux-4.9/kernel/time/posix-timers.c 2021-02-24 15:47:45.117743722 +0100
@@ -48,6 +48,7 @@
#include <linux/workqueue.h>
#include <linux/export.h>
/* If we failed to send the signal the timer stops. */
return ret > 0;
}
-diff -NurpP --minimal linux-4.9.217/kernel/time/time.c linux-4.9.217-vs2.3.9.12/kernel/time/time.c
---- linux-4.9.217/kernel/time/time.c 2020-03-27 00:51:43.780132774 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/time/time.c 2018-10-20 05:55:43.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/time/time.c linux-4.9/kernel/time/time.c
+--- linux-4.9/kernel/time/time.c 2021-02-24 15:47:32.494015471 +0100
++++ linux-4.9/kernel/time/time.c 2021-02-24 15:47:45.117743722 +0100
@@ -38,6 +38,7 @@
#include <linux/fs.h>
#include <linux/math64.h>
return 0;
}
-diff -NurpP --minimal linux-4.9.217/kernel/time/timekeeping.c linux-4.9.217-vs2.3.9.12/kernel/time/timekeeping.c
---- linux-4.9.217/kernel/time/timekeeping.c 2020-03-27 00:51:43.790132613 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/time/timekeeping.c 2019-12-25 15:09:47.185439847 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/time/timekeeping.c linux-4.9/kernel/time/timekeeping.c
+--- linux-4.9/kernel/time/timekeeping.c 2021-02-24 15:47:32.494015471 +0100
++++ linux-4.9/kernel/time/timekeeping.c 2021-02-24 15:47:45.121077159 +0100
@@ -23,6 +23,8 @@
#include <linux/stop_machine.h>
#include <linux/pvclock_gtod.h>
return ktime_add_ns(base, nsecs);
}
-diff -NurpP --minimal linux-4.9.217/kernel/time/timer.c linux-4.9.217-vs2.3.9.12/kernel/time/timer.c
---- linux-4.9.217/kernel/time/timer.c 2020-03-27 00:51:43.790132613 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/time/timer.c 2019-10-22 13:47:05.659628449 +0000
-@@ -42,6 +42,10 @@
+diff -urNp -x '*.orig' linux-4.9/kernel/time/timer.c linux-4.9/kernel/time/timer.c
+--- linux-4.9/kernel/time/timer.c 2021-02-24 15:47:32.494015471 +0100
++++ linux-4.9/kernel/time/timer.c 2021-02-24 15:47:45.121077159 +0100
+@@ -43,6 +43,10 @@
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/random.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
-diff -NurpP --minimal linux-4.9.217/kernel/user_namespace.c linux-4.9.217-vs2.3.9.12/kernel/user_namespace.c
---- linux-4.9.217/kernel/user_namespace.c 2020-03-27 00:51:43.850131671 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/user_namespace.c 2018-10-20 05:55:43.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/user_namespace.c linux-4.9/kernel/user_namespace.c
+--- linux-4.9/kernel/user_namespace.c 2021-02-24 15:47:32.504015783 +0100
++++ linux-4.9/kernel/user_namespace.c 2021-02-24 15:47:45.121077159 +0100
@@ -22,6 +22,7 @@
#include <linux/ctype.h>
#include <linux/projid.h>
/**
* make_kprojid - Map a user-namespace projid pair into a kprojid.
* @ns: User namespace that the projid is in
-diff -NurpP --minimal linux-4.9.217/kernel/utsname.c linux-4.9.217-vs2.3.9.12/kernel/utsname.c
---- linux-4.9.217/kernel/utsname.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/utsname.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/utsname.c linux-4.9/kernel/utsname.c
+--- linux-4.9/kernel/utsname.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/kernel/utsname.c 2021-02-24 15:47:45.121077159 +0100
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/user_namespace.h>
kfree(ns);
}
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/cacct.c linux-4.9.217-vs2.3.9.12/kernel/vserver/cacct.c
---- linux-4.9.217/kernel/vserver/cacct.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/cacct.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/Kconfig linux-4.9/kernel/vserver/Kconfig
+--- linux-4.9/kernel/vserver/Kconfig 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/Kconfig 2021-02-24 15:47:45.124410596 +0100
+@@ -0,0 +1,230 @@
++#
++# Linux VServer configuration
++#
++
++menu "Linux VServer"
++
++config VSERVER_AUTO_LBACK
++ bool "Automatically Assign Loopback IP"
++ default y
++ help
++ Automatically assign a guest specific loopback
++ IP and add it to the kernel network stack on
++ startup.
++
++config VSERVER_AUTO_SINGLE
++ bool "Automatic Single IP Special Casing"
++ default n
++ help
++ This allows network contexts with a single IP to
++ automatically remap 0.0.0.0 bindings to that IP,
++ avoiding further network checks and improving
++ performance.
++
++ (note: such guests do not allow to change the ip
++ on the fly and do not show loopback addresses)
++
++config VSERVER_COWBL
++ bool "Enable COW Immutable Link Breaking"
++ default y
++ help
++ This enables the COW (Copy-On-Write) link break code.
++ It allows you to treat unified files like normal files
++ when writing to them (which will implicitely break the
++ link and create a copy of the unified file)
++
++config VSERVER_VTIME
++ bool "Enable Virtualized Guest Time (EXPERIMENTAL)"
++ default n
++ help
++ This enables per guest time offsets to allow for
++ adjusting the system clock individually per guest.
++ this adds some overhead to the time functions and
++ therefore should not be enabled without good reason.
++
++config VSERVER_DEVICE
++ bool "Enable Guest Device Mapping (EXPERIMENTAL)"
++ default n
++ help
++ This enables generic device remapping.
++
++config VSERVER_PROC_SECURE
++ bool "Enable Proc Security"
++ depends on PROC_FS
++ default y
++ help
++ This configures ProcFS security to initially hide
++ non-process entries for all contexts except the main and
++ spectator context (i.e. for all guests), which is a secure
++ default.
++
++ (note: on 1.2x the entries were visible by default)
++
++choice
++ prompt "Persistent Inode Tagging"
++ default TAGGING_ID24
++ help
++ This adds persistent context information to filesystems
++ mounted with the tagxid option. Tagging is a requirement
++ for per-context disk limits and per-context quota.
++
++
++config TAGGING_NONE
++ bool "Disabled"
++ help
++ do not store per-context information in inodes.
++
++config TAGGING_UID16
++ bool "UID16/GID32"
++ help
++ reduces UID to 16 bit, but leaves GID at 32 bit.
++
++config TAGGING_GID16
++ bool "UID32/GID16"
++ help
++ reduces GID to 16 bit, but leaves UID at 32 bit.
++
++config TAGGING_ID24
++ bool "UID24/GID24"
++ help
++ uses the upper 8bit from UID and GID for XID tagging
++ which leaves 24bit for UID/GID each, which should be
++ more than sufficient for normal use.
++
++config TAGGING_INTERN
++ bool "UID32/GID32"
++ help
++ this uses otherwise reserved inode fields in the on
++ disk representation, which limits the use to a few
++ filesystems (currently ext2 and ext3)
++
++endchoice
++
++config TAG_NFSD
++ bool "Tag NFSD User Auth and Files"
++ default n
++ help
++ Enable this if you do want the in-kernel NFS
++ Server to use the tagging specified above.
++ (will require patched clients too)
++
++config VSERVER_PRIVACY
++ bool "Honor Privacy Aspects of Guests"
++ default n
++ help
++ When enabled, most context checks will disallow
++ access to structures assigned to a specific context,
++ like ptys or loop devices.
++
++config VSERVER_CONTEXTS
++ int "Maximum number of Contexts (1-65533)" if EMBEDDED
++ range 1 65533
++ default "768" if 64BIT
++ default "256"
++ help
++ This setting will optimize certain data structures
++ and memory allocations according to the expected
++ maximum.
++
++ note: this is not a strict upper limit.
++
++config VSERVER_WARN
++ bool "VServer Warnings"
++ default y
++ help
++ This enables various runtime warnings, which will
++ notify about potential manipulation attempts or
++ resource shortage. It is generally considered to
++ be a good idea to have that enabled.
++
++config VSERVER_WARN_DEVPTS
++ bool "VServer DevPTS Warnings"
++ depends on VSERVER_WARN
++ default y
++ help
++ This enables DevPTS related warnings, issued when a
++ process inside a context tries to lookup or access
++ a dynamic pts from the host or a different context.
++
++config VSERVER_DEBUG
++ bool "VServer Debugging Code"
++ default n
++ help
++ Set this to yes if you want to be able to activate
++ debugging output at runtime. It adds a very small
++ overhead to all vserver related functions and
++ increases the kernel size by about 20k.
++
++config VSERVER_HISTORY
++ bool "VServer History Tracing"
++ depends on VSERVER_DEBUG
++ default n
++ help
++ Set this to yes if you want to record the history of
++ linux-vserver activities, so they can be replayed in
++ the event of a kernel panic or oops.
++
++config VSERVER_HISTORY_SIZE
++ int "Per-CPU History Size (32-65536)"
++ depends on VSERVER_HISTORY
++ range 32 65536
++ default 64
++ help
++ This allows you to specify the number of entries in
++ the per-CPU history buffer.
++
++config VSERVER_EXTRA_MNT_CHECK
++ bool "Extra Checks for Reachability"
++ default n
++ help
++ Set this to yes if you want to do extra checks for
++ vfsmount reachability in the proc filesystem code.
++ This shouldn't be required on any setup utilizing
++ mnt namespaces.
++
++choice
++ prompt "Quotes used in debug and warn messages"
++ default QUOTES_ISO8859
++
++config QUOTES_ISO8859
++ bool "Extended ASCII (ISO 8859) angle quotes"
++ help
++ This uses the extended ASCII characters \xbb
++ and \xab for quoting file and process names.
++
++config QUOTES_UTF8
++ bool "UTF-8 angle quotes"
++ help
++ This uses the the UTF-8 sequences for angle
++ quotes to quote file and process names.
++
++config QUOTES_ASCII
++ bool "ASCII single quotes"
++ help
++ This uses the ASCII single quote character
++ (\x27) to quote file and process names.
++
++endchoice
++
++endmenu
++
++
++config VSERVER
++ bool
++ default y
++ select NAMESPACES
++ select UTS_NS
++ select IPC_NS
++# select USER_NS
++ select SYSVIPC
++
++config VSERVER_SECURITY
++ bool
++ depends on SECURITY
++ default y
++ select SECURITY_CAPABILITIES
++
++config VSERVER_DISABLED
++ bool
++ default n
++
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/Makefile linux-4.9/kernel/vserver/Makefile
+--- linux-4.9/kernel/vserver/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/Makefile 2021-02-24 15:47:45.124410596 +0100
+@@ -0,0 +1,18 @@
++#
++# Makefile for the Linux vserver routines.
++#
++
++
++obj-y += vserver.o
++
++vserver-y := switch.o context.o space.o sched.o network.o inode.o \
++ limit.o cvirt.o cacct.o signal.o helper.o init.o \
++ dlimit.o tag.o
++
++vserver-$(CONFIG_INET) += inet.o
++vserver-$(CONFIG_PROC_FS) += proc.o
++vserver-$(CONFIG_VSERVER_DEBUG) += sysctl.o debug.o
++vserver-$(CONFIG_VSERVER_HISTORY) += history.o
++vserver-$(CONFIG_VSERVER_MONITOR) += monitor.o
++vserver-$(CONFIG_VSERVER_DEVICE) += device.o
++
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/cacct.c linux-4.9/kernel/vserver/cacct.c
+--- linux-4.9/kernel/vserver/cacct.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/cacct.c 2021-02-24 15:47:45.121077159 +0100
@@ -0,0 +1,42 @@
+/*
+ * linux/kernel/vserver/cacct.c
+ return 0;
+}
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/cacct_init.h linux-4.9.217-vs2.3.9.12/kernel/vserver/cacct_init.h
---- linux-4.9.217/kernel/vserver/cacct_init.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/cacct_init.h 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/cacct_init.h linux-4.9/kernel/vserver/cacct_init.h
+--- linux-4.9/kernel/vserver/cacct_init.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/cacct_init.h 2021-02-24 15:47:45.121077159 +0100
@@ -0,0 +1,25 @@
+
+
+ return;
+}
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/cacct_proc.h linux-4.9.217-vs2.3.9.12/kernel/vserver/cacct_proc.h
---- linux-4.9.217/kernel/vserver/cacct_proc.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/cacct_proc.h 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/cacct_proc.h linux-4.9/kernel/vserver/cacct_proc.h
+--- linux-4.9/kernel/vserver/cacct_proc.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/cacct_proc.h 2021-02-24 15:47:45.121077159 +0100
@@ -0,0 +1,53 @@
+#ifndef _VX_CACCT_PROC_H
+#define _VX_CACCT_PROC_H
+}
+
+#endif /* _VX_CACCT_PROC_H */
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/context.c linux-4.9.217-vs2.3.9.12/kernel/vserver/context.c
---- linux-4.9.217/kernel/vserver/context.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/context.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/context.c linux-4.9/kernel/vserver/context.c
+--- linux-4.9/kernel/vserver/context.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/context.c 2021-02-24 15:47:45.121077159 +0100
@@ -0,0 +1,1119 @@
+/*
+ * linux/kernel/vserver/context.c
+
+EXPORT_SYMBOL_GPL(free_vx_info);
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/cvirt.c linux-4.9.217-vs2.3.9.12/kernel/vserver/cvirt.c
---- linux-4.9.217/kernel/vserver/cvirt.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/cvirt.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/cvirt.c linux-4.9/kernel/vserver/cvirt.c
+--- linux-4.9/kernel/vserver/cvirt.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/cvirt.c 2021-02-24 15:47:45.121077159 +0100
@@ -0,0 +1,350 @@
+/*
+ * linux/kernel/vserver/cvirt.c
+
+#endif
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/cvirt_init.h linux-4.9.217-vs2.3.9.12/kernel/vserver/cvirt_init.h
---- linux-4.9.217/kernel/vserver/cvirt_init.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/cvirt_init.h 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/cvirt_init.h linux-4.9/kernel/vserver/cvirt_init.h
+--- linux-4.9/kernel/vserver/cvirt_init.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/cvirt_init.h 2021-02-24 15:47:45.121077159 +0100
@@ -0,0 +1,70 @@
+
+
+ return;
+}
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/cvirt_proc.h linux-4.9.217-vs2.3.9.12/kernel/vserver/cvirt_proc.h
---- linux-4.9.217/kernel/vserver/cvirt_proc.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/cvirt_proc.h 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/cvirt_proc.h linux-4.9/kernel/vserver/cvirt_proc.h
+--- linux-4.9/kernel/vserver/cvirt_proc.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/cvirt_proc.h 2021-02-24 15:47:45.121077159 +0100
@@ -0,0 +1,123 @@
+#ifndef _VX_CVIRT_PROC_H
+#define _VX_CVIRT_PROC_H
+}
+
+#endif /* _VX_CVIRT_PROC_H */
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/debug.c linux-4.9.217-vs2.3.9.12/kernel/vserver/debug.c
---- linux-4.9.217/kernel/vserver/debug.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/debug.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/debug.c linux-4.9/kernel/vserver/debug.c
+--- linux-4.9/kernel/vserver/debug.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/debug.c 2021-02-24 15:47:45.121077159 +0100
@@ -0,0 +1,32 @@
+/*
+ * kernel/vserver/debug.c
+
+EXPORT_SYMBOL_GPL(dump_vx_info);
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/device.c linux-4.9.217-vs2.3.9.12/kernel/vserver/device.c
---- linux-4.9.217/kernel/vserver/device.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/device.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/device.c linux-4.9/kernel/vserver/device.c
+--- linux-4.9/kernel/vserver/device.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/device.c 2021-02-24 15:47:45.121077159 +0100
@@ -0,0 +1,443 @@
+/*
+ * linux/kernel/vserver/device.c
+#endif /* CONFIG_COMPAT */
+
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/dlimit.c linux-4.9.217-vs2.3.9.12/kernel/vserver/dlimit.c
---- linux-4.9.217/kernel/vserver/dlimit.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/dlimit.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/dlimit.c linux-4.9/kernel/vserver/dlimit.c
+--- linux-4.9/kernel/vserver/dlimit.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/dlimit.c 2021-02-24 15:47:45.121077159 +0100
@@ -0,0 +1,528 @@
+/*
+ * linux/kernel/vserver/dlimit.c
+EXPORT_SYMBOL_GPL(locate_dl_info);
+EXPORT_SYMBOL_GPL(rcu_free_dl_info);
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/helper.c linux-4.9.217-vs2.3.9.12/kernel/vserver/helper.c
---- linux-4.9.217/kernel/vserver/helper.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/helper.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/helper.c linux-4.9/kernel/vserver/helper.c
+--- linux-4.9/kernel/vserver/helper.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/helper.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,242 @@
+/*
+ * linux/kernel/vserver/helper.c
+ return do_vshelper(vshelper_path, argv, envp, 1);
+}
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/history.c linux-4.9.217-vs2.3.9.12/kernel/vserver/history.c
---- linux-4.9.217/kernel/vserver/history.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/history.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/history.c linux-4.9/kernel/vserver/history.c
+--- linux-4.9/kernel/vserver/history.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/history.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,258 @@
+/*
+ * kernel/vserver/history.c
+
+#endif /* CONFIG_COMPAT */
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/inet.c linux-4.9.217-vs2.3.9.12/kernel/vserver/inet.c
---- linux-4.9.217/kernel/vserver/inet.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/inet.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/inet.c linux-4.9/kernel/vserver/inet.c
+--- linux-4.9/kernel/vserver/inet.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/inet.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,236 @@
+
+#include <linux/in.h>
+
+EXPORT_SYMBOL_GPL(ip_v4_find_src);
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/init.c linux-4.9.217-vs2.3.9.12/kernel/vserver/init.c
---- linux-4.9.217/kernel/vserver/init.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/init.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/init.c linux-4.9/kernel/vserver/init.c
+--- linux-4.9/kernel/vserver/init.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/init.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,46 @@
+/*
+ * linux/kernel/init.c
+module_init(init_vserver);
+module_exit(exit_vserver);
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/inode.c linux-4.9.217-vs2.3.9.12/kernel/vserver/inode.c
---- linux-4.9.217/kernel/vserver/inode.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/inode.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/inode.c linux-4.9/kernel/vserver/inode.c
+--- linux-4.9/kernel/vserver/inode.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/inode.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,440 @@
+/*
+ * linux/kernel/vserver/inode.c
+ return -EBADF;
+
+ ret = __vc_set_iattr(filp->f_path.dentry, &vc_data.tag,
-+ &vc_data.flags, &vc_data.mask);
-+
-+ fput(filp);
-+
-+ if (copy_to_user(data, &vc_data, sizeof(vc_data)))
-+ return -EFAULT;
-+ return ret;
-+}
-+
-+
-+enum { Opt_notagcheck, Opt_tag, Opt_notag, Opt_tagid, Opt_err };
-+
-+static match_table_t tokens = {
-+ {Opt_notagcheck, "notagcheck"},
-+#ifdef CONFIG_PROPAGATE
-+ {Opt_notag, "notag"},
-+ {Opt_tag, "tag"},
-+ {Opt_tagid, "tagid=%u"},
-+#endif
-+ {Opt_err, NULL}
-+};
-+
-+
-+static void __dx_parse_remove(char *string, char *opt)
-+{
-+ char *p = strstr(string, opt);
-+ char *q = p;
-+
-+ if (p) {
-+ while (*q != '\0' && *q != ',')
-+ q++;
-+ while (*q)
-+ *p++ = *q++;
-+ while (*p)
-+ *p++ = '\0';
-+ }
-+}
-+
-+int dx_parse_tag(char *string, vtag_t *tag, int remove, int *mnt_flags,
-+ unsigned long *flags)
-+{
-+ int set = 0;
-+ substring_t args[MAX_OPT_ARGS];
-+ int token;
-+ char *s, *p, *opts;
-+#if defined(CONFIG_PROPAGATE) || defined(CONFIG_VSERVER_DEBUG)
-+ int option = 0;
-+#endif
-+
-+ if (!string)
-+ return 0;
-+ s = kstrdup(string, GFP_KERNEL | GFP_ATOMIC);
-+ if (!s)
-+ return 0;
-+
-+ opts = s;
-+ while ((p = strsep(&opts, ",")) != NULL) {
-+ token = match_token(p, tokens, args);
-+
-+ switch (token) {
-+#ifdef CONFIG_PROPAGATE
-+ case Opt_tag:
-+ if (tag)
-+ *tag = 0;
-+ if (remove)
-+ __dx_parse_remove(s, "tag");
-+ *mnt_flags |= MNT_TAGID;
-+ set |= MNT_TAGID;
-+ break;
-+ case Opt_notag:
-+ if (remove)
-+ __dx_parse_remove(s, "notag");
-+ *mnt_flags |= MNT_NOTAG;
-+ set |= MNT_NOTAG;
-+ break;
-+ case Opt_tagid:
-+ if (tag && !match_int(args, &option))
-+ *tag = option;
-+ if (remove)
-+ __dx_parse_remove(s, "tagid");
-+ *mnt_flags |= MNT_TAGID;
-+ set |= MNT_TAGID;
-+ break;
-+#endif /* CONFIG_PROPAGATE */
-+ case Opt_notagcheck:
-+ if (remove)
-+ __dx_parse_remove(s, "notagcheck");
-+ *flags |= MS_NOTAGCHECK;
-+ set |= MS_NOTAGCHECK;
-+ break;
-+ }
-+ vxdprintk(VXD_CBIT(tag, 7),
-+ "dx_parse_tag(" VS_Q("%s") "): %d:#%d",
-+ p, token, option);
-+ }
-+ if (set)
-+ strcpy(string, s);
-+ kfree(s);
-+ return set;
-+}
-+
-+#ifdef CONFIG_PROPAGATE
-+
-+void __dx_propagate_tag(struct nameidata *nd, struct inode *inode)
-+{
-+ vtag_t new_tag = 0;
-+ struct vfsmount *mnt;
-+ int propagate;
-+
-+ if (!nd)
-+ return;
-+ mnt = nd->path.mnt;
-+ if (!mnt)
-+ return;
-+
-+ propagate = (mnt->mnt_flags & MNT_TAGID);
-+ if (propagate)
-+ new_tag = mnt->mnt_tag;
-+
-+ vxdprintk(VXD_CBIT(tag, 7),
-+ "dx_propagate_tag(%p[#%lu.%d]): %d,%d",
-+ inode, inode->i_ino, inode->i_tag,
-+ new_tag, (propagate) ? 1 : 0);
-+
-+ if (propagate)
-+ i_tag_write(inode, new_tag);
-+}
-+
-+#include <linux/module.h>
-+
-+EXPORT_SYMBOL_GPL(__dx_propagate_tag);
-+
-+#endif /* CONFIG_PROPAGATE */
-+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/Kconfig linux-4.9.217-vs2.3.9.12/kernel/vserver/Kconfig
---- linux-4.9.217/kernel/vserver/Kconfig 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/Kconfig 2018-10-20 04:58:15.000000000 +0000
-@@ -0,0 +1,230 @@
-+#
-+# Linux VServer configuration
-+#
-+
-+menu "Linux VServer"
-+
-+config VSERVER_AUTO_LBACK
-+ bool "Automatically Assign Loopback IP"
-+ default y
-+ help
-+ Automatically assign a guest specific loopback
-+ IP and add it to the kernel network stack on
-+ startup.
-+
-+config VSERVER_AUTO_SINGLE
-+ bool "Automatic Single IP Special Casing"
-+ default n
-+ help
-+ This allows network contexts with a single IP to
-+ automatically remap 0.0.0.0 bindings to that IP,
-+ avoiding further network checks and improving
-+ performance.
-+
-+ (note: such guests do not allow to change the ip
-+ on the fly and do not show loopback addresses)
-+
-+config VSERVER_COWBL
-+ bool "Enable COW Immutable Link Breaking"
-+ default y
-+ help
-+ This enables the COW (Copy-On-Write) link break code.
-+ It allows you to treat unified files like normal files
-+ when writing to them (which will implicitely break the
-+ link and create a copy of the unified file)
-+
-+config VSERVER_VTIME
-+ bool "Enable Virtualized Guest Time (EXPERIMENTAL)"
-+ default n
-+ help
-+ This enables per guest time offsets to allow for
-+ adjusting the system clock individually per guest.
-+ this adds some overhead to the time functions and
-+ therefore should not be enabled without good reason.
-+
-+config VSERVER_DEVICE
-+ bool "Enable Guest Device Mapping (EXPERIMENTAL)"
-+ default n
-+ help
-+ This enables generic device remapping.
-+
-+config VSERVER_PROC_SECURE
-+ bool "Enable Proc Security"
-+ depends on PROC_FS
-+ default y
-+ help
-+ This configures ProcFS security to initially hide
-+ non-process entries for all contexts except the main and
-+ spectator context (i.e. for all guests), which is a secure
-+ default.
-+
-+ (note: on 1.2x the entries were visible by default)
-+
-+choice
-+ prompt "Persistent Inode Tagging"
-+ default TAGGING_ID24
-+ help
-+ This adds persistent context information to filesystems
-+ mounted with the tagxid option. Tagging is a requirement
-+ for per-context disk limits and per-context quota.
-+
-+
-+config TAGGING_NONE
-+ bool "Disabled"
-+ help
-+ do not store per-context information in inodes.
-+
-+config TAGGING_UID16
-+ bool "UID16/GID32"
-+ help
-+ reduces UID to 16 bit, but leaves GID at 32 bit.
-+
-+config TAGGING_GID16
-+ bool "UID32/GID16"
-+ help
-+ reduces GID to 16 bit, but leaves UID at 32 bit.
-+
-+config TAGGING_ID24
-+ bool "UID24/GID24"
-+ help
-+ uses the upper 8bit from UID and GID for XID tagging
-+ which leaves 24bit for UID/GID each, which should be
-+ more than sufficient for normal use.
-+
-+config TAGGING_INTERN
-+ bool "UID32/GID32"
-+ help
-+ this uses otherwise reserved inode fields in the on
-+ disk representation, which limits the use to a few
-+ filesystems (currently ext2 and ext3)
-+
-+endchoice
-+
-+config TAG_NFSD
-+ bool "Tag NFSD User Auth and Files"
-+ default n
-+ help
-+ Enable this if you do want the in-kernel NFS
-+ Server to use the tagging specified above.
-+ (will require patched clients too)
++ &vc_data.flags, &vc_data.mask);
+
-+config VSERVER_PRIVACY
-+ bool "Honor Privacy Aspects of Guests"
-+ default n
-+ help
-+ When enabled, most context checks will disallow
-+ access to structures assigned to a specific context,
-+ like ptys or loop devices.
++ fput(filp);
+
-+config VSERVER_CONTEXTS
-+ int "Maximum number of Contexts (1-65533)" if EMBEDDED
-+ range 1 65533
-+ default "768" if 64BIT
-+ default "256"
-+ help
-+ This setting will optimize certain data structures
-+ and memory allocations according to the expected
-+ maximum.
++ if (copy_to_user(data, &vc_data, sizeof(vc_data)))
++ return -EFAULT;
++ return ret;
++}
+
-+ note: this is not a strict upper limit.
+
-+config VSERVER_WARN
-+ bool "VServer Warnings"
-+ default y
-+ help
-+ This enables various runtime warnings, which will
-+ notify about potential manipulation attempts or
-+ resource shortage. It is generally considered to
-+ be a good idea to have that enabled.
++enum { Opt_notagcheck, Opt_tag, Opt_notag, Opt_tagid, Opt_err };
+
-+config VSERVER_WARN_DEVPTS
-+ bool "VServer DevPTS Warnings"
-+ depends on VSERVER_WARN
-+ default y
-+ help
-+ This enables DevPTS related warnings, issued when a
-+ process inside a context tries to lookup or access
-+ a dynamic pts from the host or a different context.
++static match_table_t tokens = {
++ {Opt_notagcheck, "notagcheck"},
++#ifdef CONFIG_PROPAGATE
++ {Opt_notag, "notag"},
++ {Opt_tag, "tag"},
++ {Opt_tagid, "tagid=%u"},
++#endif
++ {Opt_err, NULL}
++};
+
-+config VSERVER_DEBUG
-+ bool "VServer Debugging Code"
-+ default n
-+ help
-+ Set this to yes if you want to be able to activate
-+ debugging output at runtime. It adds a very small
-+ overhead to all vserver related functions and
-+ increases the kernel size by about 20k.
+
-+config VSERVER_HISTORY
-+ bool "VServer History Tracing"
-+ depends on VSERVER_DEBUG
-+ default n
-+ help
-+ Set this to yes if you want to record the history of
-+ linux-vserver activities, so they can be replayed in
-+ the event of a kernel panic or oops.
++static void __dx_parse_remove(char *string, char *opt)
++{
++ char *p = strstr(string, opt);
++ char *q = p;
+
-+config VSERVER_HISTORY_SIZE
-+ int "Per-CPU History Size (32-65536)"
-+ depends on VSERVER_HISTORY
-+ range 32 65536
-+ default 64
-+ help
-+ This allows you to specify the number of entries in
-+ the per-CPU history buffer.
++ if (p) {
++ while (*q != '\0' && *q != ',')
++ q++;
++ while (*q)
++ *p++ = *q++;
++ while (*p)
++ *p++ = '\0';
++ }
++}
+
-+config VSERVER_EXTRA_MNT_CHECK
-+ bool "Extra Checks for Reachability"
-+ default n
-+ help
-+ Set this to yes if you want to do extra checks for
-+ vfsmount reachability in the proc filesystem code.
-+ This shouldn't be required on any setup utilizing
-+ mnt namespaces.
++int dx_parse_tag(char *string, vtag_t *tag, int remove, int *mnt_flags,
++ unsigned long *flags)
++{
++ int set = 0;
++ substring_t args[MAX_OPT_ARGS];
++ int token;
++ char *s, *p, *opts;
++#if defined(CONFIG_PROPAGATE) || defined(CONFIG_VSERVER_DEBUG)
++ int option = 0;
++#endif
+
-+choice
-+ prompt "Quotes used in debug and warn messages"
-+ default QUOTES_ISO8859
++ if (!string)
++ return 0;
++ s = kstrdup(string, GFP_KERNEL | GFP_ATOMIC);
++ if (!s)
++ return 0;
+
-+config QUOTES_ISO8859
-+ bool "Extended ASCII (ISO 8859) angle quotes"
-+ help
-+ This uses the extended ASCII characters \xbb
-+ and \xab for quoting file and process names.
++ opts = s;
++ while ((p = strsep(&opts, ",")) != NULL) {
++ token = match_token(p, tokens, args);
+
-+config QUOTES_UTF8
-+ bool "UTF-8 angle quotes"
-+ help
-+ This uses the the UTF-8 sequences for angle
-+ quotes to quote file and process names.
++ switch (token) {
++#ifdef CONFIG_PROPAGATE
++ case Opt_tag:
++ if (tag)
++ *tag = 0;
++ if (remove)
++ __dx_parse_remove(s, "tag");
++ *mnt_flags |= MNT_TAGID;
++ set |= MNT_TAGID;
++ break;
++ case Opt_notag:
++ if (remove)
++ __dx_parse_remove(s, "notag");
++ *mnt_flags |= MNT_NOTAG;
++ set |= MNT_NOTAG;
++ break;
++ case Opt_tagid:
++ if (tag && !match_int(args, &option))
++ *tag = option;
++ if (remove)
++ __dx_parse_remove(s, "tagid");
++ *mnt_flags |= MNT_TAGID;
++ set |= MNT_TAGID;
++ break;
++#endif /* CONFIG_PROPAGATE */
++ case Opt_notagcheck:
++ if (remove)
++ __dx_parse_remove(s, "notagcheck");
++ *flags |= MS_NOTAGCHECK;
++ set |= MS_NOTAGCHECK;
++ break;
++ }
++ vxdprintk(VXD_CBIT(tag, 7),
++ "dx_parse_tag(" VS_Q("%s") "): %d:#%d",
++ p, token, option);
++ }
++ if (set)
++ strcpy(string, s);
++ kfree(s);
++ return set;
++}
+
-+config QUOTES_ASCII
-+ bool "ASCII single quotes"
-+ help
-+ This uses the ASCII single quote character
-+ (\x27) to quote file and process names.
++#ifdef CONFIG_PROPAGATE
+
-+endchoice
++void __dx_propagate_tag(struct nameidata *nd, struct inode *inode)
++{
++ vtag_t new_tag = 0;
++ struct vfsmount *mnt;
++ int propagate;
+
-+endmenu
++ if (!nd)
++ return;
++ mnt = nd->path.mnt;
++ if (!mnt)
++ return;
+
++ propagate = (mnt->mnt_flags & MNT_TAGID);
++ if (propagate)
++ new_tag = mnt->mnt_tag;
+
-+config VSERVER
-+ bool
-+ default y
-+ select NAMESPACES
-+ select UTS_NS
-+ select IPC_NS
-+# select USER_NS
-+ select SYSVIPC
++ vxdprintk(VXD_CBIT(tag, 7),
++ "dx_propagate_tag(%p[#%lu.%d]): %d,%d",
++ inode, inode->i_ino, inode->i_tag,
++ new_tag, (propagate) ? 1 : 0);
+
-+config VSERVER_SECURITY
-+ bool
-+ depends on SECURITY
-+ default y
-+ select SECURITY_CAPABILITIES
++ if (propagate)
++ i_tag_write(inode, new_tag);
++}
+
-+config VSERVER_DISABLED
-+ bool
-+ default n
++#include <linux/module.h>
++
++EXPORT_SYMBOL_GPL(__dx_propagate_tag);
++
++#endif /* CONFIG_PROPAGATE */
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/limit.c linux-4.9.217-vs2.3.9.12/kernel/vserver/limit.c
---- linux-4.9.217/kernel/vserver/limit.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/limit.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/limit.c linux-4.9/kernel/vserver/limit.c
+--- linux-4.9/kernel/vserver/limit.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/limit.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,386 @@
+/*
+ * linux/kernel/vserver/limit.c
+}
+#endif /* !CONFIG_MEMCG */
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/limit_init.h linux-4.9.217-vs2.3.9.12/kernel/vserver/limit_init.h
---- linux-4.9.217/kernel/vserver/limit_init.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/limit_init.h 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/limit_init.h linux-4.9/kernel/vserver/limit_init.h
+--- linux-4.9/kernel/vserver/limit_init.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/limit_init.h 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,31 @@
+
+
+ }
+}
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/limit_proc.h linux-4.9.217-vs2.3.9.12/kernel/vserver/limit_proc.h
---- linux-4.9.217/kernel/vserver/limit_proc.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/limit_proc.h 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/limit_proc.h linux-4.9/kernel/vserver/limit_proc.h
+--- linux-4.9/kernel/vserver/limit_proc.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/limit_proc.h 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,57 @@
+#ifndef _VX_LIMIT_PROC_H
+#define _VX_LIMIT_PROC_H
+#endif /* _VX_LIMIT_PROC_H */
+
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/Makefile linux-4.9.217-vs2.3.9.12/kernel/vserver/Makefile
---- linux-4.9.217/kernel/vserver/Makefile 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/Makefile 2018-10-20 04:58:15.000000000 +0000
-@@ -0,0 +1,18 @@
-+#
-+# Makefile for the Linux vserver routines.
-+#
-+
-+
-+obj-y += vserver.o
-+
-+vserver-y := switch.o context.o space.o sched.o network.o inode.o \
-+ limit.o cvirt.o cacct.o signal.o helper.o init.o \
-+ dlimit.o tag.o
-+
-+vserver-$(CONFIG_INET) += inet.o
-+vserver-$(CONFIG_PROC_FS) += proc.o
-+vserver-$(CONFIG_VSERVER_DEBUG) += sysctl.o debug.o
-+vserver-$(CONFIG_VSERVER_HISTORY) += history.o
-+vserver-$(CONFIG_VSERVER_MONITOR) += monitor.o
-+vserver-$(CONFIG_VSERVER_DEVICE) += device.o
-+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/network.c linux-4.9.217-vs2.3.9.12/kernel/vserver/network.c
---- linux-4.9.217/kernel/vserver/network.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/network.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/network.c linux-4.9/kernel/vserver/network.c
+--- linux-4.9/kernel/vserver/network.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/network.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,1053 @@
+/*
+ * linux/kernel/vserver/network.c
+EXPORT_SYMBOL_GPL(free_nx_info);
+EXPORT_SYMBOL_GPL(unhash_nx_info);
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/proc.c linux-4.9.217-vs2.3.9.12/kernel/vserver/proc.c
---- linux-4.9.217/kernel/vserver/proc.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/proc.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/proc.c linux-4.9/kernel/vserver/proc.c
+--- linux-4.9/kernel/vserver/proc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/proc.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,1040 @@
+/*
+ * linux/kernel/vserver/proc.c
+ return 0;
+}
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/sched.c linux-4.9.217-vs2.3.9.12/kernel/vserver/sched.c
---- linux-4.9.217/kernel/vserver/sched.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/sched.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/sched.c linux-4.9/kernel/vserver/sched.c
+--- linux-4.9/kernel/vserver/sched.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/sched.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,83 @@
+/*
+ * linux/kernel/vserver/sched.c
+ return 0;
+}
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/sched_init.h linux-4.9.217-vs2.3.9.12/kernel/vserver/sched_init.h
---- linux-4.9.217/kernel/vserver/sched_init.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/sched_init.h 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/sched_init.h linux-4.9/kernel/vserver/sched_init.h
+--- linux-4.9/kernel/vserver/sched_init.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/sched_init.h 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,27 @@
+
+static inline void vx_info_init_sched(struct _vx_sched *sched)
+{
+ return;
+}
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/sched_proc.h linux-4.9.217-vs2.3.9.12/kernel/vserver/sched_proc.h
---- linux-4.9.217/kernel/vserver/sched_proc.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/sched_proc.h 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/sched_proc.h linux-4.9/kernel/vserver/sched_proc.h
+--- linux-4.9/kernel/vserver/sched_proc.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/sched_proc.h 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,32 @@
+#ifndef _VX_SCHED_PROC_H
+#define _VX_SCHED_PROC_H
+}
+
+#endif /* _VX_SCHED_PROC_H */
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/signal.c linux-4.9.217-vs2.3.9.12/kernel/vserver/signal.c
---- linux-4.9.217/kernel/vserver/signal.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/signal.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/signal.c linux-4.9/kernel/vserver/signal.c
+--- linux-4.9/kernel/vserver/signal.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/signal.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,134 @@
+/*
+ * linux/kernel/vserver/signal.c
+ return ret;
+}
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/space.c linux-4.9.217-vs2.3.9.12/kernel/vserver/space.c
---- linux-4.9.217/kernel/vserver/space.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/space.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/space.c linux-4.9/kernel/vserver/space.c
+--- linux-4.9/kernel/vserver/space.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/space.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,437 @@
+/*
+ * linux/kernel/vserver/space.c
+ return 0;
+}
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/switch.c linux-4.9.217-vs2.3.9.12/kernel/vserver/switch.c
---- linux-4.9.217/kernel/vserver/switch.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/switch.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/switch.c linux-4.9/kernel/vserver/switch.c
+--- linux-4.9/kernel/vserver/switch.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/switch.c 2021-02-24 15:47:45.124410596 +0100
@@ -0,0 +1,556 @@
+/*
+ * linux/kernel/vserver/switch.c
+}
+
+#endif /* CONFIG_COMPAT */
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/sysctl.c linux-4.9.217-vs2.3.9.12/kernel/vserver/sysctl.c
---- linux-4.9.217/kernel/vserver/sysctl.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/sysctl.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/sysctl.c linux-4.9/kernel/vserver/sysctl.c
+--- linux-4.9/kernel/vserver/sysctl.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/sysctl.c 2021-02-24 15:47:45.127744035 +0100
@@ -0,0 +1,249 @@
+/*
+ * kernel/vserver/sysctl.c
+EXPORT_SYMBOL_GPL(vs_debug_perm);
+EXPORT_SYMBOL_GPL(vs_debug_misc);
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/tag.c linux-4.9.217-vs2.3.9.12/kernel/vserver/tag.c
---- linux-4.9.217/kernel/vserver/tag.c 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/tag.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/tag.c linux-4.9/kernel/vserver/tag.c
+--- linux-4.9/kernel/vserver/tag.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/tag.c 2021-02-24 15:47:45.127744035 +0100
@@ -0,0 +1,63 @@
+/*
+ * linux/kernel/vserver/tag.c
+}
+
+
-diff -NurpP --minimal linux-4.9.217/kernel/vserver/vci_config.h linux-4.9.217-vs2.3.9.12/kernel/vserver/vci_config.h
---- linux-4.9.217/kernel/vserver/vci_config.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/kernel/vserver/vci_config.h 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/kernel/vserver/vci_config.h linux-4.9/kernel/vserver/vci_config.h
+--- linux-4.9/kernel/vserver/vci_config.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/kernel/vserver/vci_config.h 2021-02-24 15:47:45.127744035 +0100
@@ -0,0 +1,80 @@
+
+/* interface version */
+ 0;
+}
+
-diff -NurpP --minimal linux-4.9.217/mm/memcontrol.c linux-4.9.217-vs2.3.9.12/mm/memcontrol.c
---- linux-4.9.217/mm/memcontrol.c 2020-03-27 00:51:44.810116540 +0000
-+++ linux-4.9.217-vs2.3.9.12/mm/memcontrol.c 2020-04-01 09:40:32.745397028 +0000
-@@ -2854,6 +2854,41 @@ static u64 mem_cgroup_read_u64(struct cg
+diff -urNp -x '*.orig' linux-4.9/mm/memcontrol.c linux-4.9/mm/memcontrol.c
+--- linux-4.9/mm/memcontrol.c 2021-02-24 15:47:32.527349847 +0100
++++ linux-4.9/mm/memcontrol.c 2021-02-24 15:47:45.127744035 +0100
+@@ -2873,6 +2873,41 @@ static u64 mem_cgroup_read_u64(struct cg
}
}
#ifndef CONFIG_SLOB
static int memcg_online_kmem(struct mem_cgroup *memcg)
{
-diff -NurpP --minimal linux-4.9.217/mm/oom_kill.c linux-4.9.217-vs2.3.9.12/mm/oom_kill.c
---- linux-4.9.217/mm/oom_kill.c 2020-03-27 00:51:44.980113860 +0000
-+++ linux-4.9.217-vs2.3.9.12/mm/oom_kill.c 2019-02-22 08:37:56.143042249 +0000
+diff -urNp -x '*.orig' linux-4.9/mm/oom_kill.c linux-4.9/mm/oom_kill.c
+--- linux-4.9/mm/oom_kill.c 2021-02-24 15:47:32.534016722 +0100
++++ linux-4.9/mm/oom_kill.c 2021-02-24 15:47:45.127744035 +0100
@@ -38,6 +38,8 @@
#include <linux/kthread.h>
#include <linux/init.h>
}
if (oc->chosen && oc->chosen != (void *)-1UL) {
oom_kill_process(oc, !is_memcg_oom(oc) ? "Out of memory" :
-diff -NurpP --minimal linux-4.9.217/mm/page_alloc.c linux-4.9.217-vs2.3.9.12/mm/page_alloc.c
---- linux-4.9.217/mm/page_alloc.c 2020-03-27 00:51:44.980113860 +0000
-+++ linux-4.9.217-vs2.3.9.12/mm/page_alloc.c 2020-04-01 09:45:52.210102682 +0000
-@@ -64,6 +64,8 @@
+diff -urNp -x '*.orig' linux-4.9/mm/page_alloc.c linux-4.9/mm/page_alloc.c
+--- linux-4.9/mm/page_alloc.c 2021-02-24 15:47:32.534016722 +0100
++++ linux-4.9/mm/page_alloc.c 2021-02-24 15:47:45.127744035 +0100
+@@ -65,6 +65,8 @@
#include <linux/kthread.h>
#include <linux/memcontrol.h>
#include <linux/khugepaged.h>
#include <asm/sections.h>
#include <asm/tlbflush.h>
-@@ -4164,14 +4166,17 @@ long si_mem_available(void)
+@@ -4171,14 +4173,17 @@ long si_mem_available(void)
*/
pagecache = pages[LRU_ACTIVE_FILE] + pages[LRU_INACTIVE_FILE];
pagecache -= min(pagecache / 2, wmark_low);
if (available < 0)
available = 0;
-@@ -4188,6 +4193,9 @@ void si_meminfo(struct sysinfo *val)
+@@ -4195,6 +4200,9 @@ void si_meminfo(struct sysinfo *val)
val->totalhigh = totalhigh_pages;
val->freehigh = nr_free_highpages();
val->mem_unit = PAGE_SIZE;
}
EXPORT_SYMBOL(si_meminfo);
-@@ -4222,6 +4230,9 @@ void si_meminfo_node(struct sysinfo *val
+@@ -4229,6 +4237,9 @@ void si_meminfo_node(struct sysinfo *val
val->freehigh = free_highpages;
#endif
val->mem_unit = PAGE_SIZE;
}
#endif
-diff -NurpP --minimal linux-4.9.217/mm/pgtable-generic.c linux-4.9.217-vs2.3.9.12/mm/pgtable-generic.c
---- linux-4.9.217/mm/pgtable-generic.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/mm/pgtable-generic.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/mm/pgtable-generic.c linux-4.9/mm/pgtable-generic.c
+--- linux-4.9/mm/pgtable-generic.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/mm/pgtable-generic.c 2021-02-24 15:47:45.127744035 +0100
@@ -6,6 +6,8 @@
* Copyright (C) 2010 Linus Torvalds
*/
#include <linux/pagemap.h>
#include <asm/tlb.h>
#include <asm-generic/pgtable.h>
-diff -NurpP --minimal linux-4.9.217/mm/shmem.c linux-4.9.217-vs2.3.9.12/mm/shmem.c
---- linux-4.9.217/mm/shmem.c 2020-03-27 00:51:45.000113543 +0000
-+++ linux-4.9.217-vs2.3.9.12/mm/shmem.c 2019-12-25 15:37:52.838415659 +0000
-@@ -2803,7 +2803,7 @@ static int shmem_statfs(struct dentry *d
+diff -urNp -x '*.orig' linux-4.9/mm/shmem.c linux-4.9/mm/shmem.c
+--- linux-4.9/mm/shmem.c 2021-02-24 15:47:32.537350160 +0100
++++ linux-4.9/mm/shmem.c 2021-02-24 15:47:45.127744035 +0100
+@@ -2806,7 +2806,7 @@ static int shmem_statfs(struct dentry *d
{
struct shmem_sb_info *sbinfo = SHMEM_SB(dentry->d_sb);
buf->f_bsize = PAGE_SIZE;
buf->f_namelen = NAME_MAX;
if (sbinfo->max_blocks) {
-@@ -3628,7 +3628,7 @@ int shmem_fill_super(struct super_block
+@@ -3631,7 +3631,7 @@ int shmem_fill_super(struct super_block
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT;
sb->s_op = &shmem_ops;
sb->s_time_gran = 1;
#ifdef CONFIG_TMPFS_XATTR
-diff -NurpP --minimal linux-4.9.217/mm/slab.c linux-4.9.217-vs2.3.9.12/mm/slab.c
---- linux-4.9.217/mm/slab.c 2020-03-27 00:51:45.010113388 +0000
-+++ linux-4.9.217-vs2.3.9.12/mm/slab.c 2019-10-05 14:58:46.200299086 +0000
+diff -urNp -x '*.orig' linux-4.9/mm/slab.c linux-4.9/mm/slab.c
+--- linux-4.9/mm/slab.c 2021-02-24 15:47:32.537350160 +0100
++++ linux-4.9/mm/slab.c 2021-02-24 15:47:45.131077472 +0100
@@ -307,6 +307,8 @@ static void kmem_cache_node_init(struct
#define STATS_INC_FREEMISS(x) do { } while (0)
#endif
kmemcheck_slab_free(cachep, objp, cachep->object_size);
-diff -NurpP --minimal linux-4.9.217/mm/slab_vs.h linux-4.9.217-vs2.3.9.12/mm/slab_vs.h
---- linux-4.9.217/mm/slab_vs.h 1970-01-01 00:00:00.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/mm/slab_vs.h 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/mm/slab_vs.h linux-4.9/mm/slab_vs.h
+--- linux-4.9/mm/slab_vs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-4.9/mm/slab_vs.h 2021-02-24 15:47:45.131077472 +0100
@@ -0,0 +1,29 @@
+
+#include <linux/vserver/context.h>
+ atomic_sub(cachep->size, &vxi->cacct.slab[what]);
+}
+
-diff -NurpP --minimal linux-4.9.217/mm/swapfile.c linux-4.9.217-vs2.3.9.12/mm/swapfile.c
---- linux-4.9.217/mm/swapfile.c 2020-03-27 00:51:45.030113073 +0000
-+++ linux-4.9.217-vs2.3.9.12/mm/swapfile.c 2018-10-20 05:55:43.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/mm/swapfile.c linux-4.9/mm/swapfile.c
+--- linux-4.9/mm/swapfile.c 2021-02-24 15:47:32.540683597 +0100
++++ linux-4.9/mm/swapfile.c 2021-02-24 15:47:45.131077472 +0100
@@ -39,6 +39,7 @@
#include <asm/tlbflush.h>
#include <linux/swapops.h>
}
/*
-diff -NurpP --minimal linux-4.9.217/net/bridge/br_multicast.c linux-4.9.217-vs2.3.9.12/net/bridge/br_multicast.c
---- linux-4.9.217/net/bridge/br_multicast.c 2020-03-27 00:51:45.750101724 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/bridge/br_multicast.c 2019-10-05 14:58:46.250298288 +0000
+diff -urNp -x '*.orig' linux-4.9/net/bridge/br_multicast.c linux-4.9/net/bridge/br_multicast.c
+--- linux-4.9/net/bridge/br_multicast.c 2021-02-24 15:47:32.564017661 +0100
++++ linux-4.9/net/bridge/br_multicast.c 2021-02-24 15:47:45.131077472 +0100
@@ -465,7 +465,7 @@ static struct sk_buff *br_ip6_multicast_
ip6h->hop_limit = 1;
ipv6_addr_set(&ip6h->daddr, htonl(0xff020000), 0, 0, htonl(1));
kfree_skb(skb);
br->has_ipv6_addr = 0;
return NULL;
-diff -NurpP --minimal linux-4.9.217/net/core/dev.c linux-4.9.217-vs2.3.9.12/net/core/dev.c
---- linux-4.9.217/net/core/dev.c 2020-03-27 00:51:45.850100149 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/core/dev.c 2019-12-25 15:37:52.958413722 +0000
-@@ -126,6 +126,7 @@
+diff -urNp -x '*.orig' linux-4.9/net/core/dev.c linux-4.9/net/core/dev.c
+--- linux-4.9/net/core/dev.c 2021-02-24 15:47:33.077367054 +0100
++++ linux-4.9/net/core/dev.c 2021-02-24 15:47:45.131077472 +0100
+@@ -127,6 +127,7 @@
#include <linux/in.h>
#include <linux/jhash.h>
#include <linux/random.h>
#include <trace/events/napi.h>
#include <trace/events/net.h>
#include <trace/events/skb.h>
-@@ -730,7 +731,8 @@ struct net_device *__dev_get_by_name(str
+@@ -734,7 +735,8 @@ struct net_device *__dev_get_by_name(str
struct hlist_head *head = dev_name_hash(net, name);
hlist_for_each_entry(dev, head, name_hlist)
return dev;
return NULL;
-@@ -755,7 +757,8 @@ struct net_device *dev_get_by_name_rcu(s
+@@ -759,7 +761,8 @@ struct net_device *dev_get_by_name_rcu(s
struct hlist_head *head = dev_name_hash(net, name);
hlist_for_each_entry_rcu(dev, head, name_hlist)
return dev;
return NULL;
-@@ -805,7 +808,8 @@ struct net_device *__dev_get_by_index(st
+@@ -809,7 +812,8 @@ struct net_device *__dev_get_by_index(st
struct hlist_head *head = dev_index_hash(net, ifindex);
hlist_for_each_entry(dev, head, index_hlist)
return dev;
return NULL;
-@@ -823,7 +827,7 @@ EXPORT_SYMBOL(__dev_get_by_index);
+@@ -827,7 +831,7 @@ EXPORT_SYMBOL(__dev_get_by_index);
* about locking. The caller must hold RCU lock.
*/
{
struct net_device *dev;
struct hlist_head *head = dev_index_hash(net, ifindex);
-@@ -834,6 +838,16 @@ struct net_device *dev_get_by_index_rcu(
+@@ -838,6 +842,16 @@ struct net_device *dev_get_by_index_rcu(
return NULL;
}
EXPORT_SYMBOL(dev_get_by_index_rcu);
-@@ -916,7 +930,8 @@ struct net_device *dev_getbyhwaddr_rcu(s
+@@ -915,7 +929,8 @@ struct net_device *dev_getbyhwaddr_rcu(s
for_each_netdev_rcu(net, dev)
if (dev->type == type &&
return dev;
return NULL;
-@@ -928,9 +943,11 @@ struct net_device *__dev_getfirstbyhwtyp
+@@ -927,9 +942,11 @@ struct net_device *__dev_getfirstbyhwtyp
struct net_device *dev;
ASSERT_RTNL();
return NULL;
}
-@@ -942,7 +959,8 @@ struct net_device *dev_getfirstbyhwtype(
+@@ -941,7 +958,8 @@ struct net_device *dev_getfirstbyhwtype(
rcu_read_lock();
for_each_netdev_rcu(net, dev)
dev_hold(dev);
ret = dev;
break;
-@@ -972,7 +990,8 @@ struct net_device *__dev_get_by_flags(st
+@@ -971,7 +989,8 @@ struct net_device *__dev_get_by_flags(st
ret = NULL;
for_each_netdev(net, dev) {
ret = dev;
break;
}
-@@ -1050,6 +1069,8 @@ static int __dev_alloc_name(struct net *
+@@ -1049,6 +1068,8 @@ static int __dev_alloc_name(struct net *
continue;
if (i < 0 || i >= max_netdevices)
continue;
/* avoid cases where sscanf is not exact inverse of printf */
snprintf(buf, IFNAMSIZ, name, i);
-diff -NurpP --minimal linux-4.9.217/net/core/net-procfs.c linux-4.9.217-vs2.3.9.12/net/core/net-procfs.c
---- linux-4.9.217/net/core/net-procfs.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/core/net-procfs.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/core/net-procfs.c linux-4.9/net/core/net-procfs.c
+--- linux-4.9/net/core/net-procfs.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/core/net-procfs.c 2021-02-24 15:47:45.131077472 +0100
@@ -1,6 +1,7 @@
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
struct rtnl_link_stats64 temp;
- const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
+ const struct rtnl_link_stats64 *stats;
-+
+
+ /* device visible inside network context? */
+ if (!nx_dev_visible(current_nx_info(), dev))
+ return;
-
++
+ stats = dev_get_stats(dev, &temp);
seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
"%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
dev->name, stats->rx_bytes, stats->rx_packets,
-diff -NurpP --minimal linux-4.9.217/net/core/rtnetlink.c linux-4.9.217-vs2.3.9.12/net/core/rtnetlink.c
---- linux-4.9.217/net/core/rtnetlink.c 2020-03-27 00:51:45.900099361 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/core/rtnetlink.c 2019-12-25 15:37:52.958413722 +0000
+diff -urNp -x '*.orig' linux-4.9/net/core/rtnetlink.c linux-4.9/net/core/rtnetlink.c
+--- linux-4.9/net/core/rtnetlink.c 2021-02-24 15:47:32.577351411 +0100
++++ linux-4.9/net/core/rtnetlink.c 2021-02-24 15:47:45.134410910 +0100
@@ -1615,6 +1615,8 @@ static int rtnl_dump_ifinfo(struct sk_bu
goto cont;
if (idx < s_idx)
if (dev->reg_state != NETREG_REGISTERED)
return;
-diff -NurpP --minimal linux-4.9.217/net/core/sock.c linux-4.9.217-vs2.3.9.12/net/core/sock.c
---- linux-4.9.217/net/core/sock.c 2020-03-27 00:51:45.900099361 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/core/sock.c 2020-02-26 14:31:15.988223949 +0000
+diff -urNp -x '*.orig' linux-4.9/net/core/sock.c linux-4.9/net/core/sock.c
+--- linux-4.9/net/core/sock.c 2021-02-24 15:47:32.580684849 +0100
++++ linux-4.9/net/core/sock.c 2021-02-24 15:47:45.134410910 +0100
@@ -135,6 +135,10 @@
#include <linux/filter.h>
}
return sk;
-@@ -1443,6 +1450,11 @@ static void __sk_destruct(struct rcu_hea
+@@ -1444,6 +1451,11 @@ static void __sk_destruct(struct rcu_hea
put_pid(sk->sk_peer_pid);
if (likely(sk->sk_net_refcnt))
put_net(sock_net(sk));
sk_prot_free(sk->sk_prot_creator, sk);
}
-@@ -1504,6 +1516,8 @@ struct sock *sk_clone_lock(const struct
+@@ -1505,6 +1517,8 @@ struct sock *sk_clone_lock(const struct
/* SANITY */
if (likely(newsk->sk_net_refcnt))
get_net(sock_net(newsk));
sk_node_init(&newsk->sk_node);
sock_lock_init(newsk);
bh_lock_sock(newsk);
-@@ -1574,6 +1588,12 @@ struct sock *sk_clone_lock(const struct
+@@ -1575,6 +1589,12 @@ struct sock *sk_clone_lock(const struct
smp_wmb();
atomic_set(&newsk->sk_refcnt, 2);
/*
* Increment the counter in the same struct proto as the master
* sock (sk_refcnt_debug_inc uses newsk->sk_prot->socks, that
-@@ -2477,6 +2497,12 @@ void sock_init_data(struct socket *sock,
+@@ -2500,6 +2520,12 @@ void sock_init_data(struct socket *sock,
seqlock_init(&sk->sk_stamp_seq);
#endif
#ifdef CONFIG_NET_RX_BUSY_POLL
sk->sk_napi_id = 0;
sk->sk_ll_usec = sysctl_net_busy_read;
-diff -NurpP --minimal linux-4.9.217/net/ipv4/af_inet.c linux-4.9.217-vs2.3.9.12/net/ipv4/af_inet.c
---- linux-4.9.217/net/ipv4/af_inet.c 2020-03-27 00:51:46.330092583 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/af_inet.c 2018-10-20 05:55:44.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/af_inet.c linux-4.9/net/ipv4/af_inet.c
+--- linux-4.9/net/ipv4/af_inet.c 2021-02-24 15:47:32.587351724 +0100
++++ linux-4.9/net/ipv4/af_inet.c 2021-02-24 15:47:45.134410910 +0100
@@ -303,10 +303,15 @@ lookup_protocol:
}
const struct proto_ops inet_stream_ops = {
.family = PF_INET,
-diff -NurpP --minimal linux-4.9.217/net/ipv4/arp.c linux-4.9.217-vs2.3.9.12/net/ipv4/arp.c
---- linux-4.9.217/net/ipv4/arp.c 2020-03-27 00:51:46.330092583 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/arp.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/arp.c linux-4.9/net/ipv4/arp.c
+--- linux-4.9/net/ipv4/arp.c 2021-02-24 15:47:32.587351724 +0100
++++ linux-4.9/net/ipv4/arp.c 2021-02-24 15:47:45.134410910 +0100
@@ -1320,6 +1320,7 @@ static void arp_format_neigh_entry(struc
struct net_device *dev = n->dev;
int hatype = dev->type;
sprintf(tbuf, "%pI4", n->key);
seq_printf(seq, "%-16s 0x%-10x0x%-10x%s * %s\n",
tbuf, hatype, ATF_PUBL | ATF_PERM, "00:00:00:00:00:00",
-diff -NurpP --minimal linux-4.9.217/net/ipv4/devinet.c linux-4.9.217-vs2.3.9.12/net/ipv4/devinet.c
---- linux-4.9.217/net/ipv4/devinet.c 2020-03-27 00:51:46.330092583 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/devinet.c 2019-12-25 15:37:52.998413076 +0000
-@@ -546,6 +546,7 @@ struct in_device *inetdev_by_index(struc
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/devinet.c linux-4.9/net/ipv4/devinet.c
+--- linux-4.9/net/ipv4/devinet.c 2021-02-24 15:47:32.587351724 +0100
++++ linux-4.9/net/ipv4/devinet.c 2021-02-24 15:47:45.134410910 +0100
+@@ -547,6 +547,7 @@ struct in_device *inetdev_by_index(struc
}
EXPORT_SYMBOL(inetdev_by_index);
/* Called only from RTNL semaphored context. No locks. */
struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
-@@ -1000,6 +1001,8 @@ int devinet_ioctl(struct net *net, unsig
+@@ -1006,6 +1007,8 @@ int devinet_ioctl(struct net *net, unsig
in_dev = __in_dev_get_rtnl(dev);
if (in_dev) {
if (tryaddrmatch) {
/* Matthias Andree */
/* compare label and address (4.4BSD style) */
-@@ -1008,6 +1011,8 @@ int devinet_ioctl(struct net *net, unsig
+@@ -1014,6 +1017,8 @@ int devinet_ioctl(struct net *net, unsig
This is checked above. */
for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
ifap = &ifa->ifa_next) {
if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
sin_orig.sin_addr.s_addr ==
ifa->ifa_local) {
-@@ -1020,9 +1025,12 @@ int devinet_ioctl(struct net *net, unsig
+@@ -1026,9 +1031,12 @@ int devinet_ioctl(struct net *net, unsig
comparing just the label */
if (!ifa) {
for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
}
}
-@@ -1176,6 +1184,8 @@ static int inet_gifconf(struct net_devic
+@@ -1182,6 +1190,8 @@ static int inet_gifconf(struct net_devic
goto out;
for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
if (!buf) {
done += sizeof(ifr);
continue;
-@@ -1598,6 +1608,7 @@ static int inet_dump_ifaddr(struct sk_bu
+@@ -1604,6 +1614,7 @@ static int inet_dump_ifaddr(struct sk_bu
struct net_device *dev;
struct in_device *in_dev;
struct in_ifaddr *ifa;
struct hlist_head *head;
s_h = cb->args[0];
-@@ -1621,6 +1632,8 @@ static int inet_dump_ifaddr(struct sk_bu
+@@ -1627,6 +1638,8 @@ static int inet_dump_ifaddr(struct sk_bu
for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
ifa = ifa->ifa_next, ip_idx++) {
if (ip_idx < s_ip_idx)
continue;
if (inet_fill_ifaddr(skb, ifa,
-diff -NurpP --minimal linux-4.9.217/net/ipv4/fib_trie.c linux-4.9.217-vs2.3.9.12/net/ipv4/fib_trie.c
---- linux-4.9.217/net/ipv4/fib_trie.c 2020-03-27 00:51:46.330092583 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/fib_trie.c 2019-02-22 08:37:56.413037657 +0000
-@@ -2627,6 +2627,7 @@ static int fib_route_seq_show(struct seq
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/fib_trie.c linux-4.9/net/ipv4/fib_trie.c
+--- linux-4.9/net/ipv4/fib_trie.c 2021-02-24 15:47:32.590685161 +0100
++++ linux-4.9/net/ipv4/fib_trie.c 2021-02-24 15:47:45.134410910 +0100
+@@ -2630,6 +2630,7 @@ static int fib_route_seq_show(struct seq
seq_setwidth(seq, 127);
if (fi)
seq_printf(seq,
"%s\t%08X\t%08X\t%04X\t%d\t%u\t"
-diff -NurpP --minimal linux-4.9.217/net/ipv4/inet_connection_sock.c linux-4.9.217-vs2.3.9.12/net/ipv4/inet_connection_sock.c
---- linux-4.9.217/net/ipv4/inet_connection_sock.c 2020-03-27 00:51:46.340092422 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/inet_connection_sock.c 2019-10-05 14:58:46.260298130 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/inet_connection_sock.c linux-4.9/net/ipv4/inet_connection_sock.c
+--- linux-4.9/net/ipv4/inet_connection_sock.c 2021-02-24 15:47:32.590685161 +0100
++++ linux-4.9/net/ipv4/inet_connection_sock.c 2021-02-24 15:47:45.134410910 +0100
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/jhash.h>
break;
}
}
-diff -NurpP --minimal linux-4.9.217/net/ipv4/inet_diag.c linux-4.9.217-vs2.3.9.12/net/ipv4/inet_diag.c
---- linux-4.9.217/net/ipv4/inet_diag.c 2020-03-27 00:51:46.350092266 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/inet_diag.c 2020-04-01 09:40:33.315387585 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/inet_diag.c linux-4.9/net/ipv4/inet_diag.c
+--- linux-4.9/net/ipv4/inet_diag.c 2021-02-24 15:47:32.590685161 +0100
++++ linux-4.9/net/ipv4/inet_diag.c 2021-02-24 15:47:45.134410910 +0100
@@ -31,6 +31,8 @@
#include <linux/inet.h>
}
}
EXPORT_SYMBOL_GPL(inet_diag_msg_common_fill);
-@@ -880,6 +882,9 @@ void inet_diag_dump_icsk(struct inet_has
+@@ -882,6 +884,9 @@ void inet_diag_dump_icsk(struct inet_has
if (!net_eq(sock_net(sk), net))
continue;
if (num < s_num) {
num++;
continue;
-@@ -942,6 +947,8 @@ skip_listen_ht:
+@@ -944,6 +949,8 @@ skip_listen_ht:
if (!net_eq(sock_net(sk), net))
continue;
if (num < s_num)
goto next_normal;
state = (sk->sk_state == TCP_TIME_WAIT) ?
-diff -NurpP --minimal linux-4.9.217/net/ipv4/inet_hashtables.c linux-4.9.217-vs2.3.9.12/net/ipv4/inet_hashtables.c
---- linux-4.9.217/net/ipv4/inet_hashtables.c 2020-03-27 00:51:46.350092266 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/inet_hashtables.c 2020-04-01 09:40:33.335387251 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/inet_hashtables.c linux-4.9/net/ipv4/inet_hashtables.c
+--- linux-4.9/net/ipv4/inet_hashtables.c 2021-02-24 15:47:32.594018600 +0100
++++ linux-4.9/net/ipv4/inet_hashtables.c 2021-02-24 15:47:45.134410910 +0100
@@ -24,6 +24,7 @@
#include <net/inet_connection_sock.h>
#include <net/inet_hashtables.h>
#include <net/ip.h>
#include <net/tcp.h>
#include <net/sock_reuseport.h>
-@@ -186,6 +187,11 @@ static inline int compute_score(struct s
+@@ -187,6 +188,11 @@ static inline int compute_score(struct s
if (rcv_saddr != daddr)
return -1;
score += 4;
}
if (sk->sk_bound_dev_if || exact_dif) {
if (sk->sk_bound_dev_if != dif)
-@@ -301,6 +307,7 @@ begin:
+@@ -302,6 +308,7 @@ begin:
goto found;
}
}
/*
* if the nulls value we got at the end of this lookup is
* not the expected one, we must restart lookup.
-diff -NurpP --minimal linux-4.9.217/net/ipv4/netfilter.c linux-4.9.217-vs2.3.9.12/net/ipv4/netfilter.c
---- linux-4.9.217/net/ipv4/netfilter.c 2020-03-27 00:51:46.400091478 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/netfilter.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/netfilter.c linux-4.9/net/ipv4/netfilter.c
+--- linux-4.9/net/ipv4/netfilter.c 2021-02-24 15:47:32.597352037 +0100
++++ linux-4.9/net/ipv4/netfilter.c 2021-02-24 15:47:45.134410910 +0100
@@ -11,7 +11,7 @@
#include <linux/skbuff.h>
#include <linux/gfp.h>
#include <net/xfrm.h>
#include <net/ip.h>
#include <net/netfilter/nf_queue.h>
-diff -NurpP --minimal linux-4.9.217/net/ipv4/raw.c linux-4.9.217-vs2.3.9.12/net/ipv4/raw.c
---- linux-4.9.217/net/ipv4/raw.c 2020-03-27 00:51:46.490090059 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/raw.c 2019-10-05 14:58:46.260298130 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/raw.c linux-4.9/net/ipv4/raw.c
+--- linux-4.9/net/ipv4/raw.c 2021-02-24 15:47:32.600685475 +0100
++++ linux-4.9/net/ipv4/raw.c 2021-02-24 15:47:45.137744347 +0100
@@ -128,7 +128,7 @@ static struct sock *__raw_v4_lookup(stru
if (net_eq(sock_net(sk), net) && inet->inet_num == num &&
err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
net, sk, skb, NULL, rt->dst.dev,
dst_output);
-@@ -623,6 +629,16 @@ static int raw_sendmsg(struct sock *sk,
+@@ -625,6 +631,16 @@ static int raw_sendmsg(struct sock *sk,
goto done;
}
security_sk_classify_flow(sk, flowi4_to_flowi(&fl4));
rt = ip_route_output_flow(net, &fl4, sk);
if (IS_ERR(rt)) {
-@@ -701,17 +717,19 @@ static int raw_bind(struct sock *sk, str
+@@ -703,17 +719,19 @@ static int raw_bind(struct sock *sk, str
{
struct inet_sock *inet = inet_sk(sk);
struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
inet->inet_saddr = 0; /* Use device */
sk_dst_reset(sk);
-@@ -760,7 +778,8 @@ static int raw_recvmsg(struct sock *sk,
+@@ -762,7 +780,8 @@ static int raw_recvmsg(struct sock *sk,
/* Copy the address. */
if (sin) {
sin->sin_family = AF_INET;
sin->sin_port = 0;
memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
*addr_len = sizeof(*sin);
-@@ -956,7 +975,8 @@ static struct sock *raw_get_first(struct
+@@ -958,7 +977,8 @@ static struct sock *raw_get_first(struct
for (state->bucket = 0; state->bucket < RAW_HTABLE_SIZE;
++state->bucket) {
sk_for_each(sk, &state->h->ht[state->bucket])
goto found;
}
sk = NULL;
-@@ -972,7 +992,8 @@ static struct sock *raw_get_next(struct
+@@ -974,7 +994,8 @@ static struct sock *raw_get_next(struct
sk = sk_next(sk);
try_again:
;
if (!sk && ++state->bucket < RAW_HTABLE_SIZE) {
sk = sk_head(&state->h->ht[state->bucket]);
-diff -NurpP --minimal linux-4.9.217/net/ipv4/route.c linux-4.9.217-vs2.3.9.12/net/ipv4/route.c
---- linux-4.9.217/net/ipv4/route.c 2020-03-27 00:51:46.490090059 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/route.c 2019-12-25 15:37:52.998413076 +0000
-@@ -2289,7 +2289,7 @@ struct rtable *__ip_route_output_key_has
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/route.c linux-4.9/net/ipv4/route.c
+--- linux-4.9/net/ipv4/route.c 2021-02-24 15:47:32.600685475 +0100
++++ linux-4.9/net/ipv4/route.c 2021-02-24 15:47:45.137744347 +0100
+@@ -2287,7 +2287,7 @@ struct rtable *__ip_route_output_key_has
if (fl4->flowi4_oif) {
rth = ERR_PTR(-ENODEV);
if (!dev_out)
goto out;
-diff -NurpP --minimal linux-4.9.217/net/ipv4/tcp.c linux-4.9.217-vs2.3.9.12/net/ipv4/tcp.c
---- linux-4.9.217/net/ipv4/tcp.c 2020-03-27 00:51:46.490090059 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/tcp.c 2020-04-01 09:40:33.355386921 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/tcp.c linux-4.9/net/ipv4/tcp.c
+--- linux-4.9/net/ipv4/tcp.c 2021-02-24 15:47:32.600685475 +0100
++++ linux-4.9/net/ipv4/tcp.c 2021-02-24 15:47:45.137744347 +0100
@@ -269,6 +269,7 @@
#include <linux/err.h>
#include <linux/time.h>
#include <net/icmp.h>
#include <net/inet_common.h>
-diff -NurpP --minimal linux-4.9.217/net/ipv4/tcp_ipv4.c linux-4.9.217-vs2.3.9.12/net/ipv4/tcp_ipv4.c
---- linux-4.9.217/net/ipv4/tcp_ipv4.c 2020-03-27 00:51:46.520089586 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/tcp_ipv4.c 2020-04-01 09:42:52.973073408 +0000
-@@ -1935,8 +1935,12 @@ get_head:
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/tcp_ipv4.c linux-4.9/net/ipv4/tcp_ipv4.c
+--- linux-4.9/net/ipv4/tcp_ipv4.c 2021-02-24 15:47:32.604018912 +0100
++++ linux-4.9/net/ipv4/tcp_ipv4.c 2021-02-24 15:47:45.137744347 +0100
+@@ -1944,8 +1944,12 @@ get_head:
sk = sk_nulls_next(sk);
get_sk:
sk_nulls_for_each_from(sk, node) {
if (sk->sk_family == st->family)
return sk;
}
-@@ -1990,6 +1994,11 @@ static void *established_get_first(struc
+@@ -1999,6 +2003,11 @@ static void *established_get_first(struc
spin_lock_bh(lock);
sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[st->bucket].chain) {
if (sk->sk_family != st->family ||
!net_eq(sock_net(sk), net)) {
continue;
-@@ -2016,6 +2025,11 @@ static void *established_get_next(struct
+@@ -2025,6 +2034,11 @@ static void *established_get_next(struct
sk = sk_nulls_next(sk);
sk_nulls_for_each_from(sk, node) {
if (sk->sk_family == st->family && net_eq(sock_net(sk), net))
return sk;
}
-@@ -2207,9 +2221,9 @@ static void get_openreq4(const struct re
+@@ -2216,9 +2230,9 @@ static void get_openreq4(const struct re
seq_printf(f, "%4d: %08X:%04X %08X:%04X"
" %02X %08X:%08X %02X:%08lX %08X %5u %8d %u %d %pK",
i,
ntohs(ireq->ir_rmt_port),
TCP_SYN_RECV,
0, 0, /* could print option size, but that is af dependent. */
-@@ -2232,8 +2246,8 @@ static void get_tcp4_sock(struct sock *s
+@@ -2241,8 +2255,8 @@ static void get_tcp4_sock(struct sock *s
const struct inet_connection_sock *icsk = inet_csk(sk);
const struct inet_sock *inet = inet_sk(sk);
const struct fastopen_queue *fastopenq = &icsk->icsk_accept_queue.fastopenq;
__u16 destp = ntohs(inet->inet_dport);
__u16 srcp = ntohs(inet->inet_sport);
int rx_queue;
-@@ -2292,8 +2306,8 @@ static void get_timewait4_sock(const str
+@@ -2301,8 +2315,8 @@ static void get_timewait4_sock(const str
__be32 dest, src;
__u16 destp, srcp;
destp = ntohs(tw->tw_dport);
srcp = ntohs(tw->tw_sport);
-diff -NurpP --minimal linux-4.9.217/net/ipv4/tcp_minisocks.c linux-4.9.217-vs2.3.9.12/net/ipv4/tcp_minisocks.c
---- linux-4.9.217/net/ipv4/tcp_minisocks.c 2020-03-27 00:51:46.520089586 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/tcp_minisocks.c 2018-10-20 05:55:44.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/tcp_minisocks.c linux-4.9/net/ipv4/tcp_minisocks.c
+--- linux-4.9/net/ipv4/tcp_minisocks.c 2021-02-24 15:47:32.604018912 +0100
++++ linux-4.9/net/ipv4/tcp_minisocks.c 2021-02-24 15:47:45.137744347 +0100
@@ -23,6 +23,9 @@
#include <linux/slab.h>
#include <linux/sysctl.h>
#if IS_ENABLED(CONFIG_IPV6)
if (tw->tw_family == PF_INET6) {
struct ipv6_pinfo *np = inet6_sk(sk);
-diff -NurpP --minimal linux-4.9.217/net/ipv4/udp.c linux-4.9.217-vs2.3.9.12/net/ipv4/udp.c
---- linux-4.9.217/net/ipv4/udp.c 2020-03-27 00:51:46.530089432 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/udp.c 2019-10-05 14:58:46.260298130 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/udp.c linux-4.9/net/ipv4/udp.c
+--- linux-4.9/net/ipv4/udp.c 2021-02-24 15:47:32.607352350 +0100
++++ linux-4.9/net/ipv4/udp.c 2021-02-24 15:47:45.137744347 +0100
@@ -361,12 +361,26 @@ int ipv4_rcv_saddr_equal(const struct so
bool match_wildcard)
{
__u16 destp = ntohs(inet->inet_dport);
__u16 srcp = ntohs(inet->inet_sport);
-diff -NurpP --minimal linux-4.9.217/net/ipv4/udp_diag.c linux-4.9.217-vs2.3.9.12/net/ipv4/udp_diag.c
---- linux-4.9.217/net/ipv4/udp_diag.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv4/udp_diag.c 2018-10-20 06:31:18.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv4/udp_diag.c linux-4.9/net/ipv4/udp_diag.c
+--- linux-4.9/net/ipv4/udp_diag.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/ipv4/udp_diag.c 2021-02-24 15:47:45.137744347 +0100
@@ -120,6 +120,8 @@ static void udp_dump(struct udp_table *t
if (!net_eq(sock_net(sk), net))
if (num < s_num)
goto next;
if (!(r->idiag_states & (1 << sk->sk_state)))
-diff -NurpP --minimal linux-4.9.217/net/ipv6/addrconf.c linux-4.9.217-vs2.3.9.12/net/ipv6/addrconf.c
---- linux-4.9.217/net/ipv6/addrconf.c 2020-03-27 00:51:46.560088955 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/addrconf.c 2020-04-01 09:40:33.385386423 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/addrconf.c linux-4.9/net/ipv6/addrconf.c
+--- linux-4.9/net/ipv6/addrconf.c 2021-02-24 15:47:32.607352350 +0100
++++ linux-4.9/net/ipv6/addrconf.c 2021-02-24 15:47:45.141077785 +0100
@@ -92,6 +92,7 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
}
}
-@@ -4134,7 +4141,10 @@ static void if6_seq_stop(struct seq_file
+@@ -4135,7 +4142,10 @@ static void if6_seq_stop(struct seq_file
static int if6_seq_show(struct seq_file *seq, void *v)
{
struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v;
&ifp->addr,
ifp->idev->dev->ifindex,
ifp->prefix_len,
-@@ -4718,6 +4728,11 @@ static int in6_dump_addrs(struct inet6_d
+@@ -4719,6 +4729,11 @@ static int in6_dump_addrs(struct inet6_d
struct ifacaddr6 *ifaca;
int err = 1;
int ip_idx = *p_ip_idx;
read_lock_bh(&idev->lock);
switch (type) {
-@@ -4728,6 +4743,8 @@ static int in6_dump_addrs(struct inet6_d
+@@ -4729,6 +4744,8 @@ static int in6_dump_addrs(struct inet6_d
list_for_each_entry(ifa, &idev->addr_list, if_list) {
if (ip_idx < s_ip_idx)
goto next;
err = inet6_fill_ifaddr(skb, ifa,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
-@@ -4747,6 +4764,8 @@ next:
+@@ -4748,6 +4765,8 @@ next:
ifmca = ifmca->next, ip_idx++) {
if (ip_idx < s_ip_idx)
continue;
err = inet6_fill_ifmcaddr(skb, ifmca,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
-@@ -4762,6 +4781,8 @@ next:
+@@ -4763,6 +4782,8 @@ next:
ifaca = ifaca->aca_next, ip_idx++) {
if (ip_idx < s_ip_idx)
continue;
err = inet6_fill_ifacaddr(skb, ifaca,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
-@@ -4790,6 +4811,10 @@ static int inet6_dump_addr(struct sk_buf
+@@ -4791,6 +4812,10 @@ static int inet6_dump_addr(struct sk_buf
struct inet6_dev *idev;
struct hlist_head *head;
s_h = cb->args[0];
s_idx = idx = cb->args[1];
s_ip_idx = ip_idx = cb->args[2];
-@@ -5308,6 +5333,7 @@ static int inet6_dump_ifinfo(struct sk_b
+@@ -5309,6 +5334,7 @@ static int inet6_dump_ifinfo(struct sk_b
struct net_device *dev;
struct inet6_dev *idev;
struct hlist_head *head;
s_h = cb->args[0];
s_idx = cb->args[1];
-@@ -5319,6 +5345,8 @@ static int inet6_dump_ifinfo(struct sk_b
+@@ -5320,6 +5346,8 @@ static int inet6_dump_ifinfo(struct sk_b
hlist_for_each_entry_rcu(dev, head, index_hlist) {
if (idx < s_idx)
goto cont;
idev = __in6_dev_get(dev);
if (!idev)
goto cont;
-diff -NurpP --minimal linux-4.9.217/net/ipv6/af_inet6.c linux-4.9.217-vs2.3.9.12/net/ipv6/af_inet6.c
---- linux-4.9.217/net/ipv6/af_inet6.c 2020-03-27 00:51:46.560088955 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/af_inet6.c 2019-02-22 08:37:56.413037657 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/af_inet6.c linux-4.9/net/ipv6/af_inet6.c
+--- linux-4.9/net/ipv6/af_inet6.c 2021-02-24 15:47:32.607352350 +0100
++++ linux-4.9/net/ipv6/af_inet6.c 2021-02-24 15:47:45.141077785 +0100
@@ -43,6 +43,7 @@
#include <linux/netdevice.h>
#include <linux/icmpv6.h>
if (ipv6_addr_any(&sk->sk_v6_rcv_saddr))
sin->sin6_addr = np->saddr;
else
-diff -NurpP --minimal linux-4.9.217/net/ipv6/datagram.c linux-4.9.217-vs2.3.9.12/net/ipv6/datagram.c
---- linux-4.9.217/net/ipv6/datagram.c 2020-03-27 00:51:46.560088955 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/datagram.c 2019-02-22 08:37:56.423037487 +0000
-@@ -779,7 +779,7 @@ int ip6_datagram_send_ctl(struct net *ne
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/datagram.c linux-4.9/net/ipv6/datagram.c
+--- linux-4.9/net/ipv6/datagram.c 2021-02-24 15:47:32.610685787 +0100
++++ linux-4.9/net/ipv6/datagram.c 2021-02-24 15:47:45.141077785 +0100
+@@ -792,7 +792,7 @@ int ip6_datagram_send_ctl(struct net *ne
rcu_read_lock();
if (fl6->flowi6_oif) {
if (!dev) {
rcu_read_unlock();
return -ENODEV;
-diff -NurpP --minimal linux-4.9.217/net/ipv6/fib6_rules.c linux-4.9.217-vs2.3.9.12/net/ipv6/fib6_rules.c
---- linux-4.9.217/net/ipv6/fib6_rules.c 2020-03-27 00:51:46.570088800 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/fib6_rules.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/fib6_rules.c linux-4.9/net/ipv6/fib6_rules.c
+--- linux-4.9/net/ipv6/fib6_rules.c 2021-02-24 15:47:32.610685787 +0100
++++ linux-4.9/net/ipv6/fib6_rules.c 2021-02-24 15:47:45.141077785 +0100
@@ -102,7 +102,7 @@ static int fib6_rule_action(struct fib_r
ip6_dst_idev(&rt->dst)->dev,
&flp6->daddr,
goto again;
if (!ipv6_prefix_equal(&saddr, &r->src.addr,
r->src.plen))
-diff -NurpP --minimal linux-4.9.217/net/ipv6/inet6_hashtables.c linux-4.9.217-vs2.3.9.12/net/ipv6/inet6_hashtables.c
---- linux-4.9.217/net/ipv6/inet6_hashtables.c 2020-03-27 00:51:46.600088328 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/inet6_hashtables.c 2020-04-01 09:40:33.385386423 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/inet6_hashtables.c linux-4.9/net/ipv6/inet6_hashtables.c
+--- linux-4.9/net/ipv6/inet6_hashtables.c 2021-02-24 15:47:32.610685787 +0100
++++ linux-4.9/net/ipv6/inet6_hashtables.c 2021-02-24 15:47:45.141077785 +0100
@@ -16,6 +16,7 @@
#include <linux/module.h>
}
- if (addr_type == IPV6_ADDR_ANY && addr_type2 == IPV6_ADDR_ANY)
-- return 1;
+ /* if both are wildcards, check for overlap */
+ if (addr_type1 == IPV6_ADDR_ANY && addr_type2 == IPV6_ADDR_ANY)
+ return nx_v6_addr_conflict(sk1->sk_nx_info, sk2->sk_nx_info);
-
-- if (addr_type2 == IPV6_ADDR_ANY && match_wildcard &&
-- !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
++
+ /* if both are valid ipv6 addresses, mapped handled above */
+ if (addr_type1 != IPV6_ADDR_ANY && addr_type2 != IPV6_ADDR_ANY &&
+ sk2_rcv_saddr6 && ipv6_addr_equal(sk1_rcv_saddr6, sk2_rcv_saddr6))
return 1;
-- if (addr_type == IPV6_ADDR_ANY && match_wildcard &&
-- !(ipv6_only_sock(sk) && addr_type2 == IPV6_ADDR_MAPPED))
+- if (addr_type2 == IPV6_ADDR_ANY && match_wildcard &&
+- !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
- return 1;
+ if (addr_type1 == IPV6_ADDR_ANY && match_wildcard) {
+ /* ipv6only case handled above */
+ return v6_addr_in_nx_info(sk1->sk_nx_info, sk2_rcv_saddr6, -1);
+ }
-- if (sk2_rcv_saddr6 &&
-- ipv6_addr_equal(&sk->sk_v6_rcv_saddr, sk2_rcv_saddr6))
+- if (addr_type == IPV6_ADDR_ANY && match_wildcard &&
+- !(ipv6_only_sock(sk) && addr_type2 == IPV6_ADDR_MAPPED))
- return 1;
+ if (addr_type2 == IPV6_ADDR_ANY && match_wildcard) {
+ /* ipv6only case handled above */
+ else
+ return v6_addr_in_nx_info(sk2->sk_nx_info, sk1_rcv_saddr6, -1);
+ }
-+
+
+- if (sk2_rcv_saddr6 &&
+- ipv6_addr_equal(&sk->sk_v6_rcv_saddr, sk2_rcv_saddr6))
+- return 1;
+ return 0;
+
+vs_v4:
return 0;
}
-diff -NurpP --minimal linux-4.9.217/net/ipv6/ip6_fib.c linux-4.9.217-vs2.3.9.12/net/ipv6/ip6_fib.c
---- linux-4.9.217/net/ipv6/ip6_fib.c 2020-03-27 00:51:46.610088167 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/ip6_fib.c 2020-04-01 09:40:33.385386423 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/ip6_fib.c linux-4.9/net/ipv6/ip6_fib.c
+--- linux-4.9/net/ipv6/ip6_fib.c 2021-02-24 15:47:32.610685787 +0100
++++ linux-4.9/net/ipv6/ip6_fib.c 2021-02-24 15:47:45.141077785 +0100
@@ -1977,6 +1977,7 @@ static int ipv6_route_seq_show(struct se
struct rt6_info *rt = v;
struct ipv6_route_iter *iter = seq->private;
seq_printf(seq, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
#ifdef CONFIG_IPV6_SUBTREES
-diff -NurpP --minimal linux-4.9.217/net/ipv6/ip6_output.c linux-4.9.217-vs2.3.9.12/net/ipv6/ip6_output.c
---- linux-4.9.217/net/ipv6/ip6_output.c 2020-03-27 00:51:46.610088167 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/ip6_output.c 2019-10-05 14:58:46.270297968 +0000
-@@ -962,7 +962,8 @@ static int ip6_dst_lookup_tail(struct ne
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/ip6_output.c linux-4.9/net/ipv6/ip6_output.c
+--- linux-4.9/net/ipv6/ip6_output.c 2021-02-24 15:47:33.077367054 +0100
++++ linux-4.9/net/ipv6/ip6_output.c 2021-02-24 15:47:45.141077785 +0100
+@@ -966,7 +966,8 @@ static int ip6_dst_lookup_tail(struct ne
rt = (*dst)->error ? NULL : (struct rt6_info *)*dst;
err = ip6_route_get_saddr(net, rt, &fl6->daddr,
sk ? inet6_sk(sk)->srcprefs : 0,
if (err)
goto out_err_release;
-diff -NurpP --minimal linux-4.9.217/net/ipv6/ip6_tunnel.c linux-4.9.217-vs2.3.9.12/net/ipv6/ip6_tunnel.c
---- linux-4.9.217/net/ipv6/ip6_tunnel.c 2020-03-27 00:51:46.610088167 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/ip6_tunnel.c 2020-04-01 09:40:33.385386423 +0000
-@@ -1116,7 +1116,7 @@ route_lookup:
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/ip6_tunnel.c linux-4.9/net/ipv6/ip6_tunnel.c
+--- linux-4.9/net/ipv6/ip6_tunnel.c 2021-02-24 15:47:32.614019225 +0100
++++ linux-4.9/net/ipv6/ip6_tunnel.c 2021-02-24 15:47:45.141077785 +0100
+@@ -1124,7 +1124,7 @@ route_lookup:
}
if (t->parms.collect_md &&
ipv6_dev_get_saddr(net, ip6_dst_idev(dst)->dev,
goto tx_err_link_failure;
ndst = dst;
}
-diff -NurpP --minimal linux-4.9.217/net/ipv6/ndisc.c linux-4.9.217-vs2.3.9.12/net/ipv6/ndisc.c
---- linux-4.9.217/net/ipv6/ndisc.c 2020-03-27 00:51:46.620088013 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/ndisc.c 2019-02-22 08:37:56.423037487 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/ndisc.c linux-4.9/net/ipv6/ndisc.c
+--- linux-4.9/net/ipv6/ndisc.c 2021-02-24 15:47:32.614019225 +0100
++++ linux-4.9/net/ipv6/ndisc.c 2021-02-24 15:47:45.141077785 +0100
@@ -512,7 +512,7 @@ void ndisc_send_na(struct net_device *de
} else {
if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
return;
src_addr = &tmpaddr;
}
-diff -NurpP --minimal linux-4.9.217/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c linux-4.9.217-vs2.3.9.12/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c
---- linux-4.9.217/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c linux-4.9/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c
+--- linux-4.9/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c 2021-02-24 15:47:45.141077785 +0100
@@ -39,7 +39,7 @@ nf_nat_masquerade_ipv6(struct sk_buff *s
ctinfo == IP_CT_RELATED_REPLY));
return NF_DROP;
nfct_nat(ct)->masq_index = out->ifindex;
-diff -NurpP --minimal linux-4.9.217/net/ipv6/raw.c linux-4.9.217-vs2.3.9.12/net/ipv6/raw.c
---- linux-4.9.217/net/ipv6/raw.c 2020-03-27 00:51:46.720086437 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/raw.c 2019-10-05 14:58:46.270297968 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/raw.c linux-4.9/net/ipv6/raw.c
+--- linux-4.9/net/ipv6/raw.c 2021-02-24 15:47:32.617352663 +0100
++++ linux-4.9/net/ipv6/raw.c 2021-02-24 15:47:45.141077785 +0100
@@ -293,6 +293,13 @@ static int rawv6_bind(struct sock *sk, s
goto out_unlock;
}
/* ipv4 addr of the socket is invalid. Only the
* unspecified and mapped address have a v4 equivalent.
*/
-diff -NurpP --minimal linux-4.9.217/net/ipv6/route.c linux-4.9.217-vs2.3.9.12/net/ipv6/route.c
---- linux-4.9.217/net/ipv6/route.c 2020-03-27 00:51:46.720086437 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/route.c 2020-04-01 09:40:33.385386423 +0000
-@@ -3295,7 +3295,8 @@ static int rt6_fill_node(struct net *net
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/route.c linux-4.9/net/ipv6/route.c
+--- linux-4.9/net/ipv6/route.c 2021-02-24 15:47:32.617352663 +0100
++++ linux-4.9/net/ipv6/route.c 2021-02-24 15:47:45.144411223 +0100
+@@ -3297,7 +3297,8 @@ static int rt6_fill_node(struct net *net
goto nla_put_failure;
} else if (dst) {
struct in6_addr saddr_buf;
nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
goto nla_put_failure;
}
-diff -NurpP --minimal linux-4.9.217/net/ipv6/tcp_ipv6.c linux-4.9.217-vs2.3.9.12/net/ipv6/tcp_ipv6.c
---- linux-4.9.217/net/ipv6/tcp_ipv6.c 2020-03-27 00:51:46.720086437 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/tcp_ipv6.c 2019-10-05 14:58:46.270297968 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/tcp_ipv6.c linux-4.9/net/ipv6/tcp_ipv6.c
+--- linux-4.9/net/ipv6/tcp_ipv6.c 2021-02-24 15:47:32.620686100 +0100
++++ linux-4.9/net/ipv6/tcp_ipv6.c 2021-02-24 15:47:45.144411223 +0100
@@ -149,11 +149,18 @@ static int tcp_v6_connect(struct sock *s
*/
}
addr_type = ipv6_addr_type(&usin->sin6_addr);
-diff -NurpP --minimal linux-4.9.217/net/ipv6/udp.c linux-4.9.217-vs2.3.9.12/net/ipv6/udp.c
---- linux-4.9.217/net/ipv6/udp.c 2020-03-27 00:51:46.720086437 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/udp.c 2019-10-05 14:58:46.270297968 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/udp.c linux-4.9/net/ipv6/udp.c
+--- linux-4.9/net/ipv6/udp.c 2021-02-24 15:47:32.620686100 +0100
++++ linux-4.9/net/ipv6/udp.c 2021-02-24 15:47:45.144411223 +0100
@@ -135,6 +135,10 @@ static int compute_score(struct sock *sk
if (inet->inet_dport != sport)
return -1;
}
if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
-diff -NurpP --minimal linux-4.9.217/net/ipv6/xfrm6_policy.c linux-4.9.217-vs2.3.9.12/net/ipv6/xfrm6_policy.c
---- linux-4.9.217/net/ipv6/xfrm6_policy.c 2020-03-27 00:51:46.730086276 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/ipv6/xfrm6_policy.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/ipv6/xfrm6_policy.c linux-4.9/net/ipv6/xfrm6_policy.c
+--- linux-4.9/net/ipv6/xfrm6_policy.c 2021-02-24 15:47:32.620686100 +0100
++++ linux-4.9/net/ipv6/xfrm6_policy.c 2021-02-24 15:47:45.144411223 +0100
@@ -64,7 +64,8 @@ static int xfrm6_get_saddr(struct net *n
return -EHOSTUNREACH;
dst_release(dst);
return 0;
}
-diff -NurpP --minimal linux-4.9.217/net/netfilter/ipvs/ip_vs_xmit.c linux-4.9.217-vs2.3.9.12/net/netfilter/ipvs/ip_vs_xmit.c
---- linux-4.9.217/net/netfilter/ipvs/ip_vs_xmit.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/netfilter/ipvs/ip_vs_xmit.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/netfilter/ipvs/ip_vs_xmit.c linux-4.9/net/netfilter/ipvs/ip_vs_xmit.c
+--- linux-4.9/net/netfilter/ipvs/ip_vs_xmit.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/netfilter/ipvs/ip_vs_xmit.c 2021-02-24 15:47:45.144411223 +0100
@@ -381,7 +381,7 @@ __ip_vs_route_output_v6(struct net *net,
return dst;
if (ipv6_addr_any(&fl6.saddr) &&
goto out_err;
if (do_xfrm) {
dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
-diff -NurpP --minimal linux-4.9.217/net/netlink/af_netlink.c linux-4.9.217-vs2.3.9.12/net/netlink/af_netlink.c
---- linux-4.9.217/net/netlink/af_netlink.c 2020-03-27 00:51:47.580072880 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/netlink/af_netlink.c 2020-04-01 09:40:33.515384269 +0000
+diff -urNp -x '*.orig' linux-4.9/net/netlink/af_netlink.c linux-4.9/net/netlink/af_netlink.c
+--- linux-4.9/net/netlink/af_netlink.c 2021-02-24 15:47:32.654020476 +0100
++++ linux-4.9/net/netlink/af_netlink.c 2021-02-24 15:47:45.144411223 +0100
@@ -63,6 +63,8 @@
#include <linux/hash.h>
#include <linux/genetlink.h>
return nlk;
}
-diff -NurpP --minimal linux-4.9.217/net/packet/diag.c linux-4.9.217-vs2.3.9.12/net/packet/diag.c
---- linux-4.9.217/net/packet/diag.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/packet/diag.c 2018-10-20 06:31:18.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/packet/diag.c linux-4.9/net/packet/diag.c
+--- linux-4.9/net/packet/diag.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/packet/diag.c 2021-02-24 15:47:45.144411223 +0100
@@ -4,6 +4,7 @@
#include <linux/netdevice.h>
#include <linux/packet_diag.h>
if (num < s_num)
goto next;
-diff -NurpP --minimal linux-4.9.217/net/socket.c linux-4.9.217-vs2.3.9.12/net/socket.c
---- linux-4.9.217/net/socket.c 2020-03-27 00:51:49.750038678 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/socket.c 2020-04-01 09:40:33.905377809 +0000
+diff -urNp -x '*.orig' linux-4.9/net/socket.c linux-4.9/net/socket.c
+--- linux-4.9/net/socket.c 2021-02-24 15:47:32.680687977 +0100
++++ linux-4.9/net/socket.c 2021-02-24 15:47:45.144411223 +0100
@@ -100,10 +100,12 @@
#include <net/sock.h>
err = sock1->ops->socketpair(sock1, sock2);
if (err < 0)
-diff -NurpP --minimal linux-4.9.217/net/sunrpc/auth.c linux-4.9.217-vs2.3.9.12/net/sunrpc/auth.c
---- linux-4.9.217/net/sunrpc/auth.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/sunrpc/auth.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/sunrpc/auth.c linux-4.9/net/sunrpc/auth.c
+--- linux-4.9/net/sunrpc/auth.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/sunrpc/auth.c 2021-02-24 15:47:45.144411223 +0100
@@ -15,6 +15,7 @@
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/gss_api.h>
};
dprintk("RPC: %5u looking up %s cred\n",
-diff -NurpP --minimal linux-4.9.217/net/sunrpc/auth_unix.c linux-4.9.217-vs2.3.9.12/net/sunrpc/auth_unix.c
---- linux-4.9.217/net/sunrpc/auth_unix.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/sunrpc/auth_unix.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/net/sunrpc/auth_unix.c linux-4.9/net/sunrpc/auth_unix.c
+--- linux-4.9/net/sunrpc/auth_unix.c 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/net/sunrpc/auth_unix.c 2021-02-24 15:47:45.144411223 +0100
@@ -13,11 +13,13 @@
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/auth.h>
hold = p++;
for (i = 0; i < 16 && gid_valid(cred->uc_gids[i]); i++)
*p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gids[i]));
-diff -NurpP --minimal linux-4.9.217/net/sunrpc/clnt.c linux-4.9.217-vs2.3.9.12/net/sunrpc/clnt.c
---- linux-4.9.217/net/sunrpc/clnt.c 2020-03-27 00:51:49.980035052 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/sunrpc/clnt.c 2019-10-05 14:58:47.230282628 +0000
+diff -urNp -x '*.orig' linux-4.9/net/sunrpc/clnt.c linux-4.9/net/sunrpc/clnt.c
+--- linux-4.9/net/sunrpc/clnt.c 2021-02-24 15:47:32.684021415 +0100
++++ linux-4.9/net/sunrpc/clnt.c 2021-02-24 15:47:45.147744660 +0100
@@ -31,6 +31,7 @@
#include <linux/in.h>
#include <linux/in6.h>
return clnt;
}
-diff -NurpP --minimal linux-4.9.217/net/unix/af_unix.c linux-4.9.217-vs2.3.9.12/net/unix/af_unix.c
---- linux-4.9.217/net/unix/af_unix.c 2020-03-27 00:51:51.400012675 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/unix/af_unix.c 2019-12-25 15:37:53.138410818 +0000
+diff -urNp -x '*.orig' linux-4.9/net/unix/af_unix.c linux-4.9/net/unix/af_unix.c
+--- linux-4.9/net/unix/af_unix.c 2021-02-24 15:47:32.694021728 +0100
++++ linux-4.9/net/unix/af_unix.c 2021-02-24 15:47:45.147744660 +0100
@@ -117,6 +117,8 @@
#include <net/checksum.h>
#include <linux/security.h>
struct hlist_head unix_socket_table[2 * UNIX_HASH_SIZE];
EXPORT_SYMBOL_GPL(unix_socket_table);
-@@ -284,6 +286,8 @@ static struct sock *__unix_find_socket_b
+@@ -290,6 +292,8 @@ static struct sock *__unix_find_socket_b
if (!net_eq(sock_net(s), net))
continue;
if (u->addr->len == len &&
!memcmp(u->addr->name, sunname, len))
goto found;
-@@ -2744,6 +2748,8 @@ static struct sock *unix_from_bucket(str
+@@ -2751,6 +2755,8 @@ static struct sock *unix_from_bucket(str
for (sk = sk_head(&unix_socket_table[bucket]); sk; sk = sk_next(sk)) {
if (sock_net(sk) != seq_file_net(seq))
continue;
if (++count == offset)
break;
}
-@@ -2761,6 +2767,8 @@ static struct sock *unix_next_socket(str
+@@ -2768,6 +2774,8 @@ static struct sock *unix_next_socket(str
sk = sk_next(sk);
if (!sk)
goto next_bucket;
if (sock_net(sk) == seq_file_net(seq))
return sk;
}
-diff -NurpP --minimal linux-4.9.217/net/unix/diag.c linux-4.9.217-vs2.3.9.12/net/unix/diag.c
---- linux-4.9.217/net/unix/diag.c 2020-03-27 00:51:51.400012675 +0000
-+++ linux-4.9.217-vs2.3.9.12/net/unix/diag.c 2019-10-05 14:58:47.230282628 +0000
+diff -urNp -x '*.orig' linux-4.9/net/unix/diag.c linux-4.9/net/unix/diag.c
+--- linux-4.9/net/unix/diag.c 2021-02-24 15:47:32.694021728 +0100
++++ linux-4.9/net/unix/diag.c 2021-02-24 15:47:45.147744660 +0100
@@ -4,6 +4,7 @@
#include <linux/unix_diag.h>
#include <linux/skbuff.h>
if (num < s_num)
goto next;
if (!(req->udiag_states & (1 << sk->sk_state)))
-diff -NurpP --minimal linux-4.9.217/scripts/checksyscalls.sh linux-4.9.217-vs2.3.9.12/scripts/checksyscalls.sh
---- linux-4.9.217/scripts/checksyscalls.sh 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/scripts/checksyscalls.sh 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/scripts/checksyscalls.sh linux-4.9/scripts/checksyscalls.sh
+--- linux-4.9/scripts/checksyscalls.sh 2016-12-11 20:17:54.000000000 +0100
++++ linux-4.9/scripts/checksyscalls.sh 2021-02-24 15:47:45.147744660 +0100
@@ -196,7 +196,6 @@ cat << EOF
#define __IGNORE_afs_syscall
#define __IGNORE_getpmsg
EOF
}
-diff -NurpP --minimal linux-4.9.217/security/commoncap.c linux-4.9.217-vs2.3.9.12/security/commoncap.c
---- linux-4.9.217/security/commoncap.c 2016-12-11 19:17:54.000000000 +0000
-+++ linux-4.9.217-vs2.3.9.12/security/commoncap.c 2018-10-20 04:58:15.000000000 +0000
+diff -urNp -x '*.orig' linux-4.9/security/commoncap.c linux-4.9/security/commoncap.c
+--- linux-4.9/security/commoncap.c 2021-02-24 15:47:32.714022353 +0100
++++ linux-4.9/security/commoncap.c 2021-02-24 15:47:45.147744660 +0100
@@ -71,6 +71,7 @@ static void warn_setuid_and_fcaps_mixed(
int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
int cap, int audit)
/* Have we tried all of the parent namespaces? */
if (ns == &init_user_ns)
-@@ -667,7 +672,7 @@ int cap_inode_setxattr(struct dentry *de
+@@ -668,7 +673,7 @@ int cap_inode_setxattr(struct dentry *de
if (!strncmp(name, XATTR_SECURITY_PREFIX,
sizeof(XATTR_SECURITY_PREFIX) - 1) &&
return -EPERM;
return 0;
}
-@@ -693,7 +698,7 @@ int cap_inode_removexattr(struct dentry
+@@ -694,7 +699,7 @@ int cap_inode_removexattr(struct dentry
if (!strncmp(name, XATTR_SECURITY_PREFIX,
sizeof(XATTR_SECURITY_PREFIX) - 1) &&
return -EPERM;
return 0;
}
-diff -NurpP --minimal linux-4.9.217/security/selinux/hooks.c linux-4.9.217-vs2.3.9.12/security/selinux/hooks.c
---- linux-4.9.217/security/selinux/hooks.c 2020-03-27 00:51:56.679929820 +0000
-+++ linux-4.9.217-vs2.3.9.12/security/selinux/hooks.c 2019-10-05 14:58:47.310281348 +0000
+diff -urNp -x '*.orig' linux-4.9/security/selinux/hooks.c linux-4.9/security/selinux/hooks.c
+--- linux-4.9/security/selinux/hooks.c 2021-02-24 15:47:32.720689229 +0100
++++ linux-4.9/security/selinux/hooks.c 2021-02-24 15:47:45.147744660 +0100
@@ -67,7 +67,6 @@
#include <linux/dccp.h>
#include <linux/quota.h>
%define rel 1
%define basever 4.9
-%define postver .257
+%define postver .258
# define this to '-%{basever}' for longterm branch
%define versuffix -%{basever}
# Source0-md5: 0a68ef3615c64bd5ee54a3320e46667d
%if "%{postver}" != ".0"
Patch0: https://www.kernel.org/pub/linux/kernel/v4.x/patch-%{version}.xz
-# Patch0-md5: 4dc24298b98c19fd51b596e0b80efc4b
+# Patch0-md5: 40cadabdcf54c16c87601c5542a71fce
%endif
Source1: kernel.sysconfig