LCOV - code coverage report
Current view: top level - source4/ntvfs/posix - pvfs_dirlist.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 129 173 74.6 %
Date: 2024-06-13 04:01:37 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        4186 : static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name, 
      65             :                                       const char *pattern, struct pvfs_dir *dir)
      66             : {
      67        4186 :         if (!name->exists) {
      68           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
      69             :         }
      70             : 
      71        4186 :         dir->pvfs = pvfs;
      72        4186 :         dir->no_wildcard = true;
      73        4186 :         dir->end_of_search = false;
      74        4186 :         dir->unix_path = talloc_strdup(dir, name->full_name);
      75        4186 :         if (!dir->unix_path) {
      76           0 :                 return NT_STATUS_NO_MEMORY;
      77             :         }
      78             : 
      79        4186 :         dir->single_name = talloc_strdup(dir, pattern);
      80        4186 :         if (!dir->single_name) {
      81           0 :                 return NT_STATUS_NO_MEMORY;
      82             :         }
      83             : 
      84        4186 :         dir->dir = NULL;
      85        4186 :         dir->offset = 0;
      86        4186 :         dir->pattern = NULL;
      87             : 
      88        4186 :         return NT_STATUS_OK;
      89             : }
      90             : 
      91             : /*
      92             :   destroy an open search
      93             : */
      94        5425 : static int pvfs_dirlist_destructor(struct pvfs_dir *dir)
      95             : {
      96        5425 :         if (dir->dir) closedir(dir->dir);
      97        5425 :         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        9613 : NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name, 
     106             :                          TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
     107             : {
     108             :         char *pattern;
     109             :         struct pvfs_dir *dir;
     110             : 
     111        9613 :         (*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
     112        9613 :         if (*dirp == NULL) {
     113           0 :                 return NT_STATUS_NO_MEMORY;
     114             :         }
     115             :         
     116        9613 :         dir = *dirp;
     117             : 
     118             :         /* split the unix path into a directory + pattern */
     119        9613 :         pattern = strrchr(name->full_name, '/');
     120        9613 :         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        9613 :         *pattern++ = 0;
     127             : 
     128        9613 :         if (!name->has_wildcard) {
     129        4186 :                 return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
     130             :         }
     131             : 
     132        5427 :         dir->unix_path = talloc_strdup(dir, name->full_name);
     133        5427 :         if (!dir->unix_path) {
     134           0 :                 return NT_STATUS_NO_MEMORY;
     135             :         }
     136             : 
     137        5427 :         dir->pattern = talloc_strdup(dir, pattern);
     138        5427 :         if (dir->pattern == NULL) {
     139           0 :                 return NT_STATUS_NO_MEMORY;
     140             :         }
     141             :         
     142        5427 :         dir->dir = opendir(name->full_name);
     143        5427 :         if (!dir->dir) { 
     144           2 :                 return pvfs_map_errno(pvfs, errno); 
     145             :         }
     146             : 
     147        5425 :         dir->pvfs = pvfs;
     148        5425 :         dir->no_wildcard = false;
     149        5425 :         dir->end_of_search = false;
     150        5425 :         dir->offset = DIR_OFFSET_DOT;
     151        5425 :         dir->name_cache = talloc_zero_array(dir, 
     152             :                                             struct name_cache_entry, 
     153             :                                             NAME_CACHE_SIZE);
     154        5425 :         if (dir->name_cache == NULL) {
     155           0 :                 talloc_free(dir);
     156           0 :                 return NT_STATUS_NO_MEMORY;
     157             :         }
     158             : 
     159        5425 :         talloc_set_destructor(dir, pvfs_dirlist_destructor);
     160             : 
     161        5425 :         return NT_STATUS_OK;
     162             : }
     163             : 
     164             : /*
     165             :   add an entry to the local cache
     166             : */
     167      128397 : static void dcache_add(struct pvfs_dir *dir, const char *name)
     168             : {
     169             :         struct name_cache_entry *e;
     170             : 
     171      128397 :         dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
     172      128397 :         e = &dir->name_cache[dir->name_cache_index];
     173             : 
     174      128397 :         if (e->name) talloc_free(e->name);
     175             : 
     176      128397 :         e->name = talloc_strdup(dir->name_cache, name);
     177      128397 :         e->offset = dir->offset;
     178      128397 : }
     179             : 
     180             : /* 
     181             :    return the next entry
     182             : */
     183      140405 : const char *pvfs_list_next(struct pvfs_dir *dir, off_t *ofs)
     184             : {
     185             :         struct dirent *de;
     186      140405 :         enum protocol_types protocol = dir->pvfs->ntvfs->ctx->protocol;
     187             : 
     188             :         /* non-wildcard searches are easy */
     189      140405 :         if (dir->no_wildcard) {
     190        8171 :                 dir->end_of_search = true;
     191        8171 :                 if (*ofs != 0) return NULL;
     192        4186 :                 (*ofs)++;
     193        4186 :                 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      132234 :         if (*ofs == DIR_OFFSET_DOT) {
     200        5425 :                 (*ofs) = DIR_OFFSET_DOTDOT;
     201        5425 :                 dir->offset = *ofs;
     202        5425 :                 if (ms_fnmatch_protocol(dir->pattern, ".", protocol,
     203             :                                         false) == 0) {
     204        5218 :                         dcache_add(dir, ".");
     205        5218 :                         return ".";
     206             :                 }
     207             :         }
     208             : 
     209      127016 :         if (*ofs == DIR_OFFSET_DOTDOT) {
     210        3387 :                 (*ofs) = DIR_OFFSET_BASE;
     211        3387 :                 dir->offset = *ofs;
     212        3387 :                 if (ms_fnmatch_protocol(dir->pattern, "..", protocol,
     213             :                                         false) == 0) {
     214        3180 :                         dcache_add(dir, "..");
     215        3180 :                         return "..";
     216             :                 }
     217             :         }
     218             : 
     219      123836 :         if (*ofs == DIR_OFFSET_BASE) {
     220        3451 :                 rewinddir(dir->dir);
     221      120385 :         } else if (*ofs != dir->offset) {
     222         197 :                 seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
     223             :         }
     224      123836 :         dir->offset = *ofs;
     225             :         
     226      255087 :         while ((de = readdir(dir->dir))) {
     227      127440 :                 const char *dname = de->d_name;
     228             : 
     229      127440 :                 if (ISDOT(dname) || ISDOTDOT(dname)) {
     230        6875 :                         continue;
     231             :                 }
     232             : 
     233      120565 :                 if (ms_fnmatch_protocol(dir->pattern, dname, protocol,
     234             :                                         false) != 0) {
     235         567 :                         char *short_name = pvfs_short_name_component(dir->pvfs, dname);
     236         726 :                         if (short_name == NULL ||
     237         159 :                             ms_fnmatch_protocol(dir->pattern, short_name,
     238             :                                                 protocol, false) != 0) {
     239         566 :                                 talloc_free(short_name);
     240         566 :                                 continue;
     241             :                         }
     242           1 :                         talloc_free(short_name);
     243             :                 }
     244             : 
     245      119999 :                 dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
     246      119999 :                 (*ofs) = dir->offset;
     247             : 
     248      119999 :                 dcache_add(dir, dname);
     249             : 
     250      119999 :                 return dname;
     251             :         }
     252             : 
     253        3837 :         dir->end_of_search = true;
     254        3837 :         return NULL;
     255             : }
     256             : 
     257             : /*
     258             :   return unix directory of an open search
     259             : */
     260      130545 : const char *pvfs_list_unix_path(struct pvfs_dir *dir)
     261             : {
     262      130545 :         return dir->unix_path;
     263             : }
     264             : 
     265             : /*
     266             :   return true if end of search has been reached
     267             : */
     268        3176 : bool pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
     269             : {
     270        3176 :         return dir->end_of_search;
     271             : }
     272             : 
     273             : /*
     274             :   seek to the given name
     275             : */
     276         241 : NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
     277             : {
     278             :         struct dirent *de;
     279             :         int i;
     280             : 
     281         241 :         dir->end_of_search = false;
     282             : 
     283         241 :         if (ISDOT(name)) {
     284           1 :                 dir->offset = DIR_OFFSET_DOTDOT;
     285           1 :                 *ofs = dir->offset;
     286           1 :                 return NT_STATUS_OK;
     287             :         }
     288             : 
     289         240 :         if (ISDOTDOT(name)) {
     290           0 :                 dir->offset = DIR_OFFSET_BASE;
     291           0 :                 *ofs = dir->offset;
     292           0 :                 return NT_STATUS_OK;
     293             :         }
     294             : 
     295        1612 :         for (i=dir->name_cache_index;i>=0;i--) {
     296        1451 :                 struct name_cache_entry *e = &dir->name_cache[i];
     297        1451 :                 if (e->name && strcasecmp_m(name, e->name) == 0) {
     298          79 :                         *ofs = e->offset;
     299          79 :                         return NT_STATUS_OK;
     300             :                 }
     301             :         }
     302       14390 :         for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
     303       14390 :                 struct name_cache_entry *e = &dir->name_cache[i];
     304       14390 :                 if (e->name && strcasecmp_m(name, e->name) == 0) {
     305         161 :                         *ofs = e->offset;
     306         161 :                         return NT_STATUS_OK;
     307             :                 }
     308             :         }
     309             : 
     310           0 :         rewinddir(dir->dir);
     311             : 
     312           0 :         while ((de = readdir(dir->dir))) {
     313           0 :                 if (strcasecmp_m(name, de->d_name) == 0) {
     314           0 :                         dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
     315           0 :                         *ofs = dir->offset;
     316           0 :                         return NT_STATUS_OK;
     317             :                 }
     318             :         }
     319             : 
     320           0 :         dir->end_of_search = true;
     321             : 
     322           0 :         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     323             : }
     324             : 
     325             : /*
     326             :   seek to the given offset
     327             : */
     328         116 : NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
     329             : {
     330             :         struct dirent *de;
     331             :         int i;
     332             : 
     333         116 :         dir->end_of_search = false;
     334             : 
     335         116 :         if (resume_key == DIR_OFFSET_DOT) {
     336           0 :                 *ofs = DIR_OFFSET_DOTDOT;
     337           0 :                 return NT_STATUS_OK;
     338             :         }
     339             : 
     340         116 :         if (resume_key == DIR_OFFSET_DOTDOT) {
     341           0 :                 *ofs = DIR_OFFSET_BASE;
     342           0 :                 return NT_STATUS_OK;
     343             :         }
     344             : 
     345         116 :         if (resume_key == DIR_OFFSET_BASE) {
     346           0 :                 rewinddir(dir->dir);
     347           0 :                 if ((de=readdir(dir->dir)) == NULL) {
     348           0 :                         dir->end_of_search = true;
     349           0 :                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     350             :                 }
     351           0 :                 *ofs = telldir(dir->dir) + DIR_OFFSET_BASE;
     352           0 :                 dir->offset = *ofs;
     353           0 :                 return NT_STATUS_OK;
     354             :         }
     355             : 
     356         138 :         for (i=dir->name_cache_index;i>=0;i--) {
     357         138 :                 struct name_cache_entry *e = &dir->name_cache[i];
     358         138 :                 if (resume_key == (uint32_t)e->offset) {
     359         116 :                         *ofs = e->offset;
     360         116 :                         return NT_STATUS_OK;
     361             :                 }
     362             :         }
     363           0 :         for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
     364           0 :                 struct name_cache_entry *e = &dir->name_cache[i];
     365           0 :                 if (resume_key == (uint32_t)e->offset) {
     366           0 :                         *ofs = e->offset;
     367           0 :                         return NT_STATUS_OK;
     368             :                 }
     369             :         }
     370             : 
     371           0 :         rewinddir(dir->dir);
     372             : 
     373           0 :         while ((de = readdir(dir->dir))) {
     374           0 :                 dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
     375           0 :                 if (resume_key == (uint32_t)dir->offset) {
     376           0 :                         *ofs = dir->offset;
     377           0 :                         return NT_STATUS_OK;
     378             :                 }
     379             :         }
     380             : 
     381           0 :         dir->end_of_search = true;
     382             : 
     383           0 :         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     384             : }
     385             : 
     386             : 
     387             : /*
     388             :   see if a directory is empty
     389             : */
     390         419 : bool pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
     391             : {
     392             :         struct dirent *de;
     393         419 :         DIR *dir = opendir(name->full_name);
     394         419 :         if (dir == NULL) {
     395           2 :                 return true;
     396             :         }
     397             : 
     398        1659 :         while ((de = readdir(dir))) {
     399         834 :                 if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
     400           9 :                         closedir(dir);
     401           9 :                         return false;
     402             :                 }
     403             :         }
     404             : 
     405         408 :         closedir(dir);
     406         408 :         return true;
     407             : }

Generated by: LCOV version 1.13