LCOV - code coverage report
Current view: top level - source4/ntvfs/posix - pvfs_dirlist.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 129 182 70.9 %
Date: 2024-04-21 15:09:00 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    Copyright (C) Andrew Tridgell 2004
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : /*
      20             :   directory listing functions for posix backend
      21             : */
      22             : 
      23             : #include "includes.h"
      24             : #include "vfs_posix.h"
      25             : #include "system/dir.h"
      26             : 
      27             : #define NAME_CACHE_SIZE 100
      28             : 
      29             : struct name_cache_entry {
      30             :         char *name;
      31             :         off_t offset;
      32             : };
      33             : 
      34             : struct pvfs_dir {
      35             :         struct pvfs_state *pvfs;
      36             :         bool no_wildcard;
      37             :         char *single_name;
      38             :         const char *pattern;
      39             :         off_t offset;
      40             :         DIR *dir;
      41             :         const char *unix_path;
      42             :         bool end_of_search;
      43             :         struct name_cache_entry *name_cache;
      44             :         uint32_t name_cache_index;
      45             : };
      46             : 
      47             : /* these three numbers are chosen to minimise the chances of a bad
      48             :    interaction with the OS value for 'end of directory'. On IRIX
      49             :    telldir() returns 0xFFFFFFFF at the end of a directory, and that
      50             :    caused an infinite loop with the original values of 0,1,2
      51             : 
      52             :    On XFS on linux telldir returns 0x7FFFFFFF at the end of a
      53             :    directory. Thus the change from 0x80000002, as otherwise
      54             :    0x7FFFFFFF+0x80000002==1==DIR_OFFSET_DOTDOT
      55             : */
      56             : #define DIR_OFFSET_DOT    0
      57             : #define DIR_OFFSET_DOTDOT 1
      58             : #define DIR_OFFSET_BASE   0x80000022
      59             : 
      60             : /*
      61             :   a special directory listing case where the pattern has no wildcard. We can just do a single stat()
      62             :   thus avoiding the more expensive directory scan
      63             : */
      64        4187 : static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name,
      65             :                                       const char *pattern, struct pvfs_dir *dir)
      66             : {
      67        4187 :         if (!name->exists) {
      68           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
      69             :         }
      70             : 
      71        4187 :         dir->pvfs = pvfs;
      72        4187 :         dir->no_wildcard = true;
      73        4187 :         dir->end_of_search = false;
      74        4187 :         dir->unix_path = talloc_strdup(dir, name->full_name);
      75        4187 :         if (!dir->unix_path) {
      76           0 :                 return NT_STATUS_NO_MEMORY;
      77             :         }
      78             : 
      79        4187 :         dir->single_name = talloc_strdup(dir, pattern);
      80        4187 :         if (!dir->single_name) {
      81           0 :                 return NT_STATUS_NO_MEMORY;
      82             :         }
      83             : 
      84        4187 :         dir->dir = NULL;
      85        4187 :         dir->offset = 0;
      86        4187 :         dir->pattern = NULL;
      87             : 
      88        4187 :         return NT_STATUS_OK;
      89             : }
      90             : 
      91             : /*
      92             :   destroy an open search
      93             : */
      94        5399 : static int pvfs_dirlist_destructor(struct pvfs_dir *dir)
      95             : {
      96        5399 :         if (dir->dir) closedir(dir->dir);
      97        5399 :         return 0;
      98             : }
      99             : 
     100             : /*
     101             :   start to read a directory
     102             : 
     103             :   if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
     104             : */
     105        9588 : NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name,
     106             :                          TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
     107             : {
     108           0 :         char *pattern;
     109           0 :         struct pvfs_dir *dir;
     110             : 
     111        9588 :         (*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
     112        9588 :         if (*dirp == NULL) {
     113           0 :                 return NT_STATUS_NO_MEMORY;
     114             :         }
     115             : 
     116        9588 :         dir = *dirp;
     117             : 
     118             :         /* split the unix path into a directory + pattern */
     119        9588 :         pattern = strrchr(name->full_name, '/');
     120        9588 :         if (!pattern) {
     121             :                 /* this should not happen, as pvfs_unix_path is supposed to
     122             :                    return an absolute path */
     123           0 :                 return NT_STATUS_UNSUCCESSFUL;
     124             :         }
     125             : 
     126        9588 :         *pattern++ = 0;
     127             : 
     128        9588 :         if (!name->has_wildcard) {
     129        4187 :                 return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
     130             :         }
     131             : 
     132        5401 :         dir->unix_path = talloc_strdup(dir, name->full_name);
     133        5401 :         if (!dir->unix_path) {
     134           0 :                 return NT_STATUS_NO_MEMORY;
     135             :         }
     136             : 
     137        5401 :         dir->pattern = talloc_strdup(dir, pattern);
     138        5401 :         if (dir->pattern == NULL) {
     139           0 :                 return NT_STATUS_NO_MEMORY;
     140             :         }
     141             : 
     142        5401 :         dir->dir = opendir(name->full_name);
     143        5401 :         if (!dir->dir) {
     144           2 :                 return pvfs_map_errno(pvfs, errno);
     145             :         }
     146             : 
     147        5399 :         dir->pvfs = pvfs;
     148        5399 :         dir->no_wildcard = false;
     149        5399 :         dir->end_of_search = false;
     150        5399 :         dir->offset = DIR_OFFSET_DOT;
     151        5399 :         dir->name_cache = talloc_zero_array(dir,
     152             :                                             struct name_cache_entry,
     153             :                                             NAME_CACHE_SIZE);
     154        5399 :         if (dir->name_cache == NULL) {
     155           0 :                 talloc_free(dir);
     156           0 :                 return NT_STATUS_NO_MEMORY;
     157             :         }
     158             : 
     159        5399 :         talloc_set_destructor(dir, pvfs_dirlist_destructor);
     160             : 
     161        5399 :         return NT_STATUS_OK;
     162             : }
     163             : 
     164             : /*
     165             :   add an entry to the local cache
     166             : */
     167      127156 : static void dcache_add(struct pvfs_dir *dir, const char *name)
     168             : {
     169           0 :         struct name_cache_entry *e;
     170             : 
     171      127156 :         dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
     172      127156 :         e = &dir->name_cache[dir->name_cache_index];
     173             : 
     174      127156 :         if (e->name) talloc_free(e->name);
     175             : 
     176      127156 :         e->name = talloc_strdup(dir->name_cache, name);
     177      127156 :         e->offset = dir->offset;
     178      127156 : }
     179             : 
     180             : /*
     181             :    return the next entry
     182             : */
     183      139134 : const char *pvfs_list_next(struct pvfs_dir *dir, off_t *ofs)
     184             : {
     185           0 :         struct dirent *de;
     186      139134 :         enum protocol_types protocol = dir->pvfs->ntvfs->ctx->protocol;
     187             : 
     188             :         /* non-wildcard searches are easy */
     189      139134 :         if (dir->no_wildcard) {
     190        8172 :                 dir->end_of_search = true;
     191        8172 :                 if (*ofs != 0) return NULL;
     192        4187 :                 (*ofs)++;
     193        4187 :                 return dir->single_name;
     194             :         }
     195             : 
     196             :         /* . and .. are handled separately as some unix systems will
     197             :            not return them first in a directory, but windows client
     198             :            may assume that these entries always appear first */
     199      130962 :         if (*ofs == DIR_OFFSET_DOT) {
     200        5399 :                 (*ofs) = DIR_OFFSET_DOTDOT;
     201        5399 :                 dir->offset = *ofs;
     202        5399 :                 if (ms_fnmatch_protocol(dir->pattern, ".", protocol,
     203             :                                         false) == 0) {
     204        5198 :                         dcache_add(dir, ".");
     205        5198 :                         return ".";
     206             :                 }
     207             :         }
     208             : 
     209      125764 :         if (*ofs == DIR_OFFSET_DOTDOT) {
     210        3361 :                 (*ofs) = DIR_OFFSET_BASE;
     211        3361 :                 dir->offset = *ofs;
     212        3361 :                 if (ms_fnmatch_protocol(dir->pattern, "..", protocol,
     213             :                                         false) == 0) {
     214        3160 :                         dcache_add(dir, "..");
     215        3160 :                         return "..";
     216             :                 }
     217             :         }
     218             : 
     219      122604 :         if (*ofs == DIR_OFFSET_BASE) {
     220        3427 :                 rewinddir(dir->dir);
     221      119177 :         } else if (*ofs != dir->offset) {
     222         195 :                 seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
     223             :         }
     224      122604 :         dir->offset = *ofs;
     225             : 
     226      129993 :         while ((de = readdir(dir->dir))) {
     227      126187 :                 const char *dname = de->d_name;
     228             : 
     229      126187 :                 if (ISDOT(dname) || ISDOTDOT(dname)) {
     230        6830 :                         continue;
     231             :                 }
     232             : 
     233      119357 :                 if (ms_fnmatch_protocol(dir->pattern, dname, protocol,
     234             :                                         false) != 0) {
     235         560 :                         char *short_name = pvfs_short_name_component(dir->pvfs, dname);
     236         712 :                         if (short_name == NULL ||
     237         152 :                             ms_fnmatch_protocol(dir->pattern, short_name,
     238             :                                                 protocol, false) != 0) {
     239         559 :                                 talloc_free(short_name);
     240         559 :                                 continue;
     241             :                         }
     242           1 :                         talloc_free(short_name);
     243             :                 }
     244             : 
     245             :                 /* Casting is necessary to avoid signed integer overflow. */
     246      118798 :                 dir->offset = (unsigned long)telldir(dir->dir) + (unsigned long)DIR_OFFSET_BASE;
     247      118798 :                 (*ofs) = dir->offset;
     248             : 
     249      118798 :                 dcache_add(dir, dname);
     250             : 
     251      118798 :                 return dname;
     252             :         }
     253             : 
     254        3806 :         dir->end_of_search = true;
     255        3806 :         return NULL;
     256             : }
     257             : 
     258             : /*
     259             :   return unix directory of an open search
     260             : */
     261      129301 : const char *pvfs_list_unix_path(struct pvfs_dir *dir)
     262             : {
     263      129301 :         return dir->unix_path;
     264             : }
     265             : 
     266             : /*
     267             :   return true if end of search has been reached
     268             : */
     269        3158 : bool pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
     270             : {
     271        3158 :         return dir->end_of_search;
     272             : }
     273             : 
     274             : /*
     275             :   seek to the given name
     276             : */
     277         241 : NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
     278             : {
     279           0 :         struct dirent *de;
     280           0 :         int i;
     281             : 
     282         241 :         dir->end_of_search = false;
     283             : 
     284         241 :         if (ISDOT(name)) {
     285           1 :                 dir->offset = DIR_OFFSET_DOTDOT;
     286           1 :                 *ofs = dir->offset;
     287           1 :                 return NT_STATUS_OK;
     288             :         }
     289             : 
     290         240 :         if (ISDOTDOT(name)) {
     291           0 :                 dir->offset = DIR_OFFSET_BASE;
     292           0 :                 *ofs = dir->offset;
     293           0 :                 return NT_STATUS_OK;
     294             :         }
     295             : 
     296        1612 :         for (i=dir->name_cache_index;i>=0;i--) {
     297        1451 :                 struct name_cache_entry *e = &dir->name_cache[i];
     298        1451 :                 if (e->name && strcasecmp_m(name, e->name) == 0) {
     299          79 :                         *ofs = e->offset;
     300          79 :                         return NT_STATUS_OK;
     301             :                 }
     302             :         }
     303       14390 :         for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
     304       14390 :                 struct name_cache_entry *e = &dir->name_cache[i];
     305       14390 :                 if (e->name && strcasecmp_m(name, e->name) == 0) {
     306         161 :                         *ofs = e->offset;
     307         161 :                         return NT_STATUS_OK;
     308             :                 }
     309             :         }
     310             : 
     311           0 :         rewinddir(dir->dir);
     312             : 
     313           0 :         while ((de = readdir(dir->dir))) {
     314           0 :                 if (strcasecmp_m(name, de->d_name) == 0) {
     315             :                         /* Casting is necessary to avoid signed integer overflow. */
     316           0 :                         dir->offset = (unsigned long)telldir(dir->dir) + (unsigned long)DIR_OFFSET_BASE;
     317           0 :                         *ofs = dir->offset;
     318           0 :                         return NT_STATUS_OK;
     319             :                 }
     320             :         }
     321             : 
     322           0 :         dir->end_of_search = true;
     323             : 
     324           0 :         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     325             : }
     326             : 
     327             : /*
     328             :   seek to the given offset
     329             : */
     330         116 : NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
     331             : {
     332           0 :         struct dirent *de;
     333           0 :         int i;
     334             : 
     335         116 :         dir->end_of_search = false;
     336             : 
     337         116 :         if (resume_key == DIR_OFFSET_DOT) {
     338           0 :                 *ofs = DIR_OFFSET_DOTDOT;
     339           0 :                 return NT_STATUS_OK;
     340             :         }
     341             : 
     342         116 :         if (resume_key == DIR_OFFSET_DOTDOT) {
     343           0 :                 *ofs = DIR_OFFSET_BASE;
     344           0 :                 return NT_STATUS_OK;
     345             :         }
     346             : 
     347         116 :         if (resume_key == DIR_OFFSET_BASE) {
     348           0 :                 rewinddir(dir->dir);
     349           0 :                 if ((de=readdir(dir->dir)) == NULL) {
     350           0 :                         dir->end_of_search = true;
     351           0 :                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     352             :                 }
     353             :                 /* Casting is necessary to avoid signed integer overflow. */
     354           0 :                 *ofs = (unsigned long)telldir(dir->dir) + (unsigned long)DIR_OFFSET_BASE;
     355           0 :                 dir->offset = *ofs;
     356           0 :                 return NT_STATUS_OK;
     357             :         }
     358             : 
     359         136 :         for (i=dir->name_cache_index;i>=0;i--) {
     360         136 :                 struct name_cache_entry *e = &dir->name_cache[i];
     361         136 :                 if (resume_key == (uint32_t)e->offset) {
     362         116 :                         *ofs = e->offset;
     363         116 :                         return NT_STATUS_OK;
     364             :                 }
     365             :         }
     366           0 :         for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
     367           0 :                 struct name_cache_entry *e = &dir->name_cache[i];
     368           0 :                 if (resume_key == (uint32_t)e->offset) {
     369           0 :                         *ofs = e->offset;
     370           0 :                         return NT_STATUS_OK;
     371             :                 }
     372             :         }
     373             : 
     374           0 :         rewinddir(dir->dir);
     375             : 
     376           0 :         while ((de = readdir(dir->dir))) {
     377             :                 /* Casting is necessary to avoid signed integer overflow. */
     378           0 :                 dir->offset = (unsigned long)telldir(dir->dir) + (unsigned long)DIR_OFFSET_BASE;
     379           0 :                 if (resume_key == (uint32_t)dir->offset) {
     380           0 :                         *ofs = dir->offset;
     381           0 :                         return NT_STATUS_OK;
     382             :                 }
     383             :         }
     384             : 
     385           0 :         dir->end_of_search = true;
     386             : 
     387           0 :         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     388             : }
     389             : 
     390             : 
     391             : /*
     392             :   see if a directory is empty
     393             : */
     394         427 : bool pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
     395             : {
     396           0 :         struct dirent *de;
     397         427 :         DIR *dir = opendir(name->full_name);
     398         427 :         if (dir == NULL) {
     399           2 :                 return true;
     400             :         }
     401             : 
     402        1261 :         while ((de = readdir(dir))) {
     403         845 :                 if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
     404           9 :                         closedir(dir);
     405           9 :                         return false;
     406             :                 }
     407             :         }
     408             : 
     409         416 :         closedir(dir);
     410         416 :         return true;
     411             : }

Generated by: LCOV version 1.14