move to SAFE_FREE()
[Samba.git] / source / smbd / dir.c
blobc0c728fe8fb1520efcdfbb2e732628a0c24d30f5
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 Directory handling routines
5 Copyright (C) Andrew Tridgell 1992-1998
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 2 of the License, or
10 (at your option) any later version.
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.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "includes.h"
24 extern int DEBUGLEVEL;
27 This module implements directory related functions for Samba.
30 typedef struct _dptr_struct {
31 struct _dptr_struct *next, *prev;
32 int dnum;
33 uint16 spid;
34 connection_struct *conn;
35 void *ptr;
36 BOOL expect_close;
37 char *wcard; /* Field only used for trans2_ searches */
38 uint16 attr; /* Field only used for trans2_ searches */
39 char *path;
40 } dptr_struct;
42 static struct bitmap *dptr_bmap;
43 static dptr_struct *dirptrs;
45 static int dptrs_open = 0;
47 #define INVALID_DPTR_KEY (-3)
49 /****************************************************************************
50 Initialise the dir bitmap.
51 ****************************************************************************/
53 void init_dptrs(void)
55 static BOOL dptrs_init=False;
57 if (dptrs_init)
58 return;
60 dptr_bmap = bitmap_allocate(MAX_DIRECTORY_HANDLES);
62 if (!dptr_bmap)
63 exit_server("out of memory in init_dptrs\n");
65 dptrs_init = True;
68 /****************************************************************************
69 Idle a dptr - the directory is closed but the control info is kept.
70 ****************************************************************************/
72 static void dptr_idle(dptr_struct *dptr)
74 if (dptr->ptr) {
75 DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum));
76 dptrs_open--;
77 CloseDir(dptr->ptr);
78 dptr->ptr = NULL;
82 /****************************************************************************
83 Idle the oldest dptr.
84 ****************************************************************************/
86 static void dptr_idleoldest(void)
88 dptr_struct *dptr;
91 * Go to the end of the list.
93 for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next)
96 if(!dptr) {
97 DEBUG(0,("No dptrs available to idle ?\n"));
98 return;
102 * Idle the oldest pointer.
105 for(; dptr; dptr = dptr->prev) {
106 if (dptr->ptr) {
107 dptr_idle(dptr);
108 return;
113 /****************************************************************************
114 Get the dptr_struct for a dir index.
115 ****************************************************************************/
117 static dptr_struct *dptr_get(int key, BOOL forclose)
119 dptr_struct *dptr;
121 for(dptr = dirptrs; dptr; dptr = dptr->next) {
122 if(dptr->dnum == key) {
123 if (!forclose && !dptr->ptr) {
124 if (dptrs_open >= MAX_OPEN_DIRECTORIES)
125 dptr_idleoldest();
126 DEBUG(4,("Reopening dptr key %d\n",key));
127 if ((dptr->ptr = OpenDir(dptr->conn, dptr->path, True)))
128 dptrs_open++;
130 DLIST_PROMOTE(dirptrs,dptr);
131 return dptr;
134 return(NULL);
137 /****************************************************************************
138 Get the dptr ptr for a dir index.
139 ****************************************************************************/
141 static void *dptr_ptr(int key)
143 dptr_struct *dptr = dptr_get(key, False);
145 if (dptr)
146 return(dptr->ptr);
147 return(NULL);
150 /****************************************************************************
151 Get the dir path for a dir index.
152 ****************************************************************************/
154 char *dptr_path(int key)
156 dptr_struct *dptr = dptr_get(key, False);
158 if (dptr)
159 return(dptr->path);
160 return(NULL);
163 /****************************************************************************
164 Get the dir wcard for a dir index (lanman2 specific).
165 ****************************************************************************/
167 char *dptr_wcard(int key)
169 dptr_struct *dptr = dptr_get(key, False);
171 if (dptr)
172 return(dptr->wcard);
173 return(NULL);
176 /****************************************************************************
177 Set the dir wcard for a dir index (lanman2 specific).
178 Returns 0 on ok, 1 on fail.
179 ****************************************************************************/
181 BOOL dptr_set_wcard(int key, char *wcard)
183 dptr_struct *dptr = dptr_get(key, False);
185 if (dptr) {
186 dptr->wcard = wcard;
187 return True;
189 return False;
192 /****************************************************************************
193 Set the dir attrib for a dir index (lanman2 specific).
194 Returns 0 on ok, 1 on fail.
195 ****************************************************************************/
197 BOOL dptr_set_attr(int key, uint16 attr)
199 dptr_struct *dptr = dptr_get(key, False);
201 if (dptr) {
202 dptr->attr = attr;
203 return True;
205 return False;
208 /****************************************************************************
209 Get the dir attrib for a dir index (lanman2 specific)
210 ****************************************************************************/
212 uint16 dptr_attr(int key)
214 dptr_struct *dptr = dptr_get(key, False);
216 if (dptr)
217 return(dptr->attr);
218 return(0);
221 /****************************************************************************
222 Close a dptr (internal func).
223 ****************************************************************************/
225 static void dptr_close_internal(dptr_struct *dptr)
227 DEBUG(4,("closing dptr key %d\n",dptr->dnum));
229 DLIST_REMOVE(dirptrs, dptr);
232 * Free the dnum in the bitmap. Remember the dnum value is always
233 * biased by one with respect to the bitmap.
236 if(bitmap_query( dptr_bmap, dptr->dnum - 1) != True) {
237 DEBUG(0,("dptr_close_internal : Error - closing dnum = %d and bitmap not set !\n",
238 dptr->dnum ));
241 bitmap_clear(dptr_bmap, dptr->dnum - 1);
243 if (dptr->ptr) {
244 CloseDir(dptr->ptr);
245 dptrs_open--;
248 /* Lanman 2 specific code */
249 SAFE_FREE(dptr->wcard);
250 string_set(&dptr->path,"");
251 SAFE_FREE(dptr);
254 /****************************************************************************
255 Close a dptr given a key.
256 ****************************************************************************/
258 void dptr_close(int *key)
260 dptr_struct *dptr;
262 if(*key == INVALID_DPTR_KEY)
263 return;
265 /* OS/2 seems to use -1 to indicate "close all directories" */
266 if (*key == -1) {
267 dptr_struct *next;
268 for(dptr = dirptrs; dptr; dptr = next) {
269 next = dptr->next;
270 dptr_close_internal(dptr);
272 *key = INVALID_DPTR_KEY;
273 return;
276 dptr = dptr_get(*key, True);
278 if (!dptr) {
279 DEBUG(0,("Invalid key %d given to dptr_close\n", *key));
280 return;
283 dptr_close_internal(dptr);
285 *key = INVALID_DPTR_KEY;
288 /****************************************************************************
289 Close all dptrs for a cnum.
290 ****************************************************************************/
292 void dptr_closecnum(connection_struct *conn)
294 dptr_struct *dptr, *next;
295 for(dptr = dirptrs; dptr; dptr = next) {
296 next = dptr->next;
297 if (dptr->conn == conn)
298 dptr_close_internal(dptr);
302 /****************************************************************************
303 Idle all dptrs for a cnum.
304 ****************************************************************************/
306 void dptr_idlecnum(connection_struct *conn)
308 dptr_struct *dptr;
309 for(dptr = dirptrs; dptr; dptr = dptr->next) {
310 if (dptr->conn == conn && dptr->ptr)
311 dptr_idle(dptr);
315 /****************************************************************************
316 Close a dptr that matches a given path, only if it matches the spid also.
317 ****************************************************************************/
319 void dptr_closepath(char *path,uint16 spid)
321 dptr_struct *dptr, *next;
322 for(dptr = dirptrs; dptr; dptr = next) {
323 next = dptr->next;
324 if (spid == dptr->spid && strequal(dptr->path,path))
325 dptr_close_internal(dptr);
329 /****************************************************************************
330 Start a directory listing.
331 ****************************************************************************/
333 static BOOL start_dir(connection_struct *conn,char *directory)
335 DEBUG(5,("start_dir dir=%s\n",directory));
337 if (!check_name(directory,conn))
338 return(False);
340 if (! *directory)
341 directory = ".";
343 conn->dirptr = OpenDir(conn, directory, True);
344 if (conn->dirptr) {
345 dptrs_open++;
346 string_set(&conn->dirpath,directory);
347 return(True);
350 return(False);
353 /****************************************************************************
354 Try and close the oldest handle not marked for
355 expect close in the hope that the client has
356 finished with that one.
357 ****************************************************************************/
359 static void dptr_close_oldest(BOOL old)
361 dptr_struct *dptr;
364 * Go to the end of the list.
366 for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next)
369 if(!dptr) {
370 DEBUG(0,("No old dptrs available to close oldest ?\n"));
371 return;
375 * If 'old' is true, close the oldest oldhandle dnum (ie. 1 < dnum < 256) that
376 * does not have expect_close set. If 'old' is false, close
377 * one of the new dnum handles.
380 for(; dptr; dptr = dptr->prev) {
381 if ((old && (dptr->dnum < 256) && !dptr->expect_close) ||
382 (!old && (dptr->dnum > 255))) {
383 dptr_close_internal(dptr);
384 return;
389 /****************************************************************************
390 Create a new dir ptr. If the flag old_handle is true then we must allocate
391 from the bitmap range 0 - 255 as old SMBsearch directory handles are only
392 one byte long. If old_handle is false we allocate from the range
393 256 - MAX_DIRECTORY_HANDLES. We bias the number we return by 1 to ensure
394 a directory handle is never zero. All the above is folklore taught to
395 me at Andrew's knee.... :-) :-). JRA.
396 ****************************************************************************/
398 int dptr_create(connection_struct *conn,char *path, BOOL old_handle, BOOL expect_close,uint16 spid)
400 dptr_struct *dptr;
402 if (!start_dir(conn,path))
403 return(-2); /* Code to say use a unix error return code. */
405 if (dptrs_open >= MAX_OPEN_DIRECTORIES)
406 dptr_idleoldest();
408 dptr = (dptr_struct *)malloc(sizeof(dptr_struct));
409 if(!dptr) {
410 DEBUG(0,("malloc fail in dptr_create.\n"));
411 return -1;
414 ZERO_STRUCTP(dptr);
416 if(old_handle) {
419 * This is an old-style SMBsearch request. Ensure the
420 * value we return will fit in the range 1-255.
423 dptr->dnum = bitmap_find(dptr_bmap, 0);
425 if(dptr->dnum == -1 || dptr->dnum > 254) {
428 * Try and close the oldest handle not marked for
429 * expect close in the hope that the client has
430 * finished with that one.
433 dptr_close_oldest(True);
435 /* Now try again... */
436 dptr->dnum = bitmap_find(dptr_bmap, 0);
438 if(dptr->dnum == -1 || dptr->dnum > 254) {
439 DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum));
440 SAFE_FREE(dptr);
441 return -1;
444 } else {
447 * This is a new-style trans2 request. Allocate from
448 * a range that will return 256 - MAX_DIRECTORY_HANDLES.
451 dptr->dnum = bitmap_find(dptr_bmap, 255);
453 if(dptr->dnum == -1 || dptr->dnum < 255) {
456 * Try and close the oldest handle close in the hope that
457 * the client has finished with that one. This will only
458 * happen in the case of the Win98 client bug where it leaks
459 * directory handles.
462 dptr_close_oldest(False);
464 /* Now try again... */
465 dptr->dnum = bitmap_find(dptr_bmap, 255);
467 if(dptr->dnum == -1 || dptr->dnum < 255) {
468 DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum));
469 SAFE_FREE(dptr);
470 return -1;
475 bitmap_set(dptr_bmap, dptr->dnum);
477 dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */
479 dptr->ptr = conn->dirptr;
480 string_set(&dptr->path,path);
481 dptr->conn = conn;
482 dptr->spid = spid;
483 dptr->expect_close = expect_close;
484 dptr->wcard = NULL; /* Only used in lanman2 searches */
485 dptr->attr = 0; /* Only used in lanman2 searches */
487 DLIST_ADD(dirptrs, dptr);
489 DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
490 dptr->dnum,path,expect_close));
492 return(dptr->dnum);
495 /****************************************************************************
496 Fill the 5 byte server reserved dptr field.
497 ****************************************************************************/
499 BOOL dptr_fill(char *buf1,unsigned int key)
501 unsigned char *buf = (unsigned char *)buf1;
502 void *p = dptr_ptr(key);
503 uint32 offset;
504 if (!p) {
505 DEBUG(1,("filling null dirptr %d\n",key));
506 return(False);
508 offset = TellDir(p);
509 DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key,
510 (long)p,(int)offset));
511 buf[0] = key;
512 SIVAL(buf,1,offset | DPTR_MASK);
513 return(True);
516 /****************************************************************************
517 Fetch the dir ptr and seek it given the 5 byte server field.
518 ****************************************************************************/
520 void *dptr_fetch(char *buf,int *num)
522 unsigned int key = *(unsigned char *)buf;
523 void *p = dptr_ptr(key);
524 uint32 offset;
525 if (!p) {
526 DEBUG(3,("fetched null dirptr %d\n",key));
527 return(NULL);
529 *num = key;
530 offset = IVAL(buf,1)&~DPTR_MASK;
531 SeekDir(p,offset);
532 DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
533 key,dptr_path(key),offset));
534 return(p);
537 /****************************************************************************
538 Fetch the dir ptr.
539 ****************************************************************************/
541 void *dptr_fetch_lanman2(int dptr_num)
543 void *p = dptr_ptr(dptr_num);
545 if (!p) {
546 DEBUG(3,("fetched null dirptr %d\n",dptr_num));
547 return(NULL);
549 DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
550 return(p);
553 /****************************************************************************
554 Check a filetype for being valid.
555 ****************************************************************************/
557 BOOL dir_check_ftype(connection_struct *conn,int mode,SMB_STRUCT_STAT *st,int dirtype)
559 if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
560 return False;
561 return True;
564 /****************************************************************************
565 Get an 8.3 directory entry.
566 ****************************************************************************/
568 BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype,char *fname,
569 SMB_OFF_T *size,int *mode,time_t *date,BOOL check_descend)
571 char *dname;
572 BOOL found = False;
573 SMB_STRUCT_STAT sbuf;
574 pstring path;
575 pstring pathreal;
576 BOOL isrootdir;
577 pstring filename;
578 BOOL needslash;
580 *path = *pathreal = *filename = 0;
582 isrootdir = (strequal(conn->dirpath,"./") ||
583 strequal(conn->dirpath,".") ||
584 strequal(conn->dirpath,"/"));
586 needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
588 if (!conn->dirptr)
589 return(False);
591 while (!found)
593 dname = ReadDirName(conn->dirptr);
595 DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n",
596 (long)conn->dirptr,TellDir(conn->dirptr)));
598 if (dname == NULL)
599 return(False);
601 pstrcpy(filename,dname);
603 /* notice the special *.* handling. This appears to be the only difference
604 between the wildcard handling in this routine and in the trans2 routines.
605 see masktest for a demo
607 if ((strcmp(mask,"*.*") == 0) ||
608 mask_match(filename,mask,False) ||
609 (name_map_mangle(filename,True,False,SNUM(conn)) &&
610 mask_match(filename,mask,False)))
612 if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
613 continue;
615 if (!is_8_3(filename, False)) {
616 name_map_mangle(filename,True,False,SNUM(conn));
619 pstrcpy(fname,filename);
620 *path = 0;
621 pstrcpy(path,conn->dirpath);
622 if(needslash)
623 pstrcat(path,"/");
624 pstrcpy(pathreal,path);
625 pstrcat(path,fname);
626 pstrcat(pathreal,dname);
627 if (conn->vfs_ops.stat(conn, pathreal, &sbuf) != 0)
629 DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n",path, strerror(errno) ));
630 continue;
633 *mode = dos_mode(conn,pathreal,&sbuf);
635 if (!dir_check_ftype(conn,*mode,&sbuf,dirtype))
637 DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
638 continue;
641 *size = sbuf.st_size;
642 *date = sbuf.st_mtime;
644 DEBUG(3,("get_dir_entry mask=[%s] found %s fname=%s\n",mask, pathreal,fname));
646 found = True;
650 return(found);
655 typedef struct
657 int pos;
658 int numentries;
659 int mallocsize;
660 char *data;
661 char *current;
662 } Dir;
666 /*******************************************************************
667 check to see if a user can read a file. This is only approximate,
668 it is used as part of the "hide unreadable" option. Don't
669 use it for anything security sensitive
670 ********************************************************************/
671 static BOOL user_can_read_file(connection_struct *conn, char *name)
673 SMB_STRUCT_STAT ste;
675 /* if we can't stat it does not show it */
676 if (vfs_stat(conn, name, &ste) != 0) return False;
678 if (ste.st_uid == conn->uid) {
679 return (ste.st_mode & S_IRUSR) == S_IRUSR;
680 } else {
681 int i;
682 if (ste.st_gid == conn->gid) {
683 return (ste.st_mode & S_IRGRP) == S_IRGRP;
685 for (i=0; i<conn->ngroups; i++) {
686 if (conn->groups[i] == ste.st_gid) {
687 return (ste.st_mode & S_IRGRP) == S_IRGRP;
692 return (ste.st_mode & S_IROTH) == S_IROTH;
695 /*******************************************************************
696 Open a directory.
697 ********************************************************************/
699 void *OpenDir(connection_struct *conn, char *name, BOOL use_veto)
701 Dir *dirp;
702 char *n;
703 DIR *p = conn->vfs_ops.opendir(conn,name);
704 int used=0;
706 if (!p) return(NULL);
707 dirp = (Dir *)malloc(sizeof(Dir));
708 if (!dirp) {
709 DEBUG(0,("Out of memory in OpenDir\n"));
710 conn->vfs_ops.closedir(conn,p);
711 return(NULL);
713 dirp->pos = dirp->numentries = dirp->mallocsize = 0;
714 dirp->data = dirp->current = NULL;
716 while ((n = vfs_readdirname(conn, p)))
718 int l;
720 l = strlen(n)+1;
722 /* If it's a vetoed file, pretend it doesn't even exist */
723 if (use_veto && conn && IS_VETO_PATH(conn, n)) continue;
725 /* Honour _hide unreadable_ option */
726 if (conn && lp_hideunreadable(SNUM(conn))) {
727 char *entry;
728 int ret=0;
730 if (asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) {
731 ret = user_can_read_file(conn, entry);
732 SAFE_FREE(entry);
734 if (!ret) continue;
737 if (used + l > dirp->mallocsize) {
738 int s = MAX(used+l,used+2000);
739 char *r;
740 r = (char *)Realloc(dirp->data,s);
741 if (!r) {
742 DEBUG(0,("Out of memory in OpenDir\n"));
743 break;
745 dirp->data = r;
746 dirp->mallocsize = s;
747 dirp->current = dirp->data;
749 pstrcpy(dirp->data+used,n);
750 used += l;
751 dirp->numentries++;
754 conn->vfs_ops.closedir(conn,p);
755 return((void *)dirp);
759 /*******************************************************************
760 Close a directory.
761 ********************************************************************/
763 void CloseDir(void *p)
765 if (!p) return;
766 SAFE_FREE(((Dir *)p)->data);
767 SAFE_FREE(p);
770 /*******************************************************************
771 Read from a directory.
772 ********************************************************************/
774 char *ReadDirName(void *p)
776 char *ret;
777 Dir *dirp = (Dir *)p;
779 if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
781 ret = dirp->current;
782 dirp->current = skip_string(dirp->current,1);
783 dirp->pos++;
785 return(ret);
789 /*******************************************************************
790 Seek a dir.
791 ********************************************************************/
793 BOOL SeekDir(void *p,int pos)
795 Dir *dirp = (Dir *)p;
797 if (!dirp) return(False);
799 if (pos < dirp->pos) {
800 dirp->current = dirp->data;
801 dirp->pos = 0;
804 while (dirp->pos < pos && ReadDirName(p)) ;
806 return(dirp->pos == pos);
809 /*******************************************************************
810 Tell a dir position.
811 ********************************************************************/
813 int TellDir(void *p)
815 Dir *dirp = (Dir *)p;
817 if (!dirp) return(-1);
819 return(dirp->pos);
822 /*******************************************************************************
823 This section manages a global directory cache.
824 (It should probably be split into a separate module. crh)
825 ********************************************************************************/
827 typedef struct {
828 ubi_dlNode node;
829 char *path;
830 char *name;
831 char *dname;
832 int snum;
833 } dir_cache_entry;
835 static ubi_dlNewList( dir_cache );
837 /*****************************************************************************
838 Add an entry to the directory cache.
839 Input: path -
840 name -
841 dname -
842 snum -
843 Output: None.
844 *****************************************************************************/
846 void DirCacheAdd( char *path, char *name, char *dname, int snum )
848 int pathlen;
849 int namelen;
850 dir_cache_entry *entry;
852 /* Allocate the structure & string space in one go so that it can be freed
853 * in one call to free().
855 pathlen = strlen( path ) +1; /* Bytes required to store path (with nul). */
856 namelen = strlen( name ) +1; /* Bytes required to store name (with nul). */
857 entry = (dir_cache_entry *)malloc( sizeof( dir_cache_entry )
858 + pathlen
859 + namelen
860 + strlen( dname ) +1 );
861 if( NULL == entry ) /* Not adding to the cache is not fatal, */
862 return; /* so just return as if nothing happened. */
864 /* Set pointers correctly and load values. */
865 entry->path = pstrcpy( (char *)&entry[1], path);
866 entry->name = pstrcpy( &(entry->path[pathlen]), name);
867 entry->dname = pstrcpy( &(entry->name[namelen]), dname);
868 entry->snum = snum;
870 /* Add the new entry to the linked list. */
871 (void)ubi_dlAddHead( dir_cache, entry );
872 DEBUG( 4, ("Added dir cache entry %s %s -> %s\n", path, name, dname ) );
874 /* Free excess cache entries. */
875 while( DIRCACHESIZE < dir_cache->count )
876 safe_free( ubi_dlRemTail( dir_cache ) );
880 /*****************************************************************************
881 Search for an entry to the directory cache.
882 Input: path -
883 name -
884 snum -
885 Output: The dname string of the located entry, or NULL if the entry was
886 not found.
888 Notes: This uses a linear search, which is is okay because of
889 the small size of the cache. Use a splay tree or hash
890 for large caches.
891 *****************************************************************************/
893 char *DirCacheCheck( char *path, char *name, int snum )
895 dir_cache_entry *entry;
897 for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
898 NULL != entry;
899 entry = (dir_cache_entry *)ubi_dlNext( entry ) )
901 if( entry->snum == snum
902 && 0 == strcmp( name, entry->name )
903 && 0 == strcmp( path, entry->path ) )
905 DEBUG(4, ("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
906 return( entry->dname );
910 return(NULL);
913 /*****************************************************************************
914 Remove all cache entries which have an snum that matches the input.
915 Input: snum -
916 Output: None.
917 *****************************************************************************/
919 void DirCacheFlush(int snum)
921 dir_cache_entry *entry;
922 ubi_dlNodePtr next;
924 for(entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
925 NULL != entry; ) {
926 next = ubi_dlNext( entry );
927 if( entry->snum == snum )
928 safe_free( ubi_dlRemThis( dir_cache, entry ) );
929 entry = (dir_cache_entry *)next;