GoboLinux Downloads Documentation Community Recipes Screenshots

GoboLinux Recipe & Package Search Tool

71 versions of Linux.

ProgramAgeSizeByWWWSummary
Linux 4.13.2-r1 791  88794 Luca...
The Linux Kernel.
Linux 4.9.16-r3 962  100651 Luca...
The Linux Kernel.
view entry at GitHub | download recipe.bz2 file
01-gobohide.patch
02-apple-gmux.patch
03-macbook-suspend.patch
04-apple-poweroff-quirk-workaround.patch
05-gold_linker-relocation.patch
06-gold_linker-kbuild.patch
07-virtio_net-error.patch
Recipe
Resources/BuildDependencies
Resources/BuildInformation
Resources/Dependencies
Resources/Description
cell/02-memcpy-cell.patch
cell/Recipe
cell/dot-config
i686/Recipe
i686/dot-config
ppc/Recipe
ppc/dot-config
x86_64/Recipe
x86_64/WriteBoot64
x86_64/dot-config
diff -Nurp linux-4.9.4.orig/fs/compat.c linux-4.9.4/fs/compat.c
--- linux-4.9.4.orig/fs/compat.c	2017-01-16 18:30:07.006188638 -0200
+++ linux-4.9.4/fs/compat.c	2017-01-16 18:30:16.205188810 -0200
@@ -48,6 +48,7 @@
 #include <linux/slab.h>
 #include <linux/pagemap.h>
 #include <linux/aio.h>
+#include <linux/gobohide.h>
 
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
@@ -830,6 +831,7 @@ struct compat_readdir_callback {
 	struct dir_context ctx;
 	struct compat_old_linux_dirent __user *dirent;
 	int result;
+	struct dentry *dentry;
 };
 
 static int compat_fillonedir(struct dir_context *ctx, const char *name,
@@ -879,6 +881,7 @@ COMPAT_SYSCALL_DEFINE3(old_readdir, unsi
 	if (!f.file)
 		return -EBADF;
 
+	buf.dentry = f.file->f_path.dentry;
 	error = iterate_dir(f.file, &buf.ctx);
 	if (buf.result)
 		error = buf.result;
@@ -900,6 +903,7 @@ struct compat_getdents_callback {
 	struct compat_linux_dirent __user *previous;
 	int count;
 	int error;
+	struct dentry *dentry;
 };
 
 static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
@@ -909,6 +913,7 @@ static int compat_filldir(struct dir_con
 	struct compat_getdents_callback *buf =
 		container_of(ctx, struct compat_getdents_callback, ctx);
 	compat_ulong_t d_ino;
+	struct hide *hidden;
 	int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
 		namlen + 2, sizeof(compat_long_t));
 
@@ -922,12 +927,22 @@ static int compat_filldir(struct dir_con
 	}
 	dirent = buf->previous;
 	if (dirent) {
+		hidden = gobohide_get(d_ino, name, namlen, buf->dentry);
+		if (hidden) {
+			gobohide_put(hidden);
+			return 0;
+		}
 		if (signal_pending(current))
 			return -EINTR;
 		if (__put_user(offset, &dirent->d_off))
 			goto efault;
 	}
 	dirent = buf->current_dir;
+	hidden = gobohide_get(d_ino, name, namlen, buf->dentry);
+	if (hidden) {
+		gobohide_put(hidden);
+		return 0;
+	}
 	if (__put_user(d_ino, &dirent->d_ino))
 		goto efault;
 	if (__put_user(reclen, &dirent->d_reclen))
@@ -967,6 +982,7 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigne
 	if (!f.file)
 		return -EBADF;
 
+	buf.dentry = f.file->f_path.dentry;
 	error = iterate_dir(f.file, &buf.ctx);
 	if (error >= 0)
 		error = buf.error;
@@ -989,6 +1005,7 @@ struct compat_getdents_callback64 {
 	struct linux_dirent64 __user *previous;
 	int count;
 	int error;
+	struct dentry *dentry;
 };
 
 static int compat_filldir64(struct dir_context *ctx, const char *name,
@@ -996,6 +1013,7 @@ static int compat_filldir64(struct dir_c
 			    unsigned int d_type)
 {
 	struct linux_dirent64 __user *dirent;
+	struct hide *hidden;
 	struct compat_getdents_callback64 *buf =
 		container_of(ctx, struct compat_getdents_callback64, ctx);
 	int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
@@ -1008,12 +1026,22 @@ static int compat_filldir64(struct dir_c
 	dirent = buf->previous;
 
 	if (dirent) {
+		hidden = gobohide_get(ino, name, namlen, buf->dentry);
+		if (hidden) {
+			gobohide_put(hidden);
+			return 0;
+		}
 		if (signal_pending(current))
 			return -EINTR;
 		if (__put_user_unaligned(offset, &dirent->d_off))
 			goto efault;
 	}
 	dirent = buf->current_dir;
+	hidden = gobohide_get(ino, name, namlen, buf->dentry);
+	if (hidden) {
+		gobohide_put(hidden);
+		return 0;
+	}
 	if (__put_user_unaligned(ino, &dirent->d_ino))
 		goto efault;
 	off = 0;
@@ -1056,6 +1084,7 @@ COMPAT_SYSCALL_DEFINE3(getdents64, unsig
 	if (!f.file)
 		return -EBADF;
 
+	buf.dentry = f.file->f_path.dentry;
 	error = iterate_dir(f.file, &buf.ctx);
 	if (error >= 0)
 		error = buf.error;
diff -Nurp linux-4.9.4.orig/fs/compat.c.orig linux-4.9.4/fs/compat.c.orig
--- linux-4.9.4.orig/fs/compat.c.orig	1969-12-31 21:00:00.000000000 -0300
+++ linux-4.9.4/fs/compat.c.orig	2017-01-15 10:43:07.000000000 -0200
@@ -0,0 +1,1470 @@
+/*
+ *  linux/fs/compat.c
+ *
+ *  Kernel compatibililty routines for e.g. 32 bit syscall support
+ *  on 64 bit kernels.
+ *
+ *  Copyright (C) 2002       Stephen Rothwell, IBM Corporation
+ *  Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
+ *  Copyright (C) 1998       Eddie C. Dost  (ecd@skynet.be)
+ *  Copyright (C) 2001,2002  Andi Kleen, SuSE Labs 
+ *  Copyright (C) 2003       Pavel Machek (pavel@ucw.cz)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/linkage.h>
+#include <linux/compat.h>
+#include <linux/errno.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/namei.h>
+#include <linux/file.h>
+#include <linux/fdtable.h>
+#include <linux/vfs.h>
+#include <linux/ioctl.h>
+#include <linux/init.h>
+#include <linux/ncp_mount.h>
+#include <linux/nfs4_mount.h>
+#include <linux/syscalls.h>
+#include <linux/ctype.h>
+#include <linux/dirent.h>
+#include <linux/fsnotify.h>
+#include <linux/highuid.h>
+#include <linux/personality.h>
+#include <linux/rwsem.h>
+#include <linux/tsacct_kern.h>
+#include <linux/security.h>
+#include <linux/highmem.h>
+#include <linux/signal.h>
+#include <linux/poll.h>
+#include <linux/mm.h>
+#include <linux/fs_struct.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/aio.h>
+
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+#include <asm/ioctls.h>
+#include "internal.h"
+
+/*
+ * Not all architectures have sys_utime, so implement this in terms
+ * of sys_utimes.
+ */
+COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename,
+		       struct compat_utimbuf __user *, t)
+{
+	struct timespec tv[2];
+
+	if (t) {
+		if (get_user(tv[0].tv_sec, &t->actime) ||
+		    get_user(tv[1].tv_sec, &t->modtime))
+			return -EFAULT;
+		tv[0].tv_nsec = 0;
+		tv[1].tv_nsec = 0;
+	}
+	return do_utimes(AT_FDCWD, filename, t ? tv : NULL, 0);
+}
+
+COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filename, \
struct compat_timespec __user *, t, int, flags)
+{
+	struct timespec tv[2];
+
+	if  (t) {
+		if (compat_get_timespec(&tv[0], &t[0]) ||
+		    compat_get_timespec(&tv[1], &t[1]))
+			return -EFAULT;
+
+		if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT)
+			return 0;
+	}
+	return do_utimes(dfd, filename, t ? tv : NULL, flags);
+}
+
+COMPAT_SYSCALL_DEFINE3(futimesat, unsigned int, dfd, const char __user *, filename, \
struct compat_timeval __user *, t)
+{
+	struct timespec tv[2];
+
+	if (t) {
+		if (get_user(tv[0].tv_sec, &t[0].tv_sec) ||
+		    get_user(tv[0].tv_nsec, &t[0].tv_usec) ||
+		    get_user(tv[1].tv_sec, &t[1].tv_sec) ||
+		    get_user(tv[1].tv_nsec, &t[1].tv_usec))
+			return -EFAULT;
+		if (tv[0].tv_nsec >= 1000000 || tv[0].tv_nsec < 0 ||
+		    tv[1].tv_nsec >= 1000000 || tv[1].tv_nsec < 0)
+			return -EINVAL;
+		tv[0].tv_nsec *= 1000;
+		tv[1].tv_nsec *= 1000;
+	}
+	return do_utimes(dfd, filename, t ? tv : NULL, 0);
+}
+
+COMPAT_SYSCALL_DEFINE2(utimes, const char __user *, filename, struct compat_timeval \
__user *, t)
+{
+	return compat_sys_futimesat(AT_FDCWD, filename, t);
+}
+
+static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
+{
+	struct compat_stat tmp;
+
+	if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev))
+		return -EOVERFLOW;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.st_dev = old_encode_dev(stat->dev);
+	tmp.st_ino = stat->ino;
+	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
+		return -EOVERFLOW;
+	tmp.st_mode = stat->mode;
+	tmp.st_nlink = stat->nlink;
+	if (tmp.st_nlink != stat->nlink)
+		return -EOVERFLOW;
+	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
+	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
+	tmp.st_rdev = old_encode_dev(stat->rdev);
+	if ((u64) stat->size > MAX_NON_LFS)
+		return -EOVERFLOW;
+	tmp.st_size = stat->size;
+	tmp.st_atime = stat->atime.tv_sec;
+	tmp.st_atime_nsec = stat->atime.tv_nsec;
+	tmp.st_mtime = stat->mtime.tv_sec;
+	tmp.st_mtime_nsec = stat->mtime.tv_nsec;
+	tmp.st_ctime = stat->ctime.tv_sec;
+	tmp.st_ctime_nsec = stat->ctime.tv_nsec;
+	tmp.st_blocks = stat->blocks;
+	tmp.st_blksize = stat->blksize;
+	return copy_to_user(ubuf, &tmp, sizeof(tmp)) ? -EFAULT : 0;
+}
+
+COMPAT_SYSCALL_DEFINE2(newstat, const char __user *, filename,
+		       struct compat_stat __user *, statbuf)
+{
+	struct kstat stat;
+	int error;
+
+	error = vfs_stat(filename, &stat);
+	if (error)
+		return error;
+	return cp_compat_stat(&stat, statbuf);
+}
+
+COMPAT_SYSCALL_DEFINE2(newlstat, const char __user *, filename,
+		       struct compat_stat __user *, statbuf)
+{
+	struct kstat stat;
+	int error;
+
+	error = vfs_lstat(filename, &stat);
+	if (error)
+		return error;
+	return cp_compat_stat(&stat, statbuf);
+}
+
+#ifndef __ARCH_WANT_STAT64
+COMPAT_SYSCALL_DEFINE4(newfstatat, unsigned int, dfd,
+		       const char __user *, filename,
+		       struct compat_stat __user *, statbuf, int, flag)
+{
+	struct kstat stat;
+	int error;
+
+	error = vfs_fstatat(dfd, filename, &stat, flag);
+	if (error)
+		return error;
+	return cp_compat_stat(&stat, statbuf);
+}
+#endif
+
+COMPAT_SYSCALL_DEFINE2(newfstat, unsigned int, fd,
+		       struct compat_stat __user *, statbuf)
+{
+	struct kstat stat;
+	int error = vfs_fstat(fd, &stat);
+
+	if (!error)
+		error = cp_compat_stat(&stat, statbuf);
+	return error;
+}
+
+static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *kbuf)
+{
+	
+	if (sizeof ubuf->f_blocks == 4) {
+		if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail |
+		     kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL)
+			return -EOVERFLOW;
+		/* f_files and f_ffree may be -1; it's okay
+		 * to stuff that into 32 bits */
+		if (kbuf->f_files != 0xffffffffffffffffULL
+		 && (kbuf->f_files & 0xffffffff00000000ULL))
+			return -EOVERFLOW;
+		if (kbuf->f_ffree != 0xffffffffffffffffULL
+		 && (kbuf->f_ffree & 0xffffffff00000000ULL))
+			return -EOVERFLOW;
+	}
+	if (!access_ok(VERIFY_WRITE, ubuf, sizeof(*ubuf)) ||
+	    __put_user(kbuf->f_type, &ubuf->f_type) ||
+	    __put_user(kbuf->f_bsize, &ubuf->f_bsize) ||
+	    __put_user(kbuf->f_blocks, &ubuf->f_blocks) ||
+	    __put_user(kbuf->f_bfree, &ubuf->f_bfree) ||
+	    __put_user(kbuf->f_bavail, &ubuf->f_bavail) ||
+	    __put_user(kbuf->f_files, &ubuf->f_files) ||
+	    __put_user(kbuf->f_ffree, &ubuf->f_ffree) ||
+	    __put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
+	    __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
+	    __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
+	    __put_user(kbuf->f_frsize, &ubuf->f_frsize) ||
+	    __put_user(kbuf->f_flags, &ubuf->f_flags) ||
+	    __clear_user(ubuf->f_spare, sizeof(ubuf->f_spare)))
+		return -EFAULT;
+	return 0;
+}
+
+/*
+ * The following statfs calls are copies of code from fs/statfs.c and
+ * should be checked against those from time to time
+ */
+COMPAT_SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct compat_statfs \
__user *, buf)
+{
+	struct kstatfs tmp;
+	int error = user_statfs(pathname, &tmp);
+	if (!error)
+		error = put_compat_statfs(buf, &tmp);
+	return error;
+}
+
+COMPAT_SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct compat_statfs __user *, \
buf)
+{
+	struct kstatfs tmp;
+	int error = fd_statfs(fd, &tmp);
+	if (!error)
+		error = put_compat_statfs(buf, &tmp);
+	return error;
+}
+
+static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs \
*kbuf)
+{
+	if (sizeof ubuf->f_blocks == 4) {
+		if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail |
+		     kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL)
+			return -EOVERFLOW;
+		/* f_files and f_ffree may be -1; it's okay
+		 * to stuff that into 32 bits */
+		if (kbuf->f_files != 0xffffffffffffffffULL
+		 && (kbuf->f_files & 0xffffffff00000000ULL))
+			return -EOVERFLOW;
+		if (kbuf->f_ffree != 0xffffffffffffffffULL
+		 && (kbuf->f_ffree & 0xffffffff00000000ULL))
+			return -EOVERFLOW;
+	}
+	if (!access_ok(VERIFY_WRITE, ubuf, sizeof(*ubuf)) ||
+	    __put_user(kbuf->f_type, &ubuf->f_type) ||
+	    __put_user(kbuf->f_bsize, &ubuf->f_bsize) ||
+	    __put_user(kbuf->f_blocks, &ubuf->f_blocks) ||
+	    __put_user(kbuf->f_bfree, &ubuf->f_bfree) ||
+	    __put_user(kbuf->f_bavail, &ubuf->f_bavail) ||
+	    __put_user(kbuf->f_files, &ubuf->f_files) ||
+	    __put_user(kbuf->f_ffree, &ubuf->f_ffree) ||
+	    __put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
+	    __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
+	    __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
+	    __put_user(kbuf->f_frsize, &ubuf->f_frsize) ||
+	    __put_user(kbuf->f_flags, &ubuf->f_flags) ||
+	    __clear_user(ubuf->f_spare, sizeof(ubuf->f_spare)))
+		return -EFAULT;
+	return 0;
+}
+
+COMPAT_SYSCALL_DEFINE3(statfs64, const char __user *, pathname, compat_size_t, sz, \
struct compat_statfs64 __user *, buf)
+{
+	struct kstatfs tmp;
+	int error;
+
+	if (sz != sizeof(*buf))
+		return -EINVAL;
+
+	error = user_statfs(pathname, &tmp);
+	if (!error)
+		error = put_compat_statfs64(buf, &tmp);
+	return error;
+}
+
+COMPAT_SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, compat_size_t, sz, struct compat_statfs64 \
__user *, buf)
+{
+	struct kstatfs tmp;
+	int error;
+
+	if (sz != sizeof(*buf))
+		return -EINVAL;
+
+	error = fd_statfs(fd, &tmp);
+	if (!error)
+		error = put_compat_statfs64(buf, &tmp);
+	return error;
+}
+
+/*
+ * This is a copy of sys_ustat, just dealing with a structure layout.
+ * Given how simple this syscall is that apporach is more maintainable
+ * than the various conversion hacks.
+ */
+COMPAT_SYSCALL_DEFINE2(ustat, unsigned, dev, struct compat_ustat __user *, u)
+{
+	struct compat_ustat tmp;
+	struct kstatfs sbuf;
+	int err = vfs_ustat(new_decode_dev(dev), &sbuf);
+	if (err)
+		return err;
+
+	memset(&tmp, 0, sizeof(struct compat_ustat));
+	tmp.f_tfree = sbuf.f_bfree;
+	tmp.f_tinode = sbuf.f_ffree;
+	if (copy_to_user(u, &tmp, sizeof(struct compat_ustat)))
+		return -EFAULT;
+	return 0;
+}
+
+static int get_compat_flock(struct flock *kfl, struct compat_flock __user *ufl)
+{
+	if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) ||
+	    __get_user(kfl->l_type, &ufl->l_type) ||
+	    __get_user(kfl->l_whence, &ufl->l_whence) ||
+	    __get_user(kfl->l_start, &ufl->l_start) ||
+	    __get_user(kfl->l_len, &ufl->l_len) ||
+	    __get_user(kfl->l_pid, &ufl->l_pid))
+		return -EFAULT;
+	return 0;
+}
+
+static int put_compat_flock(struct flock *kfl, struct compat_flock __user *ufl)
+{
+	if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) ||
+	    __put_user(kfl->l_type, &ufl->l_type) ||
+	    __put_user(kfl->l_whence, &ufl->l_whence) ||
+	    __put_user(kfl->l_start, &ufl->l_start) ||
+	    __put_user(kfl->l_len, &ufl->l_len) ||
+	    __put_user(kfl->l_pid, &ufl->l_pid))
+		return -EFAULT;
+	return 0;
+}
+
+#ifndef HAVE_ARCH_GET_COMPAT_FLOCK64
+static int get_compat_flock64(struct flock *kfl, struct compat_flock64 __user *ufl)
+{
+	if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)) ||
+	    __get_user(kfl->l_type, &ufl->l_type) ||
+	    __get_user(kfl->l_whence, &ufl->l_whence) ||
+	    __get_user(kfl->l_start, &ufl->l_start) ||
+	    __get_user(kfl->l_len, &ufl->l_len) ||
+	    __get_user(kfl->l_pid, &ufl->l_pid))
+		return -EFAULT;
+	return 0;
+}
+#endif
+
+#ifndef HAVE_ARCH_PUT_COMPAT_FLOCK64
+static int put_compat_flock64(struct flock *kfl, struct compat_flock64 __user *ufl)
+{
+	if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)) ||
+	    __put_user(kfl->l_type, &ufl->l_type) ||
+	    __put_user(kfl->l_whence, &ufl->l_whence) ||
+	    __put_user(kfl->l_start, &ufl->l_start) ||
+	    __put_user(kfl->l_len, &ufl->l_len) ||
+	    __put_user(kfl->l_pid, &ufl->l_pid))
+		return -EFAULT;
+	return 0;
+}
+#endif
+
+static unsigned int
+convert_fcntl_cmd(unsigned int cmd)
+{
+	switch (cmd) {
+	case F_GETLK64:
+		return F_GETLK;
+	case F_SETLK64:
+		return F_SETLK;
+	case F_SETLKW64:
+		return F_SETLKW;
+	}
+
+	return cmd;
+}
+
+COMPAT_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
+		       compat_ulong_t, arg)
+{
+	mm_segment_t old_fs;
+	struct flock f;
+	long ret;
+	unsigned int conv_cmd;
+
+	switch (cmd) {
+	case F_GETLK:
+	case F_SETLK:
+	case F_SETLKW:
+		ret = get_compat_flock(&f, compat_ptr(arg));
+		if (ret != 0)
+			break;
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+		ret = sys_fcntl(fd, cmd, (unsigned long)&f);
+		set_fs(old_fs);
+		if (cmd == F_GETLK && ret == 0) {
+			/* GETLK was successful and we need to return the data...
+			 * but it needs to fit in the compat structure.
+			 * l_start shouldn't be too big, unless the original
+			 * start + end is greater than COMPAT_OFF_T_MAX, in which
+			 * case the app was asking for trouble, so we return
+			 * -EOVERFLOW in that case.
+			 * l_len could be too big, in which case we just truncate it,
+			 * and only allow the app to see that part of the conflicting
+			 * lock that might make sense to it anyway
+			 */
+
+			if (f.l_start > COMPAT_OFF_T_MAX)
+				ret = -EOVERFLOW;
+			if (f.l_len > COMPAT_OFF_T_MAX)
+				f.l_len = COMPAT_OFF_T_MAX;
+			if (ret == 0)
+				ret = put_compat_flock(&f, compat_ptr(arg));
+		}
+		break;
+
+	case F_GETLK64:
+	case F_SETLK64:
+	case F_SETLKW64:
+	case F_OFD_GETLK:
+	case F_OFD_SETLK:
+	case F_OFD_SETLKW:
+		ret = get_compat_flock64(&f, compat_ptr(arg));
+		if (ret != 0)
+			break;
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+		conv_cmd = convert_fcntl_cmd(cmd);
+		ret = sys_fcntl(fd, conv_cmd, (unsigned long)&f);
+		set_fs(old_fs);
+		if ((conv_cmd == F_GETLK || conv_cmd == F_OFD_GETLK) && ret == 0) {
+			/* need to return lock information - see above for commentary */
+			if (f.l_start > COMPAT_LOFF_T_MAX)
+				ret = -EOVERFLOW;
+			if (f.l_len > COMPAT_LOFF_T_MAX)
+				f.l_len = COMPAT_LOFF_T_MAX;
+			if (ret == 0)
+				ret = put_compat_flock64(&f, compat_ptr(arg));
+		}
+		break;
+
+	default:
+		ret = sys_fcntl(fd, cmd, arg);
+		break;
+	}
+	return ret;
+}
+
+COMPAT_SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd,
+		       compat_ulong_t, arg)
+{
+	switch (cmd) {
+	case F_GETLK64:
+	case F_SETLK64:
+	case F_SETLKW64:
+	case F_OFD_GETLK:
+	case F_OFD_SETLK:
+	case F_OFD_SETLKW:
+		return -EINVAL;
+	}
+	return compat_sys_fcntl64(fd, cmd, arg);
+}
+
+COMPAT_SYSCALL_DEFINE2(io_setup, unsigned, nr_reqs, u32 __user *, ctx32p)
+{
+	long ret;
+	aio_context_t ctx64;
+
+	mm_segment_t oldfs = get_fs();
+	if (unlikely(get_user(ctx64, ctx32p)))
+		return -EFAULT;
+
+	set_fs(KERNEL_DS);
+	/* The __user pointer cast is valid because of the set_fs() */
+	ret = sys_io_setup(nr_reqs, (aio_context_t __user *) &ctx64);
+	set_fs(oldfs);
+	/* truncating is ok because it's a user address */
+	if (!ret)
+		ret = put_user((u32) ctx64, ctx32p);
+	return ret;
+}
+
+COMPAT_SYSCALL_DEFINE5(io_getevents, compat_aio_context_t, ctx_id,
+		       compat_long_t, min_nr,
+		       compat_long_t, nr,
+		       struct io_event __user *, events,
+		       struct compat_timespec __user *, timeout)
+{
+	struct timespec t;
+	struct timespec __user *ut = NULL;
+
+	if (timeout) {
+		if (compat_get_timespec(&t, timeout))
+			return -EFAULT;
+
+		ut = compat_alloc_user_space(sizeof(*ut));
+		if (copy_to_user(ut, &t, sizeof(t)) )
+			return -EFAULT;
+	} 
+	return sys_io_getevents(ctx_id, min_nr, nr, events, ut);
+}
+
+/* A write operation does a read from user space and vice versa */
+#define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)
+
+ssize_t compat_rw_copy_check_uvector(int type,
+		const struct compat_iovec __user *uvector, unsigned long nr_segs,
+		unsigned long fast_segs, struct iovec *fast_pointer,
+		struct iovec **ret_pointer)
+{
+	compat_ssize_t tot_len;
+	struct iovec *iov = *ret_pointer = fast_pointer;
+	ssize_t ret = 0;
+	int seg;
+
+	/*
+	 * SuS says "The readv() function *may* fail if the iovcnt argument
+	 * was less than or equal to 0, or greater than {IOV_MAX}.  Linux has
+	 * traditionally returned zero for zero segments, so...
+	 */
+	if (nr_segs == 0)
+		goto out;
+
+	ret = -EINVAL;
+	if (nr_segs > UIO_MAXIOV)
+		goto out;
+	if (nr_segs > fast_segs) {
+		ret = -ENOMEM;
+		iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
+		if (iov == NULL)
+			goto out;
+	}
+	*ret_pointer = iov;
+
+	ret = -EFAULT;
+	if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector)))
+		goto out;
+
+	/*
+	 * Single unix specification:
+	 * We should -EINVAL if an element length is not >= 0 and fitting an
+	 * ssize_t.
+	 *
+	 * In Linux, the total length is limited to MAX_RW_COUNT, there is
+	 * no overflow possibility.
+	 */
+	tot_len = 0;
+	ret = -EINVAL;
+	for (seg = 0; seg < nr_segs; seg++) {
+		compat_uptr_t buf;
+		compat_ssize_t len;
+
+		if (__get_user(len, &uvector->iov_len) ||
+		   __get_user(buf, &uvector->iov_base)) {
+			ret = -EFAULT;
+			goto out;
+		}
+		if (len < 0)	/* size_t not fitting in compat_ssize_t .. */
+			goto out;
+		if (type >= 0 &&
+		    !access_ok(vrfy_dir(type), compat_ptr(buf), len)) {
+			ret = -EFAULT;
+			goto out;
+		}
+		if (len > MAX_RW_COUNT - tot_len)
+			len = MAX_RW_COUNT - tot_len;
+		tot_len += len;
+		iov->iov_base = compat_ptr(buf);
+		iov->iov_len = (compat_size_t) len;
+		uvector++;
+		iov++;
+	}
+	ret = tot_len;
+
+out:
+	return ret;
+}
+
+static inline long
+copy_iocb(long nr, u32 __user *ptr32, struct iocb __user * __user *ptr64)
+{
+	compat_uptr_t uptr;
+	int i;
+
+	for (i = 0; i < nr; ++i) {
+		if (get_user(uptr, ptr32 + i))
+			return -EFAULT;
+		if (put_user(compat_ptr(uptr), ptr64 + i))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+#define MAX_AIO_SUBMITS 	(PAGE_SIZE/sizeof(struct iocb *))
+
+COMPAT_SYSCALL_DEFINE3(io_submit, compat_aio_context_t, ctx_id,
+		       int, nr, u32 __user *, iocb)
+{
+	struct iocb __user * __user *iocb64; 
+	long ret;
+
+	if (unlikely(nr < 0))
+		return -EINVAL;
+
+	if (nr > MAX_AIO_SUBMITS)
+		nr = MAX_AIO_SUBMITS;
+	
+	iocb64 = compat_alloc_user_space(nr * sizeof(*iocb64));
+	ret = copy_iocb(nr, iocb, iocb64);
+	if (!ret)
+		ret = do_io_submit(ctx_id, nr, iocb64, 1);
+	return ret;
+}
+
+struct compat_ncp_mount_data {
+	compat_int_t version;
+	compat_uint_t ncp_fd;
+	__compat_uid_t mounted_uid;
+	compat_pid_t wdog_pid;
+	unsigned char mounted_vol[NCP_VOLNAME_LEN + 1];
+	compat_uint_t time_out;
+	compat_uint_t retry_count;
+	compat_uint_t flags;
+	__compat_uid_t uid;
+	__compat_gid_t gid;
+	compat_mode_t file_mode;
+	compat_mode_t dir_mode;
+};
+
+struct compat_ncp_mount_data_v4 {
+	compat_int_t version;
+	compat_ulong_t flags;
+	compat_ulong_t mounted_uid;
+	compat_long_t wdog_pid;
+	compat_uint_t ncp_fd;
+	compat_uint_t time_out;
+	compat_uint_t retry_count;
+	compat_ulong_t uid;
+	compat_ulong_t gid;
+	compat_ulong_t file_mode;
+	compat_ulong_t dir_mode;
+};
+
+static void *do_ncp_super_data_conv(void *raw_data)
+{
+	int version = *(unsigned int *)raw_data;
+
+	if (version == 3) {
+		struct compat_ncp_mount_data *c_n = raw_data;
+		struct ncp_mount_data *n = raw_data;
+
+		n->dir_mode = c_n->dir_mode;
+		n->file_mode = c_n->file_mode;
+		n->gid = c_n->gid;
+		n->uid = c_n->uid;
+		memmove (n->mounted_vol, c_n->mounted_vol, (sizeof (c_n->mounted_vol) + 3 * sizeof \
(unsigned int)));
+		n->wdog_pid = c_n->wdog_pid;
+		n->mounted_uid = c_n->mounted_uid;
+	} else if (version == 4) {
+		struct compat_ncp_mount_data_v4 *c_n = raw_data;
+		struct ncp_mount_data_v4 *n = raw_data;
+
+		n->dir_mode = c_n->dir_mode;
+		n->file_mode = c_n->file_mode;
+		n->gid = c_n->gid;
+		n->uid = c_n->uid;
+		n->retry_count = c_n->retry_count;
+		n->time_out = c_n->time_out;
+		n->ncp_fd = c_n->ncp_fd;
+		n->wdog_pid = c_n->wdog_pid;
+		n->mounted_uid = c_n->mounted_uid;
+		n->flags = c_n->flags;
+	} else if (version != 5) {
+		return NULL;
+	}
+
+	return raw_data;
+}
+
+
+struct compat_nfs_string {
+	compat_uint_t len;
+	compat_uptr_t data;
+};
+
+static inline void compat_nfs_string(struct nfs_string *dst,
+				     struct compat_nfs_string *src)
+{
+	dst->data = compat_ptr(src->data);
+	dst->len = src->len;
+}
+
+struct compat_nfs4_mount_data_v1 {
+	compat_int_t version;
+	compat_int_t flags;
+	compat_int_t rsize;
+	compat_int_t wsize;
+	compat_int_t timeo;
+	compat_int_t retrans;
+	compat_int_t acregmin;
+	compat_int_t acregmax;
+	compat_int_t acdirmin;
+	compat_int_t acdirmax;
+	struct compat_nfs_string client_addr;
+	struct compat_nfs_string mnt_path;
+	struct compat_nfs_string hostname;
+	compat_uint_t host_addrlen;
+	compat_uptr_t host_addr;
+	compat_int_t proto;
+	compat_int_t auth_flavourlen;
+	compat_uptr_t auth_flavours;
+};
+
+static int do_nfs4_super_data_conv(void *raw_data)
+{
+	int version = *(compat_uint_t *) raw_data;
+
+	if (version == 1) {
+		struct compat_nfs4_mount_data_v1 *raw = raw_data;
+		struct nfs4_mount_data *real = raw_data;
+
+		/* copy the fields backwards */
+		real->auth_flavours = compat_ptr(raw->auth_flavours);
+		real->auth_flavourlen = raw->auth_flavourlen;
+		real->proto = raw->proto;
+		real->host_addr = compat_ptr(raw->host_addr);
+		real->host_addrlen = raw->host_addrlen;
+		compat_nfs_string(&real->hostname, &raw->hostname);
+		compat_nfs_string(&real->mnt_path, &raw->mnt_path);
+		compat_nfs_string(&real->client_addr, &raw->client_addr);
+		real->acdirmax = raw->acdirmax;
+		real->acdirmin = raw->acdirmin;
+		real->acregmax = raw->acregmax;
+		real->acregmin = raw->acregmin;
+		real->retrans = raw->retrans;
+		real->timeo = raw->timeo;
+		real->wsize = raw->wsize;
+		real->rsize = raw->rsize;
+		real->flags = raw->flags;
+		real->version = raw->version;
+	}
+
+	return 0;
+}
+
+#define NCPFS_NAME      "ncpfs"
+#define NFS4_NAME	"nfs4"
+
+COMPAT_SYSCALL_DEFINE5(mount, const char __user *, dev_name,
+		       const char __user *, dir_name,
+		       const char __user *, type, compat_ulong_t, flags,
+		       const void __user *, data)
+{
+	char *kernel_type;
+	void *options;
+	char *kernel_dev;
+	int retval;
+
+	kernel_type = copy_mount_string(type);
+	retval = PTR_ERR(kernel_type);
+	if (IS_ERR(kernel_type))
+		goto out;
+
+	kernel_dev = copy_mount_string(dev_name);
+	retval = PTR_ERR(kernel_dev);
+	if (IS_ERR(kernel_dev))
+		goto out1;
+
+	options = copy_mount_options(data);
+	retval = PTR_ERR(options);
+	if (IS_ERR(options))
+		goto out2;
+
+	if (kernel_type && options) {
+		if (!strcmp(kernel_type, NCPFS_NAME)) {
+			do_ncp_super_data_conv(options);
+		} else if (!strcmp(kernel_type, NFS4_NAME)) {
+			retval = -EINVAL;
+			if (do_nfs4_super_data_conv(options))
+				goto out3;
+		}
+	}
+
+	retval = do_mount(kernel_dev, dir_name, kernel_type, flags, options);
+
+ out3:
+	kfree(options);
+ out2:
+	kfree(kernel_dev);
+ out1:
+	kfree(kernel_type);
+ out:
+	return retval;
+}
+
+struct compat_old_linux_dirent {
+	compat_ulong_t	d_ino;
+	compat_ulong_t	d_offset;
+	unsigned short	d_namlen;
+	char		d_name[1];
+};
+
+struct compat_readdir_callback {
+	struct dir_context ctx;
+	struct compat_old_linux_dirent __user *dirent;
+	int result;
+};
+
+static int compat_fillonedir(struct dir_context *ctx, const char *name,
+			     int namlen, loff_t offset, u64 ino,
+			     unsigned int d_type)
+{
+	struct compat_readdir_callback *buf =
+		container_of(ctx, struct compat_readdir_callback, ctx);
+	struct compat_old_linux_dirent __user *dirent;
+	compat_ulong_t d_ino;
+
+	if (buf->result)
+		return -EINVAL;
+	d_ino = ino;
+	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
+		buf->result = -EOVERFLOW;
+		return -EOVERFLOW;
+	}
+	buf->result++;
+	dirent = buf->dirent;
+	if (!access_ok(VERIFY_WRITE, dirent,
+			(unsigned long)(dirent->d_name + namlen + 1) -
+				(unsigned long)dirent))
+		goto efault;
+	if (	__put_user(d_ino, &dirent->d_ino) ||
+		__put_user(offset, &dirent->d_offset) ||
+		__put_user(namlen, &dirent->d_namlen) ||
+		__copy_to_user(dirent->d_name, name, namlen) ||
+		__put_user(0, dirent->d_name + namlen))
+		goto efault;
+	return 0;
+efault:
+	buf->result = -EFAULT;
+	return -EFAULT;
+}
+
+COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
+		struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
+{
+	int error;
+	struct fd f = fdget_pos(fd);
+	struct compat_readdir_callback buf = {
+		.ctx.actor = compat_fillonedir,
+		.dirent = dirent
+	};
+
+	if (!f.file)
+		return -EBADF;
+
+	error = iterate_dir(f.file, &buf.ctx);
+	if (buf.result)
+		error = buf.result;
+
+	fdput_pos(f);
+	return error;
+}
+
+struct compat_linux_dirent {
+	compat_ulong_t	d_ino;
+	compat_ulong_t	d_off;
+	unsigned short	d_reclen;
+	char		d_name[1];
+};
+
+struct compat_getdents_callback {
+	struct dir_context ctx;
+	struct compat_linux_dirent __user *current_dir;
+	struct compat_linux_dirent __user *previous;
+	int count;
+	int error;
+};
+
+static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
+		loff_t offset, u64 ino, unsigned int d_type)
+{
+	struct compat_linux_dirent __user * dirent;
+	struct compat_getdents_callback *buf =
+		container_of(ctx, struct compat_getdents_callback, ctx);
+	compat_ulong_t d_ino;
+	int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
+		namlen + 2, sizeof(compat_long_t));
+
+	buf->error = -EINVAL;	/* only used if we fail.. */
+	if (reclen > buf->count)
+		return -EINVAL;
+	d_ino = ino;
+	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
+		buf->error = -EOVERFLOW;
+		return -EOVERFLOW;
+	}
+	dirent = buf->previous;
+	if (dirent) {
+		if (signal_pending(current))
+			return -EINTR;
+		if (__put_user(offset, &dirent->d_off))
+			goto efault;
+	}
+	dirent = buf->current_dir;
+	if (__put_user(d_ino, &dirent->d_ino))
+		goto efault;
+	if (__put_user(reclen, &dirent->d_reclen))
+		goto efault;
+	if (copy_to_user(dirent->d_name, name, namlen))
+		goto efault;
+	if (__put_user(0, dirent->d_name + namlen))
+		goto efault;
+	if (__put_user(d_type, (char  __user *) dirent + reclen - 1))
+		goto efault;
+	buf->previous = dirent;
+	dirent = (void __user *)dirent + reclen;
+	buf->current_dir = dirent;
+	buf->count -= reclen;
+	return 0;
+efault:
+	buf->error = -EFAULT;
+	return -EFAULT;
+}
+
+COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
+		struct compat_linux_dirent __user *, dirent, unsigned int, count)
+{
+	struct fd f;
+	struct compat_linux_dirent __user * lastdirent;
+	struct compat_getdents_callback buf = {
+		.ctx.actor = compat_filldir,
+		.current_dir = dirent,
+		.count = count
+	};
+	int error;
+
+	if (!access_ok(VERIFY_WRITE, dirent, count))
+		return -EFAULT;
+
+	f = fdget_pos(fd);
+	if (!f.file)
+		return -EBADF;
+
+	error = iterate_dir(f.file, &buf.ctx);
+	if (error >= 0)
+		error = buf.error;
+	lastdirent = buf.previous;
+	if (lastdirent) {
+		if (put_user(buf.ctx.pos, &lastdirent->d_off))
+			error = -EFAULT;
+		else
+			error = count - buf.count;
+	}
+	fdput_pos(f);
+	return error;
+}
+
+#ifdef __ARCH_WANT_COMPAT_SYS_GETDENTS64
+
+struct compat_getdents_callback64 {
+	struct dir_context ctx;
+	struct linux_dirent64 __user *current_dir;
+	struct linux_dirent64 __user *previous;
+	int count;
+	int error;
+};
+
+static int compat_filldir64(struct dir_context *ctx, const char *name,
+			    int namlen, loff_t offset, u64 ino,
+			    unsigned int d_type)
+{
+	struct linux_dirent64 __user *dirent;
+	struct compat_getdents_callback64 *buf =
+		container_of(ctx, struct compat_getdents_callback64, ctx);
+	int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
+		sizeof(u64));
+	u64 off;
+
+	buf->error = -EINVAL;	/* only used if we fail.. */
+	if (reclen > buf->count)
+		return -EINVAL;
+	dirent = buf->previous;
+
+	if (dirent) {
+		if (signal_pending(current))
+			return -EINTR;
+		if (__put_user_unaligned(offset, &dirent->d_off))
+			goto efault;
+	}
+	dirent = buf->current_dir;
+	if (__put_user_unaligned(ino, &dirent->d_ino))
+		goto efault;
+	off = 0;
+	if (__put_user_unaligned(off, &dirent->d_off))
+		goto efault;
+	if (__put_user(reclen, &dirent->d_reclen))
+		goto efault;
+	if (__put_user(d_type, &dirent->d_type))
+		goto efault;
+	if (copy_to_user(dirent->d_name, name, namlen))
+		goto efault;
+	if (__put_user(0, dirent->d_name + namlen))
+		goto efault;
+	buf->previous = dirent;
+	dirent = (void __user *)dirent + reclen;
+	buf->current_dir = dirent;
+	buf->count -= reclen;
+	return 0;
+efault:
+	buf->error = -EFAULT;
+	return -EFAULT;
+}
+
+COMPAT_SYSCALL_DEFINE3(getdents64, unsigned int, fd,
+		struct linux_dirent64 __user *, dirent, unsigned int, count)
+{
+	struct fd f;
+	struct linux_dirent64 __user * lastdirent;
+	struct compat_getdents_callback64 buf = {
+		.ctx.actor = compat_filldir64,
+		.current_dir = dirent,
+		.count = count
+	};
+	int error;
+
+	if (!access_ok(VERIFY_WRITE, dirent, count))
+		return -EFAULT;
+
+	f = fdget_pos(fd);
+	if (!f.file)
+		return -EBADF;
+
+	error = iterate_dir(f.file, &buf.ctx);
+	if (error >= 0)
+		error = buf.error;
+	lastdirent = buf.previous;
+	if (lastdirent) {
+		typeof(lastdirent->d_off) d_off = buf.ctx.pos;
+		if (__put_user_unaligned(d_off, &lastdirent->d_off))
+			error = -EFAULT;
+		else
+			error = count - buf.count;
+	}
+	fdput_pos(f);
+	return error;
+}
+#endif /* __ARCH_WANT_COMPAT_SYS_GETDENTS64 */
+
+/*
+ * Exactly like fs/open.c:sys_open(), except that it doesn't set the
+ * O_LARGEFILE flag.
+ */
+COMPAT_SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, \
mode)
+{
+	return do_sys_open(AT_FDCWD, filename, flags, mode);
+}
+
+/*
+ * Exactly like fs/open.c:sys_openat(), except that it doesn't set the
+ * O_LARGEFILE flag.
+ */
+COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, \
umode_t, mode)
+{
+	return do_sys_open(dfd, filename, flags, mode);
+}
+
+#define __COMPAT_NFDBITS       (8 * sizeof(compat_ulong_t))
+
+static int poll_select_copy_remaining(struct timespec *end_time, void __user *p,
+				      int timeval, int ret)
+{
+	struct timespec ts;
+
+	if (!p)
+		return ret;
+
+	if (current->personality & STICKY_TIMEOUTS)
+		goto sticky;
+
+	/* No update for zero timeout */
+	if (!end_time->tv_sec && !end_time->tv_nsec)
+		return ret;
+
+	ktime_get_ts(&ts);
+	ts = timespec_sub(*end_time, ts);
+	if (ts.tv_sec < 0)
+		ts.tv_sec = ts.tv_nsec = 0;
+
+	if (timeval) {
+		struct compat_timeval rtv;
+
+		rtv.tv_sec = ts.tv_sec;
+		rtv.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
+		if (!copy_to_user(p, &rtv, sizeof(rtv)))
+			return ret;
+	} else {
+		struct compat_timespec rts;
+
+		rts.tv_sec = ts.tv_sec;
+		rts.tv_nsec = ts.tv_nsec;
+
+		if (!copy_to_user(p, &rts, sizeof(rts)))
+			return ret;
+	}
+	/*
+	 * If an application puts its timeval in read-only memory, we
+	 * don't want the Linux-specific update to the timeval to
+	 * cause a fault after the select has completed
+	 * successfully. However, because we're not updating the
+	 * timeval, we can't restart the system call.
+	 */
+
+sticky:
+	if (ret == -ERESTARTNOHAND)
+		ret = -EINTR;
+	return ret;
+}
+
+/*
+ * Ooo, nasty.  We need here to frob 32-bit unsigned longs to
+ * 64-bit unsigned longs.
+ */
+static
+int compat_get_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
+			unsigned long *fdset)
+{
+	nr = DIV_ROUND_UP(nr, __COMPAT_NFDBITS);
+	if (ufdset) {
+		unsigned long odd;
+
+		if (!access_ok(VERIFY_WRITE, ufdset, nr*sizeof(compat_ulong_t)))
+			return -EFAULT;
+
+		odd = nr & 1UL;
+		nr &= ~1UL;
+		while (nr) {
+			unsigned long h, l;
+			if (__get_user(l, ufdset) || __get_user(h, ufdset+1))
+				return -EFAULT;
+			ufdset += 2;
+			*fdset++ = h << 32 | l;
+			nr -= 2;
+		}
+		if (odd && __get_user(*fdset, ufdset))
+			return -EFAULT;
+	} else {
+		/* Tricky, must clear full unsigned long in the
+		 * kernel fdset at the end, this makes sure that
+		 * actually happens.
+		 */
+		memset(fdset, 0, ((nr + 1) & ~1)*sizeof(compat_ulong_t));
+	}
+	return 0;
+}
+
+static
+int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
+		      unsigned long *fdset)
+{
+	unsigned long odd;
+	nr = DIV_ROUND_UP(nr, __COMPAT_NFDBITS);
+
+	if (!ufdset)
+		return 0;
+
+	odd = nr & 1UL;
+	nr &= ~1UL;
+	while (nr) {
+		unsigned long h, l;
+		l = *fdset++;
+		h = l >> 32;
+		if (__put_user(l, ufdset) || __put_user(h, ufdset+1))
+			return -EFAULT;
+		ufdset += 2;
+		nr -= 2;
+	}
+	if (odd && __put_user(*fdset, ufdset))
+		return -EFAULT;
+	return 0;
+}
+
+
+/*
+ * This is a virtual copy of sys_select from fs/select.c and probably
+ * should be compared to it from time to time
+ */
+
+/*
+ * We can actually return ERESTARTSYS instead of EINTR, but I'd
+ * like to be certain this leads to no problems. So I return
+ * EINTR just for safety.
+ *
+ * Update: ERESTARTSYS breaks at least the xview clock binary, so
+ * I'm trying ERESTARTNOHAND which restart only when you want to.
+ */
+int compat_core_sys_select(int n, compat_ulong_t __user *inp,
+	compat_ulong_t __user *outp, compat_ulong_t __user *exp,
+	struct timespec *end_time)
+{
+	fd_set_bits fds;
+	void *bits;
+	int size, max_fds, ret = -EINVAL;
+	struct fdtable *fdt;
+	long stack_fds[SELECT_STACK_ALLOC/sizeof(long)];
+
+	if (n < 0)
+		goto out_nofds;
+
+	/* max_fds can increase, so grab it once to avoid race */
+	rcu_read_lock();
+	fdt = files_fdtable(current->files);
+	max_fds = fdt->max_fds;
+	rcu_read_unlock();
+	if (n > max_fds)
+		n = max_fds;
+
+	/*
+	 * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
+	 * since we used fdset we need to allocate memory in units of
+	 * long-words.
+	 */
+	size = FDS_BYTES(n);
+	bits = stack_fds;
+	if (size > sizeof(stack_fds) / 6) {
+		bits = kmalloc(6 * size, GFP_KERNEL);
+		ret = -ENOMEM;
+		if (!bits)
+			goto out_nofds;
+	}
+	fds.in      = (unsigned long *)  bits;
+	fds.out     = (unsigned long *) (bits +   size);
+	fds.ex      = (unsigned long *) (bits + 2*size);
+	fds.res_in  = (unsigned long *) (bits + 3*size);
+	fds.res_out = (unsigned long *) (bits + 4*size);
+	fds.res_ex  = (unsigned long *) (bits + 5*size);
+
+	if ((ret = compat_get_fd_set(n, inp, fds.in)) ||
+	    (ret = compat_get_fd_set(n, outp, fds.out)) ||
+	    (ret = compat_get_fd_set(n, exp, fds.ex)))
+		goto out;
+	zero_fd_set(n, fds.res_in);
+	zero_fd_set(n, fds.res_out);
+	zero_fd_set(n, fds.res_ex);
+
+	ret = do_select(n, &fds, end_time);
+
+	if (ret < 0)
+		goto out;
+	if (!ret) {
+		ret = -ERESTARTNOHAND;
+		if (signal_pending(current))
+			goto out;
+		ret = 0;
+	}
+
+	if (compat_set_fd_set(n, inp, fds.res_in) ||
+	    compat_set_fd_set(n, outp, fds.res_out) ||
+	    compat_set_fd_set(n, exp, fds.res_ex))
+		ret = -EFAULT;
+out:
+	if (bits != stack_fds)
+		kfree(bits);
+out_nofds:
+	return ret;
+}
+
+COMPAT_SYSCALL_DEFINE5(select, int, n, compat_ulong_t __user *, inp,
+	compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
+	struct compat_timeval __user *, tvp)
+{
+	struct timespec end_time, *to = NULL;
+	struct compat_timeval tv;
+	int ret;
+
+	if (tvp) {
+		if (copy_from_user(&tv, tvp, sizeof(tv)))
+			return -EFAULT;
+
+		to = &end_time;
+		if (poll_select_set_timeout(to,
+				tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),
+				(tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC))
+			return -EINVAL;
+	}
+
+	ret = compat_core_sys_select(n, inp, outp, exp, to);
+	ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);
+
+	return ret;
+}
+
+struct compat_sel_arg_struct {
+	compat_ulong_t n;
+	compat_uptr_t inp;
+	compat_uptr_t outp;
+	compat_uptr_t exp;
+	compat_uptr_t tvp;
+};
+
+COMPAT_SYSCALL_DEFINE1(old_select, struct compat_sel_arg_struct __user *, arg)
+{
+	struct compat_sel_arg_struct a;
+
+	if (copy_from_user(&a, arg, sizeof(a)))
+		return -EFAULT;
+	return compat_sys_select(a.n, compat_ptr(a.inp), compat_ptr(a.outp),
+				 compat_ptr(a.exp), compat_ptr(a.tvp));
+}
+
+static long do_compat_pselect(int n, compat_ulong_t __user *inp,
+	compat_ulong_t __user *outp, compat_ulong_t __user *exp,
+	struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask,
+	compat_size_t sigsetsize)
+{
+	compat_sigset_t ss32;
+	sigset_t ksigmask, sigsaved;
+	struct compat_timespec ts;
+	struct timespec end_time, *to = NULL;
+	int ret;
+
+	if (tsp) {
+		if (copy_from_user(&ts, tsp, sizeof(ts)))
+			return -EFAULT;
+
+		to = &end_time;
+		if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
+			return -EINVAL;
+	}
+
+	if (sigmask) {
+		if (sigsetsize != sizeof(compat_sigset_t))
+			return -EINVAL;
+		if (copy_from_user(&ss32, sigmask, sizeof(ss32)))
+			return -EFAULT;
+		sigset_from_compat(&ksigmask, &ss32);
+
+		sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP));
+		sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
+	}
+
+	ret = compat_core_sys_select(n, inp, outp, exp, to);
+	ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
+
+	if (ret == -ERESTARTNOHAND) {
+		/*
+		 * Don't restore the signal mask yet. Let do_signal() deliver
+		 * the signal on the way back to userspace, before the signal
+		 * mask is restored.
+		 */
+		if (sigmask) {
+			memcpy(&current->saved_sigmask, &sigsaved,
+					sizeof(sigsaved));
+			set_restore_sigmask();
+		}
+	} else if (sigmask)
+		sigprocmask(SIG_SETMASK, &sigsaved, NULL);
+
+	return ret;
+}
+
+COMPAT_SYSCALL_DEFINE6(pselect6, int, n, compat_ulong_t __user *, inp,
+	compat_ulong_t __user *, outp, compat_ulong_t __user *, exp,
+	struct compat_timespec __user *, tsp, void __user *, sig)
+{
+	compat_size_t sigsetsize = 0;
+	compat_uptr_t up = 0;
+
+	if (sig) {
+		if (!access_ok(VERIFY_READ, sig,
+				sizeof(compat_uptr_t)+sizeof(compat_size_t)) ||
+		    	__get_user(up, (compat_uptr_t __user *)sig) ||
+		    	__get_user(sigsetsize,
+				(compat_size_t __user *)(sig+sizeof(up))))
+			return -EFAULT;
+	}
+	return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up),
+				 sigsetsize);
+}
+
+COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds,
+	unsigned int,  nfds, struct compat_timespec __user *, tsp,
+	const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
+{
+	compat_sigset_t ss32;
+	sigset_t ksigmask, sigsaved;
+	struct compat_timespec ts;
+	struct timespec end_time, *to = NULL;
+	int ret;
+
+	if (tsp) {
+		if (copy_from_user(&ts, tsp, sizeof(ts)))
+			return -EFAULT;
+
+		to = &end_time;
+		if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec))
+			return -EINVAL;
+	}
+
+	if (sigmask) {
+		if (sigsetsize != sizeof(compat_sigset_t))
+			return -EINVAL;
+		if (copy_from_user(&ss32, sigmask, sizeof(ss32)))
+			return -EFAULT;
+		sigset_from_compat(&ksigmask, &ss32);
+
+		sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP));
+		sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
+	}
+
+	ret = do_sys_poll(ufds, nfds, to);
+
+	/* We can restart this syscall, usually */
+	if (ret == -EINTR) {
+		/*
+		 * Don't restore the signal mask yet. Let do_signal() deliver
+		 * the signal on the way back to userspace, before the signal
+		 * mask is restored.
+		 */
+		if (sigmask) {
+			memcpy(&current->saved_sigmask, &sigsaved,
+				sizeof(sigsaved));
+			set_restore_sigmask();
+		}
+		ret = -ERESTARTNOHAND;
+	} else if (sigmask)
+		sigprocmask(SIG_SETMASK, &sigsaved, NULL);
+
+	ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
+
+	return ret;
+}
+
+#ifdef CONFIG_FHANDLE
+/*
+ * Exactly like fs/open.c:sys_open_by_handle_at(), except that it
+ * doesn't set the O_LARGEFILE flag.
+ */
+COMPAT_SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
+			     struct file_handle __user *, handle, int, flags)
+{
+	return do_handle_open(mountdirfd, handle, flags);
+}
+#endif
diff -Nurp linux-4.9.4.orig/fs/gobohide.c linux-4.9.4/fs/gobohide.c
--- linux-4.9.4.orig/fs/gobohide.c	1969-12-31 21:00:00.000000000 -0300
+++ linux-4.9.4/fs/gobohide.c	2017-01-16 18:30:16.205188810 -0200
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2002-2011 GoboLinux.org
+ *
+ * These modifications are released under the GNU General Public License
+ * version 2 or later, incorporated herein by reference.
+ * Modifications/features/bug fixes based on or derived from this code
+ * fall under the GPL and must retain the authorship, copyright and license
+ * notice.  This file is not a complete program and may only be used when
+ * the entire operating system is licensed under the GPL.
+ *
+ * See the file COPYING in this distribution for more information.
+ *
+ * Author: Felipe W Damasio <felipewd@gmail.com>.
+ * Original idea: Lucas C. Villa Real <lucasvr@gobolinux.org>
+ *
+ * Changes:
+ * 12-Sep-2011 - Lucas C. Villa Real
+ *               Take the superblock into account when comparing the dentries
+ *               in order to allow mount points to be hidden.
+ *
+ * 03-Sep-2011 - Lucas C. Villa Real
+ *               Security updates. Thanks to Dan Rosenberg for his code review.
+ *
+ * 18-May-2007 - Lucas C. Villa Real
+ *               Added support to unionfs.
+ *
+ * 04-Jul-2006 - Lucas C. Villa Real
+ *               Added GoboHide support to all filesystems through the VFS.
+ *
+ * 21-Feb-2004 - Lucas C. Villa Real
+ *               Added an extra check for the inode's VFS root, so that
+ *               the same inode number on different partitions don't get
+ *               hidden mistakenly.
+ *
+ * 11-Nov-2003 - Lucas C. Villa Real
+ *               Removed the spinlocks from gobolinux_show_hidden(), since
+ *               we were already working with list_for_each_safe(), which
+ *               iterates safely against removal of list entries.
+ *
+ * 05-May-2003 - Felipe W Damasio
+ *               Using read-write locks instead of spinlocks,
+ *               improving quite a bit read operations
+ *               (allow concurrent readers, but only a single writer)
+ *
+ * 28-Apr-2003 - Lucas C. Villa Real
+ *               Centralized checks for UID on gobolinux/fs/ioctl.c.
+ *               Fixed get_free_page() to work on 64-bit archs as well.
+ *
+ * 12-Apr-2003 - Lucas C. Villa Real
+ *               Removed support for UID's different than 0 hide inodes.
+ *
+ * 24-Mar-2003 - Lucas C. Villa Real
+ *               Modified struct hide and calls so we have pathnames related
+ *               to the "real" root dir and not the the mount point.
+ *
+ * 17-Mar-2003 - Lucas C. Villa Real
+ *               Added support for full pathname, rather than dealing only
+ *               with inode numbers.
+ *
+ * 10-Jan-2003 - Lucas C. Villa Real
+ *               Added statistics.
+ */
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gobohide.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/mount.h>
+#include <linux/path.h>
+#include <net/genetlink.h>
+#include "mount.h"
+
+#include <asm/uaccess.h>
+
+#define GOBOHIDE_INODE_EQUALS(p,i) (d_inode((p)->dentry)->i_ino == i)
+
+static int gobohide_inode_list_count;
+static LIST_HEAD(gobohide_inode_list);
+static DEFINE_RWLOCK(gobohide_inode_rwlock);
+
+static int gobohide_add(ino_t ino, const char *pathname);
+static int gobohide_del(ino_t ino, const char *pathname);
+static int gobohide_flush(void);
+static struct hide **gobohide_list(u32 *count);
+static int gobohide_remove_unlocked(struct hide *entry, int remove);
+static struct hide *gobohide_get_unlocked(ino_t i_ino, const char *filename,
+	int namelen, struct dentry *parent);
+
+/* Netlink callbacks */
+
+static int send_list_reply(struct genl_info *info, char *data, size_t size);
+static int send_list_size_reply(struct genl_info *info, u32 size);
+
+static int parse_path(struct nlattr *na, char **pathname)
+{
+	char *data;
+	int len;
+
+	if (na == NULL)
+		return 1;
+	len = nla_len(na);
+	if (len > PATH_MAX)
+		return -E2BIG;
+	if (len < 1)
+		return -EINVAL;
+	data = kmalloc(len, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	nla_strlcpy(data, na, len);
+	*pathname = data;
+	return 0;
+}
+
+static int parse_attrs(struct genl_info *info, char **pathname, u64 *ino)
+{
+	int ret;
+
+	if (!info->attrs[GOBOHIDE_CMD_ATTR_INODE] || !info->attrs[GOBOHIDE_CMD_ATTR_PATH])
+		return -EINVAL;
+
+	ret = parse_path(info->attrs[GOBOHIDE_CMD_ATTR_PATH], pathname);
+	if (ret != 0)
+		return ret;
+
+	*ino = nla_get_u64(info->attrs[GOBOHIDE_CMD_ATTR_INODE]);
+	return 0;
+}
+
+static int gobohide_add_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+	char *pathname = NULL;
+	u64 ino = 0;
+	int ret;
+	
+	ret = parse_attrs(info, &pathname, &ino);
+	if (ret < 0)
+		return ret;
+	ret = gobohide_add((ino_t) ino, pathname);
+	kfree(pathname);
+	return ret;
+}
+
+static int gobohide_del_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+	char *pathname = NULL;
+	u64 ino = 0;
+	int ret;
+
+	ret = parse_attrs(info, &pathname, &ino);
+	if (ret < 0)
+		return ret;
+	ret = gobohide_del((ino_t) ino, pathname);
+	kfree(pathname);
+	return ret;
+}
+
+static int gobohide_flush_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+	return gobohide_flush();
+}
+
+static int gobohide_list_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+	struct hide **hiddenlist;
+	int i = 0, ret = 0;
+	u32 count = 0;
+
+	hiddenlist = gobohide_list(&count);
+	if (! hiddenlist)
+		return 0;
+
+	ret = send_list_size_reply(info, count);
+	if (ret < 0)
+		goto out_error;
+
+	for (i=0; hiddenlist[i] != NULL; ++i) {
+		struct hide *entry = hiddenlist[i];
+		size_t size = nla_total_size(strlen(entry->pathname));
+
+		ret = send_list_reply(info, entry->pathname, size);
+		if (ret < 0)
+			goto out_error;
+		gobohide_put(entry);
+	}
+
+	kfree(hiddenlist);
+	return ret;
+
+out_error:
+	while (hiddenlist[i] != NULL) {
+		gobohide_put(hiddenlist[i]);
+		i++;
+	}
+	kfree(hiddenlist);
+	return ret;
+}
+
+/* Netlink interface registration */
+
+static const struct nla_policy gobohide_nl_policy[GOBOHIDE_CMD_ATTR_MAX+1] = {
+	[GOBOHIDE_CMD_ATTR_PATH] = { .type = NLA_STRING },
+	[GOBOHIDE_CMD_ATTR_INODE] = { .type = NLA_U64 },
+};
+
+static struct genl_ops gobohide_nl_ops[] = {
+	{
+		.cmd	= GOBOHIDE_CMD_HIDE,
+		.doit	= gobohide_add_cmd,
+		.policy = gobohide_nl_policy,
+		.flags  = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd	= GOBOHIDE_CMD_UNHIDE,
+		.doit	= gobohide_del_cmd,
+		.policy = gobohide_nl_policy,
+		.flags  = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd	= GOBOHIDE_CMD_FLUSH,
+		.doit	= gobohide_flush_cmd,
+		.policy = gobohide_nl_policy,
+		.flags  = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd	= GOBOHIDE_CMD_LIST,
+		.doit	= gobohide_list_cmd,
+		.flags  = GENL_ADMIN_PERM,
+	}
+};
+
+static struct genl_family family = {
+	.id      = GENL_ID_GENERATE,
+	.name    = GOBOHIDE_GENL_NAME,
+	.version = GOBOHIDE_GENL_VERSION,
+	.maxattr = GOBOHIDE_CMD_ATTR_MAX,
+	.hdrsize = 0,
+	.netnsok = true,
+	.n_ops   = ARRAY_SIZE(gobohide_nl_ops),
+};
+
+static int send_list_size_reply(struct genl_info *info, u32 size)
+{
+	struct genlmsghdr *genlhdr;
+	struct sk_buff *skb;
+	void *reply;
+	int ret;
+
+	skb = genlmsg_new(size, GFP_KERNEL);
+	if (! skb)
+		return -ENOMEM;
+
+	/* reply: list size as u32 */
+	reply = genlmsg_put_reply(skb, info, &family, 0, GOBOHIDE_CMD_LIST_SIZE);
+	if (! reply) {
+		nlmsg_free(skb);
+		return -EINVAL;
+	}
+	ret = nla_put(skb, GOBOHIDE_TYPE_LIST_SIZE, sizeof(u32), &size);
+	if (ret < 0) {
+		nlmsg_free(skb);
+		return -EMSGSIZE;
+	}
+
+	/* send reply to listener */
+	genlhdr = nlmsg_data(nlmsg_hdr(skb));
+	genlmsg_end(skb, reply);
+	return genlmsg_reply(skb, info);
+}
+
+static int send_list_reply(struct genl_info *info, char *data, size_t size)
+{
+	struct genlmsghdr *genlhdr;
+	struct sk_buff *skb;
+	struct nlattr *na;
+	void *reply;
+
+	skb = genlmsg_new(size, GFP_KERNEL);
+	if (! skb)
+		return -ENOMEM;
+
+	/* reply: path as a string */
+	reply = genlmsg_put_reply(skb, info, &family, 0, GOBOHIDE_CMD_LIST_REPLY);
+	if (! reply) {
+		nlmsg_free(skb);
+		return -EINVAL;
+	}
+	na = nla_reserve(skb, GOBOHIDE_TYPE_PATH, size);
+	if (! na) {
+		nlmsg_free(skb);
+		return -EMSGSIZE;
+	}
+	strncpy(nla_data(na), data, size);
+
+	/* send reply to listener */
+	genlhdr = nlmsg_data(nlmsg_hdr(skb));
+	genlmsg_end(skb, reply);
+	return genlmsg_reply(skb, info);
+}
+
+static int __init gobohide_netlink_init(void)
+{
+	return genl_register_family_with_ops(&family, gobohide_nl_ops);
+}
+
+static void __exit gobohide_netlink_exit(void)
+{
+	genl_unregister_family(&family);
+}
+
+late_initcall(gobohide_netlink_init);
+
+/**
+ * gobohide_resolve_path - Resolves the pathname of a given dentry
+ * @entry: structure holding the dentry structure and the destination buffer.
+ *
+ * After the structure has been used, its member 'page' must be returned with
+ * a call to free_page().
+ */
+static int gobohide_resolve_path(struct hide *entry)
+{
+	int len, ret;
+	struct file *filp = entry->filp;
+	struct path path = { .mnt = filp->f_path.mnt, .dentry = filp->f_path.dentry };
+
+	entry->page = __get_free_page(GFP_USER);
+	if (! entry->page)
+		return -ENOMEM;
+
+	entry->pathname = d_path(&path, (char *) entry->page, PAGE_SIZE);
+	if (IS_ERR(entry->pathname)) {
+		ret = PTR_ERR(entry->pathname);
+		entry->pathname = NULL;
+		free_page(entry->page);
+		entry->page = 0;
+		return ret;
+	}
+
+	len = PAGE_SIZE + entry->page - (unsigned long) entry->pathname;
+	return len < PATH_MAX ? len : PATH_MAX;
+}
+
+/**
+ * gobohide_list - Lists the currently hidden inodes.
+ * @num[out]: number of entries returned
+ *
+ * @hide: output structure. We fill in its pathname_len and pathname members.
+ */
+static struct hide **gobohide_list(u32 *num)
+{
+	struct hide *entry, *next, **hiddenlist;
+	int count, copied_entries = 0;
+	unsigned long flags;
+
+	read_lock(&gobohide_inode_rwlock);
+	count = gobohide_inode_list_count;
+	read_unlock(&gobohide_inode_rwlock);
+
+	hiddenlist = kmalloc(sizeof(struct hide *) * count+1, GFP_KERNEL);
+	if (! hiddenlist) {
+		*num = 0;
+		return NULL;
+	}
+
+	/* Since copy_to_user may sleep data can't be copied with the lock held */
+	write_lock_irqsave(&gobohide_inode_rwlock, flags);
+	if (gobohide_inode_list_count) {
+		list_for_each_entry_safe(entry, next, &gobohide_inode_list, head) {
+			if (entry && copied_entries < count) {
+				/* Don't let the entry go away */
+				entry->refcount++;
+				hiddenlist[copied_entries++] = entry;
+			} else
+				break;
+		}
+	}
+	hiddenlist[copied_entries] = NULL;
+	write_unlock_irqrestore(&gobohide_inode_rwlock, flags);
+
+	*num = count;
+	return hiddenlist;
+}
+
+/**
+ * gobohide_add - Add the inode to the "must hide" list
+ * @ino: inode to be added
+ * @pathname: the pathname associated with @ino
+ */
+static int gobohide_add(ino_t ino, const char *pathname)
+{
+	int len, ret;
+	struct path *path;
+	struct hide *entry, *old;
+	struct dentry *dentry;
+	unsigned long flags;
+
+	entry = kmalloc(sizeof(struct hide), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	entry->refcount = 1;
+	entry->unlinked = 0;
+
+	path = &entry->path;
+	ret = kern_path(pathname, 0, path);
+	if (ret)
+		goto out_free;
+	else if (! GOBOHIDE_INODE_EQUALS(path, ino)) {
+		ret = -ENOENT;
+		goto out_path_put;
+	}
+
+	entry->filp = dentry_open(path, O_NOFOLLOW|O_PATH, current_cred());
+	if (IS_ERR(entry->filp)) {
+		ret = PTR_ERR(entry->filp);
+		goto out_path_put;
+	}
+	len = gobohide_resolve_path(entry);
+	if (len < 0) {
+		ret = len;
+		goto out_fput;
+	}
+	dentry = entry->filp->f_path.dentry;
+	if (! dentry) {
+		ret = -ENOENT;
+		goto out_free_page;
+	}
+	entry->i_ino = d_inode(dentry)->i_ino;
+
+	write_lock_irqsave(&gobohide_inode_rwlock, flags);
+	old = gobohide_get_unlocked(entry->i_ino, dentry->d_name.name,
+		dentry->d_name.len+1, dentry->d_parent);
+
+	if (old) {
+		write_unlock_irqrestore(&gobohide_inode_rwlock, flags);
+		ret = -EEXIST;
+		goto out_free_page;
+	}
+
+	gobohide_inode_list_count++;
+	list_add(&entry->head, &gobohide_inode_list);
+	write_unlock_irqrestore(&gobohide_inode_rwlock, flags);
+
+	return 0;
+
+out_free_page:
+	free_page(entry->page);
+out_fput:
+	fput(entry->filp);
+out_path_put:
+	path_put(path);
+out_free:
+	kfree(entry);
+	return ret;
+}
+
+/**
+ * gobohide_flush - Removes all entries from the "must hide" list
+ */
+static int gobohide_flush(void)
+{
+	struct hide *entry, *aux;
+	unsigned long flags;
+
+	write_lock_irqsave(&gobohide_inode_rwlock, flags);
+	if (gobohide_inode_list_count) {
+		list_for_each_entry_safe(entry, aux, &gobohide_inode_list, head)
+			gobohide_remove_unlocked(entry, 1);
+	}
+	write_unlock_irqrestore(&gobohide_inode_rwlock, flags);
+
+	return 0;
+}
+
+/**
+ * gobohide_del - Remove the inode from the "must hide" list
+ * @ino: inode to be removed
+ * @pathname: pathname to be removed
+ */
+static int gobohide_del(ino_t ino, const char *pathname)
+{
+	int len, ret;
+	struct path *path;
+	unsigned long flags;
+	struct hide n, *entry, *aux;
+
+	path = &n.path;
+	ret = kern_path(pathname, 0, path);
+	if (ret)
+		return ret;
+	else if (! GOBOHIDE_INODE_EQUALS(path, ino)) {
+		ret = -ENOENT;
+		goto out_path_put;
+	}
+
+	n.filp = dentry_open(path, O_NOFOLLOW|O_PATH, current_cred());
+	if (IS_ERR(n.filp)) {
+		ret = PTR_ERR(n.filp);
+		goto out_path_put;
+	}
+	len = gobohide_resolve_path(&n);
+	if (len < 0) {
+		ret = len;
+		goto out_fput;
+	}
+	ino = d_inode(n.filp->f_path.dentry)->i_ino;
+
+	ret = -ENOENT;
+	write_lock_irqsave(&gobohide_inode_rwlock, flags);
+	if (gobohide_inode_list_count) {
+		list_for_each_entry_safe(entry, aux, &gobohide_inode_list, head) {
+			struct file *filp = entry->filp;
+			struct dentry *filp_dentry = filp->f_path.dentry;
+			struct mount *mnt = real_mount(filp->f_path.mnt)->mnt_parent;
+			struct dentry *mnt_dentry = mnt->mnt_mountpoint;
+			ino_t mnt_ino = mnt_dentry->d_inode->i_ino;
+
+			if ((entry->i_ino == ino && path->dentry->d_sb == filp_dentry->d_sb) ||
+				(mnt_ino == ino && path->dentry->d_sb == mnt_dentry->d_sb)) {
+				gobohide_remove_unlocked(entry, 1);
+				ret = 0;
+				break;
+			}
+		}
+	}
+	write_unlock_irqrestore(&gobohide_inode_rwlock, flags);
+
+	free_page(n.page);
+out_fput:
+	fput(n.filp);
+out_path_put:
+	path_put(path);
+	return ret;
+}
+
+/**
+ * gobohide_remove - Effectively removes the inode from the inode_list.
+ * @hide: struct hide to be removed
+ */
+int gobohide_remove(struct hide *entry)
+{
+	unsigned long flags;
+	int ret;
+
+	write_lock_irqsave(&gobohide_inode_rwlock, flags);
+	ret = gobohide_remove_unlocked(entry, 1);
+	write_unlock_irqrestore(&gobohide_inode_rwlock, flags);
+
+	return ret;
+}
+
+static int gobohide_remove_unlocked(struct hide *entry, int remove)
+{
+	if (remove && ! entry->unlinked) {
+		/* Remove from the linked list */
+		entry->unlinked = true;
+		list_del(&entry->head);
+		gobohide_inode_list_count--;
+	}
+	if (--entry->refcount == 0) {
+		free_page(entry->page);
+		fput(entry->filp);
+		path_put(&entry->path);
+		kfree(entry);
+	}
+	return 0;
+}
+
+/**
+ * gobohide_get - Get the struct hide associated to the given inode. The inode
+ *  is verified to exist in the "must hide" list through the comparison of the
+ *  inode number and the superblock.
+ *
+ * @ino: inode being readdir'd
+ * @filename: inode's filename
+ * @namelen: inodes's filename length in bytes
+ * @parent: the parent dentry for the given inode.
+ *
+ * If the inode number is in the inode_list, returns a pointer to its entry
+ * in the inode_list or NULL if it isn't there. The returned entry must be
+ * released with gobohide_put().
+ */
+struct hide *gobohide_get(ino_t ino, const char *filename, int namelen,
+	struct dentry *parent)
+{
+	unsigned long flags;
+	struct hide *entry;
+
+	write_lock_irqsave(&gobohide_inode_rwlock, flags);
+	entry = gobohide_get_unlocked(ino, filename, namelen, parent);
+	write_unlock_irqrestore(&gobohide_inode_rwlock, flags);
+
+	return entry;
+}
+
+static struct hide *gobohide_get_unlocked(ino_t ino, const char *filename,
+	int namelen, struct dentry *parent)
+{
+	struct hide *entry = NULL;
+
+	if (! ino || ! gobohide_inode_list_count)
+		return NULL;
+
+	list_for_each_entry(entry, &gobohide_inode_list, head) {
+		struct file *filp = entry->filp;
+		struct dentry *filp_dentry = filp->f_path.dentry;
+		struct mount *mnt = real_mount(filp->f_path.mnt);
+		struct dentry *mnt_dentry = mnt->mnt_mountpoint;
+		ino_t mnt_ino = mnt_dentry->d_inode->i_ino;
+
+		if ((entry->i_ino == ino && parent->d_sb == filp_dentry->d_sb) || 
+			(mnt_ino == ino && parent->d_sb == mnt_dentry->d_sb)) {
+			/* Increment the reference count and return the object */
+			entry->refcount++;
+			return entry;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Return an entry obtained from the gobohide_inode_list with gobohide_get().
+ * @param entry Entry obtained from gobohide_get().
+ */
+int gobohide_put(struct hide *entry)
+{
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	if (entry) {
+		write_lock_irqsave(&gobohide_inode_rwlock, flags);
+		ret = gobohide_remove_unlocked(entry, 0);
+		write_unlock_irqrestore(&gobohide_inode_rwlock, flags);
+	}
+
+	return ret;
+}
+
+EXPORT_SYMBOL(gobohide_get);
+EXPORT_SYMBOL(gobohide_put);
+EXPORT_SYMBOL(gobohide_remove);
diff -Nurp linux-4.9.4.orig/fs/Kconfig linux-4.9.4/fs/Kconfig
--- linux-4.9.4.orig/fs/Kconfig	2017-01-16 18:30:06.999188638 -0200
+++ linux-4.9.4/fs/Kconfig	2017-01-16 18:30:16.205188810 -0200
@@ -121,6 +121,16 @@ source "fs/udf/Kconfig"
 endmenu
 endif # BLOCK
 
+config GOBOHIDE_FS
+	bool "GoboHide support on file systems"
+	default y
+	help
+	  GoboHide is a general interface for providing real hidden files in
+	  a filesystem. GoboHide supports all filesystems sitting on the VFS,
+	  such as EXT3, SquashFS and JFFS2.
+	  To use it, see the documentation of the gobohide userspace tool at
+	  <http://gobolinux.org/?page=doc/articles/gobohide>.
+
 if BLOCK
 menu "DOS/FAT/NT Filesystems"
 
diff -Nurp linux-4.9.4.orig/fs/Kconfig.orig linux-4.9.4/fs/Kconfig.orig
--- linux-4.9.4.orig/fs/Kconfig.orig	1969-12-31 21:00:00.000000000 -0300
+++ linux-4.9.4/fs/Kconfig.orig	2017-01-15 10:43:07.000000000 -0200
@@ -0,0 +1,312 @@
+#
+# File system configuration
+#
+
+menu "File systems"
+
+# Use unaligned word dcache accesses
+config DCACHE_WORD_ACCESS
+       bool
+
+if BLOCK
+
+config FS_IOMAP
+	bool
+
+source "fs/ext2/Kconfig"
+source "fs/ext4/Kconfig"
+source "fs/jbd2/Kconfig"
+
+config FS_MBCACHE
+# Meta block cache for Extended Attributes (ext2/ext3/ext4)
+	tristate
+	default y if EXT2_FS=y && EXT2_FS_XATTR
+	default y if EXT4_FS=y
+	default m if EXT2_FS_XATTR || EXT4_FS
+
+source "fs/reiserfs/Kconfig"
+source "fs/jfs/Kconfig"
+
+source "fs/xfs/Kconfig"
+source "fs/gfs2/Kconfig"
+source "fs/ocfs2/Kconfig"
+source "fs/btrfs/Kconfig"
+source "fs/nilfs2/Kconfig"
+source "fs/f2fs/Kconfig"
+
+config FS_DAX
+	bool "Direct Access (DAX) support"
+	depends on MMU
+	depends on !(ARM || MIPS || SPARC)
+	help
+	  Direct Access (DAX) can be used on memory-backed block devices.
+	  If the block device supports DAX and the filesystem supports DAX,
+	  then you can avoid using the pagecache to buffer I/Os.  Turning
+	  on this option will compile in support for DAX; you will need to
+	  mount the filesystem using the -o dax option.
+
+	  If you do not have a block device that is capable of using this,
+	  or if unsure, say N.  Saying Y will increase the size of the kernel
+	  by about 5kB.
+
+config FS_DAX_PMD
+	bool
+	default FS_DAX
+	depends on FS_DAX
+	depends on ZONE_DEVICE
+	depends on TRANSPARENT_HUGEPAGE
+	depends on BROKEN
+
+endif # BLOCK
+
+# Posix ACL utility routines
+#
+# Note: Posix ACLs can be implemented without these helpers.  Never use
+# this symbol for ifdefs in core code.
+#
+config FS_POSIX_ACL
+	def_bool n
+
+config EXPORTFS
+	tristate
+
+config EXPORTFS_BLOCK_OPS
+	bool "Enable filesystem export operations for block IO"
+	help
+	  This option enables the export operations for a filesystem to support
+	  external block IO.
+
+config FILE_LOCKING
+	bool "Enable POSIX file locking API" if EXPERT
+	default y
+	select PERCPU_RWSEM
+	help
+	  This option enables standard file locking support, required
+          for filesystems like NFS and for the flock() system
+          call. Disabling this option saves about 11k.
+
+config MANDATORY_FILE_LOCKING
+	bool "Enable Mandatory file locking"
+	depends on FILE_LOCKING
+	default y
+	help
+	  This option enables files appropriately marked files on appropriely
+	  mounted filesystems to support mandatory locking.
+
+	  To the best of my knowledge this is dead code that no one cares about.
+
+source "fs/crypto/Kconfig"
+
+source "fs/notify/Kconfig"
+
+source "fs/quota/Kconfig"
+
+source "fs/autofs4/Kconfig"
+source "fs/fuse/Kconfig"
+source "fs/overlayfs/Kconfig"
+
+menu "Caches"
+
+source "fs/fscache/Kconfig"
+source "fs/cachefiles/Kconfig"
+
+endmenu
+
+if BLOCK
+menu "CD-ROM/DVD Filesystems"
+
+source "fs/isofs/Kconfig"
+source "fs/udf/Kconfig"
+
+endmenu
+endif # BLOCK
+
+if BLOCK
+menu "DOS/FAT/NT Filesystems"
+
+source "fs/fat/Kconfig"
+source "fs/ntfs/Kconfig"
+
+endmenu
+endif # BLOCK
+
+menu "Pseudo filesystems"
+
+source "fs/proc/Kconfig"
+source "fs/kernfs/Kconfig"
+source "fs/sysfs/Kconfig"
+
+config TMPFS
+	bool "Tmpfs virtual memory file system support (former shm fs)"
+	depends on SHMEM
+	help
+	  Tmpfs is a file system which keeps all files in virtual memory.
+
+	  Everything in tmpfs is temporary in the sense that no files will be
+	  created on your hard drive. The files live in memory and swap
+	  space. If you unmount a tmpfs instance, everything stored therein is
+	  lost.
+
+	  See <file:Documentation/filesystems/tmpfs.txt> for details.
+
+config TMPFS_POSIX_ACL
+	bool "Tmpfs POSIX Access Control Lists"
+	depends on TMPFS
+	select TMPFS_XATTR
+	select FS_POSIX_ACL
+	help
+	  POSIX Access Control Lists (ACLs) support additional access rights
+	  for users and groups beyond the standard owner/group/world scheme,
+	  and this option selects support for ACLs specifically for tmpfs
+	  filesystems.
+
+	  If you've selected TMPFS, it's possible that you'll also need
+	  this option as there are a number of Linux distros that require
+	  POSIX ACL support under /dev for certain features to work properly.
+	  For example, some distros need this feature for ALSA-related /dev
+	  files for sound to work properly.  In short, if you're not sure,
+	  say Y.
+
+	  To learn more about Access Control Lists, visit the POSIX ACLs for
+	  Linux website <http://acl.bestbits.at/>.
+
+config TMPFS_XATTR
+	bool "Tmpfs extended attributes"
+	depends on TMPFS
+	default n
+	help
+	  Extended attributes are name:value pairs associated with inodes by
+	  the kernel or by users (see the attr(5) manual page, or visit
+	  <http://acl.bestbits.at/> for details).
+
+	  Currently this enables support for the trusted.* and
+	  security.* namespaces.
+
+	  You need this for POSIX ACL support on tmpfs.
+
+	  If unsure, say N.
+
+config HUGETLBFS
+	bool "HugeTLB file system support"
+	depends on X86 || IA64 || SPARC64 || (S390 && 64BIT) || \
+		   SYS_SUPPORTS_HUGETLBFS || BROKEN
+	help
+	  hugetlbfs is a filesystem backing for HugeTLB pages, based on
+	  ramfs. For architectures that support it, say Y here and read
+	  <file:Documentation/vm/hugetlbpage.txt> for details.
+
+	  If unsure, say N.
+
+config HUGETLB_PAGE
+	def_bool HUGETLBFS
+
+config ARCH_HAS_GIGANTIC_PAGE
+	bool
+
+source "fs/configfs/Kconfig"
+source "fs/efivarfs/Kconfig"
+
+endmenu
+
+menuconfig MISC_FILESYSTEMS
+	bool "Miscellaneous filesystems"
+	default y
+	---help---
+	  Say Y here to get to see options for various miscellaneous
+	  filesystems, such as filesystems that came from other
+	  operating systems.
+
+	  This option alone does not add any kernel code.
+
+	  If you say N, all options in this submenu will be skipped and
+	  disabled; if unsure, say Y here.
+
+if MISC_FILESYSTEMS
+
+source "fs/orangefs/Kconfig"
+source "fs/adfs/Kconfig"
+source "fs/affs/Kconfig"
+source "fs/ecryptfs/Kconfig"
+source "fs/hfs/Kconfig"
+source "fs/hfsplus/Kconfig"
+source "fs/befs/Kconfig"
+source "fs/bfs/Kconfig"
+source "fs/efs/Kconfig"
+source "fs/jffs2/Kconfig"
+# UBIFS File system configuration
+source "fs/ubifs/Kconfig"
+source "fs/logfs/Kconfig"
+source "fs/cramfs/Kconfig"
+source "fs/squashfs/Kconfig"
+source "fs/freevxfs/Kconfig"
+source "fs/minix/Kconfig"
+source "fs/omfs/Kconfig"
+source "fs/hpfs/Kconfig"
+source "fs/qnx4/Kconfig"
+source "fs/qnx6/Kconfig"
+source "fs/romfs/Kconfig"
+source "fs/pstore/Kconfig"
+source "fs/sysv/Kconfig"
+source "fs/ufs/Kconfig"
+source "fs/exofs/Kconfig"
+
+endif # MISC_FILESYSTEMS
+
+source "fs/exofs/Kconfig.ore"
+
+menuconfig NETWORK_FILESYSTEMS
+	bool "Network File Systems"
+	default y
+	depends on NET
+	---help---
+	  Say Y here to get to see options for network filesystems and
+	  filesystem-related networking code, such as NFS daemon and
+	  RPCSEC security modules.
+
+	  This option alone does not add any kernel code.
+
+	  If you say N, all options in this submenu will be skipped and
+	  disabled; if unsure, say Y here.
+
+if NETWORK_FILESYSTEMS
+
+source "fs/nfs/Kconfig"
+source "fs/nfsd/Kconfig"
+
+config GRACE_PERIOD
+	tristate
+
+config LOCKD
+	tristate
+	depends on FILE_LOCKING
+	select GRACE_PERIOD
+
+config LOCKD_V4
+	bool
+	depends on NFSD_V3 || NFS_V3
+	depends on FILE_LOCKING
+	default y
+
+config NFS_ACL_SUPPORT
+	tristate
+	select FS_POSIX_ACL
+
+config NFS_COMMON
+	bool
+	depends on NFSD || NFS_FS || LOCKD
+	default y
+
+source "net/sunrpc/Kconfig"
+source "fs/ceph/Kconfig"
+source "fs/cifs/Kconfig"
+source "fs/ncpfs/Kconfig"
+source "fs/coda/Kconfig"
+source "fs/afs/Kconfig"
+source "fs/9p/Kconfig"
+
+endif # NETWORK_FILESYSTEMS
+
+source "fs/nls/Kconfig"
+source "fs/dlm/Kconfig"
+
+endmenu
diff -Nurp linux-4.9.4.orig/fs/Makefile linux-4.9.4/fs/Makefile
--- linux-4.9.4.orig/fs/Makefile	2017-01-16 18:30:06.999188638 -0200
+++ linux-4.9.4/fs/Makefile	2017-01-16 18:30:16.205188810 -0200
@@ -47,6 +47,7 @@ obj-$(CONFIG_FS_POSIX_ACL)	+= posix_acl.
 obj-$(CONFIG_NFS_COMMON)	+= nfs_common/
 obj-$(CONFIG_COREDUMP)		+= coredump.o
 obj-$(CONFIG_SYSCTL)		+= drop_caches.o
+obj-$(CONFIG_GOBOHIDE_FS)	+= gobohide.o
 
 obj-$(CONFIG_FHANDLE)		+= fhandle.o
 obj-$(CONFIG_FS_IOMAP)		+= iomap.o
diff -Nurp linux-4.9.4.orig/fs/namei.c linux-4.9.4/fs/namei.c
--- linux-4.9.4.orig/fs/namei.c	2017-01-16 18:30:07.019188638 -0200
+++ linux-4.9.4/fs/namei.c	2017-01-16 18:30:16.207188810 -0200
@@ -35,6 +35,7 @@
 #include <linux/fs_struct.h>
 #include <linux/posix_acl.h>
 #include <linux/hash.h>
+#include <linux/gobohide.h>
 #include <linux/bitops.h>
 #include <linux/init_task.h>
 #include <asm/uaccess.h>
@@ -3814,6 +3815,7 @@ SYSCALL_DEFINE2(mkdir, const char __user
 
 int vfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
+	struct hide *hidden = NULL;
 	int error = may_delete(dir, dentry, 1);
 
 	if (error)
@@ -3845,8 +3847,13 @@ int vfs_rmdir(struct inode *dir, struct
 out:
 	inode_unlock(dentry->d_inode);
 	dput(dentry);
-	if (!error)
+	if (!error) {
+		if (hidden)
+			gobohide_remove(hidden);
 		d_delete(dentry);
+	}
+	if (hidden)
+		gobohide_put(hidden);
 	return error;
 }
 EXPORT_SYMBOL(vfs_rmdir);
@@ -3935,6 +3942,7 @@ SYSCALL_DEFINE1(rmdir, const char __user
  */
 int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
 {
+	struct hide *hidden = NULL;
 	struct inode *target = dentry->d_inode;
 	int error = may_delete(dir, dentry, 0);
 
@@ -3944,6 +3952,13 @@ int vfs_unlink(struct inode *dir, struct
 	if (!dir->i_op->unlink)
 		return -EPERM;
 
+	if (dentry->d_inode) {
+		ino_t ino = d_inode(dentry)->i_ino;
+		if (ino)
+			hidden = gobohide_get(ino, dentry->d_name.name,
+				dentry->d_name.len, dentry->d_parent);
+	}
+
 	inode_lock(target);
 	if (is_local_mountpoint(dentry))
 		error = -EBUSY;
@@ -3965,10 +3980,14 @@ out:
 
 	/* We don't d_delete() NFS sillyrenamed files--they still exist. */
 	if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
+		if (hidden)
+			gobohide_remove(hidden);
 		fsnotify_link_count(target);
 		d_delete(dentry);
 	}
 
+	if (hidden)
+		gobohide_put(hidden);
 	return error;
 }
 EXPORT_SYMBOL(vfs_unlink);
@@ -4070,6 +4089,7 @@ SYSCALL_DEFINE1(unlink, const char __use
 
 int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
 {
+	struct hide *hidden = NULL;
 	int error = may_create(dir, dentry);
 
 	if (error)
@@ -4078,6 +4098,13 @@ int vfs_symlink(struct inode *dir, struc
 	if (!dir->i_op->symlink)
 		return -EPERM;
 
+	if (dentry->d_inode) {
+		ino_t ino = d_inode(dentry)->i_ino;
+		if (ino)
+			hidden = gobohide_get(ino, dentry->d_name.name,
+				dentry->d_name.len, dentry->d_parent);
+	}
+
 	error = security_inode_symlink(dir, dentry, oldname);
 	if (error)
 		return error;
diff -Nurp linux-4.9.4.orig/fs/readdir.c linux-4.9.4/fs/readdir.c
--- linux-4.9.4.orig/fs/readdir.c	2017-01-16 18:30:07.042188638 -0200
+++ linux-4.9.4/fs/readdir.c	2017-01-16 18:30:16.207188810 -0200
@@ -18,6 +18,7 @@
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/unistd.h>
+#include <linux/gobohide.h>
 
 #include <asm/uaccess.h>
 
@@ -85,6 +86,7 @@ struct readdir_callback {
 	struct dir_context ctx;
 	struct old_linux_dirent __user * dirent;
 	int result;
+	struct dentry *dentry;
 };
 
 static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
@@ -133,6 +135,7 @@ SYSCALL_DEFINE3(old_readdir, unsigned in
 	if (!f.file)
 		return -EBADF;
 
+	buf.dentry = f.file->f_path.dentry;
 	error = iterate_dir(f.file, &buf.ctx);
 	if (buf.result)
 		error = buf.result;
@@ -160,6 +163,7 @@ struct getdents_callback {
 	struct linux_dirent __user * previous;
 	int count;
 	int error;
+	struct dentry *dentry;
 };
 
 static int filldir(struct dir_context *ctx, const char *name, int namlen,
@@ -169,6 +173,7 @@ static int filldir(struct dir_context *c
 	struct getdents_callback *buf =
 		container_of(ctx, struct getdents_callback, ctx);
 	unsigned long d_ino;
+	struct hide *hidden;
 	int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
 		sizeof(long));
 
@@ -182,12 +187,22 @@ static int filldir(struct dir_context *c
 	}
 	dirent = buf->previous;
 	if (dirent) {
+		hidden = gobohide_get(d_ino, name, namlen, buf->dentry);
+		if (hidden) {
+			gobohide_put(hidden);
+			return 0;
+		}
 		if (signal_pending(current))
 			return -EINTR;
 		if (__put_user(offset, &dirent->d_off))
 			goto efault;
 	}
 	dirent = buf->current_dir;
+	hidden = gobohide_get(d_ino, name, namlen, buf->dentry);
+	if (hidden) {
+		gobohide_put(hidden);
+		return 0;
+	}
 	if (__put_user(d_ino, &dirent->d_ino))
 		goto efault;
 	if (__put_user(reclen, &dirent->d_reclen))
@@ -227,6 +242,7 @@ SYSCALL_DEFINE3(getdents, unsigned int,
 	if (!f.file)
 		return -EBADF;
 
+	buf.dentry = f.file->f_path.dentry;
 	error = iterate_dir(f.file, &buf.ctx);
 	if (error >= 0)
 		error = buf.error;
@@ -247,12 +263,14 @@ struct getdents_callback64 {
 	struct linux_dirent64 __user * previous;
 	int count;
 	int error;
+	struct dentry *dentry;
 };
 
 static int filldir64(struct dir_context *ctx, const char *name, int namlen,
 		     loff_t offset, u64 ino, unsigned int d_type)
 {
 	struct linux_dirent64 __user *dirent;
+	struct hide *hidden;
 	struct getdents_callback64 *buf =
 		container_of(ctx, struct getdents_callback64, ctx);
 	int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
@@ -263,12 +281,22 @@ static int filldir64(struct dir_context
 		return -EINVAL;
 	dirent = buf->previous;
 	if (dirent) {
+		hidden = gobohide_get(ino, name, namlen, buf->dentry);
+		if (hidden) {
+			gobohide_put(hidden);
+			return 0;
+		}
 		if (signal_pending(current))
 			return -EINTR;
 		if (__put_user(offset, &dirent->d_off))
 			goto efault;
 	}
 	dirent = buf->current_dir;
+	hidden = gobohide_get(ino, name, namlen, buf->dentry);
+	if (hidden) {
+		gobohide_put(hidden);
+		return 0;
+	}
 	if (__put_user(ino, &dirent->d_ino))
 		goto efault;
 	if (__put_user(0, &dirent->d_off))
@@ -310,6 +338,7 @@ SYSCALL_DEFINE3(getdents64, unsigned int
 	if (!f.file)
 		return -EBADF;
 
+	buf.dentry = f.file->f_path.dentry;
 	error = iterate_dir(f.file, &buf.ctx);
 	if (error >= 0)
 		error = buf.error;
diff -Nurp linux-4.9.4.orig/include/uapi/linux/gobohide.h linux-4.9.4/include/uapi/linux/gobohide.h
--- linux-4.9.4.orig/include/uapi/linux/gobohide.h	1969-12-31 21:00:00.000000000 \
-0300
+++ linux-4.9.4/include/uapi/linux/gobohide.h	2017-01-16 18:30:16.207188810 -0200
@@ -0,0 +1,67 @@
+#ifndef _LINUX_GOBOHIDE_H
+#define _LINUX_GOBOHIDE_H
+
+#define GOBOHIDE_GENL_NAME    "gobohide"
+#define GOBOHIDE_GENL_VERSION  0x01
+
+/* netlink commands */
+enum {
+	GOBOHIDE_CMD_INVALID = 0,
+	GOBOHIDE_CMD_HIDE,       /* userspace -> kernel */
+	GOBOHIDE_CMD_UNHIDE,     /* userspace -> kernel */
+	GOBOHIDE_CMD_FLUSH,      /* userspace -> kernel */
+	GOBOHIDE_CMD_LIST,       /* userspace -> kernel */
+	GOBOHIDE_CMD_LIST_SIZE,  /* kernel -> userspace */
+	GOBOHIDE_CMD_LIST_REPLY, /* kernel -> userspace */
+	__GOBOHIDE_CMD_MAX
+};
+#define GOBOHIDE_CMD_MAX (__GOBOHIDE_CMD_MAX - 1)
+
+/* netlink policies */
+enum {
+	GOBOHIDE_CMD_ATTR_UNSPEC = 0,
+	GOBOHIDE_CMD_ATTR_PATH,
+	GOBOHIDE_CMD_ATTR_INODE,
+	__GOBOHIDE_CMD_ATTR_MAX,
+};
+#define GOBOHIDE_CMD_ATTR_MAX (__GOBOHIDE_CMD_ATTR_MAX - 1)
+
+/* netlink data types (kernel -> userspace) */
+enum {
+	GOBOHIDE_TYPE_UNSPECT = 0,
+	GOBOHIDE_TYPE_PATH,
+	GOBOHIDE_TYPE_LIST_SIZE,
+	__GOBOHIDE_TYPE_MAX,
+};
+#define GOBOHIDE_TYPE_MAX (__GOBOHIDE_TYPE_MAX - 1)
+
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/dcache.h>
+
+/* internal structure representing a hidden entry */
+struct hide {
+   ino_t i_ino;            /* shortcut to inode number */
+   struct file *filp;      /* used to recover the inode's pathname */
+   struct path path;       /* stores the path after a call to user_lpath */
+   char *pathname;         /* a fresh cache of the inode's pathname */
+   unsigned long page;     /* page on which pathname has been copied to */
+   unsigned long refcount; /* number of reference counts to this object */
+   int unlinked;           /* has the structure been unlinked yet? */
+   struct list_head head;  /* a simple doubly linked list */
+};
+
+#ifdef CONFIG_GOBOHIDE_FS
+struct hide *gobohide_get(ino_t ino, const char *filename,
+	int namelen, struct dentry *parent);
+int  gobohide_put(struct hide *entry);
+int  gobohide_remove(struct hide *hide);
+#else
+# define gobohide_get(ino, filename, namelen, parent) NULL
+# define gobohide_put(entry) 0
+# define gobohide_remove(hide) 0
+#endif  /* CONFIG_GOBOHIDE_FS */
+#endif  /* __KERNEL__ */
+#endif  /* _LINUX_GOBOHIDE_H */
Linux 4.9.4-r5 1018  99374 Luca...
The Linux Kernel.
Linux 4.8.2-r2 1084  89394 Luca...
The Linux Kernel.
Linux 4.7.4-r1 1154  82767 Luca...
The Linux Kernel.
Linux 4.7.0-r4 1209  82585 Luca...
The Linux Kernel.
Linux 3.13.3-r1 2089  252629 Luca...
The Linux Kernel.
Linux 3.12.6-r1 2149  238949 Luca...
The Linux Kernel.
Linux 3.9.4-r2 2344  70048 Luca...
The Linux Kernel.
Linux 3.7.1-r2 2517  67579 Luca...
The Linux Kernel.
Linux 3.5.0-r1 2668  124391 Luca...
The Linux Kernel.
Linux 3.4.4-r1 2668  124348 Luca...
The Linux Kernel.
Linux 3.3.6-r1 2668  124410 Luca...
The Linux Kernel.
Linux 3.2.12-r2 2796  124345 Luca...
The Linux Kernel.
Linux 3.2.7-r1 2826  123550 Mich...
The Linux Kernel.
Linux 3.1.1-r1 2931  122907 Mich...
The Linux Kernel.
Linux 3.0.4-r4 2992  122754 Luca...
The Linux Kernel.
Linux 2.6.36.3-r1 3215  116087 Diog...
The Linux Kernel.
Linux 2.6.32.3-r1 3593  117990 Luca...
The Linux Kernel.
Linux 2.6.32-r1 3634  117751 Luca...
The Linux Kernel.
Linux 2.6.31.6-r3 3639  126499 Luca...
The Linux Kernel.
Linux 2.6.30.5-r1 3718  166102 Jona...
The Linux Kernel.
Linux 2.6.29.1-r1 3861  117500 Luca...
The Linux Kernel.
Linux 2.6.28.7-r1 3917  115518 Giam...
The Linux Kernel.
Linux 2.6.28.1-r1 3917  115487 Giam...
The Linux Kernel.
Linux 2.6.28-r1 3917  116681 Mich...
The Linux Kernel.
Linux 2.6.27.8-r1 3917  134160 Giam...
The Linux Kernel.
Linux 2.6.27.4-r3 3917  149529 Luca...
The Linux Kernel.
Linux 2.6.25.17-r1 3917  172834 Giam...
The Linux Kernel.
Linux 2.6.25.16-r1 3917  166500 Giam...
The Linux Kernel.
Linux 2.6.25.10-r2 3917  165320 Giam...
The Linux Kernel.
Linux 2.6.25.7-r1 3917  157294 Giam...
The Linux Kernel.
Linux 2.6.25.4-r1 3917  133017 Hopp...
The Linux Kernel.
Linux 2.6.25-r1 3917  133216 Luca...
The Linux Kernel.
Linux 2.6.24.4-r5 3917  150733
The Linux Kernel.
Linux 2.6.24.3-r5 3917  150221 Luca...
The Linux Kernel.
Linux 2.6.24.2-r3 3917  146488 Giam...
The Linux Kernel.
Linux 2.6.24.1-r1 3917  146454 Giam...
The Linux Kernel.
Linux 2.6.24-r1 3917  146428 Luca...
The Linux Kernel.
Linux 2.6.23.8-r4 3917  155842 Luca...
The Linux Kernel.
Linux 2.6.22.7-r4 3917  114727 Luca...
The Linux Kernel.
Linux 2.6.22.1-r1 3917  121391 Luca...
The Linux Kernel.
Linux 2.6.21.1-r3 3917  118854 Luca...
The Linux Kernel.
Linux 2.6.20.7-r1 3917  117945 Luca...
The Linux Kernel.
Linux 2.6.20.4-r3 3917  151150 Luca...
The Linux Kernel.
Linux 2.6.20-r1 3917  106429 Luca...
The Linux Kernel.
Linux 2.6.18.3-r2 3917  111124 Luca...
The Linux Kernel.
Linux 2.6.17.11-r1 3917  164053 Luca...
The Linux Kernel.
Linux 2.6.17.3-r1 3917  165067 Luca...
The Linux Kernel.
Linux 2.6.16.20-r1 3917  133625 Luca...
The Linux Kernel.
Linux 2.6.16.14-r1 3917  168270 Luca...
The Linux Kernel.
Linux 2.6.15.5-r1 3917  224686 Jona...
The Linux Kernel.
Linux 2.6.15.2-r1 3917  177165 Carl...
The Linux Kernel.
Linux 2.6.15.1-r1 3917  149219 Jona...
The Linux Kernel.
Linux 2.6.15-r1 3917  149214 Luca...
The Linux Kernel.
Linux 2.6.14.4-r1 3917  150166 Jona...
The Linux Kernel.
Linux 2.6.14.3-r1 3917  150060 Jona...
The Linux Kernel.
Linux 2.6.14.2-r1 3917  149791 Carl...
The Linux Kernel.
Linux 2.6.13.4-r1 3917  149559 Luca...
The Linux Kernel.
Linux 2.6.13.2-r1 3917  56611 Jona...
The Linux Kernel.
Linux 2.6.13.1-r1 3917  56378 Luca...
The Linux Kernel.
Linux 2.6.12.2-r1 3917  50355 Luca...
The Linux Kernel.
Linux 2.6.11.9-r1 3917  98969 Luca...
The Linux Kernel.
Linux 2.6.11.8-r1 3917  109424 Jona...
The Linux Kernel.
Linux 2.6.11-r1 3917  99032 Luca...
The Linux Kernel.
Linux 2.6.10-r1 3917  65969 Luca...
The Linux Kernel.
Linux 2.6.9-r1 3917  278461 Luca...
The Linux Kernel.
Linux 2.6.8.1-r1 3917  35576 Luca...
The Linux Kernel.
Linux 2.6.7-r1 3917  22610 Luca...
The Linux Kernel.
Linux 2.6.6-r1 3917  21958 Luca...
The Linux Kernel.
Linux 2.4.26-r1 3917  22359 Luca...
The Linux Kernel.