From owner-p4-projects@FreeBSD.ORG Sun Jun 3 15:08:35 2007 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id B0AA616A46B; Sun, 3 Jun 2007 15:08:34 +0000 (UTC) X-Original-To: perforce@FreeBSD.org Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 6C4EC16A421 for ; Sun, 3 Jun 2007 15:08:34 +0000 (UTC) (envelope-from rdivacky@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [69.147.83.41]) by mx1.freebsd.org (Postfix) with ESMTP id 5C92813C448 for ; Sun, 3 Jun 2007 15:08:34 +0000 (UTC) (envelope-from rdivacky@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.13.8/8.13.8) with ESMTP id l53F8YLT064765 for ; Sun, 3 Jun 2007 15:08:34 GMT (envelope-from rdivacky@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.13.8/8.13.8/Submit) id l53F8Ypm064756 for perforce@freebsd.org; Sun, 3 Jun 2007 15:08:34 GMT (envelope-from rdivacky@FreeBSD.org) Date: Sun, 3 Jun 2007 15:08:34 GMT Message-Id: <200706031508.l53F8Ypm064756@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to rdivacky@FreeBSD.org using -f From: Roman Divacky To: Perforce Change Reviews Cc: Subject: PERFORCE change 120842 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 03 Jun 2007 15:08:35 -0000 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,