LCOV - code coverage report
Current view: top level - source3/smbd - filename.c (source / functions) Hit Total Coverage
Test: coverage report for vadcx-master-patch-75612 fe003de8 Lines: 473 549 86.2 %
Date: 2024-02-29 22:57:05 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      623104 : uint32_t ucf_flags_from_smb_request(struct smb_request *req)
      36             : {
      37      623104 :         uint32_t ucf_flags = 0;
      38             : 
      39      623104 :         if (req == NULL) {
      40           0 :                 return 0;
      41             :         }
      42             : 
      43      623104 :         if (req->posix_pathnames) {
      44        4429 :                 ucf_flags |= UCF_POSIX_PATHNAMES;
      45             : 
      46        4429 :                 if (!req->sconn->using_smb2) {
      47        2285 :                         ucf_flags |= UCF_LCOMP_LNK_OK;
      48             :                 }
      49             :         }
      50      623104 :         if (req->flags2 & FLAGS2_DFS_PATHNAMES) {
      51        1624 :                 ucf_flags |= UCF_DFS_PATHNAME;
      52             :         }
      53      623104 :         if (req->flags2 & FLAGS2_REPARSE_PATH) {
      54        4178 :                 ucf_flags |= UCF_GMT_PATHNAME;
      55             :         }
      56             : 
      57      612627 :         return ucf_flags;
      58             : }
      59             : 
      60      516114 : uint32_t filename_create_ucf_flags(struct smb_request *req, uint32_t create_disposition)
      61             : {
      62      516114 :         uint32_t ucf_flags = 0;
      63             : 
      64      516114 :         ucf_flags |= ucf_flags_from_smb_request(req);
      65             : 
      66      516114 :         switch (create_disposition) {
      67      333021 :         case FILE_OPEN:
      68             :         case FILE_OVERWRITE:
      69      333021 :                 break;
      70      182461 :         case FILE_SUPERSEDE:
      71             :         case FILE_CREATE:
      72             :         case FILE_OPEN_IF:
      73             :         case FILE_OVERWRITE_IF:
      74      182461 :                 ucf_flags |= UCF_PREP_CREATEFILE;
      75      182461 :                 break;
      76             :         }
      77             : 
      78      516114 :         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     1227415 : static NTSTATUS normalize_filename_case(connection_struct *conn,
     152             :                                         char *filename,
     153             :                                         uint32_t ucf_flags)
     154             : {
     155       23613 :         bool ok;
     156             : 
     157     1227415 :         if (ucf_flags & UCF_POSIX_PATHNAMES) {
     158             :                 /*
     159             :                  * POSIX never normalizes filename case.
     160             :                  */
     161        4955 :                 return NT_STATUS_OK;
     162             :         }
     163     1222460 :         if (!conn->case_sensitive) {
     164     1220118 :                 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   158832314 : static bool fname_equal(const char *name1, const char *name2,
     185             :                 bool case_sensitive)
     186             : {
     187             :         /* Normal filename handling */
     188   158832314 :         if (case_sensitive) {
     189           0 :                 return(strcmp(name1,name2) == 0);
     190             :         }
     191             : 
     192   158832314 :         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      273540 : 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      273540 :         struct connection_struct *conn = dirfsp->conn;
     277      273540 :         struct smb_Dir *cur_dir = NULL;
     278      273540 :         const char *dname = NULL;
     279      273540 :         char *talloced = NULL;
     280      273540 :         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      273540 :         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      273540 :         if (mangled && !conn->case_sensitive) {
     307         138 :                 mangled = !mangle_lookup_name_from_8_3(talloc_tos(), name,
     308             :                                                        &unmangled_name,
     309         138 :                                                        conn->params);
     310         138 :                 if (!mangled) {
     311             :                         /* Name is now unmangled. */
     312         114 :                         name = unmangled_name;
     313             :                 }
     314             :         }
     315             : 
     316             :         /* open the directory */
     317      273540 :         status = OpenDir_from_pathref(talloc_tos(), dirfsp, NULL, 0, &cur_dir);
     318      273540 :         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   159642784 :         while ((dname = ReadDirName(cur_dir, &talloced))) {
     328             : 
     329             :                 /* Is it dot or dot dot. */
     330   159375454 :                 if (ISDOT(dname) || ISDOTDOT(dname)) {
     331      547078 :                         TALLOC_FREE(talloced);
     332      547078 :                         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   317656728 :                 if ((mangled && mangled_equal(name,dname,conn->params)) ||
     347   158828352 :                         fname_equal(name, dname, conn->case_sensitive)) {
     348             :                         /* we've found the file, change it's name and return */
     349        6209 :                         *found_name = talloc_strdup(mem_ctx, dname);
     350        6209 :                         TALLOC_FREE(unmangled_name);
     351        6209 :                         TALLOC_FREE(cur_dir);
     352        6209 :                         if (!*found_name) {
     353           0 :                                 TALLOC_FREE(talloced);
     354           0 :                                 return NT_STATUS_NO_MEMORY;
     355             :                         }
     356        6209 :                         TALLOC_FREE(talloced);
     357        6209 :                         return NT_STATUS_OK;
     358             :                 }
     359   158828836 :                 TALLOC_FREE(talloced);
     360             :         }
     361             : 
     362      267330 :         TALLOC_FREE(unmangled_name);
     363      267330 :         TALLOC_FREE(cur_dir);
     364      267330 :         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      273540 : NTSTATUS get_real_filename_at(struct files_struct *dirfsp,
     373             :                               const char *name,
     374             :                               TALLOC_CTX *mem_ctx,
     375             :                               char **found_name)
     376             : {
     377      273540 :         struct connection_struct *conn = dirfsp->conn;
     378         883 :         NTSTATUS status;
     379         883 :         bool mangled;
     380             : 
     381      273540 :         mangled = mangle_is_mangled(name, conn->params);
     382             : 
     383      273540 :         if (mangled) {
     384         138 :                 status = get_real_filename_full_scan_at(
     385             :                         dirfsp, name, mangled, mem_ctx, found_name);
     386         138 :                 return status;
     387             :         }
     388             : 
     389             :         /* Try the vfs first to take advantage of case-insensitive stat. */
     390      273402 :         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      273402 :         if (NT_STATUS_IS_OK(status) ||
     399      272519 :             !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
     400           0 :                 return status;
     401             :         }
     402             : 
     403      273402 :         status = get_real_filename_full_scan_at(
     404             :                 dirfsp, name, mangled, mem_ctx, found_name);
     405      273402 :         return status;
     406             : }
     407             : 
     408             : /*
     409             :  * Lightweight function to just get last component
     410             :  * for rename / enumerate directory calls.
     411             :  */
     412             : 
     413       32492 : char *get_original_lcomp(TALLOC_CTX *ctx,
     414             :                         connection_struct *conn,
     415             :                         const char *filename_in,
     416             :                         uint32_t ucf_flags)
     417             : {
     418       32492 :         char *last_slash = NULL;
     419        4300 :         char *orig_lcomp;
     420        4300 :         NTSTATUS status;
     421             : 
     422       32492 :         last_slash = strrchr(filename_in, '/');
     423       32492 :         if (last_slash != NULL) {
     424       28941 :                 orig_lcomp = talloc_strdup(ctx, last_slash+1);
     425             :         } else {
     426        3551 :                 orig_lcomp = talloc_strdup(ctx, filename_in);
     427             :         }
     428       32492 :         if (orig_lcomp == NULL) {
     429           0 :                 return NULL;
     430             :         }
     431       32492 :         status = normalize_filename_case(conn, orig_lcomp, ucf_flags);
     432       32492 :         if (!NT_STATUS_IS_OK(status)) {
     433           0 :                 TALLOC_FREE(orig_lcomp);
     434           0 :                 return NULL;
     435             :         }
     436       28192 :         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      691565 : 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      691565 :         const char *lcomp = NULL;
     487      691565 :         const char *fname_rel = NULL;
     488      691565 :         const char *streamname = NULL;
     489      691565 :         char *dirname = NULL;
     490             : 
     491      691565 :         if (name_in[0] == '\0') {
     492       37065 :                 fname_rel = ".";
     493       37065 :                 dirname = talloc_strdup(mem_ctx, "");
     494       37065 :                 if (dirname == NULL) {
     495           0 :                         return false;
     496             :                 }
     497       37065 :                 goto done;
     498             :         }
     499             : 
     500      654500 :         lcomp = strrchr_m(name_in, '/');
     501      654500 :         if (lcomp != NULL) {
     502      575396 :                 fname_rel = lcomp+1;
     503      575396 :                 dirname = talloc_strndup(mem_ctx, name_in, lcomp - name_in);
     504      575396 :                 if (dirname == NULL) {
     505           0 :                         return false;
     506             :                 }
     507      575396 :                 goto find_stream;
     508             :         }
     509             : 
     510             :         /*
     511             :          * No slash, dir is empty
     512             :          */
     513       79104 :         dirname = talloc_strdup(mem_ctx, "");
     514       79104 :         if (dirname == NULL) {
     515           0 :                 return false;
     516             :         }
     517             : 
     518       79104 :         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       77677 :         fname_rel = name_in;
     528             : 
     529      654468 : find_stream:
     530      654468 :         if (!posix) {
     531      650085 :                 streamname = strchr_m(fname_rel, ':');
     532             : 
     533      650085 :                 if (streamname != NULL) {
     534        6401 :                         fname_rel = talloc_strndup(
     535             :                                 mem_ctx,
     536             :                                 fname_rel,
     537        6400 :                                 streamname - fname_rel);
     538        6400 :                         if (fname_rel == NULL) {
     539           0 :                                 TALLOC_FREE(dirname);
     540           0 :                                 return false;
     541             :                         }
     542             :                 }
     543             :         }
     544             : 
     545      654468 : done:
     546      691565 :         *_dirname = dirname;
     547      691565 :         *_fname_rel = fname_rel;
     548      691565 :         *_streamname = streamname;
     549      691565 :         return true;
     550             : }
     551             : 
     552             : /*
     553             :  * Create the correct capitalization of a file name to be created.
     554             :  */
     555      268609 : 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      268609 :         char *name = name_in;
     562             : 
     563      268609 :         *_normalized = NULL;
     564             : 
     565      537214 :         if (!conn->case_preserve ||
     566      268605 :             (mangle_is_8_3(name, false,
     567      268605 :                            conn->params) &&
     568      237496 :              !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      268609 :         if (mangle_is_mangled(name, conn->params)) {
     580           0 :                 bool found;
     581          38 :                 char *unmangled = NULL;
     582             : 
     583          38 :                 found = mangle_lookup_name_from_8_3(
     584          38 :                         mem_ctx, name, &unmangled, conn->params);
     585          38 :                 if (found) {
     586          30 :                         name = unmangled;
     587             :                 }
     588             :         }
     589             : 
     590      268609 :         if (name != name_in) {
     591          34 :                 *_normalized = name;
     592             :         }
     593             : 
     594      268609 :         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       22809 : NTSTATUS safe_symlink_target_path(TALLOC_CTX *mem_ctx,
     663             :                                   const char *connectpath,
     664             :                                   const char *dir,
     665             :                                   const char *target,
     666             :                                   size_t unparsed,
     667             :                                   char **_relative)
     668             : {
     669       22809 :         char *abs_target = NULL;
     670       22809 :         char *abs_target_canon = NULL;
     671       22809 :         const char *relative = NULL;
     672           0 :         bool in_share;
     673       22809 :         NTSTATUS status = NT_STATUS_NO_MEMORY;
     674             : 
     675       22809 :         DBG_DEBUG("connectpath [%s] target [%s] unparsed [%zu]\n",
     676             :                   connectpath, target, unparsed);
     677             : 
     678       22809 :         if (target[0] == '/') {
     679       13049 :                 abs_target = talloc_strdup(mem_ctx, target);
     680        9760 :         } else if (dir == NULL) {
     681        9340 :                 abs_target = talloc_asprintf(mem_ctx,
     682             :                                              "%s/%s",
     683             :                                              connectpath,
     684             :                                              target);
     685         420 :         } else if (dir[0] == '/') {
     686          20 :                 abs_target = talloc_asprintf(mem_ctx,
     687             :                                              "%s/%s",
     688             :                                              dir,
     689             :                                              target);
     690             :         } else {
     691         400 :                 abs_target = talloc_asprintf(mem_ctx,
     692             :                                              "%s/%s/%s",
     693             :                                              connectpath,
     694             :                                              dir,
     695             :                                              target);
     696             :         }
     697       22809 :         if (abs_target == NULL) {
     698           0 :                 goto fail;
     699             :         }
     700             : 
     701       22809 :         abs_target_canon = canonicalize_absolute_path(abs_target, abs_target);
     702       22809 :         if (abs_target_canon == NULL) {
     703           0 :                 goto fail;
     704             :         }
     705             : 
     706       22809 :         DBG_DEBUG("abs_target_canon=%s\n", abs_target_canon);
     707             : 
     708       22809 :         in_share = subdir_of(
     709             :                 connectpath, strlen(connectpath), abs_target_canon, &relative);
     710       22809 :         if (!in_share) {
     711        2249 :                 DBG_DEBUG("wide link to %s\n", abs_target_canon);
     712        2249 :                 status = (unparsed != 0) ? NT_STATUS_OBJECT_PATH_NOT_FOUND
     713           4 :                                          : NT_STATUS_OBJECT_NAME_NOT_FOUND;
     714        2249 :                 goto fail;
     715             :         }
     716             : 
     717       20560 :         *_relative = talloc_strdup(mem_ctx, relative);
     718       20560 :         if (*_relative == NULL) {
     719           0 :                 goto fail;
     720             :         }
     721             : 
     722       20560 :         status = NT_STATUS_OK;
     723       22809 : fail:
     724       22809 :         TALLOC_FREE(abs_target);
     725       22809 :         return status;
     726             : }
     727             : 
     728             : /*
     729             :  * Split up name_in as sent by the client into a directory pathref fsp
     730             :  * and a relative smb_filename.
     731             :  */
     732      691612 : static NTSTATUS filename_convert_dirfsp_nosymlink(
     733             :         TALLOC_CTX *mem_ctx,
     734             :         connection_struct *conn,
     735             :         const char *name_in,
     736             :         uint32_t ucf_flags,
     737             :         NTTIME twrp,
     738             :         struct files_struct **_dirfsp,
     739             :         struct smb_filename **_smb_fname,
     740             :         struct open_symlink_err **_symlink_err)
     741             : {
     742      691612 :         struct smb_filename *smb_dirname = NULL;
     743      691612 :         struct smb_filename *smb_fname_rel = NULL;
     744      691612 :         struct smb_filename *smb_fname = NULL;
     745      691612 :         struct open_symlink_err *symlink_err = NULL;
     746      691612 :         const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
     747      691612 :         char *dirname = NULL;
     748      691612 :         const char *fname_rel = NULL;
     749      691612 :         const char *streamname = NULL;
     750      691612 :         char *saved_streamname = NULL;
     751      691612 :         struct files_struct *base_fsp = NULL;
     752       10413 :         bool ok;
     753      691612 :         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
     754             : 
     755      691612 :         SMB_ASSERT(!(ucf_flags & UCF_DFS_PATHNAME));
     756             : 
     757      691612 :         if (is_fake_file_path(name_in)) {
     758          21 :                 smb_fname = synthetic_smb_fname_split(mem_ctx, name_in, posix);
     759          21 :                 if (smb_fname == NULL) {
     760           0 :                         return NT_STATUS_NO_MEMORY;
     761             :                 }
     762          21 :                 smb_fname->st = (SMB_STRUCT_STAT){
     763             :                         .st_ex_nlink = 1,
     764             :                         .st_ex_mode = S_IFREG | 0644,
     765             :                 };
     766          21 :                 smb_fname->st.st_ex_btime =
     767             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     768          21 :                 smb_fname->st.st_ex_atime =
     769             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     770          21 :                 smb_fname->st.st_ex_mtime =
     771             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     772          21 :                 smb_fname->st.st_ex_ctime =
     773             :                         (struct timespec){0, SAMBA_UTIME_OMIT};
     774             : 
     775          21 :                 *_dirfsp = conn->cwd_fsp;
     776          21 :                 *_smb_fname = smb_fname;
     777          21 :                 return NT_STATUS_OK;
     778             :         }
     779             : 
     780             :         /*
     781             :          * Catch an invalid path of "." before we
     782             :          * call filename_split_lcomp(). We need to
     783             :          * do this as filename_split_lcomp() will
     784             :          * use "." for the missing relative component
     785             :          * when an empty name_in path is sent by
     786             :          * the client.
     787             :          */
     788      691591 :         if (ISDOT(name_in)) {
     789          26 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     790          26 :                 goto fail;
     791             :         }
     792             : 
     793      691565 :         ok = filename_split_lcomp(
     794             :                 talloc_tos(),
     795             :                 name_in,
     796             :                 posix,
     797             :                 &dirname,
     798             :                 &fname_rel,
     799             :                 &streamname);
     800      691565 :         if (!ok) {
     801           0 :                 status = NT_STATUS_NO_MEMORY;
     802           0 :                 goto fail;
     803             :         }
     804             : 
     805      691565 :         if ((streamname != NULL) &&
     806        6432 :             ((conn->fs_capabilities & FILE_NAMED_STREAMS) == 0)) {
     807           8 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     808           8 :                 goto fail;
     809             :         }
     810             : 
     811      691557 :         if (!posix) {
     812      687066 :                 bool name_has_wild = ms_has_wild(dirname);
     813      687066 :                 name_has_wild |= ms_has_wild(fname_rel);
     814      687066 :                 if (name_has_wild) {
     815        4456 :                         status = NT_STATUS_OBJECT_NAME_INVALID;
     816        4456 :                         goto fail;
     817             :                 }
     818             :         }
     819             : 
     820      687101 :         if (dirname[0] == '\0') {
     821      117635 :                 status = synthetic_pathref(
     822             :                         mem_ctx,
     823             :                         conn->cwd_fsp,
     824             :                         ".",
     825             :                         NULL,
     826             :                         NULL,
     827             :                         0,
     828             :                         posix ? SMB_FILENAME_POSIX_PATH : 0,
     829             :                         &smb_dirname);
     830             :         } else {
     831      570964 :                 status = normalize_filename_case(conn, dirname, ucf_flags);
     832      570964 :                 if (!NT_STATUS_IS_OK(status)) {
     833           0 :                         DBG_ERR("normalize_filename_case %s failed: %s\n",
     834             :                                 dirname,
     835             :                                 nt_errstr(status));
     836           0 :                         goto fail;
     837             :                 }
     838             : 
     839      570964 :                 status = openat_pathref_fsp_nosymlink(mem_ctx,
     840             :                                                       conn,
     841             :                                                       conn->cwd_fsp,
     842             :                                                       dirname,
     843             :                                                       twrp,
     844             :                                                       posix,
     845             :                                                       &smb_dirname,
     846             :                                                       &symlink_err);
     847             : 
     848      570964 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
     849           0 :                         size_t name_in_len, dirname_len;
     850             : 
     851       62094 :                         name_in_len = strlen(name_in);
     852       62094 :                         dirname_len = strlen(dirname);
     853             : 
     854       62094 :                         SMB_ASSERT(name_in_len >= dirname_len);
     855             : 
     856       62094 :                         symlink_err->unparsed += (name_in_len - dirname_len);
     857       62094 :                         *_symlink_err = symlink_err;
     858             : 
     859       62094 :                         goto fail;
     860             :                 }
     861             :         }
     862             : 
     863      625007 :         if (!NT_STATUS_IS_OK(status)) {
     864        1028 :                 DBG_DEBUG("opening directory %s failed: %s\n",
     865             :                           dirname,
     866             :                           nt_errstr(status));
     867        1028 :                 TALLOC_FREE(dirname);
     868             : 
     869        1028 :                 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
     870             :                         /*
     871             :                          * Except ACCESS_DENIED, everything else leads
     872             :                          * to PATH_NOT_FOUND.
     873             :                          */
     874        1016 :                         status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
     875             :                 }
     876             : 
     877        1028 :                 goto fail;
     878             :         }
     879             : 
     880      623979 :         if (!VALID_STAT_OF_DIR(smb_dirname->st)) {
     881          20 :                 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
     882          20 :                 goto fail;
     883             :         }
     884      623959 :         smb_dirname->fsp->fsp_flags.is_directory = true;
     885             : 
     886             :         /*
     887             :          * Only look at bad last component values
     888             :          * once we know we have a valid directory. That
     889             :          * way we won't confuse error messages from
     890             :          * opening the directory path with error
     891             :          * messages from a bad last component.
     892             :          */
     893             : 
     894             :         /* Relative filename can't be empty */
     895      623959 :         if (fname_rel[0] == '\0') {
     896           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     897           0 :                 goto fail;
     898             :         }
     899             : 
     900             :         /* Relative filename can't be ".." */
     901      623959 :         if (ISDOTDOT(fname_rel)) {
     902           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     903           0 :                 goto fail;
     904             :         }
     905             :         /* Relative name can only be dot if directory is empty. */
     906      623959 :         if (ISDOT(fname_rel) && dirname[0] != '\0') {
     907           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     908           0 :                 goto fail;
     909             :         }
     910             : 
     911      623959 :         TALLOC_FREE(dirname);
     912             : 
     913      634363 :         smb_fname_rel = synthetic_smb_fname(
     914             :                 mem_ctx,
     915             :                 fname_rel,
     916             :                 streamname,
     917             :                 NULL,
     918             :                 twrp,
     919             :                 posix ? SMB_FILENAME_POSIX_PATH : 0);
     920      623959 :         if (smb_fname_rel == NULL) {
     921           0 :                 status = NT_STATUS_NO_MEMORY;
     922           0 :                 goto fail;
     923             :         }
     924             : 
     925     1185393 :         if ((conn->fs_capabilities & FILE_NAMED_STREAMS) &&
     926      561434 :             is_named_stream(smb_fname_rel)) {
     927             :                 /*
     928             :                  * Find the base_fsp first without the stream.
     929             :                  */
     930        6380 :                 saved_streamname = smb_fname_rel->stream_name;
     931        6380 :                 smb_fname_rel->stream_name = NULL;
     932             :         }
     933             : 
     934      623959 :         status = normalize_filename_case(
     935             :                 conn, smb_fname_rel->base_name, ucf_flags);
     936      623959 :         if (!NT_STATUS_IS_OK(status)) {
     937           0 :                 DBG_ERR("normalize_filename_case %s failed: %s\n",
     938             :                         smb_fname_rel->base_name,
     939             :                         nt_errstr(status));
     940           0 :                 goto fail;
     941             :         }
     942             : 
     943      623959 :         status = openat_pathref_fsp_lcomp(smb_dirname->fsp,
     944             :                                           smb_fname_rel,
     945             :                                           ucf_flags);
     946             : 
     947      623959 :         if (NT_STATUS_IS_OK(status) && S_ISLNK(smb_fname_rel->st.st_ex_mode)) {
     948             : 
     949             :                 /*
     950             :                  * Upper layers might need the link target. Here we
     951             :                  * still have the relname around, get the symlink err.
     952             :                  */
     953        9488 :                 status = create_open_symlink_err(mem_ctx,
     954        9488 :                                                  smb_dirname->fsp,
     955             :                                                  smb_fname_rel,
     956             :                                                  &symlink_err);
     957        9488 :                 if (!NT_STATUS_IS_OK(status)) {
     958           0 :                         DBG_DEBUG("Could not read symlink for %s: %s\n",
     959             :                                   smb_fname_str_dbg(
     960             :                                           smb_fname_rel->fsp->fsp_name),
     961             :                                   nt_errstr(status));
     962           0 :                         goto fail;
     963             :                 }
     964             :         }
     965             : 
     966      623959 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
     967      268609 :             !VALID_STAT(smb_fname_rel->st)) {
     968             : 
     969      268609 :                 char *normalized = NULL;
     970             : 
     971             :                 /*
     972             :                  * Creating a new file
     973             :                  */
     974             : 
     975      268609 :                 status = filename_convert_normalize_new(
     976             :                         smb_fname_rel,
     977             :                         conn,
     978             :                         smb_fname_rel->base_name,
     979             :                         &normalized);
     980      268609 :                 if (!NT_STATUS_IS_OK(status)) {
     981           0 :                         DBG_DEBUG("filename_convert_normalize_new failed: "
     982             :                                   "%s\n",
     983             :                                   nt_errstr(status));
     984           0 :                         goto fail;
     985             :                 }
     986      268609 :                 if (normalized != NULL) {
     987          34 :                         smb_fname_rel->base_name = normalized;
     988             :                 }
     989             : 
     990      268609 :                 smb_fname_rel->stream_name = saved_streamname;
     991             : 
     992      269485 :                 smb_fname = full_path_from_dirfsp_atname(
     993      268609 :                         mem_ctx, smb_dirname->fsp, smb_fname_rel);
     994      268609 :                 if (smb_fname == NULL) {
     995           0 :                         status = NT_STATUS_NO_MEMORY;
     996           0 :                         goto fail;
     997             :                 }
     998      268609 :                 goto done;
     999             :         }
    1000             : 
    1001      355350 :         if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_OPEN_RESTRICTION)) {
    1002             :                 /* A vetoed file, pretend it's not there  */
    1003          14 :                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
    1004             :         }
    1005      355350 :         if (!NT_STATUS_IS_OK(status)) {
    1006          24 :                 goto fail;
    1007             :         }
    1008             : 
    1009      355326 :         if (saved_streamname == NULL) {
    1010             :                 /* smb_fname must be allocated off mem_ctx. */
    1011      358685 :                 smb_fname = cp_smb_filename(mem_ctx,
    1012      349158 :                                             smb_fname_rel->fsp->fsp_name);
    1013      349158 :                 if (smb_fname == NULL) {
    1014           0 :                         goto fail;
    1015             :                 }
    1016      349158 :                 status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel);
    1017      349158 :                 if (!NT_STATUS_IS_OK(status)) {
    1018           0 :                         goto fail;
    1019             :                 }
    1020      349158 :                 goto done;
    1021             :         }
    1022             : 
    1023        6168 :         base_fsp = smb_fname_rel->fsp;
    1024        6168 :         smb_fname_fsp_unlink(smb_fname_rel);
    1025        6168 :         SET_STAT_INVALID(smb_fname_rel->st);
    1026             : 
    1027        6168 :         smb_fname_rel->stream_name = saved_streamname;
    1028             : 
    1029        6168 :         status = open_stream_pathref_fsp(&base_fsp, smb_fname_rel);
    1030             : 
    1031        6168 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
    1032        3821 :             !conn->case_sensitive) {
    1033        3821 :                 char *found = NULL;
    1034             : 
    1035        3822 :                 status = get_real_stream_name(
    1036             :                         smb_fname_rel,
    1037             :                         base_fsp,
    1038        3821 :                         smb_fname_rel->stream_name,
    1039             :                         &found);
    1040             : 
    1041        3821 :                 if (NT_STATUS_IS_OK(status)) {
    1042          60 :                         smb_fname_rel->stream_name = found;
    1043          60 :                         found = NULL;
    1044          60 :                         status = open_stream_pathref_fsp(
    1045             :                                 &base_fsp, smb_fname_rel);
    1046             :                 }
    1047             :         }
    1048             : 
    1049        6168 :         if (NT_STATUS_IS_OK(status)) {
    1050             :                 /* smb_fname must be allocated off mem_ctx. */
    1051        2359 :                 smb_fname = cp_smb_filename(mem_ctx,
    1052        2359 :                                             smb_fname_rel->fsp->fsp_name);
    1053        2359 :                 if (smb_fname == NULL) {
    1054           0 :                         goto fail;
    1055             :                 }
    1056        2359 :                 status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel);
    1057        2359 :                 if (!NT_STATUS_IS_OK(status)) {
    1058           0 :                         goto fail;
    1059             :                 }
    1060        2359 :                 goto done;
    1061             :         }
    1062             : 
    1063        3809 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
    1064             :                 /*
    1065             :                  * Creating a new stream
    1066             :                  *
    1067             :                  * We should save the already-open base fsp for
    1068             :                  * create_file_unixpath() somehow.
    1069             :                  */
    1070        3762 :                 smb_fname = full_path_from_dirfsp_atname(
    1071        3761 :                         mem_ctx, smb_dirname->fsp, smb_fname_rel);
    1072        3761 :                 if (smb_fname == NULL) {
    1073           0 :                         status = NT_STATUS_NO_MEMORY;
    1074           0 :                         goto fail;
    1075             :                 }
    1076             :                 /*
    1077             :                  * When open_stream_pathref_fsp() returns
    1078             :                  * NT_STATUS_OBJECT_NAME_NOT_FOUND, smb_fname_rel->fsp
    1079             :                  * has been set to NULL, so we must free base_fsp separately
    1080             :                  * to prevent fd-leaks when opening a stream that doesn't
    1081             :                  * exist.
    1082             :                  */
    1083        3761 :                 fd_close(base_fsp);
    1084        3761 :                 file_free(NULL, base_fsp);
    1085        3761 :                 base_fsp = NULL;
    1086        3761 :                 goto done;
    1087             :         }
    1088             : 
    1089          48 :         if (!NT_STATUS_IS_OK(status)) {
    1090          48 :                 goto fail;
    1091             :         }
    1092             : 
    1093       10404 : done:
    1094      623887 :         *_dirfsp = smb_dirname->fsp;
    1095      623887 :         *_smb_fname = smb_fname;
    1096      623887 :         *_symlink_err = symlink_err;
    1097             : 
    1098      623887 :         smb_fname_fsp_unlink(smb_fname_rel);
    1099      623887 :         TALLOC_FREE(smb_fname_rel);
    1100      623887 :         return NT_STATUS_OK;
    1101             : 
    1102       67704 : fail:
    1103             :         /*
    1104             :          * If open_stream_pathref_fsp() returns an error, smb_fname_rel->fsp
    1105             :          * has been set to NULL, so we must free base_fsp separately
    1106             :          * to prevent fd-leaks when opening a stream that doesn't
    1107             :          * exist.
    1108             :          */
    1109       67704 :         if (base_fsp != NULL) {
    1110          48 :                 fd_close(base_fsp);
    1111          48 :                 file_free(NULL, base_fsp);
    1112          48 :                 base_fsp = NULL;
    1113             :         }
    1114       67704 :         TALLOC_FREE(dirname);
    1115       67704 :         TALLOC_FREE(smb_dirname);
    1116       67704 :         TALLOC_FREE(smb_fname_rel);
    1117       67704 :         return status;
    1118             : }
    1119             : 
    1120      673062 : NTSTATUS filename_convert_dirfsp(
    1121             :         TALLOC_CTX *mem_ctx,
    1122             :         connection_struct *conn,
    1123             :         const char *name_in,
    1124             :         uint32_t ucf_flags,
    1125             :         NTTIME twrp,
    1126             :         struct files_struct **_dirfsp,
    1127             :         struct smb_filename **_smb_fname)
    1128             : {
    1129      673062 :         struct open_symlink_err *symlink_err = NULL;
    1130       10413 :         NTSTATUS status;
    1131      673062 :         char *target = NULL;
    1132      673062 :         char *safe_target = NULL;
    1133      673062 :         size_t symlink_redirects = 0;
    1134             : 
    1135      691632 : next:
    1136      691632 :         if (symlink_redirects > 40) {
    1137          20 :                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
    1138             :         }
    1139             : 
    1140      691612 :         status = filename_convert_dirfsp_nosymlink(mem_ctx,
    1141             :                                                    conn,
    1142             :                                                    name_in,
    1143             :                                                    ucf_flags,
    1144             :                                                    twrp,
    1145             :                                                    _dirfsp,
    1146             :                                                    _smb_fname,
    1147             :                                                    &symlink_err);
    1148             : 
    1149      691612 :         if (NT_STATUS_IS_OK(status) && S_ISLNK((*_smb_fname)->st.st_ex_mode)) {
    1150             :                 /*
    1151             :                  * lcomp is a symlink
    1152             :                  */
    1153        9488 :                 if (ucf_flags & UCF_LCOMP_LNK_OK) {
    1154         740 :                         TALLOC_FREE(symlink_err);
    1155         740 :                         return NT_STATUS_OK;
    1156             :                 }
    1157        8748 :                 close_file_free(NULL, _dirfsp, ERROR_CLOSE);
    1158        8748 :                 status = NT_STATUS_STOPPED_ON_SYMLINK;
    1159             :         }
    1160             : 
    1161      690872 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
    1162      620030 :                 return status;
    1163             :         }
    1164             : 
    1165             :         /*
    1166             :          * If we're on an MSDFS share, see if this is
    1167             :          * an MSDFS link.
    1168             :          */
    1169      121208 :         if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&
    1170       50366 :             strnequal(symlink_err->reparse->substitute_name, "msdfs:", 6))
    1171             :         {
    1172       50366 :                 TALLOC_FREE(*_smb_fname);
    1173       50366 :                 TALLOC_FREE(symlink_err);
    1174       50366 :                 return NT_STATUS_PATH_NOT_COVERED;
    1175             :         }
    1176             : 
    1177       20476 :         if (!lp_follow_symlinks(SNUM(conn))) {
    1178          28 :                 status = (symlink_err->unparsed == 0)
    1179             :                                  ? NT_STATUS_OBJECT_NAME_NOT_FOUND
    1180           2 :                                  : NT_STATUS_OBJECT_PATH_NOT_FOUND;
    1181          28 :                 TALLOC_FREE(symlink_err);
    1182          28 :                 return status;
    1183             :         }
    1184             : 
    1185             :         /*
    1186             :          * Right now, SMB2 and SMB1 always traverse symlinks
    1187             :          * within the share. SMB1+POSIX traverses non-terminal
    1188             :          * symlinks within the share.
    1189             :          *
    1190             :          * When we add SMB2+POSIX we need to return
    1191             :          * a NT_STATUS_STOPPED_ON_SYMLINK error here, using the
    1192             :          * symlink target data read below if SMB2+POSIX has
    1193             :          * UCF_POSIX_PATHNAMES set to cause the client to
    1194             :          * resolve all symlinks locally.
    1195             :          */
    1196             : 
    1197       20448 :         target = symlink_target_path(mem_ctx,
    1198             :                                      name_in,
    1199       20448 :                                      symlink_err->reparse->substitute_name,
    1200       20448 :                                      symlink_err->unparsed);
    1201       20448 :         if (target == NULL) {
    1202           0 :                 return NT_STATUS_NO_MEMORY;
    1203             :         }
    1204             : 
    1205       20448 :         status = safe_symlink_target_path(mem_ctx,
    1206       20448 :                                           conn->connectpath,
    1207             :                                           NULL,
    1208             :                                           target,
    1209       20448 :                                           symlink_err->unparsed,
    1210             :                                           &safe_target);
    1211       20448 :         TALLOC_FREE(symlink_err);
    1212       20448 :         if (!NT_STATUS_IS_OK(status)) {
    1213        1878 :                 return status;
    1214             :         }
    1215       18570 :         name_in = safe_target;
    1216             : 
    1217       18570 :         symlink_redirects += 1;
    1218             : 
    1219       18570 :         goto next;
    1220             : }
    1221             : 
    1222     5937675 : char *full_path_from_dirfsp_at_basename(TALLOC_CTX *mem_ctx,
    1223             :                                         const struct files_struct *dirfsp,
    1224             :                                         const char *at_base_name)
    1225             : {
    1226     5937675 :         char *path = NULL;
    1227             : 
    1228     5937675 :         if (dirfsp == dirfsp->conn->cwd_fsp ||
    1229     2465705 :             ISDOT(dirfsp->fsp_name->base_name) || at_base_name[0] == '/') {
    1230     3707402 :                 path = talloc_strdup(mem_ctx, at_base_name);
    1231             :         } else {
    1232     2230273 :                 path = talloc_asprintf(mem_ctx,
    1233             :                                        "%s/%s",
    1234     2220698 :                                        dirfsp->fsp_name->base_name,
    1235             :                                        at_base_name);
    1236             :         }
    1237             : 
    1238     5937675 :         return path;
    1239             : }
    1240             : 
    1241             : /*
    1242             :  * Build the full path from a dirfsp and dirfsp relative name
    1243             :  */
    1244             : struct smb_filename *
    1245     5935538 : full_path_from_dirfsp_atname(TALLOC_CTX *mem_ctx,
    1246             :                              const struct files_struct *dirfsp,
    1247             :                              const struct smb_filename *atname)
    1248             : {
    1249     5935538 :         struct smb_filename *fname = NULL;
    1250     5935538 :         char *path = NULL;
    1251             : 
    1252     5958329 :         path = full_path_from_dirfsp_at_basename(mem_ctx,
    1253             :                                                  dirfsp,
    1254     5935538 :                                                  atname->base_name);
    1255     5935538 :         if (path == NULL) {
    1256           0 :                 return NULL;
    1257             :         }
    1258             : 
    1259     5958329 :         fname = synthetic_smb_fname(mem_ctx,
    1260             :                                     path,
    1261     5935538 :                                     atname->stream_name,
    1262             :                                     &atname->st,
    1263     5935538 :                                     atname->twrp,
    1264     5935538 :                                     atname->flags);
    1265     5935538 :         TALLOC_FREE(path);
    1266     5935538 :         if (fname == NULL) {
    1267           0 :                 return NULL;
    1268             :         }
    1269             : 
    1270     5912747 :         return fname;
    1271             : }

Generated by: LCOV version 1.14