summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2008-04-08 09:45:49 +0000
committerkib <kib@FreeBSD.org>2008-04-08 09:45:49 +0000
commiteb77b477b46f73f94adb6a57311b621ff373ec0d (patch)
tree27aa9d496f901903e5e31001326287aab78c768b /sys
parentc2a6bed59d13cefde1d4e2483c484d2ff7f534dd (diff)
downloadFreeBSD-src-eb77b477b46f73f94adb6a57311b621ff373ec0d.zip
FreeBSD-src-eb77b477b46f73f94adb6a57311b621ff373ec0d.tar.gz
Implement the linux syscalls
openat, mkdirat, mknodat, fchownat, futimesat, fstatat, unlinkat, renameat, linkat, symlinkat, readlinkat, fchmodat, faccessat. Submitted by: rdivacky Sponsored by: Google Summer of Code 2007 Tested by: pho
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/linux32/linux.h2
-rw-r--r--sys/amd64/linux32/linux32_dummy.c12
-rw-r--r--sys/amd64/linux32/linux32_sysvec.c3
-rw-r--r--sys/amd64/linux32/syscalls.master37
-rw-r--r--sys/compat/linux/linux_file.c358
-rw-r--r--sys/compat/linux/linux_file.h36
-rw-r--r--sys/compat/linux/linux_misc.c84
-rw-r--r--sys/compat/linux/linux_stats.c43
-rw-r--r--sys/compat/linux/linux_util.c7
-rw-r--r--sys/compat/linux/linux_util.h12
-rw-r--r--sys/compat/svr4/svr4_sysvec.c2
-rw-r--r--sys/i386/ibcs2/ibcs2_util.c2
-rw-r--r--sys/i386/linux/linux.h2
-rw-r--r--sys/i386/linux/linux_dummy.c12
-rw-r--r--sys/i386/linux/linux_sysvec.c2
-rw-r--r--sys/i386/linux/syscalls.master37
-rw-r--r--sys/kern/vfs_lookup.c13
-rw-r--r--sys/sys/syscallsubr.h4
18 files changed, 483 insertions, 185 deletions
diff --git a/sys/amd64/linux32/linux.h b/sys/amd64/linux32/linux.h
index a904afc..918f7ef 100644
--- a/sys/amd64/linux32/linux.h
+++ b/sys/amd64/linux32/linux.h
@@ -579,8 +579,6 @@ int linux_ioctl_unregister_handler(struct linux_ioctl_handler *h);
#define LINUX_F_WRLCK 1
#define LINUX_F_UNLCK 2
-#define LINUX_AT_FDCWD -100
-
/*
* mount flags
*/
diff --git a/sys/amd64/linux32/linux32_dummy.c b/sys/amd64/linux32/linux32_dummy.c
index 43d4573..6573668 100644
--- a/sys/amd64/linux32/linux32_dummy.c
+++ b/sys/amd64/linux32/linux32_dummy.c
@@ -96,18 +96,6 @@ DUMMY(inotify_init);
DUMMY(inotify_add_watch);
DUMMY(inotify_rm_watch);
DUMMY(migrate_pages);
-DUMMY(mkdirat);
-DUMMY(mknodat);
-DUMMY(fchownat);
-DUMMY(futimesat);
-DUMMY(fstatat64);
-DUMMY(unlinkat);
-DUMMY(renameat);
-DUMMY(linkat);
-DUMMY(symlinkat);
-DUMMY(readlinkat);
-DUMMY(fchmodat);
-DUMMY(faccessat);
DUMMY(pselect6);
DUMMY(ppoll);
DUMMY(unshare);
diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c
index 9259092..3848fb0 100644
--- a/sys/amd64/linux32/linux32_sysvec.c
+++ b/sys/amd64/linux32/linux32_sysvec.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/exec.h>
+#include <sys/fcntl.h>
#include <sys/imgact.h>
#include <sys/imgact_elf.h>
#include <sys/kernel.h>
@@ -788,7 +789,7 @@ exec_linux_imgact_try(struct image_params *imgp)
*/
if ((error = exec_shell_imgact(imgp)) == 0) {
linux_emul_convpath(FIRST_THREAD_IN_PROC(imgp->proc),
- imgp->interpreter_name, UIO_SYSSPACE, &rpath, 0);
+ imgp->interpreter_name, UIO_SYSSPACE, &rpath, 0, AT_FDCWD);
if (rpath != NULL) {
len = strlen(rpath) + 1;
diff --git a/sys/amd64/linux32/syscalls.master b/sys/amd64/linux32/syscalls.master
index 86350c9..29a15e9 100644
--- a/sys/amd64/linux32/syscalls.master
+++ b/sys/amd64/linux32/syscalls.master
@@ -465,20 +465,31 @@
292 AUE_NULL STD { int linux_inotify_add_watch(void); }
293 AUE_NULL STD { int linux_inotify_rm_watch(void); }
294 AUE_NULL STD { int linux_migrate_pages(void); }
-295 AUE_OPEN_RWTC STD { int linux_openat(l_int dfd, char *filename, \
+295 AUE_OPEN_RWTC STD { int linux_openat(l_int dfd, const char *filename, \
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(void); }
-299 AUE_NULL STD { int linux_futimesat(void); }
-300 AUE_NULL STD { int linux_fstatat64(void); }
-301 AUE_NULL STD { int linux_unlinkat(void); }
-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(void); }
-306 AUE_NULL STD { int linux_fchmodat(void); }
-307 AUE_NULL STD { int linux_faccessat(void); }
+296 AUE_MKDIRAT STD { int linux_mkdirat(l_int dfd, const char *pathname, \
+ l_int mode); }
+297 AUE_MKNODAT STD { int linux_mknodat(l_int dfd, const char *filename, \
+ l_int mode, l_uint dev); }
+298 AUE_FCHOWNAT STD { int linux_fchownat(l_int dfd, const char *filename, \
+ l_uid16_t uid, l_gid16_t gid, l_int flag); }
+299 AUE_FUTIMESAT STD { int linux_futimesat(l_int dfd, char *filename, \
+ struct l_timeval *utimes); }
+300 AUE_FSTATAT STD { int linux_fstatat64(l_int dfd, char *pathname, \
+ struct l_stat64 *statbuf, l_int flag); }
+301 AUE_UNLINKAT STD { int linux_unlinkat(l_int dfd, const char *pathname, \
+ l_int flag); }
+302 AUE_RENAMEAT STD { int linux_renameat(l_int olddfd, const char *oldname, \
+ l_int newdfd, const char *newname); }
+303 AUE_LINKAT STD { int linux_linkat(l_int olddfd, const char *oldname, \
+ l_int newdfd, const char *newname, l_int flags); }
+304 AUE_SYMLINKAT STD { int linux_symlinkat(const char *oldname, l_int newdfd, \
+ const char *newname); }
+305 AUE_READLINKAT STD { int linux_readlinkat(l_int dfd, const char *path, \
+ char *buf, l_int bufsiz); }
+306 AUE_FCHMODAT STD { int linux_fchmodat(l_int dfd, const char *filename, \
+ l_mode_t mode); }
+307 AUE_FACCESSAT STD { int linux_faccessat(l_int dfd, const char *filename, l_int mode); }
308 AUE_NULL STD { int linux_pselect6(void); }
309 AUE_NULL STD { int linux_ppoll(void); }
310 AUE_NULL STD { int linux_unshare(void); }
diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c
index 85a0cca..646f6eb 100644
--- a/sys/compat/linux/linux_file.c
+++ b/sys/compat/linux/linux_file.c
@@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$");
#include <machine/../linux/linux_proto.h>
#endif
#include <compat/linux/linux_util.h>
+#include <compat/linux/linux_file.h>
int
linux_creat(struct thread *td, struct linux_creat_args *args)
@@ -88,7 +89,7 @@ linux_creat(struct thread *td, struct linux_creat_args *args)
static int
-linux_common_open(struct thread *td, char *path, int l_flags, int mode, int openat)
+linux_common_open(struct thread *td, int dirfd, char *path, int l_flags, int mode)
{
struct proc *p = td->td_proc;
struct file *fp;
@@ -130,7 +131,10 @@ linux_common_open(struct thread *td, char *path, int l_flags, int mode, int open
bsd_flags |= O_NOFOLLOW;
/* XXX LINUX_O_NOATIME: unable to be easily implemented. */
- error = kern_open(td, path, UIO_SYSSPACE, bsd_flags, mode);
+ if (dirfd != -1)
+ error = kern_openat(td, dirfd, path, UIO_SYSSPACE, bsd_flags, mode);
+ else
+ error = kern_open(td, path, UIO_SYSSPACE, bsd_flags, mode);
if (!error) {
fd = td->td_retval[0];
/*
@@ -172,122 +176,27 @@ linux_common_open(struct thread *td, char *path, int l_flags, int mode, int open
if (ldebug(open))
printf(LMSG("open returns error %d"), error);
#endif
- if (!openat)
- LFREEPATH(path);
- return error;
-}
-
-/*
- * common code for linux *at set of syscalls
- *
- * works like this:
- * if filename is absolute
- * ignore dirfd
- * else
- * if dirfd == AT_FDCWD
- * return CWD/filename
- * else
- * return DIRFD/filename
- */
-static int
-linux_at(struct thread *td, int dirfd, char *filename, char **newpath, char **freebuf)
-{
- struct file *fp;
- int error = 0, vfslocked;
- struct vnode *dvp;
- struct filedesc *fdp = td->td_proc->p_fd;
- char *fullpath = "unknown";
- char *freepath = NULL;
-
- /* don't do anything if the pathname is absolute */
- if (*filename == '/') {
- *newpath= filename;
- return (0);
- }
-
- /* check for AT_FDWCD */
- if (dirfd == LINUX_AT_FDCWD) {
- FILEDESC_SLOCK(fdp);
- dvp = fdp->fd_cdir;
- vref(dvp);
- FILEDESC_SUNLOCK(fdp);
- } else {
- error = fget(td, dirfd, &fp);
- if (error)
- return (error);
- dvp = fp->f_vnode;
- /* only a dir can be dfd */
- if (dvp->v_type != VDIR) {
- fdrop(fp, td);
- return (ENOTDIR);
- }
- vref(dvp);
- fdrop(fp, td);
- }
-
- /*
- * XXXRW: This is bogus, as vn_fullpath() returns only an advisory
- * file path, and may fail in several common situations, including
- * for file systmes that don't use the name cache, and if the entry
- * for the file falls out of the name cache. We should implement
- * openat() in the FreeBSD native system call layer properly (using a
- * requested starting directory), and have Linux and other ABIs wrap
- * the native implementation.
- */
- error = vn_fullpath(td, dvp, &fullpath, &freepath);
- if (!error) {
- *newpath = malloc(strlen(fullpath) + strlen(filename) + 2, M_TEMP, M_WAITOK | M_ZERO);
- *freebuf = freepath;
- sprintf(*newpath, "%s/%s", fullpath, filename);
- } else {
- *newpath = NULL;
- }
- vfslocked = VFS_LOCK_GIANT(dvp->v_mount);
- vrele(dvp);
- VFS_UNLOCK_GIANT(vfslocked);
- return (error);
+ LFREEPATH(path);
+ return (error);
}
int
linux_openat(struct thread *td, struct linux_openat_args *args)
{
- char *newpath, *oldpath, *freebuf, *path;
- int error;
+ char *path;
+ int dfd;
- oldpath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
- error = copyinstr(args->filename, oldpath, MAXPATHLEN, NULL);
- if (error) {
- free(oldpath, M_TEMP);
- return (error);
- }
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ if (args->flags & LINUX_O_CREAT)
+ LCONVPATH_AT(td, args->filename, &path, 1, dfd);
+ else
+ LCONVPATH_AT(td, args->filename, &path, 0, dfd);
#ifdef DEBUG
if (ldebug(openat))
printf(ARGS(openat, "%i, %s, 0x%x, 0x%x"), args->dfd,
- oldpath, args->flags, args->mode);
-#endif
- newpath = freebuf = NULL;
- error = linux_at(td, args->dfd, oldpath, &newpath, &freebuf);
- if (error == 0) {
-#ifdef DEBUG
- if (ldebug(openat))
- printf(LMSG("newpath: %s"), newpath);
+ path, args->flags, args->mode);
#endif
- if (args->flags & LINUX_O_CREAT)
- LCONVPATH_SEG(td, newpath, &path, 1, UIO_SYSSPACE);
- else
- LCONVPATH_SEG(td, newpath, &path, 0, UIO_SYSSPACE);
- }
- if (freebuf)
- free(freebuf, M_TEMP);
- if (*oldpath != '/')
- free(newpath, M_TEMP);
- if (error == 0) {
- error = linux_common_open(td, path, args->flags,
- args->mode, 1);
- LFREEPATH(path);
- }
- free(oldpath, M_TEMP);
- return (error);
+ return (linux_common_open(td, dfd, path, args->flags, args->mode));
}
int
@@ -306,7 +215,7 @@ linux_open(struct thread *td, struct linux_open_args *args)
path, args->flags, args->mode);
#endif
- return linux_common_open(td, path, args->flags, args->mode, 0);
+ return (linux_common_open(td, -1, path, args->flags, args->mode));
}
int
@@ -656,6 +565,31 @@ linux_access(struct thread *td, struct linux_access_args *args)
}
int
+linux_faccessat(struct thread *td, struct linux_faccessat_args *args)
+{
+ char *path;
+ int error, dfd;
+
+ /* linux convention */
+ if (args->mode & ~(F_OK | X_OK | W_OK | R_OK))
+ return (EINVAL);
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->filename, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(access))
+ printf(ARGS(access, "%s, %d"), path, args->mode);
+#endif
+
+ error = kern_accessat(td, dfd, path, UIO_SYSSPACE, 0 /* XXX */,
+ args->mode);
+ LFREEPATH(path);
+
+ return (error);
+}
+
+int
linux_unlink(struct thread *td, struct linux_unlink_args *args)
{
char *path;
@@ -680,6 +614,37 @@ linux_unlink(struct thread *td, struct linux_unlink_args *args)
}
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);
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->pathname, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(unlinkat))
+ printf(ARGS(unlinkat, "%s"), path);
+#endif
+
+ if (args->flag & LINUX_AT_REMOVEDIR)
+ error = kern_rmdirat(td, dfd, path, UIO_SYSSPACE);
+ else
+ error = kern_unlinkat(td, dfd, path, UIO_SYSSPACE);
+ if (error == EPERM && !(args->flag & LINUX_AT_REMOVEDIR)) {
+ /* Introduce POSIX noncompliant behaviour of Linux */
+ if (kern_statat(td, AT_SYMLINK_NOFOLLOW, dfd, path,
+ UIO_SYSSPACE, &st) == 0 && S_ISDIR(st.st_mode))
+ error = EISDIR;
+ }
+ LFREEPATH(path);
+ return (error);
+}
+int
linux_chdir(struct thread *td, struct linux_chdir_args *args)
{
char *path;
@@ -714,6 +679,25 @@ linux_chmod(struct thread *td, struct linux_chmod_args *args)
}
int
+linux_fchmodat(struct thread *td, struct linux_fchmodat_args *args)
+{
+ char *path;
+ int error, dfd;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->filename, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(fchmodat))
+ printf(ARGS(fchmodat, "%s, %d"), path, args->mode);
+#endif
+
+ error = kern_fchmodat(td, dfd, path, UIO_SYSSPACE, args->mode, 0);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
linux_mkdir(struct thread *td, struct linux_mkdir_args *args)
{
char *path;
@@ -731,6 +715,24 @@ linux_mkdir(struct thread *td, struct linux_mkdir_args *args)
}
int
+linux_mkdirat(struct thread *td, struct linux_mkdirat_args *args)
+{
+ char *path;
+ int error, dfd;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHCREAT_AT(td, args->pathname, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(mkdirat))
+ printf(ARGS(mkdirat, "%s, %d"), path, args->mode);
+#endif
+ error = kern_mkdirat(td, dfd, path, UIO_SYSSPACE, args->mode);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
linux_rmdir(struct thread *td, struct linux_rmdir_args *args)
{
char *path;
@@ -755,7 +757,7 @@ linux_rename(struct thread *td, struct linux_rename_args *args)
LCONVPATHEXIST(td, args->from, &from);
/* Expand LCONVPATHCREATE so that `from' can be freed on errors */
- error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1);
+ error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1, AT_FDCWD);
if (to == NULL) {
LFREEPATH(from);
return (error);
@@ -772,6 +774,32 @@ linux_rename(struct thread *td, struct linux_rename_args *args)
}
int
+linux_renameat(struct thread *td, struct linux_renameat_args *args)
+{
+ char *from, *to;
+ int error, olddfd, newdfd;
+
+ olddfd = (args->olddfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->olddfd;
+ newdfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
+ LCONVPATHEXIST_AT(td, args->oldname, &from, olddfd);
+ /* Expand LCONVPATHCREATE so that `from' can be freed on errors */
+ error = linux_emul_convpath(td, args->newname, UIO_USERSPACE, &to, 1, newdfd);
+ if (to == NULL) {
+ LFREEPATH(from);
+ return (error);
+ }
+
+#ifdef DEBUG
+ if (ldebug(renameat))
+ printf(ARGS(renameat, "%s, %s"), from, to);
+#endif
+ error = kern_renameat(td, olddfd, from, newdfd, to, UIO_SYSSPACE);
+ LFREEPATH(from);
+ LFREEPATH(to);
+ return (error);
+}
+
+int
linux_symlink(struct thread *td, struct linux_symlink_args *args)
{
char *path, *to;
@@ -779,7 +807,7 @@ linux_symlink(struct thread *td, struct linux_symlink_args *args)
LCONVPATHEXIST(td, args->path, &path);
/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
- error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1);
+ error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1, AT_FDCWD);
if (to == NULL) {
LFREEPATH(path);
return (error);
@@ -796,6 +824,32 @@ linux_symlink(struct thread *td, struct linux_symlink_args *args)
}
int
+linux_symlinkat(struct thread *td, struct linux_symlinkat_args *args)
+{
+ char *path, *to;
+ int error, dfd;
+
+ dfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
+ LCONVPATHEXIST_AT(td, args->oldname, &path, dfd);
+ /* Expand LCONVPATHCREATE so that `path' can be freed on errors */
+ error = linux_emul_convpath(td, args->newname, UIO_USERSPACE, &to, 1, dfd);
+ if (to == NULL) {
+ LFREEPATH(path);
+ return (error);
+ }
+
+#ifdef DEBUG
+ if (ldebug(symlinkat))
+ printf(ARGS(symlinkat, "%s, %s"), path, to);
+#endif
+
+ error = kern_symlinkat(td, path, dfd, to, UIO_SYSSPACE);
+ LFREEPATH(path);
+ LFREEPATH(to);
+ return (error);
+}
+
+int
linux_readlink(struct thread *td, struct linux_readlink_args *args)
{
char *name;
@@ -815,6 +869,26 @@ linux_readlink(struct thread *td, struct linux_readlink_args *args)
}
int
+linux_readlinkat(struct thread *td, struct linux_readlinkat_args *args)
+{
+ char *name;
+ int error, dfd;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->path, &name, dfd);
+
+#ifdef DEBUG
+ if (ldebug(readlinkat))
+ printf(ARGS(readlinkat, "%s, %p, %d"), name, (void *)args->buf,
+ args->bufsiz);
+#endif
+
+ error = kern_readlinkat(td, dfd, name, UIO_SYSSPACE, args->buf,
+ UIO_USERSPACE, args->bufsiz);
+ LFREEPATH(name);
+ return (error);
+}
+int
linux_truncate(struct thread *td, struct linux_truncate_args *args)
{
char *path;
@@ -854,7 +928,7 @@ linux_link(struct thread *td, struct linux_link_args *args)
LCONVPATHEXIST(td, args->path, &path);
/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
- error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1);
+ error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1, AT_FDCWD);
if (to == NULL) {
LFREEPATH(path);
return (error);
@@ -871,6 +945,41 @@ linux_link(struct thread *td, struct linux_link_args *args)
}
int
+linux_linkat(struct thread *td, struct linux_linkat_args *args)
+{
+ char *path, *to;
+ int error, olddfd, newdfd;
+
+ /*
+ * They really introduced flags argument which is forbidden to
+ * use.
+ */
+ if (args->flags != 0)
+ return (EINVAL);
+
+ olddfd = (args->olddfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->olddfd;
+ newdfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
+ LCONVPATHEXIST_AT(td, args->oldname, &path, olddfd);
+ /* Expand LCONVPATHCREATE so that `path' can be freed on errors */
+ error = linux_emul_convpath(td, args->newname, UIO_USERSPACE, &to, 1, newdfd);
+ 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
+
+ error = kern_linkat(td, olddfd, newdfd, path, to, UIO_SYSSPACE, FOLLOW);
+ LFREEPATH(path);
+ LFREEPATH(to);
+ return (error);
+}
+
+int
linux_fdatasync(td, uap)
struct thread *td;
struct linux_fdatasync_args *uap;
@@ -1338,6 +1447,31 @@ linux_chown(struct thread *td, struct linux_chown_args *args)
}
int
+linux_fchownat(struct thread *td, struct linux_fchownat_args *args)
+{
+ char *path;
+ int error, dfd, follow;
+
+ if (args->flag & ~LINUX_AT_SYMLINK_NOFOLLOW)
+ return (EINVAL);
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->filename, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(fchownat))
+ printf(ARGS(fchownat, "%s, %d, %d"), path, args->uid, args->gid);
+#endif
+
+ follow = (args->flag & LINUX_AT_SYMLINK_NOFOLLOW) == 0 ? 0 :
+ AT_SYMLINK_NOFOLLOW;
+ error = kern_fchownat(td, dfd, path, UIO_SYSSPACE, args->uid, args->gid,
+ follow);
+ LFREEPATH(path);
+ return (error);
+}
+
+int
linux_lchown(struct thread *td, struct linux_lchown_args *args)
{
char *path;
diff --git a/sys/compat/linux/linux_file.h b/sys/compat/linux/linux_file.h
new file mode 100644
index 0000000..e229cb6
--- /dev/null
+++ b/sys/compat/linux/linux_file.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2007 Roman Divacky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_FILE_H_
+#define _LINUX_FILE_H_
+
+#define LINUX_AT_FDCWD -100
+#define LINUX_AT_SYMLINK_NOFOLLOW 0x100
+#define LINUX_AT_REMOVEDIR 0x200
+
+#endif /* !_LINUX_FILE_H_ */
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
index 4d441de..1ce930f 100644
--- a/sys/compat/linux/linux_misc.c
+++ b/sys/compat/linux/linux_misc.c
@@ -87,6 +87,7 @@ __FBSDID("$FreeBSD$");
#include <machine/../linux/linux_proto.h>
#endif
+#include <compat/linux/linux_file.h>
#include <compat/linux/linux_mib.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_util.h>
@@ -836,6 +837,39 @@ linux_utimes(struct thread *td, struct linux_utimes_args *args)
LFREEPATH(fname);
return (error);
}
+
+int
+linux_futimesat(struct thread *td, struct linux_futimesat_args *args)
+{
+ l_timeval ltv[2];
+ struct timeval tv[2], *tvp = NULL;
+ char *fname;
+ int error, dfd;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->filename, &fname, dfd);
+
+#ifdef DEBUG
+ if (ldebug(futimesat))
+ printf(ARGS(futimesat, "%s, *"), fname);
+#endif
+
+ if (args->utimes != NULL) {
+ if ((error = copyin(args->utimes, ltv, sizeof ltv))) {
+ LFREEPATH(fname);
+ return (error);
+ }
+ tv[0].tv_sec = ltv[0].tv_sec;
+ tv[0].tv_usec = ltv[0].tv_usec;
+ tv[1].tv_sec = ltv[1].tv_sec;
+ tv[1].tv_usec = ltv[1].tv_usec;
+ tvp = tv;
+ }
+
+ error = kern_utimesat(td, dfd, fname, UIO_SYSSPACE, tvp, UIO_SYSSPACE);
+ LFREEPATH(fname);
+ return (error);
+}
#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
#define __WCLONE 0x80000000
@@ -963,6 +997,56 @@ linux_mknod(struct thread *td, struct linux_mknod_args *args)
case S_IFREG:
error = kern_open(td, path, UIO_SYSSPACE,
O_WRONLY | O_CREAT | O_TRUNC, args->mode);
+ if (error == 0)
+ kern_close(td, td->td_retval[0]);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ LFREEPATH(path);
+ return (error);
+}
+
+int
+linux_mknodat(struct thread *td, struct linux_mknodat_args *args)
+{
+ char *path;
+ int error, dfd;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHCREAT_AT(td, args->filename, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(mknodat))
+ printf(ARGS(mknodat, "%s, %d, %d"), path, args->mode, args->dev);
+#endif
+
+ switch (args->mode & S_IFMT) {
+ case S_IFIFO:
+ case S_IFSOCK:
+ error = kern_mkfifoat(td, dfd, path, UIO_SYSSPACE, args->mode);
+ break;
+
+ case S_IFCHR:
+ case S_IFBLK:
+ error = kern_mknodat(td, dfd, path, UIO_SYSSPACE, args->mode,
+ args->dev);
+ break;
+
+ case S_IFDIR:
+ error = EPERM;
+ break;
+
+ case 0:
+ args->mode |= S_IFREG;
+ /* FALLTHROUGH */
+ case S_IFREG:
+ error = kern_openat(td, dfd, path, UIO_SYSSPACE,
+ O_WRONLY | O_CREAT | O_TRUNC, args->mode);
+ if (error == 0)
+ kern_close(td, td->td_retval[0]);
break;
default:
diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c
index 374ce39..670ef22 100644
--- a/sys/compat/linux/linux_stats.c
+++ b/sys/compat/linux/linux_stats.c
@@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
#endif
#include <compat/linux/linux_util.h>
+#include <compat/linux/linux_file.h>
#include <security/mac/mac_framework.h>
@@ -114,9 +115,10 @@ translate_fd_major_minor(struct thread *td, int fd, struct stat *buf)
}
static void
-translate_path_major_minor(struct thread *td, char *path, struct stat *buf)
+translate_path_major_minor_at(struct thread *td, char *path,
+ struct stat *buf, int dfd)
{
- struct proc *p = td->td_proc;
+ struct proc *p = td->td_proc;
struct filedesc *fdp = p->p_fd;
int fd;
int temp;
@@ -124,7 +126,7 @@ translate_path_major_minor(struct thread *td, char *path, struct stat *buf)
if (!S_ISCHR(buf->st_mode) && !S_ISBLK(buf->st_mode))
return;
temp = td->td_retval[0];
- if (kern_open(td, path, UIO_SYSSPACE, O_RDONLY, 0) != 0)
+ if (kern_openat(td, dfd, path, UIO_SYSSPACE, O_RDONLY, 0) != 0)
return;
fd = td->td_retval[0];
td->td_retval[0] = temp;
@@ -132,6 +134,12 @@ translate_path_major_minor(struct thread *td, char *path, struct stat *buf)
fdclose(fdp, fdp->fd_ofiles[fd], fd, td);
}
+static inline void
+translate_path_major_minor(struct thread *td, char *path, struct stat *buf)
+{
+ translate_path_major_minor_at(td, path, buf, AT_FDCWD);
+}
+
static int
newstat_copyout(struct stat *buf, void *ubuf)
{
@@ -581,4 +589,33 @@ linux_fstat64(struct thread *td, struct linux_fstat64_args *args)
return (error);
}
+int
+linux_fstatat64(struct thread *td, struct linux_fstatat64_args *args)
+{
+ char *path;
+ int error, dfd, flag;
+ struct stat buf;
+
+ if (args->flag & ~LINUX_AT_SYMLINK_NOFOLLOW)
+ return (EINVAL);
+ flag = (args->flag & LINUX_AT_SYMLINK_NOFOLLOW) ?
+ AT_SYMLINK_NOFOLLOW : 0;
+
+ dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+ LCONVPATHEXIST_AT(td, args->pathname, &path, dfd);
+
+#ifdef DEBUG
+ if (ldebug(fstatat64))
+ printf(ARGS(fstatat64, "%i, %s, %i"), args->dfd, path, args->flag);
+#endif
+
+ error = kern_statat(td, flag, dfd, path, UIO_SYSSPACE, &buf);
+ translate_path_major_minor_at(td, args->pathname, &buf, dfd);
+ if (!error)
+ error = stat64_copyout(&buf, args->statbuf);
+ LFREEPATH(path);
+
+ return (error);
+}
+
#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
diff --git a/sys/compat/linux/linux_util.c b/sys/compat/linux/linux_util.c
index 2186e9a..76d6288 100644
--- a/sys/compat/linux/linux_util.c
+++ b/sys/compat/linux/linux_util.c
@@ -66,16 +66,17 @@ const char linux_emul_path[] = "/compat/linux";
* named file, i.e. we check if the directory it should be in exists.
*/
int
-linux_emul_convpath(td, path, pathseg, pbuf, cflag)
+linux_emul_convpath(td, path, pathseg, pbuf, cflag, dfd)
struct thread *td;
- char *path;
+ const char *path;
enum uio_seg pathseg;
char **pbuf;
int cflag;
+ int dfd;
{
return (kern_alternate_path(td, linux_emul_path, path, pathseg, pbuf,
- cflag));
+ cflag, dfd));
}
void
diff --git a/sys/compat/linux/linux_util.h b/sys/compat/linux/linux_util.h
index 5264766..4caad9d 100644
--- a/sys/compat/linux/linux_util.h
+++ b/sys/compat/linux/linux_util.h
@@ -51,23 +51,25 @@
extern const char linux_emul_path[];
-int linux_emul_convpath(struct thread *, char *, enum uio_seg, char **, int);
+int linux_emul_convpath(struct thread *, const char *, enum uio_seg, char **, int, int);
-#define LCONVPATH_SEG(td, upath, pathp, i, seg) \
+#define LCONVPATH_AT(td, upath, pathp, i, dfd) \
do { \
int _error; \
\
- _error = linux_emul_convpath(td, upath, seg, \
- pathp, i); \
+ _error = linux_emul_convpath(td, upath, UIO_USERSPACE, \
+ pathp, i, dfd); \
if (*(pathp) == NULL) \
return (_error); \
} while (0)
#define LCONVPATH(td, upath, pathp, i) \
- LCONVPATH_SEG(td, upath, pathp, i, UIO_USERSPACE)
+ LCONVPATH_AT(td, upath, pathp, i, AT_FDCWD)
#define LCONVPATHEXIST(td, upath, pathp) LCONVPATH(td, upath, pathp, 0)
+#define LCONVPATHEXIST_AT(td, upath, pathp, dfd) LCONVPATH_AT(td, upath, pathp, 0, dfd)
#define LCONVPATHCREAT(td, upath, pathp) LCONVPATH(td, upath, pathp, 1)
+#define LCONVPATHCREAT_AT(td, upath, pathp, dfd) LCONVPATH_AT(td, upath, pathp, 1, dfd)
#define LFREEPATH(path) free(path, M_TEMP)
#define DUMMY(s) \
diff --git a/sys/compat/svr4/svr4_sysvec.c b/sys/compat/svr4/svr4_sysvec.c
index 7f3b3c2..d8c1974 100644
--- a/sys/compat/svr4/svr4_sysvec.c
+++ b/sys/compat/svr4/svr4_sysvec.c
@@ -258,7 +258,7 @@ svr4_emul_find(struct thread *td, char *path, enum uio_seg pathseg,
{
return (kern_alternate_path(td, svr4_emul_path, path, pathseg, pbuf,
- create));
+ create, AT_FDCWD));
}
static int
diff --git a/sys/i386/ibcs2/ibcs2_util.c b/sys/i386/ibcs2/ibcs2_util.c
index 9f385b7..d63a640 100644
--- a/sys/i386/ibcs2/ibcs2_util.c
+++ b/sys/i386/ibcs2/ibcs2_util.c
@@ -56,5 +56,5 @@ ibcs2_emul_find(struct thread *td, char *path, enum uio_seg pathseg,
{
return (kern_alternate_path(td, ibcs2_emul_path, path, pathseg, pbuf,
- cflag));
+ cflag, AT_FDCWD));
}
diff --git a/sys/i386/linux/linux.h b/sys/i386/linux/linux.h
index e689475..4d556c6 100644
--- a/sys/i386/linux/linux.h
+++ b/sys/i386/linux/linux.h
@@ -550,8 +550,6 @@ int linux_ioctl_unregister_handler(struct linux_ioctl_handler *h);
#define LINUX_F_WRLCK 1
#define LINUX_F_UNLCK 2
-#define LINUX_AT_FDCWD -100
-
/*
* mount flags
*/
diff --git a/sys/i386/linux/linux_dummy.c b/sys/i386/linux/linux_dummy.c
index fec4f65..f4e267c 100644
--- a/sys/i386/linux/linux_dummy.c
+++ b/sys/i386/linux/linux_dummy.c
@@ -87,18 +87,6 @@ DUMMY(inotify_init);
DUMMY(inotify_add_watch);
DUMMY(inotify_rm_watch);
DUMMY(migrate_pages);
-DUMMY(mkdirat);
-DUMMY(mknodat);
-DUMMY(fchownat);
-DUMMY(futimesat);
-DUMMY(fstatat64);
-DUMMY(unlinkat);
-DUMMY(renameat);
-DUMMY(linkat);
-DUMMY(symlinkat);
-DUMMY(readlinkat);
-DUMMY(fchmodat);
-DUMMY(faccessat);
DUMMY(pselect6);
DUMMY(ppoll);
DUMMY(unshare);
diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c
index df3f8d1..2de5a6f 100644
--- a/sys/i386/linux/linux_sysvec.c
+++ b/sys/i386/linux/linux_sysvec.c
@@ -777,7 +777,7 @@ exec_linux_imgact_try(struct image_params *imgp)
*/
if ((error = exec_shell_imgact(imgp)) == 0) {
linux_emul_convpath(FIRST_THREAD_IN_PROC(imgp->proc),
- imgp->interpreter_name, UIO_SYSSPACE, &rpath, 0);
+ imgp->interpreter_name, UIO_SYSSPACE, &rpath, 0, AT_FDCWD);
if (rpath != NULL) {
len = strlen(rpath) + 1;
diff --git a/sys/i386/linux/syscalls.master b/sys/i386/linux/syscalls.master
index af0755a..98df86d 100644
--- a/sys/i386/linux/syscalls.master
+++ b/sys/i386/linux/syscalls.master
@@ -475,20 +475,31 @@
292 AUE_NULL STD { int linux_inotify_add_watch(void); }
293 AUE_NULL STD { int linux_inotify_rm_watch(void); }
294 AUE_NULL STD { int linux_migrate_pages(void); }
-295 AUE_OPEN_RWTC STD { int linux_openat(l_int dfd, char *filename, \
+295 AUE_OPEN_RWTC STD { int linux_openat(l_int dfd, const char *filename, \
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(void); }
-299 AUE_NULL STD { int linux_futimesat(void); }
-300 AUE_NULL STD { int linux_fstatat64(void); }
-301 AUE_NULL STD { int linux_unlinkat(void); }
-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(void); }
-306 AUE_NULL STD { int linux_fchmodat(void); }
-307 AUE_NULL STD { int linux_faccessat(void); }
+296 AUE_MKDIRAT STD { int linux_mkdirat(l_int dfd, const char *pathname, \
+ l_int mode); }
+297 AUE_MKNODAT STD { int linux_mknodat(l_int dfd, const char *filename, \
+ l_int mode, l_uint dev); }
+298 AUE_FCHOWNAT STD { int linux_fchownat(l_int dfd, const char *filename, \
+ l_uid16_t uid, l_gid16_t gid, l_int flag); }
+299 AUE_FUTIMESAT STD { int linux_futimesat(l_int dfd, char *filename, \
+ struct l_timeval *utimes); }
+300 AUE_FSTATAT STD { int linux_fstatat64(l_int dfd, char *pathname, \
+ struct l_stat64 *statbuf, l_int flag); }
+301 AUE_UNLINKAT STD { int linux_unlinkat(l_int dfd, const char *pathname, \
+ l_int flag); }
+302 AUE_RENAMEAT STD { int linux_renameat(l_int olddfd, const char *oldname, \
+ l_int newdfd, const char *newname); }
+303 AUE_LINKAT STD { int linux_linkat(l_int olddfd, const char *oldname, \
+ l_int newdfd, const char *newname, l_int flags); }
+304 AUE_SYMLINKAT STD { int linux_symlinkat(const char *oldname, l_int newdfd, \
+ const char *newname); }
+305 AUE_READLINKAT STD { int linux_readlinkat(l_int dfd, const char *path, \
+ char *buf, l_int bufsiz); }
+306 AUE_FCHMODAT STD { int linux_fchmodat(l_int dfd, const char *filename, \
+ l_mode_t mode); }
+307 AUE_FACCESSAT STD { int linux_faccessat(l_int dfd, const char *filename, l_int mode); }
308 AUE_NULL STD { int linux_pselect6(void); }
309 AUE_NULL STD { int linux_ppoll(void); }
310 AUE_NULL STD { int linux_unshare(void); }
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index dc34249..1b295fc 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -1032,8 +1032,8 @@ NDFREE(struct nameidata *ndp, const u_int flags)
* the M_TEMP bucket if one is returned.
*/
int
-kern_alternate_path(struct thread *td, const char *prefix, char *path,
- enum uio_seg pathseg, char **pathbuf, int create)
+kern_alternate_path(struct thread *td, const char *prefix, const char *path,
+ enum uio_seg pathseg, char **pathbuf, int create, int dirfd)
{
struct nameidata nd, ndroot;
char *ptr, *buf, *cp;
@@ -1071,6 +1071,15 @@ kern_alternate_path(struct thread *td, const char *prefix, char *path,
goto keeporig;
}
+ if (dirfd != AT_FDCWD) {
+ /*
+ * We want the original because the "prefix" is
+ * included in the already opened dirfd.
+ */
+ bcopy(ptr, buf, len);
+ return (0);
+ }
+
/*
* We know that there is a / somewhere in this pathname.
* Search backwards for it, to find the file's parent dir
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
index c492ff4..ec352f8 100644
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -60,8 +60,8 @@ int kern_accessat(struct thread *td, int fd, char *path,
enum uio_seg pathseg, int flags, int mode);
int kern_adjtime(struct thread *td, struct timeval *delta,
struct timeval *olddelta);
-int kern_alternate_path(struct thread *td, const char *prefix, char *path,
- enum uio_seg pathseg, char **pathbuf, int create);
+int kern_alternate_path(struct thread *td, const char *prefix, const char *path,
+ enum uio_seg pathseg, char **pathbuf, int create, int dirfd);
int kern_bind(struct thread *td, int fd, struct sockaddr *sa);
int kern_chdir(struct thread *td, char *path, enum uio_seg pathseg);
int kern_chmod(struct thread *td, char *path, enum uio_seg pathseg,
OpenPOWER on IntegriCloud