It does (OK, is supposed to do) the following: make noexec, nosuid
and nodev properties of vfsmount, not superblock.
	In other words, different instances of the same fs may differ in
these flags.
	It has quite a few lovely uses. Example:
I'm sick and tired of a little skript k1dd13 wannabe (who happens to be
a son of $BIG_BOSS and thus is a holy cow). I want to make his $HOME
noexec (all public writable places already are). I don't want to
bother with giving him a partition of his own and I don't want to screw
normal users.
Solution:
	mount --bind /home/little_wanker /home/little_wanker
	mount -o remount,noexec /home/little_wanker
Opposite examples (selectively allowing exec or suid on subtrees of
fs that is otherwise noexec/nosuid) are along the same lines.  We can
even do it for individual files ;-)
More serious applications may be in situations when we want to set a
chroot environment with selective (or global, for that matter)
removal/allowing suid/exec/dev without messing everything else.
	Patch is pretty straightforward - in call cases when we did
IS_NOSUID(), etc. we have relevant struct vfsmount, so we can check
->mnt_flags instead of ->s_flags. do_mount() separates these flags
from the rest and does the (obvious) right thing. That, and we have
to look both at ->mnt_sb->s_flags and ->mnt_flags when we read from
/proc/mounts.
	You are welcome to play with it ;-) Its equivalent works fine
in full namespace patch. This is a backport, and all I promise is
that it builds. Help with testing is welcome.
								Al
diff -urN S5-pre4/arch/sparc64/solaris/fs.c S5-pre4-noexe/arch/sparc64/solaris/fs.c
--- S5-pre4/arch/sparc64/solaris/fs.c	Thu Feb 22 06:47:53 2001
+++ S5-pre4-noexe/arch/sparc64/solaris/fs.c	Sun May 20 12:50:36 2001
@@ -406,21 +406,21 @@
 	u32	f_filler[16];
 };
 
-static int report_statvfs(struct inode *inode, u32 buf)
+static int report_statvfs(struct vfsmount *mnt, struct inode *inode, u32 buf)
 {
 	struct statfs s;
 	int error;
 	struct sol_statvfs *ss = (struct sol_statvfs *)A(buf);
 
-	error = vfs_statfs(inode->i_sb, &s);
+	error = vfs_statfs(mnt->mnt_sb, &s);
 	if (!error) {
-		const char *p = inode->i_sb->s_type->name;
+		const char *p = mnt->mnt_sb->s_type->name;
 		int i = 0;
 		int j = strlen (p);
 		
 		if (j > 15) j = 15;
 		if (IS_RDONLY(inode)) i = 1;
-		if (IS_NOSUID(inode)) i |= 2;
+		if (mnt->mnt_flags & MNT_NOSUID) i |= 2;
 		if (put_user (s.f_bsize, &ss->f_bsize)		||
 		    __put_user (0, &ss->f_frsize)		||
 		    __put_user (s.f_blocks, &ss->f_blocks)	||
@@ -440,21 +440,21 @@
 	return error;
 }
 
-static int report_statvfs64(struct inode *inode, u32 buf)
+static int report_statvfs64(struct vfsmount *mnt, struct inode *inode, u32 buf)
 {
 	struct statfs s;
 	int error;
 	struct sol_statvfs64 *ss = (struct sol_statvfs64 *)A(buf);
 			
-	error = vfs_statfs(inode->i_sb, &s);
+	error = vfs_statfs(mnt->mnt_sb, &s);
 	if (!error) {
-		const char *p = inode->i_sb->s_type->name;
+		const char *p = mnt->mnt_sb->s_type->name;
 		int i = 0;
 		int j = strlen (p);
 		
 		if (j > 15) j = 15;
 		if (IS_RDONLY(inode)) i = 1;
-		if (IS_NOSUID(inode)) i |= 2;
+		if (mnt->mnt_flags & MNT_NOSUID) i |= 2;
 		if (put_user (s.f_bsize, &ss->f_bsize)		||
 		    __put_user (0, &ss->f_frsize)		||
 		    __put_user (s.f_blocks, &ss->f_blocks)	||
@@ -482,7 +482,7 @@
 	error = user_path_walk((const char *)A(path),&nd);
 	if (!error) {
 		struct inode * inode = nd.dentry->d_inode;
-		error = report_statvfs(inode, buf);
+		error = report_statvfs(nd.mnt, inode, buf);
 		path_release(&nd);
 	}
 	return error;
@@ -496,7 +496,7 @@
 	error = -EBADF;
 	file = fget(fd);
 	if (file) {
-		error = report_statvfs(file->f_dentry->d_inode, buf);
+		error = report_statvfs(file->f_vfsmnt, file->f_dentry->d_inode, buf);
 		fput(file);
 	}
 
@@ -512,7 +512,7 @@
 	error = user_path_walk((const char *)A(path), &nd);
 	if (!error) {
 		struct inode * inode = nd.dentry->d_inode;
-		error = report_statvfs64(inode, buf);
+		error = report_statvfs64(nd.mnt, inode, buf);
 		path_release(&nd);
 	}
 	unlock_kernel();
@@ -528,7 +528,7 @@
 	file = fget(fd);
 	if (file) {
 		lock_kernel();
-		error = report_statvfs64(file->f_dentry->d_inode, buf);
+		error = report_statvfs64(file->f_vfsmnt, file->f_dentry->d_inode, buf);
 		unlock_kernel();
 		fput(file);
 	}
diff -urN S5-pre4/fs/exec.c S5-pre4-noexe/fs/exec.c
--- S5-pre4/fs/exec.c	Sat Apr 28 02:12:56 2001
+++ S5-pre4-noexe/fs/exec.c	Sun May 20 12:46:46 2001
@@ -347,7 +347,8 @@
 	if (!err) {
 		inode = nd.dentry->d_inode;
 		file = ERR_PTR(-EACCES);
-		if (!IS_NOEXEC(inode) && S_ISREG(inode->i_mode)) {
+		if (!(nd.mnt->mnt_flags & MNT_NOEXEC) &&
+		    S_ISREG(inode->i_mode)) {
 			int err = permission(inode, MAY_EXEC);
 			file = ERR_PTR(err);
 			if (!err) {
@@ -615,7 +616,7 @@
 	bprm->e_uid = current->euid;
 	bprm->e_gid = current->egid;
 
-	if(!IS_NOSUID(inode)) {
+	if(!(bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)) {
 		/* Set-uid? */
 		if (mode & S_ISUID)
 			bprm->e_uid = inode->i_uid;
diff -urN S5-pre4/fs/fat/inode.c S5-pre4-noexe/fs/fat/inode.c
--- S5-pre4/fs/fat/inode.c	Sat Apr 28 02:12:56 2001
+++ S5-pre4-noexe/fs/fat/inode.c	Sun May 20 12:48:59 2001
@@ -806,9 +806,8 @@
 		MSDOS_I(inode)->mmu_private = inode->i_size;
 	} else { /* not a directory */
 		inode->i_mode = MSDOS_MKMODE(de->attr,
-		    ((IS_NOEXEC(inode) || 
-		      (sbi->options.showexec &&
-		       !is_exec(de->ext)))
+		    ((sbi->options.showexec &&
+		       !is_exec(de->ext))
 		    	? S_IRUGO|S_IWUGO : S_IRWXUGO)
 		    & ~sbi->options.fs_umask) | S_IFREG;
 		MSDOS_I(inode)->i_start = CF_LE_W(de->start);
@@ -927,9 +926,7 @@
 
 	inode_setattr(inode, attr);
 
-	if (IS_NOEXEC(inode) && !S_ISDIR(inode->i_mode))
-		inode->i_mode &= S_IFMT | S_IRUGO | S_IWUGO;
-	else
+	if (S_ISDIR(inode->i_mode))
 		inode->i_mode |= S_IXUGO;
 
 	inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
diff -urN S5-pre4/fs/hfs/inode.c S5-pre4-noexe/fs/hfs/inode.c
--- S5-pre4/fs/hfs/inode.c	Fri Feb 16 22:55:36 2001
+++ S5-pre4-noexe/fs/hfs/inode.c	Sun May 20 12:49:15 2001
@@ -38,7 +38,7 @@
 	struct hfs_fork *fk;
 	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
 
-	if (!IS_NOEXEC(inode) && (fork == HFS_FK_DATA)) {
+	if (fork == HFS_FK_DATA) {
 		inode->i_mode = S_IRWXUGO | S_IFREG;
 	} else {
 		inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
diff -urN S5-pre4/fs/namei.c S5-pre4-noexe/fs/namei.c
--- S5-pre4/fs/namei.c	Sat May 19 22:46:35 2001
+++ S5-pre4-noexe/fs/namei.c	Sun May 20 12:48:17 2001
@@ -1051,7 +1051,7 @@
 	    	flag &= ~O_TRUNC;
 	} else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
 		error = -EACCES;
-		if (IS_NODEV(inode))
+		if (nd->mnt->mnt_flags & MNT_NODEV)
 			goto exit;
 
 		flag &= ~O_TRUNC;
diff -urN S5-pre4/fs/super.c S5-pre4-noexe/fs/super.c
--- S5-pre4/fs/super.c	Sat May 19 22:46:35 2001
+++ S5-pre4-noexe/fs/super.c	Sun May 20 13:12:31 2001
@@ -55,7 +55,7 @@
 extern int root_mountflags;
 
 static int do_remount_sb(struct super_block *sb, int flags, char * data);
-static int do_remount(const char *dir, int flags, char * data);
+static int do_remount(const char *dir, int flags, int mnt_flags, char * data);
 
 /* this is initialized in init/main.c */
 kdev_t ROOT_DEV;
@@ -307,7 +307,8 @@
 
 static struct vfsmount *add_vfsmnt(struct nameidata *nd,
 				struct dentry *root,
-				const char *dev_name)
+				const char *dev_name,
+				int mnt_flags)
 {
 	struct vfsmount *mnt;
 	struct super_block *sb = root->d_inode->i_sb;
@@ -318,8 +319,9 @@
 		goto out;
 	memset(mnt, 0, sizeof(struct vfsmount));
 
+	mnt->mnt_flags = mnt_flags;
 	if (nd || dev_name)
-		mnt->mnt_flags = MNT_VISIBLE;
+		mnt->mnt_flags |= MNT_VISIBLE;
 
 	/* It may be NULL, but who cares? */
 	if (dev_name) {
@@ -460,16 +462,17 @@
 	int flag;
 	char *str;
 } fs_info[] = {
-	{ MS_NOEXEC, ",noexec" },
-	{ MS_NOSUID, ",nosuid" },
-	{ MS_NODEV, ",nodev" },
 	{ MS_SYNCHRONOUS, ",sync" },
 	{ MS_MANDLOCK, ",mand" },
 	{ MS_NOATIME, ",noatime" },
 	{ MS_NODIRATIME, ",nodiratime" },
-#ifdef MS_NOSUB			/* Can't find this except in mount.c */
-	{ MS_NOSUB, ",nosub" },
-#endif
+	{ 0, NULL }
+};
+
+static struct proc_fs_info mnt_info[] = {
+	{ MNT_NOSUID, ",nosuid" },
+	{ MNT_NODEV, ",nodev" },
+	{ MNT_NOEXEC, ",noexec" },
 	{ 0, NULL }
 };
 
@@ -522,6 +525,10 @@
 			if (tmp->mnt_sb->s_flags & fs_infop->flag)
 				MANGLE(fs_infop->str);
 		}
+		for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) {
+			if (tmp->mnt_flags & fs_infop->flag)
+				MANGLE(fs_infop->str);
+		}
 		if (!strcmp("nfs", tmp->mnt_sb->s_type->name)) {
 			nfss = &tmp->mnt_sb->u.nfs_sb.s_server;
 			len += sprintf(buf+len, ",v%d", nfss->rpc_ops->version);
@@ -786,7 +793,7 @@
 	if (!S_ISBLK(inode->i_mode))
 		goto out;
 	error = -EACCES;
-	if (IS_NODEV(inode))
+	if (nd.mnt->mnt_flags & MNT_NODEV)
 		goto out;
 	bdev = inode->i_bdev;
 	bdops = devfs_get_ops ( devfs_get_handle_from_inode (inode) );
@@ -963,7 +970,7 @@
 		put_unnamed_dev(dev);
 		return ERR_PTR(-EINVAL);
 	}
-	mnt = add_vfsmnt(NULL, sb->s_root, NULL);
+	mnt = add_vfsmnt(NULL, sb->s_root, NULL, 0);
 	if (!mnt) {
 		kill_super(sb, 0);
 		return ERR_PTR(-ENOMEM);
@@ -1014,7 +1021,7 @@
 		 * we just try to remount it readonly.
 		 */
 		mntput(mnt);
-		return do_remount("/", MS_RDONLY, NULL);
+		return do_remount("/", MS_RDONLY, 0, NULL);
 	}
 
 	spin_lock(&dcache_lock);
@@ -1155,11 +1162,11 @@
 	int err = 0;
 	if (!old_name || !*old_name)
 		return -EINVAL;
-	if (path_init(old_name, LOOKUP_POSITIVE, &old_nd))
+	if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd))
 		err = path_walk(old_name, &old_nd);
 	if (err)
 		goto out;
-	if (path_init(new_name, LOOKUP_POSITIVE, &new_nd))
+	if (path_init(new_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &new_nd))
 		err = path_walk(new_name, &new_nd);
 	if (err)
 		goto out1;
@@ -1180,7 +1187,8 @@
 	down(&new_nd.dentry->d_inode->i_zombie);
 	if (IS_DEADDIR(new_nd.dentry->d_inode))
 		err = -ENOENT;
-	else if (add_vfsmnt(&new_nd, old_nd.dentry, old_nd.mnt->mnt_devname))
+	else if (add_vfsmnt(&new_nd, old_nd.dentry, old_nd.mnt->mnt_devname,
+				old_nd.mnt->mnt_flags))
 		err = 0;
 	up(&new_nd.dentry->d_inode->i_zombie);
 	up(&mount_sem);
@@ -1200,7 +1208,7 @@
  * on it - tough luck.
  */
 
-static int do_remount(const char *dir,int flags,char *data)
+static int do_remount(const char *dir,int flags,int mnt_flags,char *data)
 {
 	struct nameidata nd;
 	int retval = 0;
@@ -1215,7 +1223,7 @@
 		retval = -ENODEV;
 		if (sb) {
 			retval = -EINVAL;
-			if (nd.dentry == sb->s_root) {
+			if (nd.dentry == nd.mnt->mnt_root) {
 				/*
 				 * Shrink the dcache and sync the device.
 				 */
@@ -1224,6 +1232,8 @@
 				if (flags & MS_RDONLY)
 					acct_auto_close(sb->s_dev);
 				retval = do_remount_sb(sb, flags, data);
+				if (!retval)
+					nd.mnt->mnt_flags=MNT_VISIBLE|mnt_flags;
 			}
 		}
 		path_release(&nd);
@@ -1286,6 +1296,7 @@
 	struct vfsmount *mnt = NULL;
 	struct super_block *sb;
 	int retval = 0;
+	int mnt_flags = 0;
 
 	/* Discard magic */
 	if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
@@ -1298,11 +1309,20 @@
 	if (dev_name && !memchr(dev_name, 0, PAGE_SIZE))
 		return -EINVAL;
 
+	/* Separate the per-mountpoint flags */
+	if (flags & MS_NOSUID)
+		mnt_flags |= MNT_NOSUID;
+	if (flags & MS_NODEV)
+		mnt_flags |= MNT_NODEV;
+	if (flags & MS_NOEXEC)
+		mnt_flags |= MNT_NOEXEC;
+	flags &= ~(MS_NOSUID|MS_NOEXEC|MS_NODEV);
+
 	/* OK, looks good, now let's see what do they want */
 
 	/* just change the flags? - capabilities are checked in do_remount() */
 	if (flags & MS_REMOUNT)
-		return do_remount(dir_name, flags & ~MS_REMOUNT,
+		return do_remount(dir_name, flags & ~MS_REMOUNT, mnt_flags,
 				  (char *) data_page);
 
 	/* "mount --bind"? Equivalent to older "mount -t bind" */
@@ -1361,7 +1381,7 @@
 	down(&nd.dentry->d_inode->i_zombie);
 	if (!IS_DEADDIR(nd.dentry->d_inode)) {
 		retval = -ENOMEM;
-		mnt = add_vfsmnt(&nd, sb->s_root, dev_name);
+		mnt = add_vfsmnt(&nd, sb->s_root, dev_name, mnt_flags);
 	}
 	up(&nd.dentry->d_inode->i_zombie);
 	if (!mnt)
@@ -1568,10 +1588,10 @@
 		devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
 				  path + 5 + path_start, NULL, NULL);
 		memcpy (path + path_start, "/dev/", 5);
-		vfsmnt = add_vfsmnt(NULL, sb->s_root, path + path_start);
+		vfsmnt = add_vfsmnt(NULL, sb->s_root, path + path_start, 0);
 	}
 	else
-		vfsmnt = add_vfsmnt(NULL, sb->s_root, "/dev/root");
+		vfsmnt = add_vfsmnt(NULL, sb->s_root, "/dev/root", 0);
 	/* FIXME: if something will try to umount us right now... */
 	if (vfsmnt) {
 		set_fs_root(current->fs, vfsmnt, sb->s_root);
diff -urN S5-pre4/include/linux/fs.h S5-pre4-noexe/include/linux/fs.h
--- S5-pre4/include/linux/fs.h	Sat May 19 22:46:36 2001
+++ S5-pre4-noexe/include/linux/fs.h	Sun May 20 13:03:23 2001
@@ -116,10 +116,10 @@
 #define MS_BIND		4096
 
 /*
- * Flags that can be altered by MS_REMOUNT
+ * Superblock flags that can be altered by MS_REMOUNT
  */
-#define MS_RMT_MASK	(MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|\
-			MS_SYNCHRONOUS|MS_MANDLOCK|MS_NOATIME|MS_NODIRATIME)
+#define MS_RMT_MASK	(MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_NOATIME|\
+			 MS_NODIRATIME)
 
 /*
  * Old magic mount flag and mask
@@ -152,9 +152,6 @@
 #define __IS_FLG(inode,flg) ((inode)->i_sb->s_flags & (flg))
 
 #define IS_RDONLY(inode) ((inode)->i_sb->s_flags & MS_RDONLY)
-#define IS_NOSUID(inode)	__IS_FLG(inode, MS_NOSUID)
-#define IS_NODEV(inode)		__IS_FLG(inode, MS_NODEV)
-#define IS_NOEXEC(inode)	__IS_FLG(inode, MS_NOEXEC)
 #define IS_SYNC(inode)		(__IS_FLG(inode, MS_SYNCHRONOUS) || ((inode)->i_flags & S_SYNC))
 #define IS_MANDLOCK(inode)	__IS_FLG(inode, MS_MANDLOCK)
 
diff -urN S5-pre4/include/linux/mount.h S5-pre4-noexe/include/linux/mount.h
--- S5-pre4/include/linux/mount.h	Fri Feb 16 18:33:56 2001
+++ S5-pre4-noexe/include/linux/mount.h	Sun May 20 12:44:02 2001
@@ -12,7 +12,10 @@
 #define _LINUX_MOUNT_H
 #ifdef __KERNEL__
 
-#define MNT_VISIBLE	1
+#define MNT_NOSUID	1
+#define MNT_NODEV	2
+#define MNT_NOEXEC	4
+#define MNT_VISIBLE	8
 
 struct vfsmount
 {
-
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/