diff options
-rw-r--r-- | source3/smbd/filename.c | 147 |
1 files changed, 105 insertions, 42 deletions
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; |