]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smbd: Simplify non_widelink_open()
authorVolker Lendecke <vl@samba.org>
Fri, 4 Mar 2022 13:56:24 +0000 (14:56 +0100)
committerJeremy Allison <jra@samba.org>
Thu, 10 Mar 2022 19:19:06 +0000 (19:19 +0000)
Don't depend on fsp->fsp_flags.is_directory: We can always take the
parent directory fname, chdir into it and openat(O_PATH|O_NOFOLLOW)
the relative file name. To properly handle the symlink case without
having O_PATH, upon failure we need the call to
fstatat(AT_SYMLINK_NOFOLLOW) as a replacement for the fstat-call that
we can do when we successfully opened the relative file name with
O_NOFOLLOW.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Thu Mar 10 19:19:06 UTC 2022 on sn-devel-184

source3/smbd/open.c

index baae51f157e6d296fc8accb22dd9caaab10f58ae..b3df09852390e776295a881a777523ae53dd1f2b 100644 (file)
@@ -667,39 +667,32 @@ static NTSTATUS non_widelink_open(const struct files_struct *dirfsp,
        struct smb_filename *oldwd_fname = NULL;
        struct smb_filename *parent_dir_fname = NULL;
        bool have_opath = false;
+       bool is_share_root = false;
        int ret;
 
 #ifdef O_PATH
        have_opath = true;
 #endif
 
-       if (dirfsp == conn->cwd_fsp) {
-               if (fsp->fsp_flags.is_directory) {
-                       parent_dir_fname = cp_smb_filename(talloc_tos(), smb_fname);
-                       if (parent_dir_fname == NULL) {
-                               status = NT_STATUS_NO_MEMORY;
-                               goto out;
-                       }
+       if (smb_fname->base_name[0] == '/') {
+               const char *connpath = SMB_VFS_CONNECTPATH(conn, smb_fname);
+               int cmp = strcmp(connpath, smb_fname->base_name);
 
-                       smb_fname_rel = synthetic_smb_fname(parent_dir_fname,
-                                                           ".",
-                                                           smb_fname->stream_name,
-                                                           &smb_fname->st,
-                                                           smb_fname->twrp,
-                                                           smb_fname->flags);
-                       if (smb_fname_rel == NULL) {
-                               status = NT_STATUS_NO_MEMORY;
-                               goto out;
-                       }
-               } else {
-                       status = SMB_VFS_PARENT_PATHNAME(fsp->conn,
-                                                        talloc_tos(),
-                                                        smb_fname,
-                                                        &parent_dir_fname,
-                                                        &smb_fname_rel);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               goto out;
-                       }
+               if (cmp == 0) {
+                       is_share_root = true;
+               }
+       }
+
+       if (!is_share_root && (dirfsp == conn->cwd_fsp)) {
+               struct smb_filename *smb_fname_dot = NULL;
+
+               status = SMB_VFS_PARENT_PATHNAME(fsp->conn,
+                                                talloc_tos(),
+                                                smb_fname,
+                                                &parent_dir_fname,
+                                                &smb_fname_rel);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto out;
                }
 
                if (!ISDOT(parent_dir_fname->base_name)) {
@@ -716,8 +709,21 @@ static NTSTATUS non_widelink_open(const struct files_struct *dirfsp,
                        }
                }
 
+               smb_fname_dot = synthetic_smb_fname(
+                       parent_dir_fname,
+                       ".",
+                       NULL,
+                       NULL,
+                       0,
+                       smb_fname->flags);
+               if (smb_fname_dot == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto out;
+               }
+
                /* Ensure the relative path is below the share. */
-               status = check_reduced_name(conn, parent_dir_fname, smb_fname_rel);
+               status = check_reduced_name(conn, parent_dir_fname, smb_fname_dot);
+               TALLOC_FREE(smb_fname_dot);
                if (!NT_STATUS_IS_OK(status)) {
                        goto out;
                }
@@ -765,6 +771,11 @@ static NTSTATUS non_widelink_open(const struct files_struct *dirfsp,
                smb_fname_rel = smb_fname;
        }
 
+       if (!is_share_root) {
+               char *slash = strchr_m(smb_fname_rel->base_name, '/');
+               SMB_ASSERT(slash == NULL);
+       }
+
        flags |= O_NOFOLLOW;
 
        fd = SMB_VFS_OPENAT(conn,
@@ -778,6 +789,25 @@ static NTSTATUS non_widelink_open(const struct files_struct *dirfsp,
        }
        fsp_set_fd(fsp, fd);
 
+       if ((fd == -1) &&
+           NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
+           fsp->fsp_flags.is_pathref &&
+           !have_opath) {
+               ret = SMB_VFS_FSTATAT(
+                       fsp->conn,
+                       dirfsp,
+                       smb_fname_rel,
+                       &fsp->fsp_name->st,
+                       AT_SYMLINK_NOFOLLOW);
+               if (ret == -1) {
+                       status = map_nt_error_from_unix(errno);
+                       DBG_DEBUG("fstatat(%s) failed: %s\n",
+                                 smb_fname_str_dbg(smb_fname),
+                                 strerror(errno));
+                       goto out;
+               }
+       }
+
        if (fd != -1) {
                ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
                if (ret != 0) {