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
This commit is contained in:
kib 2008-04-08 09:45:49 +00:00
parent c2a6bed59d
commit eb77b477b4
18 changed files with 483 additions and 185 deletions

View File

@ -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
*/

View File

@ -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);

View File

@ -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;

View File

@ -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); }

View File

@ -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,6 +131,9 @@ 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. */
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);
}
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);
path, 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);
#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
@ -655,6 +564,31 @@ linux_access(struct thread *td, struct linux_access_args *args)
return (error);
}
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)
{
@ -679,6 +613,37 @@ linux_unlink(struct thread *td, struct linux_unlink_args *args)
return (error);
}
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)
{
@ -713,6 +678,25 @@ linux_chmod(struct thread *td, struct linux_chmod_args *args)
return (error);
}
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)
{
@ -730,6 +714,24 @@ linux_mkdir(struct thread *td, struct linux_mkdir_args *args)
return (error);
}
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)
{
@ -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);
@ -771,6 +773,32 @@ linux_rename(struct thread *td, struct linux_rename_args *args)
return (error);
}
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)
{
@ -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);
@ -795,6 +823,32 @@ linux_symlink(struct thread *td, struct linux_symlink_args *args)
return (error);
}
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)
{
@ -814,6 +868,26 @@ linux_readlink(struct thread *td, struct linux_readlink_args *args)
return (error);
}
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)
{
@ -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);
@ -870,6 +944,41 @@ linux_link(struct thread *td, struct linux_link_args *args)
return (error);
}
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;
@ -1337,6 +1446,31 @@ linux_chown(struct thread *td, struct linux_chown_args *args)
return (error);
}
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)
{

View File

@ -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_ */

View File

@ -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:

View File

@ -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,7 +115,8 @@ 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 filedesc *fdp = p->p_fd;
@ -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) */

View File

@ -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

View File

@ -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) \

View File

@ -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

View File

@ -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));
}

View File

@ -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
*/

View File

@ -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);

View File

@ -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;

View File

@ -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); }

View File

@ -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

View File

@ -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,