s3:smbd: check for allow_wcard_last_component early in unix_convert()
[metze/samba/wip.git] / source3 / smbd / filename.c
index 80722a7cd0864a5679dfd6af7911b35b6a336fdc..81532a06dac8c819e66f168886ac71dc04763d29 100644 (file)
@@ -268,6 +268,14 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                return NT_STATUS_NO_MEMORY;
        }
 
+       name_has_wildcard = ms_has_wild(name);
+
+       /* Wildcard not valid anywhere. */
+       if (name_has_wildcard && !allow_wcard_last_component) {
+               result = NT_STATUS_OBJECT_NAME_INVALID;
+               goto fail;
+       }
+
        /*
         * stat the name - if it exists then we are all done!
         */
@@ -374,12 +382,6 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
 
                name_has_wildcard = ms_has_wild(start);
 
-               /* Wildcard not valid anywhere. */
-               if (name_has_wildcard && !allow_wcard_last_component) {
-                       result = NT_STATUS_OBJECT_NAME_INVALID;
-                       goto fail;
-               }
-
                /* Wildcards never valid within a pathname. */
                if (name_has_wildcard && end) {
                        result = NT_STATUS_OBJECT_NAME_INVALID;
@@ -401,7 +403,7 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                         * It exists. it must either be a directory or this must
                         * be the last part of the path for it to be OK.
                         */
-                       if (end && !(st.st_mode & S_IFDIR)) {
+                       if (end && !S_ISDIR(st.st_ex_mode)) {
                                /*
                                 * An intermediate part of the name isn't
                                 * a directory.
@@ -447,9 +449,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) {
@@ -490,8 +492,14 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                                        goto fail;
                                }
 
-                               /* ENOENT is the only valid error here. */
-                               if ((errno != 0) && (errno != ENOENT)) {
+                               /*
+                                * ENOENT/EACCESS are the only valid errors
+                                * here. EACCESS needs handling here for
+                                * "dropboxes", i.e. directories where users
+                                * can only put stuff with permission -wx.
+                                */
+                               if ((errno != 0) && (errno != ENOENT)
+                                   && (errno != EACCES)) {
                                        /*
                                         * ENOTDIR and ELOOP both map to
                                         * NT_STATUS_OBJECT_PATH_NOT_FOUND
@@ -501,8 +509,7 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                                                        errno == ELOOP) {
                                                result =
                                                NT_STATUS_OBJECT_PATH_NOT_FOUND;
-                                       }
-                                       else {
+                                       } else {
                                                result =
                                                map_nt_error_from_unix(errno);
                                        }
@@ -789,18 +796,16 @@ 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,
+                                      bool mangled,
+                                      TALLOC_CTX *mem_ctx, char **found_name)
 {
        struct smb_Dir *cur_dir;
        const char *dname;
-       bool mangled;
        char *unmangled_name = NULL;
        long curpos;
 
-       mangled = mangle_is_mangled(name, conn->params);
-
        /* handle null paths */
        if ((path == NULL) || (*path == 0)) {
                path = ".";
@@ -887,6 +892,41 @@ 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;
+       bool mangled;
+
+       mangled = mangle_is_mangled(name, conn->params);
+
+       if (mangled) {
+               return get_real_filename_full_scan(conn, path, name, mangled,
+                                                  mem_ctx, found_name);
+       }
+
+       /* 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;
+       }
+
+       return get_real_filename_full_scan(conn, path, name, mangled, mem_ctx,
+                                          found_name);
+}
+
 static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx,
                                  connection_struct *conn,
                                  const char *orig_path,