LCOV - code coverage report
Current view: top level - source3/smbd - dir.c (source / functions) Hit Total Coverage
Test: coverage report for recycleplus df22b230 Lines: 373 861 43.3 %
Date: 2024-02-14 10:14:15 Functions: 21 48 43.8 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Directory handling routines
       4             :    Copyright (C) Andrew Tridgell 1992-1998
       5             :    Copyright (C) Jeremy Allison 2007
       6             : 
       7             :    This program is free software; you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation; either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "includes.h"
      22             : #include "system/filesys.h"
      23             : #include "locking/share_mode_lock.h"
      24             : #include "smbd/smbd.h"
      25             : #include "smbd/globals.h"
      26             : #include "libcli/security/security.h"
      27             : #include "lib/util/bitmap.h"
      28             : #include "../lib/util/memcache.h"
      29             : #include "../librpc/gen_ndr/open_files.h"
      30             : #include "lib/util/string_wrappers.h"
      31             : 
      32             : /*
      33             :    This module implements directory related functions for Samba.
      34             : */
      35             : 
      36             : /* "Special" directory offsets. */
      37             : #define END_OF_DIRECTORY_OFFSET ((long)-1)
      38             : #define START_OF_DIRECTORY_OFFSET ((long)0)
      39             : #define DOT_DOT_DIRECTORY_OFFSET ((long)0x80000000)
      40             : 
      41             : /* "Special" directory offsets in 32-bit wire format. */
      42             : #define WIRE_END_OF_DIRECTORY_OFFSET ((uint32_t)0xFFFFFFFF)
      43             : #define WIRE_START_OF_DIRECTORY_OFFSET ((uint32_t)0)
      44             : #define WIRE_DOT_DOT_DIRECTORY_OFFSET ((uint32_t)0x80000000)
      45             : 
      46             : /* Make directory handle internals available. */
      47             : 
      48             : struct name_cache_entry {
      49             :         char *name;
      50             :         long offset;
      51             : };
      52             : 
      53             : struct smb_Dir {
      54             :         connection_struct *conn;
      55             :         DIR *dir;
      56             :         long offset;
      57             :         struct smb_filename *dir_smb_fname;
      58             :         size_t name_cache_size;
      59             :         struct name_cache_entry *name_cache;
      60             :         unsigned int name_cache_index;
      61             :         unsigned int file_number;
      62             :         bool case_sensitive;
      63             :         files_struct *fsp; /* Back pointer to containing fsp, only
      64             :                               set from OpenDir_fsp(). */
      65             : };
      66             : 
      67             : struct dptr_struct {
      68             :         struct dptr_struct *next, *prev;
      69             :         int dnum;
      70             :         uint16_t spid;
      71             :         struct connection_struct *conn;
      72             :         struct smb_Dir *dir_hnd;
      73             :         bool expect_close;
      74             :         char *wcard;
      75             :         uint32_t attr;
      76             :         struct smb_filename *smb_dname;
      77             :         bool has_wild; /* Set to true if the wcard entry has MS wildcard characters in it. */
      78             :         bool did_stat; /* Optimisation for non-wcard searches. */
      79             :         bool priv;     /* Directory handle opened with privilege. */
      80             :         uint32_t counter;
      81             :         struct memcache *dptr_cache;
      82             : };
      83             : 
      84             : static NTSTATUS OpenDir_fsp(
      85             :         TALLOC_CTX *mem_ctx,
      86             :         connection_struct *conn,
      87             :         files_struct *fsp,
      88             :         const char *mask,
      89             :         uint32_t attr,
      90             :         struct smb_Dir **_dir_hnd);
      91             : 
      92             : static void DirCacheAdd(struct smb_Dir *dir_hnd, const char *name, long offset);
      93             : 
      94             : static int smb_Dir_destructor(struct smb_Dir *dir_hnd);
      95             : 
      96             : static bool SearchDir(struct smb_Dir *dir_hnd, const char *name, long *poffset);
      97             : 
      98             : #define INVALID_DPTR_KEY (-3)
      99             : 
     100             : /****************************************************************************
     101             :  Initialise the dir bitmap.
     102             : ****************************************************************************/
     103             : 
     104        5251 : bool init_dptrs(struct smbd_server_connection *sconn)
     105             : {
     106        5251 :         if (sconn->searches.dptr_bmap) {
     107           0 :                 return true;
     108             :         }
     109             : 
     110        5251 :         sconn->searches.dptr_bmap = bitmap_talloc(
     111             :                 sconn, MAX_DIRECTORY_HANDLES);
     112             : 
     113        5251 :         if (sconn->searches.dptr_bmap == NULL) {
     114           0 :                 return false;
     115             :         }
     116             : 
     117        5251 :         return true;
     118             : }
     119             : 
     120             : /****************************************************************************
     121             :  Get the struct dptr_struct for a dir index.
     122             : ****************************************************************************/
     123             : 
     124           0 : static struct dptr_struct *dptr_get(struct smbd_server_connection *sconn,
     125             :                                     int key)
     126             : {
     127             :         struct dptr_struct *dptr;
     128             : 
     129           0 :         for (dptr = sconn->searches.dirptrs; dptr != NULL; dptr = dptr->next) {
     130           0 :                 if(dptr->dnum != key) {
     131           0 :                         continue;
     132             :                 }
     133           0 :                 DLIST_PROMOTE(sconn->searches.dirptrs, dptr);
     134           0 :                 return dptr;
     135             :         }
     136           0 :         return(NULL);
     137             : }
     138             : 
     139             : /****************************************************************************
     140             :  Get the dir path for a dir index.
     141             : ****************************************************************************/
     142             : 
     143           0 : const char *dptr_path(struct smbd_server_connection *sconn, int key)
     144             : {
     145           0 :         struct dptr_struct *dptr = dptr_get(sconn, key);
     146           0 :         if (dptr)
     147           0 :                 return(dptr->smb_dname->base_name);
     148           0 :         return(NULL);
     149             : }
     150             : 
     151             : /****************************************************************************
     152             :  Get the dir wcard for a dir index.
     153             : ****************************************************************************/
     154             : 
     155           0 : const char *dptr_wcard(struct smbd_server_connection *sconn, int key)
     156             : {
     157           0 :         struct dptr_struct *dptr = dptr_get(sconn, key);
     158           0 :         if (dptr)
     159           0 :                 return(dptr->wcard);
     160           0 :         return(NULL);
     161             : }
     162             : 
     163             : /****************************************************************************
     164             :  Get the dir attrib for a dir index.
     165             : ****************************************************************************/
     166             : 
     167           0 : uint16_t dptr_attr(struct smbd_server_connection *sconn, int key)
     168             : {
     169           0 :         struct dptr_struct *dptr = dptr_get(sconn, key);
     170           0 :         if (dptr)
     171           0 :                 return(dptr->attr);
     172           0 :         return(0);
     173             : }
     174             : 
     175             : /****************************************************************************
     176             :  Close all dptrs for a cnum.
     177             : ****************************************************************************/
     178             : 
     179           0 : void dptr_closecnum(connection_struct *conn)
     180             : {
     181             :         struct dptr_struct *dptr, *next;
     182           0 :         struct smbd_server_connection *sconn = conn->sconn;
     183             : 
     184           0 :         if (sconn == NULL) {
     185           0 :                 return;
     186             :         }
     187             : 
     188           0 :         for(dptr = sconn->searches.dirptrs; dptr; dptr = next) {
     189           0 :                 next = dptr->next;
     190           0 :                 if (dptr->conn == conn) {
     191             :                         /*
     192             :                          * Need to make a copy, "dptr" will be gone
     193             :                          * after close_file_free() returns
     194             :                          */
     195           0 :                         struct files_struct *fsp = dptr->dir_hnd->fsp;
     196           0 :                         close_file_free(NULL, &fsp, NORMAL_CLOSE);
     197             :                 }
     198             :         }
     199             : }
     200             : 
     201             : /****************************************************************************
     202             :  Create a new dir ptr. If the flag old_handle is true then we must allocate
     203             :  from the bitmap range 0 - 255 as old SMBsearch directory handles are only
     204             :  one byte long. If old_handle is false we allocate from the range
     205             :  256 - MAX_DIRECTORY_HANDLES. We bias the number we return by 1 to ensure
     206             :  a directory handle is never zero.
     207             :  wcard must not be zero.
     208             : ****************************************************************************/
     209             : 
     210        2171 : NTSTATUS dptr_create(connection_struct *conn,
     211             :                 struct smb_request *req,
     212             :                 files_struct *fsp,
     213             :                 bool old_handle,
     214             :                 bool expect_close,
     215             :                 uint16_t spid,
     216             :                 const char *wcard,
     217             :                 uint32_t attr,
     218             :                 struct dptr_struct **dptr_ret)
     219             : {
     220        2171 :         struct smbd_server_connection *sconn = conn->sconn;
     221        2171 :         struct dptr_struct *dptr = NULL;
     222        2171 :         struct smb_Dir *dir_hnd = NULL;
     223             :         NTSTATUS status;
     224             : 
     225        2171 :         DBG_INFO("dir=%s\n", fsp_str_dbg(fsp));
     226             : 
     227        2171 :         if (sconn == NULL) {
     228           0 :                 DEBUG(0,("dptr_create: called with fake connection_struct\n"));
     229           0 :                 return NT_STATUS_INTERNAL_ERROR;
     230             :         }
     231             : 
     232        2171 :         if (!wcard) {
     233           0 :                 return NT_STATUS_INVALID_PARAMETER;
     234             :         }
     235             : 
     236        2171 :         status = check_any_access_fsp(fsp, SEC_DIR_LIST);
     237        2171 :         if (!NT_STATUS_IS_OK(status)) {
     238           0 :                 DBG_INFO("dptr_create: directory %s "
     239             :                         "not open for LIST access\n",
     240             :                         fsp_str_dbg(fsp));
     241           0 :                 return status;
     242             :         }
     243        2171 :         status = OpenDir_fsp(NULL, conn, fsp, wcard, attr, &dir_hnd);
     244        2171 :         if (!NT_STATUS_IS_OK(status)) {
     245           0 :                 return status;
     246             :         }
     247             : 
     248        2171 :         dptr = talloc_zero(NULL, struct dptr_struct);
     249        2171 :         if(!dptr) {
     250           0 :                 DEBUG(0,("talloc fail in dptr_create.\n"));
     251           0 :                 TALLOC_FREE(dir_hnd);
     252           0 :                 return NT_STATUS_NO_MEMORY;
     253             :         }
     254             : 
     255        2171 :         dptr->smb_dname = cp_smb_filename(dptr, fsp->fsp_name);
     256        2171 :         if (dptr->smb_dname == NULL) {
     257           0 :                 TALLOC_FREE(dptr);
     258           0 :                 TALLOC_FREE(dir_hnd);
     259           0 :                 return NT_STATUS_NO_MEMORY;
     260             :         }
     261        2171 :         dptr->conn = conn;
     262        2171 :         dptr->dir_hnd = dir_hnd;
     263        2171 :         dptr->spid = spid;
     264        2171 :         dptr->expect_close = expect_close;
     265        2171 :         dptr->wcard = talloc_strdup(dptr, wcard);
     266        2171 :         if (!dptr->wcard) {
     267           0 :                 TALLOC_FREE(dptr);
     268           0 :                 TALLOC_FREE(dir_hnd);
     269           0 :                 return NT_STATUS_NO_MEMORY;
     270             :         }
     271        2171 :         if ((req != NULL && req->posix_pathnames) ||
     272        2171 :                         (wcard[0] == '.' && wcard[1] == 0)) {
     273           0 :                 dptr->has_wild = True;
     274             :         } else {
     275        2171 :                 dptr->has_wild = ms_has_wild(dptr->wcard);
     276             :         }
     277             : 
     278        2171 :         dptr->attr = attr;
     279             : 
     280        2171 :         if (sconn->using_smb2) {
     281        2171 :                 goto done;
     282             :         }
     283             : 
     284           0 :         if(old_handle) {
     285             : 
     286             :                 /*
     287             :                  * This is an old-style SMBsearch request. Ensure the
     288             :                  * value we return will fit in the range 1-255.
     289             :                  */
     290             : 
     291           0 :                 dptr->dnum = bitmap_find(sconn->searches.dptr_bmap, 0);
     292             : 
     293           0 :                 if(dptr->dnum == -1 || dptr->dnum > 254) {
     294           0 :                         DBG_ERR("returned %d: Error - all old "
     295             :                                 "dirptrs in use ?\n",
     296             :                                 dptr->dnum);
     297           0 :                         TALLOC_FREE(dptr);
     298           0 :                         TALLOC_FREE(dir_hnd);
     299           0 :                         return NT_STATUS_TOO_MANY_OPENED_FILES;
     300             :                 }
     301             :         } else {
     302             : 
     303             :                 /*
     304             :                  * This is a new-style trans2 request. Allocate from
     305             :                  * a range that will return 256 - MAX_DIRECTORY_HANDLES.
     306             :                  */
     307             : 
     308           0 :                 dptr->dnum = bitmap_find(sconn->searches.dptr_bmap, 255);
     309             : 
     310           0 :                 if(dptr->dnum == -1 || dptr->dnum < 255) {
     311           0 :                         DBG_ERR("returned %d: Error - all new "
     312             :                                 "dirptrs in use ?\n",
     313             :                                 dptr->dnum);
     314           0 :                         TALLOC_FREE(dptr);
     315           0 :                         TALLOC_FREE(dir_hnd);
     316           0 :                         return NT_STATUS_TOO_MANY_OPENED_FILES;
     317             :                 }
     318             :         }
     319             : 
     320           0 :         bitmap_set(sconn->searches.dptr_bmap, dptr->dnum);
     321             : 
     322           0 :         dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */
     323             : 
     324           0 :         DLIST_ADD(sconn->searches.dirptrs, dptr);
     325             : 
     326        2171 : done:
     327        2171 :         DBG_INFO("creating new dirptr [%d] for path [%s], expect_close = %d\n",
     328             :                  dptr->dnum, fsp_str_dbg(fsp), expect_close);
     329             : 
     330        2171 :         *dptr_ret = dptr;
     331             : 
     332        2171 :         return NT_STATUS_OK;
     333             : }
     334             : 
     335             : 
     336             : /****************************************************************************
     337             :  Wrapper functions to access the lower level directory handles.
     338             : ****************************************************************************/
     339             : 
     340        2171 : void dptr_CloseDir(files_struct *fsp)
     341             : {
     342        2171 :         struct smbd_server_connection *sconn = NULL;
     343             : 
     344        2171 :         if (fsp->dptr == NULL) {
     345           0 :                 return;
     346             :         }
     347        2171 :         sconn = fsp->dptr->conn->sconn;
     348             : 
     349             :         /*
     350             :          * The destructor for the struct smb_Dir (fsp->dptr->dir_hnd)
     351             :          * now handles all resource deallocation.
     352             :          */
     353             : 
     354        2171 :         DBG_INFO("closing dptr key %d\n", fsp->dptr->dnum);
     355             : 
     356        2171 :         if (sconn != NULL && !sconn->using_smb2) {
     357           0 :                 DLIST_REMOVE(sconn->searches.dirptrs, fsp->dptr);
     358             : 
     359             :                 /*
     360             :                  * Free the dnum in the bitmap. Remember the dnum value is
     361             :                  * always biased by one with respect to the bitmap.
     362             :                  */
     363             : 
     364           0 :                 if (!bitmap_query(sconn->searches.dptr_bmap,
     365           0 :                                   fsp->dptr->dnum - 1))
     366             :                 {
     367           0 :                         DBG_ERR("closing dnum = %d and bitmap not set !\n",
     368             :                                 fsp->dptr->dnum);
     369             :                 }
     370             : 
     371           0 :                 bitmap_clear(sconn->searches.dptr_bmap, fsp->dptr->dnum - 1);
     372             :         }
     373             : 
     374        2171 :         TALLOC_FREE(fsp->dptr->dir_hnd);
     375        2171 :         TALLOC_FREE(fsp->dptr);
     376             : }
     377             : 
     378           0 : void dptr_SeekDir(struct dptr_struct *dptr, long offset)
     379             : {
     380           0 :         SeekDir(dptr->dir_hnd, offset);
     381           0 : }
     382             : 
     383       15947 : long dptr_TellDir(struct dptr_struct *dptr)
     384             : {
     385       15947 :         return TellDir(dptr->dir_hnd);
     386             : }
     387             : 
     388       14920 : bool dptr_has_wild(struct dptr_struct *dptr)
     389             : {
     390       14920 :         return dptr->has_wild;
     391             : }
     392             : 
     393           0 : int dptr_dnum(struct dptr_struct *dptr)
     394             : {
     395           0 :         return dptr->dnum;
     396             : }
     397             : 
     398           0 : bool dptr_get_priv(struct dptr_struct *dptr)
     399             : {
     400           0 :         return dptr->priv;
     401             : }
     402             : 
     403           0 : void dptr_set_priv(struct dptr_struct *dptr)
     404             : {
     405           0 :         dptr->priv = true;
     406           0 : }
     407             : 
     408       14920 : bool dptr_case_sensitive(struct dptr_struct *dptr)
     409             : {
     410       14920 :         return dptr->dir_hnd->case_sensitive;
     411             : }
     412             : 
     413             : /****************************************************************************
     414             :  Return the next visible file name, skipping veto'd and invisible files.
     415             : ****************************************************************************/
     416             : 
     417       15947 : static char *dptr_ReadDirName(TALLOC_CTX *ctx,
     418             :                               struct dptr_struct *dptr,
     419             :                               long *poffset,
     420             :                               SMB_STRUCT_STAT *pst)
     421             : {
     422             :         struct smb_filename smb_fname_base;
     423       15947 :         char *name = NULL;
     424       15947 :         const char *name_temp = NULL;
     425       15947 :         char *talloced = NULL;
     426       15947 :         char *pathreal = NULL;
     427       15947 :         char *found_name = NULL;
     428             :         NTSTATUS status;
     429             : 
     430       15947 :         SET_STAT_INVALID(*pst);
     431             : 
     432       15947 :         if (dptr->has_wild || dptr->did_stat) {
     433       15805 :                 name_temp = ReadDirName(dptr->dir_hnd, poffset, pst,
     434             :                                                     &talloced);
     435       15805 :                 if (name_temp == NULL) {
     436        4299 :                         return NULL;
     437             :                 }
     438       11506 :                 if (talloced != NULL) {
     439           0 :                         return talloc_move(ctx, &talloced);
     440             :                 }
     441       11506 :                 return talloc_strdup(ctx, name_temp);
     442             :         }
     443             : 
     444             :         /* If poffset is -1 then we know we returned this name before and we
     445             :          * have no wildcards. We're at the end of the directory. */
     446         142 :         if (*poffset == END_OF_DIRECTORY_OFFSET) {
     447           0 :                 return NULL;
     448             :         }
     449             : 
     450             :         /* We know the stored wcard contains no wildcard characters.
     451             :          * See if we can match with a stat call. If we can't, then set
     452             :          * did_stat to true to ensure we only do this once and keep
     453             :          * searching. */
     454             : 
     455         142 :         dptr->did_stat = true;
     456             : 
     457         142 :         if (VALID_STAT(*pst)) {
     458           0 :                 name = talloc_strdup(ctx, dptr->wcard);
     459           0 :                 goto ret;
     460             :         }
     461             : 
     462         142 :         pathreal = talloc_asprintf(ctx,
     463             :                                 "%s/%s",
     464         142 :                                 dptr->smb_dname->base_name,
     465             :                                 dptr->wcard);
     466         142 :         if (!pathreal)
     467           0 :                 return NULL;
     468             : 
     469             :         /* Create an smb_filename with stream_name == NULL. */
     470         142 :         smb_fname_base = (struct smb_filename) {
     471             :                 .base_name = pathreal,
     472         142 :                 .flags = dptr->dir_hnd->fsp->fsp_name->flags,
     473         142 :                 .twrp = dptr->smb_dname->twrp,
     474             :         };
     475             : 
     476         142 :         if (vfs_stat(dptr->conn, &smb_fname_base) == 0) {
     477         113 :                 *pst = smb_fname_base.st;
     478         113 :                 name = talloc_strdup(ctx, dptr->wcard);
     479         113 :                 goto clean;
     480             :         } else {
     481             :                 /* If we get any other error than ENOENT or ENOTDIR
     482             :                    then the file exists we just can't stat it. */
     483          29 :                 if (errno != ENOENT && errno != ENOTDIR) {
     484           0 :                         name = talloc_strdup(ctx, dptr->wcard);
     485           0 :                         goto clean;
     486             :                 }
     487             :         }
     488             : 
     489             :         /* Stat failed. We know this is authoritative if we are
     490             :          * providing case sensitive semantics or the underlying
     491             :          * filesystem is case sensitive.
     492             :          */
     493          29 :         if (dptr->dir_hnd->case_sensitive ||
     494          29 :             !(dptr->conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH))
     495             :         {
     496           0 :                 goto clean;
     497             :         }
     498             : 
     499             :         /*
     500             :          * Try case-insensitive stat if the fs has the ability. This avoids
     501             :          * scanning the whole directory.
     502             :          */
     503          29 :         status = SMB_VFS_GET_REAL_FILENAME_AT(dptr->conn,
     504             :                                               dptr->dir_hnd->fsp,
     505             :                                               dptr->wcard,
     506             :                                               ctx,
     507             :                                               &found_name);
     508          29 :         if (NT_STATUS_IS_OK(status)) {
     509           0 :                 name = found_name;
     510           0 :                 goto clean;
     511             :         }
     512          29 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
     513             :                 /* The case-insensitive lookup was authoritative. */
     514           0 :                 goto clean;
     515             :         }
     516             : 
     517          29 :         TALLOC_FREE(pathreal);
     518             : 
     519          29 :         name_temp = ReadDirName(dptr->dir_hnd, poffset, pst, &talloced);
     520          29 :         if (name_temp == NULL) {
     521           0 :                 return NULL;
     522             :         }
     523          29 :         if (talloced != NULL) {
     524           0 :                 return talloc_move(ctx, &talloced);
     525             :         }
     526          29 :         return talloc_strdup(ctx, name_temp);
     527             : 
     528         113 : clean:
     529         113 :         TALLOC_FREE(pathreal);
     530           0 : ret:
     531             :         /* We need to set the underlying dir_hnd offset to -1
     532             :          * also as this function is usually called with the
     533             :          * output from TellDir. */
     534         113 :         dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET;
     535         113 :         return name;
     536             : }
     537             : 
     538             : /****************************************************************************
     539             :  Search for a file by name.
     540             : ****************************************************************************/
     541             : 
     542           0 : bool dptr_SearchDir(struct dptr_struct *dptr, const char *name, long *poffset, SMB_STRUCT_STAT *pst)
     543             : {
     544           0 :         SET_STAT_INVALID(*pst);
     545             : 
     546           0 :         if (!dptr->has_wild && (dptr->dir_hnd->offset == END_OF_DIRECTORY_OFFSET)) {
     547             :                 /* This is a singleton directory and we're already at the end. */
     548           0 :                 *poffset = END_OF_DIRECTORY_OFFSET;
     549           0 :                 return False;
     550             :         }
     551             : 
     552           0 :         return SearchDir(dptr->dir_hnd, name, poffset);
     553             : }
     554             : 
     555             : /****************************************************************************
     556             :  Map a native directory offset to a 32-bit cookie.
     557             : ****************************************************************************/
     558             : 
     559           0 : static uint32_t map_dir_offset_to_wire(struct dptr_struct *dptr, long offset)
     560             : {
     561             :         DATA_BLOB key;
     562             :         DATA_BLOB val;
     563             : 
     564           0 :         if (offset == END_OF_DIRECTORY_OFFSET) {
     565           0 :                 return WIRE_END_OF_DIRECTORY_OFFSET;
     566             :         }
     567           0 :         if (offset == START_OF_DIRECTORY_OFFSET) {
     568           0 :                 return WIRE_START_OF_DIRECTORY_OFFSET;
     569             :         }
     570           0 :         if (offset == DOT_DOT_DIRECTORY_OFFSET) {
     571           0 :                 return WIRE_DOT_DOT_DIRECTORY_OFFSET;
     572             :         }
     573             :         if (sizeof(long) == 4) {
     574             :                 /* 32-bit machine. We can cheat... */
     575             :                 return (uint32_t)offset;
     576             :         }
     577           0 :         if (dptr->dptr_cache == NULL) {
     578             :                 /* Lazy initialize cache. */
     579           0 :                 dptr->dptr_cache = memcache_init(dptr, 0);
     580           0 :                 if (dptr->dptr_cache == NULL) {
     581           0 :                         return WIRE_END_OF_DIRECTORY_OFFSET;
     582             :                 }
     583             :         } else {
     584             :                 /* Have we seen this offset before ? */
     585           0 :                 key.data = (void *)&offset;
     586           0 :                 key.length = sizeof(offset);
     587           0 :                 if (memcache_lookup(dptr->dptr_cache,
     588             :                                         SMB1_SEARCH_OFFSET_MAP,
     589             :                                         key,
     590             :                                         &val)) {
     591             :                         uint32_t wire_offset;
     592           0 :                         SMB_ASSERT(val.length == sizeof(wire_offset));
     593           0 :                         memcpy(&wire_offset, val.data, sizeof(wire_offset));
     594           0 :                         DEBUG(10,("found wire %u <-> offset %ld\n",
     595             :                                 (unsigned int)wire_offset,
     596             :                                 (long)offset));
     597           0 :                         return wire_offset;
     598             :                 }
     599             :         }
     600             :         /* Allocate a new wire cookie. */
     601             :         do {
     602           0 :                 dptr->counter++;
     603           0 :         } while (dptr->counter == WIRE_START_OF_DIRECTORY_OFFSET ||
     604           0 :                  dptr->counter == WIRE_END_OF_DIRECTORY_OFFSET ||
     605           0 :                  dptr->counter == WIRE_DOT_DOT_DIRECTORY_OFFSET);
     606             :         /* Store it in the cache. */
     607           0 :         key.data = (void *)&offset;
     608           0 :         key.length = sizeof(offset);
     609           0 :         val.data = (void *)&dptr->counter;
     610           0 :         val.length = sizeof(dptr->counter); /* MUST BE uint32_t ! */
     611           0 :         memcache_add(dptr->dptr_cache,
     612             :                         SMB1_SEARCH_OFFSET_MAP,
     613             :                         key,
     614             :                         val);
     615             :         /* And the reverse mapping for lookup from
     616             :            map_wire_to_dir_offset(). */
     617           0 :         memcache_add(dptr->dptr_cache,
     618             :                         SMB1_SEARCH_OFFSET_MAP,
     619             :                         val,
     620             :                         key);
     621           0 :         DEBUG(10,("stored wire %u <-> offset %ld\n",
     622             :                 (unsigned int)dptr->counter,
     623             :                 (long)offset));
     624           0 :         return dptr->counter;
     625             : }
     626             : 
     627             : /****************************************************************************
     628             :  Fill the 5 byte server reserved dptr field.
     629             : ****************************************************************************/
     630             : 
     631           0 : bool dptr_fill(struct smbd_server_connection *sconn,
     632             :                char *buf1,unsigned int key)
     633             : {
     634           0 :         unsigned char *buf = (unsigned char *)buf1;
     635           0 :         struct dptr_struct *dptr = dptr_get(sconn, key);
     636             :         uint32_t wire_offset;
     637           0 :         if (!dptr) {
     638           0 :                 DEBUG(1,("filling null dirptr %d\n",key));
     639           0 :                 return(False);
     640             :         }
     641           0 :         wire_offset = map_dir_offset_to_wire(dptr,TellDir(dptr->dir_hnd));
     642           0 :         DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key,
     643             :                 (long)dptr->dir_hnd,(int)wire_offset));
     644           0 :         buf[0] = key;
     645           0 :         SIVAL(buf,1,wire_offset);
     646           0 :         return(True);
     647             : }
     648             : 
     649             : /****************************************************************************
     650             :  Map a 32-bit wire cookie to a native directory offset.
     651             : ****************************************************************************/
     652             : 
     653           0 : static long map_wire_to_dir_offset(struct dptr_struct *dptr, uint32_t wire_offset)
     654             : {
     655             :         DATA_BLOB key;
     656             :         DATA_BLOB val;
     657             : 
     658           0 :         if (wire_offset == WIRE_END_OF_DIRECTORY_OFFSET) {
     659           0 :                 return END_OF_DIRECTORY_OFFSET;
     660           0 :         } else if(wire_offset == WIRE_START_OF_DIRECTORY_OFFSET) {
     661           0 :                 return START_OF_DIRECTORY_OFFSET;
     662           0 :         } else if (wire_offset == WIRE_DOT_DOT_DIRECTORY_OFFSET) {
     663           0 :                 return DOT_DOT_DIRECTORY_OFFSET;
     664             :         }
     665             :         if (sizeof(long) == 4) {
     666             :                 /* 32-bit machine. We can cheat... */
     667             :                 return (long)wire_offset;
     668             :         }
     669           0 :         if (dptr->dptr_cache == NULL) {
     670             :                 /* Logic error, cache should be initialized. */
     671           0 :                 return END_OF_DIRECTORY_OFFSET;
     672             :         }
     673           0 :         key.data = (void *)&wire_offset;
     674           0 :         key.length = sizeof(wire_offset);
     675           0 :         if (memcache_lookup(dptr->dptr_cache,
     676             :                                 SMB1_SEARCH_OFFSET_MAP,
     677             :                                 key,
     678             :                                 &val)) {
     679             :                 /* Found mapping. */
     680             :                 long offset;
     681           0 :                 SMB_ASSERT(val.length == sizeof(offset));
     682           0 :                 memcpy(&offset, val.data, sizeof(offset));
     683           0 :                 DEBUG(10,("lookup wire %u <-> offset %ld\n",
     684             :                         (unsigned int)wire_offset,
     685             :                         (long)offset));
     686           0 :                 return offset;
     687             :         }
     688           0 :         return END_OF_DIRECTORY_OFFSET;
     689             : }
     690             : 
     691             : /****************************************************************************
     692             :  Return the associated fsp and seek the dir_hnd on it it given the 5 byte
     693             :  server field.
     694             : ****************************************************************************/
     695             : 
     696           0 : files_struct *dptr_fetch_fsp(struct smbd_server_connection *sconn,
     697             :                                char *buf, int *num)
     698             : {
     699           0 :         unsigned int key = *(unsigned char *)buf;
     700           0 :         struct dptr_struct *dptr = dptr_get(sconn, key);
     701             :         uint32_t wire_offset;
     702             :         long seekoff;
     703             : 
     704           0 :         if (dptr == NULL) {
     705           0 :                 DEBUG(3,("fetched null dirptr %d\n",key));
     706           0 :                 return(NULL);
     707             :         }
     708           0 :         *num = key;
     709           0 :         wire_offset = IVAL(buf,1);
     710           0 :         seekoff = map_wire_to_dir_offset(dptr, wire_offset);
     711           0 :         SeekDir(dptr->dir_hnd,seekoff);
     712           0 :         DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
     713             :                 key, dptr->smb_dname->base_name, (int)seekoff));
     714           0 :         return dptr->dir_hnd->fsp;
     715             : }
     716             : 
     717           0 : struct files_struct *dir_hnd_fetch_fsp(struct smb_Dir *dir_hnd)
     718             : {
     719           0 :         return dir_hnd->fsp;
     720             : }
     721             : 
     722             : /****************************************************************************
     723             :  Fetch the fsp associated with the dptr_num.
     724             : ****************************************************************************/
     725             : 
     726           0 : files_struct *dptr_fetch_lanman2_fsp(struct smbd_server_connection *sconn,
     727             :                                        int dptr_num)
     728             : {
     729           0 :         struct dptr_struct *dptr  = dptr_get(sconn, dptr_num);
     730           0 :         if (dptr == NULL) {
     731           0 :                 return NULL;
     732             :         }
     733           0 :         DBG_NOTICE("fetching dirptr %d for path %s\n",
     734             :                 dptr_num,
     735             :                 dptr->smb_dname->base_name);
     736           0 :         return dptr->dir_hnd->fsp;
     737             : }
     738             : 
     739           0 : static bool mangle_mask_match(connection_struct *conn,
     740             :                 const char *filename,
     741             :                 const char *mask)
     742             : {
     743             :         char mname[13];
     744             : 
     745           0 :         if (!name_to_8_3(filename,mname,False,conn->params)) {
     746           0 :                 return False;
     747             :         }
     748           0 :         return mask_match_search(mname,mask,False);
     749             : }
     750             : 
     751       14920 : bool smbd_dirptr_get_entry(TALLOC_CTX *ctx,
     752             :                            struct dptr_struct *dirptr,
     753             :                            const char *mask,
     754             :                            uint32_t dirtype,
     755             :                            bool dont_descend,
     756             :                            bool ask_sharemode,
     757             :                            bool get_dosmode_in,
     758             :                            bool (*match_fn)(TALLOC_CTX *ctx,
     759             :                                             void *private_data,
     760             :                                             const char *dname,
     761             :                                             const char *mask,
     762             :                                             char **_fname),
     763             :                            bool (*mode_fn)(TALLOC_CTX *ctx,
     764             :                                            void *private_data,
     765             :                                            struct files_struct *dirfsp,
     766             :                                            struct smb_filename *atname,
     767             :                                            struct smb_filename *smb_fname,
     768             :                                            bool get_dosmode,
     769             :                                            uint32_t *_mode),
     770             :                            void *private_data,
     771             :                            char **_fname,
     772             :                            struct smb_filename **_smb_fname,
     773             :                            uint32_t *_mode,
     774             :                            long *_prev_offset)
     775             : {
     776       14920 :         connection_struct *conn = dirptr->conn;
     777             :         size_t slashlen;
     778             :         size_t pathlen;
     779       14920 :         const char *dpath = dirptr->smb_dname->base_name;
     780       14920 :         bool dirptr_path_is_dot = ISDOT(dpath);
     781             :         NTSTATUS status;
     782             :         int ret;
     783             : 
     784       14920 :         *_smb_fname = NULL;
     785       14920 :         *_mode = 0;
     786             : 
     787       14920 :         pathlen = strlen(dpath);
     788       14920 :         slashlen = ( dpath[pathlen-1] != '/') ? 1 : 0;
     789             : 
     790        1027 :         while (true) {
     791             :                 long cur_offset;
     792             :                 long prev_offset;
     793       15947 :                 SMB_STRUCT_STAT sbuf = { 0 };
     794       15947 :                 char *dname = NULL;
     795             :                 bool isdots;
     796       15947 :                 char *fname = NULL;
     797       15947 :                 char *pathreal = NULL;
     798       15947 :                 struct smb_filename *atname = NULL;
     799       15947 :                 struct smb_filename *smb_fname = NULL;
     800       15947 :                 uint32_t mode = 0;
     801       15947 :                 bool check_dfs_symlink = false;
     802       15947 :                 bool get_dosmode = get_dosmode_in;
     803             :                 bool ok;
     804             : 
     805       15947 :                 cur_offset = dptr_TellDir(dirptr);
     806       15947 :                 prev_offset = cur_offset;
     807       15947 :                 dname = dptr_ReadDirName(ctx, dirptr, &cur_offset, &sbuf);
     808             : 
     809       15947 :                 DBG_DEBUG("dir [%s] dirptr [0x%lx] offset [%ld] => dname [%s]\n",
     810             :                           smb_fname_str_dbg(dirptr->smb_dname), (long)dirptr,
     811             :                           cur_offset, dname ? dname : "(finished)");
     812             : 
     813       15947 :                 if (dname == NULL) {
     814       14920 :                         return false;
     815             :                 }
     816             : 
     817       11648 :                 isdots = (ISDOT(dname) || ISDOTDOT(dname));
     818       11648 :                 if (dont_descend && !isdots) {
     819           0 :                         TALLOC_FREE(dname);
     820        1027 :                         continue;
     821             :                 }
     822             : 
     823       11648 :                 if (IS_VETO_PATH(conn, dname)) {
     824           0 :                         TALLOC_FREE(dname);
     825           0 :                         continue;
     826             :                 }
     827             : 
     828             :                 /*
     829             :                  * fname may get mangled, dname is never mangled.
     830             :                  * Whenever we're accessing the filesystem we use
     831             :                  * pathreal which is composed from dname.
     832             :                  */
     833             : 
     834       11648 :                 ok = match_fn(ctx, private_data, dname, mask, &fname);
     835       11648 :                 if (!ok) {
     836        1003 :                         TALLOC_FREE(dname);
     837        1003 :                         continue;
     838             :                 }
     839             : 
     840             :                 /*
     841             :                  * This used to be
     842             :                  * pathreal = talloc_asprintf(ctx, "%s%s%s", dirptr->path,
     843             :                  *                            needslash?"/":"", dname);
     844             :                  * but this was measurably slower than doing the memcpy.
     845             :                  */
     846             : 
     847       10645 :                 pathreal = talloc_array(
     848             :                         ctx, char,
     849             :                         pathlen + slashlen + talloc_get_size(dname));
     850       10645 :                 if (!pathreal) {
     851           0 :                         TALLOC_FREE(dname);
     852           0 :                         TALLOC_FREE(fname);
     853           0 :                         return false;
     854             :                 }
     855             : 
     856             :                 /*
     857             :                  * We don't want to pass ./xxx to modules below us so don't
     858             :                  * add the path if it is just . by itself.
     859             :                  */
     860       10645 :                 if (dirptr_path_is_dot) {
     861        5350 :                         memcpy(pathreal, dname, talloc_get_size(dname));
     862             :                 } else {
     863        5295 :                         memcpy(pathreal, dpath, pathlen);
     864        5295 :                         pathreal[pathlen] = '/';
     865        5295 :                         memcpy(pathreal + slashlen + pathlen, dname,
     866             :                                talloc_get_size(dname));
     867             :                 }
     868             : 
     869             :                 /* Create smb_fname with NULL stream_name. */
     870       10645 :                 smb_fname = synthetic_smb_fname(talloc_tos(),
     871             :                                                 pathreal,
     872             :                                                 NULL,
     873             :                                                 &sbuf,
     874       10645 :                                                 dirptr->smb_dname->twrp,
     875       10645 :                                                 dirptr->smb_dname->flags);
     876       10645 :                 TALLOC_FREE(pathreal);
     877       10645 :                 if (smb_fname == NULL) {
     878           0 :                         TALLOC_FREE(dname);
     879           0 :                         TALLOC_FREE(fname);
     880           0 :                         return false;
     881             :                 }
     882             : 
     883       10645 :                 if (!VALID_STAT(smb_fname->st)) {
     884             :                         /*
     885             :                          * If stat() fails with ENOENT it might be a
     886             :                          * msdfs-symlink in Windows context, this is checked
     887             :                          * below, for now we just want to fill stat info as good
     888             :                          * as we can.
     889             :                          */
     890        4464 :                         ret = vfs_stat(conn, smb_fname);
     891        4464 :                         if (ret != 0 && errno != ENOENT) {
     892           0 :                                 TALLOC_FREE(smb_fname);
     893           0 :                                 TALLOC_FREE(dname);
     894           0 :                                 TALLOC_FREE(fname);
     895           0 :                                 continue;
     896             :                         }
     897             :                 }
     898             : 
     899             :                 /* Create smb_fname with NULL stream_name. */
     900       10645 :                 atname = synthetic_smb_fname(talloc_tos(),
     901             :                                              dname,
     902             :                                              NULL,
     903       10645 :                                              &smb_fname->st,
     904       10645 :                                              dirptr->smb_dname->twrp,
     905       10645 :                                              dirptr->smb_dname->flags);
     906       10645 :                 if (atname == NULL) {
     907           0 :                         TALLOC_FREE(dname);
     908           0 :                         TALLOC_FREE(fname);
     909           0 :                         TALLOC_FREE(smb_fname);
     910           0 :                         return false;
     911             :                 }
     912             : 
     913             :                 /*
     914             :                  * openat_pathref_fsp() will return
     915             :                  * NT_STATUS_OBJECT_NAME_NOT_FOUND in non-POSIX context when
     916             :                  * hitting a dangling symlink. It may be a DFS symlink, this is
     917             :                  * checked below by the mode_fn() call, so we have to allow this
     918             :                  * here.
     919             :                  *
     920             :                  * NT_STATUS_STOPPED_ON_SYMLINK is returned in POSIX context
     921             :                  * when hitting a symlink and ensures we always return directory
     922             :                  * entries that are symlinks in POSIX context.
     923             :                  */
     924       10645 :                 status = openat_pathref_fsp(dirptr->dir_hnd->fsp, atname);
     925       10645 :                 if (!NT_STATUS_IS_OK(status) &&
     926          80 :                     !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND))
     927             :                 {
     928           0 :                         TALLOC_FREE(atname);
     929           0 :                         TALLOC_FREE(dname);
     930           0 :                         TALLOC_FREE(fname);
     931           0 :                         TALLOC_FREE(smb_fname);
     932           0 :                         continue;
     933             :                 }
     934             : 
     935       10645 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
     936          80 :                         if (!(atname->flags & SMB_FILENAME_POSIX_PATH)) {
     937          80 :                                 check_dfs_symlink = true;
     938             :                         }
     939             :                         /*
     940             :                          * Check if it's a symlink. We only want to return this
     941             :                          * if it's a DFS symlink or in POSIX mode. Disable
     942             :                          * getting dosmode in the mode_fn() and prime the mode
     943             :                          * as FILE_ATTRIBUTE_NORMAL.
     944             :                          */
     945          80 :                         mode = FILE_ATTRIBUTE_NORMAL;
     946          80 :                         get_dosmode = false;
     947             :                 }
     948             : 
     949       10645 :                 status = move_smb_fname_fsp_link(smb_fname, atname);
     950       10645 :                 if (!NT_STATUS_IS_OK(status)) {
     951           0 :                         DBG_WARNING("Failed to move pathref for [%s]: %s\n",
     952             :                                     smb_fname_str_dbg(smb_fname),
     953             :                                     nt_errstr(status));
     954           0 :                         TALLOC_FREE(atname);
     955           0 :                         TALLOC_FREE(smb_fname);
     956           0 :                         TALLOC_FREE(dname);
     957           0 :                         TALLOC_FREE(fname);
     958           0 :                         continue;
     959             :                 }
     960             : 
     961       10645 :                 if (!is_visible_fsp(smb_fname->fsp)) {
     962           0 :                         TALLOC_FREE(atname);
     963           0 :                         TALLOC_FREE(smb_fname);
     964           0 :                         TALLOC_FREE(dname);
     965           0 :                         TALLOC_FREE(fname);
     966           0 :                         continue;
     967             :                 }
     968             : 
     969             :                 /*
     970             :                  * Don't leak metadata about the containing
     971             :                  * directory of the share.
     972             :                  */
     973       10645 :                 if (dirptr_path_is_dot && ISDOTDOT(dname)) {
     974             :                         /*
     975             :                          * Making a copy here, then freeing
     976             :                          * the original will close the smb_fname->fsp.
     977             :                          */
     978             :                         struct smb_filename *tmp_smb_fname =
     979         422 :                                 cp_smb_filename(ctx, smb_fname);
     980             : 
     981         422 :                         if (tmp_smb_fname == NULL) {
     982           0 :                                 TALLOC_FREE(atname);
     983           0 :                                 TALLOC_FREE(smb_fname);
     984           0 :                                 TALLOC_FREE(dname);
     985           0 :                                 TALLOC_FREE(fname);
     986           0 :                                 return false;
     987             :                         }
     988         422 :                         TALLOC_FREE(smb_fname);
     989         422 :                         smb_fname = tmp_smb_fname;
     990         422 :                         mode = FILE_ATTRIBUTE_DIRECTORY;
     991         422 :                         get_dosmode = false;
     992             : 
     993             :                         /* Ensure posix fileid and sids are hidden
     994             :                          */
     995         422 :                         smb_fname->st.st_ex_ino = 0;
     996         422 :                         smb_fname->st.st_ex_dev = 0;
     997         422 :                         smb_fname->st.st_ex_uid = -1;
     998         422 :                         smb_fname->st.st_ex_gid = -1;
     999             :                 }
    1000             : 
    1001       10645 :                 ok = mode_fn(ctx,
    1002             :                              private_data,
    1003       10645 :                              dirptr->dir_hnd->fsp,
    1004             :                              atname,
    1005             :                              smb_fname,
    1006             :                              get_dosmode,
    1007             :                              &mode);
    1008       10645 :                 if (!ok) {
    1009          20 :                         TALLOC_FREE(atname);
    1010          20 :                         TALLOC_FREE(smb_fname);
    1011          20 :                         TALLOC_FREE(dname);
    1012          20 :                         TALLOC_FREE(fname);
    1013          20 :                         continue;
    1014             :                 }
    1015             : 
    1016       10625 :                 TALLOC_FREE(atname);
    1017             : 
    1018             :                 /*
    1019             :                  * The only valid cases where we return the directory entry if
    1020             :                  * it's a symlink are:
    1021             :                  *
    1022             :                  * 1. POSIX context, always return it, or
    1023             :                  *
    1024             :                  * 2. a DFS symlink where the mode_fn() call above has verified
    1025             :                  *    this and set mode to FILE_ATTRIBUTE_REPARSE_POINT.
    1026             :                  */
    1027       10625 :                 if (check_dfs_symlink &&
    1028          60 :                     !(mode & FILE_ATTRIBUTE_REPARSE_POINT))
    1029             :                 {
    1030           4 :                         TALLOC_FREE(smb_fname);
    1031           4 :                         TALLOC_FREE(dname);
    1032           4 :                         TALLOC_FREE(fname);
    1033           4 :                         continue;
    1034             :                 }
    1035             : 
    1036       10621 :                 if (!dir_check_ftype(mode, dirtype)) {
    1037           0 :                         DEBUG(5,("[%s] attribs 0x%x didn't match 0x%x\n",
    1038             :                                 fname, (unsigned int)mode, (unsigned int)dirtype));
    1039           0 :                         TALLOC_FREE(smb_fname);
    1040           0 :                         TALLOC_FREE(dname);
    1041           0 :                         TALLOC_FREE(fname);
    1042           0 :                         continue;
    1043             :                 }
    1044             : 
    1045       10621 :                 if (ask_sharemode && !S_ISDIR(smb_fname->st.st_ex_mode)) {
    1046             :                         struct timespec write_time_ts;
    1047             :                         struct file_id fileid;
    1048             : 
    1049         916 :                         fileid = vfs_file_id_from_sbuf(conn,
    1050         916 :                                                        &smb_fname->st);
    1051         916 :                         get_file_infos(fileid, 0, NULL, &write_time_ts);
    1052         916 :                         if (!is_omit_timespec(&write_time_ts)) {
    1053           0 :                                 update_stat_ex_mtime(&smb_fname->st,
    1054             :                                                      write_time_ts);
    1055             :                         }
    1056             :                 }
    1057             : 
    1058       10621 :                 DEBUG(3,("smbd_dirptr_get_entry mask=[%s] found %s "
    1059             :                         "fname=%s (%s)\n",
    1060             :                         mask, smb_fname_str_dbg(smb_fname),
    1061             :                         dname, fname));
    1062             : 
    1063       10621 :                 if (!conn->sconn->using_smb2) {
    1064             :                         /*
    1065             :                          * The dircache is only needed for SMB1 because SMB1
    1066             :                          * uses a name for the resume wheras SMB2 always
    1067             :                          * continues from the next position (unless it's told to
    1068             :                          * restart or close-and-reopen the listing).
    1069             :                          */
    1070           0 :                         DirCacheAdd(dirptr->dir_hnd, dname, cur_offset);
    1071             :                 }
    1072             : 
    1073       10621 :                 TALLOC_FREE(dname);
    1074             : 
    1075       10621 :                 *_smb_fname = talloc_move(ctx, &smb_fname);
    1076       10621 :                 if (*_smb_fname == NULL) {
    1077           0 :                         return false;
    1078             :                 }
    1079       10621 :                 *_fname = fname;
    1080       10621 :                 *_mode = mode;
    1081       10621 :                 *_prev_offset = prev_offset;
    1082             : 
    1083       10621 :                 return true;
    1084             :         }
    1085             : 
    1086             :         return false;
    1087             : }
    1088             : 
    1089             : /****************************************************************************
    1090             :  Get an 8.3 directory entry.
    1091             : ****************************************************************************/
    1092             : 
    1093           0 : static bool smbd_dirptr_8_3_match_fn(TALLOC_CTX *ctx,
    1094             :                                      void *private_data,
    1095             :                                      const char *dname,
    1096             :                                      const char *mask,
    1097             :                                      char **_fname)
    1098             : {
    1099           0 :         connection_struct *conn = (connection_struct *)private_data;
    1100             : 
    1101           0 :         if ((strcmp(mask,"*.*") == 0) ||
    1102           0 :             mask_match_search(dname, mask, false) ||
    1103           0 :             mangle_mask_match(conn, dname, mask)) {
    1104             :                 char mname[13];
    1105             :                 const char *fname;
    1106             :                 /*
    1107             :                  * Ensure we can push the original name as UCS2. If
    1108             :                  * not, then just don't return this name.
    1109             :                  */
    1110             :                 NTSTATUS status;
    1111           0 :                 size_t ret_len = 0;
    1112           0 :                 size_t len = (strlen(dname) + 2) * 4; /* Allow enough space. */
    1113           0 :                 uint8_t *tmp = talloc_array(talloc_tos(),
    1114             :                                         uint8_t,
    1115             :                                         len);
    1116             : 
    1117           0 :                 status = srvstr_push(NULL,
    1118             :                         FLAGS2_UNICODE_STRINGS,
    1119             :                         tmp,
    1120             :                         dname,
    1121             :                         len,
    1122             :                         STR_TERMINATE,
    1123             :                         &ret_len);
    1124             : 
    1125           0 :                 TALLOC_FREE(tmp);
    1126             : 
    1127           0 :                 if (!NT_STATUS_IS_OK(status)) {
    1128           0 :                         return false;
    1129             :                 }
    1130             : 
    1131           0 :                 if (!mangle_is_8_3(dname, false, conn->params)) {
    1132           0 :                         bool ok = name_to_8_3(dname, mname, false,
    1133           0 :                                               conn->params);
    1134           0 :                         if (!ok) {
    1135           0 :                                 return false;
    1136             :                         }
    1137           0 :                         fname = mname;
    1138             :                 } else {
    1139           0 :                         fname = dname;
    1140             :                 }
    1141             : 
    1142           0 :                 *_fname = talloc_strdup(ctx, fname);
    1143           0 :                 if (*_fname == NULL) {
    1144           0 :                         return false;
    1145             :                 }
    1146             : 
    1147           0 :                 return true;
    1148             :         }
    1149             : 
    1150           0 :         return false;
    1151             : }
    1152             : 
    1153           0 : static bool smbd_dirptr_8_3_mode_fn(TALLOC_CTX *ctx,
    1154             :                                     void *private_data,
    1155             :                                     struct files_struct *dirfsp,
    1156             :                                     struct smb_filename *atname,
    1157             :                                     struct smb_filename *smb_fname,
    1158             :                                     bool get_dosmode,
    1159             :                                     uint32_t *_mode)
    1160             : {
    1161           0 :         connection_struct *conn = (connection_struct *)private_data;
    1162             : 
    1163           0 :         if (!VALID_STAT(smb_fname->st)) {
    1164           0 :                 if ((SMB_VFS_STAT(conn, smb_fname)) != 0) {
    1165           0 :                         DEBUG(5,("smbd_dirptr_8_3_mode_fn: "
    1166             :                                  "Couldn't stat [%s]. Error "
    1167             :                                  "= %s\n",
    1168             :                                  smb_fname_str_dbg(smb_fname),
    1169             :                                  strerror(errno)));
    1170           0 :                         return false;
    1171             :                 }
    1172             :         }
    1173             : 
    1174           0 :         if (get_dosmode) {
    1175           0 :                 *_mode = fdos_mode(smb_fname->fsp);
    1176           0 :                 smb_fname->st = smb_fname->fsp->fsp_name->st;
    1177             :         }
    1178           0 :         return true;
    1179             : }
    1180             : 
    1181           0 : bool get_dir_entry(TALLOC_CTX *ctx,
    1182             :                 struct dptr_struct *dirptr,
    1183             :                 const char *mask,
    1184             :                 uint32_t dirtype,
    1185             :                 char **_fname,
    1186             :                 off_t *_size,
    1187             :                 uint32_t *_mode,
    1188             :                 struct timespec *_date,
    1189             :                 bool check_descend,
    1190             :                 bool ask_sharemode)
    1191             : {
    1192           0 :         connection_struct *conn = dirptr->conn;
    1193           0 :         char *fname = NULL;
    1194           0 :         struct smb_filename *smb_fname = NULL;
    1195           0 :         uint32_t mode = 0;
    1196             :         long prev_offset;
    1197             :         bool ok;
    1198             : 
    1199           0 :         ok = smbd_dirptr_get_entry(ctx,
    1200             :                                    dirptr,
    1201             :                                    mask,
    1202             :                                    dirtype,
    1203             :                                    check_descend,
    1204             :                                    ask_sharemode,
    1205             :                                    true,
    1206             :                                    smbd_dirptr_8_3_match_fn,
    1207             :                                    smbd_dirptr_8_3_mode_fn,
    1208             :                                    conn,
    1209             :                                    &fname,
    1210             :                                    &smb_fname,
    1211             :                                    &mode,
    1212             :                                    &prev_offset);
    1213           0 :         if (!ok) {
    1214           0 :                 return false;
    1215             :         }
    1216             : 
    1217           0 :         *_fname = talloc_move(ctx, &fname);
    1218           0 :         *_size = smb_fname->st.st_ex_size;
    1219           0 :         *_mode = mode;
    1220           0 :         *_date = smb_fname->st.st_ex_mtime;
    1221           0 :         TALLOC_FREE(smb_fname);
    1222           0 :         return true;
    1223             : }
    1224             : 
    1225             : /*******************************************************************
    1226             :  Check to see if a user can read an fsp . This is only approximate,
    1227             :  it is used as part of the "hide unreadable" option. Don't
    1228             :  use it for anything security sensitive.
    1229             : ********************************************************************/
    1230             : 
    1231           0 : static bool user_can_read_fsp(struct files_struct *fsp)
    1232             : {
    1233             :         NTSTATUS status;
    1234           0 :         uint32_t rejected_share_access = 0;
    1235           0 :         uint32_t rejected_mask = 0;
    1236           0 :         struct security_descriptor *sd = NULL;
    1237           0 :         uint32_t access_mask = FILE_READ_DATA|
    1238             :                                 FILE_READ_EA|
    1239             :                                 FILE_READ_ATTRIBUTES|
    1240             :                                 SEC_STD_READ_CONTROL;
    1241             : 
    1242             :         /*
    1243             :          * Never hide files from the root user.
    1244             :          * We use (uid_t)0 here not sec_initial_uid()
    1245             :          * as make test uses a single user context.
    1246             :          */
    1247             : 
    1248           0 :         if (get_current_uid(fsp->conn) == (uid_t)0) {
    1249           0 :                 return true;
    1250             :         }
    1251             : 
    1252             :         /*
    1253             :          * We can't directly use smbd_check_access_rights_fsp()
    1254             :          * here, as this implicitly grants FILE_READ_ATTRIBUTES
    1255             :          * which the Windows access-based-enumeration code
    1256             :          * explicitly checks for on the file security descriptor.
    1257             :          * See bug:
    1258             :          *
    1259             :          * https://bugzilla.samba.org/show_bug.cgi?id=10252
    1260             :          *
    1261             :          * and the smb2.acl2.ACCESSBASED test for details.
    1262             :          */
    1263             : 
    1264           0 :         rejected_share_access = access_mask & ~(fsp->conn->share_access);
    1265           0 :         if (rejected_share_access) {
    1266           0 :                 DBG_DEBUG("rejected share access 0x%x "
    1267             :                         "on %s (0x%x)\n",
    1268             :                         (unsigned int)access_mask,
    1269             :                         fsp_str_dbg(fsp),
    1270             :                         (unsigned int)rejected_share_access);
    1271           0 :                 return false;
    1272             :         }
    1273             : 
    1274           0 :         status = SMB_VFS_FGET_NT_ACL(metadata_fsp(fsp),
    1275             :                         (SECINFO_OWNER |
    1276             :                          SECINFO_GROUP |
    1277             :                          SECINFO_DACL),
    1278             :                         talloc_tos(),
    1279             :                         &sd);
    1280             : 
    1281           0 :         if (!NT_STATUS_IS_OK(status)) {
    1282           0 :                 DBG_DEBUG("Could not get acl "
    1283             :                         "on %s: %s\n",
    1284             :                         fsp_str_dbg(fsp),
    1285             :                         nt_errstr(status));
    1286           0 :                 return false;
    1287             :         }
    1288             : 
    1289           0 :         status = se_file_access_check(sd,
    1290           0 :                                 get_current_nttok(fsp->conn),
    1291             :                                 false,
    1292             :                                 access_mask,
    1293             :                                 &rejected_mask);
    1294             : 
    1295           0 :         TALLOC_FREE(sd);
    1296             : 
    1297           0 :         if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
    1298           0 :                 DBG_DEBUG("rejected bits 0x%x read access for %s\n",
    1299             :                         (unsigned int)rejected_mask,
    1300             :                         fsp_str_dbg(fsp));
    1301           0 :                 return false;
    1302             :         }
    1303           0 :         return true;
    1304             : }
    1305             : 
    1306             : /*******************************************************************
    1307             :  Check to see if a user can write to an fsp.
    1308             :  Always return true for directories.
    1309             :  This is only approximate,
    1310             :  it is used as part of the "hide unwriteable" option. Don't
    1311             :  use it for anything security sensitive.
    1312             : ********************************************************************/
    1313             : 
    1314           0 : static bool user_can_write_fsp(struct files_struct *fsp)
    1315             : {
    1316             :         /*
    1317             :          * Never hide files from the root user.
    1318             :          * We use (uid_t)0 here not sec_initial_uid()
    1319             :          * as make test uses a single user context.
    1320             :          */
    1321             : 
    1322           0 :         if (get_current_uid(fsp->conn) == (uid_t)0) {
    1323           0 :                 return true;
    1324             :         }
    1325             : 
    1326           0 :         if (fsp->fsp_flags.is_directory) {
    1327           0 :                 return true;
    1328             :         }
    1329             : 
    1330           0 :         return can_write_to_fsp(fsp);
    1331             : }
    1332             : 
    1333             : /*******************************************************************
    1334             :   Is a file a "special" type ?
    1335             : ********************************************************************/
    1336             : 
    1337           0 : static bool file_is_special(connection_struct *conn,
    1338             :                             const struct smb_filename *smb_fname)
    1339             : {
    1340             :         /*
    1341             :          * Never hide files from the root user.
    1342             :          * We use (uid_t)0 here not sec_initial_uid()
    1343             :          * as make test uses a single user context.
    1344             :          */
    1345             : 
    1346           0 :         if (get_current_uid(conn) == (uid_t)0) {
    1347           0 :                 return False;
    1348             :         }
    1349             : 
    1350           0 :         SMB_ASSERT(VALID_STAT(smb_fname->st));
    1351             : 
    1352           0 :         if (S_ISREG(smb_fname->st.st_ex_mode) ||
    1353           0 :             S_ISDIR(smb_fname->st.st_ex_mode) ||
    1354           0 :             S_ISLNK(smb_fname->st.st_ex_mode))
    1355           0 :                 return False;
    1356             : 
    1357           0 :         return True;
    1358             : }
    1359             : 
    1360             : /*******************************************************************
    1361             :  Should the file be seen by the client?
    1362             : ********************************************************************/
    1363             : 
    1364       10651 : bool is_visible_fsp(struct files_struct *fsp)
    1365             : {
    1366       10651 :         bool hide_unreadable = false;
    1367       10651 :         bool hide_unwriteable = false;
    1368       10651 :         bool hide_special = false;
    1369       10651 :         int hide_new_files_timeout = 0;
    1370       10651 :         const char *last_component = NULL;
    1371             : 
    1372             :         /*
    1373             :          * If the file does not exist, there's no point checking
    1374             :          * the configuration options. We succeed, on the basis that the
    1375             :          * checks *might* have passed if the file was present.
    1376             :          */
    1377       10651 :         if (fsp == NULL) {
    1378          80 :                 return true;
    1379             :         }
    1380             : 
    1381       10571 :         hide_unreadable = lp_hide_unreadable(SNUM(fsp->conn));
    1382       10571 :         hide_unwriteable = lp_hide_unwriteable_files(SNUM(fsp->conn));
    1383       10571 :         hide_special = lp_hide_special_files(SNUM(fsp->conn));
    1384       10571 :         hide_new_files_timeout = lp_hide_new_files_timeout(SNUM(fsp->conn));
    1385             : 
    1386       10571 :         if (!hide_unreadable &&
    1387       10571 :             !hide_unwriteable &&
    1388       10571 :             !hide_special &&
    1389             :             (hide_new_files_timeout == 0))
    1390             :         {
    1391       10571 :                 return true;
    1392             :         }
    1393             : 
    1394           0 :         fsp = metadata_fsp(fsp);
    1395             : 
    1396             :         /* Get the last component of the base name. */
    1397           0 :         last_component = strrchr_m(fsp->fsp_name->base_name, '/');
    1398           0 :         if (!last_component) {
    1399           0 :                 last_component = fsp->fsp_name->base_name;
    1400             :         } else {
    1401           0 :                 last_component++; /* Go past '/' */
    1402             :         }
    1403             : 
    1404           0 :         if (ISDOT(last_component) || ISDOTDOT(last_component)) {
    1405           0 :                 return true; /* . and .. are always visible. */
    1406             :         }
    1407             : 
    1408           0 :         if (fsp_get_pathref_fd(fsp) == -1) {
    1409             :                 /*
    1410             :                  * Symlink in POSIX mode or MS-DFS.
    1411             :                  * We've checked veto files so the
    1412             :                  * only thing we can check is the
    1413             :                  * hide_new_files_timeout.
    1414             :                  */
    1415           0 :                 if ((hide_new_files_timeout != 0) &&
    1416           0 :                     !S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
    1417           0 :                         double age = timespec_elapsed(
    1418           0 :                                 &fsp->fsp_name->st.st_ex_mtime);
    1419             : 
    1420           0 :                         if (age < (double)hide_new_files_timeout) {
    1421           0 :                                 return false;
    1422             :                         }
    1423             :                 }
    1424           0 :                 return true;
    1425             :         }
    1426             : 
    1427             :         /* Honour _hide unreadable_ option */
    1428           0 :         if (hide_unreadable && !user_can_read_fsp(fsp)) {
    1429           0 :                 DBG_DEBUG("file %s is unreadable.\n", fsp_str_dbg(fsp));
    1430           0 :                 return false;
    1431             :         }
    1432             : 
    1433             :         /* Honour _hide unwriteable_ option */
    1434           0 :         if (hide_unwriteable && !user_can_write_fsp(fsp)) {
    1435           0 :                 DBG_DEBUG("file %s is unwritable.\n", fsp_str_dbg(fsp));
    1436           0 :                 return false;
    1437             :         }
    1438             : 
    1439             :         /* Honour _hide_special_ option */
    1440           0 :         if (hide_special && file_is_special(fsp->conn, fsp->fsp_name)) {
    1441           0 :                 DBG_DEBUG("file %s is special.\n", fsp_str_dbg(fsp));
    1442           0 :                 return false;
    1443             :         }
    1444             : 
    1445           0 :         if ((hide_new_files_timeout != 0) &&
    1446           0 :             !S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
    1447           0 :                 double age = timespec_elapsed(&fsp->fsp_name->st.st_ex_mtime);
    1448             : 
    1449           0 :                 if (age < (double)hide_new_files_timeout) {
    1450           0 :                         return false;
    1451             :                 }
    1452             :         }
    1453             : 
    1454           0 :         return true;
    1455             : }
    1456             : 
    1457        6397 : static int smb_Dir_destructor(struct smb_Dir *dir_hnd)
    1458             : {
    1459        6397 :         files_struct *fsp = dir_hnd->fsp;
    1460             : 
    1461        6397 :         SMB_VFS_CLOSEDIR(dir_hnd->conn, dir_hnd->dir);
    1462        6397 :         fsp_set_fd(fsp, -1);
    1463        6397 :         if (fsp->dptr != NULL) {
    1464        2171 :                 SMB_ASSERT(fsp->dptr->dir_hnd == dir_hnd);
    1465        2171 :                 fsp->dptr->dir_hnd = NULL;
    1466             :         }
    1467        6397 :         dir_hnd->fsp = NULL;
    1468        6397 :         return 0;
    1469             : }
    1470             : 
    1471             : /*******************************************************************
    1472             :  Open a directory.
    1473             : ********************************************************************/
    1474             : 
    1475        4226 : static int smb_Dir_OpenDir_destructor(struct smb_Dir *dir_hnd)
    1476             : {
    1477        4226 :         files_struct *fsp = dir_hnd->fsp;
    1478             : 
    1479        4226 :         smb_Dir_destructor(dir_hnd);
    1480        4226 :         file_free(NULL, fsp);
    1481        4226 :         return 0;
    1482             : }
    1483             : 
    1484         846 : NTSTATUS OpenDir(TALLOC_CTX *mem_ctx,
    1485             :                  connection_struct *conn,
    1486             :                  const struct smb_filename *smb_dname,
    1487             :                  const char *mask,
    1488             :                  uint32_t attr,
    1489             :                  struct smb_Dir **_dir_hnd)
    1490             : {
    1491         846 :         struct files_struct *fsp = NULL;
    1492         846 :         struct smb_Dir *dir_hnd = NULL;
    1493             :         NTSTATUS status;
    1494             : 
    1495         846 :         status = open_internal_dirfsp(conn,
    1496             :                                       smb_dname,
    1497             :                                       O_RDONLY,
    1498             :                                       &fsp);
    1499         846 :         if (!NT_STATUS_IS_OK(status)) {
    1500           0 :                 return status;
    1501             :         }
    1502             : 
    1503         846 :         status = OpenDir_fsp(mem_ctx, conn, fsp, mask, attr, &dir_hnd);
    1504         846 :         if (!NT_STATUS_IS_OK(status)) {
    1505           0 :                 return status;
    1506             :         }
    1507             : 
    1508             :         /*
    1509             :          * This overwrites the destructor set by OpenDir_fsp() but
    1510             :          * smb_Dir_OpenDir_destructor() calls the OpenDir_fsp()
    1511             :          * destructor.
    1512             :          */
    1513         846 :         talloc_set_destructor(dir_hnd, smb_Dir_OpenDir_destructor);
    1514             : 
    1515         846 :         *_dir_hnd = dir_hnd;
    1516         846 :         return NT_STATUS_OK;
    1517             : }
    1518             : 
    1519        3380 : NTSTATUS OpenDir_from_pathref(TALLOC_CTX *mem_ctx,
    1520             :                               struct files_struct *dirfsp,
    1521             :                               const char *mask,
    1522             :                               uint32_t attr,
    1523             :                               struct smb_Dir **_dir_hnd)
    1524             : {
    1525        3380 :         struct files_struct *fsp = NULL;
    1526        3380 :         struct smb_Dir *dir_hnd = NULL;
    1527             :         NTSTATUS status;
    1528             : 
    1529        3380 :         status = openat_internal_dir_from_pathref(dirfsp, O_RDONLY, &fsp);
    1530        3380 :         if (!NT_STATUS_IS_OK(status)) {
    1531           0 :                 return status;
    1532             :         }
    1533             : 
    1534        3380 :         status = OpenDir_fsp(mem_ctx, fsp->conn, fsp, mask, attr, &dir_hnd);
    1535        3380 :         if (!NT_STATUS_IS_OK(status)) {
    1536           0 :                 return status;
    1537             :         }
    1538             : 
    1539             :         /*
    1540             :          * This overwrites the destructor set by OpenDir_fsp() but
    1541             :          * smb_Dir_OpenDir_destructor() calls the OpenDir_fsp()
    1542             :          * destructor.
    1543             :          */
    1544        3380 :         talloc_set_destructor(dir_hnd, smb_Dir_OpenDir_destructor);
    1545             : 
    1546        3380 :         *_dir_hnd = dir_hnd;
    1547        3380 :         return NT_STATUS_OK;
    1548             : }
    1549             : 
    1550             : /*******************************************************************
    1551             :  Open a directory from an fsp.
    1552             : ********************************************************************/
    1553             : 
    1554        6397 : static NTSTATUS OpenDir_fsp(
    1555             :         TALLOC_CTX *mem_ctx,
    1556             :         connection_struct *conn,
    1557             :         files_struct *fsp,
    1558             :         const char *mask,
    1559             :         uint32_t attr,
    1560             :         struct smb_Dir **_dir_hnd)
    1561             : {
    1562        6397 :         struct smb_Dir *dir_hnd = talloc_zero(mem_ctx, struct smb_Dir);
    1563             :         NTSTATUS status;
    1564             : 
    1565        6397 :         if (!dir_hnd) {
    1566           0 :                 return NT_STATUS_NO_MEMORY;
    1567             :         }
    1568             : 
    1569        6397 :         if (!fsp->fsp_flags.is_directory) {
    1570           0 :                 status = NT_STATUS_INVALID_HANDLE;
    1571           0 :                 goto fail;
    1572             :         }
    1573             : 
    1574        6397 :         if (fsp_get_io_fd(fsp) == -1) {
    1575           0 :                 status = NT_STATUS_INVALID_HANDLE;
    1576           0 :                 goto fail;
    1577             :         }
    1578             : 
    1579        6397 :         dir_hnd->conn = conn;
    1580             : 
    1581        6397 :         if (!conn->sconn->using_smb2) {
    1582             :                 /*
    1583             :                  * The dircache is only needed for SMB1 because SMB1 uses a name
    1584             :                  * for the resume wheras SMB2 always continues from the next
    1585             :                  * position (unless it's told to restart or close-and-reopen the
    1586             :                  * listing).
    1587             :                  */
    1588         836 :                 dir_hnd->name_cache_size =
    1589         836 :                         lp_directory_name_cache_size(SNUM(conn));
    1590             :         }
    1591             : 
    1592        6397 :         dir_hnd->dir_smb_fname = cp_smb_filename(dir_hnd, fsp->fsp_name);
    1593        6397 :         if (!dir_hnd->dir_smb_fname) {
    1594           0 :                 status = NT_STATUS_NO_MEMORY;
    1595           0 :                 goto fail;
    1596             :         }
    1597             : 
    1598        6397 :         dir_hnd->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr);
    1599        6397 :         if (dir_hnd->dir == NULL) {
    1600           0 :                 status = map_nt_error_from_unix(errno);
    1601           0 :                 goto fail;
    1602             :         }
    1603        6397 :         dir_hnd->fsp = fsp;
    1604        6397 :         if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) {
    1605           0 :                 dir_hnd->case_sensitive = true;
    1606             :         } else {
    1607        6397 :                 dir_hnd->case_sensitive = conn->case_sensitive;
    1608             :         }
    1609             : 
    1610        6397 :         talloc_set_destructor(dir_hnd, smb_Dir_destructor);
    1611             : 
    1612        6397 :         *_dir_hnd = dir_hnd;
    1613        6397 :         return NT_STATUS_OK;
    1614             : 
    1615           0 :   fail:
    1616           0 :         TALLOC_FREE(dir_hnd);
    1617           0 :         return status;
    1618             : }
    1619             : 
    1620             : 
    1621             : /*******************************************************************
    1622             :  Read from a directory.
    1623             :  Return directory entry, current offset, and optional stat information.
    1624             :  Don't check for veto or invisible files.
    1625             : ********************************************************************/
    1626             : 
    1627       39101 : const char *ReadDirName(struct smb_Dir *dir_hnd, long *poffset,
    1628             :                         SMB_STRUCT_STAT *sbuf, char **ptalloced)
    1629             : {
    1630             :         const char *n;
    1631       39101 :         char *talloced = NULL;
    1632       39101 :         connection_struct *conn = dir_hnd->conn;
    1633             : 
    1634             :         /* Cheat to allow . and .. to be the first entries returned. */
    1635       39101 :         if (((*poffset == START_OF_DIRECTORY_OFFSET) ||
    1636       26533 :              (*poffset == DOT_DOT_DIRECTORY_OFFSET)) &&
    1637       18852 :             (dir_hnd->file_number < 2))
    1638             :         {
    1639       12568 :                 if (dir_hnd->file_number == 0) {
    1640        6284 :                         n = ".";
    1641        6284 :                         *poffset = dir_hnd->offset = START_OF_DIRECTORY_OFFSET;
    1642             :                 } else {
    1643        6284 :                         n = "..";
    1644        6284 :                         *poffset = dir_hnd->offset = DOT_DOT_DIRECTORY_OFFSET;
    1645             :                 }
    1646       12568 :                 dir_hnd->file_number++;
    1647       12568 :                 *ptalloced = NULL;
    1648       12568 :                 return n;
    1649             :         }
    1650             : 
    1651       26533 :         if (*poffset == END_OF_DIRECTORY_OFFSET) {
    1652        2241 :                 *poffset = dir_hnd->offset = END_OF_DIRECTORY_OFFSET;
    1653        2241 :                 return NULL;
    1654             :         }
    1655             : 
    1656             :         /* A real offset, seek to it. */
    1657       24292 :         SeekDir(dir_hnd, *poffset);
    1658             : 
    1659       33866 :         while ((n = vfs_readdirname(conn, dir_hnd->fsp, dir_hnd->dir, sbuf, &talloced))) {
    1660             :                 /* Ignore . and .. - we've already returned them. */
    1661       29091 :                 if (ISDOT(n) || ISDOTDOT(n)) {
    1662        9574 :                         TALLOC_FREE(talloced);
    1663        9574 :                         continue;
    1664             :                 }
    1665       19517 :                 *poffset = dir_hnd->offset = SMB_VFS_TELLDIR(conn, dir_hnd->dir);
    1666       19517 :                 *ptalloced = talloced;
    1667       19517 :                 dir_hnd->file_number++;
    1668       19517 :                 return n;
    1669             :         }
    1670        4775 :         *poffset = dir_hnd->offset = END_OF_DIRECTORY_OFFSET;
    1671        4775 :         *ptalloced = NULL;
    1672        4775 :         return NULL;
    1673             : }
    1674             : 
    1675             : /*******************************************************************
    1676             :  Rewind to the start.
    1677             : ********************************************************************/
    1678             : 
    1679           0 : void RewindDir(struct smb_Dir *dir_hnd, long *poffset)
    1680             : {
    1681           0 :         SMB_VFS_REWINDDIR(dir_hnd->conn, dir_hnd->dir);
    1682           0 :         dir_hnd->file_number = 0;
    1683           0 :         dir_hnd->offset = START_OF_DIRECTORY_OFFSET;
    1684           0 :         *poffset = START_OF_DIRECTORY_OFFSET;
    1685           0 : }
    1686             : 
    1687             : /*******************************************************************
    1688             :  Seek a dir.
    1689             : ********************************************************************/
    1690             : 
    1691       24292 : void SeekDir(struct smb_Dir *dirp, long offset)
    1692             : {
    1693       24292 :         if (offset != dirp->offset) {
    1694           0 :                 if (offset == START_OF_DIRECTORY_OFFSET) {
    1695           0 :                         RewindDir(dirp, &offset);
    1696             :                         /*
    1697             :                          * Ok we should really set the file number here
    1698             :                          * to 1 to enable ".." to be returned next. Trouble
    1699             :                          * is I'm worried about callers using SeekDir(dirp,0)
    1700             :                          * as equivalent to RewindDir(). So leave this alone
    1701             :                          * for now.
    1702             :                          */
    1703           0 :                 } else if  (offset == DOT_DOT_DIRECTORY_OFFSET) {
    1704           0 :                         RewindDir(dirp, &offset);
    1705             :                         /*
    1706             :                          * Set the file number to 2 - we want to get the first
    1707             :                          * real file entry (the one we return after "..")
    1708             :                          * on the next ReadDir.
    1709             :                          */
    1710           0 :                         dirp->file_number = 2;
    1711           0 :                 } else if (offset == END_OF_DIRECTORY_OFFSET) {
    1712             :                         ; /* Don't seek in this case. */
    1713             :                 } else {
    1714           0 :                         SMB_VFS_SEEKDIR(dirp->conn, dirp->dir, offset);
    1715             :                 }
    1716           0 :                 dirp->offset = offset;
    1717             :         }
    1718       24292 : }
    1719             : 
    1720             : /*******************************************************************
    1721             :  Tell a dir position.
    1722             : ********************************************************************/
    1723             : 
    1724       15947 : long TellDir(struct smb_Dir *dir_hnd)
    1725             : {
    1726       15947 :         return(dir_hnd->offset);
    1727             : }
    1728             : 
    1729             : /*******************************************************************
    1730             :  Add an entry into the dcache.
    1731             : ********************************************************************/
    1732             : 
    1733           0 : static void DirCacheAdd(struct smb_Dir *dir_hnd, const char *name, long offset)
    1734             : {
    1735             :         struct name_cache_entry *e;
    1736             : 
    1737           0 :         if (dir_hnd->name_cache_size == 0) {
    1738           0 :                 return;
    1739             :         }
    1740             : 
    1741           0 :         if (dir_hnd->name_cache == NULL) {
    1742           0 :                 dir_hnd->name_cache = talloc_zero_array(dir_hnd,
    1743             :                                                 struct name_cache_entry,
    1744             :                                                 dir_hnd->name_cache_size);
    1745             : 
    1746           0 :                 if (dir_hnd->name_cache == NULL) {
    1747           0 :                         return;
    1748             :                 }
    1749             :         }
    1750             : 
    1751           0 :         dir_hnd->name_cache_index = (dir_hnd->name_cache_index+1) %
    1752           0 :                                         dir_hnd->name_cache_size;
    1753           0 :         e = &dir_hnd->name_cache[dir_hnd->name_cache_index];
    1754           0 :         TALLOC_FREE(e->name);
    1755           0 :         e->name = talloc_strdup(dir_hnd, name);
    1756           0 :         e->offset = offset;
    1757             : }
    1758             : 
    1759             : /*******************************************************************
    1760             :  Find an entry by name. Leave us at the offset after it.
    1761             :  Don't check for veto or invisible files.
    1762             : ********************************************************************/
    1763             : 
    1764           0 : static bool SearchDir(struct smb_Dir *dir_hnd, const char *name, long *poffset)
    1765             : {
    1766             :         int i;
    1767           0 :         const char *entry = NULL;
    1768           0 :         char *talloced = NULL;
    1769           0 :         connection_struct *conn = dir_hnd->conn;
    1770             : 
    1771             :         /* Search back in the name cache. */
    1772           0 :         if (dir_hnd->name_cache_size && dir_hnd->name_cache) {
    1773           0 :                 for (i = dir_hnd->name_cache_index; i >= 0; i--) {
    1774           0 :                         struct name_cache_entry *e = &dir_hnd->name_cache[i];
    1775           0 :                         if (e->name && (dir_hnd->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) {
    1776           0 :                                 *poffset = e->offset;
    1777           0 :                                 SeekDir(dir_hnd, e->offset);
    1778           0 :                                 return True;
    1779             :                         }
    1780             :                 }
    1781           0 :                 for (i = dir_hnd->name_cache_size - 1;
    1782           0 :                                 i > dir_hnd->name_cache_index; i--) {
    1783           0 :                         struct name_cache_entry *e = &dir_hnd->name_cache[i];
    1784           0 :                         if (e->name && (dir_hnd->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) {
    1785           0 :                                 *poffset = e->offset;
    1786           0 :                                 SeekDir(dir_hnd, e->offset);
    1787           0 :                                 return True;
    1788             :                         }
    1789             :                 }
    1790             :         }
    1791             : 
    1792             :         /* Not found in the name cache. Rewind directory and start from scratch. */
    1793           0 :         SMB_VFS_REWINDDIR(conn, dir_hnd->dir);
    1794           0 :         dir_hnd->file_number = 0;
    1795           0 :         *poffset = START_OF_DIRECTORY_OFFSET;
    1796           0 :         while ((entry = ReadDirName(dir_hnd, poffset, NULL, &talloced))) {
    1797           0 :                 if (dir_hnd->case_sensitive ? (strcmp(entry, name) == 0) : strequal(entry, name)) {
    1798           0 :                         TALLOC_FREE(talloced);
    1799           0 :                         return True;
    1800             :                 }
    1801           0 :                 TALLOC_FREE(talloced);
    1802             :         }
    1803           0 :         return False;
    1804             : }
    1805             : 
    1806             : struct files_below_forall_state {
    1807             :         char *dirpath;
    1808             :         ssize_t dirpath_len;
    1809             :         int (*fn)(struct file_id fid, const struct share_mode_data *data,
    1810             :                   void *private_data);
    1811             :         void *private_data;
    1812             : };
    1813             : 
    1814         132 : static int files_below_forall_fn(struct file_id fid,
    1815             :                                  const struct share_mode_data *data,
    1816             :                                  void *private_data)
    1817             : {
    1818         132 :         struct files_below_forall_state *state = private_data;
    1819             :         char tmpbuf[PATH_MAX];
    1820             :         char *fullpath, *to_free;
    1821             :         ssize_t len;
    1822             : 
    1823         132 :         len = full_path_tos(data->servicepath, data->base_name,
    1824             :                             tmpbuf, sizeof(tmpbuf),
    1825             :                             &fullpath, &to_free);
    1826         132 :         if (len == -1) {
    1827           0 :                 return 0;
    1828             :         }
    1829         132 :         if (state->dirpath_len >= len) {
    1830             :                 /*
    1831             :                  * Filter files above dirpath
    1832             :                  */
    1833         128 :                 goto out;
    1834             :         }
    1835           4 :         if (fullpath[state->dirpath_len] != '/') {
    1836             :                 /*
    1837             :                  * Filter file that don't have a path separator at the end of
    1838             :                  * dirpath's length
    1839             :                  */
    1840           4 :                 goto out;
    1841             :         }
    1842             : 
    1843           0 :         if (memcmp(state->dirpath, fullpath, state->dirpath_len) != 0) {
    1844             :                 /*
    1845             :                  * Not a parent
    1846             :                  */
    1847           0 :                 goto out;
    1848             :         }
    1849             : 
    1850           0 :         TALLOC_FREE(to_free);
    1851           0 :         return state->fn(fid, data, state->private_data);
    1852             : 
    1853         132 : out:
    1854         132 :         TALLOC_FREE(to_free);
    1855         132 :         return 0;
    1856             : }
    1857             : 
    1858         104 : static int files_below_forall(connection_struct *conn,
    1859             :                               const struct smb_filename *dir_name,
    1860             :                               int (*fn)(struct file_id fid,
    1861             :                                         const struct share_mode_data *data,
    1862             :                                         void *private_data),
    1863             :                               void *private_data)
    1864             : {
    1865         104 :         struct files_below_forall_state state = {
    1866             :                         .fn = fn,
    1867             :                         .private_data = private_data,
    1868             :         };
    1869             :         int ret;
    1870             :         char tmpbuf[PATH_MAX];
    1871             :         char *to_free;
    1872             : 
    1873         208 :         state.dirpath_len = full_path_tos(conn->connectpath,
    1874         104 :                                           dir_name->base_name,
    1875             :                                           tmpbuf, sizeof(tmpbuf),
    1876             :                                           &state.dirpath, &to_free);
    1877         104 :         if (state.dirpath_len == -1) {
    1878           0 :                 return -1;
    1879             :         }
    1880             : 
    1881         104 :         ret = share_mode_forall(files_below_forall_fn, &state);
    1882         104 :         TALLOC_FREE(to_free);
    1883         104 :         return ret;
    1884             : }
    1885             : 
    1886             : struct have_file_open_below_state {
    1887             :         bool found_one;
    1888             : };
    1889             : 
    1890           0 : static int have_file_open_below_fn(struct file_id fid,
    1891             :                                    const struct share_mode_data *data,
    1892             :                                    void *private_data)
    1893             : {
    1894           0 :         struct have_file_open_below_state *state = private_data;
    1895           0 :         state->found_one = true;
    1896           0 :         return 1;
    1897             : }
    1898             : 
    1899         104 : bool have_file_open_below(connection_struct *conn,
    1900             :                                  const struct smb_filename *name)
    1901             : {
    1902         104 :         struct have_file_open_below_state state = {
    1903             :                 .found_one = false,
    1904             :         };
    1905             :         int ret;
    1906             : 
    1907         104 :         if (!VALID_STAT(name->st)) {
    1908           0 :                 return false;
    1909             :         }
    1910         104 :         if (!S_ISDIR(name->st.st_ex_mode)) {
    1911           0 :                 return false;
    1912             :         }
    1913             : 
    1914         104 :         ret = files_below_forall(conn, name, have_file_open_below_fn, &state);
    1915         104 :         if (ret == -1) {
    1916           0 :                 return false;
    1917             :         }
    1918             : 
    1919         104 :         return state.found_one;
    1920             : }
    1921             : 
    1922             : /*****************************************************************
    1923             :  Is this directory empty ?
    1924             : *****************************************************************/
    1925             : 
    1926         814 : NTSTATUS can_delete_directory_fsp(files_struct *fsp)
    1927             : {
    1928         814 :         NTSTATUS status = NT_STATUS_OK;
    1929         814 :         long dirpos = 0;
    1930         814 :         const char *dname = NULL;
    1931         814 :         char *talloced = NULL;
    1932         814 :         struct connection_struct *conn = fsp->conn;
    1933         814 :         struct smb_Dir *dir_hnd = NULL;
    1934             : 
    1935         814 :         status = OpenDir(
    1936         814 :                 talloc_tos(), conn, fsp->fsp_name, NULL, 0, &dir_hnd);
    1937         814 :         if (!NT_STATUS_IS_OK(status)) {
    1938           0 :                 return status;
    1939             :         }
    1940             : 
    1941        2442 :         while ((dname = ReadDirName(dir_hnd, &dirpos, NULL, &talloced))) {
    1942        1634 :                 struct smb_filename *smb_dname_full = NULL;
    1943        1634 :                 struct smb_filename *direntry_fname = NULL;
    1944        1634 :                 char *fullname = NULL;
    1945             :                 int ret;
    1946             : 
    1947        1634 :                 if (ISDOT(dname) || (ISDOTDOT(dname))) {
    1948        1628 :                         TALLOC_FREE(talloced);
    1949        1628 :                         continue;
    1950             :                 }
    1951           6 :                 if (IS_VETO_PATH(conn, dname)) {
    1952           0 :                         TALLOC_FREE(talloced);
    1953           0 :                         continue;
    1954             :                 }
    1955             : 
    1956           6 :                 fullname = talloc_asprintf(talloc_tos(),
    1957             :                                            "%s/%s",
    1958           6 :                                            fsp->fsp_name->base_name,
    1959             :                                            dname);
    1960           6 :                 if (fullname == NULL) {
    1961           0 :                         status = NT_STATUS_NO_MEMORY;
    1962           0 :                         break;
    1963             :                 }
    1964             : 
    1965           6 :                 smb_dname_full = synthetic_smb_fname(talloc_tos(),
    1966             :                                                      fullname,
    1967             :                                                      NULL,
    1968             :                                                      NULL,
    1969           6 :                                                      fsp->fsp_name->twrp,
    1970           6 :                                                      fsp->fsp_name->flags);
    1971           6 :                 if (smb_dname_full == NULL) {
    1972           0 :                         TALLOC_FREE(talloced);
    1973           0 :                         TALLOC_FREE(fullname);
    1974           0 :                         status = NT_STATUS_NO_MEMORY;
    1975           0 :                         break;
    1976             :                 }
    1977             : 
    1978           6 :                 ret = SMB_VFS_LSTAT(conn, smb_dname_full);
    1979           6 :                 if (ret != 0) {
    1980           0 :                         status = map_nt_error_from_unix(errno);
    1981           0 :                         TALLOC_FREE(talloced);
    1982           0 :                         TALLOC_FREE(fullname);
    1983           0 :                         TALLOC_FREE(smb_dname_full);
    1984           0 :                         break;
    1985             :                 }
    1986             : 
    1987           6 :                 if (S_ISLNK(smb_dname_full->st.st_ex_mode)) {
    1988             :                         /* Could it be an msdfs link ? */
    1989           0 :                         if (lp_host_msdfs() &&
    1990           0 :                             lp_msdfs_root(SNUM(conn))) {
    1991             :                                 struct smb_filename *smb_dname;
    1992           0 :                                 smb_dname = synthetic_smb_fname(talloc_tos(),
    1993             :                                                         dname,
    1994             :                                                         NULL,
    1995           0 :                                                         &smb_dname_full->st,
    1996           0 :                                                         fsp->fsp_name->twrp,
    1997           0 :                                                         fsp->fsp_name->flags);
    1998           0 :                                 if (smb_dname == NULL) {
    1999           0 :                                         TALLOC_FREE(talloced);
    2000           0 :                                         TALLOC_FREE(fullname);
    2001           0 :                                         TALLOC_FREE(smb_dname_full);
    2002           0 :                                         status = NT_STATUS_NO_MEMORY;
    2003           0 :                                         break;
    2004             :                                 }
    2005           0 :                                 if (is_msdfs_link(fsp, smb_dname)) {
    2006           0 :                                         TALLOC_FREE(talloced);
    2007           0 :                                         TALLOC_FREE(fullname);
    2008           0 :                                         TALLOC_FREE(smb_dname_full);
    2009           0 :                                         TALLOC_FREE(smb_dname);
    2010           0 :                                         DBG_DEBUG("got msdfs link name %s "
    2011             :                                                 "- can't delete directory %s\n",
    2012             :                                                 dname,
    2013             :                                                 fsp_str_dbg(fsp));
    2014           0 :                                         status = NT_STATUS_DIRECTORY_NOT_EMPTY;
    2015           0 :                                         break;
    2016             :                                 }
    2017           0 :                                 TALLOC_FREE(smb_dname);
    2018             :                         }
    2019             :                         /* Not a DFS link - could it be a dangling symlink ? */
    2020           0 :                         ret = SMB_VFS_STAT(conn, smb_dname_full);
    2021           0 :                         if (ret == -1 && (errno == ENOENT || errno == ELOOP)) {
    2022             :                                 /*
    2023             :                                  * Dangling symlink.
    2024             :                                  * Allow if "delete veto files = yes"
    2025             :                                  */
    2026           0 :                                 if (lp_delete_veto_files(SNUM(conn))) {
    2027           0 :                                         TALLOC_FREE(talloced);
    2028           0 :                                         TALLOC_FREE(fullname);
    2029           0 :                                         TALLOC_FREE(smb_dname_full);
    2030           0 :                                         continue;
    2031             :                                 }
    2032             :                         }
    2033           0 :                         DBG_DEBUG("got symlink name %s - "
    2034             :                                 "can't delete directory %s\n",
    2035             :                                 dname,
    2036             :                                 fsp_str_dbg(fsp));
    2037           0 :                         TALLOC_FREE(talloced);
    2038           0 :                         TALLOC_FREE(fullname);
    2039           0 :                         TALLOC_FREE(smb_dname_full);
    2040           0 :                         status = NT_STATUS_DIRECTORY_NOT_EMPTY;
    2041           0 :                         break;
    2042             :                 }
    2043             : 
    2044             :                 /* Not a symlink, get a pathref. */
    2045           6 :                 status = synthetic_pathref(talloc_tos(),
    2046             :                                            fsp,
    2047             :                                            dname,
    2048             :                                            NULL,
    2049           6 :                                            &smb_dname_full->st,
    2050           6 :                                            fsp->fsp_name->twrp,
    2051           6 :                                            fsp->fsp_name->flags,
    2052             :                                            &direntry_fname);
    2053           6 :                 if (!NT_STATUS_IS_OK(status)) {
    2054           0 :                         status = map_nt_error_from_unix(errno);
    2055           0 :                         TALLOC_FREE(talloced);
    2056           0 :                         TALLOC_FREE(fullname);
    2057           0 :                         TALLOC_FREE(smb_dname_full);
    2058           0 :                         break;
    2059             :                 }
    2060             : 
    2061           6 :                 if (!is_visible_fsp(direntry_fname->fsp)) {
    2062             :                         /*
    2063             :                          * Hidden file.
    2064             :                          * Allow if "delete veto files = yes"
    2065             :                          */
    2066           0 :                         if (lp_delete_veto_files(SNUM(conn))) {
    2067           0 :                                 TALLOC_FREE(talloced);
    2068           0 :                                 TALLOC_FREE(fullname);
    2069           0 :                                 TALLOC_FREE(smb_dname_full);
    2070           0 :                                 TALLOC_FREE(direntry_fname);
    2071           0 :                                 continue;
    2072             :                         }
    2073             :                 }
    2074             : 
    2075           6 :                 TALLOC_FREE(talloced);
    2076           6 :                 TALLOC_FREE(fullname);
    2077           6 :                 TALLOC_FREE(smb_dname_full);
    2078           6 :                 TALLOC_FREE(direntry_fname);
    2079             : 
    2080           6 :                 DBG_DEBUG("got name %s - can't delete\n", dname);
    2081           6 :                 status = NT_STATUS_DIRECTORY_NOT_EMPTY;
    2082           6 :                 break;
    2083             :         }
    2084         814 :         TALLOC_FREE(talloced);
    2085         814 :         TALLOC_FREE(dir_hnd);
    2086             : 
    2087         814 :         if (!NT_STATUS_IS_OK(status)) {
    2088           6 :                 return status;
    2089             :         }
    2090             : 
    2091        1616 :         if (!(fsp->posix_flags & FSP_POSIX_FLAGS_RENAME) &&
    2092         908 :             lp_strict_rename(SNUM(conn)) &&
    2093         100 :             have_file_open_below(fsp->conn, fsp->fsp_name))
    2094             :         {
    2095           0 :                 return NT_STATUS_ACCESS_DENIED;
    2096             :         }
    2097             : 
    2098         808 :         return NT_STATUS_OK;
    2099             : }

Generated by: LCOV version 1.14