NTSTATUS unix_convert(TALLOC_CTX *ctx, connection_struct *conn, const char *orig_path, struct smb_filename **smb_fname_out, uint32_t ucf_flags) { struct smb_filename *smb_fname = NULL; char *start, *end; char *dirpath = NULL; char *stream = NULL; bool component_was_mangled = False; bool name_has_wildcard = False; bool posix_pathnames = false; bool allow_wcard_last_component = (ucf_flags & UCF_ALWAYS_ALLOW_WCARD_LCOMP); bool save_last_component = ucf_flags & UCF_SAVE_LCOMP; NTSTATUS status; int ret = -1; *smb_fname_out = NULL; smb_fname = talloc_zero(ctx, struct smb_filename); if (smb_fname == NULL) { return NT_STATUS_NO_MEMORY; } if (conn->printer) { /* we don't ever use the filenames on a printer share as a filename - so don't convert them */ if (!(smb_fname->base_name = talloc_strdup(smb_fname, orig_path))) { status = NT_STATUS_NO_MEMORY; goto err; } goto done; } DEBUG(5, ("unix_convert called on file \"%s\"\n", orig_path)); /* * Conversion to basic unix format is already done in * check_path_syntax(). */ /* * Names must be relative to the root of the service - any leading /. * and trailing /'s should have been trimmed by check_path_syntax(). */ #ifdef DEVELOPER SMB_ASSERT(*orig_path != '/'); #endif /* * If we trimmed down to a single '\0' character * then we should use the "." directory to avoid * searching the cache, but not if we are in a * printing share. * As we know this is valid we can return true here. */ if (!*orig_path) { if (!(smb_fname->base_name = talloc_strdup(smb_fname, "."))) { status = NT_STATUS_NO_MEMORY; goto err; } if (SMB_VFS_STAT(conn, smb_fname) != 0) { status = map_nt_error_from_unix(errno); goto err; } DEBUG(5, ("conversion finished \"\" -> %s\n", smb_fname->base_name)); goto done; } if (orig_path[0] == '.' && (orig_path[1] == '/' || orig_path[1] == '\0')) { /* Start of pathname can't be "." only. */ if (orig_path[1] == '\0' || orig_path[2] == '\0') { status = NT_STATUS_OBJECT_NAME_INVALID; } else { status =determine_path_error(&orig_path[2], allow_wcard_last_component); } goto err; } /* Start with the full orig_path as given by the caller. */ if (!(smb_fname->base_name = talloc_strdup(smb_fname, orig_path))) { DEBUG(0, ("talloc_strdup failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } /* * Large directory fix normalization. If we're case sensitive, and * the case preserving parameters are set to "no", normalize the case of * the incoming filename from the client WHETHER IT EXISTS OR NOT ! * This is in conflict with the current (3.0.20) man page, but is * what people expect from the "large directory howto". I'll update * the man page. Thanks to [email protected] for finding this. JRA. */ if (conn->case_sensitive && !conn->case_preserve && !conn->short_case_preserve) { strnorm(smb_fname->base_name, lp_defaultcase(SNUM(conn))); } /* * Ensure saved_last_component is valid even if file exists. */ if(save_last_component) { end = strrchr_m(smb_fname->base_name, '/'); if (end) { smb_fname->original_lcomp = talloc_strdup(smb_fname, end + 1); } else { smb_fname->original_lcomp = talloc_strdup(smb_fname, smb_fname->base_name); } if (smb_fname->original_lcomp == NULL) { status = NT_STATUS_NO_MEMORY; goto err; } } posix_pathnames = (lp_posix_pathnames() || (ucf_flags & UCF_POSIX_PATHNAMES)); /* * Strip off the stream, and add it back when we're done with the * base_name. */ if (!posix_pathnames) { stream = strchr_m(smb_fname->base_name, ':'); if (stream != NULL) { char *tmp = talloc_strdup(smb_fname, stream); if (tmp == NULL) { status = NT_STATUS_NO_MEMORY; goto err; } /* * Since this is actually pointing into * smb_fname->base_name this truncates base_name. */ *stream = '\0'; stream = tmp; } } start = smb_fname->base_name; /* * If we're providing case insensitive semantics or * the underlying filesystem is case insensitive, * then a case-normalized hit in the stat-cache is * authoratitive. JRA. * * Note: We're only checking base_name. The stream_name will be * added and verified in build_stream_path(). */ if((!conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && stat_cache_lookup(conn, posix_pathnames, &smb_fname->base_name, &dirpath, &start, &smb_fname->st)) { goto done; } /* * Make sure "dirpath" is an allocated string, we use this for * building the directories with talloc_asprintf and free it. */ if ((dirpath == NULL) && (!(dirpath = talloc_strdup(ctx,"")))) { DEBUG(0, ("talloc_strdup failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } /* * If we have a wildcard we must walk the path to * find where the error is, even if case sensitive * is true. */ 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; } 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; } /* Stat failed - ensure we don't use it. */ SET_STAT_INVALID(smb_fname->st); if (errno == ENOENT) { /* Optimization when creating a new file - only the last component doesn't exist. */ status = check_parent_exists(ctx, conn, posix_pathnames, smb_fname, &dirpath, &start); if (!NT_STATUS_IS_OK(status)) { goto fail; } } /* * A special case - if we don't have any wildcards or mangling chars and are case * sensitive or the underlying filesystem is case insensitive 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)) { 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; const char *last_component = NULL; ZERO_STRUCT(parent_fname); if (!parent_dirname(ctx, smb_fname->base_name, &parent_fname.base_name, &last_component)) { 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; } } } else { /* * We have a wildcard in the pathname. * * Optimization for common case where the wildcard * is in the last component and the client already * sent the correct case. */ status = check_parent_exists(ctx, conn, posix_pathnames, smb_fname, &dirpath, &start); if (!NT_STATUS_IS_OK(status)) { goto fail; } } /* * is_mangled() was changed to look at an entire pathname, not * just a component. JRA. */ if (mangle_is_mangled(start, conn->params)) { component_was_mangled = True; } /* * Now we need to recursively match the name against the real * directory structure. */ /* * Match each part of the path name separately, trying the names * as is first, then trying to scan the directory for matching names. */ for (; start ; start = (end?end+1:(char *)NULL)) { /* * Pinpoint the end of this section of the filename. */ /* mb safe. '/' can't be in any encoded char. */ end = strchr(start, '/'); /* * Chop the name at this point. */ if (end) { *end = 0; } if (save_last_component) { TALLOC_FREE(smb_fname->original_lcomp); smb_fname->original_lcomp = talloc_strdup(smb_fname, end ? end + 1 : start); if (!smb_fname->original_lcomp) { DEBUG(0, ("talloc failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } } /* The name cannot have a component of "." */ if (ISDOT(start)) { if (!end) { /* Error code at the end of a pathname. */ status = NT_STATUS_OBJECT_NAME_INVALID; } else { status = determine_path_error(end+1, allow_wcard_last_component); } goto fail; } /* The name cannot have a wildcard if it's not the last component. */ name_has_wildcard = ms_has_wild(start); /* Wildcards never valid within a pathname. */ if (name_has_wildcard && end) { status = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } /* Skip the stat call if it's a wildcard end. */ if (name_has_wildcard) { DEBUG(5,("Wildcard %s\n",start)); goto done; } /* * Check if the name exists up to this point. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn, smb_fname); } else { ret = SMB_VFS_STAT(conn, smb_fname); } if (ret == 0) { /* * 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 && !S_ISDIR(smb_fname->st.st_ex_mode)) { /* * An intermediate part of the name isn't * a directory. */ DEBUG(5,("Not a dir %s\n",start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. This * is used to return an error of ERRbadpath * rather than ERRbadfile. Some Windows * applications depend on the difference between * these two errors. */ status = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } } else { char *found_name = NULL; /* Stat failed - ensure we don't use it. */ SET_STAT_INVALID(smb_fname->st); /* * Reset errno so we can detect * directory open errors. */ errno = 0; /* * Try to find this part of the path in the directory. */ if (name_has_wildcard || (get_real_filename(conn, dirpath, start, talloc_tos(), &found_name) == -1)) { char *unmangled; if (end) { /* * An intermediate part of the name * can't be found. */ DEBUG(5,("Intermediate not found %s\n", start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. * This is used to return an error of * ERRbadpath rather than ERRbadfile. * Some Windows applications depend on * the difference between these two * errors. */ /* * ENOENT, ENOTDIR and ELOOP all map * to NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOENT || errno == ENOTDIR || errno == ELOOP) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { status = map_nt_error_from_unix(errno); } goto fail; } /* * 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 * in the filename walk. */ if (errno == ENOTDIR || errno == ELOOP) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { status = map_nt_error_from_unix(errno); } goto fail; } /* * Just the last part of the name doesn't exist. * We need to strupper() or strlower() it as * this conversion may be used for file creation * purposes. Fix inspired by * Thomas Neumann <*****@*****.**>. */ if (!conn->case_preserve || (mangle_is_8_3(start, False, conn->params) && !conn->short_case_preserve)) { strnorm(start, lp_defaultcase(SNUM(conn))); } /* * check on the mangled stack to see if we can * recover the base of the filename. */ if (mangle_is_mangled(start, conn->params) && mangle_lookup_name_from_8_3(ctx, start, &unmangled, conn->params)) { char *tmp; size_t start_ofs = start - smb_fname->base_name; if (*dirpath != '\0') { tmp = talloc_asprintf( smb_fname, "%s/%s", dirpath, unmangled); TALLOC_FREE(unmangled); } else { tmp = unmangled; } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; start = smb_fname->base_name + start_ofs; end = start + strlen(start); } DEBUG(5,("New file %s\n",start)); goto done; } /* * Restore the rest of the string. If the string was * mangled the size may have changed. */ if (end) { char *tmp; size_t start_ofs = start - smb_fname->base_name; if (*dirpath != '\0') { tmp = talloc_asprintf(smb_fname, "%s/%s/%s", dirpath, found_name, end+1); } else { tmp = talloc_asprintf(smb_fname, "%s/%s", found_name, end+1); } if (tmp == NULL) { DEBUG(0, ("talloc_asprintf failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; start = smb_fname->base_name + start_ofs; end = start + strlen(found_name); *end = '\0'; } else { char *tmp; size_t start_ofs = start - smb_fname->base_name; if (*dirpath != '\0') { tmp = talloc_asprintf(smb_fname, "%s/%s", dirpath, found_name); } else { tmp = talloc_strdup(smb_fname, found_name); } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; start = smb_fname->base_name + start_ofs; /* * We just scanned for, and found the end of * the path. We must return a valid stat struct * if it exists. JRA. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn, smb_fname); } else { ret = SMB_VFS_STAT(conn, smb_fname); } if (ret != 0) { SET_STAT_INVALID(smb_fname->st); } } TALLOC_FREE(found_name); } /* end else */ #ifdef DEVELOPER /* * This sucks! * We should never provide different behaviors * depending on DEVELOPER!!! */ if (VALID_STAT(smb_fname->st)) { bool delete_pending; uint32_t name_hash; status = file_name_hash(conn, smb_fname_str_dbg(smb_fname), &name_hash); if (!NT_STATUS_IS_OK(status)) { goto fail; } get_file_infos(vfs_file_id_from_sbuf(conn, &smb_fname->st), name_hash, &delete_pending, NULL); if (delete_pending) { status = NT_STATUS_DELETE_PENDING; goto fail; } } #endif /* * Add to the dirpath that we have resolved so far. */ if (*dirpath != '\0') { char *tmp = talloc_asprintf(ctx, "%s/%s", dirpath, start); if (!tmp) { DEBUG(0, ("talloc_asprintf failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(dirpath); dirpath = tmp; } else { TALLOC_FREE(dirpath); if (!(dirpath = talloc_strdup(ctx,start))) { DEBUG(0, ("talloc_strdup failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } } /* * Cache the dirpath thus far. Don't cache a name with mangled * or wildcard components as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, dirpath, conn->case_sensitive); } /* * Restore the / that we wiped out earlier. */ if (end) { *end = '/'; } } /* * Cache the full path. Don't cache a name with mangled or wildcard * components as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, smb_fname->base_name, conn->case_sensitive); } /* * The name has been resolved. */ DEBUG(5,("conversion finished %s -> %s\n", orig_path, smb_fname->base_name)); done: /* Add back the stream if one was stripped off originally. */ if (stream != NULL) { smb_fname->stream_name = stream; /* Check path now that the base_name has been converted. */ status = build_stream_path(ctx, conn, orig_path, smb_fname); if (!NT_STATUS_IS_OK(status)) { goto fail; } } TALLOC_FREE(dirpath); *smb_fname_out = smb_fname; return NT_STATUS_OK; fail: DEBUG(10, ("dirpath = [%s] start = [%s]\n", dirpath, start)); if (*dirpath != '\0') { smb_fname->base_name = talloc_asprintf(smb_fname, "%s/%s", dirpath, start); } else { smb_fname->base_name = talloc_strdup(smb_fname, start); } if (!smb_fname->base_name) { DEBUG(0, ("talloc_asprintf failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } *smb_fname_out = smb_fname; TALLOC_FREE(dirpath); return status; err: TALLOC_FREE(smb_fname); return status; }
NTSTATUS unix_convert(TALLOC_CTX *ctx, connection_struct *conn, const char *orig_path, bool allow_wcard_last_component, char **pp_conv_path, char **pp_saved_last_component, SMB_STRUCT_STAT *pst) { SMB_STRUCT_STAT st; char *start, *end; char *dirpath = NULL; char *name = NULL; char *stream = NULL; bool component_was_mangled = False; bool name_has_wildcard = False; bool posix_pathnames = false; NTSTATUS result; int ret = -1; SET_STAT_INVALID(*pst); *pp_conv_path = NULL; if(pp_saved_last_component) { *pp_saved_last_component = NULL; } if (conn->printer) { /* we don't ever use the filenames on a printer share as a filename - so don't convert them */ if (!(*pp_conv_path = talloc_strdup(ctx,orig_path))) { return NT_STATUS_NO_MEMORY; } return NT_STATUS_OK; } DEBUG(5, ("unix_convert called on file \"%s\"\n", orig_path)); /* * Conversion to basic unix format is already done in * check_path_syntax(). */ /* * Names must be relative to the root of the service - any leading /. * and trailing /'s should have been trimmed by check_path_syntax(). */ #ifdef DEVELOPER SMB_ASSERT(*orig_path != '/'); #endif /* * If we trimmed down to a single '\0' character * then we should use the "." directory to avoid * searching the cache, but not if we are in a * printing share. * As we know this is valid we can return true here. */ if (!*orig_path) { if (!(name = talloc_strdup(ctx,"."))) { return NT_STATUS_NO_MEMORY; } if (SMB_VFS_STAT(conn,name,&st) == 0) { *pst = st; } else { return map_nt_error_from_unix(errno); } DEBUG(5,("conversion finished \"\" -> %s\n",name)); goto done; } if (orig_path[0] == '.' && (orig_path[1] == '/' || orig_path[1] == '\0')) { /* Start of pathname can't be "." only. */ if (orig_path[1] == '\0' || orig_path[2] == '\0') { result = NT_STATUS_OBJECT_NAME_INVALID; } else { result =determine_path_error( &orig_path[2], allow_wcard_last_component); } return result; } if (!(name = talloc_strdup(ctx, orig_path))) { DEBUG(0, ("talloc_strdup failed\n")); return NT_STATUS_NO_MEMORY; } /* * Large directory fix normalization. If we're case sensitive, and * the case preserving parameters are set to "no", normalize the case of * the incoming filename from the client WHETHER IT EXISTS OR NOT ! * This is in conflict with the current (3.0.20) man page, but is * what people expect from the "large directory howto". I'll update * the man page. Thanks to [email protected] for finding this. JRA. */ if (conn->case_sensitive && !conn->case_preserve && !conn->short_case_preserve) { strnorm(name, lp_defaultcase(SNUM(conn))); } /* * Ensure saved_last_component is valid even if file exists. */ if(pp_saved_last_component) { end = strrchr_m(name, '/'); if (end) { *pp_saved_last_component = talloc_strdup(ctx, end + 1); } else { *pp_saved_last_component = talloc_strdup(ctx, name); } } posix_pathnames = lp_posix_pathnames(); if (!posix_pathnames) { stream = strchr_m(name, ':'); if (stream != NULL) { char *tmp = talloc_strdup(ctx, stream); if (tmp == NULL) { TALLOC_FREE(name); return NT_STATUS_NO_MEMORY; } *stream = '\0'; stream = tmp; } } start = name; /* If we're providing case insentive semantics or * the underlying filesystem is case insensitive, * then a case-normalized hit in the stat-cache is * authoratitive. JRA. */ if((!conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && stat_cache_lookup(conn, &name, &dirpath, &start, &st)) { *pst = st; goto done; } /* * Make sure "dirpath" is an allocated string, we use this for * building the directories with asprintf and free it. */ if ((dirpath == NULL) && (!(dirpath = talloc_strdup(ctx,"")))) { DEBUG(0, ("talloc_strdup failed\n")); TALLOC_FREE(name); return NT_STATUS_NO_MEMORY; } /* * stat the name - if it exists then we are all done! */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn,name,&st); } else { ret = SMB_VFS_STAT(conn,name,&st); } if (ret == 0) { /* Ensure we catch all names with in "/." this is disallowed under Windows. */ const char *p = strstr(name, "/."); /* mb safe. */ if (p) { if (p[2] == '/') { /* Error code within a pathname. */ result = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } else if (p[2] == '\0') { /* Error code at the end of a pathname. */ result = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } } stat_cache_add(orig_path, name, conn->case_sensitive); DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); *pst = st; goto done; } DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", name, dirpath, start)); /* * 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(name, conn->params)) { goto done; } /* * is_mangled() was changed to look at an entire pathname, not * just a component. JRA. */ if (mangle_is_mangled(start, conn->params)) { component_was_mangled = True; } /* * Now we need to recursively match the name against the real * directory structure. */ /* * Match each part of the path name separately, trying the names * as is first, then trying to scan the directory for matching names. */ for (; start ; start = (end?end+1:(char *)NULL)) { /* * Pinpoint the end of this section of the filename. */ /* mb safe. '/' can't be in any encoded char. */ end = strchr(start, '/'); /* * Chop the name at this point. */ if (end) { *end = 0; } if (pp_saved_last_component) { TALLOC_FREE(*pp_saved_last_component); *pp_saved_last_component = talloc_strdup(ctx, end ? end + 1 : start); if (!*pp_saved_last_component) { DEBUG(0, ("talloc failed\n")); return NT_STATUS_NO_MEMORY; } } /* The name cannot have a component of "." */ if (ISDOT(start)) { if (!end) { /* Error code at the end of a pathname. */ result = NT_STATUS_OBJECT_NAME_INVALID; } else { result = determine_path_error(end+1, allow_wcard_last_component); } goto fail; } /* The name cannot have a wildcard if it's not the last component. */ 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; goto fail; } /* * Check if the name exists up to this point. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn,name, &st); } else { ret = SMB_VFS_STAT(conn,name, &st); } if (ret == 0) { /* * 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)) { /* * An intermediate part of the name isn't * a directory. */ DEBUG(5,("Not a dir %s\n",start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. This * is used to return an error of ERRbadpath * rather than ERRbadfile. Some Windows * applications depend on the difference between * these two errors. */ result = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } if (!end) { /* * We just scanned for, and found the end of * the path. We must return the valid stat * struct. JRA. */ *pst = st; } } else { char *found_name = NULL; /* Stat failed - ensure we don't use it. */ SET_STAT_INVALID(st); /* * Reset errno so we can detect * directory open errors. */ errno = 0; /* * Try to find this part of the path in the directory. */ if (name_has_wildcard || (get_real_filename_mangled( conn, dirpath, start, talloc_tos(), &found_name) == -1)) { char *unmangled; if (end) { /* * An intermediate part of the name * can't be found. */ DEBUG(5,("Intermediate not found %s\n", start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. * This is used to return an error of * ERRbadpath rather than ERRbadfile. * Some Windows applications depend on * the difference between these two * errors. */ /* * ENOENT, ENOTDIR and ELOOP all map * to NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOENT || errno == ENOTDIR || errno == ELOOP) { result = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { result = map_nt_error_from_unix(errno); } goto fail; } /* ENOENT is the only valid error here. */ if ((errno != 0) && (errno != ENOENT)) { /* * ENOTDIR and ELOOP both map to * NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOTDIR || errno == ELOOP) { result = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { result = map_nt_error_from_unix(errno); } goto fail; } /* * Just the last part of the name doesn't exist. * We need to strupper() or strlower() it as * this conversion may be used for file creation * purposes. Fix inspired by * Thomas Neumann <*****@*****.**>. */ if (!conn->case_preserve || (mangle_is_8_3(start, False, conn->params) && !conn->short_case_preserve)) { strnorm(start, lp_defaultcase(SNUM(conn))); } /* * check on the mangled stack to see if we can * recover the base of the filename. */ if (mangle_is_mangled(start, conn->params) && mangle_lookup_name_from_8_3(ctx, start, &unmangled, conn->params)) { char *tmp; size_t start_ofs = start - name; if (*dirpath != '\0') { tmp = talloc_asprintf(ctx, "%s/%s", dirpath, unmangled); TALLOC_FREE(unmangled); } else { tmp = unmangled; } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); name = tmp; start = name + start_ofs; end = start + strlen(start); } DEBUG(5,("New file %s\n",start)); goto done; } /* * Restore the rest of the string. If the string was * mangled the size may have changed. */ if (end) { char *tmp; size_t start_ofs = start - name; if (*dirpath != '\0') { tmp = talloc_asprintf(ctx, "%s/%s/%s", dirpath, found_name, end+1); } else { tmp = talloc_asprintf(ctx, "%s/%s", found_name, end+1); } if (tmp == NULL) { DEBUG(0, ("talloc_asprintf failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); name = tmp; start = name + start_ofs; end = start + strlen(found_name); *end = '\0'; } else { char *tmp; size_t start_ofs = start - name; if (*dirpath != '\0') { tmp = talloc_asprintf(ctx, "%s/%s", dirpath, found_name); } else { tmp = talloc_strdup(ctx, found_name); } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); name = tmp; start = name + start_ofs; /* * We just scanned for, and found the end of * the path. We must return a valid stat struct * if it exists. JRA. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn,name, &st); } else { ret = SMB_VFS_STAT(conn,name, &st); } if (ret == 0) { *pst = st; } else { SET_STAT_INVALID(st); } } TALLOC_FREE(found_name); } /* end else */ #ifdef DEVELOPER /* * This sucks! * We should never provide different behaviors * depending on DEVELOPER!!! */ if (VALID_STAT(st)) { bool delete_pending; get_file_infos(vfs_file_id_from_sbuf(conn, &st), &delete_pending, NULL); if (delete_pending) { result = NT_STATUS_DELETE_PENDING; goto fail; } } #endif /* * Add to the dirpath that we have resolved so far. */ if (*dirpath != '\0') { char *tmp = talloc_asprintf(ctx, "%s/%s", dirpath, start); if (!tmp) { DEBUG(0, ("talloc_asprintf failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(dirpath); dirpath = tmp; } else { TALLOC_FREE(dirpath); if (!(dirpath = talloc_strdup(ctx,start))) { DEBUG(0, ("talloc_strdup failed\n")); return NT_STATUS_NO_MEMORY; } } /* * Don't cache a name with mangled or wildcard components * as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, dirpath, conn->case_sensitive); } /* * Restore the / that we wiped out earlier. */ if (end) { *end = '/'; } } /* * Don't cache a name with mangled or wildcard components * as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, name, conn->case_sensitive); } /* * The name has been resolved. */ DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); done: if (stream != NULL) { char *tmp = NULL; result = build_stream_path(ctx, conn, orig_path, name, stream, pst, &tmp); if (!NT_STATUS_IS_OK(result)) { goto fail; } DEBUG(10, ("build_stream_path returned %s\n", tmp)); TALLOC_FREE(name); name = tmp; } *pp_conv_path = name; TALLOC_FREE(dirpath); return NT_STATUS_OK; fail: DEBUG(10, ("dirpath = [%s] start = [%s]\n", dirpath, start)); if (*dirpath != '\0') { *pp_conv_path = talloc_asprintf(ctx, "%s/%s", dirpath, start); } else { *pp_conv_path = talloc_strdup(ctx, start); } if (!*pp_conv_path) { DEBUG(0, ("talloc_asprintf failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); TALLOC_FREE(dirpath); return result; }