From c96d487ae3c65c17b377bb316adac4b5775448f3 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 25 Nov 2009 13:17:56 -0800 Subject: Make us pass RAW-CHKPATH with a case sensitive share. I know Volker will look at this closely so here's the explaination :-). Originally on a case-sensitive share we simply did a stat (or lstat) call and returned success of fail based on the result. However this failed to take account of incoming paths with a wildcard (which must always fail, and with different error messages depending on whether the wildcard is the last component or in the path). Also it failed to take account of a stat fail with ENOENT due to a missing component of the path as the last component (which is ok as it could be a new file) or if the ENOENT was due to the missing component within the path (not the last component) - which must return the correct error. What this means is that with "case sensitive = yes" we do one more talloc call (to get the parent directory) and one more stat call (on the parent directory) in the case where the stat call fails. I think this is an acceptable overhead to enable case sensitive shares to return the correct error messages for applications. Volker please examine carefully :-). Jeremy. --- source3/smbd/filename.c | 147 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 42 deletions(-) (limited to 'source3/smbd/filename.c') diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 16e36312bb..ab79dfd926 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -80,6 +80,24 @@ static NTSTATUS determine_path_error(const char *name, } } +static NTSTATUS check_for_dot_component(const struct smb_filename *smb_fname) +{ + /* Ensure we catch all names with in "/." + this is disallowed under Windows and + in POSIX they've already been removed. */ + const char *p = strstr(smb_fname->base_name, "/."); /*mb safe*/ + if (p) { + if (p[2] == '/') { + /* Error code within a pathname. */ + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } else if (p[2] == '\0') { + /* Error code at the end of a pathname. */ + return NT_STATUS_OBJECT_NAME_INVALID; + } + } + 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, @@ -294,52 +312,103 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx, } /* - * stat the name - if it exists then we can add the stream back (if - * there was one) and be done! + * If we have a wildcard we must walk the path to + * find where the error is, even if case sensitive + * is true. */ - if (posix_pathnames) { - ret = SMB_VFS_LSTAT(conn, smb_fname); - } else { - ret = SMB_VFS_STAT(conn, smb_fname); + name_has_wildcard = ms_has_wild(smb_fname->base_name); + if (name_has_wildcard && !allow_wcard_last_component) { + /* Wildcard not valid anywhere. */ + status = NT_STATUS_OBJECT_NAME_INVALID; + goto fail; } - if (ret == 0) { - /* Ensure we catch all names with in "/." - this is disallowed under Windows. */ - const char *p = strstr(smb_fname->base_name, "/."); /*mb safe*/ - if (p) { - if (p[2] == '/') { - /* Error code within a pathname. */ - status = NT_STATUS_OBJECT_PATH_NOT_FOUND; - goto fail; - } else if (p[2] == '\0') { - /* Error code at the end of a pathname. */ - status = NT_STATUS_OBJECT_NAME_INVALID; + DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", + smb_fname->base_name, dirpath, start)); + + if (!name_has_wildcard) { + /* + * stat the name - if it exists then we can add the stream back (if + * there was one) and be done! + */ + + if (posix_pathnames) { + ret = SMB_VFS_LSTAT(conn, smb_fname); + } else { + ret = SMB_VFS_STAT(conn, smb_fname); + } + + if (ret == 0) { + status = check_for_dot_component(smb_fname); + if (!NT_STATUS_IS_OK(status)) { goto fail; } + /* Add the path (not including the stream) to the cache. */ + stat_cache_add(orig_path, smb_fname->base_name, + conn->case_sensitive); + DEBUG(5,("conversion of base_name finished %s -> %s\n", + orig_path, smb_fname->base_name)); + goto done; } - /* Add the path (not including the stream) to the cache. */ - stat_cache_add(orig_path, smb_fname->base_name, - conn->case_sensitive); - DEBUG(5,("conversion of base_name finished %s -> %s\n", - orig_path, smb_fname->base_name)); - goto done; - } - DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", - smb_fname->base_name, dirpath, start)); + /* + * 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 + * won't help. + */ - /* - * A special case - if we don't have any mangling chars and are case - * sensitive or the underlying filesystem is case insentive then searching - * won't help. - */ + if ((conn->case_sensitive || !(conn->fs_capabilities & + FILE_CASE_SENSITIVE_SEARCH)) && + !mangle_is_mangled(smb_fname->base_name, conn->params)) { - if ((conn->case_sensitive || !(conn->fs_capabilities & - FILE_CASE_SENSITIVE_SEARCH)) && - !mangle_is_mangled(smb_fname->base_name, conn->params)) { - goto done; + status = check_for_dot_component(smb_fname); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* + * The stat failed. Could be ok as it could be + * a new file. + */ + + if (errno == ENOTDIR || errno == ELOOP) { + status = NT_STATUS_OBJECT_PATH_NOT_FOUND; + goto fail; + } else if (errno == ENOENT) { + /* + * Was it a missing last component ? + * or a missing intermediate component ? + */ + struct smb_filename parent_fname; + ZERO_STRUCT(parent_fname); + if (!parent_dirname(ctx, smb_fname->base_name, + &parent_fname.base_name, + NULL)) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + if (posix_pathnames) { + ret = SMB_VFS_LSTAT(conn, &parent_fname); + } else { + ret = SMB_VFS_STAT(conn, &parent_fname); + } + if (ret == -1) { + if (errno == ENOTDIR || + errno == ENOENT || + errno == ELOOP) { + status = NT_STATUS_OBJECT_PATH_NOT_FOUND; + goto fail; + } + } + /* + * Missing last component is ok - new file. + * Also deal with permission denied elsewhere. + * Just drop out to done. + */ + goto done; + } + } } /* @@ -404,12 +473,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) { - status = NT_STATUS_OBJECT_NAME_INVALID; - goto fail; - } - /* Wildcards never valid within a pathname. */ if (name_has_wildcard && end) { status = NT_STATUS_OBJECT_NAME_INVALID; -- cgit