diff options
author | julian <julian@FreeBSD.org> | 2007-03-29 02:11:46 +0000 |
---|---|---|
committer | julian <julian@FreeBSD.org> | 2007-03-29 02:11:46 +0000 |
commit | 93fc8e768e49d2737c73c10e780d085f9fe2de7d (patch) | |
tree | f48ac269cb2bbda7e0d9278c796db1a7d0a2cb59 /sys | |
parent | fdf75b902074d70bcb83a0421e1e7a1ef8f5bed5 (diff) | |
download | FreeBSD-src-93fc8e768e49d2737c73c10e780d085f9fe2de7d.zip FreeBSD-src-93fc8e768e49d2737c73c10e780d085f9fe2de7d.tar.gz |
Implement the openat() linux syscall
Submitted by: Roman Divacky (rdivacky@)
MFC after: 2 weeks
Diffstat (limited to 'sys')
-rw-r--r-- | sys/amd64/linux32/linux.h | 3 | ||||
-rw-r--r-- | sys/amd64/linux32/linux32_dummy.c | 1 | ||||
-rw-r--r-- | sys/amd64/linux32/linux32_proto.h | 7 | ||||
-rw-r--r-- | sys/amd64/linux32/linux32_sysent.c | 2 | ||||
-rw-r--r-- | sys/amd64/linux32/syscalls.master | 3 | ||||
-rw-r--r-- | sys/compat/linux/linux_file.c | 168 | ||||
-rw-r--r-- | sys/compat/linux/linux_util.h | 7 | ||||
-rw-r--r-- | sys/i386/linux/linux.h | 3 | ||||
-rw-r--r-- | sys/i386/linux/linux_dummy.c | 1 | ||||
-rw-r--r-- | sys/i386/linux/linux_proto.h | 7 | ||||
-rw-r--r-- | sys/i386/linux/linux_sysent.c | 2 | ||||
-rw-r--r-- | sys/i386/linux/syscalls.master | 3 |
12 files changed, 164 insertions, 43 deletions
diff --git a/sys/amd64/linux32/linux.h b/sys/amd64/linux32/linux.h index cf9b14c..b998402 100644 --- a/sys/amd64/linux32/linux.h +++ b/sys/amd64/linux32/linux.h @@ -531,6 +531,7 @@ int linux_ioctl_unregister_handler(struct linux_ioctl_handler *h); #define LINUX_O_RDONLY 00000000 #define LINUX_O_WRONLY 00000001 #define LINUX_O_RDWR 00000002 +#define LINUX_O_ACCMODE 00000003 #define LINUX_O_CREAT 00000100 #define LINUX_O_EXCL 00000200 #define LINUX_O_NOCTTY 00000400 @@ -565,6 +566,8 @@ 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 5babd4c..5f4b797 100644 --- a/sys/amd64/linux32/linux32_dummy.c +++ b/sys/amd64/linux32/linux32_dummy.c @@ -97,7 +97,6 @@ DUMMY(inotify_init); DUMMY(inotify_add_watch); DUMMY(inotify_rm_watch); DUMMY(migrate_pages); -DUMMY(openat); DUMMY(mkdirat); DUMMY(mknodat); DUMMY(fchownat); diff --git a/sys/amd64/linux32/linux32_proto.h b/sys/amd64/linux32/linux32_proto.h index e8a5b10..0ad4576 100644 --- a/sys/amd64/linux32/linux32_proto.h +++ b/sys/amd64/linux32/linux32_proto.h @@ -871,7 +871,10 @@ struct linux_migrate_pages_args { register_t dummy; }; struct linux_openat_args { - register_t dummy; + char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)]; + char filename_l_[PADL_(char *)]; char * filename; char filename_r_[PADR_(char *)]; + char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)]; + char mode_l_[PADL_(l_int)]; l_int mode; char mode_r_[PADR_(l_int)]; }; struct linux_mkdirat_args { register_t dummy; @@ -1381,7 +1384,7 @@ int linux_unshare(struct thread *, struct linux_unshare_args *); #define LINUX_SYS_AUE_linux_inotify_add_watch AUE_NULL #define LINUX_SYS_AUE_linux_inotify_rm_watch AUE_NULL #define LINUX_SYS_AUE_linux_migrate_pages AUE_NULL -#define LINUX_SYS_AUE_linux_openat AUE_NULL +#define LINUX_SYS_AUE_linux_openat AUE_OPEN_RWTC #define LINUX_SYS_AUE_linux_mkdirat AUE_NULL #define LINUX_SYS_AUE_linux_mknodat AUE_NULL #define LINUX_SYS_AUE_linux_fchownat AUE_NULL diff --git a/sys/amd64/linux32/linux32_sysent.c b/sys/amd64/linux32/linux32_sysent.c index 3bd1885..19c86ce 100644 --- a/sys/amd64/linux32/linux32_sysent.c +++ b/sys/amd64/linux32/linux32_sysent.c @@ -315,7 +315,7 @@ struct sysent linux_sysent[] = { { 0, (sy_call_t *)linux_inotify_add_watch, AUE_NULL, NULL, 0, 0 }, /* 292 = linux_inotify_add_watch */ { 0, (sy_call_t *)linux_inotify_rm_watch, AUE_NULL, NULL, 0, 0 }, /* 293 = linux_inotify_rm_watch */ { 0, (sy_call_t *)linux_migrate_pages, AUE_NULL, NULL, 0, 0 }, /* 294 = linux_migrate_pages */ - { 0, (sy_call_t *)linux_openat, AUE_NULL, NULL, 0, 0 }, /* 295 = linux_openat */ + { AS(linux_openat_args), (sy_call_t *)linux_openat, AUE_OPEN_RWTC, NULL, 0, 0 }, /* 295 = linux_openat */ { 0, (sy_call_t *)linux_mkdirat, AUE_NULL, NULL, 0, 0 }, /* 296 = linux_mkdirat */ { 0, (sy_call_t *)linux_mknodat, AUE_NULL, NULL, 0, 0 }, /* 297 = linux_mknodat */ { 0, (sy_call_t *)linux_fchownat, AUE_NULL, NULL, 0, 0 }, /* 298 = linux_fchownat */ diff --git a/sys/amd64/linux32/syscalls.master b/sys/amd64/linux32/syscalls.master index 9c3ee1e..9fc4cb0 100644 --- a/sys/amd64/linux32/syscalls.master +++ b/sys/amd64/linux32/syscalls.master @@ -463,7 +463,8 @@ 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_NULL STD { int linux_openat(void); } +295 AUE_OPEN_RWTC STD { int linux_openat(l_int dfd, 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); } diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c index e09f915..ae99416 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) { diff --git a/sys/compat/linux/linux_util.h b/sys/compat/linux/linux_util.h index ee47baf..5264766 100644 --- a/sys/compat/linux/linux_util.h +++ b/sys/compat/linux/linux_util.h @@ -53,16 +53,19 @@ extern const char linux_emul_path[]; int linux_emul_convpath(struct thread *, char *, enum uio_seg, char **, int); -#define LCONVPATH(td, upath, pathp, i) \ +#define LCONVPATH_SEG(td, upath, pathp, i, seg) \ do { \ int _error; \ \ - _error = linux_emul_convpath(td, upath, UIO_USERSPACE, \ + _error = linux_emul_convpath(td, upath, seg, \ pathp, i); \ if (*(pathp) == NULL) \ return (_error); \ } while (0) +#define LCONVPATH(td, upath, pathp, i) \ + LCONVPATH_SEG(td, upath, pathp, i, UIO_USERSPACE) + #define LCONVPATHEXIST(td, upath, pathp) LCONVPATH(td, upath, pathp, 0) #define LCONVPATHCREAT(td, upath, pathp) LCONVPATH(td, upath, pathp, 1) #define LFREEPATH(path) free(path, M_TEMP) diff --git a/sys/i386/linux/linux.h b/sys/i386/linux/linux.h index c7219ba..ef41399 100644 --- a/sys/i386/linux/linux.h +++ b/sys/i386/linux/linux.h @@ -502,6 +502,7 @@ int linux_ioctl_unregister_handler(struct linux_ioctl_handler *h); #define LINUX_O_RDONLY 00000000 #define LINUX_O_WRONLY 00000001 #define LINUX_O_RDWR 00000002 +#define LINUX_O_ACCMODE 00000003 #define LINUX_O_CREAT 00000100 #define LINUX_O_EXCL 00000200 #define LINUX_O_NOCTTY 00000400 @@ -536,6 +537,8 @@ 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 cd2c615..fec4f65 100644 --- a/sys/i386/linux/linux_dummy.c +++ b/sys/i386/linux/linux_dummy.c @@ -87,7 +87,6 @@ DUMMY(inotify_init); DUMMY(inotify_add_watch); DUMMY(inotify_rm_watch); DUMMY(migrate_pages); -DUMMY(openat); DUMMY(mkdirat); DUMMY(mknodat); DUMMY(fchownat); diff --git a/sys/i386/linux/linux_proto.h b/sys/i386/linux/linux_proto.h index f828447..53b0520 100644 --- a/sys/i386/linux/linux_proto.h +++ b/sys/i386/linux/linux_proto.h @@ -893,7 +893,10 @@ struct linux_migrate_pages_args { register_t dummy; }; struct linux_openat_args { - register_t dummy; + char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)]; + char filename_l_[PADL_(char *)]; char * filename; char filename_r_[PADR_(char *)]; + char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)]; + char mode_l_[PADL_(l_int)]; l_int mode; char mode_r_[PADR_(l_int)]; }; struct linux_mkdirat_args { register_t dummy; @@ -1407,7 +1410,7 @@ int linux_unshare(struct thread *, struct linux_unshare_args *); #define LINUX_SYS_AUE_linux_inotify_add_watch AUE_NULL #define LINUX_SYS_AUE_linux_inotify_rm_watch AUE_NULL #define LINUX_SYS_AUE_linux_migrate_pages AUE_NULL -#define LINUX_SYS_AUE_linux_openat AUE_NULL +#define LINUX_SYS_AUE_linux_openat AUE_OPEN_RWTC #define LINUX_SYS_AUE_linux_mkdirat AUE_NULL #define LINUX_SYS_AUE_linux_mknodat AUE_NULL #define LINUX_SYS_AUE_linux_fchownat AUE_NULL diff --git a/sys/i386/linux/linux_sysent.c b/sys/i386/linux/linux_sysent.c index b961a25..a38e54a 100644 --- a/sys/i386/linux/linux_sysent.c +++ b/sys/i386/linux/linux_sysent.c @@ -314,7 +314,7 @@ struct sysent linux_sysent[] = { { 0, (sy_call_t *)linux_inotify_add_watch, AUE_NULL, NULL, 0, 0 }, /* 292 = linux_inotify_add_watch */ { 0, (sy_call_t *)linux_inotify_rm_watch, AUE_NULL, NULL, 0, 0 }, /* 293 = linux_inotify_rm_watch */ { 0, (sy_call_t *)linux_migrate_pages, AUE_NULL, NULL, 0, 0 }, /* 294 = linux_migrate_pages */ - { 0, (sy_call_t *)linux_openat, AUE_NULL, NULL, 0, 0 }, /* 295 = linux_openat */ + { AS(linux_openat_args), (sy_call_t *)linux_openat, AUE_OPEN_RWTC, NULL, 0, 0 }, /* 295 = linux_openat */ { 0, (sy_call_t *)linux_mkdirat, AUE_NULL, NULL, 0, 0 }, /* 296 = linux_mkdirat */ { 0, (sy_call_t *)linux_mknodat, AUE_NULL, NULL, 0, 0 }, /* 297 = linux_mknodat */ { 0, (sy_call_t *)linux_fchownat, AUE_NULL, NULL, 0, 0 }, /* 298 = linux_fchownat */ diff --git a/sys/i386/linux/syscalls.master b/sys/i386/linux/syscalls.master index fa9d444..43946f2 100644 --- a/sys/i386/linux/syscalls.master +++ b/sys/i386/linux/syscalls.master @@ -473,7 +473,8 @@ 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_NULL STD { int linux_openat(void); } +295 AUE_OPEN_RWTC STD { int linux_openat(l_int dfd, 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); } |