From: Volker Lendecke Date: Sun, 13 Mar 2022 15:31:20 +0000 (+0100) Subject: smbd: Add get_real_filename_at() X-Git-Tag: talloc-2.3.4~328 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c4d4fa68d61dcc037e5cdfc67bd6b98a93929a83;p=thirdparty%2Fsamba.git smbd: Add get_real_filename_at() Make get_real_filename() a wrapper. Right now shadow_copy2 does a fallback to do get_real_filename() on the twrp=0 tree in case of snapdirseverywhere because snapdirs can be somewhere deep in the tree, and doing that correctly would be a full-tree walk. I'd say that snapdirseverywhere is impossible to implement if you want symlink safety, i.e. careful top-down tree traversal together with snapdirseverywhere. If you have snapdirseverywhere you need to pass down the full path very deep down, which contradicts our fd-based approach we want to take. Also, I believe that our test does not 100% correctly reflect what actually is there: My understanding is that if you activate snapdirseverywhere for example in GPFS, you see all snapshots at every level (this would need to be verified). Our test does something more nasty: It creates and tests a specific snapshot only at one place deep in the directory hierarchy, which makes it impossible to find without the full path. This is all a big mess, but for now we need to deal with it. This adds the twrp=0 fallback to core smbd, but I don't see any other way to do that properly. And I do want a fd-based getrealfilename.... Signed-off-by: Volker Lendecke Reviewed-by: Ralph Boehme --- diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 552a87f7025..b1e3402e371 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -1680,32 +1680,26 @@ NTSTATUS get_real_filename_full_scan(connection_struct *conn, fallback. ****************************************************************************/ -static NTSTATUS get_real_filename(connection_struct *conn, - struct smb_filename *path, - const char *name, - TALLOC_CTX *mem_ctx, - char **found_name) +static NTSTATUS get_real_filename_at(struct files_struct *dirfsp, + const char *name, + TALLOC_CTX *mem_ctx, + char **found_name) { + struct connection_struct *conn = dirfsp->conn; NTSTATUS status; bool mangled; mangled = mangle_is_mangled(name, conn->params); if (mangled) { - return get_real_filename_full_scan(conn, - path->base_name, - name, - mangled, - mem_ctx, - found_name); + status = get_real_filename_full_scan_at( + dirfsp, name, mangled, mem_ctx, found_name); + return status; } /* Try the vfs first to take advantage of case-insensitive stat. */ - status = SMB_VFS_GET_REAL_FILENAME(conn, - path, - name, - mem_ctx, - found_name); + status = SMB_VFS_GET_REAL_FILENAME_AT( + dirfsp->conn, dirfsp, name, mem_ctx, found_name); /* * If the case-insensitive stat was successful, or returned an error @@ -1717,12 +1711,70 @@ static NTSTATUS get_real_filename(connection_struct *conn, return status; } - return get_real_filename_full_scan(conn, - path->base_name, - name, - mangled, - mem_ctx, - found_name); + status = get_real_filename_full_scan_at( + dirfsp, name, mangled, mem_ctx, found_name); + return status; +} + +static NTSTATUS get_real_filename(connection_struct *conn, + struct smb_filename *path, + const char *name, + TALLOC_CTX *mem_ctx, + char **found_name) +{ + struct smb_filename *smb_dname = NULL; + NTSTATUS status; + + smb_dname = cp_smb_filename_nostream(talloc_tos(), path); + if (smb_dname == NULL) { + return NT_STATUS_NO_MEMORY; + } + +again: + status = openat_pathref_fsp(conn->cwd_fsp, smb_dname); + + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) && + S_ISLNK(smb_dname->st.st_ex_mode)) { + status = NT_STATUS_STOPPED_ON_SYMLINK; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) && + (smb_dname->twrp != 0)) { + /* + * Retry looking at the non-snapshot path, copying the + * fallback mechanism from vfs_shadow_copy2.c when + * shadow_copy2_convert() fails. This path-based + * routine get_real_filename() should go away and be + * replaced with a fd-based one, so spoiling it with a + * shadow_copy2 specific mechanism should not be too + * bad. + */ + smb_dname->twrp = 0; + goto again; + } + + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("openat_pathref_fsp(%s) failed: %s\n", + smb_fname_str_dbg(smb_dname), + nt_errstr(status)); + + /* + * ENOTDIR and ELOOP both map to + * NT_STATUS_OBJECT_PATH_NOT_FOUND in the filename + * walk. + */ + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY) || + NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { + status = NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + + return status; + } + + status = get_real_filename_at( + smb_dname->fsp, name, mem_ctx, found_name); + TALLOC_FREE(smb_dname); + return status; } static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx,