LCOV - code coverage report
Current view: top level - source3/smbd - filename.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 473 549 86.2 %
Date: 2024-02-28 12:06:22 Functions: 21 21 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    filename handling routines
       4             :    Copyright (C) Andrew Tridgell 1992-1998
       5             :    Copyright (C) Jeremy Allison 1999-2007
       6             :    Copyright (C) Ying Chen 2000
       7             :    Copyright (C) Volker Lendecke 2007
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : /*
      24             :  * New hash table stat cache code added by Ying Chen.
      25             :  */
      26             : 
      27             : #include "includes.h"
      28             : #include "system/filesys.h"
      29             : #include "fake_file.h"
      30             : #include "smbd/smbd.h"
      31             : #include "smbd/globals.h"
      32             : #include "libcli/smb/reparse.h"
      33             : #include "source3/smbd/dir.h"
      34             : 
      35      658520 : uint32_t ucf_flags_from_smb_request(struct smb_request *req)
      36             : {
      37      658520 :         uint32_t ucf_flags = 0;
      38             : 
      39      658520 :         if (req == NULL) {
      40           0 :                 return 0;
      41             :         }
      42             : 
      43      658520 :         if (req->posix_pathnames) {
      44        4435 :                 ucf_flags |= UCF_POSIX_PATHNAMES;
      45             : 
      46        4435 :                 if (!req->sconn->using_smb2) {
      47        2285 :                         ucf_flags |= UCF_LCOMP_LNK_OK;
      48             :                 }
      49             :         }
      50      658520 :         if (req->flags2 & FLAGS2_DFS_PATHNAMES) {
      51        1624 :                 ucf_flags |= UCF_DFS_PATHNAME;
      52             :         }
      53      658520 :         if (req->flags2 & FLAGS2_REPARSE_PATH) {
      54        4178 :                 ucf_flags |= UCF_GMT_PATHNAME;
      55             :         }
      56             : 
      57      648043 :         return ucf_flags;
      58             : }
      59             : 
      60      551415 : uint32_t filename_create_ucf_flags(struct smb_request *req, uint32_t create_disposition)
      61             : {
      62      551415 :         uint32_t ucf_flags = 0;
      63             : 
      64      551415 :         ucf_flags |= ucf_flags_from_smb_request(req);
      65             : 
      66      551415 :         switch (create_disposition) {
      67      357235 :         case FILE_OPEN:
      68             :         case FILE_OVERWRITE:
      69      357235 :                 break;
      70      193548 :         case FILE_SUPERSEDE:
      71             :         case FILE_CREATE:
      72             :         case FILE_OPEN_IF:
      73             :         case FILE_OVERWRITE_IF:
      74      193548 :                 ucf_flags |= UCF_PREP_CREATEFILE;
      75      193548 :                 break;
      76             :         }
      77             : 
      78      551415 :         return ucf_flags;
      79             : }
      80             : 
      81             : /****************************************************************************
      82             :  Mangle the 2nd name and check if it is then equal to the first name.
      83             : ****************************************************************************/
      84             : 
      85          72 : static bool mangled_equal(const char *name1,
      86             :                         const char *name2,
      87             :                         const struct share_params *p)
      88             : {
      89           0 :         char mname[13];
      90             : 
      91          72 :         if (!name_to_8_3(name2, mname, False, p)) {
      92           0 :                 return False;
      93             :         }
      94          72 :         return strequal(name1, mname);
      95             : }
      96             : 
      97             : /*
      98             :  * Strip a valid @GMT-token from any incoming filename path,
      99             :  * adding any NTTIME encoded in the pathname into the
     100             :  * twrp field of the passed in smb_fname.
     101             :  *
     102             :  * Valid @GMT-tokens look like @GMT-YYYY-MM-DD-HH-MM-SS
     103             :  * at the *start* of a pathname component.
     104             :  *
     105             :  * If twrp is passed in then smb_fname->twrp is set to that
     106             :  * value, and the @GMT-token part of the filename is removed
     107             :  * and does not change the stored smb_fname->twrp.
     108             :  *
     109             :  */
     110             : 
     111         128 : NTSTATUS canonicalize_snapshot_path(struct smb_filename *smb_fname,
     112             :                                     uint32_t ucf_flags,
     113             :                                     NTTIME twrp)
     114             : {
     115           0 :         bool found;
     116             : 
     117         128 :         if (twrp != 0) {
     118           0 :                 smb_fname->twrp = twrp;
     119             :         }
     120             : 
     121         128 :         if (!(ucf_flags & UCF_GMT_PATHNAME)) {
     122           0 :                 return NT_STATUS_OK;
     123             :         }
     124             : 
     125         128 :         found = extract_snapshot_token(smb_fname->base_name, &twrp);
     126         128 :         if (!found) {
     127         128 :                 return NT_STATUS_OK;
     128             :         }
     129             : 
     130           0 :         if (smb_fname->twrp == 0) {
     131           0 :                 smb_fname->twrp = twrp;
     132             :         }
     133             : 
     134           0 :         return NT_STATUS_OK;
     135             : }
     136             : 
     137         264 : static bool strnorm(char *s, int case_default)
     138             : {
     139         264 :         if (case_default == CASE_UPPER)
     140           0 :                 return strupper_m(s);
     141             :         else
     142         264 :                 return strlower_m(s);
     143             : }
     144             : 
     145             : /*
     146             :  * Utility function to normalize case on an incoming client filename
     147             :  * if required on this connection struct.
     148             :  * Performs an in-place case conversion guaranteed to stay the same size.
     149             :  */
     150             : 
     151     1295981 : static NTSTATUS normalize_filename_case(connection_struct *conn,
     152             :                                         char *filename,
     153             :                                         uint32_t ucf_flags)
     154             : {
     155       23613 :         bool ok;
     156             : 
     157     1295981 :         if (ucf_flags & UCF_POSIX_PATHNAMES) {
     158             :                 /*
     159             :                  * POSIX never normalizes filename case.
     160             :                  */
     161        4961 :                 return NT_STATUS_OK;
     162             :         }
     163     1291020 :         if (!conn->case_sensitive) {
     164     1288610 :                 return NT_STATUS_OK;
     165             :         }
     166        2410 :         if (conn->case_preserve) {
     167        2150 :                 return NT_STATUS_OK;
     168             :         }
     169         260 :         if (conn->short_case_preserve) {
     170           0 :                 return NT_STATUS_OK;
     171             :         }
     172         260 :         ok = strnorm(filename, lp_default_case(SNUM(conn)));
     173         260 :         if (!ok) {
     174           0 :                 return NT_STATUS_INVALID_PARAMETER;
     175             :         }
     176         260 :         return NT_STATUS_OK;
     177             : }
     178             : 
     179             : /****************************************************************************
     180             :  Check if two filenames are equal.
     181             :  This needs to be careful about whether we are case sensitive.
     182             : ****************************************************************************/
     183             : 
     184   183843640 : static bool fname_equal(const char *name1, const char *name2,
     185             :                 bool case_sensitive)
     186             : {
     187             :         /* Normal filename handling */
     188   183843640 :         if (case_sensitive) {
     189           0 :                 return(strcmp(name1,name2) == 0);
     190             :         }
     191             : 
     192   183843640 :         return(strequal(name1,name2));
     193             : }
     194             : 
     195        3972 : static bool sname_equal(const char *name1, const char *name2,
     196             :                 bool case_sensitive)
     197             : {
     198           0 :         bool match;
     199        3972 :         const char *s1 = NULL;
     200        3972 :         const char *s2 = NULL;
     201           0 :         size_t n1;
     202           0 :         size_t n2;
     203        3972 :         const char *e1 = NULL;
     204        3972 :         const char *e2 = NULL;
     205        3972 :         char *c1 = NULL;
     206        3972 :         char *c2 = NULL;
     207             : 
     208        3972 :         match = fname_equal(name1, name2, case_sensitive);
     209        3972 :         if (match) {
     210          28 :                 return true;
     211             :         }
     212             : 
     213        3944 :         if (name1[0] != ':') {
     214           0 :                 return false;
     215             :         }
     216        3944 :         if (name2[0] != ':') {
     217           0 :                 return false;
     218             :         }
     219        3944 :         s1 = &name1[1];
     220        3944 :         e1 = strchr(s1, ':');
     221        3944 :         if (e1 == NULL) {
     222         556 :                 n1 = strlen(s1);
     223             :         } else {
     224        3388 :                 n1 = PTR_DIFF(e1, s1);
     225             :         }
     226        3944 :         s2 = &name2[1];
     227        3944 :         e2 = strchr(s2, ':');
     228        3944 :         if (e2 == NULL) {
     229           0 :                 n2 = strlen(s2);
     230             :         } else {
     231        3944 :                 n2 = PTR_DIFF(e2, s2);
     232             :         }
     233             : 
     234             :         /* Normal filename handling */
     235        3944 :         if (case_sensitive) {
     236           0 :                 return (strncmp(s1, s2, n1) == 0);
     237             :         }
     238             : 
     239             :         /*
     240             :          * We can't use strnequal() here
     241             :          * as it takes the number of codepoints
     242             :          * and not the number of bytes.
     243             :          *
     244             :          * So we make a copy before calling
     245             :          * strequal().
     246             :          *
     247             :          * Note that we TALLOC_FREE() in reverse order
     248             :          * in order to avoid memory fragmentation.
     249             :          */
     250             : 
     251        3944 :         c1 = talloc_strndup(talloc_tos(), s1, n1);
     252        3944 :         c2 = talloc_strndup(talloc_tos(), s2, n2);
     253        3944 :         if (c1 == NULL || c2 == NULL) {
     254           0 :                 TALLOC_FREE(c2);
     255           0 :                 TALLOC_FREE(c1);
     256           0 :                 return (strncmp(s1, s2, n1) == 0);
     257             :         }
     258             : 
     259        3944 :         match = strequal(c1, c2);
     260        3944 :         TALLOC_FREE(c2);
     261        3944 :         TALLOC_FREE(c1);
     262        3944 :         return match;
     263             : }
     264             : 
     265             : /****************************************************************************
     266             :  Scan a directory to find a filename, matching without case sensitivity.
     267             :  If the name looks like a mangled name then try via the mangling functions
     268             : ****************************************************************************/
     269             : 
     270      287081 : NTSTATUS get_real_filename_full_scan_at(struct files_struct *dirfsp,
     271             :                                         const char *name,
     272             :                                         bool mangled,
     273             :                                         TALLOC_CTX *mem_ctx,
     274             :                                         char **found_name)
     275             : {
     276      287081 :         struct connection_struct *conn = dirfsp->conn;
     277      287081 :         struct smb_Dir *cur_dir = NULL;
     278      287081 :         const char *dname = NULL;
     279      287081 :         char *talloced = NULL;
     280      287081 :         char *unmangled_name = NULL;
     281         883 :         NTSTATUS status;
     282             : 
     283             :         /* If we have a case-sensitive filesystem, it doesn't do us any
     284             :          * good to search for a name. If a case variation of the name was
     285             :          * there, then the original stat(2) would have found it.
     286             :          */
     287      287081 :         if (!mangled && !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) {
     288           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     289             :         }
     290             : 
     291             :         /*
     292             :          * The incoming name can be mangled, and if we de-mangle it
     293             :          * here it will not compare correctly against the filename (name2)
     294             :          * read from the directory and then mangled by the name_to_8_3()
     295             :          * call. We need to mangle both names or neither.
     296             :          * (JRA).
     297             :          *
     298             :          * Fix for bug found by Dina Fine. If in case sensitive mode then
     299             :          * the mangle cache is no good (3 letter extension could be wrong
     300             :          * case - so don't demangle in this case - leave as mangled and
     301             :          * allow the mangling of the directory entry read (which is done
     302             :          * case insensitively) to match instead. This will lead to more
     303             :          * false positive matches but we fail completely without it. JRA.
     304             :          */
     305             : 
     306      287081 :         if (mangled && !conn->case_sensitive) {
     307         172 :                 mangled = !mangle_lookup_name_from_8_3(talloc_tos(), name,
     308             :                                                        &unmangled_name,
     309         172 :                                                        conn->params);
     310         172 :                 if (!mangled) {
     311             :                         /* Name is now unmangled. */
     312         136 :                         name = unmangled_name;
     313             :                 }
     314             :         }
     315             : 
     316             :         /* open the directory */
     317      287081 :         status = OpenDir_from_pathref(talloc_tos(), dirfsp, NULL, 0, &cur_dir);
     318      287081 :         if (!NT_STATUS_IS_OK(status)) {
     319           2 :                 DBG_NOTICE("scan dir didn't open dir [%s]: %s\n",
     320             :                            fsp_str_dbg(dirfsp),
     321             :                            nt_errstr(status));
     322           2 :                 TALLOC_FREE(unmangled_name);
     323           2 :                 return status;
     324             :         }
     325             : 
     326             :         /* now scan for matching names */
     327   184694255 :         while ((dname = ReadDirName(cur_dir, &talloced))) {
     328             : 
     329             :                 /* Is it dot or dot dot. */
     330   184413862 :                 if (ISDOT(dname) || ISDOTDOT(dname)) {
     331      574158 :                         TALLOC_FREE(talloced);
     332      574158 :                         continue;
     333             :                 }
     334             : 
     335             :                 /*
     336             :                  * At this point dname is the unmangled name.
     337             :                  * name is either mangled or not, depending on the state
     338             :                  * of the "mangled" variable. JRA.
     339             :                  */
     340             : 
     341             :                 /*
     342             :                  * Check mangled name against mangled name, or unmangled name
     343             :                  * against unmangled name.
     344             :                  */
     345             : 
     346   367679372 :                 if ((mangled && mangled_equal(name,dname,conn->params)) ||
     347   183839668 :                         fname_equal(name, dname, conn->case_sensitive)) {
     348             :                         /* we've found the file, change it's name and return */
     349        6686 :                         *found_name = talloc_strdup(mem_ctx, dname);
     350        6686 :                         TALLOC_FREE(unmangled_name);
     351        6686 :                         TALLOC_FREE(cur_dir);
     352        6686 :                         if (!*found_name) {
     353           0 :                                 TALLOC_FREE(talloced);
     354           0 :                                 return NT_STATUS_NO_MEMORY;
     355             :                         }
     356        6686 :                         TALLOC_FREE(talloced);
     357        6686 :                         return NT_STATUS_OK;
     358             :                 }
     359   183839687 :                 TALLOC_FREE(talloced);
     360             :         }
     361             : 
     362      280393 :         TALLOC_FREE(unmangled_name);
     363      280393 :         TALLOC_FREE(cur_dir);
     364      280393 :         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     365             : }
     366             : 
     367             : /****************************************************************************
     368             :  Wrapper around the vfs get_real_filename and the full directory scan
     369             :  fallback.
     370             : ****************************************************************************/
     371             : 
     372      287081 : NTSTATUS get_real_filename_at(struct files_struct *dirfsp,
     373             :                               const char *name,
     374             :                               TALLOC_CTX *mem_ctx,
     375             :                               char **found_name)
     376             : {
     377      287081 :         struct connection_struct *conn = dirfsp->conn;
     378         883 :         NTSTATUS status;
     379         883 :         bool mangled;
     380             : 
     381      287081 :         mangled = mangle_is_mangled(name, conn->params);
     382             : 
     383      287081 :         if (mangled) {
     384         172 :                 status = get_real_filename_full_scan_at(
     385             :                         dirfsp, name, mangled, mem_ctx, found_name);
     386         172 :                 return status;
     387             :         }
     388             : 
     389             :         /* Try the vfs first to take advantage of case-insensitive stat. */
     390      286909 :         status = SMB_VFS_GET_REAL_FILENAME_AT(
     391             :                 dirfsp->conn, dirfsp, name, mem_ctx, found_name);
     392             : 
     393             :         /*
     394             :          * If the case-insensitive stat was successful, or returned an error
     395             :          * other than EOPNOTSUPP then there is no need to fall back on the
     396             :          * full directory scan.
     397             :          */
     398      286909 :         if (NT_STATUS_IS_OK(status) ||
     399      286026 :             !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
     400           0 :                 return status;
     401             :         }
     402             : 
     403      286909 :         status = get_real_filename_full_scan_at(
     404             :                 dirfsp, name, mangled, mem_ctx, found_name);
     405      286909 :         return status;
     406             : }
     407             : 
     408             : /*
     409             :  * Lightweight function to just get last component
     410             :  * for rename / enumerate directory calls.
     411             :  */
     412             : 
     413       32934 : char *get_original_lcomp(TALLOC_CTX *ctx,
     414             :                         connection_struct *conn,
     415             :                         const char *filename_in,
     416             :                         uint32_t ucf_flags)
     417             : {
     418       32934 :         char *last_slash = NULL;
     419        4300 :         char *orig_lcomp;
     420        4300 :         NTSTATUS status;
     421             : 
     422       32934 :         last_slash = strrchr(filename_in, '/');
     423       32934 :         if (last_slash != NULL) {
     424       28952 :                 orig_lcomp = talloc_strdup(ctx, last_slash+1);
     425             :         } else {
     426        3982 :                 orig_lcomp = talloc_strdup(ctx, filename_in);
     427             :         }
     428       32934 :         if (orig_lcomp == NULL) {
     429           0 :                 return NULL;
     430             :         }
     431       32934 :         status = normalize_filename_case(conn, orig_lcomp, ucf_flags);
     432       32934 :         if (!NT_STATUS_IS_OK(status)) {
     433           0 :                 TALLOC_FREE(orig_lcomp);
     434           0 :                 return NULL;
     435             :         }
     436       28634 :         return orig_lcomp;
     437             : }
     438             : 
     439             : /*
     440             :  * Get the correct capitalized stream name hanging off
     441             :  * base_fsp. Equivalent of get_real_filename(), but for streams.
     442             :  */
     443        3831 : static NTSTATUS get_real_stream_name(
     444             :         TALLOC_CTX *mem_ctx,
     445             :         struct files_struct *base_fsp,
     446             :         const char *stream_name,
     447             :         char **_found)
     448             : {
     449        3831 :         unsigned int i, num_streams = 0;
     450        3831 :         struct stream_struct *streams = NULL;
     451           1 :         NTSTATUS status;
     452             : 
     453        3831 :         status = vfs_fstreaminfo(
     454             :                 base_fsp, talloc_tos(), &num_streams, &streams);
     455        3831 :         if (!NT_STATUS_IS_OK(status)) {
     456           0 :                 return status;
     457             :         }
     458             : 
     459        7743 :         for (i=0; i<num_streams; i++) {
     460        3972 :                 bool equal = sname_equal(stream_name, streams[i].name, false);
     461             : 
     462        3972 :                 DBG_DEBUG("comparing [%s] and [%s]: %sequal\n",
     463             :                           stream_name,
     464             :                           streams[i].name,
     465             :                           equal ? "" : "not ");
     466             : 
     467        3972 :                 if (equal) {
     468          60 :                         *_found = talloc_move(mem_ctx, &streams[i].name);
     469          60 :                         TALLOC_FREE(streams);
     470          60 :                         return NT_STATUS_OK;
     471             :                 }
     472             :         }
     473             : 
     474        3771 :         TALLOC_FREE(streams);
     475        3771 :         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     476             : }
     477             : 
     478      727744 : static bool filename_split_lcomp(
     479             :         TALLOC_CTX *mem_ctx,
     480             :         const char *name_in,
     481             :         bool posix,
     482             :         char **_dirname,
     483             :         const char **_fname_rel,
     484             :         const char **_streamname)
     485             : {
     486      727744 :         const char *lcomp = NULL;
     487      727744 :         const char *fname_rel = NULL;
     488      727744 :         const char *streamname = NULL;
     489      727744 :         char *dirname = NULL;
     490             : 
     491      727744 :         if (name_in[0] == '\0') {
     492       37996 :                 fname_rel = ".";
     493       37996 :                 dirname = talloc_strdup(mem_ctx, "");
     494       37996 :                 if (dirname == NULL) {
     495           0 :                         return false;
     496             :                 }
     497       37996 :                 goto done;
     498             :         }
     499             : 
     500      689748 :         lcomp = strrchr_m(name_in, '/');
     501      689748 :         if (lcomp != NULL) {
     502      607812 :                 fname_rel = lcomp+1;
     503      607812 :                 dirname = talloc_strndup(mem_ctx, name_in, lcomp - name_in);
     504      607812 :                 if (dirname == NULL) {
     505           0 :                         return false;
     506             :                 }
     507      607812 :                 goto find_stream;
     508             :         }
     509             : 
     510             :         /*
     511             :          * No slash, dir is empty
     512             :          */
     513       81936 :         dirname = talloc_strdup(mem_ctx, "");
     514       81936 :         if (dirname == NULL) {
     515           0 :                 return false;
     516             :         }
     517             : 
     518       81936 :         if (!posix && (name_in[0] == ':')) {
     519             :                 /*
     520             :                  * Special case for stream on root directory
     521             :                  */
     522          32 :                 fname_rel = ".";
     523          32 :                 streamname = name_in;
     524          32 :                 goto done;
     525             :         }
     526             : 
     527       80509 :         fname_rel = name_in;
     528             : 
     529      689716 : find_stream:
     530      689716 :         if (!posix) {
     531      685330 :                 streamname = strchr_m(fname_rel, ':');
     532             : 
     533      685330 :                 if (streamname != NULL) {
     534        6423 :                         fname_rel = talloc_strndup(
     535             :                                 mem_ctx,
     536             :                                 fname_rel,
     537        6422 :                                 streamname - fname_rel);
     538        6422 :                         if (fname_rel == NULL) {
     539           0 :                                 TALLOC_FREE(dirname);
     540           0 :                                 return false;
     541             :                         }
     542             :                 }
     543             :         }
     544             : 
     545      689716 : done:
     546      727744 :         *_dirname = dirname;
     547      727744 :         *_fname_rel = fname_rel;
     548      727744 :         *_streamname = streamname;
     549      727744 :         return true;
     550             : }
     551             : 
     552             : /*
     553             :  * Create the correct capitalization of a file name to be created.
     554             :  */
     555      281655 : static NTSTATUS filename_convert_normalize_new(
     556             :         TALLOC_CTX *mem_ctx,
     557             :         struct connection_struct *conn,
     558             :         char *name_in,
     559             :         char **_normalized)
     560             : {
     561      281655 :         char *name = name_in;
     562             : 
     563      281655 :         *_normalized = NULL;
     564             : 
     565      563306 :         if (!conn->case_preserve ||
     566      281651 :             (mangle_is_8_3(name, false,
     567      281651 :                            conn->params) &&
     568      250089 :              !conn->short_case_preserve)) {
     569             : 
     570           4 :                 char *normalized = talloc_strdup(mem_ctx, name);
     571           4 :                 if (normalized == NULL) {
     572           0 :                         return NT_STATUS_NO_MEMORY;
     573             :                 }
     574             : 
     575           4 :                 strnorm(normalized, lp_default_case(SNUM(conn)));
     576           4 :                 name = normalized;
     577             :         }
     578             : 
     579      281655 :         if (mangle_is_mangled(name, conn->params)) {
     580           0 :                 bool found;
     581          53 :                 char *unmangled = NULL;
     582             : 
     583          53 :                 found = mangle_lookup_name_from_8_3(
     584          53 :                         mem_ctx, name, &unmangled, conn->params);
     585          53 :                 if (found) {
     586          41 :                         name = unmangled;
     587             :                 }
     588             :         }
     589             : 
     590      281655 :         if (name != name_in) {
     591          45 :                 *_normalized = name;
     592             :         }
     593             : 
     594      281655 :         return NT_STATUS_OK;
     595             : }
     596             : 
     597        6738 : static const char *previous_slash(const char *name_in, const char *slash)
     598             : {
     599        6738 :         const char *prev = NULL;
     600             : 
     601        6738 :         SMB_ASSERT((name_in <= slash) && (slash[0] == '/'));
     602             : 
     603        6738 :         prev = strchr_m(name_in, '/');
     604             : 
     605        6738 :         if (prev == slash) {
     606             :                 /* No previous slash */
     607         159 :                 return NULL;
     608             :         }
     609             : 
     610           0 :         while (true) {
     611        6579 :                 const char *next = strchr_m(prev + 1, '/');
     612             : 
     613        6579 :                 if (next == slash) {
     614        6579 :                         return prev;
     615             :                 }
     616           0 :                 prev = next;
     617             :         }
     618             : 
     619             :         return NULL; /* unreachable */
     620             : }
     621             : 
     622       21180 : static char *symlink_target_path(
     623             :         TALLOC_CTX *mem_ctx,
     624             :         const char *name_in,
     625             :         const char *substitute,
     626             :         size_t unparsed)
     627             : {
     628       21180 :         size_t name_in_len = strlen(name_in);
     629       21180 :         const char *p_unparsed = NULL;
     630       21180 :         const char *parent = NULL;
     631           0 :         char *ret;
     632             : 
     633       21180 :         SMB_ASSERT(unparsed <= name_in_len);
     634             : 
     635       21180 :         p_unparsed = name_in + (name_in_len - unparsed);
     636             : 
     637       21180 :         if (substitute[0] == '/') {
     638       11413 :                 ret = talloc_asprintf(mem_ctx, "%s%s", substitute, p_unparsed);
     639       11413 :                 return ret;
     640             :         }
     641             : 
     642        9767 :         if (unparsed == 0) {
     643        3029 :                 parent = strrchr_m(name_in, '/');
     644             :         } else {
     645        6738 :                 parent = previous_slash(name_in, p_unparsed);
     646             :         }
     647             : 
     648        9767 :         if (parent == NULL) {
     649         304 :                 ret = talloc_asprintf(mem_ctx, "%s%s", substitute, p_unparsed);
     650             :         } else {
     651        9463 :                 ret = talloc_asprintf(mem_ctx,
     652             :                                       "%.*s/%s%s",
     653        9463 :                                       (int)(parent - name_in),
     654             :                                       name_in,
     655             :                                       substitute,
     656             :                                       p_unparsed);
     657             :         }
     658             : 
     659        9767 :         return ret;
     660             : }
     661             : 
     662       23670 : NTSTATUS safe_symlink_target_path(TALLOC_CTX *mem_ctx,
     663             :                                   const char *connectpath,
     664             :                                   const char *dir,
     665             :                                   const char *target,
     666             :                                   size_t unparsed,
     667             :                                   char **_relative)
     668             : {
     669       23670 :         char *abs_target = NULL;
     670       23670 :         char *abs_target_canon = NULL;
     671       23670 :         const char *relative = NULL;
     672           0 :         bool in_share;
     673       23670 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
     674             : 
     675       23670 :         DBG_DEBUG("connectpath [%s] target [%s] unparsed [%zu]\n",
     676             :                   connectpath, target, unparsed);
     677             : 
     678       23670 :         if (target[0] == '/') {
     679       13405 :                 abs_target = talloc_strdup(mem_ctx, target);
     680       10265 :         } else if (dir == NULL) {
     681        9767 :                 abs_target = talloc_asprintf(mem_ctx,
     682             :                                              "%s/%s",
     683             :                                              connectpath,
     684             :                                              target);
     685         498 :         } else if (dir[0] == '/') {
     686          24 :                 abs_target = talloc_asprintf(mem_ctx,
     687             :                                              "%s/%s",
     688             :                                              dir,
     689             :                                              target);
     690             :         } else {
     691         474 :                 abs_target = talloc_asprintf(mem_ctx,
     692             :                                              "%s/%s/%s",
     693             :                                              connectpath,
     694             :                                              dir,
     695             :                                              target);
     696             :         }
     697       23670 :         if (abs_target == NULL) {
     698           0 :                 goto fail;
     699             :         }
     700             : 
     701       23670 :         abs_target_canon = canonicalize_absolute_path(abs_target, abs_target);
     702       23670 :         if (abs_target_canon == NULL) {
     703           0 :                 goto fail;
     704             :         }
     705             : 
     706       23670 :         DBG_DEBUG("abs_target_canon=%s\n", abs_target_canon);
     707             : 
     708       23670 :         in_share = subdir_of(
     709             :                 connectpath, strlen(connectpath), abs_target_canon, &relative);
     710       23670 :         if (!in_share) {
     711        2806 :                 DBG_DEBUG("wide link to %s\n", abs_target_canon);
     712        2806 :                 status = (unparsed != 0) ? NT_STATUS_OBJECT_PATH_NOT_FOUND
     713           4 :                                          : NT_STATUS_OBJECT_NAME_NOT_FOUND;
     714        2806 :                 goto fail;
     715             :         }
     716             : 
     717       20864 :         *_relative = talloc_strdup(mem_ctx, relative);
     718       20864 :         if (*_relative == NULL) {
     719           0 :                 goto fail;
     720             :         }
     721             : 
     722       20864 :         status = NT_STATUS_OK;
     723       23670 : fail:
     724       23670 :         TALLOC_FREE(abs_target);
     725       23670 :         return status;
     726             : }
     727             : 
     728             : /*
     729             :  * Split up name_in as sent by the client into a directory pathref fsp
     730             :  * and a relative smb_filename.
     731             :  */
     732      727799 : static NTSTATUS filename_convert_dirfsp_nosymlink(
     733             :         TALLOC_CTX *mem_ctx,
     734             :         connection_struct *conn,
     735             :         const char *name_in,
     736             :         uint32_t ucf_flags,
     737             :         NTTIME twrp,
     738             :         struct files_struct **_dirfsp,
     739             :         struct smb_filename **_smb_fname,
     740             :         struct open_symlink_err **_symlink_err)
     741             : {
     742      727799 :         struct smb_filename *smb_dirname = NULL;
     743      727799 :         struct smb_filename *smb_fname_rel = NULL;
     744      727799 :         struct smb_filename *smb_fname = NULL;
     745      727799 :         struct open_symlink_err *symlink_err = NULL;
     746      727799 :         const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
     747      727799 :         char *dirname = NULL;
     748      727799 :         const char *fname_rel = NULL;
     749      727799 :         const char *streamname = NULL;
     750      727799 :         char *saved_streamname = NULL;
     751      727799 :         struct files_struct *base_fsp = NULL;
     752       10413 :         bool ok;
     753      727799 :         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
     754             : 
     755      727799 :         SMB_ASSERT(!(ucf_flags & UCF_DFS_PATHNAME));
     756             : 
     757      727799 :         if (is_fake_file_path(name_in)) {
     758          28 :                 smb_fname = synthetic_smb_fname_split(mem_ctx, name_in, posix);
     759          28 :                 if (smb_fname == NULL) {
     760           0 :                         return NT_STATUS_NO_MEMORY;
     761             :                 }
     762          28 :                 smb_fname->st = (SMB_STRUCT_STAT){
     763             :                         .st_ex_nlink = 1,
     764             :                         .st_ex_mode = S_IFREG | 0644,
     765             :                 };
     766          28 :                 smb_fname->st.st_ex_btime =
     767             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     768          28 :                 smb_fname->st.st_ex_atime =
     769             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     770          28 :                 smb_fname->st.st_ex_mtime =
     771             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     772          28 :                 smb_fname->st.st_ex_ctime =
     773             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     774             : 
     775          28 :                 *_dirfsp = conn->cwd_fsp;
     776          28 :                 *_smb_fname = smb_fname;
     777          28 :                 return NT_STATUS_OK;
     778             :         }
     779             : 
     780             :         /*
     781             :          * Catch an invalid path of "." before we
     782             :          * call filename_split_lcomp(). We need to
     783             :          * do this as filename_split_lcomp() will
     784             :          * use "." for the missing relative component
     785             :          * when an empty name_in path is sent by
     786             :          * the client.
     787             :          */
     788      727771 :         if (ISDOT(name_in)) {
     789          27 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     790          27 :                 goto fail;
     791             :         }
     792             : 
     793      727744 :         ok = filename_split_lcomp(
     794             :                 talloc_tos(),
     795             :                 name_in,
     796             :                 posix,
     797             :                 &dirname,
     798             :                 &fname_rel,
     799             :                 &streamname);
     800      727744 :         if (!ok) {
     801           0 :                 status = NT_STATUS_NO_MEMORY;
     802           0 :                 goto fail;
     803             :         }
     804             : 
     805      727744 :         if ((streamname != NULL) &&
     806        6454 :             ((conn->fs_capabilities & FILE_NAMED_STREAMS) == 0)) {
     807          12 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     808          12 :                 goto fail;
     809             :         }
     810             : 
     811      727732 :         if (!posix) {
     812      723235 :                 bool name_has_wild = ms_has_wild(dirname);
     813      723235 :                 name_has_wild |= ms_has_wild(fname_rel);
     814      723235 :                 if (name_has_wild) {
     815        4516 :                         status = NT_STATUS_OBJECT_NAME_INVALID;
     816        4516 :                         goto fail;
     817             :                 }
     818             :         }
     819             : 
     820      723216 :         if (dirname[0] == '\0') {
     821      121394 :                 status = synthetic_pathref(
     822             :                         mem_ctx,
     823             :                         conn->cwd_fsp,
     824             :                         ".",
     825             :                         NULL,
     826             :                         NULL,
     827             :                         0,
     828             :                         posix ? SMB_FILENAME_POSIX_PATH : 0,
     829             :                         &smb_dirname);
     830             :         } else {
     831      603320 :                 status = normalize_filename_case(conn, dirname, ucf_flags);
     832      603320 :                 if (!NT_STATUS_IS_OK(status)) {
     833           0 :                         DBG_ERR("normalize_filename_case %s failed: %s\n",
     834             :                                 dirname,
     835             :                                 nt_errstr(status));
     836           0 :                         goto fail;
     837             :                 }
     838             : 
     839      603320 :                 status = openat_pathref_fsp_nosymlink(mem_ctx,
     840             :                                                       conn,
     841             :                                                       conn->cwd_fsp,
     842             :                                                       dirname,
     843             :                                                       twrp,
     844             :                                                       posix,
     845             :                                                       &smb_dirname,
     846             :                                                       &symlink_err);
     847             : 
     848      603320 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
     849           0 :                         size_t name_in_len, dirname_len;
     850             : 
     851       62379 :                         name_in_len = strlen(name_in);
     852       62379 :                         dirname_len = strlen(dirname);
     853             : 
     854       62379 :                         SMB_ASSERT(name_in_len >= dirname_len);
     855             : 
     856       62379 :                         symlink_err->unparsed += (name_in_len - dirname_len);
     857       62379 :                         *_symlink_err = symlink_err;
     858             : 
     859       62379 :                         goto fail;
     860             :                 }
     861             :         }
     862             : 
     863      660837 :         if (!NT_STATUS_IS_OK(status)) {
     864        1082 :                 DBG_DEBUG("opening directory %s failed: %s\n",
     865             :                           dirname,
     866             :                           nt_errstr(status));
     867        1082 :                 TALLOC_FREE(dirname);
     868             : 
     869        1082 :                 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
     870             :                         /*
     871             :                          * Except ACCESS_DENIED, everything else leads
     872             :                          * to PATH_NOT_FOUND.
     873             :                          */
     874        1067 :                         status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
     875             :                 }
     876             : 
     877        1082 :                 goto fail;
     878             :         }
     879             : 
     880      659755 :         if (!VALID_STAT_OF_DIR(smb_dirname->st)) {
     881          28 :                 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
     882          28 :                 goto fail;
     883             :         }
     884      659727 :         smb_dirname->fsp->fsp_flags.is_directory = true;
     885             : 
     886             :         /*
     887             :          * Only look at bad last component values
     888             :          * once we know we have a valid directory. That
     889             :          * way we won't confuse error messages from
     890             :          * opening the directory path with error
     891             :          * messages from a bad last component.
     892             :          */
     893             : 
     894             :         /* Relative filename can't be empty */
     895      659727 :         if (fname_rel[0] == '\0') {
     896           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     897           0 :                 goto fail;
     898             :         }
     899             : 
     900             :         /* Relative filename can't be ".." */
     901      659727 :         if (ISDOTDOT(fname_rel)) {
     902           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     903           0 :                 goto fail;
     904             :         }
     905             :         /* Relative name can only be dot if directory is empty. */
     906      659727 :         if (ISDOT(fname_rel) && dirname[0] != '\0') {
     907           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     908           0 :                 goto fail;
     909             :         }
     910             : 
     911      659727 :         TALLOC_FREE(dirname);
     912             : 
     913      670131 :         smb_fname_rel = synthetic_smb_fname(
     914             :                 mem_ctx,
     915             :                 fname_rel,
     916             :                 streamname,
     917             :                 NULL,
     918             :                 twrp,
     919             :                 posix ? SMB_FILENAME_POSIX_PATH : 0);
     920      659727 :         if (smb_fname_rel == NULL) {
     921           0 :                 status = NT_STATUS_NO_MEMORY;
     922           0 :                 goto fail;
     923             :         }
     924             : 
     925     1253833 :         if ((conn->fs_capabilities & FILE_NAMED_STREAMS) &&
     926      594106 :             is_named_stream(smb_fname_rel)) {
     927             :                 /*
     928             :                  * Find the base_fsp first without the stream.
     929             :                  */
     930        6398 :                 saved_streamname = smb_fname_rel->stream_name;
     931        6398 :                 smb_fname_rel->stream_name = NULL;
     932             :         }
     933             : 
     934      659727 :         status = normalize_filename_case(
     935             :                 conn, smb_fname_rel->base_name, ucf_flags);
     936      659727 :         if (!NT_STATUS_IS_OK(status)) {
     937           0 :                 DBG_ERR("normalize_filename_case %s failed: %s\n",
     938             :                         smb_fname_rel->base_name,
     939             :                         nt_errstr(status));
     940           0 :                 goto fail;
     941             :         }
     942             : 
     943      659727 :         status = openat_pathref_fsp_lcomp(smb_dirname->fsp,
     944             :                                           smb_fname_rel,
     945             :                                           ucf_flags);
     946             : 
     947      659727 :         if (NT_STATUS_IS_OK(status) && S_ISLNK(smb_fname_rel->st.st_ex_mode)) {
     948             : 
     949             :                 /*
     950             :                  * Upper layers might need the link target. Here we
     951             :                  * still have the relname around, get the symlink err.
     952             :                  */
     953       10448 :                 status = create_open_symlink_err(mem_ctx,
     954       10448 :                                                  smb_dirname->fsp,
     955             :                                                  smb_fname_rel,
     956             :                                                  &symlink_err);
     957       10448 :                 if (!NT_STATUS_IS_OK(status)) {
     958           0 :                         DBG_DEBUG("Could not read symlink for %s: %s\n",
     959             :                                   smb_fname_str_dbg(
     960             :                                           smb_fname_rel->fsp->fsp_name),
     961             :                                   nt_errstr(status));
     962           0 :                         goto fail;
     963             :                 }
     964             :         }
     965             : 
     966      659727 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
     967      281655 :             !VALID_STAT(smb_fname_rel->st)) {
     968             : 
     969      281655 :                 char *normalized = NULL;
     970             : 
     971             :                 /*
     972             :                  * Creating a new file
     973             :                  */
     974             : 
     975      281655 :                 status = filename_convert_normalize_new(
     976             :                         smb_fname_rel,
     977             :                         conn,
     978             :                         smb_fname_rel->base_name,
     979             :                         &normalized);
     980      281655 :                 if (!NT_STATUS_IS_OK(status)) {
     981           0 :                         DBG_DEBUG("filename_convert_normalize_new failed: "
     982             :                                   "%s\n",
     983             :                                   nt_errstr(status));
     984           0 :                         goto fail;
     985             :                 }
     986      281655 :                 if (normalized != NULL) {
     987          45 :                         smb_fname_rel->base_name = normalized;
     988             :                 }
     989             : 
     990      281655 :                 smb_fname_rel->stream_name = saved_streamname;
     991             : 
     992      282531 :                 smb_fname = full_path_from_dirfsp_atname(
     993      281655 :                         mem_ctx, smb_dirname->fsp, smb_fname_rel);
     994      281655 :                 if (smb_fname == NULL) {
     995           0 :                         status = NT_STATUS_NO_MEMORY;
     996           0 :                         goto fail;
     997             :                 }
     998      281655 :                 goto done;
     999             :         }
    1000             : 
    1001      378072 :         if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_OPEN_RESTRICTION)) {
    1002             :                 /* A vetoed file, pretend it's not there  */
    1003          21 :                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
    1004             :         }
    1005      378072 :         if (!NT_STATUS_IS_OK(status)) {
    1006          33 :                 goto fail;
    1007             :         }
    1008             : 
    1009      378039 :         if (saved_streamname == NULL) {
    1010             :                 /* smb_fname must be allocated off mem_ctx. */
    1011      381380 :                 smb_fname = cp_smb_filename(mem_ctx,
    1012      371853 :                                             smb_fname_rel->fsp->fsp_name);
    1013      371853 :                 if (smb_fname == NULL) {
    1014           0 :                         goto fail;
    1015             :                 }
    1016      371853 :                 status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel);
    1017      371853 :                 if (!NT_STATUS_IS_OK(status)) {
    1018           0 :                         goto fail;
    1019             :                 }
    1020      371853 :                 goto done;
    1021             :         }
    1022             : 
    1023        6186 :         base_fsp = smb_fname_rel->fsp;
    1024        6186 :         smb_fname_fsp_unlink(smb_fname_rel);
    1025        6186 :         SET_STAT_INVALID(smb_fname_rel->st);
    1026             : 
    1027        6186 :         smb_fname_rel->stream_name = saved_streamname;
    1028             : 
    1029        6186 :         status = open_stream_pathref_fsp(&base_fsp, smb_fname_rel);
    1030             : 
    1031        6186 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
    1032        3831 :             !conn->case_sensitive) {
    1033        3831 :                 char *found = NULL;
    1034             : 
    1035        3832 :                 status = get_real_stream_name(
    1036             :                         smb_fname_rel,
    1037             :                         base_fsp,
    1038        3831 :                         smb_fname_rel->stream_name,
    1039             :                         &found);
    1040             : 
    1041        3831 :                 if (NT_STATUS_IS_OK(status)) {
    1042          60 :                         smb_fname_rel->stream_name = found;
    1043          60 :                         found = NULL;
    1044          60 :                         status = open_stream_pathref_fsp(
    1045             :                                 &base_fsp, smb_fname_rel);
    1046             :                 }
    1047             :         }
    1048             : 
    1049        6186 :         if (NT_STATUS_IS_OK(status)) {
    1050             :                 /* smb_fname must be allocated off mem_ctx. */
    1051        2367 :                 smb_fname = cp_smb_filename(mem_ctx,
    1052        2367 :                                             smb_fname_rel->fsp->fsp_name);
    1053        2367 :                 if (smb_fname == NULL) {
    1054           0 :                         goto fail;
    1055             :                 }
    1056        2367 :                 status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel);
    1057        2367 :                 if (!NT_STATUS_IS_OK(status)) {
    1058           0 :                         goto fail;
    1059             :                 }
    1060        2367 :                 goto done;
    1061             :         }
    1062             : 
    1063        3819 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
    1064             :                 /*
    1065             :                  * Creating a new stream
    1066             :                  *
    1067             :                  * We should save the already-open base fsp for
    1068             :                  * create_file_unixpath() somehow.
    1069             :                  */
    1070        3772 :                 smb_fname = full_path_from_dirfsp_atname(
    1071        3771 :                         mem_ctx, smb_dirname->fsp, smb_fname_rel);
    1072        3771 :                 if (smb_fname == NULL) {
    1073           0 :                         status = NT_STATUS_NO_MEMORY;
    1074           0 :                         goto fail;
    1075             :                 }
    1076             :                 /*
    1077             :                  * When open_stream_pathref_fsp() returns
    1078             :                  * NT_STATUS_OBJECT_NAME_NOT_FOUND, smb_fname_rel->fsp
    1079             :                  * has been set to NULL, so we must free base_fsp separately
    1080             :                  * to prevent fd-leaks when opening a stream that doesn't
    1081             :                  * exist.
    1082             :                  */
    1083        3771 :                 fd_close(base_fsp);
    1084        3771 :                 file_free(NULL, base_fsp);
    1085        3771 :                 base_fsp = NULL;
    1086        3771 :                 goto done;
    1087             :         }
    1088             : 
    1089          48 :         if (!NT_STATUS_IS_OK(status)) {
    1090          48 :                 goto fail;
    1091             :         }
    1092             : 
    1093       10404 : done:
    1094      659646 :         *_dirfsp = smb_dirname->fsp;
    1095      659646 :         *_smb_fname = smb_fname;
    1096      659646 :         *_symlink_err = symlink_err;
    1097             : 
    1098      659646 :         smb_fname_fsp_unlink(smb_fname_rel);
    1099      659646 :         TALLOC_FREE(smb_fname_rel);
    1100      659646 :         return NT_STATUS_OK;
    1101             : 
    1102       68125 : fail:
    1103             :         /*
    1104             :          * If open_stream_pathref_fsp() returns an error, smb_fname_rel->fsp
    1105             :          * has been set to NULL, so we must free base_fsp separately
    1106             :          * to prevent fd-leaks when opening a stream that doesn't
    1107             :          * exist.
    1108             :          */
    1109       68125 :         if (base_fsp != NULL) {
    1110          48 :                 fd_close(base_fsp);
    1111          48 :                 file_free(NULL, base_fsp);
    1112          48 :                 base_fsp = NULL;
    1113             :         }
    1114       68125 :         TALLOC_FREE(dirname);
    1115       68125 :         TALLOC_FREE(smb_dirname);
    1116       68125 :         TALLOC_FREE(smb_fname_rel);
    1117       68125 :         return status;
    1118             : }
    1119             : 
    1120      708992 : NTSTATUS filename_convert_dirfsp(
    1121             :         TALLOC_CTX *mem_ctx,
    1122             :         connection_struct *conn,
    1123             :         const char *name_in,
    1124             :         uint32_t ucf_flags,
    1125             :         NTTIME twrp,
    1126             :         struct files_struct **_dirfsp,
    1127             :         struct smb_filename **_smb_fname)
    1128             : {
    1129      708992 :         struct open_symlink_err *symlink_err = NULL;
    1130       10413 :         NTSTATUS status;
    1131      708992 :         char *target = NULL;
    1132      708992 :         char *safe_target = NULL;
    1133      708992 :         size_t symlink_redirects = 0;
    1134             : 
    1135      727819 : next:
    1136      727819 :         if (symlink_redirects > 40) {
    1137          20 :                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
    1138             :         }
    1139             : 
    1140      727799 :         status = filename_convert_dirfsp_nosymlink(mem_ctx,
    1141             :                                                    conn,
    1142             :                                                    name_in,
    1143             :                                                    ucf_flags,
    1144             :                                                    twrp,
    1145             :                                                    _dirfsp,
    1146             :                                                    _smb_fname,
    1147             :                                                    &symlink_err);
    1148             : 
    1149      727799 :         if (NT_STATUS_IS_OK(status) && S_ISLNK((*_smb_fname)->st.st_ex_mode)) {
    1150             :                 /*
    1151             :                  * lcomp is a symlink
    1152             :                  */
    1153       10448 :                 if (ucf_flags & UCF_LCOMP_LNK_OK) {
    1154         740 :                         TALLOC_FREE(symlink_err);
    1155         740 :                         return NT_STATUS_OK;
    1156             :                 }
    1157        9708 :                 close_file_free(NULL, _dirfsp, ERROR_CLOSE);
    1158        9708 :                 status = NT_STATUS_STOPPED_ON_SYMLINK;
    1159             :         }
    1160             : 
    1161      727059 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
    1162      654972 :                 return status;
    1163             :         }
    1164             : 
    1165             :         /*
    1166             :          * If we're on an MSDFS share, see if this is
    1167             :          * an MSDFS link.
    1168             :          */
    1169      122966 :         if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&
    1170       50879 :             strnequal(symlink_err->reparse->substitute_name, "msdfs:", 6))
    1171             :         {
    1172       50879 :                 TALLOC_FREE(*_smb_fname);
    1173       50879 :                 TALLOC_FREE(symlink_err);
    1174       50879 :                 return NT_STATUS_PATH_NOT_COVERED;
    1175             :         }
    1176             : 
    1177       21208 :         if (!lp_follow_symlinks(SNUM(conn))) {
    1178          28 :                 status = (symlink_err->unparsed == 0)
    1179             :                                  ? NT_STATUS_OBJECT_NAME_NOT_FOUND
    1180           2 :                                  : NT_STATUS_OBJECT_PATH_NOT_FOUND;
    1181          28 :                 TALLOC_FREE(symlink_err);
    1182          28 :                 return status;
    1183             :         }
    1184             : 
    1185             :         /*
    1186             :          * Right now, SMB2 and SMB1 always traverse symlinks
    1187             :          * within the share. SMB1+POSIX traverses non-terminal
    1188             :          * symlinks within the share.
    1189             :          *
    1190             :          * When we add SMB2+POSIX we need to return
    1191             :          * a NT_STATUS_STOPPED_ON_SYMLINK error here, using the
    1192             :          * symlink target data read below if SMB2+POSIX has
    1193             :          * UCF_POSIX_PATHNAMES set to cause the client to
    1194             :          * resolve all symlinks locally.
    1195             :          */
    1196             : 
    1197       21180 :         target = symlink_target_path(mem_ctx,
    1198             :                                      name_in,
    1199       21180 :                                      symlink_err->reparse->substitute_name,
    1200       21180 :                                      symlink_err->unparsed);
    1201       21180 :         if (target == NULL) {
    1202           0 :                 return NT_STATUS_NO_MEMORY;
    1203             :         }
    1204             : 
    1205       21180 :         status = safe_symlink_target_path(mem_ctx,
    1206       21180 :                                           conn->connectpath,
    1207             :                                           NULL,
    1208             :                                           target,
    1209       21180 :                                           symlink_err->unparsed,
    1210             :                                           &safe_target);
    1211       21180 :         TALLOC_FREE(symlink_err);
    1212       21180 :         if (!NT_STATUS_IS_OK(status)) {
    1213        2353 :                 return status;
    1214             :         }
    1215       18827 :         name_in = safe_target;
    1216             : 
    1217       18827 :         symlink_redirects += 1;
    1218             : 
    1219       18827 :         goto next;
    1220             : }
    1221             : 
    1222     6294155 : char *full_path_from_dirfsp_at_basename(TALLOC_CTX *mem_ctx,
    1223             :                                         const struct files_struct *dirfsp,
    1224             :                                         const char *at_base_name)
    1225             : {
    1226     6294155 :         char *path = NULL;
    1227             : 
    1228     6294155 :         if (dirfsp == dirfsp->conn->cwd_fsp ||
    1229     2590493 :             ISDOT(dirfsp->fsp_name->base_name) || at_base_name[0] == '/') {
    1230     3947678 :                 path = talloc_strdup(mem_ctx, at_base_name);
    1231             :         } else {
    1232     2346477 :                 path = talloc_asprintf(mem_ctx,
    1233             :                                        "%s/%s",
    1234     2336902 :                                        dirfsp->fsp_name->base_name,
    1235             :                                        at_base_name);
    1236             :         }
    1237             : 
    1238     6294155 :         return path;
    1239             : }
    1240             : 
    1241             : /*
    1242             :  * Build the full path from a dirfsp and dirfsp relative name
    1243             :  */
    1244             : struct smb_filename *
    1245     6291663 : full_path_from_dirfsp_atname(TALLOC_CTX *mem_ctx,
    1246             :                              const struct files_struct *dirfsp,
    1247             :                              const struct smb_filename *atname)
    1248             : {
    1249     6291663 :         struct smb_filename *fname = NULL;
    1250     6291663 :         char *path = NULL;
    1251             : 
    1252     6314454 :         path = full_path_from_dirfsp_at_basename(mem_ctx,
    1253             :                                                  dirfsp,
    1254     6291663 :                                                  atname->base_name);
    1255     6291663 :         if (path == NULL) {
    1256           0 :                 return NULL;
    1257             :         }
    1258             : 
    1259     6314454 :         fname = synthetic_smb_fname(mem_ctx,
    1260             :                                     path,
    1261     6291663 :                                     atname->stream_name,
    1262             :                                     &atname->st,
    1263     6291663 :                                     atname->twrp,
    1264     6291663 :                                     atname->flags);
    1265     6291663 :         TALLOC_FREE(path);
    1266     6291663 :         if (fname == NULL) {
    1267           0 :                 return NULL;
    1268             :         }
    1269             : 
    1270     6268872 :         return fname;
    1271             : }

Generated by: LCOV version 1.14