Index: sys/amd64/linux/syscalls.master =================================================================== --- sys/amd64/linux/syscalls.master +++ sys/amd64/linux/syscalls.master @@ -1792,10 +1792,20 @@ } ; Linux 2.6.39 (glibc 2.14): 303 AUE_NULL STD { - int linux_name_to_handle_at(void); + int linux_name_to_handle_at( + l_int dirfd, + const char *name, + struct l_file_handle *handle, + l_int *mnt_id, + l_int flags + ); } 304 AUE_NULL STD { - int linux_open_by_handle_at(void); + int linux_open_by_handle_at( + l_int mountdirfd, + struct l_file_handle *handle, + l_int flags + ); } 305 AUE_NULL STD { int linux_clock_adjtime(void); Index: sys/amd64/linux32/syscalls.master =================================================================== --- sys/amd64/linux32/syscalls.master +++ sys/amd64/linux32/syscalls.master @@ -1916,10 +1916,20 @@ } ; Linux 2.6.39: 341 AUE_NULL STD { - int linux_name_to_handle_at(void); + int linux_name_to_handle_at( + l_int dirfd, + const char *name, + struct l_file_handle *handle, + l_int *mnt_id, + l_int flags + ); } 342 AUE_NULL STD { - int linux_open_by_handle_at(void); + int linux_open_by_handle_at( + l_int mountdirfd, + struct l_file_handle *handle, + l_int flags + ); } 343 AUE_NULL STD { int linux_clock_adjtime(void); Index: sys/arm64/linux/syscalls.master =================================================================== --- sys/arm64/linux/syscalls.master +++ sys/arm64/linux/syscalls.master @@ -1473,10 +1473,20 @@ int linux_fanotify_mark(void); } 264 AUE_NULL STD { - int linux_name_to_handle_at(void); + int linux_name_to_handle_at( + l_int dirfd, + const char *name, + struct l_file_handle *handle, + l_int *mnt_id, + l_int flags + ); } 265 AUE_NULL STD { - int linux_open_by_handle_at(void); + int linux_open_by_handle_at( + l_int mountdirfd, + struct l_file_handle *handle, + l_int flags + ); } 266 AUE_NULL STD { int linux_clock_adjtime(void); Index: sys/compat/linux/linux_dummy.c =================================================================== --- sys/compat/linux/linux_dummy.c +++ sys/compat/linux/linux_dummy.c @@ -103,8 +103,6 @@ DUMMY(fanotify_init); DUMMY(fanotify_mark); /* Linux 2.6.39: */ -DUMMY(name_to_handle_at); -DUMMY(open_by_handle_at); DUMMY(clock_adjtime); /* Linux 3.0: */ DUMMY(setns); Index: sys/compat/linux/linux_file.h =================================================================== --- sys/compat/linux/linux_file.h +++ sys/compat/linux/linux_file.h @@ -36,6 +36,7 @@ #define LINUX_AT_EACCESS 0x200 #define LINUX_AT_REMOVEDIR 0x200 #define LINUX_AT_SYMLINK_FOLLOW 0x400 +#define LINUX_AT_EMPTY_PATH 0x1000 /* * posix_fadvise advice @@ -174,4 +175,10 @@ #define LINUX_HUGETLB_FLAG_ENCODE_2GB (31 << LINUX_HUGETLB_FLAG_ENCODE_SHIFT) #define LINUX_HUGETLB_FLAG_ENCODE_16GB (34U << LINUX_HUGETLB_FLAG_ENCODE_SHIFT) +struct l_file_handle { + l_uint handle_bytes; + l_int handle_type; + unsigned char f_handle[0]; +}; + #endif /* !_LINUX_FILE_H_ */ Index: sys/compat/linux/linux_file.c =================================================================== --- sys/compat/linux/linux_file.c +++ sys/compat/linux/linux_file.c @@ -121,13 +121,9 @@ #endif static int -linux_common_open(struct thread *td, int dirfd, const char *path, int l_flags, - int mode, enum uio_seg seg) +linux_common_openflags(int l_flags) { - struct proc *p = td->td_proc; - struct file *fp; - int fd; - int bsd_flags, error; + int bsd_flags; bsd_flags = 0; switch (l_flags & LINUX_O_ACCMODE) { @@ -167,7 +163,19 @@ if (l_flags & LINUX_O_DIRECTORY) bsd_flags |= O_DIRECTORY; /* XXX LINUX_O_NOATIME: unable to be easily implemented. */ + return (bsd_flags); +} +static int +linux_common_open(struct thread *td, int dirfd, const char *path, int l_flags, + int mode, enum uio_seg seg) +{ + struct proc *p = td->td_proc; + struct file *fp; + int fd; + int bsd_flags, error; + + bsd_flags = linux_common_openflags(l_flags); error = kern_openat(td, dirfd, path, seg, bsd_flags, mode); if (error != 0) { if (error == EMLINK) @@ -254,6 +262,102 @@ } #endif +int +linux_name_to_handle_at(struct thread *td, + struct linux_name_to_handle_at_args *args) +{ + static const l_int valid_flags = (LINUX_AT_SYMLINK_FOLLOW | + LINUX_AT_EMPTY_PATH); + static const l_uint fh_size = sizeof(fhandle_t); + + fhandle_t fh; + l_uint fh_bytes; + l_int mount_id; + int error, fd, bsd_flags; + + if (args->flags & ~valid_flags) + return (EINVAL); + if (args->flags & LINUX_AT_EMPTY_PATH) + /* XXX: not supported yet */ + return (EOPNOTSUPP); + + fd = args->dirfd; + if (fd == LINUX_AT_FDCWD) + fd = AT_FDCWD; + + bsd_flags = 0; + if (!(args->flags & LINUX_AT_SYMLINK_FOLLOW)) + bsd_flags |= AT_SYMLINK_NOFOLLOW; + + if (!LUSECONVPATH(td)) { + error = kern_getfhat(td, bsd_flags, fd, args->name, + UIO_USERSPACE, &fh, UIO_SYSSPACE); + } else { + char *path; + + LCONVPATH_AT(td, args->name, &path, 0, fd); + error = kern_getfhat(td, bsd_flags, fd, path, UIO_SYSSPACE, + &fh, UIO_SYSSPACE); + LFREEPATH(path); + } + if (error != 0) + return (error); + + /* Emit mount_id -- required before EOVERFLOW case. */ + mount_id = (fh.fh_fsid.val[0] ^ fh.fh_fsid.val[1]); + error = copyout(&mount_id, args->mnt_id, sizeof(mount_id)); + if (error != 0) + return (error); + + /* Check if there is room for handle. */ + error = copyin(&args->handle->handle_bytes, &fh_bytes, + sizeof(fh_bytes)); + if (error != 0) + return (error); + + if (fh_bytes < fh_size) { + error = copyout(&fh_size, &args->handle->handle_bytes, + sizeof(fh_size)); + if (error == 0) + error = EOVERFLOW; + return (error); + } + + /* Emit handle. */ + mount_id = 0; + /* + * We don't use handle_type for anything yet, but initialize a known + * value. + */ + error = copyout(&mount_id, &args->handle->handle_type, + sizeof(mount_id)); + if (error != 0) + return (error); + + error = copyout(&fh, &args->handle->f_handle, + sizeof(fh)); + return (error); +} + +int +linux_open_by_handle_at(struct thread *td, + struct linux_open_by_handle_at_args *args) +{ + l_uint fh_bytes; + int bsd_flags, error; + + error = copyin(&args->handle->handle_bytes, &fh_bytes, + sizeof(fh_bytes)); + if (error != 0) + return (error); + + if (fh_bytes < sizeof(fhandle_t)) + return (EINVAL); + + bsd_flags = linux_common_openflags(args->flags); + return (kern_fhopen(td, (void *)&args->handle->f_handle, bsd_flags)); +} + int linux_lseek(struct thread *td, struct linux_lseek_args *args) { Index: sys/i386/linux/syscalls.master =================================================================== --- sys/i386/linux/syscalls.master +++ sys/i386/linux/syscalls.master @@ -1934,10 +1934,20 @@ } ; Linux 2.6.39: 341 AUE_NULL STD { - int linux_name_to_handle_at(void); + int linux_name_to_handle_at( + l_int dirfd, + const char *name, + struct l_file_handle *handle, + l_int *mnt_id, + l_int flags + ); } 342 AUE_NULL STD { - int linux_open_by_handle_at(void); + int linux_open_by_handle_at( + l_int mountdirfd, + struct l_file_handle *handle, + l_int flags + ); } 343 AUE_NULL STD { int linux_clock_adjtime(void); Index: sys/kern/vfs_syscalls.c =================================================================== --- sys/kern/vfs_syscalls.c +++ sys/kern/vfs_syscalls.c @@ -105,8 +105,6 @@ struct thread *td); static int kern_fhlinkat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, fhandle_t *fhp); -static int kern_getfhat(struct thread *td, int flags, int fd, - const char *path, enum uio_seg pathseg, fhandle_t *fhp); static int kern_readlink_vp(struct vnode *vp, char *buf, enum uio_seg bufseg, size_t count, struct thread *td); static int kern_linkat_vp(struct thread *td, struct vnode *vp, int fd, @@ -4296,7 +4294,7 @@ { return (kern_getfhat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, uap->fname, - UIO_USERSPACE, uap->fhp)); + UIO_USERSPACE, uap->fhp, UIO_USERSPACE)); } #ifndef _SYS_SYSPROTO_H_ @@ -4310,7 +4308,7 @@ { return (kern_getfhat(td, 0, AT_FDCWD, uap->fname, UIO_USERSPACE, - uap->fhp)); + uap->fhp, UIO_USERSPACE)); } /* @@ -4336,12 +4334,12 @@ AT_RESOLVE_BENEATH)) != 0) return (EINVAL); return (kern_getfhat(td, uap->flags, uap->fd, uap->path, UIO_USERSPACE, - uap->fhp)); + uap->fhp, UIO_USERSPACE)); } -static int +int kern_getfhat(struct thread *td, int flags, int fd, const char *path, - enum uio_seg pathseg, fhandle_t *fhp) + enum uio_seg pathseg, fhandle_t *fhp, enum uio_seg fhseg) { struct nameidata nd; fhandle_t fh; @@ -4363,8 +4361,12 @@ fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VOP_VPTOFH(vp, &fh.fh_fid); vput(vp); - if (error == 0) - error = copyout(&fh, fhp, sizeof (fh)); + if (error == 0) { + if (fhseg == UIO_USERSPACE) + error = copyout(&fh, fhp, sizeof (fh)); + else + memcpy(fhp, &fh, sizeof(fh)); + } return (error); } @@ -4472,6 +4474,12 @@ #endif int sys_fhopen(struct thread *td, struct fhopen_args *uap) +{ + return (kern_fhopen(td, uap->u_fhp, uap->flags)); +} + +int +kern_fhopen(struct thread *td, const struct fhandle *u_fhp, int flags) { struct mount *mp; struct vnode *vp; @@ -4484,11 +4492,11 @@ if (error != 0) return (error); indx = -1; - fmode = FFLAGS(uap->flags); + fmode = FFLAGS(flags); /* why not allow a non-read/write open for our lockd? */ if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT)) return (EINVAL); - error = copyin(uap->u_fhp, &fhp, sizeof(fhp)); + error = copyin(u_fhp, &fhp, sizeof(fhp)); if (error != 0) return(error); /* find the mount point */ Index: sys/sys/syscallsubr.h =================================================================== --- sys/sys/syscallsubr.h +++ sys/sys/syscallsubr.h @@ -137,6 +137,7 @@ enum uio_seg pathseg, int uid, int gid, int flag); int kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg); int kern_fcntl_freebsd(struct thread *td, int fd, int cmd, long arg); +int kern_fhopen(struct thread *td, const struct fhandle *u_fhp, int flags); int kern_fhstat(struct thread *td, fhandle_t fh, struct stat *buf); int kern_fhstatfs(struct thread *td, fhandle_t fh, struct statfs *buf); int kern_fpathconf(struct thread *td, int fd, int name, long *valuep); @@ -150,6 +151,8 @@ enum uio_seg tptrseg); int kern_getdirentries(struct thread *td, int fd, char *buf, size_t count, off_t *basep, ssize_t *residp, enum uio_seg bufseg); +int kern_getfhat(struct thread *td, int flags, int fd, const char *path, + enum uio_seg pathseg, fhandle_t *fhp, enum uio_seg fhseg); int kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize, size_t *countp, enum uio_seg bufseg, int mode); int kern_getitimer(struct thread *, u_int, struct itimerval *);