smbd: Simplify dptr_ReadDirName()
authorVolker Lendecke <vl@samba.org>
Tue, 13 Jun 2023 13:42:19 +0000 (15:42 +0200)
committerJeremy Allison <jra@samba.org>
Tue, 13 Jun 2023 23:33:39 +0000 (23:33 +0000)
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 <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
source3/smbd/dir.c

index 428ecd45d2032c007c9d02fc7f52bb41b78f164b..b1d595d46f11a190be62b6e5218f17d0043356cc 100644 (file)
@@ -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;
 }
 
 /****************************************************************************