LCOV - code coverage report
Current view: top level - source3/smbd - filename.c (source / functions) Hit Total Coverage
Test: coverage report for master 70ed9daf Lines: 472 547 86.3 %
Date: 2024-01-11 09:59:51 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      619910 : uint32_t ucf_flags_from_smb_request(struct smb_request *req)
      36             : {
      37      619910 :         uint32_t ucf_flags = 0;
      38             : 
      39      619910 :         if (req == NULL) {
      40           0 :                 return 0;
      41             :         }
      42             : 
      43      619910 :         if (req->posix_pathnames) {
      44        4411 :                 ucf_flags |= UCF_POSIX_PATHNAMES;
      45             : 
      46        4411 :                 if (!req->sconn->using_smb2) {
      47        2275 :                         ucf_flags |= UCF_LCOMP_LNK_OK;
      48             :                 }
      49             :         }
      50      619910 :         if (req->flags2 & FLAGS2_DFS_PATHNAMES) {
      51        1588 :                 ucf_flags |= UCF_DFS_PATHNAME;
      52             :         }
      53      619910 :         if (req->flags2 & FLAGS2_REPARSE_PATH) {
      54        3584 :                 ucf_flags |= UCF_GMT_PATHNAME;
      55             :         }
      56             : 
      57      609433 :         return ucf_flags;
      58             : }
      59             : 
      60      513866 : uint32_t filename_create_ucf_flags(struct smb_request *req, uint32_t create_disposition)
      61             : {
      62      513866 :         uint32_t ucf_flags = 0;
      63             : 
      64      513866 :         ucf_flags |= ucf_flags_from_smb_request(req);
      65             : 
      66      513866 :         switch (create_disposition) {
      67      331194 :         case FILE_OPEN:
      68             :         case FILE_OVERWRITE:
      69      331194 :                 break;
      70      182040 :         case FILE_SUPERSEDE:
      71             :         case FILE_CREATE:
      72             :         case FILE_OPEN_IF:
      73             :         case FILE_OVERWRITE_IF:
      74      182040 :                 ucf_flags |= UCF_PREP_CREATEFILE;
      75      182040 :                 break;
      76             :         }
      77             : 
      78      513866 :         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          46 : 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          46 :         if (!name_to_8_3(name2, mname, False, p)) {
      92           0 :                 return False;
      93             :         }
      94          46 :         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         196 : static bool strnorm(char *s, int case_default)
     138             : {
     139         196 :         if (case_default == CASE_UPPER)
     140           0 :                 return strupper_m(s);
     141             :         else
     142         196 :                 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     1222023 : static NTSTATUS normalize_filename_case(connection_struct *conn,
     152             :                                         char *filename,
     153             :                                         uint32_t ucf_flags)
     154             : {
     155       23613 :         bool ok;
     156             : 
     157     1222023 :         if (ucf_flags & UCF_POSIX_PATHNAMES) {
     158             :                 /*
     159             :                  * POSIX never normalizes filename case.
     160             :                  */
     161        4933 :                 return NT_STATUS_OK;
     162             :         }
     163     1217090 :         if (!conn->case_sensitive) {
     164     1214748 :                 return NT_STATUS_OK;
     165             :         }
     166        2342 :         if (conn->case_preserve) {
     167        2150 :                 return NT_STATUS_OK;
     168             :         }
     169         192 :         if (conn->short_case_preserve) {
     170           0 :                 return NT_STATUS_OK;
     171             :         }
     172         192 :         ok = strnorm(filename, lp_default_case(SNUM(conn)));
     173         192 :         if (!ok) {
     174           0 :                 return NT_STATUS_INVALID_PARAMETER;
     175             :         }
     176         192 :         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   158801146 : static bool fname_equal(const char *name1, const char *name2,
     185             :                 bool case_sensitive)
     186             : {
     187             :         /* Normal filename handling */
     188   158801146 :         if (case_sensitive) {
     189           0 :                 return(strcmp(name1,name2) == 0);
     190             :         }
     191             : 
     192   158801146 :         return(strequal(name1,name2));
     193             : }
     194             : 
     195        3962 : static bool sname_equal(const char *name1, const char *name2,
     196             :                 bool case_sensitive)
     197             : {
     198           0 :         bool match;
     199        3962 :         const char *s1 = NULL;
     200        3962 :         const char *s2 = NULL;
     201           0 :         size_t n1;
     202           0 :         size_t n2;
     203        3962 :         const char *e1 = NULL;
     204        3962 :         const char *e2 = NULL;
     205        3962 :         char *c1 = NULL;
     206        3962 :         char *c2 = NULL;
     207             : 
     208        3962 :         match = fname_equal(name1, name2, case_sensitive);
     209        3962 :         if (match) {
     210          28 :                 return true;
     211             :         }
     212             : 
     213        3934 :         if (name1[0] != ':') {
     214           0 :                 return false;
     215             :         }
     216        3934 :         if (name2[0] != ':') {
     217           0 :                 return false;
     218             :         }
     219        3934 :         s1 = &name1[1];
     220        3934 :         e1 = strchr(s1, ':');
     221        3934 :         if (e1 == NULL) {
     222         546 :                 n1 = strlen(s1);
     223             :         } else {
     224        3388 :                 n1 = PTR_DIFF(e1, s1);
     225             :         }
     226        3934 :         s2 = &name2[1];
     227        3934 :         e2 = strchr(s2, ':');
     228        3934 :         if (e2 == NULL) {
     229           0 :                 n2 = strlen(s2);
     230             :         } else {
     231        3934 :                 n2 = PTR_DIFF(e2, s2);
     232             :         }
     233             : 
     234             :         /* Normal filename handling */
     235        3934 :         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        3934 :         c1 = talloc_strndup(talloc_tos(), s1, n1);
     252        3934 :         c2 = talloc_strndup(talloc_tos(), s2, n2);
     253        3934 :         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        3934 :         match = strequal(c1, c2);
     260        3934 :         TALLOC_FREE(c2);
     261        3934 :         TALLOC_FREE(c1);
     262        3934 :         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      272593 : 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      272593 :         struct connection_struct *conn = dirfsp->conn;
     277      272593 :         struct smb_Dir *cur_dir = NULL;
     278      272593 :         const char *dname = NULL;
     279      272593 :         char *talloced = NULL;
     280      272593 :         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      272593 :         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      272593 :         if (mangled && !conn->case_sensitive) {
     307         164 :                 mangled = !mangle_lookup_name_from_8_3(talloc_tos(), name,
     308             :                                                        &unmangled_name,
     309         164 :                                                        conn->params);
     310         164 :                 if (!mangled) {
     311             :                         /* Name is now unmangled. */
     312         140 :                         name = unmangled_name;
     313             :                 }
     314             :         }
     315             : 
     316             :         /* open the directory */
     317      272593 :         status = OpenDir_from_pathref(talloc_tos(), dirfsp, NULL, 0, &cur_dir);
     318      272593 :         if (!NT_STATUS_IS_OK(status)) {
     319           1 :                 DBG_NOTICE("scan dir didn't open dir [%s]: %s\n",
     320             :                            fsp_str_dbg(dirfsp),
     321             :                            nt_errstr(status));
     322           1 :                 TALLOC_FREE(unmangled_name);
     323           1 :                 return status;
     324             :         }
     325             : 
     326             :         /* now scan for matching names */
     327   159609714 :         while ((dname = ReadDirName(cur_dir, &talloced))) {
     328             : 
     329             :                 /* Is it dot or dot dot. */
     330   159342392 :                 if (ISDOT(dname) || ISDOTDOT(dname)) {
     331      545184 :                         TALLOC_FREE(talloced);
     332      545184 :                         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   317594392 :                 if ((mangled && mangled_equal(name,dname,conn->params)) ||
     347   158797184 :                         fname_equal(name, dname, conn->case_sensitive)) {
     348             :                         /* we've found the file, change it's name and return */
     349        5270 :                         *found_name = talloc_strdup(mem_ctx, dname);
     350        5270 :                         TALLOC_FREE(unmangled_name);
     351        5270 :                         TALLOC_FREE(cur_dir);
     352        5270 :                         if (!*found_name) {
     353           0 :                                 TALLOC_FREE(talloced);
     354           0 :                                 return NT_STATUS_NO_MEMORY;
     355             :                         }
     356        5270 :                         TALLOC_FREE(talloced);
     357        5270 :                         return NT_STATUS_OK;
     358             :                 }
     359   158798607 :                 TALLOC_FREE(talloced);
     360             :         }
     361             : 
     362      267322 :         TALLOC_FREE(unmangled_name);
     363      267322 :         TALLOC_FREE(cur_dir);
     364      267322 :         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      272593 : NTSTATUS get_real_filename_at(struct files_struct *dirfsp,
     373             :                               const char *name,
     374             :                               TALLOC_CTX *mem_ctx,
     375             :                               char **found_name)
     376             : {
     377      272593 :         struct connection_struct *conn = dirfsp->conn;
     378         883 :         NTSTATUS status;
     379         883 :         bool mangled;
     380             : 
     381      272593 :         mangled = mangle_is_mangled(name, conn->params);
     382             : 
     383      272593 :         if (mangled) {
     384         164 :                 status = get_real_filename_full_scan_at(
     385             :                         dirfsp, name, mangled, mem_ctx, found_name);
     386         164 :                 return status;
     387             :         }
     388             : 
     389             :         /* Try the vfs first to take advantage of case-insensitive stat. */
     390      272429 :         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      272429 :         if (NT_STATUS_IS_OK(status) ||
     399      271538 :             !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
     400           8 :                 return status;
     401             :         }
     402             : 
     403      272421 :         status = get_real_filename_full_scan_at(
     404             :                 dirfsp, name, mangled, mem_ctx, found_name);
     405      272421 :         return status;
     406             : }
     407             : 
     408             : /*
     409             :  * Lightweight function to just get last component
     410             :  * for rename / enumerate directory calls.
     411             :  */
     412             : 
     413       32451 : char *get_original_lcomp(TALLOC_CTX *ctx,
     414             :                         connection_struct *conn,
     415             :                         const char *filename_in,
     416             :                         uint32_t ucf_flags)
     417             : {
     418       32451 :         char *last_slash = NULL;
     419        4300 :         char *orig_lcomp;
     420        4300 :         NTSTATUS status;
     421             : 
     422       32451 :         last_slash = strrchr(filename_in, '/');
     423       32451 :         if (last_slash != NULL) {
     424       28923 :                 orig_lcomp = talloc_strdup(ctx, last_slash+1);
     425             :         } else {
     426        3528 :                 orig_lcomp = talloc_strdup(ctx, filename_in);
     427             :         }
     428       32451 :         if (orig_lcomp == NULL) {
     429           0 :                 return NULL;
     430             :         }
     431       32451 :         status = normalize_filename_case(conn, orig_lcomp, ucf_flags);
     432       32451 :         if (!NT_STATUS_IS_OK(status)) {
     433           0 :                 TALLOC_FREE(orig_lcomp);
     434           0 :                 return NULL;
     435             :         }
     436       28151 :         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        3821 : 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        3821 :         unsigned int i, num_streams = 0;
     450        3821 :         struct stream_struct *streams = NULL;
     451           1 :         NTSTATUS status;
     452             : 
     453        3821 :         status = vfs_fstreaminfo(
     454             :                 base_fsp, talloc_tos(), &num_streams, &streams);
     455        3821 :         if (!NT_STATUS_IS_OK(status)) {
     456           0 :                 return status;
     457             :         }
     458             : 
     459        7723 :         for (i=0; i<num_streams; i++) {
     460        3962 :                 bool equal = sname_equal(stream_name, streams[i].name, false);
     461             : 
     462        3962 :                 DBG_DEBUG("comparing [%s] and [%s]: %sequal\n",
     463             :                           stream_name,
     464             :                           streams[i].name,
     465             :                           equal ? "" : "not ");
     466             : 
     467        3962 :                 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        3761 :         TALLOC_FREE(streams);
     475        3761 :         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     476             : }
     477             : 
     478      687951 : 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      687951 :         const char *lcomp = NULL;
     487      687951 :         const char *fname_rel = NULL;
     488      687951 :         const char *streamname = NULL;
     489      687951 :         char *dirname = NULL;
     490             : 
     491      687951 :         if (name_in[0] == '\0') {
     492       36095 :                 fname_rel = ".";
     493       36095 :                 dirname = talloc_strdup(mem_ctx, "");
     494       36095 :                 if (dirname == NULL) {
     495           0 :                         return false;
     496             :                 }
     497       36095 :                 goto done;
     498             :         }
     499             : 
     500      651856 :         lcomp = strrchr_m(name_in, '/');
     501      651856 :         if (lcomp != NULL) {
     502      573360 :                 fname_rel = lcomp+1;
     503      573360 :                 dirname = talloc_strndup(mem_ctx, name_in, lcomp - name_in);
     504      573360 :                 if (dirname == NULL) {
     505           0 :                         return false;
     506             :                 }
     507      573360 :                 goto find_stream;
     508             :         }
     509             : 
     510             :         /*
     511             :          * No slash, dir is empty
     512             :          */
     513       78496 :         dirname = talloc_strdup(mem_ctx, "");
     514       78496 :         if (dirname == NULL) {
     515           0 :                 return false;
     516             :         }
     517             : 
     518       78496 :         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       77069 :         fname_rel = name_in;
     528             : 
     529      651824 : find_stream:
     530      651824 :         if (!posix) {
     531      647451 :                 streamname = strchr_m(fname_rel, ':');
     532             : 
     533      647451 :                 if (streamname != NULL) {
     534        6402 :                         fname_rel = talloc_strndup(
     535             :                                 mem_ctx,
     536             :                                 fname_rel,
     537        6401 :                                 streamname - fname_rel);
     538        6401 :                         if (fname_rel == NULL) {
     539           0 :                                 TALLOC_FREE(dirname);
     540           0 :                                 return false;
     541             :                         }
     542             :                 }
     543             :         }
     544             : 
     545      651824 : done:
     546      687951 :         *_dirname = dirname;
     547      687951 :         *_fname_rel = fname_rel;
     548      687951 :         *_streamname = streamname;
     549      687951 :         return true;
     550             : }
     551             : 
     552             : /*
     553             :  * Create the correct capitalization of a file name to be created.
     554             :  */
     555      268600 : 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      268600 :         char *name = name_in;
     562             : 
     563      268600 :         *_normalized = NULL;
     564             : 
     565      537196 :         if (!conn->case_preserve ||
     566      268596 :             (mangle_is_8_3(name, false,
     567      268596 :                            conn->params) &&
     568      237491 :              !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      268600 :         if (mangle_is_mangled(name, conn->params)) {
     580           0 :                 bool found;
     581          51 :                 char *unmangled = NULL;
     582             : 
     583          51 :                 found = mangle_lookup_name_from_8_3(
     584          51 :                         mem_ctx, name, &unmangled, conn->params);
     585          51 :                 if (found) {
     586          43 :                         name = unmangled;
     587             :                 }
     588             :         }
     589             : 
     590      268600 :         if (name != name_in) {
     591          47 :                 *_normalized = name;
     592             :         }
     593             : 
     594      268600 :         return NT_STATUS_OK;
     595             : }
     596             : 
     597        6704 : static const char *previous_slash(const char *name_in, const char *slash)
     598             : {
     599        6704 :         const char *prev = NULL;
     600             : 
     601        6704 :         SMB_ASSERT((name_in <= slash) && (slash[0] == '/'));
     602             : 
     603        6704 :         prev = strchr_m(name_in, '/');
     604             : 
     605        6704 :         if (prev == slash) {
     606             :                 /* No previous slash */
     607         128 :                 return NULL;
     608             :         }
     609             : 
     610           0 :         while (true) {
     611        6576 :                 const char *next = strchr_m(prev + 1, '/');
     612             : 
     613        6576 :                 if (next == slash) {
     614        6576 :                         return prev;
     615             :                 }
     616           0 :                 prev = next;
     617             :         }
     618             : 
     619             :         return NULL; /* unreachable */
     620             : }
     621             : 
     622       20448 : 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       20448 :         size_t name_in_len = strlen(name_in);
     629       20448 :         const char *p_unparsed = NULL;
     630       20448 :         const char *parent = NULL;
     631           0 :         char *ret;
     632             : 
     633       20448 :         SMB_ASSERT(unparsed <= name_in_len);
     634             : 
     635       20448 :         p_unparsed = name_in + (name_in_len - unparsed);
     636             : 
     637       20448 :         if (substitute[0] == '/') {
     638       11108 :                 ret = talloc_asprintf(mem_ctx, "%s%s", substitute, p_unparsed);
     639       11108 :                 return ret;
     640             :         }
     641             : 
     642        9340 :         if (unparsed == 0) {
     643        2636 :                 parent = strrchr_m(name_in, '/');
     644             :         } else {
     645        6704 :                 parent = previous_slash(name_in, p_unparsed);
     646             :         }
     647             : 
     648        9340 :         if (parent == NULL) {
     649         228 :                 ret = talloc_asprintf(mem_ctx, "%s%s", substitute, p_unparsed);
     650             :         } else {
     651        9112 :                 ret = talloc_asprintf(mem_ctx,
     652             :                                       "%.*s/%s%s",
     653        9112 :                                       (int)(parent - name_in),
     654             :                                       name_in,
     655             :                                       substitute,
     656             :                                       p_unparsed);
     657             :         }
     658             : 
     659        9340 :         return ret;
     660             : }
     661             : 
     662       20448 : static NTSTATUS safe_symlink_target_path(
     663             :         TALLOC_CTX *mem_ctx,
     664             :         const char *connectpath,
     665             :         const char *name_in,
     666             :         const char *substitute,
     667             :         size_t unparsed,
     668             :         char **_name_out)
     669             : {
     670       20448 :         char *target = NULL;
     671       20448 :         char *abs_target = NULL;
     672       20448 :         char *abs_target_canon = NULL;
     673       20448 :         const char *relative = NULL;
     674       20448 :         char *name_out = NULL;
     675       20448 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
     676           0 :         bool in_share;
     677             : 
     678       20448 :         target = symlink_target_path(mem_ctx, name_in, substitute, unparsed);
     679       20448 :         if (target == NULL) {
     680           0 :                 goto fail;
     681             :         }
     682             : 
     683       20448 :         DBG_DEBUG("name_in: %s, substitute: %s, unparsed: %zu, target=%s\n",
     684             :                   name_in,
     685             :                   substitute,
     686             :                   unparsed,
     687             :                   target);
     688             : 
     689       20448 :         if (target[0] == '/') {
     690       11108 :                 abs_target = target;
     691             :         } else {
     692        9340 :                 abs_target = talloc_asprintf(
     693             :                         target, "%s/%s", connectpath, target);
     694        9340 :                 if (abs_target == NULL) {
     695           0 :                         goto fail;
     696             :                 }
     697             :         }
     698             : 
     699       20448 :         abs_target_canon = canonicalize_absolute_path(target, abs_target);
     700       20448 :         if (abs_target_canon == NULL) {
     701           0 :                 goto fail;
     702             :         }
     703             : 
     704       20448 :         DBG_DEBUG("abs_target_canon=%s\n", abs_target_canon);
     705             : 
     706       20448 :         in_share = subdir_of(
     707             :                 connectpath, strlen(connectpath), abs_target_canon, &relative);
     708       20448 :         if (!in_share) {
     709        1878 :                 DBG_DEBUG("wide link to %s\n", abs_target_canon);
     710        1878 :                 status = (unparsed != 0) ? NT_STATUS_OBJECT_PATH_NOT_FOUND
     711           2 :                                          : NT_STATUS_OBJECT_NAME_NOT_FOUND;
     712        1878 :                 goto fail;
     713             :         }
     714             : 
     715       18570 :         name_out = talloc_strdup(mem_ctx, relative);
     716       18570 :         if (name_out == NULL) {
     717           0 :                 goto fail;
     718             :         }
     719             : 
     720       18570 :         status = NT_STATUS_OK;
     721       18570 :         *_name_out = name_out;
     722       20448 : fail:
     723       20448 :         TALLOC_FREE(target);
     724       20448 :         return status;
     725             : }
     726             : 
     727             : /*
     728             :  * Split up name_in as sent by the client into a directory pathref fsp
     729             :  * and a relative smb_filename.
     730             :  */
     731      687998 : static NTSTATUS filename_convert_dirfsp_nosymlink(
     732             :         TALLOC_CTX *mem_ctx,
     733             :         connection_struct *conn,
     734             :         const char *name_in,
     735             :         uint32_t ucf_flags,
     736             :         NTTIME twrp,
     737             :         struct files_struct **_dirfsp,
     738             :         struct smb_filename **_smb_fname,
     739             :         struct open_symlink_err **_symlink_err)
     740             : {
     741      687998 :         struct smb_filename *smb_dirname = NULL;
     742      687998 :         struct smb_filename *smb_fname_rel = NULL;
     743      687998 :         struct smb_filename *smb_fname = NULL;
     744      687998 :         struct open_symlink_err *symlink_err = NULL;
     745      687998 :         const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
     746      687998 :         char *dirname = NULL;
     747      687998 :         const char *fname_rel = NULL;
     748      687998 :         const char *streamname = NULL;
     749      687998 :         char *saved_streamname = NULL;
     750      687998 :         struct files_struct *base_fsp = NULL;
     751       10413 :         bool ok;
     752      687998 :         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
     753             : 
     754      687998 :         SMB_ASSERT(!(ucf_flags & UCF_DFS_PATHNAME));
     755             : 
     756      687998 :         if (is_fake_file_path(name_in)) {
     757          21 :                 smb_fname = synthetic_smb_fname_split(mem_ctx, name_in, posix);
     758          21 :                 if (smb_fname == NULL) {
     759           0 :                         return NT_STATUS_NO_MEMORY;
     760             :                 }
     761          21 :                 smb_fname->st = (SMB_STRUCT_STAT){
     762             :                         .st_ex_nlink = 1,
     763             :                         .st_ex_mode = S_IFREG | 0644,
     764             :                 };
     765          21 :                 smb_fname->st.st_ex_btime =
     766             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     767          21 :                 smb_fname->st.st_ex_atime =
     768             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     769          21 :                 smb_fname->st.st_ex_mtime =
     770             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     771          21 :                 smb_fname->st.st_ex_ctime =
     772             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     773             : 
     774          21 :                 *_dirfsp = conn->cwd_fsp;
     775          21 :                 *_smb_fname = smb_fname;
     776          21 :                 return NT_STATUS_OK;
     777             :         }
     778             : 
     779             :         /*
     780             :          * Catch an invalid path of "." before we
     781             :          * call filename_split_lcomp(). We need to
     782             :          * do this as filename_split_lcomp() will
     783             :          * use "." for the missing relative component
     784             :          * when an empty name_in path is sent by
     785             :          * the client.
     786             :          */
     787      687977 :         if (ISDOT(name_in)) {
     788          26 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     789          26 :                 goto fail;
     790             :         }
     791             : 
     792      687951 :         ok = filename_split_lcomp(
     793             :                 talloc_tos(),
     794             :                 name_in,
     795             :                 posix,
     796             :                 &dirname,
     797             :                 &fname_rel,
     798             :                 &streamname);
     799      687951 :         if (!ok) {
     800           0 :                 status = NT_STATUS_NO_MEMORY;
     801           0 :                 goto fail;
     802             :         }
     803             : 
     804      687951 :         if ((streamname != NULL) &&
     805        6433 :             ((conn->fs_capabilities & FILE_NAMED_STREAMS) == 0)) {
     806           8 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     807           8 :                 goto fail;
     808             :         }
     809             : 
     810      687943 :         if (!posix) {
     811      683470 :                 bool name_has_wild = ms_has_wild(dirname);
     812      683470 :                 name_has_wild |= ms_has_wild(fname_rel);
     813      683470 :                 if (name_has_wild) {
     814        4456 :                         status = NT_STATUS_OBJECT_NAME_INVALID;
     815        4456 :                         goto fail;
     816             :                 }
     817             :         }
     818             : 
     819      683487 :         if (dirname[0] == '\0') {
     820      116057 :                 status = synthetic_pathref(
     821             :                         mem_ctx,
     822             :                         conn->cwd_fsp,
     823             :                         ".",
     824             :                         NULL,
     825             :                         NULL,
     826             :                         0,
     827             :                         posix ? SMB_FILENAME_POSIX_PATH : 0,
     828             :                         &smb_dirname);
     829             :         } else {
     830      568928 :                 status = normalize_filename_case(conn, dirname, ucf_flags);
     831      568928 :                 if (!NT_STATUS_IS_OK(status)) {
     832           0 :                         DBG_ERR("normalize_filename_case %s failed: %s\n",
     833             :                                 dirname,
     834             :                                 nt_errstr(status));
     835           0 :                         goto fail;
     836             :                 }
     837             : 
     838      568928 :                 status = openat_pathref_fsp_nosymlink(mem_ctx,
     839             :                                                       conn,
     840             :                                                       conn->cwd_fsp,
     841             :                                                       dirname,
     842             :                                                       twrp,
     843             :                                                       posix,
     844             :                                                       &smb_dirname,
     845             :                                                       &symlink_err);
     846             : 
     847      568928 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
     848           0 :                         size_t name_in_len, dirname_len;
     849             : 
     850       61794 :                         name_in_len = strlen(name_in);
     851       61794 :                         dirname_len = strlen(dirname);
     852             : 
     853       61794 :                         SMB_ASSERT(name_in_len >= dirname_len);
     854             : 
     855       61794 :                         symlink_err->unparsed += (name_in_len - dirname_len);
     856       61794 :                         *_symlink_err = symlink_err;
     857             : 
     858       61794 :                         goto fail;
     859             :                 }
     860             :         }
     861             : 
     862      621693 :         if (!NT_STATUS_IS_OK(status)) {
     863        1029 :                 DBG_DEBUG("opening directory %s failed: %s\n",
     864             :                           dirname,
     865             :                           nt_errstr(status));
     866        1029 :                 TALLOC_FREE(dirname);
     867             : 
     868        1029 :                 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
     869             :                         /*
     870             :                          * Except ACCESS_DENIED, everything else leads
     871             :                          * to PATH_NOT_FOUND.
     872             :                          */
     873        1017 :                         status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
     874             :                 }
     875             : 
     876        1029 :                 goto fail;
     877             :         }
     878             : 
     879      620664 :         if (!VALID_STAT_OF_DIR(smb_dirname->st)) {
     880          20 :                 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
     881          20 :                 goto fail;
     882             :         }
     883      620644 :         smb_dirname->fsp->fsp_flags.is_directory = true;
     884             : 
     885             :         /*
     886             :          * Only look at bad last component values
     887             :          * once we know we have a valid directory. That
     888             :          * way we won't confuse error messages from
     889             :          * opening the directory path with error
     890             :          * messages from a bad last component.
     891             :          */
     892             : 
     893             :         /* Relative filename can't be empty */
     894      620644 :         if (fname_rel[0] == '\0') {
     895           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     896           0 :                 goto fail;
     897             :         }
     898             : 
     899             :         /* Relative filename can't be ".." */
     900      620644 :         if (ISDOTDOT(fname_rel)) {
     901           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     902           0 :                 goto fail;
     903             :         }
     904             :         /* Relative name can only be dot if directory is empty. */
     905      620644 :         if (ISDOT(fname_rel) && dirname[0] != '\0') {
     906           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     907           0 :                 goto fail;
     908             :         }
     909             : 
     910      620644 :         TALLOC_FREE(dirname);
     911             : 
     912      631048 :         smb_fname_rel = synthetic_smb_fname(
     913             :                 mem_ctx,
     914             :                 fname_rel,
     915             :                 streamname,
     916             :                 NULL,
     917             :                 twrp,
     918             :                 posix ? SMB_FILENAME_POSIX_PATH : 0);
     919      620644 :         if (smb_fname_rel == NULL) {
     920           0 :                 status = NT_STATUS_NO_MEMORY;
     921           0 :                 goto fail;
     922             :         }
     923             : 
     924     1180537 :         if ((conn->fs_capabilities & FILE_NAMED_STREAMS) &&
     925      559893 :             is_named_stream(smb_fname_rel)) {
     926             :                 /*
     927             :                  * Find the base_fsp first without the stream.
     928             :                  */
     929        6381 :                 saved_streamname = smb_fname_rel->stream_name;
     930        6381 :                 smb_fname_rel->stream_name = NULL;
     931             :         }
     932             : 
     933      620644 :         status = normalize_filename_case(
     934             :                 conn, smb_fname_rel->base_name, ucf_flags);
     935      620644 :         if (!NT_STATUS_IS_OK(status)) {
     936           0 :                 DBG_ERR("normalize_filename_case %s failed: %s\n",
     937             :                         smb_fname_rel->base_name,
     938             :                         nt_errstr(status));
     939           0 :                 goto fail;
     940             :         }
     941             : 
     942      620644 :         status = openat_pathref_fsp_lcomp(smb_dirname->fsp,
     943             :                                           smb_fname_rel,
     944             :                                           ucf_flags);
     945             : 
     946      620644 :         if (NT_STATUS_IS_OK(status) && S_ISLNK(smb_fname_rel->st.st_ex_mode)) {
     947             : 
     948             :                 /*
     949             :                  * Upper layers might need the link target. Here we
     950             :                  * still have the relname around, get the symlink err.
     951             :                  */
     952        9368 :                 status = create_open_symlink_err(mem_ctx,
     953        9368 :                                                  smb_dirname->fsp,
     954             :                                                  smb_fname_rel,
     955             :                                                  &symlink_err);
     956        9368 :                 if (!NT_STATUS_IS_OK(status)) {
     957           0 :                         DBG_DEBUG("Could not read symlink for %s: %s\n",
     958             :                                   smb_fname_str_dbg(
     959             :                                           smb_fname_rel->fsp->fsp_name),
     960             :                                   nt_errstr(status));
     961           0 :                         goto fail;
     962             :                 }
     963             :         }
     964             : 
     965      620644 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
     966      268600 :             !VALID_STAT(smb_fname_rel->st)) {
     967             : 
     968      268600 :                 char *normalized = NULL;
     969             : 
     970             :                 /*
     971             :                  * Creating a new file
     972             :                  */
     973             : 
     974      268600 :                 status = filename_convert_normalize_new(
     975             :                         smb_fname_rel,
     976             :                         conn,
     977             :                         smb_fname_rel->base_name,
     978             :                         &normalized);
     979      268600 :                 if (!NT_STATUS_IS_OK(status)) {
     980           0 :                         DBG_DEBUG("filename_convert_normalize_new failed: "
     981             :                                   "%s\n",
     982             :                                   nt_errstr(status));
     983           0 :                         goto fail;
     984             :                 }
     985      268600 :                 if (normalized != NULL) {
     986          47 :                         smb_fname_rel->base_name = normalized;
     987             :                 }
     988             : 
     989      268600 :                 smb_fname_rel->stream_name = saved_streamname;
     990             : 
     991      269476 :                 smb_fname = full_path_from_dirfsp_atname(
     992      268600 :                         mem_ctx, smb_dirname->fsp, smb_fname_rel);
     993      268600 :                 if (smb_fname == NULL) {
     994           0 :                         status = NT_STATUS_NO_MEMORY;
     995           0 :                         goto fail;
     996             :                 }
     997      268600 :                 goto done;
     998             :         }
     999             : 
    1000      352044 :         if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_OPEN_RESTRICTION)) {
    1001             :                 /* A vetoed file, pretend it's not there  */
    1002          14 :                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
    1003             :         }
    1004      352044 :         if (!NT_STATUS_IS_OK(status)) {
    1005          24 :                 goto fail;
    1006             :         }
    1007             : 
    1008      352020 :         if (saved_streamname == NULL) {
    1009             :                 /* smb_fname must be allocated off mem_ctx. */
    1010      355378 :                 smb_fname = cp_smb_filename(mem_ctx,
    1011      345851 :                                             smb_fname_rel->fsp->fsp_name);
    1012      345851 :                 if (smb_fname == NULL) {
    1013           0 :                         goto fail;
    1014             :                 }
    1015      345851 :                 status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel);
    1016      345851 :                 if (!NT_STATUS_IS_OK(status)) {
    1017           0 :                         goto fail;
    1018             :                 }
    1019      345851 :                 goto done;
    1020             :         }
    1021             : 
    1022        6169 :         base_fsp = smb_fname_rel->fsp;
    1023        6169 :         smb_fname_fsp_unlink(smb_fname_rel);
    1024        6169 :         SET_STAT_INVALID(smb_fname_rel->st);
    1025             : 
    1026        6169 :         smb_fname_rel->stream_name = saved_streamname;
    1027             : 
    1028        6169 :         status = open_stream_pathref_fsp(&base_fsp, smb_fname_rel);
    1029             : 
    1030        6169 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
    1031        3821 :             !conn->case_sensitive) {
    1032        3821 :                 char *found = NULL;
    1033             : 
    1034        3822 :                 status = get_real_stream_name(
    1035             :                         smb_fname_rel,
    1036             :                         base_fsp,
    1037        3821 :                         smb_fname_rel->stream_name,
    1038             :                         &found);
    1039             : 
    1040        3821 :                 if (NT_STATUS_IS_OK(status)) {
    1041          60 :                         smb_fname_rel->stream_name = found;
    1042          60 :                         found = NULL;
    1043          60 :                         status = open_stream_pathref_fsp(
    1044             :                                 &base_fsp, smb_fname_rel);
    1045             :                 }
    1046             :         }
    1047             : 
    1048        6169 :         if (NT_STATUS_IS_OK(status)) {
    1049             :                 /* smb_fname must be allocated off mem_ctx. */
    1050        2360 :                 smb_fname = cp_smb_filename(mem_ctx,
    1051        2360 :                                             smb_fname_rel->fsp->fsp_name);
    1052        2360 :                 if (smb_fname == NULL) {
    1053           0 :                         goto fail;
    1054             :                 }
    1055        2360 :                 status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel);
    1056        2360 :                 if (!NT_STATUS_IS_OK(status)) {
    1057           0 :                         goto fail;
    1058             :                 }
    1059        2360 :                 goto done;
    1060             :         }
    1061             : 
    1062        3809 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
    1063             :                 /*
    1064             :                  * Creating a new stream
    1065             :                  *
    1066             :                  * We should save the already-open base fsp for
    1067             :                  * create_file_unixpath() somehow.
    1068             :                  */
    1069        3762 :                 smb_fname = full_path_from_dirfsp_atname(
    1070        3761 :                         mem_ctx, smb_dirname->fsp, smb_fname_rel);
    1071        3761 :                 if (smb_fname == NULL) {
    1072           0 :                         status = NT_STATUS_NO_MEMORY;
    1073           0 :                         goto fail;
    1074             :                 }
    1075             :                 /*
    1076             :                  * When open_stream_pathref_fsp() returns
    1077             :                  * NT_STATUS_OBJECT_NAME_NOT_FOUND, smb_fname_rel->fsp
    1078             :                  * has been set to NULL, so we must free base_fsp separately
    1079             :                  * to prevent fd-leaks when opening a stream that doesn't
    1080             :                  * exist.
    1081             :                  */
    1082        3761 :                 fd_close(base_fsp);
    1083        3761 :                 file_free(NULL, base_fsp);
    1084        3761 :                 base_fsp = NULL;
    1085        3761 :                 goto done;
    1086             :         }
    1087             : 
    1088          48 :         if (!NT_STATUS_IS_OK(status)) {
    1089          48 :                 goto fail;
    1090             :         }
    1091             : 
    1092       10404 : done:
    1093      620572 :         *_dirfsp = smb_dirname->fsp;
    1094      620572 :         *_smb_fname = smb_fname;
    1095      620572 :         *_symlink_err = symlink_err;
    1096             : 
    1097      620572 :         smb_fname_fsp_unlink(smb_fname_rel);
    1098      620572 :         TALLOC_FREE(smb_fname_rel);
    1099      620572 :         return NT_STATUS_OK;
    1100             : 
    1101       67405 : fail:
    1102             :         /*
    1103             :          * If open_stream_pathref_fsp() returns an error, smb_fname_rel->fsp
    1104             :          * has been set to NULL, so we must free base_fsp separately
    1105             :          * to prevent fd-leaks when opening a stream that doesn't
    1106             :          * exist.
    1107             :          */
    1108       67405 :         if (base_fsp != NULL) {
    1109          48 :                 fd_close(base_fsp);
    1110          48 :                 file_free(NULL, base_fsp);
    1111          48 :                 base_fsp = NULL;
    1112             :         }
    1113       67405 :         TALLOC_FREE(dirname);
    1114       67405 :         TALLOC_FREE(smb_dirname);
    1115       67405 :         TALLOC_FREE(smb_fname_rel);
    1116       67405 :         return status;
    1117             : }
    1118             : 
    1119      669448 : NTSTATUS filename_convert_dirfsp(
    1120             :         TALLOC_CTX *mem_ctx,
    1121             :         connection_struct *conn,
    1122             :         const char *name_in,
    1123             :         uint32_t ucf_flags,
    1124             :         NTTIME twrp,
    1125             :         struct files_struct **_dirfsp,
    1126             :         struct smb_filename **_smb_fname)
    1127             : {
    1128      669448 :         struct open_symlink_err *symlink_err = NULL;
    1129       10413 :         NTSTATUS status;
    1130      669448 :         char *substitute = NULL;
    1131      669448 :         char *target = NULL;
    1132      669448 :         size_t symlink_redirects = 0;
    1133             : 
    1134      688018 : next:
    1135      688018 :         if (symlink_redirects > 40) {
    1136          20 :                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
    1137             :         }
    1138             : 
    1139      687998 :         status = filename_convert_dirfsp_nosymlink(mem_ctx,
    1140             :                                                    conn,
    1141             :                                                    name_in,
    1142             :                                                    ucf_flags,
    1143             :                                                    twrp,
    1144             :                                                    _dirfsp,
    1145             :                                                    _smb_fname,
    1146             :                                                    &symlink_err);
    1147             : 
    1148      687998 :         if (NT_STATUS_IS_OK(status) && S_ISLNK((*_smb_fname)->st.st_ex_mode)) {
    1149             :                 /*
    1150             :                  * lcomp is a symlink
    1151             :                  */
    1152        9368 :                 if (ucf_flags & UCF_LCOMP_LNK_OK) {
    1153         740 :                         TALLOC_FREE(symlink_err);
    1154         740 :                         return NT_STATUS_OK;
    1155             :                 }
    1156        8628 :                 close_file_free(NULL, _dirfsp, ERROR_CLOSE);
    1157        8628 :                 status = NT_STATUS_STOPPED_ON_SYMLINK;
    1158             :         }
    1159             : 
    1160      687258 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
    1161      616836 :                 return status;
    1162             :         }
    1163             : 
    1164             :         /*
    1165             :          * If we're on an MSDFS share, see if this is
    1166             :          * an MSDFS link.
    1167             :          */
    1168      120368 :         if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&
    1169       49946 :             strnequal(symlink_err->reparse->substitute_name, "msdfs:", 6))
    1170             :         {
    1171       49946 :                 TALLOC_FREE(*_smb_fname);
    1172       49946 :                 TALLOC_FREE(symlink_err);
    1173       49946 :                 return NT_STATUS_PATH_NOT_COVERED;
    1174             :         }
    1175             : 
    1176       20476 :         if (!lp_follow_symlinks(SNUM(conn))) {
    1177          28 :                 status = (symlink_err->unparsed == 0)
    1178             :                                  ? NT_STATUS_OBJECT_NAME_NOT_FOUND
    1179           2 :                                  : NT_STATUS_OBJECT_PATH_NOT_FOUND;
    1180          28 :                 TALLOC_FREE(symlink_err);
    1181          28 :                 return status;
    1182             :         }
    1183             : 
    1184             :         /*
    1185             :          * Right now, SMB2 and SMB1 always traverse symlinks
    1186             :          * within the share. SMB1+POSIX traverses non-terminal
    1187             :          * symlinks within the share.
    1188             :          *
    1189             :          * When we add SMB2+POSIX we need to return
    1190             :          * a NT_STATUS_STOPPED_ON_SYMLINK error here, using the
    1191             :          * symlink target data read below if SMB2+POSIX has
    1192             :          * UCF_POSIX_PATHNAMES set to cause the client to
    1193             :          * resolve all symlinks locally.
    1194             :          */
    1195             : 
    1196       20448 :         substitute = symlink_err->reparse->substitute_name;
    1197             : 
    1198       20448 :         status = safe_symlink_target_path(mem_ctx,
    1199       20448 :                                           conn->connectpath,
    1200             :                                           name_in,
    1201             :                                           substitute,
    1202       20448 :                                           symlink_err->unparsed,
    1203             :                                           &target);
    1204       20448 :         TALLOC_FREE(symlink_err);
    1205       20448 :         if (!NT_STATUS_IS_OK(status)) {
    1206        1878 :                 return status;
    1207             :         }
    1208       18570 :         name_in = target;
    1209             : 
    1210       18570 :         symlink_redirects += 1;
    1211             : 
    1212       18570 :         goto next;
    1213             : }
    1214             : 
    1215     5917095 : char *full_path_from_dirfsp_at_basename(TALLOC_CTX *mem_ctx,
    1216             :                                         const struct files_struct *dirfsp,
    1217             :                                         const char *at_base_name)
    1218             : {
    1219     5917095 :         char *path = NULL;
    1220             : 
    1221     5917095 :         if (dirfsp == dirfsp->conn->cwd_fsp ||
    1222     2450845 :             ISDOT(dirfsp->fsp_name->base_name) || at_base_name[0] == '/') {
    1223     3697169 :                 path = talloc_strdup(mem_ctx, at_base_name);
    1224             :         } else {
    1225     2219926 :                 path = talloc_asprintf(mem_ctx,
    1226             :                                        "%s/%s",
    1227     2210351 :                                        dirfsp->fsp_name->base_name,
    1228             :                                        at_base_name);
    1229             :         }
    1230             : 
    1231     5917095 :         return path;
    1232             : }
    1233             : 
    1234             : /*
    1235             :  * Build the full path from a dirfsp and dirfsp relative name
    1236             :  */
    1237             : struct smb_filename *
    1238     5916657 : full_path_from_dirfsp_atname(TALLOC_CTX *mem_ctx,
    1239             :                              const struct files_struct *dirfsp,
    1240             :                              const struct smb_filename *atname)
    1241             : {
    1242     5916657 :         struct smb_filename *fname = NULL;
    1243     5916657 :         char *path = NULL;
    1244             : 
    1245     5939448 :         path = full_path_from_dirfsp_at_basename(mem_ctx,
    1246             :                                                  dirfsp,
    1247     5916657 :                                                  atname->base_name);
    1248     5916657 :         if (path == NULL) {
    1249           0 :                 return NULL;
    1250             :         }
    1251             : 
    1252     5939448 :         fname = synthetic_smb_fname(mem_ctx,
    1253             :                                     path,
    1254     5916657 :                                     atname->stream_name,
    1255             :                                     &atname->st,
    1256     5916657 :                                     atname->twrp,
    1257     5916657 :                                     atname->flags);
    1258     5916657 :         TALLOC_FREE(path);
    1259     5916657 :         if (fname == NULL) {
    1260           0 :                 return NULL;
    1261             :         }
    1262             : 
    1263     5893866 :         return fname;
    1264             : }

Generated by: LCOV version 1.14