From: Volker Lendecke Date: Tue, 13 Jun 2023 13:42:19 +0000 (+0200) Subject: smbd: Simplify dptr_ReadDirName() X-Git-Tag: talloc-2.4.1~425 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=91c76f6514b02f7f73b28fd4915fefd6a772f7f1;p=thirdparty%2Fsamba.git smbd: Simplify dptr_ReadDirName() While trying to understand the ReadDirName() at the end of dptr_ReadDirName() in a code path that was supposed to be just a "stat"-style readdir with a non-wcard mask I came to the conclusion that this was there to find dptr->wcard with a mangled name. get_real_filename_at() already takes care of name mangling, so I think I could eliminate a source of confusion by using it. Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 428ecd45d20..b1d595d46f1 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -360,11 +360,13 @@ void dptr_RewindDir(struct dptr_struct *dptr) { long offset; RewindDir(dptr->dir_hnd, &offset); + dptr->did_stat = false; } void dptr_SeekDir(struct dptr_struct *dptr, long offset) { SeekDir(dptr->dir_hnd, offset); + dptr->did_stat = false; } long dptr_TellDir(struct dptr_struct *dptr) @@ -411,19 +413,21 @@ static char *dptr_ReadDirName(TALLOC_CTX *ctx, long *poffset, SMB_STRUCT_STAT *pst) { + struct smb_Dir *dir_hnd = dptr->dir_hnd; + struct files_struct *dir_fsp = dir_hnd->fsp; + struct smb_filename *dir_name = dir_fsp->fsp_name; struct smb_filename smb_fname_base; - char *name = NULL; - const char *name_temp = NULL; - char *talloced = NULL; - char *pathreal = NULL; - char *found_name = NULL; - NTSTATUS status; + bool retry_scanning = false; + int ret; + int flags = 0; SET_STAT_INVALID(*pst); - if (dptr->has_wild || dptr->did_stat) { - name_temp = ReadDirName(dptr->dir_hnd, poffset, pst, - &talloced); + if (dptr->has_wild) { + const char *name_temp = NULL; + char *talloced = NULL; + + name_temp = ReadDirName(dir_hnd, poffset, pst, &talloced); if (name_temp == NULL) { return NULL; } @@ -433,93 +437,69 @@ static char *dptr_ReadDirName(TALLOC_CTX *ctx, return talloc_strdup(ctx, name_temp); } - /* If poffset is -1 then we know we returned this name before and we - * have no wildcards. We're at the end of the directory. */ - if (*poffset == END_OF_DIRECTORY_OFFSET) { + if (dptr->did_stat) { + /* + * No wildcard, this is not a real directory traverse + * but a "stat" call behind a query_directory. We've + * been here, nothing else to look at. + */ return NULL; } - - /* We know the stored wcard contains no wildcard characters. - * See if we can match with a stat call. If we can't, then set - * did_stat to true to ensure we only do this once and keep - * searching. */ - dptr->did_stat = true; - pathreal = talloc_asprintf(ctx, - "%s/%s", - dptr->dir_hnd->dir_smb_fname->base_name, - dptr->wcard); - if (!pathreal) - return NULL; - /* Create an smb_filename with stream_name == NULL. */ - smb_fname_base = (struct smb_filename) { - .base_name = pathreal, - .flags = dptr->dir_hnd->fsp->fsp_name->flags, - .twrp = dptr->dir_hnd->fsp->fsp_name->twrp, + smb_fname_base = (struct smb_filename){ + .base_name = dptr->wcard, + .flags = dir_name->flags, + .twrp = dir_name->twrp, }; - if (vfs_stat(dptr->conn, &smb_fname_base) == 0) { - *pst = smb_fname_base.st; - name = talloc_strdup(ctx, dptr->wcard); - goto clean; - } else { - /* If we get any other error than ENOENT or ENOTDIR - then the file exists we just can't stat it. */ - if (errno != ENOENT && errno != ENOTDIR) { - name = talloc_strdup(ctx, dptr->wcard); - goto clean; - } + if (dir_name->flags & SMB_FILENAME_POSIX_PATH) { + flags |= AT_SYMLINK_NOFOLLOW; + } + + ret = SMB_VFS_FSTATAT(dptr->conn, dir_fsp, &smb_fname_base, pst, flags); + if (ret == 0) { + return talloc_strdup(ctx, dptr->wcard); } - /* Stat failed. We know this is authoritative if we are - * providing case sensitive semantics or the underlying - * filesystem is case sensitive. + /* + * If we get any other error than ENOENT or ENOTDIR + * then the file exists, we just can't stat it. */ - if (dptr->dir_hnd->case_sensitive || - !(dptr->conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) - { - goto clean; + if (errno != ENOENT && errno != ENOTDIR) { + return talloc_strdup(ctx, dptr->wcard); } /* - * Try case-insensitive stat if the fs has the ability. This avoids - * scanning the whole directory. + * A scan will find the long version of a mangled name as + * wildcard. + */ + retry_scanning |= mangle_is_mangled(dptr->wcard, dptr->conn->params); + + /* + * Also retry scanning if the client requested case + * insensitive semantics and the file system does not provide + * it. */ - status = SMB_VFS_GET_REAL_FILENAME_AT(dptr->conn, - dptr->dir_hnd->fsp, + retry_scanning |= + (!dir_hnd->case_sensitive && + (dptr->conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)); + + if (retry_scanning) { + char *found_name = NULL; + NTSTATUS status; + + status = get_real_filename_at(dir_fsp, dptr->wcard, ctx, &found_name); - if (NT_STATUS_IS_OK(status)) { - name = found_name; - goto clean; - } - if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { - /* The case-insensitive lookup was authoritative. */ - goto clean; - } - - TALLOC_FREE(pathreal); - - name_temp = ReadDirName(dptr->dir_hnd, poffset, pst, &talloced); - if (name_temp == NULL) { - return NULL; - } - if (talloced != NULL) { - return talloc_move(ctx, &talloced); + if (NT_STATUS_IS_OK(status)) { + return found_name; + } } - return talloc_strdup(ctx, name_temp); -clean: - TALLOC_FREE(pathreal); -ret: - /* We need to set the underlying dir_hnd offset to -1 - * also as this function is usually called with the - * output from TellDir. */ - dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET; - return name; + return NULL; } /****************************************************************************