s3: smbd: Remove unix_convert() and associated functions.
authorJeremy Allison <jra@samba.org>
Tue, 9 Aug 2022 04:59:14 +0000 (21:59 -0700)
committerJeremy Allison <jra@samba.org>
Fri, 12 Aug 2022 19:18:25 +0000 (19:18 +0000)
All code now uses filename_convert_dirfsp() for race-free
filename conversion.

Best viewed with:
$ git show --patience

               ----------------
              /                \
             /       REST       \
            /         IN         \
           /         PEACE        \
          /                        \
          |                        |
          |      unix_convert      |
          |                        |
          |                        |
          |       9th August       |
          |          2022          |
          |                        |
          |                        |
         *|     *  *  *            | *
_________)/\\_//(\/(/\)/\//\/\///\/|_)_______

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15144

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Fri Aug 12 19:18:25 UTC 2022 on sn-devel-184

source3/smbd/filename.c
source3/smbd/proto.h

index b64fb908f16cd85de88c410757d39cc67b24b7fc..f362aee94525274addd1e950dd7f4e8ce015c66f 100644 (file)
 #include "smbd/globals.h"
 #include "lib/util/memcache.h"
 
-static NTSTATUS get_real_filename(connection_struct *conn,
-                                 struct smb_filename *path,
-                                 const char *name,
-                                 TALLOC_CTX *mem_ctx,
-                                 char **found_name);
-
-static NTSTATUS check_name(connection_struct *conn,
-                          const struct smb_filename *smb_fname);
-
 uint32_t ucf_flags_from_smb_request(struct smb_request *req)
 {
        uint32_t ucf_flags = 0;
@@ -80,10 +71,6 @@ uint32_t filename_create_ucf_flags(struct smb_request *req, uint32_t create_disp
        return ucf_flags;
 }
 
-static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx,
-                                 connection_struct *conn,
-                                 struct smb_filename *smb_fname);
-
 /****************************************************************************
  Mangle the 2nd name and check if it is then equal to the first name.
 ****************************************************************************/
@@ -100,153 +87,6 @@ static bool mangled_equal(const char *name1,
        return strequal(name1, mname);
 }
 
-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;
-}
-
-/****************************************************************************
- Optimization for common case where the missing part
- is in the last component and the client already
- sent the correct case.
- Returns NT_STATUS_OK to mean continue the tree walk
- (possibly with modified start pointer).
- Any other NT_STATUS_XXX error means terminate the path
- lookup here.
-****************************************************************************/
-
-static NTSTATUS check_parent_exists(TALLOC_CTX *ctx,
-                               connection_struct *conn,
-                               bool posix_pathnames,
-                               const struct smb_filename *smb_fname,
-                               char **pp_dirpath,
-                               char **pp_start,
-                               int *p_parent_stat_errno)
-{
-       char *parent_name = NULL;
-       struct smb_filename *parent_fname = NULL;
-       const char *last_component = NULL;
-       NTSTATUS status;
-       int ret;
-
-       if (!parent_dirname(ctx, smb_fname->base_name,
-                               &parent_name,
-                               &last_component)) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       if (!posix_pathnames) {
-               if (ms_has_wild(parent_name)) {
-                       goto no_optimization_out;
-               }
-       }
-
-       /*
-        * If there was no parent component in
-        * smb_fname->base_name then don't do this
-        * optimization.
-        */
-       if (smb_fname->base_name == last_component) {
-               goto no_optimization_out;
-       }
-
-       parent_fname = synthetic_smb_fname(ctx,
-                                          parent_name,
-                                          NULL,
-                                          NULL,
-                                          smb_fname->twrp,
-                                          smb_fname->flags);
-       if (parent_fname == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       ret = vfs_stat(conn, parent_fname);
-
-       /* If the parent stat failed, just continue
-          with the normal tree walk. */
-
-       if (ret == -1) {
-               /*
-                * Optimization. Preserving the
-                * errno from the STAT/LSTAT here
-                * will allow us to save a duplicate
-                * STAT/LSTAT system call of the parent
-                * pathname in a hot code path in the caller.
-                */
-               if (p_parent_stat_errno != NULL) {
-                       *p_parent_stat_errno = errno;
-               }
-               goto no_optimization_out;
-       }
-
-       status = check_for_dot_component(parent_fname);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
-       /* Parent exists - set "start" to be the
-        * last component to shorten the tree walk. */
-
-       /*
-        * Safe to use discard_const_p
-        * here as last_component points
-        * into our smb_fname->base_name.
-        */
-       *pp_start = discard_const_p(char, last_component);
-
-       /* Update dirpath. */
-       TALLOC_FREE(*pp_dirpath);
-       *pp_dirpath = talloc_strdup(ctx, parent_fname->base_name);
-       if (!*pp_dirpath) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       DEBUG(5,("check_parent_exists: name "
-               "= %s, dirpath = %s, "
-               "start = %s\n",
-               smb_fname->base_name,
-               *pp_dirpath,
-               *pp_start));
-
-       return NT_STATUS_OK;
-
-  no_optimization_out:
-
-       /*
-        * We must still return an *pp_dirpath
-        * initialized to ".", and a *pp_start
-        * pointing at smb_fname->base_name.
-        */
-
-       TALLOC_FREE(parent_name);
-       TALLOC_FREE(parent_fname);
-
-       *pp_dirpath = talloc_strdup(ctx, ".");
-       if (*pp_dirpath == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       /*
-        * Safe to use discard_const_p
-        * here as by convention smb_fname->base_name
-        * is allocated off ctx.
-        */
-       *pp_start = discard_const_p(char, smb_fname->base_name);
-       return NT_STATUS_OK;
-}
-
 static bool find_snapshot_token(
        const char *filename,
        const char **_start,
@@ -255,1138 +95,148 @@ static bool find_snapshot_token(
 {
        const char *start = NULL;
        const char *end = NULL;
-       struct tm tm;
-       time_t t;
-
-       start = strstr_m(filename, "@GMT-");
-
-       if (start == NULL) {
-               return false;
-       }
-
-       if ((start > filename) && (start[-1] != '/')) {
-               /* the GMT-token does not start a path-component */
-               return false;
-       }
-
-       end = strptime(start, GMT_FORMAT, &tm);
-       if (end == NULL) {
-               /* Not a valid timestring. */
-               return false;
-       }
-
-       if ((end[0] != '\0') && (end[0] != '/')) {
-               /*
-                * It is not a complete path component, i.e. the path
-                * component continues after the gmt-token.
-                */
-               return false;
-       }
-
-       tm.tm_isdst = -1;
-       t = timegm(&tm);
-       unix_to_nt_time(twrp, t);
-
-       DBG_DEBUG("Extracted @GMT-Timestamp %s\n",
-                 nt_time_string(talloc_tos(), *twrp));
-
-       *_start = start;
-
-       if (end[0] == '/') {
-               end += 1;
-       }
-       *_next_component = end;
-
-       return true;
-}
-
-bool extract_snapshot_token(char *fname, NTTIME *twrp)
-{
-       const char *start = NULL;
-       const char *next = NULL;
-       size_t remaining;
-       bool found;
-
-       found = find_snapshot_token(fname, &start, &next, twrp);
-       if (!found) {
-               return false;
-       }
-
-       remaining = strlen(next);
-       memmove(discard_const_p(char, start), next, remaining+1);
-
-       return true;
-}
-
-/*
- * Strip a valid @GMT-token from any incoming filename path,
- * adding any NTTIME encoded in the pathname into the
- * twrp field of the passed in smb_fname.
- *
- * Valid @GMT-tokens look like @GMT-YYYY-MM-DD-HH-MM-SS
- * at the *start* of a pathname component.
- *
- * If twrp is passed in then smb_fname->twrp is set to that
- * value, and the @GMT-token part of the filename is removed
- * and does not change the stored smb_fname->twrp.
- *
- */
-
-NTSTATUS canonicalize_snapshot_path(struct smb_filename *smb_fname,
-                                   uint32_t ucf_flags,
-                                   NTTIME twrp)
-{
-       bool found;
-
-       if (twrp != 0) {
-               smb_fname->twrp = twrp;
-       }
-
-       if (!(ucf_flags & UCF_GMT_PATHNAME)) {
-               return NT_STATUS_OK;
-       }
-
-       found = extract_snapshot_token(smb_fname->base_name, &twrp);
-       if (!found) {
-               return NT_STATUS_OK;
-       }
-
-       if (smb_fname->twrp == 0) {
-               smb_fname->twrp = twrp;
-       }
-
-       return NT_STATUS_OK;
-}
-
-static bool strnorm(char *s, int case_default)
-{
-       if (case_default == CASE_UPPER)
-               return strupper_m(s);
-       else
-               return strlower_m(s);
-}
-
-/*
- * Utility function to normalize case on an incoming client filename
- * if required on this connection struct.
- * Performs an in-place case conversion guaranteed to stay the same size.
- */
-
-static NTSTATUS normalize_filename_case(connection_struct *conn,
-                                       char *filename,
-                                       uint32_t ucf_flags)
-{
-       bool ok;
-
-       if (ucf_flags & UCF_POSIX_PATHNAMES) {
-               /*
-                * POSIX never normalizes filename case.
-                */
-               return NT_STATUS_OK;
-       }
-       if (!conn->case_sensitive) {
-               return NT_STATUS_OK;
-       }
-       if (conn->case_preserve) {
-               return NT_STATUS_OK;
-       }
-       if (conn->short_case_preserve) {
-               return NT_STATUS_OK;
-       }
-       ok = strnorm(filename, lp_default_case(SNUM(conn)));
-       if (!ok) {
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-       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,
-streams etc.
-
-We assume that we have already done a chdir() to the right "root" directory
-for this service.
-
-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().
-
-The function will return an NTSTATUS error if some part of the name except for
-the last part cannot be resolved, else NT_STATUS_OK.
-
-Note NT_STATUS_OK doesn't mean the name exists or is valid, just that we
-didn't get any fatal errors that should immediately terminate the calling SMB
-processing whilst resolving.
-
-If the orig_path was a stream, smb_filename->base_name will point to the base
-filename, and smb_filename->stream_name will point to the stream name.  If
-orig_path was not a stream, then smb_filename->stream_name will be NULL.
-
-On exit from unix_convert, the smb_filename->st stat struct will be populated
-if the file exists and was found, if not this stat struct will be filled with
-zeros (and this can be detected by checking for nlinks = 0, which can never be
-true for any file).
-****************************************************************************/
-
-struct uc_state {
-       TALLOC_CTX *mem_ctx;
-       struct connection_struct *conn;
-       struct smb_filename *smb_fname;
-       const char *orig_path;
-       uint32_t ucf_flags;
-       char *name;
-       char *end;
-       char *dirpath;
-       char *stream;
-       bool component_was_mangled;
-       bool posix_pathnames;
-       bool done;
-       bool case_sensitive;
-       bool case_preserve;
-       bool short_case_preserve;
-};
-
-static NTSTATUS unix_convert_step_search_fail(
-       struct uc_state *state, NTSTATUS status)
-{
-       char *unmangled;
-
-       if (state->end) {
-               /*
-                * An intermediate part of the name
-                * can't be found.
-                */
-               DBG_DEBUG("Intermediate [%s] missing\n",
-                         state->name);
-               *state->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 (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
-                   NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
-                   NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
-                       status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
-               }
-               return status;
-       }
-
-       /*
-        * ENOENT/EACCESS are the only valid errors
-        * here.
-        */
-
-       if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
-               if ((state->ucf_flags & UCF_PREP_CREATEFILE) == 0) {
-                       /*
-                        * Could be a symlink pointing to
-                        * a directory outside the share
-                        * to which we don't have access.
-                        * If so, we need to know that here
-                        * so we can return the correct error code.
-                        * check_name() is never called if we
-                        * error out of filename_convert().
-                        */
-                       int ret;
-                       struct smb_filename dname = (struct smb_filename) {
-                                       .base_name = state->dirpath,
-                                       .twrp = state->smb_fname->twrp,
-                       };
-
-                       /* handle null paths */
-                       if ((dname.base_name == NULL) ||
-                                       (dname.base_name[0] == '\0')) {
-                               return NT_STATUS_ACCESS_DENIED;
-                       }
-                       ret = SMB_VFS_LSTAT(state->conn, &dname);
-                       if (ret != 0) {
-                               return NT_STATUS_ACCESS_DENIED;
-                       }
-                       if (!S_ISLNK(dname.st.st_ex_mode)) {
-                               return NT_STATUS_ACCESS_DENIED;
-                       }
-                       status = check_name(state->conn, &dname);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               /* We know this is an intermediate path. */
-                               return NT_STATUS_OBJECT_PATH_NOT_FOUND;
-                       }
-                       return NT_STATUS_ACCESS_DENIED;
-               } else {
-                       /*
-                        * This is the dropbox
-                        * behaviour. A dropbox is a
-                        * directory with only -wx
-                        * permissions, so
-                        * get_real_filename fails
-                        * with EACCESS, it needs to
-                        * list the directory. We
-                        * nevertheless want to allow
-                        * users creating a file.
-                        */
-                       status = NT_STATUS_OK;
-               }
-       }
-
-       if (!NT_STATUS_IS_OK(status) &&
-           !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
-               /*
-                * ENOTDIR and ELOOP both map to
-                * NT_STATUS_OBJECT_PATH_NOT_FOUND
-                * in the filename walk.
-                */
-               if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY) ||
-                   NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
-                       status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
-               }
-               return status;
-       }
-
-       /*
-        * POSIX pathnames must never call into mangling.
-        */
-       if (state->posix_pathnames) {
-               goto done;
-       }
-
-       /*
-        * 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 <t.neumann@iku-ag.de>.
-        */
-       if (!state->case_preserve ||
-           (mangle_is_8_3(state->name, false,
-                          state->conn->params) &&
-            !state->short_case_preserve)) {
-               if (!strnorm(state->name,
-                            lp_default_case(SNUM(state->conn)))) {
-                       DBG_DEBUG("strnorm %s failed\n",
-                                 state->name);
-                       return NT_STATUS_INVALID_PARAMETER;
-               }
-       }
-
-       /*
-        * check on the mangled stack to see if we can
-        * recover the base of the filename.
-        */
-
-       if (mangle_is_mangled(state->name, state->conn->params)
-           && mangle_lookup_name_from_8_3(state->mem_ctx,
-                                          state->name,
-                                          &unmangled,
-                                          state->conn->params)) {
-               char *tmp;
-               size_t name_ofs =
-                       state->name - state->smb_fname->base_name;
-
-               if (!ISDOT(state->dirpath)) {
-                       tmp = talloc_asprintf(
-                               state->smb_fname, "%s/%s",
-                               state->dirpath, unmangled);
-                       TALLOC_FREE(unmangled);
-               }
-               else {
-                       tmp = unmangled;
-               }
-               if (tmp == NULL) {
-                       DBG_ERR("talloc failed\n");
-                       return NT_STATUS_NO_MEMORY;
-               }
-               TALLOC_FREE(state->smb_fname->base_name);
-               state->smb_fname->base_name = tmp;
-               state->name =
-                       state->smb_fname->base_name + name_ofs;
-               state->end = state->name + strlen(state->name);
-       }
-
-  done:
-
-       DBG_DEBUG("New file [%s]\n", state->name);
-       state->done = true;
-       return NT_STATUS_OK;
-}
-
-static NTSTATUS unix_convert_step_stat(struct uc_state *state)
-{
-       struct smb_filename dname;
-       char dot[2] = ".";
-       char *found_name = NULL;
-       int ret;
-       NTSTATUS status;
-
-       /*
-        * Check if the name exists up to this point.
-        */
-
-       DBG_DEBUG("smb_fname [%s]\n", smb_fname_str_dbg(state->smb_fname));
-
-       ret = vfs_stat(state->conn, state->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 (state->end && !S_ISDIR(state->smb_fname->st.st_ex_mode)) {
-                       /*
-                        * An intermediate part of the name isn't
-                        * a directory.
-                        */
-                       DBG_DEBUG("Not a dir [%s]\n", state->name);
-                       *state->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.
-                        */
-                       return NT_STATUS_OBJECT_PATH_NOT_FOUND;
-               }
-               return NT_STATUS_OK;
-       }
-
-       /* Stat failed - ensure we don't use it. */
-       SET_STAT_INVALID(state->smb_fname->st);
-
-       if (state->posix_pathnames) {
-               /*
-                * For posix_pathnames, we're done.
-                * Don't blunder into the
-                * get_real_filename() codepath as they may
-                * be doing case insensitive lookups. So when
-                * creating a new POSIX directory Foo they might
-                * match on name foo.
-                *
-                * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13803
-                */
-               if (state->end != NULL) {
-                       const char *morepath = NULL;
-                       /*
-                        * If this is intermediate we must
-                        * restore the full path.
-                        */
-                       *state->end = '/';
-                       /*
-                        * If there are any more components
-                        * after the failed LSTAT we cannot
-                        * continue.
-                        */
-                       morepath = strchr(state->end + 1, '/');
-                       if (morepath != NULL) {
-                               return NT_STATUS_OBJECT_PATH_NOT_FOUND;
-                       }
-               }
-               if (errno == ENOENT) {
-                       /* New file or directory. */
-                       state->done = true;
-                       return NT_STATUS_OK;
-               }
-               if ((errno == EACCES) &&
-                   (state->ucf_flags & UCF_PREP_CREATEFILE)) {
-                       /* POSIX Dropbox case. */
-                       errno = 0;
-                       state->done = true;
-                       return NT_STATUS_OK;
-               }
-               return map_nt_error_from_unix(errno);
-       }
-
-       /*
-        * Reset errno so we can detect
-        * directory open errors.
-        */
-       errno = 0;
-
-       /*
-        * Try to find this part of the path in the directory.
-        */
-
-       dname = (struct smb_filename) {
-               .base_name = state->dirpath,
-               .twrp = state->smb_fname->twrp,
-       };
-
-       /* handle null paths */
-       if ((dname.base_name == NULL) || (dname.base_name[0] == '\0')) {
-               dname.base_name = dot;
-       }
-
-       status = get_real_filename(state->conn,
-                                  &dname,
-                                  state->name,
-                                  talloc_tos(),
-                                  &found_name);
-       if (!NT_STATUS_IS_OK(status)) {
-               return unix_convert_step_search_fail(state, status);
-       }
-
-       /*
-        * Restore the rest of the string. If the string was
-        * mangled the size may have changed.
-        */
-       if (state->end) {
-               char *tmp;
-               size_t name_ofs =
-                       state->name - state->smb_fname->base_name;
-
-               if (!ISDOT(state->dirpath)) {
-                       tmp = talloc_asprintf(state->smb_fname,
-                                             "%s/%s/%s", state->dirpath,
-                                             found_name, state->end+1);
-               }
-               else {
-                       tmp = talloc_asprintf(state->smb_fname,
-                                             "%s/%s", found_name,
-                                             state->end+1);
-               }
-               if (tmp == NULL) {
-                       DBG_ERR("talloc_asprintf failed\n");
-                       return NT_STATUS_NO_MEMORY;
-               }
-               TALLOC_FREE(state->smb_fname->base_name);
-               state->smb_fname->base_name = tmp;
-               state->name = state->smb_fname->base_name + name_ofs;
-               state->end = state->name + strlen(found_name);
-               *state->end = '\0';
-       } else {
-               char *tmp;
-               size_t name_ofs =
-                       state->name - state->smb_fname->base_name;
-
-               if (!ISDOT(state->dirpath)) {
-                       tmp = talloc_asprintf(state->smb_fname,
-                                             "%s/%s", state->dirpath,
-                                             found_name);
-               } else {
-                       tmp = talloc_strdup(state->smb_fname,
-                                           found_name);
-               }
-               if (tmp == NULL) {
-                       DBG_ERR("talloc failed\n");
-                       return NT_STATUS_NO_MEMORY;
-               }
-               TALLOC_FREE(state->smb_fname->base_name);
-               state->smb_fname->base_name = tmp;
-               state->name = state->smb_fname->base_name + name_ofs;
-
-               /*
-                * We just scanned for, and found the end of
-                * the path. We must return a valid stat struct
-                * if it exists. JRA.
-                */
-
-               ret = vfs_stat(state->conn, state->smb_fname);
-               if (ret != 0) {
-                       SET_STAT_INVALID(state->smb_fname->st);
-               }
-       }
-
-       TALLOC_FREE(found_name);
-       return NT_STATUS_OK;
-}
-
-static NTSTATUS unix_convert_step(struct uc_state *state)
-{
-       NTSTATUS status;
-
-       /*
-        * Pinpoint the end of this section of the filename.
-        */
-       /* mb safe. '/' can't be in any encoded char. */
-       state->end = strchr(state->name, '/');
-
-       /*
-        * Chop the name at this point.
-        */
-       if (state->end != NULL) {
-               *state->end = 0;
-       }
-
-       DBG_DEBUG("dirpath [%s] name [%s]\n", state->dirpath, state->name);
-
-       /* The name cannot have a component of "." */
-
-       if (ISDOT(state->name)) {
-               if (state->end == NULL)  {
-                       /* Error code at the end of a pathname. */
-                       return NT_STATUS_OBJECT_NAME_INVALID;
-               }
-               return NT_STATUS_OBJECT_PATH_NOT_FOUND;
-       }
-
-       status = unix_convert_step_stat(state);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-       if (state->done) {
-               return NT_STATUS_OK;
-       }
-
-       /*
-        * Add to the dirpath that we have resolved so far.
-        */
-
-       if (!ISDOT(state->dirpath)) {
-               char *tmp = talloc_asprintf(state->mem_ctx,
-                                           "%s/%s", state->dirpath, state->name);
-               if (!tmp) {
-                       DBG_ERR("talloc_asprintf failed\n");
-                       return NT_STATUS_NO_MEMORY;
-               }
-               TALLOC_FREE(state->dirpath);
-               state->dirpath = tmp;
-       }
-       else {
-               TALLOC_FREE(state->dirpath);
-               if (!(state->dirpath = talloc_strdup(state->mem_ctx,state->name))) {
-                       DBG_ERR("talloc_strdup failed\n");
-                       return NT_STATUS_NO_MEMORY;
-               }
-       }
-
-       /*
-        * Cache the dirpath thus far. Don't cache a name with mangled
-        * components as this can change the size.
-        */
-       if(!state->component_was_mangled) {
-               stat_cache_add(state->orig_path,
-                              state->dirpath,
-                              state->smb_fname->twrp,
-                              state->case_sensitive);
-       }
-
-       /*
-        * Restore the / that we wiped out earlier.
-        */
-       if (state->end != NULL) {
-               *state->end = '/';
-       }
-
-       return NT_STATUS_OK;
-}
-
-NTSTATUS unix_convert(TALLOC_CTX *mem_ctx,
-                     connection_struct *conn,
-                     const char *orig_path,
-                     NTTIME twrp,
-                     struct smb_filename **smb_fname_out,
-                     uint32_t ucf_flags)
-{
-       struct uc_state uc_state;
-       struct uc_state *state = &uc_state;
-       NTSTATUS status;
-       int ret = -1;
-       int parent_stat_errno = 0;
-
-       *state = (struct uc_state) {
-               .mem_ctx = mem_ctx,
-               .conn = conn,
-               .orig_path = orig_path,
-               .ucf_flags = ucf_flags,
-               .posix_pathnames = (ucf_flags & UCF_POSIX_PATHNAMES),
-               .case_sensitive = conn->case_sensitive,
-               .case_preserve = conn->case_preserve,
-               .short_case_preserve = conn->short_case_preserve,
-       };
-
-       *smb_fname_out = NULL;
-
-       if (state->posix_pathnames) {
-               /* POSIX means ignore case settings on share. */
-               state->case_sensitive = true;
-               state->case_preserve = true;
-               state->short_case_preserve = true;
-       }
-
-       state->smb_fname = talloc_zero(state->mem_ctx, struct smb_filename);
-       if (state->smb_fname == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       if (state->conn->printer) {
-               /* we don't ever use the filenames on a printer share as a
-                       filename - so don't convert them */
-               state->smb_fname->base_name = talloc_strdup(
-                       state->smb_fname, state->orig_path);
-               if (state->smb_fname->base_name == NULL) {
-                       status = NT_STATUS_NO_MEMORY;
-                       goto err;
-               }
-               goto done;
-       }
-
-       state->smb_fname->flags = state->posix_pathnames ? SMB_FILENAME_POSIX_PATH : 0;
-
-       DBG_DEBUG("Called on file [%s]\n", state->orig_path);
-
-       if (state->orig_path[0] == '/') {
-               DBG_ERR("Path [%s] starts with '/'\n", state->orig_path);
-               return NT_STATUS_OBJECT_NAME_INVALID;
-       }
-
-       /* Start with the full orig_path as given by the caller. */
-       state->smb_fname->base_name = talloc_strdup(
-               state->smb_fname, state->orig_path);
-       if (state->smb_fname->base_name == NULL) {
-               DBG_ERR("talloc_strdup failed\n");
-               status = NT_STATUS_NO_MEMORY;
-               goto err;
-       }
-
-       /* Canonicalize any @GMT- paths. */
-       status = canonicalize_snapshot_path(state->smb_fname, ucf_flags, twrp);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto err;
-       }
-
-       /*
-        * 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 (state->smb_fname->base_name[0] == '\0') {
-               state->smb_fname->base_name = talloc_strdup(state->smb_fname, ".");
-               if (state->smb_fname->base_name == NULL) {
-                       status = NT_STATUS_NO_MEMORY;
-                       goto err;
-               }
-               if (SMB_VFS_STAT(state->conn, state->smb_fname) != 0) {
-                       status = map_nt_error_from_unix(errno);
-                       goto err;
-               }
-               DBG_DEBUG("conversion finished [] -> [%s]\n",
-                         state->smb_fname->base_name);
-               goto done;
-       }
-
-       if (state->orig_path[0] == '.' && (state->orig_path[1] == '/' ||
-                               state->orig_path[1] == '\0')) {
-               /* Start of pathname can't be "." only. */
-               if (state->orig_path[1] == '\0' || state->orig_path[2] == '\0') {
-                       status = NT_STATUS_OBJECT_NAME_INVALID;
-               } else {
-                       status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
-               }
-               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 jht@samba.org for finding this. JRA.
-        */
-
-       status = normalize_filename_case(state->conn,
-                                        state->smb_fname->base_name,
-                                        ucf_flags);
-       if (!NT_STATUS_IS_OK(status)) {
-               DBG_ERR("normalize_filename_case %s failed\n",
-                               state->smb_fname->base_name);
-               goto err;
-       }
-
-       /*
-        * Strip off the stream, and add it back when we're done with the
-        * base_name.
-        */
-       if (!state->posix_pathnames) {
-               state->stream = strchr_m(state->smb_fname->base_name, ':');
-
-               if (state->stream != NULL) {
-                       char *tmp = talloc_strdup(state->smb_fname, state->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.
-                        */
-                       *state->stream = '\0';
-                       state->stream = tmp;
-
-                       if (state->smb_fname->base_name[0] == '\0') {
-                               /*
-                                * orig_name was just a stream name.
-                                * This is a stream on the root of
-                                * the share. Replace base_name with
-                                * a "."
-                                */
-                               state->smb_fname->base_name =
-                                       talloc_strdup(state->smb_fname, ".");
-                               if (state->smb_fname->base_name == NULL) {
-                                       status = NT_STATUS_NO_MEMORY;
-                                       goto err;
-                               }
-                               if (SMB_VFS_STAT(state->conn, state->smb_fname) != 0) {
-                                       status = map_nt_error_from_unix(errno);
-                                       goto err;
-                               }
-                               /* dirpath must exist. */
-                               state->dirpath = talloc_strdup(state->mem_ctx,".");
-                               if (state->dirpath == NULL) {
-                                       status = NT_STATUS_NO_MEMORY;
-                                       goto err;
-                               }
-                               DBG_INFO("conversion finished [%s] -> [%s]\n",
-                                        state->orig_path,
-                                        state->smb_fname->base_name);
-                               goto done;
-                       }
-               }
-       }
-
-       state->name = state->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
-        * authoritative. JRA.
-        *
-        * Note: We're only checking base_name.  The stream_name will be
-        * added and verified in build_stream_path().
-        */
-
-       if (!state->case_sensitive ||
-           !(state->conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH))
-       {
-               bool found;
-
-               found = stat_cache_lookup(state->conn,
-                                         &state->smb_fname->base_name,
-                                         &state->dirpath,
-                                         &state->name,
-                                         state->smb_fname->twrp,
-                                         &state->smb_fname->st);
-               /*
-                * stat_cache_lookup() allocates on talloc_tos() even
-                * when !found, reparent correctly
-                */
-               talloc_steal(state->smb_fname, state->smb_fname->base_name);
-               talloc_steal(state->mem_ctx, state->dirpath);
-
-               if (found) {
-                       goto done;
-               }
-       }
-
-       /*
-        * Make sure "dirpath" is an allocated string, we use this for
-        * building the directories with talloc_asprintf and free it.
-        */
-
-       if (state->dirpath == NULL) {
-               state->dirpath = talloc_strdup(state->mem_ctx,".");
-               if (state->dirpath == NULL) {
-                       DBG_ERR("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.
-        */
-
-       if (!state->posix_pathnames) {
-               /* POSIX pathnames have no wildcards. */
-               bool name_has_wildcard = ms_has_wild(state->smb_fname->base_name);
-               if (name_has_wildcard) {
-                       /* Wildcard not valid anywhere. */
-                       status = NT_STATUS_OBJECT_NAME_INVALID;
-                       goto fail;
-               }
-       }
-
-       DBG_DEBUG("Begin: name [%s] dirpath [%s] name [%s]\n",
-                 state->smb_fname->base_name, state->dirpath, state->name);
-
-       /*
-        * stat the name - if it exists then we can add the stream back (if
-        * there was one) and be done!
-        */
-
-       ret = vfs_stat(state->conn, state->smb_fname);
-       if (ret == 0) {
-               status = check_for_dot_component(state->smb_fname);
-               if (!NT_STATUS_IS_OK(status)) {
-                       goto fail;
-               }
-               /* Add the path (not including the stream) to the cache. */
-               stat_cache_add(state->orig_path,
-                              state->smb_fname->base_name,
-                              state->smb_fname->twrp,
-                              state->case_sensitive);
-               DBG_DEBUG("Conversion of base_name finished "
-                         "[%s] -> [%s]\n",
-                         state->orig_path, state->smb_fname->base_name);
-               goto done;
-       }
-
-       /* Stat failed - ensure we don't use it. */
-       SET_STAT_INVALID(state->smb_fname->st);
-
-       /*
-        * Note: we must continue processing a path if we get EACCES
-        * from stat. With NFS4 permissions the file might be lacking
-        * READ_ATTR, but if the parent has LIST permissions we can
-        * resolve the path in the path traversal loop down below.
-        */
-
-       if (errno == ENOENT) {
-               /* Optimization when creating a new file - only
-                  the last component doesn't exist.
-                  NOTE : check_parent_exists() doesn't preserve errno.
-               */
-               int saved_errno = errno;
-               status = check_parent_exists(state->mem_ctx,
-                                       state->conn,
-                                       state->posix_pathnames,
-                                       state->smb_fname,
-                                       &state->dirpath,
-                                       &state->name,
-                                       &parent_stat_errno);
-               errno = saved_errno;
-               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.
-        *
-        * NB. As POSIX sets state->case_sensitive as
-        * true we will never call into mangle_is_mangled() here.
-        */
-
-       if ((state->case_sensitive || !(state->conn->fs_capabilities &
-                               FILE_CASE_SENSITIVE_SEARCH)) &&
-                       !mangle_is_mangled(state->smb_fname->base_name, state->conn->params)) {
-
-               status = check_for_dot_component(state->smb_fname);
-               if (!NT_STATUS_IS_OK(status)) {
-                       goto fail;
-               }
+       struct tm tm;
+       time_t t;
 
-               /*
-                * The stat failed. Could be ok as it could be
-                * a new file.
-                */
+       start = strstr_m(filename, "@GMT-");
 
-               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 ?
-                        *
-                        * Optimization.
-                        *
-                        * For this code path we can guarantee that
-                        * we have gone through check_parent_exists()
-                        * and it returned NT_STATUS_OK.
-                        *
-                        * Either there was no parent component (".")
-                        * parent_stat_errno == 0 and we have a missing
-                        * last component here.
-                        *
-                        * OR check_parent_exists() called STAT/LSTAT
-                        * and if it failed parent_stat_errno has been
-                        * set telling us if the parent existed or not.
-                        *
-                        * Either way we can avoid another STAT/LSTAT
-                        * system call on the parent here.
-                        */
-                       if (parent_stat_errno == ENOTDIR ||
-                                       parent_stat_errno == ENOENT ||
-                                       parent_stat_errno == ELOOP) {
-                               status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
-                               goto fail;
-                       }
+       if (start == NULL) {
+               return false;
+       }
 
-                       /*
-                        * Missing last component is ok - new file.
-                        * Also deal with permission denied elsewhere.
-                        * Just drop out to done.
-                        */
-                       goto done;
-               }
+       if ((start > filename) && (start[-1] != '/')) {
+               /* the GMT-token does not start a path-component */
+               return false;
        }
 
-       /*
-        * is_mangled() was changed to look at an entire pathname, not
-        * just a component. JRA.
-        */
+       end = strptime(start, GMT_FORMAT, &tm);
+       if (end == NULL) {
+               /* Not a valid timestring. */
+               return false;
+       }
 
-       if (state->posix_pathnames) {
+       if ((end[0] != '\0') && (end[0] != '/')) {
                /*
-                * POSIX names are never mangled and we must not
-                * call into mangling functions.
+                * It is not a complete path component, i.e. the path
+                * component continues after the gmt-token.
                 */
-               state->component_was_mangled = false;
-       } else if (mangle_is_mangled(state->name, state->conn->params)) {
-               state->component_was_mangled = true;
+               return false;
        }
 
-       /*
-        * Now we need to recursively match the name against the real
-        * directory structure.
-        */
+       tm.tm_isdst = -1;
+       t = timegm(&tm);
+       unix_to_nt_time(twrp, t);
 
-       /*
-        * Match each part of the path name separately, trying the names
-        * as is first, then trying to scan the directory for matching names.
-        */
+       DBG_DEBUG("Extracted @GMT-Timestamp %s\n",
+                 nt_time_string(talloc_tos(), *twrp));
 
-       for (; state->name ; state->name = (state->end ? state->end + 1:(char *)NULL)) {
-               status = unix_convert_step(state);
-               if (!NT_STATUS_IS_OK(status)) {
-                       if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
-                               goto err;
-                       }
-                       goto fail;
-               }
-               if (state->done) {
-                       goto done;
-               }
+       *_start = start;
+
+       if (end[0] == '/') {
+               end += 1;
        }
+       *_next_component = end;
 
-       /*
-        * Cache the full path. Don't cache a name with mangled or wildcard
-        * components as this can change the size.
-        */
+       return true;
+}
+
+bool extract_snapshot_token(char *fname, NTTIME *twrp)
+{
+       const char *start = NULL;
+       const char *next = NULL;
+       size_t remaining;
+       bool found;
 
-       if(!state->component_was_mangled) {
-               stat_cache_add(state->orig_path,
-                              state->smb_fname->base_name,
-                              state->smb_fname->twrp,
-                              state->case_sensitive);
+       found = find_snapshot_token(fname, &start, &next, twrp);
+       if (!found) {
+               return false;
        }
 
-       /*
-        * The name has been resolved.
-        */
+       remaining = strlen(next);
+       memmove(discard_const_p(char, start), next, remaining+1);
 
- done:
-       /* Add back the stream if one was stripped off originally. */
-       if (state->stream != NULL) {
-               state->smb_fname->stream_name = state->stream;
+       return true;
+}
 
-               /* Check path now that the base_name has been converted. */
-               status = build_stream_path(state->mem_ctx, state->conn, state->smb_fname);
-               if (!NT_STATUS_IS_OK(status)) {
-                       goto fail;
-               }
+/*
+ * Strip a valid @GMT-token from any incoming filename path,
+ * adding any NTTIME encoded in the pathname into the
+ * twrp field of the passed in smb_fname.
+ *
+ * Valid @GMT-tokens look like @GMT-YYYY-MM-DD-HH-MM-SS
+ * at the *start* of a pathname component.
+ *
+ * If twrp is passed in then smb_fname->twrp is set to that
+ * value, and the @GMT-token part of the filename is removed
+ * and does not change the stored smb_fname->twrp.
+ *
+ */
+
+NTSTATUS canonicalize_snapshot_path(struct smb_filename *smb_fname,
+                                   uint32_t ucf_flags,
+                                   NTTIME twrp)
+{
+       bool found;
+
+       if (twrp != 0) {
+               smb_fname->twrp = twrp;
        }
 
-       DBG_DEBUG("Conversion finished [%s] -> [%s]\n",
-                  state->orig_path, smb_fname_str_dbg(state->smb_fname));
+       if (!(ucf_flags & UCF_GMT_PATHNAME)) {
+               return NT_STATUS_OK;
+       }
 
-       TALLOC_FREE(state->dirpath);
-       *smb_fname_out = state->smb_fname;
-       return NT_STATUS_OK;
- fail:
-       DBG_DEBUG("Conversion failed: dirpath [%s] name [%s]\n",
-                 state->dirpath, state->name);
-       if ((state->dirpath != NULL) && !ISDOT(state->dirpath)) {
-               state->smb_fname->base_name = talloc_asprintf(
-                       state->smb_fname,
-                       "%s/%s",
-                       state->dirpath,
-                       state->name);
-       } else {
-               state->smb_fname->base_name = talloc_strdup(
-                       state->smb_fname, state->name);
+       found = extract_snapshot_token(smb_fname->base_name, &twrp);
+       if (!found) {
+               return NT_STATUS_OK;
        }
-       if (state->smb_fname->base_name == NULL) {
-               DBG_ERR("talloc_asprintf failed\n");
-               status = NT_STATUS_NO_MEMORY;
-               goto err;
+
+       if (smb_fname->twrp == 0) {
+               smb_fname->twrp = twrp;
        }
 
-       *smb_fname_out = state->smb_fname;
-       TALLOC_FREE(state->dirpath);
-       return status;
- err:
-       TALLOC_FREE(state->smb_fname);
-       return status;
+       return NT_STATUS_OK;
 }
 
-/****************************************************************************
- Ensure a path is not vetoed.
-****************************************************************************/
-
-static NTSTATUS check_veto_path(connection_struct *conn,
-                       const struct smb_filename *smb_fname)
+static bool strnorm(char *s, int case_default)
 {
-       const char *name = smb_fname->base_name;
-
-       if (IS_VETO_PATH(conn, name))  {
-               /* Is it not dot or dot dot. */
-               if (!(ISDOT(name) || ISDOTDOT(name))) {
-                       DEBUG(5,("check_veto_path: file path name %s vetoed\n",
-                                               name));
-                       return map_nt_error_from_unix(ENOENT);
-               }
-       }
-       return NT_STATUS_OK;
+       if (case_default == CASE_UPPER)
+               return strupper_m(s);
+       else
+               return strlower_m(s);
 }
 
-/****************************************************************************
- Check a filename - possibly calling check_reduced_name.
- This is called by every routine before it allows an operation on a filename.
- It does any final confirmation necessary to ensure that the filename is
- a valid one for the user to access.
-****************************************************************************/
+/*
+ * Utility function to normalize case on an incoming client filename
+ * if required on this connection struct.
+ * Performs an in-place case conversion guaranteed to stay the same size.
+ */
 
-static NTSTATUS check_name(connection_struct *conn,
-                       const struct smb_filename *smb_fname)
+static NTSTATUS normalize_filename_case(connection_struct *conn,
+                                       char *filename,
+                                       uint32_t ucf_flags)
 {
-       NTSTATUS status = check_veto_path(conn, smb_fname);
+       bool ok;
 
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       if (ucf_flags & UCF_POSIX_PATHNAMES) {
+               /*
+                * POSIX never normalizes filename case.
+                */
+               return NT_STATUS_OK;
        }
-
-       if (!lp_widelinks(SNUM(conn)) || !lp_follow_symlinks(SNUM(conn))) {
-               status = check_reduced_name(conn, NULL, smb_fname);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(5,("check_name: name %s failed with %s\n",
-                                       smb_fname->base_name,
-                                       nt_errstr(status)));
-                       return status;
-               }
+       if (!conn->case_sensitive) {
+               return NT_STATUS_OK;
+       }
+       if (conn->case_preserve) {
+               return NT_STATUS_OK;
+       }
+       if (conn->short_case_preserve) {
+               return NT_STATUS_OK;
+       }
+       ok = strnorm(filename, lp_default_case(SNUM(conn)));
+       if (!ok) {
+               return NT_STATUS_INVALID_PARAMETER;
        }
-
        return NT_STATUS_OK;
 }
 
@@ -1580,41 +430,6 @@ NTSTATUS get_real_filename_full_scan_at(struct files_struct *dirfsp,
        return NT_STATUS_OBJECT_NAME_NOT_FOUND;
 }
 
-NTSTATUS get_real_filename_full_scan(connection_struct *conn,
-                                    const char *path,
-                                    const char *name,
-                                    bool mangled,
-                                    TALLOC_CTX *mem_ctx,
-                                    char **found_name)
-{
-       struct smb_filename *smb_dname = NULL;
-       NTSTATUS status;
-
-       /* handle null paths */
-       if ((path == NULL) || (*path == 0)) {
-               path = ".";
-       }
-
-       status = synthetic_pathref(
-               talloc_tos(),
-               conn->cwd_fsp,
-               path,
-               NULL,
-               NULL,
-               0,
-               0,
-               &smb_dname);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
-       status = get_real_filename_full_scan_at(
-               smb_dname->fsp, name, mangled, mem_ctx, found_name);
-
-       TALLOC_FREE(smb_dname);
-       return status;
-}
-
 /****************************************************************************
  Wrapper around the vfs get_real_filename and the full directory scan
  fallback.
@@ -1703,172 +518,6 @@ static bool get_real_filename_cache_key(
        return true;
 }
 
-static NTSTATUS get_real_filename(connection_struct *conn,
-                                 struct smb_filename *path,
-                                 const char *name,
-                                 TALLOC_CTX *mem_ctx,
-                                 char **found_name)
-{
-       struct smb_filename *smb_dname = NULL;
-       NTSTATUS status;
-
-       smb_dname = cp_smb_filename_nostream(talloc_tos(), path);
-       if (smb_dname == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-again:
-       status = openat_pathref_fsp(conn->cwd_fsp, smb_dname);
-
-       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
-           S_ISLNK(smb_dname->st.st_ex_mode)) {
-               status = NT_STATUS_STOPPED_ON_SYMLINK;
-       }
-
-       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
-           (smb_dname->twrp != 0)) {
-               /*
-                * Retry looking at the non-snapshot path, copying the
-                * fallback mechanism from vfs_shadow_copy2.c when
-                * shadow_copy2_convert() fails. This path-based
-                * routine get_real_filename() should go away and be
-                * replaced with a fd-based one, so spoiling it with a
-                * shadow_copy2 specific mechanism should not be too
-                * bad.
-                */
-               smb_dname->twrp = 0;
-               goto again;
-       }
-
-       if (!NT_STATUS_IS_OK(status)) {
-               DBG_DEBUG("openat_pathref_fsp(%s) failed: %s\n",
-                         smb_fname_str_dbg(smb_dname),
-                         nt_errstr(status));
-
-               /*
-                * ENOTDIR and ELOOP both map to
-                * NT_STATUS_OBJECT_PATH_NOT_FOUND in the filename
-                * walk.
-                */
-               if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY) ||
-                   NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
-                       status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
-               }
-
-               return status;
-       }
-
-       status = get_real_filename_at(
-               smb_dname->fsp, name, mem_ctx, found_name);
-       TALLOC_FREE(smb_dname);
-       return status;
-}
-
-static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx,
-                                 connection_struct *conn,
-                                 struct smb_filename *smb_fname)
-{
-       NTSTATUS status;
-       unsigned int i, num_streams = 0;
-       struct stream_struct *streams = NULL;
-       struct smb_filename *pathref = NULL;
-
-       if (SMB_VFS_STAT(conn, smb_fname) == 0) {
-               DEBUG(10, ("'%s' exists\n", smb_fname_str_dbg(smb_fname)));
-               return NT_STATUS_OK;
-       }
-
-       if (errno != ENOENT) {
-               DEBUG(10, ("vfs_stat failed: %s\n", strerror(errno)));
-               status = map_nt_error_from_unix(errno);
-               goto fail;
-       }
-
-       if (smb_fname->fsp == NULL) {
-               status = synthetic_pathref(mem_ctx,
-                                       conn->cwd_fsp,
-                                       smb_fname->base_name,
-                                       NULL,
-                                       NULL,
-                                       smb_fname->twrp,
-                                       smb_fname->flags,
-                                       &pathref);
-               if (!NT_STATUS_IS_OK(status)) {
-                       if (NT_STATUS_EQUAL(status,
-                               NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
-                               TALLOC_FREE(pathref);
-                               SET_STAT_INVALID(smb_fname->st);
-                               return NT_STATUS_OK;
-                       }
-                       DBG_DEBUG("synthetic_pathref failed: %s\n",
-                                 nt_errstr(status));
-                       goto fail;
-               }
-       } else {
-               pathref = smb_fname;
-       }
-
-       /* Fall back to a case-insensitive scan of all streams on the file. */
-       status = vfs_fstreaminfo(pathref->fsp, mem_ctx,
-                               &num_streams, &streams);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
-               SET_STAT_INVALID(smb_fname->st);
-               TALLOC_FREE(pathref);
-               return NT_STATUS_OK;
-       }
-
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("vfs_fstreaminfo failed: %s\n", nt_errstr(status)));
-               goto fail;
-       }
-
-       for (i=0; i<num_streams; i++) {
-               bool equal = sname_equal(
-                       smb_fname->stream_name,
-                       streams[i].name,
-                       conn->case_sensitive);
-
-               DBG_DEBUG("comparing [%s] and [%s]: %sequal\n",
-                         smb_fname->stream_name,
-                         streams[i].name,
-                         equal ? "" : "not ");
-
-               if (equal) {
-                       break;
-               }
-       }
-
-       /* Couldn't find the stream. */
-       if (i == num_streams) {
-               SET_STAT_INVALID(smb_fname->st);
-               TALLOC_FREE(pathref);
-               TALLOC_FREE(streams);
-               return NT_STATUS_OK;
-       }
-
-       DEBUG(10, ("case insensitive stream. requested: %s, actual: %s\n",
-               smb_fname->stream_name, streams[i].name));
-
-
-       TALLOC_FREE(smb_fname->stream_name);
-       smb_fname->stream_name = talloc_strdup(smb_fname, streams[i].name);
-       if (smb_fname->stream_name == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto fail;
-       }
-
-       SET_STAT_INVALID(smb_fname->st);
-
-       if (SMB_VFS_STAT(conn, smb_fname) == 0) {
-               DEBUG(10, ("'%s' exists\n", smb_fname_str_dbg(smb_fname)));
-       }
-       status = NT_STATUS_OK;
- fail:
-       TALLOC_FREE(pathref);
-       TALLOC_FREE(streams);
-       return status;
-}
-
 /*
  * Lightweight function to just get last component
  * for rename / enumerate directory calls.
index bc305bce2966326a3d41f01d533f24247b24014b..5ac0f713958268b3fe028bd2d0a45ca2aad93e2b 100644 (file)
@@ -350,22 +350,10 @@ NTSTATUS sync_file(connection_struct *conn, files_struct *fsp, bool write_throug
 
 uint32_t ucf_flags_from_smb_request(struct smb_request *req);
 uint32_t filename_create_ucf_flags(struct smb_request *req, uint32_t create_disposition);
-NTSTATUS unix_convert(TALLOC_CTX *ctx,
-                     connection_struct *conn,
-                     const char *orig_path,
-                     NTTIME twrp,
-                     struct smb_filename **smb_fname,
-                     uint32_t ucf_flags);
 bool extract_snapshot_token(char *fname, NTTIME *twrp);
 NTSTATUS canonicalize_snapshot_path(struct smb_filename *smb_fname,
                                    uint32_t ucf_flags,
                                    NTTIME twrp);
-NTSTATUS get_real_filename_full_scan(connection_struct *conn,
-                                    const char *path,
-                                    const char *name,
-                                    bool mangled,
-                                    TALLOC_CTX *mem_ctx,
-                                    char **found_name);
 NTSTATUS get_real_filename_full_scan_at(struct files_struct *dirfsp,
                                        const char *name,
                                        bool mangled,