diff options
author | Konstantin Belousov <kib@FreeBSD.org> | 2008-04-08 09:45:49 +0000 |
---|---|---|
committer | Konstantin Belousov <kib@FreeBSD.org> | 2008-04-08 09:45:49 +0000 |
commit | 48b05c3f8217016d464c5cd6dc8e61de1f8eda9c (patch) | |
tree | 27aa9d496f901903e5e31001326287aab78c768b /sys | |
parent | 1cf165afe7ea0188d85fa96d7b7480935b4edcb8 (diff) |
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
Notes
Notes:
svn path=/head/; revision=177997
Diffstat (limited to 'sys')
-rw-r--r-- | sys/amd64/linux32/linux.h | 2 | ||||
-rw-r--r-- | sys/amd64/linux32/linux32_dummy.c | 12 | ||||
-rw-r--r-- | sys/amd64/linux32/linux32_sysvec.c | 3 | ||||
-rw-r--r-- | sys/amd64/linux32/syscalls.master | 37 | ||||
-rw-r--r-- | sys/compat/linux/linux_file.c | 358 | ||||
-rw-r--r-- | sys/compat/linux/linux_file.h | 36 | ||||
-rw-r--r-- | sys/compat/linux/linux_misc.c | 84 | ||||
-rw-r--r-- | sys/compat/linux/linux_stats.c | 43 | ||||
-rw-r--r-- | sys/compat/linux/linux_util.c | 7 | ||||
-rw-r--r-- | sys/compat/linux/linux_util.h | 12 | ||||
-rw-r--r-- | sys/compat/svr4/svr4_sysvec.c | 2 | ||||
-rw-r--r-- | sys/i386/ibcs2/ibcs2_util.c | 2 | ||||
-rw-r--r-- | sys/i386/linux/linux.h | 2 | ||||
-rw-r--r-- | sys/i386/linux/linux_dummy.c | 12 | ||||
-rw-r--r-- | sys/i386/linux/linux_sysvec.c | 2 | ||||
-rw-r--r-- | sys/i386/linux/syscalls.master | 37 | ||||
-rw-r--r-- | sys/kern/vfs_lookup.c | 13 | ||||
-rw-r--r-- | sys/sys/syscallsubr.h | 4 |
18 files changed, 483 insertions, 185 deletions
diff --git a/sys/amd64/linux32/linux.h b/sys/amd64/linux32/linux.h index a904afc2da86..918f7ef78382 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 43d4573be9c7..657366817950 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 9259092928f3..3848fb0c08ed 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 86350c91b6e3..29a15e9648db 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 85a0ccaca55c..646f6ebfb5e8 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 000000000000..e229cb64fb41 --- /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 4d441de73878..1ce930f4d46c 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 374ce39a0eb3..670ef224ecd1 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 2186e9a39872..76d62885fab2 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 52647666898b..4caad9d857b1 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 7f3b3c27a420..d8c1974c88b1 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 9f385b7daa17..d63a640f92b3 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 e68947572e61..4d556c65895f 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 fec4f6539573..f4e267ce9ce4 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 df3f8d15978f..2de5a6f22b14 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 af0755aedc59..98df86de01c2 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 dc34249c95f3..1b295fc30834 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 c492ff4cb497..ec352f8c568b 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, |