root/source4/ntvfs/posix/pvfs_dirlist.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pvfs_list_no_wildcard
  2. pvfs_dirlist_destructor
  3. pvfs_list_start
  4. dcache_add
  5. pvfs_list_next
  6. pvfs_list_unix_path
  7. pvfs_list_eos
  8. pvfs_list_seek
  9. pvfs_list_seek_ofs
  10. pvfs_directory_empty

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

/* [<][>][^][v][top][bottom][index][help] */