From: Ralph Boehme Date: Mon, 12 Oct 2020 13:28:08 +0000 (+0200) Subject: smbd: optimisation using pathref fd to open real fd if possible X-Git-Tag: samba-4.14.0rc1~331 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5770cdd7635a018817418f58bd37268aedebd6a9;p=thirdparty%2Fsamba.git smbd: optimisation using pathref fd to open real fd if possible This is an optimisation that avoids going through the expensive non_widelink_open() logic a second time. It depends on a usable /proc/%d/fd/%d filesystem and this is checked and set as "can_reopen" flag by the VFS in the openat() function in the fsp. Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison --- diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 106d827dcc9..857643ddacf 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -1188,6 +1188,71 @@ static NTSTATUS fd_open_atomic(files_struct *fsp, return status; } +static NTSTATUS reopen_from_procfd(struct files_struct *fsp, + struct smb_filename *smb_fname, + int flags, + mode_t mode) +{ + struct smb_filename proc_fname; + const char *p = NULL; + char buf[PATH_MAX]; + int old_fd; + int new_fd; + NTSTATUS status; + int ret; + + if (!fsp->fsp_flags.have_proc_fds) { + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + + old_fd = fsp_get_pathref_fd(fsp); + if (old_fd == -1) { + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + + if (!fsp->fsp_flags.is_pathref) { + DBG_ERR("[%s] is not a pathref\n", + smb_fname_str_dbg(smb_fname)); +#ifdef DEVELOPER + smb_panic("Not a pathref"); +#endif + return NT_STATUS_INVALID_HANDLE; + } + + p = sys_proc_fd_path(old_fd, buf, sizeof(buf)); + if (p == NULL) { + return NT_STATUS_NO_MEMORY; + } + + proc_fname = (struct smb_filename) { + .base_name = discard_const_p(char, p), + }; + + fsp->fsp_flags.is_pathref = false; + + new_fd = SMB_VFS_OPENAT(fsp->conn, + fsp->conn->cwd_fsp, + &proc_fname, + fsp, + flags, + mode); + if (new_fd == -1) { + status = map_nt_error_from_unix(errno); + SMB_VFS_CLOSE(fsp); + fsp_set_fd(fsp, -1); + return status; + } + + ret = SMB_VFS_CLOSE(fsp); + fsp_set_fd(fsp, -1); + if (ret != 0) { + return map_nt_error_from_unix(errno); + } + + fsp_set_fd(fsp, new_fd); + return NT_STATUS_OK; +} + /**************************************************************************** Open a file. ****************************************************************************/ @@ -1366,24 +1431,33 @@ static NTSTATUS open_file(files_struct *fsp, } } - /* - * Close the existing pathref fd and set fsp_flags.is_pathref to - * false so we get a full fd this time. - */ - status = fd_close(fsp); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - fsp->fsp_flags.is_pathref = false; - /* * Actually do the open - if O_TRUNC is needed handle it * below under the share mode lock. */ - status = fd_open_atomic(fsp, - local_flags & ~O_TRUNC, - unx_mode, - p_file_created); + status = reopen_from_procfd(fsp, + smb_fname, + local_flags & ~O_TRUNC, + unx_mode); + if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) + { + /* + * Close the existing pathref fd and set the fsp flag + * is_pathref to false so we get a "normal" fd this + * time. + */ + status = fd_close(fsp); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + fsp->fsp_flags.is_pathref = false; + + status = fd_open_atomic(fsp, + local_flags & ~O_TRUNC, + unx_mode, + p_file_created); + } if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { /* * POSIX client that hit a symlink. We don't want to @@ -4562,28 +4636,32 @@ static NTSTATUS open_directory(connection_struct *conn, */ mtimespec = make_omit_timespec(); - /* - * Close the existing pathref fd and set fsp_flags.is_pathref to - * false so we get a full fd this time. - */ - status = fd_close(fsp); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - fsp->fsp_flags.is_pathref = false; - /* POSIX allows us to open a directory with O_RDONLY. */ flags = O_RDONLY; #ifdef O_DIRECTORY flags |= O_DIRECTORY; #endif - status = fd_openat(conn->cwd_fsp, fsp->fsp_name, fsp, flags, 0); + status = reopen_from_procfd(fsp, smb_dname, flags, 0); + if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + /* + * Close the existing pathref fd and set the fsp flag + * is_pathref to false so we get a "normal" fd this + * time. + */ + status = fd_close(fsp); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + fsp->fsp_flags.is_pathref = false; + + status = fd_openat(conn->cwd_fsp, fsp->fsp_name, fsp, flags, 0); + } if (!NT_STATUS_IS_OK(status)) { - DBG_INFO("Could not open fd for " - "%s (%s)\n", - smb_fname_str_dbg(smb_dname), - nt_errstr(status)); + DBG_INFO("Could not open fd for%s (%s)\n", + smb_fname_str_dbg(smb_dname), + nt_errstr(status)); return status; }