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

Generated by: LCOV version 1.14