Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 3 Jun 2007 15:08:34 GMT
From:      Roman Divacky <rdivacky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 120842 for review
Message-ID:  <200706031508.l53F8Ypm064756@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=120842

Change 120842 by rdivacky@rdivacky_witten on 2007/06/03 15:07:41

	Implement linux_unlinkat(), linux_linkat() and linux_symlinkat().
	The corresponding kern_* functions for unlinkat and symlinkat are copied
	from *at-less versions because there is a loop.

Affected files ...

.. //depot/projects/soc2007/rdivacky/linux_at/sys/amd64/linux32/linux32_dummy.c#7 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/amd64/linux32/linux32_proto.h#6 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/amd64/linux32/linux32_syscall.h#6 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/amd64/linux32/linux32_sysent.c#6 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/amd64/linux32/syscalls.master#6 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/compat/linux/linux_file.c#8 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/i386/linux/linux_dummy.c#6 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/i386/linux/linux_proto.h#6 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/i386/linux/linux_syscall.h#6 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/i386/linux/linux_sysent.c#6 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/i386/linux/syscalls.master#6 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/kern/vfs_syscalls.c#14 edit
.. //depot/projects/soc2007/rdivacky/linux_at/sys/sys/syscallsubr.h#7 edit

Differences ...

==== //depot/projects/soc2007/rdivacky/linux_at/sys/amd64/linux32/linux32_dummy.c#7 (text+ko) ====

@@ -99,10 +99,7 @@
 DUMMY(mkdirat);
 DUMMY(mknodat);
 DUMMY(futimesat);
-DUMMY(unlinkat);
 DUMMY(renameat);
-DUMMY(linkat);
-DUMMY(symlinkat);
 DUMMY(pselect6);
 DUMMY(ppoll);
 DUMMY(unshare);

==== //depot/projects/soc2007/rdivacky/linux_at/sys/amd64/linux32/linux32_proto.h#6 (text+ko) ====

@@ -902,16 +902,24 @@
 	char flag_l_[PADL_(l_int)]; l_int flag; char flag_r_[PADR_(l_int)];
 };
 struct linux_unlinkat_args {
-	register_t dummy;
+	char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+	char pathname_l_[PADL_(char *)]; char * pathname; char pathname_r_[PADR_(char *)];
+	char flag_l_[PADL_(l_int)]; l_int flag; char flag_r_[PADR_(l_int)];
 };
 struct linux_renameat_args {
 	register_t dummy;
 };
 struct linux_linkat_args {
-	register_t dummy;
+	char olddfd_l_[PADL_(l_int)]; l_int olddfd; char olddfd_r_[PADR_(l_int)];
+	char oldname_l_[PADL_(char *)]; char * oldname; char oldname_r_[PADR_(char *)];
+	char newdfd_l_[PADL_(l_int)]; l_int newdfd; char newdfd_r_[PADR_(l_int)];
+	char newname_l_[PADL_(char *)]; char * newname; char newname_r_[PADR_(char *)];
+	char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
 };
 struct linux_symlinkat_args {
-	register_t dummy;
+	char oldname_l_[PADL_(char *)]; char * oldname; char oldname_r_[PADR_(char *)];
+	char newdfd_l_[PADL_(l_int)]; l_int newdfd; char newdfd_r_[PADR_(l_int)];
+	char newname_l_[PADL_(char *)]; char * newname; char newname_r_[PADR_(char *)];
 };
 struct linux_readlinkat_args {
 	char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];

==== //depot/projects/soc2007/rdivacky/linux_at/sys/amd64/linux32/linux32_syscall.h#6 (text+ko) ====


==== //depot/projects/soc2007/rdivacky/linux_at/sys/amd64/linux32/linux32_sysent.c#6 (text+ko) ====

@@ -321,10 +321,10 @@
 	{ AS(linux_fchownat_args), (sy_call_t *)linux_fchownat, AUE_NULL, NULL, 0, 0 },	/* 298 = linux_fchownat */
 	{ 0, (sy_call_t *)linux_futimesat, AUE_NULL, NULL, 0, 0 },	/* 299 = linux_futimesat */
 	{ AS(linux_fstatat64_args), (sy_call_t *)linux_fstatat64, AUE_NULL, NULL, 0, 0 },	/* 300 = linux_fstatat64 */
-	{ 0, (sy_call_t *)linux_unlinkat, AUE_NULL, NULL, 0, 0 },	/* 301 = linux_unlinkat */
+	{ AS(linux_unlinkat_args), (sy_call_t *)linux_unlinkat, AUE_NULL, NULL, 0, 0 },	/* 301 = linux_unlinkat */
 	{ 0, (sy_call_t *)linux_renameat, AUE_NULL, NULL, 0, 0 },	/* 302 = linux_renameat */
-	{ 0, (sy_call_t *)linux_linkat, AUE_NULL, NULL, 0, 0 },	/* 303 = linux_linkat */
-	{ 0, (sy_call_t *)linux_symlinkat, AUE_NULL, NULL, 0, 0 },	/* 304 = linux_symlinkat */
+	{ AS(linux_linkat_args), (sy_call_t *)linux_linkat, AUE_NULL, NULL, 0, 0 },	/* 303 = linux_linkat */
+	{ AS(linux_symlinkat_args), (sy_call_t *)linux_symlinkat, AUE_NULL, NULL, 0, 0 },	/* 304 = linux_symlinkat */
 	{ AS(linux_readlinkat_args), (sy_call_t *)linux_readlinkat, AUE_NULL, NULL, 0, 0 },	/* 305 = linux_readlinkat */
 	{ AS(linux_fchmodat_args), (sy_call_t *)linux_fchmodat, AUE_NULL, NULL, 0, 0 },	/* 306 = linux_fchmodat */
 	{ AS(linux_faccessat_args), (sy_call_t *)linux_faccessat, AUE_NULL, NULL, 0, 0 },	/* 307 = linux_faccessat */

==== //depot/projects/soc2007/rdivacky/linux_at/sys/amd64/linux32/syscalls.master#6 (text+ko) ====

@@ -467,18 +467,19 @@
 					l_int flags, l_int mode); }
 296	AUE_NULL	STD	{ int linux_mkdirat(void); }
 297	AUE_NULL	STD	{ int linux_mknodat(void); }
-298     AUE_NULL        STD     { int linux_fchownat(l_int dfd, char *filename, \
+298	AUE_NULL	STD	{ int linux_fchownat(l_int dfd, char *filename, \
 					l_uid16_t uid, l_gid16_t gid, l_int flag); }
 299	AUE_NULL	STD	{ int linux_futimesat(void); }
 300	AUE_NULL	STD	{ int linux_fstatat64(l_int dfd, char *pathname, \
 					struct l_stat64 *statbuf, l_int flag); }
-301	AUE_NULL	STD	{ int linux_unlinkat(void); }
+301	AUE_NULL	STD	{ int linux_unlinkat(l_int dfd, char *pathname, l_int flag); }
 302	AUE_NULL	STD	{ int linux_renameat(void); }
-303	AUE_NULL	STD	{ int linux_linkat(void); }
-304	AUE_NULL	STD	{ int linux_symlinkat(void); }
-305     AUE_NULL        STD     { int linux_readlinkat(l_int dfd, char *path, \
+303	AUE_NULL	STD	{ int linux_linkat(l_int olddfd, char *oldname, \
+					l_int newdfd, char *newname, l_int flags); }
+304	AUE_NULL	STD	{ int linux_symlinkat(char *oldname, l_int newdfd, char *newname); }
+305	AUE_NULL	STD	{ int linux_readlinkat(l_int dfd, char *path, \
 					char *buf, l_int bufsiz); }
-306     AUE_NULL        STD     { int linux_fchmodat(l_int dfd, char *filename, \
+306	AUE_NULL	STD	{ int linux_fchmodat(l_int dfd, char *filename, \
 					l_mode_t mode); }
 307	AUE_NULL	STD	{ int linux_faccessat(l_int dfd, char *filename, l_int mode); }
 308	AUE_NULL	STD	{ int linux_pselect6(void); }

==== //depot/projects/soc2007/rdivacky/linux_at/sys/compat/linux/linux_file.c#8 (text+ko) ====

@@ -630,6 +630,40 @@
 }
 
 int
+linux_unlinkat(struct thread *td, struct linux_unlinkat_args *args)
+{
+	char *path;
+	int error, dfd;
+	struct stat st;
+
+	if (args->flag & ~LINUX_AT_REMOVEDIR)
+		return (EINVAL);
+
+	LCONVPATHEXIST(td, args->pathname, &path);
+
+#ifdef DEBUG
+	if (ldebug(unlinkat))
+		printf(ARGS(unlinkat, "%s"), path);
+#endif
+
+	if (args->dfd == LINUX_AT_FDCWD)
+		dfd = AT_FDCWD;
+	else
+		dfd = args->dfd;
+
+	if (args->flag & LINUX_AT_REMOVEDIR)
+		error = kern_rmdirat(td, path, UIO_SYSSPACE, dfd);
+	else
+		error = kern_unlinkat(td, path, UIO_SYSSPACE, dfd);
+	if (error == EPERM)
+		/* Introduce POSIX noncompliant behaviour of Linux */
+		if (kern_stat(td, path, UIO_SYSSPACE, &st) == 0)
+			if (S_ISDIR(st.st_mode))
+				error = EISDIR;
+	LFREEPATH(path);
+	return (error);
+}
+int
 linux_chdir(struct thread *td, struct linux_chdir_args *args)
 {
 	char *path;
@@ -673,7 +707,7 @@
 
 #ifdef DEBUG
 	if (ldebug(fchmodat))
-		printf(ARGS(fchmodat, "%s, %d, %d"), path, args->uid, args->gid);
+		printf(ARGS(fchmodat, "%s, %d"), path, args->mode);
 #endif
 	
 	if (args->dfd == LINUX_AT_FDCWD)
@@ -768,6 +802,35 @@
 }
 
 int
+linux_symlinkat(struct thread *td, struct linux_symlinkat_args *args)
+{
+	char *path, *to;
+	int error, dfd;
+
+	LCONVPATHEXIST(td, args->oldname, &path);
+	/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
+	error = linux_emul_convpath(td, args->newname, UIO_USERSPACE, &to, 1);
+	if (to == NULL) {
+		LFREEPATH(path);
+		return (error);
+	}
+
+#ifdef DEBUG
+	if (ldebug(symlinkat))
+		printf(ARGS(symlinkat, "%s, %s"), path, to);
+#endif
+	if (args->newdfd == LINUX_AT_FDCWD)
+		dfd = AT_FDCWD;
+	else
+		dfd = args->newdfd;
+
+	error = kern_symlinkat(td, path, to, UIO_SYSSPACE, dfd);
+	LFREEPATH(path);
+	LFREEPATH(to);
+	return (error);
+}
+
+int
 linux_readlink(struct thread *td, struct linux_readlink_args *args)
 {
 	char *name;
@@ -868,6 +931,48 @@
 }
 
 int
+linux_linkat(struct thread *td, struct linux_linkat_args *args)
+{
+	char *path, *to;
+	int error, olddfd, newdfd;
+
+	/* 
+	 * don't laugh they really introduced flags argument
+	 * which is forbidden to use ;)
+	 */
+	if (args->flags != 0)
+		return (EINVAL);
+
+	LCONVPATHEXIST(td, args->oldname, &path);
+	/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
+	error = linux_emul_convpath(td, args->newname, UIO_USERSPACE, &to, 1);
+	if (to == NULL) {
+		LFREEPATH(path);
+		return (error);
+	}
+
+#ifdef DEBUG
+	if (ldebug(linkat))
+		printf(ARGS(linkat, "%i, %s, %i, %s, %i"), args->olddfd, path,
+			args->newdfd, to, args->flags);
+#endif
+	if (args->olddfd == LINUX_AT_FDCWD)
+		olddfd = AT_FDCWD;
+	else
+		olddfd = args->olddfd;
+
+	if (args->newdfd == LINUX_AT_FDCWD)
+		newdfd = AT_FDCWD;
+	else
+		newdfd = args->newdfd;
+
+	error = kern_linkat(td, path, to, UIO_SYSSPACE, olddfd, newdfd);
+	LFREEPATH(path);
+	LFREEPATH(to);
+	return (error);
+}
+
+int
 linux_fdatasync(td, uap)
 	struct thread *td;
 	struct linux_fdatasync_args *uap;

==== //depot/projects/soc2007/rdivacky/linux_at/sys/i386/linux/linux_dummy.c#6 (text+ko) ====

@@ -90,10 +90,7 @@
 DUMMY(mkdirat);
 DUMMY(mknodat);
 DUMMY(futimesat);
-DUMMY(unlinkat);
 DUMMY(renameat);
-DUMMY(linkat);
-DUMMY(symlinkat);
 DUMMY(pselect6);
 DUMMY(ppoll);
 DUMMY(unshare);

==== //depot/projects/soc2007/rdivacky/linux_at/sys/i386/linux/linux_proto.h#6 (text+ko) ====

@@ -921,16 +921,24 @@
 	char flag_l_[PADL_(l_int)]; l_int flag; char flag_r_[PADR_(l_int)];
 };
 struct linux_unlinkat_args {
-	register_t dummy;
+	char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+	char pathname_l_[PADL_(char *)]; char * pathname; char pathname_r_[PADR_(char *)];
+	char flag_l_[PADL_(l_int)]; l_int flag; char flag_r_[PADR_(l_int)];
 };
 struct linux_renameat_args {
 	register_t dummy;
 };
 struct linux_linkat_args {
-	register_t dummy;
+	char olddfd_l_[PADL_(l_int)]; l_int olddfd; char olddfd_r_[PADR_(l_int)];
+	char oldname_l_[PADL_(char *)]; char * oldname; char oldname_r_[PADR_(char *)];
+	char newdfd_l_[PADL_(l_int)]; l_int newdfd; char newdfd_r_[PADR_(l_int)];
+	char newname_l_[PADL_(char *)]; char * newname; char newname_r_[PADR_(char *)];
+	char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
 };
 struct linux_symlinkat_args {
-	register_t dummy;
+	char oldname_l_[PADL_(char *)]; char * oldname; char oldname_r_[PADR_(char *)];
+	char newdfd_l_[PADL_(l_int)]; l_int newdfd; char newdfd_r_[PADR_(l_int)];
+	char newname_l_[PADL_(char *)]; char * newname; char newname_r_[PADR_(char *)];
 };
 struct linux_readlinkat_args {
 	char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];

==== //depot/projects/soc2007/rdivacky/linux_at/sys/i386/linux/linux_syscall.h#6 (text+ko) ====


==== //depot/projects/soc2007/rdivacky/linux_at/sys/i386/linux/linux_sysent.c#6 (text+ko) ====

@@ -320,10 +320,10 @@
 	{ AS(linux_fchownat_args), (sy_call_t *)linux_fchownat, AUE_NULL, NULL, 0, 0 },	/* 298 = linux_fchownat */
 	{ 0, (sy_call_t *)linux_futimesat, AUE_NULL, NULL, 0, 0 },	/* 299 = linux_futimesat */
 	{ AS(linux_fstatat64_args), (sy_call_t *)linux_fstatat64, AUE_NULL, NULL, 0, 0 },	/* 300 = linux_fstatat64 */
-	{ 0, (sy_call_t *)linux_unlinkat, AUE_NULL, NULL, 0, 0 },	/* 301 = linux_unlinkat */
+	{ AS(linux_unlinkat_args), (sy_call_t *)linux_unlinkat, AUE_NULL, NULL, 0, 0 },	/* 301 = linux_unlinkat */
 	{ 0, (sy_call_t *)linux_renameat, AUE_NULL, NULL, 0, 0 },	/* 302 = linux_renameat */
-	{ 0, (sy_call_t *)linux_linkat, AUE_NULL, NULL, 0, 0 },	/* 303 = linux_linkat */
-	{ 0, (sy_call_t *)linux_symlinkat, AUE_NULL, NULL, 0, 0 },	/* 304 = linux_symlinkat */
+	{ AS(linux_linkat_args), (sy_call_t *)linux_linkat, AUE_NULL, NULL, 0, 0 },	/* 303 = linux_linkat */
+	{ AS(linux_symlinkat_args), (sy_call_t *)linux_symlinkat, AUE_NULL, NULL, 0, 0 },	/* 304 = linux_symlinkat */
 	{ AS(linux_readlinkat_args), (sy_call_t *)linux_readlinkat, AUE_NULL, NULL, 0, 0 },	/* 305 = linux_readlinkat */
 	{ AS(linux_fchmodat_args), (sy_call_t *)linux_fchmodat, AUE_NULL, NULL, 0, 0 },	/* 306 = linux_fchmodat */
 	{ AS(linux_faccessat_args), (sy_call_t *)linux_faccessat, AUE_NULL, NULL, 0, 0 },	/* 307 = linux_faccessat */

==== //depot/projects/soc2007/rdivacky/linux_at/sys/i386/linux/syscalls.master#6 (text+ko) ====

@@ -482,10 +482,11 @@
 299	AUE_NULL	STD	{ int linux_futimesat(void); }
 300	AUE_NULL	STD	{ int linux_fstatat64(l_int dfd, char *pathname, \
 					struct l_stat64 *statbuf, l_int flag); }
-301	AUE_NULL	STD	{ int linux_unlinkat(void); }
+301	AUE_NULL	STD	{ int linux_unlinkat(l_int dfd, char *pathname, l_int flag); }
 302	AUE_NULL	STD	{ int linux_renameat(void); }
-303	AUE_NULL	STD	{ int linux_linkat(void); }
-304	AUE_NULL	STD	{ int linux_symlinkat(void); }
+303	AUE_NULL	STD	{ int linux_linkat(l_int olddfd, char *oldname, \
+					l_int newdfd, char *newname, l_int flags); }
+304	AUE_NULL	STD	{ int linux_symlinkat(char *oldname, l_int newdfd, char *newname); }
 305	AUE_NULL	STD	{ int linux_readlinkat(l_int dfd, char *path, \
 					char *buf, l_int bufsiz); }
 306	AUE_NULL	STD	{ int linux_fchmodat(l_int dfd, char *filename, \

==== //depot/projects/soc2007/rdivacky/linux_at/sys/kern/vfs_syscalls.c#14 (text+ko) ====

@@ -102,6 +102,8 @@
 static int kern_common_chmod(struct thread *td, int mode, struct nameidata *nd);
 static int kern_common_readlink(struct thread *td, char *buf,
     enum uio_seg bufseg, int count, struct nameidata *nd);
+static int kern_common_link(struct thread *td, struct nameidata *ndp,
+    struct nameidata *ndl);
 
 /*
  * The module initialization routine for POSIX asynchronous I/O will
@@ -1442,20 +1444,82 @@
 int
 kern_link(struct thread *td, char *path, char *link, enum uio_seg segflg)
 {
+	struct nameidata ndp, ndl;
+
+	bwillwrite();
+	NDINIT(&ndp, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1, segflg, path, td);
+	NDINIT(&ndl, CREATE, LOCKPARENT | SAVENAME | MPSAFE | AUDITVNODE2,
+	    segflg, link, td);
+
+	return kern_common_link(td, &ndp, &ndl);
+}
+
+int
+kern_linkat(struct thread *td, char *path, char *link, enum uio_seg segflg,
+    int olddirfd, int newdirfd)
+{
+	struct nameidata ndp, ndl;
+	int error;
+	struct vnode *pdir_vn, *ldir_vn;
+
+	if (olddirfd == AT_FDCWD)
+		pdir_vn = NULL;
+	else {
+		error = fgetvp(td, olddirfd, &pdir_vn);
+		if (error)
+			return (error);
+		if (pdir_vn->v_type != VDIR) {
+			vrele(pdir_vn);
+			return (ENOTDIR);
+		}
+	}
+
+	NDINIT_AT(&ndp, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1, segflg, path, td, pdir_vn);
+
+	if (newdirfd == AT_FDCWD)
+		ldir_vn = NULL;
+	else {
+		error = fgetvp(td, newdirfd, &ldir_vn);
+		if (error)
+			return (error);
+		if (ldir_vn->v_type != VDIR) {
+			vrele(ldir_vn);
+			return (ENOTDIR);
+		}
+	}
+
+	NDINIT_AT(&ndl, CREATE, LOCKPARENT | SAVENAME| MPSAFE | AUDITVNODE1, segflg,
+		link, td, ldir_vn);
+
+	error = kern_common_link(td, &ndp, &ndl);
+	if (olddirfd != AT_FDCWD)
+		vrele(pdir_vn);
+	if (newdirfd != AT_FDCWD)
+		vrele(ldir_vn);
+	return (error);
+
+	NDINIT(&ndp, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1, segflg, path, td);
+	NDINIT(&ndl, CREATE, LOCKPARENT | SAVENAME | MPSAFE | AUDITVNODE2,
+	    segflg, link, td);
+
+	return kern_common_link(td, &ndp, &ndl);
+}
+
+static int
+kern_common_link(struct thread *td, struct nameidata *ndp, struct nameidata *ndl)
+{
 	struct vnode *vp;
 	struct mount *mp;
-	struct nameidata nd;
 	int vfslocked;
 	int lvfslocked;
 	int error;
 
 	bwillwrite();
-	NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1, segflg, path, td);
-	if ((error = namei(&nd)) != 0)
+	if ((error = namei(ndp)) != 0)
 		return (error);
-	vfslocked = NDHASGIANT(&nd);
-	NDFREE(&nd, NDF_ONLY_PNBUF);
-	vp = nd.ni_vp;
+	vfslocked = NDHASGIANT(ndp);
+	NDFREE(ndp, NDF_ONLY_PNBUF);
+	vp = ndp->ni_vp;
 	if (vp->v_type == VDIR) {
 		vrele(vp);
 		VFS_UNLOCK_GIANT(vfslocked);
@@ -1466,33 +1530,31 @@
 		VFS_UNLOCK_GIANT(vfslocked);
 		return (error);
 	}
-	NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME | MPSAFE | AUDITVNODE2,
-	    segflg, link, td);
-	if ((error = namei(&nd)) == 0) {
-		lvfslocked = NDHASGIANT(&nd);
-		if (nd.ni_vp != NULL) {
-			if (nd.ni_dvp == nd.ni_vp)
-				vrele(nd.ni_dvp);
+	if ((error = namei(ndl)) == 0) {
+		lvfslocked = NDHASGIANT(ndl);
+		if (ndl->ni_vp != NULL) {
+			if (ndl->ni_dvp == ndl->ni_vp)
+				vrele(ndl->ni_dvp);
 			else
-				vput(nd.ni_dvp);
-			vrele(nd.ni_vp);
+				vput(ndl->ni_dvp);
+			vrele(ndl->ni_vp);
 			error = EEXIST;
 		} else if ((error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td))
 		    == 0) {
-			VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE);
+			VOP_LEASE(ndl->ni_dvp, td, td->td_ucred, LEASE_WRITE);
 			VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE);
 			error = can_hardlink(vp, td, td->td_ucred);
 			if (error == 0)
 #ifdef MAC
 				error = mac_check_vnode_link(td->td_ucred,
-				    nd.ni_dvp, vp, &nd.ni_cnd);
+				    ndl->ni_dvp, vp, &(ndl->ni_cnd));
 			if (error == 0)
 #endif
-				error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
+				error = VOP_LINK(ndl->ni_dvp, vp, &(ndl->ni_cnd));
 			VOP_UNLOCK(vp, 0, td);
-			vput(nd.ni_dvp);
+			vput(ndl->ni_dvp);
 		}
-		NDFREE(&nd, NDF_ONLY_PNBUF);
+		NDFREE(ndl, NDF_ONLY_PNBUF);
 		VFS_UNLOCK_GIANT(lvfslocked);
 	}
 	vrele(vp);
@@ -1594,6 +1656,91 @@
 	return (error);
 }
 
+int
+kern_symlinkat(struct thread *td, char *path, char *link, enum uio_seg segflg,
+    int dirfd)
+{
+	struct mount *mp;
+	struct vattr vattr;
+	char *syspath;
+	int error;
+	struct nameidata nd;
+	int vfslocked;
+	struct vnode *dir_vn;
+
+	if (segflg == UIO_SYSSPACE) {
+		syspath = path;
+	} else {
+		syspath = uma_zalloc(namei_zone, M_WAITOK);
+		if ((error = copyinstr(path, syspath, MAXPATHLEN, NULL)) != 0)
+			goto out;
+	}
+	AUDIT_ARG(text, syspath);
+restart:
+	if (dirfd == AT_FDCWD)
+		dir_vn = NULL;
+	else {
+		error = fgetvp(td, dirfd, &dir_vn);
+		if (error)
+			return (error);
+		if (dir_vn->v_type != VDIR) {
+			vrele(dir_vn);
+			return (ENOTDIR);
+		}
+	}
+	bwillwrite();
+	NDINIT_AT(&nd, CREATE, LOCKPARENT | SAVENAME | MPSAFE | AUDITVNODE1,
+	    segflg, link, td, dir_vn);
+	if ((error = namei(&nd)) != 0)
+		goto out;
+	vfslocked = NDHASGIANT(&nd);
+	if (nd.ni_vp) {
+		NDFREE(&nd, NDF_ONLY_PNBUF);
+		if (nd.ni_vp == nd.ni_dvp)
+			vrele(nd.ni_dvp);
+		else
+			vput(nd.ni_dvp);
+		vrele(nd.ni_vp);
+		VFS_UNLOCK_GIANT(vfslocked);
+		error = EEXIST;
+		goto out;
+	}
+	if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) {
+		NDFREE(&nd, NDF_ONLY_PNBUF);
+		vput(nd.ni_dvp);
+		VFS_UNLOCK_GIANT(vfslocked);
+		if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0)
+			goto out;
+		goto restart;
+	}
+	VATTR_NULL(&vattr);
+	FILEDESC_SLOCK(td->td_proc->p_fd);
+	vattr.va_mode = ACCESSPERMS &~ td->td_proc->p_fd->fd_cmask;
+	FILEDESC_SUNLOCK(td->td_proc->p_fd);
+#ifdef MAC
+	vattr.va_type = VLNK;
+	error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd,
+	    &vattr);
+	if (error)
+		goto out2;
+#endif
+	VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE);
+	error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, syspath);
+	if (error == 0)
+		vput(nd.ni_vp);
+#ifdef MAC
+out2:
+#endif
+	NDFREE(&nd, NDF_ONLY_PNBUF);
+	vput(nd.ni_dvp);
+	vn_finished_write(mp);
+	VFS_UNLOCK_GIANT(vfslocked);
+out:
+	if (segflg != UIO_SYSSPACE)
+		uma_zfree(namei_zone, syspath);
+	return (error);
+}
+
 /*
  * Delete a whiteout from the filesystem.
  */
@@ -1732,6 +1879,82 @@
 	return (error);
 }
 
+int
+kern_unlinkat(struct thread *td, char *path, enum uio_seg pathseg, int dirfd)
+{
+	struct mount *mp;
+	struct vnode *vp, *dir_vn;
+	int error;
+	struct nameidata nd;
+	int vfslocked;
+	
+restart:
+	if (dirfd == AT_FDCWD)
+		dir_vn = NULL;
+	else {
+		error = fgetvp(td, dirfd, &dir_vn);
+		if (error)
+			return (error);
+		if (dir_vn->v_type != VDIR) {
+			vrele(dir_vn);
+			return (ENOTDIR);
+		}
+	}
+	bwillwrite();
+	NDINIT_AT(&nd, DELETE, LOCKPARENT | LOCKLEAF | MPSAFE | AUDITVNODE1,
+	    pathseg, path, td, dir_vn);
+	if ((error = namei(&nd)) != 0)
+		return (error == EINVAL ? EPERM : error);
+	vfslocked = NDHASGIANT(&nd);
+	vp = nd.ni_vp;
+	if (vp->v_type == VDIR)
+		error = EPERM;		/* POSIX */
+	else {
+		/*
+		 * The root of a mounted filesystem cannot be deleted.
+		 *
+		 * XXX: can this only be a VDIR case?
+		 */
+		if (vp->v_vflag & VV_ROOT)
+			error = EBUSY;
+	}
+	if (error == 0) {
+		if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) {
+			NDFREE(&nd, NDF_ONLY_PNBUF);
+			vput(nd.ni_dvp);
+			if (vp == nd.ni_dvp)
+				vrele(vp);
+			else
+				vput(vp);
+			VFS_UNLOCK_GIANT(vfslocked);
+			if ((error = vn_start_write(NULL, &mp,
+			    V_XSLEEP | PCATCH)) != 0)
+				return (error);
+			goto restart;
+		}
+#ifdef MAC
+		error = mac_check_vnode_delete(td->td_ucred, nd.ni_dvp, vp,
+		    &nd.ni_cnd);
+		if (error)
+			goto out;
+#endif
+		VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE);
+		error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd);
+#ifdef MAC
+out:
+#endif
+		vn_finished_write(mp);
+	}
+	NDFREE(&nd, NDF_ONLY_PNBUF);
+	vput(nd.ni_dvp);
+	if (vp == nd.ni_dvp)
+		vrele(vp);
+	else
+		vput(vp);
+	VFS_UNLOCK_GIANT(vfslocked);
+	return (error);
+}
+
 /*
  * Reposition read/write file offset.
  */
@@ -2660,6 +2883,7 @@
 }
 
 /*
+ *
  * Common implementation code for chmod(), lchmod() and fchmod().
  */
 static int
@@ -3819,6 +4043,85 @@
 	return (error);
 }
 
+int
+kern_rmdirat(struct thread *td, char *path, enum uio_seg pathseg, int dirfd)
+{
+	struct mount *mp;
+	struct vnode *vp, *dir_vn;
+	int error;
+	struct nameidata nd;
+	int vfslocked;
+
+restart:
+	if (dirfd == AT_FDCWD)
+		dir_vn = NULL;
+	else {
+		error = fgetvp(td, dirfd, &dir_vn);
+		if (error)
+			return (error);
+		if (dir_vn->v_type != VDIR) {
+			vrele(dir_vn);
+			return (ENOTDIR);
+		}
+	}
+	bwillwrite();
+	NDINIT_AT(&nd, DELETE, LOCKPARENT | LOCKLEAF | MPSAFE | AUDITVNODE1,
+	    pathseg, path, td, dir_vn);
+	if ((error = namei(&nd)) != 0)
+		return (error);
+	vfslocked = NDHASGIANT(&nd);
+	vp = nd.ni_vp;
+	if (vp->v_type != VDIR) {
+		error = ENOTDIR;
+		goto out;
+	}
+	/*
+	 * No rmdir "." please.
+	 */
+	if (nd.ni_dvp == vp) {
+		error = EINVAL;
+		goto out;
+	}
+	/*
+	 * The root of a mounted filesystem cannot be deleted.
+	 */
+	if (vp->v_vflag & VV_ROOT) {
+		error = EBUSY;
+		goto out;
+	}
+#ifdef MAC
+	error = mac_check_vnode_delete(td->td_ucred, nd.ni_dvp, vp,
+	    &nd.ni_cnd);
+	if (error)
+		goto out;
+#endif
+	if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) {
+		NDFREE(&nd, NDF_ONLY_PNBUF);
+		vput(vp);
+		if (nd.ni_dvp == vp)
+			vrele(nd.ni_dvp);
+		else
+			vput(nd.ni_dvp);
+		VFS_UNLOCK_GIANT(vfslocked);
+		if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0)
+			return (error);
+		goto restart;
+	}
+	VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE);
+	VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE);
+	error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+	vn_finished_write(mp);
+out:
+	NDFREE(&nd, NDF_ONLY_PNBUF);
+	vput(vp);
+	if (nd.ni_dvp == vp)
+		vrele(nd.ni_dvp);
+	else
+		vput(nd.ni_dvp);
+	VFS_UNLOCK_GIANT(vfslocked);
+	return (error);
+}
+
 #ifdef COMPAT_43
 /*
  * Read a block of directory entries in a filesystem independent format.

==== //depot/projects/soc2007/rdivacky/linux_at/sys/sys/syscallsubr.h#7 (text+ko) ====

@@ -111,6 +111,8 @@
 	    int uid, int gid, int dirfd);
 int	kern_link(struct thread *td, char *path, char *link,
 	    enum uio_seg segflg);
+int	kern_linkat(struct thread *td, char *path, char *link,
+	    enum uio_seg segflg, int olddfd, int newdfd);
 int	kern_lstat(struct thread *td, char *path, enum uio_seg pathseg,
 	    struct stat *sbp);
 int	kern_lstatat(struct thread *td, char *path, enum uio_seg pathseg,
@@ -148,6 +150,7 @@
 int	kern_rename(struct thread *td, char *from, char *to,
 	    enum uio_seg pathseg);
 int	kern_rmdir(struct thread *td, char *path, enum uio_seg pathseg);
+int	kern_rmdirat(struct thread *td, char *path, enum uio_seg pathseg, int dirfd);
 int	kern_sched_rr_get_interval(struct thread *td, pid_t pid,
 	    struct timespec *ts);
 int	kern_semctl(struct thread *td, int semid, int semnum, int cmd,
@@ -184,9 +187,12 @@
 	    struct statfs *buf);
 int	kern_symlink(struct thread *td, char *path, char *link,
 	    enum uio_seg segflg);
+int	kern_symlinkat(struct thread *td, char *path, char *link,
+	    enum uio_seg segflg, int dirfd);
 int	kern_truncate(struct thread *td, char *path, enum uio_seg pathseg,
 	    off_t length);
 int	kern_unlink(struct thread *td, char *path, enum uio_seg pathseg);
+int	kern_unlinkat(struct thread *td, char *path, enum uio_seg pathseg, int dirfd);
 int	kern_utimes(struct thread *td, char *path, enum uio_seg pathseg,
 	    struct timeval *tptr, enum uio_seg tptrseg);
 int	kern_wait(struct thread *td, pid_t pid, int *status, int options,



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200706031508.l53F8Ypm064756>