LCOV - code coverage report
Current view: top level - source3/smbd - filename.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 479 558 85.8 %
Date: 2024-04-21 15:09:00 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      622872 : uint32_t ucf_flags_from_smb_request(struct smb_request *req)
      36             : {
      37      622872 :         uint32_t ucf_flags = 0;
      38             : 
      39      622872 :         if (req == NULL) {
      40           0 :                 return 0;
      41             :         }
      42             : 
      43      622872 :         if (req->posix_pathnames) {
      44        4429 :                 ucf_flags |= UCF_POSIX_PATHNAMES;
      45             : 
      46        4429 :                 if (!conn_using_smb2(req->sconn)) {
      47        2285 :                         ucf_flags |= UCF_LCOMP_LNK_OK;
      48             :                 }
      49             :         }
      50      622872 :         if (req->flags2 & FLAGS2_DFS_PATHNAMES) {
      51        1624 :                 ucf_flags |= UCF_DFS_PATHNAME;
      52             :         }
      53      622872 :         if (req->flags2 & FLAGS2_REPARSE_PATH) {
      54        4178 :                 ucf_flags |= UCF_GMT_PATHNAME;
      55             :         }
      56             : 
      57      612395 :         return ucf_flags;
      58             : }
      59             : 
      60      515882 : uint32_t filename_create_ucf_flags(struct smb_request *req, uint32_t create_disposition)
      61             : {
      62      515882 :         uint32_t ucf_flags = 0;
      63             : 
      64      515882 :         ucf_flags |= ucf_flags_from_smb_request(req);
      65             : 
      66      515882 :         switch (create_disposition) {
      67      333223 :         case FILE_OPEN:
      68             :         case FILE_OVERWRITE:
      69      333223 :                 break;
      70      182027 :         case FILE_SUPERSEDE:
      71             :         case FILE_CREATE:
      72             :         case FILE_OPEN_IF:
      73             :         case FILE_OVERWRITE_IF:
      74      182027 :                 ucf_flags |= UCF_PREP_CREATEFILE;
      75      182027 :                 break;
      76             :         }
      77             : 
      78      515882 :         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     1227182 : static NTSTATUS normalize_filename_case(connection_struct *conn,
     152             :                                         char *filename,
     153             :                                         uint32_t ucf_flags)
     154             : {
     155       23613 :         bool ok;
     156             : 
     157     1227182 :         if (ucf_flags & UCF_POSIX_PATHNAMES) {
     158             :                 /*
     159             :                  * POSIX never normalizes filename case.
     160             :                  */
     161        4955 :                 return NT_STATUS_OK;
     162             :         }
     163     1222227 :         if (!conn->case_sensitive) {
     164     1219885 :                 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   158832391 : static bool fname_equal(const char *name1, const char *name2,
     185             :                 bool case_sensitive)
     186             : {
     187             :         /* Normal filename handling */
     188   158832391 :         if (case_sensitive) {
     189           0 :                 return(strcmp(name1,name2) == 0);
     190             :         }
     191             : 
     192   158832391 :         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      273549 : 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      273549 :         struct connection_struct *conn = dirfsp->conn;
     277      273549 :         struct smb_Dir *cur_dir = NULL;
     278      273549 :         const char *dname = NULL;
     279      273549 :         char *talloced = NULL;
     280      273549 :         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      273549 :         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      273549 :         if (mangled && !conn->case_sensitive) {
     307         166 :                 mangled = !mangle_lookup_name_from_8_3(talloc_tos(), name,
     308             :                                                        &unmangled_name,
     309         166 :                                                        conn->params);
     310         166 :                 if (!mangled) {
     311             :                         /* Name is now unmangled. */
     312         142 :                         name = unmangled_name;
     313             :                 }
     314             :         }
     315             : 
     316             :         /* open the directory */
     317      273549 :         status = OpenDir_from_pathref(talloc_tos(), dirfsp, NULL, 0, &cur_dir);
     318      273549 :         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   159642877 :         while ((dname = ReadDirName(cur_dir, &talloced))) {
     328             : 
     329             :                 /* Is it dot or dot dot. */
     330   159375549 :                 if (ISDOT(dname) || ISDOTDOT(dname)) {
     331      547096 :                         TALLOC_FREE(talloced);
     332      547096 :                         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   317656882 :                 if ((mangled && mangled_equal(name,dname,conn->params)) ||
     347   158828429 :                         fname_equal(name, dname, conn->case_sensitive)) {
     348             :                         /* we've found the file, change it's name and return */
     349        6220 :                         *found_name = talloc_strdup(mem_ctx, dname);
     350        6220 :                         TALLOC_FREE(unmangled_name);
     351        6220 :                         TALLOC_FREE(cur_dir);
     352        6220 :                         if (!*found_name) {
     353           0 :                                 TALLOC_FREE(talloced);
     354           0 :                                 return NT_STATUS_NO_MEMORY;
     355             :                         }
     356        6220 :                         TALLOC_FREE(talloced);
     357        6220 :                         return NT_STATUS_OK;
     358             :                 }
     359   158828902 :                 TALLOC_FREE(talloced);
     360             :         }
     361             : 
     362      267328 :         TALLOC_FREE(unmangled_name);
     363      267328 :         TALLOC_FREE(cur_dir);
     364      267328 :         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      273549 : NTSTATUS get_real_filename_at(struct files_struct *dirfsp,
     373             :                               const char *name,
     374             :                               TALLOC_CTX *mem_ctx,
     375             :                               char **found_name)
     376             : {
     377      273549 :         struct connection_struct *conn = dirfsp->conn;
     378         883 :         NTSTATUS status;
     379         883 :         bool mangled;
     380             : 
     381      273549 :         mangled = mangle_is_mangled(name, conn->params);
     382             : 
     383      273549 :         if (mangled) {
     384         166 :                 status = get_real_filename_full_scan_at(
     385             :                         dirfsp, name, mangled, mem_ctx, found_name);
     386         166 :                 return status;
     387             :         }
     388             : 
     389             :         /* Try the vfs first to take advantage of case-insensitive stat. */
     390      273383 :         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      273383 :         if (NT_STATUS_IS_OK(status) ||
     399      272500 :             !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
     400           0 :                 return status;
     401             :         }
     402             : 
     403      273383 :         status = get_real_filename_full_scan_at(
     404             :                 dirfsp, name, mangled, mem_ctx, found_name);
     405      273383 :         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      691333 : 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      691333 :         const char *lcomp = NULL;
     487      691333 :         const char *fname_rel = NULL;
     488      691333 :         const char *streamname = NULL;
     489      691333 :         char *dirname = NULL;
     490             : 
     491      691333 :         if (name_in[0] == '\0') {
     492       37267 :                 fname_rel = ".";
     493       37267 :                 dirname = talloc_strdup(mem_ctx, "");
     494       37267 :                 if (dirname == NULL) {
     495           0 :                         return false;
     496             :                 }
     497       37267 :                 goto done;
     498             :         }
     499             : 
     500      654066 :         lcomp = strrchr_m(name_in, '/');
     501      654066 :         if (lcomp != NULL) {
     502      575395 :                 fname_rel = lcomp+1;
     503      575395 :                 dirname = talloc_strndup(mem_ctx, name_in, lcomp - name_in);
     504      575395 :                 if (dirname == NULL) {
     505           0 :                         return false;
     506             :                 }
     507      575395 :                 goto find_stream;
     508             :         }
     509             : 
     510             :         /*
     511             :          * No slash, dir is empty
     512             :          */
     513       78671 :         dirname = talloc_strdup(mem_ctx, "");
     514       78671 :         if (dirname == NULL) {
     515           0 :                 return false;
     516             :         }
     517             : 
     518       78671 :         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       77244 :         fname_rel = name_in;
     528             : 
     529      654034 : find_stream:
     530      654034 :         if (!posix) {
     531      649651 :                 streamname = strchr_m(fname_rel, ':');
     532             : 
     533      649651 :                 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      654034 : done:
     546      691333 :         *_dirname = dirname;
     547      691333 :         *_fname_rel = fname_rel;
     548      691333 :         *_streamname = streamname;
     549      691333 :         return true;
     550             : }
     551             : 
     552             : /*
     553             :  * Create the correct capitalization of a file name to be created.
     554             :  */
     555      268607 : 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      268607 :         char *name = name_in;
     562             : 
     563      268607 :         *_normalized = NULL;
     564             : 
     565      537210 :         if (!conn->case_preserve ||
     566      268603 :             (mangle_is_8_3(name, false,
     567      268603 :                            conn->params) &&
     568      237482 :              !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      268607 :         if (mangle_is_mangled(name, conn->params)) {
     580           0 :                 bool found;
     581          52 :                 char *unmangled = NULL;
     582             : 
     583          52 :                 found = mangle_lookup_name_from_8_3(
     584          52 :                         mem_ctx, name, &unmangled, conn->params);
     585          52 :                 if (found) {
     586          44 :                         name = unmangled;
     587             :                 }
     588             :         }
     589             : 
     590      268607 :         if (name != name_in) {
     591          48 :                 *_normalized = name;
     592             :         }
     593             : 
     594      268607 :         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      691380 : 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 reparse_data_buffer **_symlink_err)
     741             : {
     742      691380 :         struct smb_filename *smb_dirname = NULL;
     743      691380 :         struct smb_filename *smb_fname_rel = NULL;
     744      691380 :         struct smb_filename *smb_fname = NULL;
     745      691380 :         struct reparse_data_buffer *symlink_err = NULL;
     746      691380 :         const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
     747      691380 :         char *dirname = NULL;
     748      691380 :         const char *fname_rel = NULL;
     749      691380 :         const char *streamname = NULL;
     750      691380 :         char *saved_streamname = NULL;
     751      691380 :         struct files_struct *base_fsp = NULL;
     752       10413 :         bool ok;
     753      691380 :         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
     754             : 
     755      691380 :         SMB_ASSERT(!(ucf_flags & UCF_DFS_PATHNAME));
     756             : 
     757      691380 :         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      691359 :         if (ISDOT(name_in)) {
     789          26 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     790          26 :                 goto fail;
     791             :         }
     792             : 
     793      691333 :         ok = filename_split_lcomp(
     794             :                 talloc_tos(),
     795             :                 name_in,
     796             :                 posix,
     797             :                 &dirname,
     798             :                 &fname_rel,
     799             :                 &streamname);
     800      691333 :         if (!ok) {
     801           0 :                 status = NT_STATUS_NO_MEMORY;
     802           0 :                 goto fail;
     803             :         }
     804             : 
     805      691333 :         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      691325 :         if (!posix) {
     812      686834 :                 bool name_has_wild = ms_has_wild(dirname);
     813      686834 :                 name_has_wild |= ms_has_wild(fname_rel);
     814      686834 :                 if (name_has_wild) {
     815        4456 :                         status = NT_STATUS_OBJECT_NAME_INVALID;
     816        4456 :                         goto fail;
     817             :                 }
     818             :         }
     819             : 
     820      686869 :         if (dirname[0] == '\0') {
     821      117404 :                 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      570963 :                 status = normalize_filename_case(conn, dirname, ucf_flags);
     832      570963 :                 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      570963 :                 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      570963 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
     849           0 :                         struct symlink_reparse_struct
     850       62094 :                                 *lnk = &symlink_err->parsed.lnk;
     851       62094 :                         size_t unparsed = lnk->unparsed_path_length;
     852           0 :                         size_t name_in_len, dirname_len;
     853             : 
     854       62094 :                         name_in_len = strlen(name_in);
     855       62094 :                         dirname_len = strlen(dirname);
     856             : 
     857       62094 :                         SMB_ASSERT(name_in_len >= dirname_len);
     858             : 
     859       62094 :                         unparsed += (name_in_len - dirname_len);
     860             : 
     861       62094 :                         if (unparsed > UINT16_MAX) {
     862           0 :                                 status = NT_STATUS_BUFFER_OVERFLOW;
     863           0 :                                 goto fail;
     864             :                         }
     865             : 
     866       62094 :                         lnk->unparsed_path_length = unparsed;
     867       62094 :                         *_symlink_err = symlink_err;
     868             : 
     869       62094 :                         goto fail;
     870             :                 }
     871             :         }
     872             : 
     873      624775 :         if (!NT_STATUS_IS_OK(status)) {
     874        1028 :                 DBG_DEBUG("opening directory %s failed: %s\n",
     875             :                           dirname,
     876             :                           nt_errstr(status));
     877        1028 :                 TALLOC_FREE(dirname);
     878             : 
     879        1028 :                 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
     880             :                         /*
     881             :                          * Except ACCESS_DENIED, everything else leads
     882             :                          * to PATH_NOT_FOUND.
     883             :                          */
     884        1016 :                         status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
     885             :                 }
     886             : 
     887        1028 :                 goto fail;
     888             :         }
     889             : 
     890      623747 :         if (!VALID_STAT_OF_DIR(smb_dirname->st)) {
     891          20 :                 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
     892          20 :                 goto fail;
     893             :         }
     894      623727 :         smb_dirname->fsp->fsp_flags.is_directory = true;
     895             : 
     896             :         /*
     897             :          * Only look at bad last component values
     898             :          * once we know we have a valid directory. That
     899             :          * way we won't confuse error messages from
     900             :          * opening the directory path with error
     901             :          * messages from a bad last component.
     902             :          */
     903             : 
     904             :         /* Relative filename can't be empty */
     905      623727 :         if (fname_rel[0] == '\0') {
     906           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     907           0 :                 goto fail;
     908             :         }
     909             : 
     910             :         /* Relative filename can't be ".." */
     911      623727 :         if (ISDOTDOT(fname_rel)) {
     912           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     913           0 :                 goto fail;
     914             :         }
     915             :         /* Relative name can only be dot if directory is empty. */
     916      623727 :         if (ISDOT(fname_rel) && dirname[0] != '\0') {
     917           0 :                 status = NT_STATUS_OBJECT_NAME_INVALID;
     918           0 :                 goto fail;
     919             :         }
     920             : 
     921      623727 :         TALLOC_FREE(dirname);
     922             : 
     923      634131 :         smb_fname_rel = synthetic_smb_fname(
     924             :                 mem_ctx,
     925             :                 fname_rel,
     926             :                 streamname,
     927             :                 NULL,
     928             :                 twrp,
     929             :                 posix ? SMB_FILENAME_POSIX_PATH : 0);
     930      623727 :         if (smb_fname_rel == NULL) {
     931           0 :                 status = NT_STATUS_NO_MEMORY;
     932           0 :                 goto fail;
     933             :         }
     934             : 
     935     1184929 :         if ((conn->fs_capabilities & FILE_NAMED_STREAMS) &&
     936      561202 :             is_named_stream(smb_fname_rel)) {
     937             :                 /*
     938             :                  * Find the base_fsp first without the stream.
     939             :                  */
     940        6380 :                 saved_streamname = smb_fname_rel->stream_name;
     941        6380 :                 smb_fname_rel->stream_name = NULL;
     942             :         }
     943             : 
     944      623727 :         status = normalize_filename_case(
     945             :                 conn, smb_fname_rel->base_name, ucf_flags);
     946      623727 :         if (!NT_STATUS_IS_OK(status)) {
     947           0 :                 DBG_ERR("normalize_filename_case %s failed: %s\n",
     948             :                         smb_fname_rel->base_name,
     949             :                         nt_errstr(status));
     950           0 :                 goto fail;
     951             :         }
     952             : 
     953      623727 :         status = openat_pathref_fsp_lcomp(smb_dirname->fsp,
     954             :                                           smb_fname_rel,
     955             :                                           ucf_flags);
     956             : 
     957      623727 :         if (NT_STATUS_IS_OK(status) && S_ISLNK(smb_fname_rel->st.st_ex_mode)) {
     958             : 
     959             :                 /*
     960             :                  * Upper layers might need the link target. Here we
     961             :                  * still have the relname around, get the symlink err.
     962             :                  */
     963        9488 :                 status = read_symlink_reparse(mem_ctx,
     964        9488 :                                               smb_dirname->fsp,
     965             :                                               smb_fname_rel,
     966             :                                               &symlink_err);
     967        9488 :                 if (!NT_STATUS_IS_OK(status)) {
     968           0 :                         DBG_DEBUG("Could not read symlink for %s: %s\n",
     969             :                                   smb_fname_str_dbg(
     970             :                                           smb_fname_rel->fsp->fsp_name),
     971             :                                   nt_errstr(status));
     972           0 :                         goto fail;
     973             :                 }
     974             :         }
     975             : 
     976      623727 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
     977      268607 :             !VALID_STAT(smb_fname_rel->st)) {
     978             : 
     979      268607 :                 char *normalized = NULL;
     980             : 
     981             :                 /*
     982             :                  * Creating a new file
     983             :                  */
     984             : 
     985      268607 :                 status = filename_convert_normalize_new(
     986             :                         smb_fname_rel,
     987             :                         conn,
     988             :                         smb_fname_rel->base_name,
     989             :                         &normalized);
     990      268607 :                 if (!NT_STATUS_IS_OK(status)) {
     991           0 :                         DBG_DEBUG("filename_convert_normalize_new failed: "
     992             :                                   "%s\n",
     993             :                                   nt_errstr(status));
     994           0 :                         goto fail;
     995             :                 }
     996      268607 :                 if (normalized != NULL) {
     997          48 :                         smb_fname_rel->base_name = normalized;
     998             :                 }
     999             : 
    1000      268607 :                 smb_fname_rel->stream_name = saved_streamname;
    1001             : 
    1002      269483 :                 smb_fname = full_path_from_dirfsp_atname(
    1003      268607 :                         mem_ctx, smb_dirname->fsp, smb_fname_rel);
    1004      268607 :                 if (smb_fname == NULL) {
    1005           0 :                         status = NT_STATUS_NO_MEMORY;
    1006           0 :                         goto fail;
    1007             :                 }
    1008      268607 :                 goto done;
    1009             :         }
    1010             : 
    1011      355120 :         if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_OPEN_RESTRICTION)) {
    1012             :                 /* A vetoed file, pretend it's not there  */
    1013          14 :                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
    1014             :         }
    1015      355120 :         if (!NT_STATUS_IS_OK(status)) {
    1016          24 :                 goto fail;
    1017             :         }
    1018             : 
    1019      355096 :         if (saved_streamname == NULL) {
    1020             :                 /* smb_fname must be allocated off mem_ctx. */
    1021      358455 :                 smb_fname = cp_smb_filename(mem_ctx,
    1022      348928 :                                             smb_fname_rel->fsp->fsp_name);
    1023      348928 :                 if (smb_fname == NULL) {
    1024           0 :                         goto fail;
    1025             :                 }
    1026      348928 :                 status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel);
    1027      348928 :                 if (!NT_STATUS_IS_OK(status)) {
    1028           0 :                         goto fail;
    1029             :                 }
    1030      348928 :                 goto done;
    1031             :         }
    1032             : 
    1033        6168 :         base_fsp = smb_fname_rel->fsp;
    1034        6168 :         smb_fname_fsp_unlink(smb_fname_rel);
    1035        6168 :         SET_STAT_INVALID(smb_fname_rel->st);
    1036             : 
    1037        6168 :         smb_fname_rel->stream_name = saved_streamname;
    1038             : 
    1039        6168 :         status = open_stream_pathref_fsp(&base_fsp, smb_fname_rel);
    1040             : 
    1041        6168 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
    1042        3821 :             !conn->case_sensitive) {
    1043        3821 :                 char *found = NULL;
    1044             : 
    1045        3822 :                 status = get_real_stream_name(
    1046             :                         smb_fname_rel,
    1047             :                         base_fsp,
    1048        3821 :                         smb_fname_rel->stream_name,
    1049             :                         &found);
    1050             : 
    1051        3821 :                 if (NT_STATUS_IS_OK(status)) {
    1052          60 :                         smb_fname_rel->stream_name = found;
    1053          60 :                         found = NULL;
    1054          60 :                         status = open_stream_pathref_fsp(
    1055             :                                 &base_fsp, smb_fname_rel);
    1056             :                 }
    1057             :         }
    1058             : 
    1059        6168 :         if (NT_STATUS_IS_OK(status)) {
    1060             :                 /* smb_fname must be allocated off mem_ctx. */
    1061        2359 :                 smb_fname = cp_smb_filename(mem_ctx,
    1062        2359 :                                             smb_fname_rel->fsp->fsp_name);
    1063        2359 :                 if (smb_fname == NULL) {
    1064           0 :                         goto fail;
    1065             :                 }
    1066        2359 :                 status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel);
    1067        2359 :                 if (!NT_STATUS_IS_OK(status)) {
    1068           0 :                         goto fail;
    1069             :                 }
    1070        2359 :                 goto done;
    1071             :         }
    1072             : 
    1073        3809 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
    1074             :                 /*
    1075             :                  * Creating a new stream
    1076             :                  *
    1077             :                  * We should save the already-open base fsp for
    1078             :                  * create_file_unixpath() somehow.
    1079             :                  */
    1080        3762 :                 smb_fname = full_path_from_dirfsp_atname(
    1081        3761 :                         mem_ctx, smb_dirname->fsp, smb_fname_rel);
    1082        3761 :                 if (smb_fname == NULL) {
    1083           0 :                         status = NT_STATUS_NO_MEMORY;
    1084           0 :                         goto fail;
    1085             :                 }
    1086             :                 /*
    1087             :                  * When open_stream_pathref_fsp() returns
    1088             :                  * NT_STATUS_OBJECT_NAME_NOT_FOUND, smb_fname_rel->fsp
    1089             :                  * has been set to NULL, so we must free base_fsp separately
    1090             :                  * to prevent fd-leaks when opening a stream that doesn't
    1091             :                  * exist.
    1092             :                  */
    1093        3761 :                 fd_close(base_fsp);
    1094        3761 :                 file_free(NULL, base_fsp);
    1095        3761 :                 base_fsp = NULL;
    1096        3761 :                 goto done;
    1097             :         }
    1098             : 
    1099          48 :         if (!NT_STATUS_IS_OK(status)) {
    1100          48 :                 goto fail;
    1101             :         }
    1102             : 
    1103       10404 : done:
    1104      623655 :         *_dirfsp = smb_dirname->fsp;
    1105      623655 :         *_smb_fname = smb_fname;
    1106      623655 :         *_symlink_err = symlink_err;
    1107             : 
    1108      623655 :         smb_fname_fsp_unlink(smb_fname_rel);
    1109      623655 :         TALLOC_FREE(smb_fname_rel);
    1110      623655 :         return NT_STATUS_OK;
    1111             : 
    1112       67704 : fail:
    1113             :         /*
    1114             :          * If open_stream_pathref_fsp() returns an error, smb_fname_rel->fsp
    1115             :          * has been set to NULL, so we must free base_fsp separately
    1116             :          * to prevent fd-leaks when opening a stream that doesn't
    1117             :          * exist.
    1118             :          */
    1119       67704 :         if (base_fsp != NULL) {
    1120          48 :                 fd_close(base_fsp);
    1121          48 :                 file_free(NULL, base_fsp);
    1122          48 :                 base_fsp = NULL;
    1123             :         }
    1124       67704 :         TALLOC_FREE(dirname);
    1125       67704 :         TALLOC_FREE(smb_dirname);
    1126       67704 :         TALLOC_FREE(smb_fname_rel);
    1127       67704 :         return status;
    1128             : }
    1129             : 
    1130      672830 : NTSTATUS filename_convert_dirfsp(
    1131             :         TALLOC_CTX *mem_ctx,
    1132             :         connection_struct *conn,
    1133             :         const char *name_in,
    1134             :         uint32_t ucf_flags,
    1135             :         NTTIME twrp,
    1136             :         struct files_struct **_dirfsp,
    1137             :         struct smb_filename **_smb_fname)
    1138             : {
    1139      672830 :         struct reparse_data_buffer *symlink_err = NULL;
    1140      672830 :         struct symlink_reparse_struct *lnk = NULL;
    1141       10413 :         NTSTATUS status;
    1142      672830 :         char *target = NULL;
    1143      672830 :         char *safe_target = NULL;
    1144      672830 :         size_t symlink_redirects = 0;
    1145             : 
    1146      691400 : next:
    1147      691400 :         if (symlink_redirects > 40) {
    1148          20 :                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
    1149             :         }
    1150             : 
    1151      691380 :         status = filename_convert_dirfsp_nosymlink(mem_ctx,
    1152             :                                                    conn,
    1153             :                                                    name_in,
    1154             :                                                    ucf_flags,
    1155             :                                                    twrp,
    1156             :                                                    _dirfsp,
    1157             :                                                    _smb_fname,
    1158             :                                                    &symlink_err);
    1159             : 
    1160      691380 :         if (NT_STATUS_IS_OK(status) && S_ISLNK((*_smb_fname)->st.st_ex_mode)) {
    1161             :                 /*
    1162             :                  * lcomp is a symlink
    1163             :                  */
    1164        9488 :                 if (ucf_flags & UCF_LCOMP_LNK_OK) {
    1165         740 :                         TALLOC_FREE(symlink_err);
    1166         740 :                         return NT_STATUS_OK;
    1167             :                 }
    1168        8748 :                 close_file_free(NULL, _dirfsp, ERROR_CLOSE);
    1169        8748 :                 status = NT_STATUS_STOPPED_ON_SYMLINK;
    1170             :         }
    1171             : 
    1172      690640 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
    1173      619798 :                 return status;
    1174             :         }
    1175       70842 :         lnk = &symlink_err->parsed.lnk;
    1176             : 
    1177             :         /*
    1178             :          * If we're on an MSDFS share, see if this is
    1179             :          * an MSDFS link.
    1180             :          */
    1181      121208 :         if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&
    1182       50366 :             strnequal(lnk->substitute_name, "msdfs:", 6))
    1183             :         {
    1184       50366 :                 TALLOC_FREE(*_smb_fname);
    1185       50366 :                 TALLOC_FREE(symlink_err);
    1186       50366 :                 return NT_STATUS_PATH_NOT_COVERED;
    1187             :         }
    1188             : 
    1189       20476 :         if (!lp_follow_symlinks(SNUM(conn))) {
    1190          28 :                 status = (lnk->unparsed_path_length == 0)
    1191             :                                  ? NT_STATUS_OBJECT_NAME_NOT_FOUND
    1192           2 :                                  : NT_STATUS_OBJECT_PATH_NOT_FOUND;
    1193          28 :                 TALLOC_FREE(symlink_err);
    1194          28 :                 return status;
    1195             :         }
    1196             : 
    1197             :         /*
    1198             :          * Right now, SMB2 and SMB1 always traverse symlinks
    1199             :          * within the share. SMB1+POSIX traverses non-terminal
    1200             :          * symlinks within the share.
    1201             :          *
    1202             :          * When we add SMB2+POSIX we need to return
    1203             :          * a NT_STATUS_STOPPED_ON_SYMLINK error here, using the
    1204             :          * symlink target data read below if SMB2+POSIX has
    1205             :          * UCF_POSIX_PATHNAMES set to cause the client to
    1206             :          * resolve all symlinks locally.
    1207             :          */
    1208             : 
    1209       20448 :         target = symlink_target_path(mem_ctx,
    1210             :                                      name_in,
    1211       20448 :                                      lnk->substitute_name,
    1212       20448 :                                      lnk->unparsed_path_length);
    1213       20448 :         if (target == NULL) {
    1214           0 :                 return NT_STATUS_NO_MEMORY;
    1215             :         }
    1216             : 
    1217       20448 :         status = safe_symlink_target_path(mem_ctx,
    1218       20448 :                                           conn->connectpath,
    1219             :                                           NULL,
    1220             :                                           target,
    1221       20448 :                                           lnk->unparsed_path_length,
    1222             :                                           &safe_target);
    1223       20448 :         TALLOC_FREE(symlink_err);
    1224       20448 :         if (!NT_STATUS_IS_OK(status)) {
    1225        1878 :                 return status;
    1226             :         }
    1227       18570 :         name_in = safe_target;
    1228             : 
    1229       18570 :         symlink_redirects += 1;
    1230             : 
    1231       18570 :         goto next;
    1232             : }
    1233             : 
    1234     5937675 : char *full_path_from_dirfsp_at_basename(TALLOC_CTX *mem_ctx,
    1235             :                                         const struct files_struct *dirfsp,
    1236             :                                         const char *at_base_name)
    1237             : {
    1238     5937675 :         char *path = NULL;
    1239             : 
    1240     5937675 :         if (dirfsp == dirfsp->conn->cwd_fsp ||
    1241     2465478 :             ISDOT(dirfsp->fsp_name->base_name) || at_base_name[0] == '/') {
    1242     3707397 :                 path = talloc_strdup(mem_ctx, at_base_name);
    1243             :         } else {
    1244     2230278 :                 path = talloc_asprintf(mem_ctx,
    1245             :                                        "%s/%s",
    1246     2220703 :                                        dirfsp->fsp_name->base_name,
    1247             :                                        at_base_name);
    1248             :         }
    1249             : 
    1250     5937675 :         return path;
    1251             : }
    1252             : 
    1253             : /*
    1254             :  * Build the full path from a dirfsp and dirfsp relative name
    1255             :  */
    1256             : struct smb_filename *
    1257     5935527 : full_path_from_dirfsp_atname(TALLOC_CTX *mem_ctx,
    1258             :                              const struct files_struct *dirfsp,
    1259             :                              const struct smb_filename *atname)
    1260             : {
    1261     5935527 :         struct smb_filename *fname = NULL;
    1262     5935527 :         char *path = NULL;
    1263             : 
    1264     5958318 :         path = full_path_from_dirfsp_at_basename(mem_ctx,
    1265             :                                                  dirfsp,
    1266     5935527 :                                                  atname->base_name);
    1267     5935527 :         if (path == NULL) {
    1268           0 :                 return NULL;
    1269             :         }
    1270             : 
    1271     5958318 :         fname = synthetic_smb_fname(mem_ctx,
    1272             :                                     path,
    1273     5935527 :                                     atname->stream_name,
    1274             :                                     &atname->st,
    1275     5935527 :                                     atname->twrp,
    1276     5935527 :                                     atname->flags);
    1277     5935527 :         TALLOC_FREE(path);
    1278     5935527 :         if (fname == NULL) {
    1279           0 :                 return NULL;
    1280             :         }
    1281             : 
    1282     5912736 :         return fname;
    1283             : }

Generated by: LCOV version 1.14