https://bugzilla.samba.org/show_bug.cgi?id=15376 # 2024-08-05 # NOTE: Patch is partially split apart from the copy in net/samba419 due to Samba upstream # making some Linux-specific changes. FreeBSD-specific linprocfs mount path bits are # moved to a separate patch due to upstream hardcoding the Linux-specific procfs # path in multiple places in commit 9f63fad392f3: # https://git.samba.org/?p=samba.git;a=commitdiff;h=9f63fad392f3cff34d6a8e318e0427499170c417 diff -Naurp a/source3/include/proto.h b/source3/include/proto.h --- a/source3/include/proto.h 2024-02-02 04:33:51.168489200 -0500 +++ b/source3/include/proto.h 2024-08-05 21:25:56.723092000 -0400 @@ -211,6 +211,10 @@ char *sys_proc_fd_path(int fd, struct sys_proc_fd_path bool sys_have_proc_fds(void); char *sys_proc_fd_path(int fd, struct sys_proc_fd_path_buf *buf); +int sys_open_real_fd_from_pathref_fd(int fd, + int *mfd, + int flags); + struct stat; void init_stat_ex_from_stat (struct stat_ex *dst, const struct stat *src, diff -Naurp a/source3/lib/system.c b/source3/lib/system.c --- a/source3/lib/system.c 2024-02-02 04:33:51.188489400 -0500 +++ b/source3/lib/system.c 2024-08-05 21:25:56.723571000 -0400 @@ -1074,3 +1074,23 @@ char *sys_proc_fd_path(int fd, struct sys_proc_fd_path return buf->buf; } + +/* Helper function that opens a usable fd for accessing data + (metadata & content) from a pathref fd */ +int sys_open_real_fd_from_pathref_fd(int fd, int *rfd, int flags) +{ + int tfd; + +#if defined(HAVE_OPENAT) && defined(O_EMPTY_PATH) + /* This works for FreeBSD 13+ atleast */ + tfd = openat(fd, "", O_EMPTY_PATH|flags); + if (tfd < 0) { + return errno; + } + + *rfd = tfd; + return 0; +#else + return ENOSYS; +#endif +} diff -Naurp a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c --- a/source3/modules/vfs_default.c 2024-08-02 07:54:09.629892300 -0400 +++ b/source3/modules/vfs_default.c 2024-08-05 21:25:56.724593000 -0400 @@ -2688,7 +2688,7 @@ static int vfswrap_fchmod(vfs_handle_struct *handle, f static int vfswrap_fchmod(vfs_handle_struct *handle, files_struct *fsp, mode_t mode) { - int result; + int result, fd, real_fd; START_PROFILE(syscall_fchmod); @@ -2698,8 +2698,9 @@ static int vfswrap_fchmod(vfs_handle_struct *handle, f return result; } + fd = fsp_get_pathref_fd(fsp); + if (fsp->fsp_flags.have_proc_fds) { - int fd = fsp_get_pathref_fd(fsp); struct sys_proc_fd_path_buf buf; result = chmod(sys_proc_fd_path(fd, &buf), mode); @@ -2708,6 +2709,17 @@ static int vfswrap_fchmod(vfs_handle_struct *handle, f return result; } + if (sys_open_real_fd_from_pathref_fd(fd, &real_fd, O_RDONLY|O_NONBLOCK) == 0) { + int saved_errno; + + result = fchmod(real_fd, mode); + saved_errno = errno; + close(real_fd); + errno = saved_errno; + END_PROFILE(syscall_fchmod); + return result; + } + /* * This is no longer a handle based call. */ @@ -2720,7 +2732,7 @@ static int vfswrap_fchown(vfs_handle_struct *handle, f static int vfswrap_fchown(vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid) { #ifdef HAVE_FCHOWN - int result; + int result, fd, real_fd; START_PROFILE(syscall_fchown); if (!fsp->fsp_flags.is_pathref) { @@ -2729,8 +2741,9 @@ static int vfswrap_fchown(vfs_handle_struct *handle, f return result; } + fd = fsp_get_pathref_fd(fsp); + if (fsp->fsp_flags.have_proc_fds) { - int fd = fsp_get_pathref_fd(fsp); struct sys_proc_fd_path_buf buf; result = chown(sys_proc_fd_path(fd, &buf), uid, gid); @@ -2739,6 +2752,17 @@ static int vfswrap_fchown(vfs_handle_struct *handle, f return result; } + if (sys_open_real_fd_from_pathref_fd(fd, &real_fd, O_RDONLY|O_NONBLOCK) == 0) { + int saved_errno; + + result = fchown(real_fd, uid, gid); + saved_errno = errno; + close(real_fd); + errno = saved_errno; + END_PROFILE(syscall_fchown); + return result; + } + /* * This is no longer a handle based call. */ @@ -2812,7 +2836,7 @@ static int vfswrap_fntimes(vfs_handle_struct *handle, files_struct *fsp, struct smb_file_time *ft) { - int result = -1; + int result = -1, fd, real_fd; struct timespec ts[2]; struct timespec *times = NULL; @@ -2857,8 +2881,9 @@ static int vfswrap_fntimes(vfs_handle_struct *handle, goto out; } + fd = fsp_get_pathref_fd(fsp); + if (fsp->fsp_flags.have_proc_fds) { - int fd = fsp_get_pathref_fd(fsp); struct sys_proc_fd_path_buf buf; result = utimensat(AT_FDCWD, @@ -2869,6 +2894,16 @@ static int vfswrap_fntimes(vfs_handle_struct *handle, goto out; } + if (sys_open_real_fd_from_pathref_fd(fd, &real_fd, O_RDONLY|O_NONBLOCK) == 0) { + int saved_errno; + + result = futimens(real_fd, times); + saved_errno = errno; + close(real_fd); + errno = saved_errno; + goto out; + } + /* * The fd is a pathref (opened with O_PATH) and there isn't fd to * path translation mechanism. Fallback to path based call. @@ -3272,6 +3307,7 @@ static int vfswrap_fchflags(vfs_handle_struct *handle, { #ifdef HAVE_FCHFLAGS int fd = fsp_get_pathref_fd(fsp); + int real_fd; SMB_ASSERT(!fsp_is_alternate_stream(fsp)); @@ -3285,6 +3321,16 @@ static int vfswrap_fchflags(vfs_handle_struct *handle, return chflags(sys_proc_fd_path(fd, &buf), flags); } + if (sys_open_real_fd_from_pathref_fd(fd, &real_fd, O_RDONLY|O_NONBLOCK) == 0) { + int saved_errno, result; + + result = fchflags(real_fd, flags); + saved_errno = errno; + close(real_fd); + errno = saved_errno; + return result; + } + /* * This is no longer a handle based call. */ @@ -3513,6 +3559,7 @@ static ssize_t vfswrap_fgetxattr(struct vfs_handle_str size_t size) { int fd = fsp_get_pathref_fd(fsp); + int real_fd; SMB_ASSERT(!fsp_is_alternate_stream(fsp)); @@ -3526,6 +3573,16 @@ static ssize_t vfswrap_fgetxattr(struct vfs_handle_str return getxattr(sys_proc_fd_path(fd, &buf), name, value, size); } + if (sys_open_real_fd_from_pathref_fd(fd, &real_fd, O_RDONLY|O_NONBLOCK) == 0) { + int saved_errno, result; + + result = fgetxattr(real_fd, name, value, size); + saved_errno = errno; + close(real_fd); + errno = saved_errno; + return result; + } + /* * This is no longer a handle based call. */ @@ -3833,6 +3890,7 @@ static ssize_t vfswrap_flistxattr(struct vfs_handle_st static ssize_t vfswrap_flistxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, char *list, size_t size) { int fd = fsp_get_pathref_fd(fsp); + int real_fd; SMB_ASSERT(!fsp_is_alternate_stream(fsp)); @@ -3846,6 +3904,16 @@ static ssize_t vfswrap_flistxattr(struct vfs_handle_st return listxattr(sys_proc_fd_path(fd, &buf), list, size); } + if (sys_open_real_fd_from_pathref_fd(fd, &real_fd, O_RDONLY|O_NONBLOCK) == 0) { + int saved_errno, result; + + result = flistxattr(real_fd, list, size); + saved_errno = errno; + close(real_fd); + errno = saved_errno; + return result; + } + /* * This is no longer a handle based call. */ @@ -3855,6 +3923,7 @@ static int vfswrap_fremovexattr(struct vfs_handle_stru static int vfswrap_fremovexattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name) { int fd = fsp_get_pathref_fd(fsp); + int real_fd; SMB_ASSERT(!fsp_is_alternate_stream(fsp)); @@ -3868,6 +3937,16 @@ static int vfswrap_fremovexattr(struct vfs_handle_stru return removexattr(sys_proc_fd_path(fd, &buf), name); } + if (sys_open_real_fd_from_pathref_fd(fd, &real_fd, O_RDONLY|O_NONBLOCK) == 0) { + int saved_errno, result; + + result = fremovexattr(real_fd, name); + saved_errno = errno; + close(real_fd); + errno = saved_errno; + return result; + } + /* * This is no longer a handle based call. */ @@ -3877,6 +3956,7 @@ static int vfswrap_fsetxattr(struct vfs_handle_struct static int vfswrap_fsetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, const void *value, size_t size, int flags) { int fd = fsp_get_pathref_fd(fsp); + int real_fd; SMB_ASSERT(!fsp_is_alternate_stream(fsp)); @@ -3892,6 +3972,16 @@ static int vfswrap_fsetxattr(struct vfs_handle_struct value, size, flags); + } + + if (sys_open_real_fd_from_pathref_fd(fd, &real_fd, O_RDONLY|O_NONBLOCK) == 0) { + int saved_errno, result; + + result = fsetxattr(real_fd, name, value, size, flags); + saved_errno = errno; + close(real_fd); + errno = saved_errno; + return result; } /* diff -Naurp a/source3/modules/vfs_zfsacl.c b/source3/modules/vfs_zfsacl.c --- a/source3/modules/vfs_zfsacl.c 2024-02-02 04:33:51.236489800 -0500 +++ b/source3/modules/vfs_zfsacl.c 2024-08-05 21:25:56.724941000 -0400 @@ -234,13 +234,39 @@ static bool zfs_process_smbacl(vfs_handle_struct *hand SMB_ASSERT(i == naces); - /* store acl */ - fd = fsp_get_pathref_fd(fsp); - if (fd == -1) { - errno = EBADF; - return false; + if (!fsp->fsp_flags.is_pathref) { + rv = facl(fsp_get_io_fd(fsp), ACE_SETACL, naces, acebuf); + } else { + const char *procfd_p = NULL; + char buf[PATH_MAX]; + + fd = fsp_get_pathref_fd(fsp); + if (fsp->fsp_flags.have_proc_fds && (procfd_p = sys_proc_fd_path(fd, &buf))) { + rv = acl(procfd_p, ACE_SETACL, naces, acebuf); + } else { + int real_fd; + + fd = fsp_get_pathref_fd(fsp); + + /* First try this for versions of FreeBSD 13+ that allows facl() on O_PATH fd's */ + rv = facl(fd, ACE_SETACL, naces, acebuf); + + if (rv < 0 && errno == EBADF && + sys_open_real_fd_from_pathref_fd(fd, &real_fd, O_RDONLY|O_NONBLOCK) == 0) { + /* Works on FreeBSD 13+ */ + int saved_errno; + + rv = facl(real_fd, ACE_SETACL, naces, acebuf); + saved_errno = errno; + close(real_fd); + errno = saved_errno; + } else { + /* Last ditch fallback */ + rv = acl(fsp->fsp_name->base_name, ACE_SETACL, naces, acebuf); + } + } } - rv = facl(fd, ACE_SETACL, naces, acebuf); + if (rv != 0) { if(errno == ENOSYS) { DEBUG(9, ("acl(ACE_SETACL, %s): Operation is not " @@ -284,14 +310,39 @@ static int fget_zfsacl(TALLOC_CTX *mem_ctx, { int naces, rv; ace_t *acebuf = NULL; - int fd; + int fd = -1; + const char *procfd_p = NULL; + char buf[PATH_MAX]; - fd = fsp_get_pathref_fd(fsp); - if (fd == -1) { - errno = EBADF; - return -1; + if (!fsp->fsp_flags.is_pathref) { + naces = facl(fsp_get_io_fd(fsp), ACE_GETACLCNT, 0, NULL); + } else { + fd = fsp_get_pathref_fd(fsp); + + if (fsp->fsp_flags.have_proc_fds && (procfd_p = sys_proc_fd_path(fd, &buf))) { + /* If we have procfd support, try this first */ + naces = acl(procfd_p, ACE_GETACLCNT, 0, NULL); + } else { + int real_fd; + + /* First try this for versions of FreeBSD 13+ that allows facl() on O_PATH fd's */ + naces = facl(fd, ACE_GETACLCNT, 0, NULL); + if (naces < 0 && errno == EBADF && + sys_open_real_fd_from_pathref_fd(fd, &real_fd, O_RDONLY|O_NONBLOCK) == 0) { + /* Works on FreeBSD 13+ */ + int saved_errno; + + naces = facl(real_fd, ACE_GETACLCNT, 0, NULL); + saved_errno = errno; + close(real_fd); + errno = saved_errno; + } else { + /* Last ditch fallback */ + naces = acl(fsp->fsp_name->base_name, ACE_GETACLCNT, 0, NULL); + } + } } - naces = facl(fd, ACE_GETACLCNT, 0, NULL); + if (naces == -1) { int dbg_level = 10; @@ -309,7 +360,32 @@ static int fget_zfsacl(TALLOC_CTX *mem_ctx, return -1; } - rv = facl(fd, ACE_GETACL, naces, acebuf); + if (!fsp->fsp_flags.is_pathref) { + rv = facl(fsp_get_io_fd(fsp), ACE_GETACL, naces, acebuf); + } else { + if (procfd_p) { + rv = acl(procfd_p, ACE_GETACL, naces, acebuf); + } else { + int real_fd; + + /* First try this for versions of FreeBSD that allows facl() on O_PATH fd's */ + rv = facl(fd, ACE_GETACL, naces, acebuf); + if (rv < 0 && errno == EBADF && + sys_open_real_fd_from_pathref_fd(fd, &real_fd, O_RDONLY|O_NONBLOCK) == 0) { + /* Works on FreeBSD 13+ */ + int saved_errno; + + rv = facl(real_fd, ACE_GETACL, naces, acebuf); + saved_errno = errno; + close(real_fd); + errno = saved_errno; + } else { + /* Last ditch fallback */ + rv = acl(fsp->fsp_name->base_name, ACE_GETACL, naces, acebuf); + } + } + } + if (rv == -1) { DBG_DEBUG("acl(ACE_GETACL, %s): %s\n", fsp_str_dbg(fsp), strerror(errno)); diff -Naurp a/source3/smbd/open.c b/source3/smbd/open.c --- a/source3/smbd/open.c 2024-08-02 07:54:09.637892500 -0400 +++ b/source3/smbd/open.c 2024-08-05 21:27:26.052148000 -0400 @@ -1165,47 +1165,52 @@ static NTSTATUS reopen_from_fsp(struct files_struct *d bool *p_file_created) { NTSTATUS status; + int new_fd; int old_fd; - if (fsp->fsp_flags.have_proc_fds && - ((old_fd = fsp_get_pathref_fd(fsp)) != -1)) { + old_fd = fsp_get_pathref_fd(fsp); + if (old_fd == -1) { + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } - struct sys_proc_fd_path_buf buf; - struct smb_filename proc_fname = (struct smb_filename){ - .base_name = sys_proc_fd_path(old_fd, &buf), - }; - mode_t mode = fsp->fsp_name->st.st_ex_mode; - int new_fd; + if (sys_open_real_fd_from_pathref_fd(old_fd, &new_fd, how->flags) != 0) { + if (fsp->fsp_flags.have_proc_fds) { + struct sys_proc_fd_path_buf buf; + struct smb_filename proc_fname = (struct smb_filename){ + .base_name = sys_proc_fd_path(old_fd, &buf), + }; + mode_t mode = fsp->fsp_name->st.st_ex_mode; - SMB_ASSERT(fsp->fsp_flags.is_pathref); + SMB_ASSERT(fsp->fsp_flags.is_pathref); - if (S_ISLNK(mode)) { - return NT_STATUS_STOPPED_ON_SYMLINK; - } - if (!(S_ISREG(mode) || S_ISDIR(mode))) { - return NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED; - } + if (S_ISLNK(mode)) { + return NT_STATUS_STOPPED_ON_SYMLINK; + } + if (!(S_ISREG(mode) || S_ISDIR(mode))) { + return NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED; + } - fsp->fsp_flags.is_pathref = false; + new_fd = SMB_VFS_OPENAT(fsp->conn, + fsp->conn->cwd_fsp, + &proc_fname, + fsp, + how); + if (new_fd == -1) { + status = map_nt_error_from_unix(errno); + fd_close(fsp); + return status; + } - new_fd = SMB_VFS_OPENAT(fsp->conn, - fsp->conn->cwd_fsp, - &proc_fname, - fsp, - how); - if (new_fd == -1) { - status = map_nt_error_from_unix(errno); - fd_close(fsp); - return status; - } + status = fd_close(fsp); + if (!NT_STATUS_IS_OK(status)) { + return status; + } - status = fd_close(fsp); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + fsp_set_fd(fsp, new_fd); + fsp->fsp_flags.is_pathref = false; - fsp_set_fd(fsp, new_fd); - return NT_STATUS_OK; + return NT_STATUS_OK; + } } /*