diff options
author | tprouty <tprouty@b72e2a10-2d34-0410-9a71-d3beadf02b57> | 2009-05-02 00:28:38 +0000 |
---|---|---|
committer | Tim Prouty <tprouty@samba.org> | 2009-05-05 16:43:53 -0700 |
commit | 69d61453df6019caef4e7960fa78c6a3c51f3d2a (patch) | |
tree | 406d028c81bc1bf0f33507c00c5f0da7a83cf984 | |
parent | e091fdc5658e65be290ebb3b62a1bdf517781a65 (diff) | |
download | samba-69d61453df6019caef4e7960fa78c6a3c51f3d2a.tar.gz samba-69d61453df6019caef4e7960fa78c6a3c51f3d2a.tar.bz2 samba-69d61453df6019caef4e7960fa78c6a3c51f3d2a.zip |
s3: Fix trans2 path to use case-insensitive stat optimization
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.
-rw-r--r-- | source3/modules/vfs_default.c | 8 | ||||
-rw-r--r-- | source3/smbd/dir.c | 17 | ||||
-rw-r--r-- | source3/smbd/filename.c | 40 |
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, |