Subject: Re: mkdir with trailing / (patch proposed)
To: Bill Studenmund <wrstuden@netbsd.org>
From: Matthew Orgass <darkstar@pgh.net>
List: tech-userlevel
Date: 05/09/2002 16:36:44
On Mon, 6 May 2002, Bill Studenmund wrote:
> On Sun, 5 May 2002, Matthew Orgass wrote:
> >   I'd be happy to provide a revised patch once its decided what should be
> > done.  My previous patch was incorrect in that it allowed pathnames with a
> > trailing slash to refer to symlinks (under emulation only), which is
> > forbidden.
>
> Well, fix that and let's see it. :-) Changing native behavior is real easy
> with any of these patches once we have lookup() fixed.

  Ok, here is the update.  I also checked the Linux source and found that
they haven't allowed trailing slashes on regular files in any 2.X kernels.
I thought I had seen mention of a Linux binary that wanted to do this, but
trying to find it again I've only seen reference to mkdir.  Unless someone
knows of a Linux app that really wants the old behavior it's probably
better to restrict it in Linux emul too.

Matthew Orgass
darkstar@pgh.net

Index: sys/proc.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/proc.h,v
retrieving revision 1.136
diff -u -r1.136 proc.h
--- proc.h	2002/01/11 21:16:27	1.136
+++ proc.h	2002/05/08 22:10:50
@@ -91,9 +91,9 @@
 struct emul {
 	const char	*e_name;	/* Symbolic name */
 	const char	*e_path;	/* Extra emulation path (NULL if none)*/
-#ifndef __HAVE_MINIMAL_EMUL
 	int		e_flags;	/* Miscellaneous flags, see above */
 					/* Syscall handling function */
+#ifndef __HAVE_MINIMAL_EMUL
 	const int	*e_errno;	/* Errno array */
 	int		e_nosys;	/* Offset of the nosys() syscall */
 	int		e_nsysent;	/* Number of system call entries */
@@ -127,6 +127,8 @@
  * Emulation miscelaneous flags
  */
 #define	EMUL_HAS_SYS___syscall	0x001	/* Has SYS___syscall */
+#define	EMUL_TSLASHOK		0x002	/* Trailing slashes always valid */
+#define	EMUL_TSLASHMK		0x004	/* Trailing slashes ok on mkdir */

 /*
  * Description of a process.
Index: kern/vfs_lookup.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/vfs_lookup.c,v
retrieving revision 1.39
diff -u -r1.39 vfs_lookup.c
--- vfs_lookup.c	2001/12/08 04:09:59	1.39
+++ vfs_lookup.c	2002/05/08 22:10:53
@@ -107,6 +107,10 @@
 #endif
 	cwdi = cnp->cn_proc->p_cwdi;

+	/* Some systems allow trailing slashes on all files */
+	if (cnp->cn_proc->p_emul->e_flags & EMUL_TSLASHOK)
+		cnp->cn_flags |= TSLASHOK;
+
 	/*
 	 * Get a buffer for the name to be translated, and copy the
 	 * name into the buffer.
@@ -420,6 +424,16 @@
 		else
 			cnp->cn_flags &= ~MAKEENTRY;
 		cnp->cn_flags |= ISLASTCN;
+
+		/* The treatment of trailing slashes differs between
+		 * systems and standards.  By default they can refer only
+		 * to existing directories.  With TSLASHOK, they will be
+		 * ignored.  SUSv3 specifies that they should be treated
+		 * as '/.', which is not currently implemented. */
+		if (cnp->cn_flags & TSLASHOK) {
+			cnp->cn_flags &= ~REQUIREDIR;
+			cnp->cn_flags |= FOLLOW;
+		}
 	} else {
 		cnp->cn_flags |= MAKEENTRY;
 		cnp->cn_flags &= ~ISLASTCN;
Index: ufs/ufs/ufs_lookup.c
===================================================================
RCS file: /cvsroot/syssrc/sys/ufs/ufs/ufs_lookup.c,v
retrieving revision 1.38
diff -u -r1.38 ufs_lookup.c
--- ufs_lookup.c	2001/12/18 10:57:23	1.38
+++ ufs_lookup.c	2002/05/08 22:11:00
@@ -697,7 +697,8 @@
 #endif
 	newdirp->d_ino = ip->i_number;
 	newdirp->d_namlen = cnp->cn_namelen;
-	memcpy(newdirp->d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1);
+	memcpy(newdirp->d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen);
+	newdirp->d_name[cnp->cn_namelen] = 0;
 	if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0)
 		newdirp->d_type = IFTODT(ip->i_ffs_mode);
 	else {
Index: ufs/ufs/ufs_vnops.c
===================================================================
RCS file: /cvsroot/syssrc/sys/ufs/ufs/ufs_vnops.c,v
retrieving revision 1.85
diff -u -r1.85 ufs_vnops.c
--- ufs_vnops.c	2001/12/23 16:16:59	1.85
+++ ufs_vnops.c	2002/05/08 22:11:03
@@ -729,7 +729,8 @@
 		newdir.d_ino = WINO;
 		newdir.d_namlen = cnp->cn_namelen;
 		memcpy(newdir.d_name, cnp->cn_nameptr,
-		    (unsigned)cnp->cn_namelen + 1);
+		    (unsigned)cnp->cn_namelen);
+		newdir.d_name[cnp->cn_namelen] = 0;
 		newdir.d_type = DT_WHT;
 		error = ufs_direnter(dvp, NULL, &newdir, cnp, NULL);
 		break;
Index: ufs/ext2fs/ext2fs_lookup.c
===================================================================
RCS file: /cvsroot/syssrc/sys/ufs/ext2fs/ext2fs_lookup.c,v
retrieving revision 1.18
diff -u -r1.18 ext2fs_lookup.c
--- ext2fs_lookup.c	2001/11/08 02:39:07	1.18
+++ ext2fs_lookup.c	2002/05/08 22:11:06
@@ -778,7 +778,8 @@
 	} else {
 		newdir.e2d_type = 0;
 	};
-	memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1);
+	memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen);
+	newdir.e2d_name[cnp->cn_namelen] = 0;
 	newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen);
 	if (dp->i_count == 0) {
 		/*
Index: compat/aout/aout_exec.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/aout/aout_exec.c,v
retrieving revision 1.13
diff -u -r1.13 aout_exec.c
--- aout_exec.c	2001/11/13 02:07:52	1.13
+++ aout_exec.c	2002/05/08 22:11:06
@@ -65,8 +65,8 @@
 struct emul emul_netbsd_aout = {
 	"netbsd",
 	"/emul/aout",
-#ifndef __HAVE_MINIMAL_EMUL
 	EMUL_HAS_SYS___syscall,
+#ifndef __HAVE_MINIMAL_EMUL
 	NULL,
 	AOUT_SYS_syscall,
 	AOUT_SYS_MAXSYSCALL,
Index: compat/freebsd/freebsd_exec.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/freebsd/freebsd_exec.c,v
retrieving revision 1.17
diff -u -r1.17 freebsd_exec.c
--- freebsd_exec.c	2001/11/13 02:08:06	1.17
+++ freebsd_exec.c	2002/05/08 22:11:06
@@ -53,8 +53,8 @@
 const struct emul emul_freebsd = {
 	"freebsd",
 	"/emul/freebsd",
+	EMUL_HAS_SYS___syscall | EMUL_TSLASHMK,
 #ifndef __HAVE_MINIMAL_EMUL
-	EMUL_HAS_SYS___syscall,
 	NULL,
 	FREEBSD_SYS_syscall,
 	FREEBSD_SYS_MAXSYSCALL,
Index: compat/ibcs2/ibcs2_exec.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/ibcs2/ibcs2_exec.c,v
retrieving revision 1.49
diff -u -r1.49 ibcs2_exec.c
--- ibcs2_exec.c	2001/11/13 02:08:22	1.49
+++ ibcs2_exec.c	2002/05/08 22:11:06
@@ -72,8 +72,8 @@
 const struct emul emul_ibcs2 = {
 	"ibcs2",
 	"/emul/ibcs2",
+	EMUL_TSLASHMK,
 #ifndef __HAVE_MINIMAL_EMUL
-	0,
 	native_to_ibcs2_errno,
 	IBCS2_SYS_syscall,
 	IBCS2_SYS_MAXSYSCALL,
Index: compat/irix/irix_exec.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/irix/irix_exec.c,v
retrieving revision 1.12
diff -u -r1.12 irix_exec.c
--- irix_exec.c	2002/02/21 21:53:00	1.12
+++ irix_exec.c	2002/05/08 22:11:07
@@ -75,8 +75,8 @@
 const struct emul emul_irix_o32 = {
 	"irix o32",
 	"/emul/irix",
+	EMUL_TSLASHMK,
 #ifndef __HAVE_MINIMAL_EMUL
-	0,
 	native_to_irix_errno,
 	IRIX_SYS_syscall,
 	IRIX_SYS_MAXSYSCALL,
Index: compat/linux/common/linux_exec.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/linux/common/linux_exec.c,v
retrieving revision 1.57
diff -u -r1.57 linux_exec.c
--- linux_exec.c	2002/03/16 20:43:53	1.57
+++ linux_exec.c	2002/05/08 22:11:07
@@ -115,8 +115,8 @@
 const struct emul emul_linux = {
 	"linux",
 	"/emul/linux",
+	EMUL_TSLASHMK,
 #ifndef __HAVE_MINIMAL_EMUL
-	0,
 	(int*)native_to_linux_errno,
 	LINUX_SYS_syscall,
 	LINUX_SYS_MAXSYSCALL,
Index: compat/mach/mach_exec.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/mach/mach_exec.c,v
retrieving revision 1.5
diff -u -r1.5 mach_exec.c
--- mach_exec.c	2001/11/13 02:09:02	1.5
+++ mach_exec.c	2002/05/08 22:11:08
@@ -64,8 +64,8 @@
 const struct emul emul_mach = {
 	"mach",
 	"/emul/mach",
-#ifndef __HAVE_MINIMAL_EMUL
 	0,
+#ifndef __HAVE_MINIMAL_EMUL
 	0,
 	SYS_syscall,
 	SYS_MAXSYSCALL,
Index: compat/netbsd32/netbsd32_netbsd.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/netbsd32/netbsd32_netbsd.c,v
retrieving revision 1.63
diff -u -r1.63 netbsd32_netbsd.c
--- netbsd32_netbsd.c	2002/03/16 20:43:54	1.63
+++ netbsd32_netbsd.c	2002/05/08 22:11:13
@@ -107,8 +107,8 @@
 const struct emul emul_netbsd32 = {
 	"netbsd32",
 	"/emul/netbsd32",
-#ifndef __HAVE_MINIMAL_EMUL
 	0,
+#ifndef __HAVE_MINIMAL_EMUL
 	NULL,
 	netbsd32_SYS_syscall,
 	netbsd32_SYS_MAXSYSCALL,
Index: compat/osf1/osf1_exec.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/osf1/osf1_exec.c,v
retrieving revision 1.28
diff -u -r1.28 osf1_exec.c
--- osf1_exec.c	2001/11/13 02:09:12	1.28
+++ osf1_exec.c	2002/05/08 22:11:13
@@ -55,8 +55,8 @@
 const struct emul emul_osf1 = {
 	"osf1",
 	"/emul/osf1",
+	EMUL_TSLASHMK,
 #ifndef __HAVE_MINIMAL_EMUL
-	0,
 	(int *)osf1_errno_rxlist,
 	OSF1_SYS_syscall,
 	OSF1_SYS_MAXSYSCALL,
Index: compat/pecoff/pecoff_emul.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/pecoff/pecoff_emul.c,v
retrieving revision 1.1
diff -u -r1.1 pecoff_emul.c
--- pecoff_emul.c	2002/03/25 06:44:46	1.1
+++ pecoff_emul.c	2002/05/08 22:11:13
@@ -74,8 +74,8 @@
 const struct emul emul_pecoff = {
 	"pecoff",
 	"/emul/pecoff",
-#ifndef __HAVE_MINIMAL_EMUL
 	EMUL_HAS_SYS___syscall,
+#ifndef __HAVE_MINIMAL_EMUL
 	0,
 	PECOFF_SYS_syscall,
 	PECOFF_SYS_MAXSYSCALL,
Index: compat/svr4/svr4_exec.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/svr4/svr4_exec.c,v
retrieving revision 1.47
diff -u -r1.47 svr4_exec.c
--- svr4_exec.c	2001/11/13 02:09:21	1.47
+++ svr4_exec.c	2002/05/08 22:11:13
@@ -61,8 +61,8 @@
 const struct emul emul_svr4 = {
 	"svr4",
 	"/emul/svr4",
+	EMUL_TSLASHOK,
 #ifndef __HAVE_MINIMAL_EMUL
-	0,
 	native_to_svr4_errno,
 	SVR4_SYS_syscall,
 	SVR4_SYS_MAXSYSCALL,
Index: compat/svr4_32/svr4_32_exec.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/svr4_32/svr4_32_exec.c,v
retrieving revision 1.7
diff -u -r1.7 svr4_32_exec.c
--- svr4_32_exec.c	2001/11/13 02:09:27	1.7
+++ svr4_32_exec.c	2002/05/08 22:11:14
@@ -63,8 +63,8 @@
 const struct emul emul_svr4_32 = {
 	"svr4_32",
 	"/emul/svr4_32",
+	EMUL_TSLASHOK,
 #ifndef __HAVE_MINIMAL_EMUL
-	0,
 	native_to_svr4_errno,
 	SVR4_32_SYS_syscall,
 	SVR4_32_SYS_MAXSYSCALL,
Index: compat/ultrix/ultrix_misc.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/ultrix/ultrix_misc.c,v
retrieving revision 1.76
diff -u -r1.76 ultrix_misc.c
--- ultrix_misc.c	2002/03/16 23:55:57	1.76
+++ ultrix_misc.c	2002/05/08 22:11:15
@@ -163,8 +163,8 @@
 const struct emul emul_ultrix = {
 	"ultrix",
 	"/emul/ultrix",
-#ifndef __HAVE_MINIMAL_EMUL
 	0,
+#ifndef __HAVE_MINIMAL_EMUL
 	NULL,
 	ULTRIX_SYS_syscall,
 	ULTRIX_SYS_MAXSYSCALL,
Index: sys/namei.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/namei.h,v
retrieving revision 1.25
diff -u -r1.25 namei.h
--- namei.h	2001/12/08 04:10:00	1.25
+++ namei.h	2002/05/08 22:13:40
@@ -111,6 +111,7 @@
 #define	NOCACHE		0x0020	/* name must not be left in cache */
 #define	FOLLOW		0x0040	/* follow symbolic links */
 #define	NOFOLLOW	0x0000	/* do not follow symbolic links (pseudo) */
+#define	TSLASHOK	0x0080	/* trailing slash allowed on non-dir */
 #define	MODMASK		0x00fc	/* mask of operational modifiers */
 /*
  * Namei parameter descriptors.
Index: kern/vfs_syscalls.c
===================================================================
RCS file: /cvsroot/syssrc/sys/kern/vfs_syscalls.c,v
retrieving revision 1.173
diff -u -r1.173 vfs_syscalls.c
--- vfs_syscalls.c	2001/11/12 15:25:41	1.173
+++ vfs_syscalls.c	2002/05/08 22:23:12
@@ -2825,8 +2825,9 @@
 	if ((error = namei(&fromnd)) != 0)
 		return (error);
 	fvp = fromnd.ni_vp;
-	NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART,
-	    UIO_USERSPACE, to, p);
+	NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART |
+	    ((fvp->v_type == VDIR) && (p->p_emul->e_flags & EMUL_TSLASHMK) ?
+	    TSLASHOK : 0), UIO_USERSPACE, to, p);
 	if ((error = namei(&tond)) != 0) {
 		VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
 		vrele(fromnd.ni_dvp);
@@ -2913,7 +2914,14 @@
 	int error;
 	struct nameidata nd;

-	NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p);
+#ifdef DIAGNOSTIC
+	if (!p)
+		panic("mkdir: no proc!\n");
+#endif
+
+	NDINIT(&nd, CREATE, LOCKPARENT | (p->p_emul->e_flags &
+	    EMUL_TSLASHMK ? TSLASHOK : 0), UIO_USERSPACE, SCARG(uap,
+	    path), p);
 	if ((error = namei(&nd)) != 0)
 		return (error);
 	vp = nd.ni_vp;