s3: smbd: Currently if getwd() fails after a chdir(), we panic.
authorJeremy Allison <jra@samba.org>
Wed, 4 Oct 2017 19:43:22 +0000 (12:43 -0700)
committerJeremy Allison <jra@samba.org>
Mon, 9 Oct 2017 21:01:17 +0000 (23:01 +0200)
Change this to return to the previous $cwd, and return -1 for the chdir().

If the return to the previous $cwd fails, still panic as we
can't return an unknown state.

Also do early return from failing SMB_VFS_CHDIR, reducing indentation level

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13027

Signed-off-by: Jeremy Allison <jra@samba.org>
Signed-off-by: Ralph Böhme <slow@samba.org>
source3/smbd/vfs.c

index dc0d14dc82ffd8e12a166a40c1cdcde71984c446..d4fccb7eda9362cded1616124fb58138da3dce84 100644 (file)
@@ -858,6 +858,8 @@ const char *vfs_readdirname(connection_struct *conn, void *p,
 int vfs_ChDir(connection_struct *conn, const struct smb_filename *smb_fname)
 {
        int ret;
+       int saved_errno = 0;
+       struct smb_filename *saved_cwd = NULL;
 
        if (!LastDir) {
                LastDir = SMB_STRDUP("");
@@ -872,23 +874,80 @@ int vfs_ChDir(connection_struct *conn, const struct smb_filename *smb_fname)
                return 0;
        }
 
+       if (conn->cwd_fname != NULL) {
+               /*
+                * Save off where we are in case we need to return
+                * on vfs_GetWd() failure after successful SMB_VFS_CHDIR().
+                */
+               saved_cwd = cp_smb_filename(conn, conn->cwd_fname);
+               if (saved_cwd == NULL) {
+                       return -1;
+               }
+       }
+
        DEBUG(4,("vfs_ChDir to %s\n", smb_fname->base_name));
 
        ret = SMB_VFS_CHDIR(conn, smb_fname);
-       if (ret == 0) {
-               /* Global cache. */
-               SAFE_FREE(LastDir);
-               LastDir = SMB_STRDUP(smb_fname->base_name);
-
-               /* conn cache. */
-               TALLOC_FREE(conn->cwd_fname);
-               conn->cwd_fname = vfs_GetWd(conn, conn);
-               if (conn->cwd_fname == NULL) {
-                       smb_panic("con->cwd getwd failed\n");
+       if (ret != 0) {
+               saved_errno = errno;
+               TALLOC_FREE(saved_cwd);
+               errno = saved_errno;
+               return -1;
+       }
+
+       /*
+        * Always replace conn->cwd_fname. We
+        * don't know if it's been modified by
+        * VFS modules in the stack.
+        */
+
+       /* conn cache. */
+       TALLOC_FREE(conn->cwd_fname);
+       conn->cwd_fname = vfs_GetWd(conn, conn);
+       if (conn->cwd_fname == NULL) {
+               /*
+                * vfs_GetWd() failed.
+                * We must be able to read cwd.
+                * Return to original directory
+                * and return -1.
+                */
+               saved_errno = errno;
+
+               if (saved_cwd == NULL) {
+                       /*
+                        * Failed on the very first chdir()+getwd()
+                        * for this connection. We can't
+                        * continue.
+                        */
+                       smb_panic("conn->cwd getwd failed\n");
                        /* NOTREACHED */
                        return -1;
                }
-               DEBUG(4,("vfs_ChDir got %s\n",conn->cwd_fname->base_name));
+
+               /* Return to the previous $cwd. */
+               ret = SMB_VFS_CHDIR(conn, saved_cwd);
+               if (ret != 0) {
+                       smb_panic("conn->cwd getwd failed\n");
+                       /* NOTREACHED */
+                       return -1;
+               }
+               /* Restore original conn->cwd_fname. */
+               conn->cwd_fname = saved_cwd;
+               errno = saved_errno;
+               /* And fail the chdir(). */
+               return -1;
+       }
+
+       /* vfs_GetWd() succeeded. */
+       /* Replace global cache. */
+       SAFE_FREE(LastDir);
+       LastDir = SMB_STRDUP(smb_fname->base_name);
+
+       DEBUG(4,("vfs_ChDir got %s\n", conn->cwd_fname->base_name));
+
+       TALLOC_FREE(saved_cwd);
+       if (saved_errno != 0) {
+               errno = saved_errno;
        }
        return ret;
 }