diff options
Diffstat (limited to 'sys/compat/linux/linux_file.c')
-rw-r--r-- | sys/compat/linux/linux_file.c | 168 |
1 files changed, 137 insertions, 31 deletions
diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c index e09f91511753..ae994164273c 100644 --- a/sys/compat/linux/linux_file.c +++ b/sys/compat/linux/linux_file.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/mount.h> #include <sys/mutex.h> +#include <sys/namei.h> #include <sys/proc.h> #include <sys/stat.h> #include <sys/sx.h> @@ -85,57 +86,51 @@ linux_creat(struct thread *td, struct linux_creat_args *args) return (error); } -int -linux_open(struct thread *td, struct linux_open_args *args) + +static int +linux_common_open(struct thread *td, char *path, int l_flags, int mode, int openat) { struct proc *p = td->td_proc; struct file *fp; int fd; - char *path; int bsd_flags, error; - if (args->flags & LINUX_O_CREAT) - LCONVPATHCREAT(td, args->path, &path); - else - LCONVPATHEXIST(td, args->path, &path); - -#ifdef DEBUG - if (ldebug(open)) - printf(ARGS(open, "%s, 0x%x, 0x%x"), - path, args->flags, args->mode); -#endif bsd_flags = 0; - if (args->flags & LINUX_O_RDONLY) - bsd_flags |= O_RDONLY; - if (args->flags & LINUX_O_WRONLY) + switch (l_flags & LINUX_O_ACCMODE) { + case LINUX_O_WRONLY: bsd_flags |= O_WRONLY; - if (args->flags & LINUX_O_RDWR) + break; + case LINUX_O_RDWR: bsd_flags |= O_RDWR; - if (args->flags & LINUX_O_NDELAY) + break; + default: + bsd_flags |= O_RDONLY; + } + if (l_flags & LINUX_O_NDELAY) bsd_flags |= O_NONBLOCK; - if (args->flags & LINUX_O_APPEND) + if (l_flags & LINUX_O_APPEND) bsd_flags |= O_APPEND; - if (args->flags & LINUX_O_SYNC) + if (l_flags & LINUX_O_SYNC) bsd_flags |= O_FSYNC; - if (args->flags & LINUX_O_NONBLOCK) + if (l_flags & LINUX_O_NONBLOCK) bsd_flags |= O_NONBLOCK; - if (args->flags & LINUX_FASYNC) + if (l_flags & LINUX_FASYNC) bsd_flags |= O_ASYNC; - if (args->flags & LINUX_O_CREAT) + if (l_flags & LINUX_O_CREAT) bsd_flags |= O_CREAT; - if (args->flags & LINUX_O_TRUNC) + if (l_flags & LINUX_O_TRUNC) bsd_flags |= O_TRUNC; - if (args->flags & LINUX_O_EXCL) + if (l_flags & LINUX_O_EXCL) bsd_flags |= O_EXCL; - if (args->flags & LINUX_O_NOCTTY) + if (l_flags & LINUX_O_NOCTTY) bsd_flags |= O_NOCTTY; - if (args->flags & LINUX_O_DIRECT) + if (l_flags & LINUX_O_DIRECT) bsd_flags |= O_DIRECT; - if (args->flags & LINUX_O_NOFOLLOW) + if (l_flags & LINUX_O_NOFOLLOW) bsd_flags |= O_NOFOLLOW; /* XXX LINUX_O_NOATIME: unable to be easily implemented. */ - error = kern_open(td, path, UIO_SYSSPACE, bsd_flags, args->mode); + error = kern_open(td, path, UIO_SYSSPACE, bsd_flags, mode); if (!error) { fd = td->td_retval[0]; /* @@ -158,7 +153,7 @@ linux_open(struct thread *td, struct linux_open_args *args) PROC_UNLOCK(p); sx_sunlock(&proctree_lock); } - if (args->flags & LINUX_O_DIRECTORY) { + if (l_flags & LINUX_O_DIRECTORY) { if (fp->f_type != DTYPE_VNODE || fp->f_vnode->v_type != VDIR) { error = ENOTDIR; @@ -177,10 +172,121 @@ linux_open(struct thread *td, struct linux_open_args *args) if (ldebug(open)) printf(LMSG("open returns error %d"), error); #endif - LFREEPATH(path); + 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; + 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_LOCK(fdp); + dvp = fdp->fd_cdir; + FILEDESC_UNLOCK(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); + } + fdrop(fp, td); + } + + 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); + } + + return (error); +} + +int +linux_openat(struct thread *td, struct linux_openat_args *args) +{ + char *newpath, *oldpath, *freebuf = NULL, *path; + int error; + + oldpath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); + error = copyinstr(args->filename, oldpath, MAXPATHLEN, NULL); + +#ifdef DEBUG + if (ldebug(openat)) + printf(ARGS(openat, "%i, %s, 0x%x, 0x%x"), args->dfd, + oldpath, args->flags, args->mode); +#endif + + error = linux_at(td, args->dfd, oldpath, &newpath, &freebuf); + if (error) + return (error); +#ifdef DEBUG + 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); + + error = linux_common_open(td, path, args->flags, args->mode, 1); + free(oldpath, M_TEMP); + return (error); +} + +int +linux_open(struct thread *td, struct linux_open_args *args) +{ + char *path; + + if (args->flags & LINUX_O_CREAT) + LCONVPATHCREAT(td, args->path, &path); + else + LCONVPATHEXIST(td, args->path, &path); + +#ifdef DEBUG + if (ldebug(open)) + printf(ARGS(open, "%s, 0x%x, 0x%x"), + path, args->flags, args->mode); +#endif + + return linux_common_open(td, path, args->flags, args->mode, 0); +} + int linux_lseek(struct thread *td, struct linux_lseek_args *args) { |