summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/modules/vfs_default.c8
-rw-r--r--source3/smbd/dir.c17
-rw-r--r--source3/smbd/filename.c40
3 files changed, 57 insertions, 8 deletions
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index bb01f98588..6c1946a99d 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -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,
diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index b4a8f942c2..e7902871d3 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -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);
diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c
index 80722a7cd0..774ab27a74 100644
--- a/source3/smbd/filename.c
+++ b/source3/smbd/filename.c
@@ -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,