Factor out the recent changes into a function - check_parent_exists().
authorJeremy Allison <jra@samba.org>
Sat, 11 Sep 2010 06:28:15 +0000 (23:28 -0700)
committerJeremy Allison <jra@samba.org>
Sat, 11 Sep 2010 06:28:15 +0000 (23:28 -0700)
Fix this to ensure that if "start" is manipulated, then "dirpath"
is changed also.

Ensures that when the path:

/a/long/file/name/path.txt

is processed, we first stat:

/a/long/file/name/path.txt

and if this fails, we try to stat:

/a/long/file/name

if this path exists (the normal case when creating a new
entry in a directory) then we no longer do the individual
path name walk, but only do case insensitive lookup on the
last component. If the stat fails we do the full pathname
walk as normal in 3.5.x and below. Metze, examine this
change for your back-port.

Jeremy.

source3/smbd/filename.c

index eadb977a72f742ea01ebcd31838613809c5db792..49171732a9d12c3204d6ace5cb3e7983c1221358 100644 (file)
@@ -99,6 +99,88 @@ static NTSTATUS check_for_dot_component(const struct smb_filename *smb_fname)
        return NT_STATUS_OK;
 }
 
+/****************************************************************************
+ Optimization for common case where the missing part
+ is in the last component and the client already
+ sent the correct case.
+ Returns NT_STATUS_OK to mean continue the tree walk
+ (possibly with modified start pointer).
+ Any other NT_STATUS_XXX error means terminate the path
+ lookup here.
+****************************************************************************/
+
+static NTSTATUS check_parent_exists(TALLOC_CTX *ctx,
+                               connection_struct *conn,
+                               bool posix_pathnames,
+                               struct smb_filename *smb_fname,
+                               char **pp_dirpath,
+                               char **pp_start)
+{
+       struct smb_filename parent_fname;
+       const char *last_component = NULL;
+       NTSTATUS status;
+       int ret;
+
+       ZERO_STRUCT(parent_fname);
+       if (!parent_dirname(ctx, smb_fname->base_name,
+                               &parent_fname.base_name,
+                               &last_component)) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /*
+        * If there was no parent component in
+        * smb_fname->base_name of the parent name
+        * contained a wildcard then don't do this
+        * optimization.
+        */
+       if ((smb_fname->base_name == last_component) ||
+                       ms_has_wild(parent_fname.base_name)) {
+               return NT_STATUS_OK;
+       }
+
+       if (posix_pathnames) {
+               ret = SMB_VFS_LSTAT(conn, &parent_fname);
+       } else {
+               ret = SMB_VFS_STAT(conn, &parent_fname);
+       }
+
+       /* If the parent stat failed, just continue
+          with the normal tree walk. */
+
+       if (ret == -1) {
+               return NT_STATUS_OK;
+       }
+
+       status = check_for_dot_component(&parent_fname);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* Parent exists - set "start" to be the
+        * last compnent to shorten the tree walk. */
+
+       /*
+        * Safe to use CONST_DISCARD
+        * here as last_component points
+        * into our smb_fname->base_name.
+        */
+       *pp_start = CONST_DISCARD(char *,last_component);
+
+       /* Update dirpath. */
+       TALLOC_FREE(*pp_dirpath);
+       *pp_dirpath = talloc_strdup(ctx, parent_fname.base_name);
+
+       DEBUG(5,("check_parent_exists: name "
+               "= %s, dirpath = %s, "
+               "start = %s\n",
+               smb_fname->base_name,
+               *pp_dirpath,
+               *pp_start));
+
+       return NT_STATUS_OK;
+}
+
 /****************************************************************************
 This routine is called to convert names from the dos namespace to unix
 namespace. It needs to handle any case conversions, mangling, format changes,
@@ -353,6 +435,20 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                        goto done;
                }
 
+               if (errno == ENOENT) {
+                       /* Optimization when creating a new file - only
+                          the last component doesn't exist. */
+                       status = check_parent_exists(ctx,
+                                               conn,
+                                               posix_pathnames,
+                                               smb_fname,
+                                               &dirpath,
+                                               &start);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               goto fail;
+                       }
+               }
+
                /*
                 * A special case - if we don't have any wildcards or mangling chars and are case
                 * sensitive or the underlying filesystem is case insentive then searching
@@ -403,38 +499,6 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                                                status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
                                                goto fail;
                                        }
-                               } else if (ret == 0) {
-                                       /*
-                                        * stat() or lstat() of the parent dir
-                                        * succeeded. So start the walk
-                                        * at this point.
-                                        */
-                                       status = check_for_dot_component(&parent_fname);
-                                       if (!NT_STATUS_IS_OK(status)) {
-                                               goto fail;
-                                       }
-
-                                       /*
-                                        * If there was no parent component in
-                                        * smb_fname->base_name then
-                                        * don't do this optimization.
-                                        */
-                                       if (smb_fname->base_name != last_component) {
-                                               /*
-                                                * Safe to use CONST_DISCARD
-                                                * here as last_component points
-                                                * into our smb_fname->base_name.
-                                                */
-                                               start = CONST_DISCARD(char *,
-                                                       last_component);
-
-                                               DEBUG(5,("unix_convert optimize1: name "
-                                                       "= %s, dirpath = %s, "
-                                                       "start = %s\n",
-                                                       smb_fname->base_name,
-                                                       dirpath,
-                                                       start));
-                                       }
                                }
 
                                /*
@@ -453,53 +517,15 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                 * is in the last component and the client already
                 * sent the correct case.
                 */
-               struct smb_filename parent_fname;
-               const char *last_component = NULL;
-
-               ZERO_STRUCT(parent_fname);
-               if (!parent_dirname(ctx, smb_fname->base_name,
-                                       &parent_fname.base_name,
-                                       &last_component)) {
-                       status = NT_STATUS_NO_MEMORY;
+               status = check_parent_exists(ctx,
+                                       conn,
+                                       posix_pathnames,
+                                       smb_fname,
+                                       &dirpath,
+                                       &start);
+               if (!NT_STATUS_IS_OK(status)) {
                        goto fail;
                }
-               /*
-                * If there was no parent component in
-                * smb_fname->base_name then
-                * don't do this optimization.
-                */
-               if ((smb_fname->base_name != last_component) &&
-                               !ms_has_wild(parent_fname.base_name)) {
-                       /*
-                        * Wildcard isn't in the parent, i.e.
-                        * it must be in the last component.
-                        */
-                       if (posix_pathnames) {
-                               ret = SMB_VFS_LSTAT(conn, &parent_fname);
-                       } else {
-                               ret = SMB_VFS_STAT(conn, &parent_fname);
-                       }
-                       if (ret == 0) {
-                               status = check_for_dot_component(&parent_fname);
-                               if (!NT_STATUS_IS_OK(status)) {
-                                       goto fail;
-                               }
-
-                               /*
-                                * Safe to use CONST_DISCARD
-                                * here as last_component points
-                                * into our smb_fname->base_name.
-                                */
-                               start = CONST_DISCARD(char *,last_component);
-
-                               DEBUG(5,("unix_convert optimize2: name "
-                                       "= %s, dirpath = %s, "
-                                       "start = %s\n",
-                                       smb_fname->base_name,
-                                       dirpath,
-                                       start));
-                       }
-               }
        }
 
        /*