s3: Fix trans2 path to use case-insensitive stat optimization
authortprouty <tprouty@b72e2a10-2d34-0410-9a71-d3beadf02b57>
Sat, 2 May 2009 00:28:38 +0000 (00:28 +0000)
committerTim Prouty <tprouty@samba.org>
Tue, 5 May 2009 23:46:21 +0000 (16:46 -0700)
Often times before creating a file, a client will first query to see
if it already exists.  Since some systems have a case-insensitive stat
that is called from unix_convert, we can definitively return
STATUS_NO_SUCH_FILE to the client without scanning the whole
directory.

This code path is taken from trans2querypathinfo, but trans2findfirst
still does a full directory scan even though the get_real_filename
(the case-insensitive stat vfs call) can prevent this.

This patch adds the get_real_filename call to the trans2find* path,
and also changes the vfs_default behavior for
SMB_VFS_GET_REAL_FILENAME.  Previously, in the absence of a
get_real_filename implementation, we would fallback to the full
directory scan.  The default behavior now returns -1 and sets errno to
EOPNOTSUPP.  This allows SMB_VFS_GET_REALFILENAME to be called from
trans2* and unix_convert.

source3/modules/vfs_default.c
source3/smbd/dir.c
source3/smbd/filename.c

index bb01f9858817f21ec732226baa2b0f35a7388ba6..6c1946a99d042be80ef7db2f872226bb7cf0e7d2 100644 (file)
@@ -1120,8 +1120,12 @@ static int vfswrap_get_real_filename(struct vfs_handle_struct *handle,
                                     TALLOC_CTX *mem_ctx,
                                     char **found_name)
 {
-       return get_real_filename(handle->conn, path, name, mem_ctx,
-                                found_name);
+       /*
+        * Don't fall back to get_real_filename so callers can differentiate
+        * between a full directory scan and an actual case-insensitive stat.
+        */
+       errno = EOPNOTSUPP;
+       return -1;
 }
 
 static NTSTATUS vfswrap_brl_lock_windows(struct vfs_handle_struct *handle,
index b4a8f942c248d29d4cc68b5f0c5ed10ab0a0177c..e7902871d38042fd9e80e19ccd8101831755f484 100644 (file)
@@ -576,6 +576,9 @@ const char *dptr_ReadDirName(TALLOC_CTX *ctx,
 {
        char *name = NULL;
        char *pathreal = NULL;
+       char *found_name = NULL;
+       int ret;
+
        SET_STAT_INVALID(*pst);
 
        if (dptr->has_wild || dptr->did_stat) {
@@ -640,6 +643,20 @@ const char *dptr_ReadDirName(TALLOC_CTX *ctx,
                goto clean;
        }
 
+       /*
+        * Try case-insensitive stat if the fs has the ability. This avoids
+        * scanning the whole directory.
+        */
+       ret = SMB_VFS_GET_REAL_FILENAME(dptr->conn, dptr->path, dptr->wcard,
+                                       ctx, &found_name);
+       if (ret == 0) {
+               name = found_name;
+               goto clean;
+       } else if (errno == ENOENT) {
+               /* The case-insensitive lookup was authoritative. */
+               goto clean;
+       }
+
        TALLOC_FREE(pathreal);
 
        return dptr_normal_ReadDirName(dptr, poffset, pst);
index 80722a7cd0864a5679dfd6af7911b35b6a336fdc..774ab27a74b958ba0834ff8fe2f9ce312539adf8 100644 (file)
@@ -447,9 +447,9 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                         */
 
                        if (name_has_wildcard ||
-                           (SMB_VFS_GET_REAL_FILENAME(
-                                    conn, dirpath, start,
-                                    talloc_tos(), &found_name) == -1)) {
+                           (get_real_filename(conn, dirpath, start,
+                                              talloc_tos(),
+                                              &found_name) == -1)) {
                                char *unmangled;
 
                                if (end) {
@@ -789,9 +789,9 @@ static bool fname_equal(const char *name1, const char *name2,
  If the name looks like a mangled name then try via the mangling functions
 ****************************************************************************/
 
-int get_real_filename(connection_struct *conn, const char *path,
-                     const char *name, TALLOC_CTX *mem_ctx,
-                     char **found_name)
+static int get_real_filename_full_scan(connection_struct *conn,
+                                      const char *path, const char *name,
+                                      TALLOC_CTX *mem_ctx, char **found_name)
 {
        struct smb_Dir *cur_dir;
        const char *dname;
@@ -887,6 +887,34 @@ int get_real_filename(connection_struct *conn, const char *path,
        return -1;
 }
 
+/****************************************************************************
+ Wrapper around the vfs get_real_filename and the full directory scan
+ fallback.
+****************************************************************************/
+
+int get_real_filename(connection_struct *conn, const char *path,
+                     const char *name, TALLOC_CTX *mem_ctx,
+                     char **found_name)
+{
+       int ret;
+
+       /* Try the vfs first to take advantage of case-insensitive stat. */
+       ret = SMB_VFS_GET_REAL_FILENAME(conn, path, name, mem_ctx, found_name);
+
+       /*
+        * If the case-insensitive stat was successful, or returned an error
+        * other than EOPNOTSUPP then there is no need to fall back on the
+        * full directory scan.
+        */
+       if (ret == 0 || (ret == -1 && errno != EOPNOTSUPP)) {
+               return ret;
+       }
+
+       ret = get_real_filename_full_scan(conn, path, name, mem_ctx,
+                                         found_name);
+       return ret;
+}
+
 static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx,
                                  connection_struct *conn,
                                  const char *orig_path,