Restructuring of vfs layer to include a "this" pointer - can be an fsp or
[jra/samba/.git] / source3 / smbd / dir.c
1 #define OLD_NTDOMAIN 1
2
3 /* 
4    Unix SMB/Netbios implementation.
5    Version 1.9.
6    Directory handling routines
7    Copyright (C) Andrew Tridgell 1992-1998
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 extern int DEBUGLEVEL;
27
28 /*
29    This module implements directory related functions for Samba.
30 */
31
32 typedef struct _dptr_struct {
33         struct _dptr_struct *next, *prev;
34         int dnum;
35         uint16 spid;
36         connection_struct *conn;
37         void *ptr;
38         BOOL expect_close;
39         char *wcard; /* Field only used for trans2_ searches */
40         uint16 attr; /* Field only used for trans2_ searches */
41         char *path;
42 } dptr_struct;
43
44 static struct bitmap *dptr_bmap;
45 static dptr_struct *dirptrs;
46
47 static int dptrs_open = 0;
48
49 #define INVALID_DPTR_KEY (-3)
50
51 /****************************************************************************
52  Initialise the dir bitmap.
53 ****************************************************************************/
54
55 void init_dptrs(void)
56 {
57   static BOOL dptrs_init=False;
58
59   if (dptrs_init)
60     return;
61
62   dptr_bmap = bitmap_allocate(MAX_DIRECTORY_HANDLES);
63
64   if (!dptr_bmap)
65     exit_server("out of memory in init_dptrs\n");
66
67   dptrs_init = True;
68 }
69
70 /****************************************************************************
71  Idle a dptr - the directory is closed but the control info is kept.
72 ****************************************************************************/
73
74 static void dptr_idle(dptr_struct *dptr)
75 {
76   if (dptr->ptr) {
77     DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum));
78     dptrs_open--;
79     CloseDir(dptr->ptr);
80     dptr->ptr = NULL;
81   }
82 }
83
84 /****************************************************************************
85  Idle the oldest dptr.
86 ****************************************************************************/
87
88 static void dptr_idleoldest(void)
89 {
90   dptr_struct *dptr;
91
92   /*
93    * Go to the end of the list.
94    */
95   for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next)
96     ;
97
98   if(!dptr) {
99     DEBUG(0,("No dptrs available to idle ?\n"));
100     return;
101   }
102
103   /*
104    * Idle the oldest pointer.
105    */
106
107   for(; dptr; dptr = dptr->prev) {
108     if (dptr->ptr) {
109       dptr_idle(dptr);
110       return;
111     }
112   }
113 }
114
115 /****************************************************************************
116  Get the dptr_struct for a dir index.
117 ****************************************************************************/
118
119 static dptr_struct *dptr_get(int key, BOOL forclose)
120 {
121   dptr_struct *dptr;
122
123   for(dptr = dirptrs; dptr; dptr = dptr->next) {
124     if(dptr->dnum == key) {
125       if (!forclose && !dptr->ptr) {
126         if (dptrs_open >= MAX_OPEN_DIRECTORIES)
127           dptr_idleoldest();
128         DEBUG(4,("Reopening dptr key %d\n",key));
129         if ((dptr->ptr = OpenDir(dptr->conn, dptr->path, True)))
130           dptrs_open++;
131       }
132       DLIST_PROMOTE(dirptrs,dptr);
133       return dptr;
134     }
135   }
136   return(NULL);
137 }
138
139 /****************************************************************************
140  Get the dptr ptr for a dir index.
141 ****************************************************************************/
142
143 static void *dptr_ptr(int key)
144 {
145   dptr_struct *dptr = dptr_get(key, False);
146
147   if (dptr)
148     return(dptr->ptr);
149   return(NULL);
150 }
151
152 /****************************************************************************
153  Get the dir path for a dir index.
154 ****************************************************************************/
155
156 char *dptr_path(int key)
157 {
158   dptr_struct *dptr = dptr_get(key, False);
159
160   if (dptr)
161     return(dptr->path);
162   return(NULL);
163 }
164
165 /****************************************************************************
166  Get the dir wcard for a dir index (lanman2 specific).
167 ****************************************************************************/
168
169 char *dptr_wcard(int key)
170 {
171   dptr_struct *dptr = dptr_get(key, False);
172
173   if (dptr)
174     return(dptr->wcard);
175   return(NULL);
176 }
177
178 /****************************************************************************
179  Set the dir wcard for a dir index (lanman2 specific).
180  Returns 0 on ok, 1 on fail.
181 ****************************************************************************/
182
183 BOOL dptr_set_wcard(int key, char *wcard)
184 {
185   dptr_struct *dptr = dptr_get(key, False);
186
187   if (dptr) {
188     dptr->wcard = wcard;
189     return True;
190   }
191   return False;
192 }
193
194 /****************************************************************************
195  Set the dir attrib for a dir index (lanman2 specific).
196  Returns 0 on ok, 1 on fail.
197 ****************************************************************************/
198
199 BOOL dptr_set_attr(int key, uint16 attr)
200 {
201   dptr_struct *dptr = dptr_get(key, False);
202
203   if (dptr) {
204     dptr->attr = attr;
205     return True;
206   }
207   return False;
208 }
209
210 /****************************************************************************
211  Get the dir attrib for a dir index (lanman2 specific)
212 ****************************************************************************/
213
214 uint16 dptr_attr(int key)
215 {
216   dptr_struct *dptr = dptr_get(key, False);
217
218   if (dptr)
219     return(dptr->attr);
220   return(0);
221 }
222
223 /****************************************************************************
224  Close a dptr (internal func).
225 ****************************************************************************/
226
227 static void dptr_close_internal(dptr_struct *dptr)
228 {
229   DEBUG(4,("closing dptr key %d\n",dptr->dnum));
230
231   DLIST_REMOVE(dirptrs, dptr);
232
233   /* 
234    * Free the dnum in the bitmap. Remember the dnum value is always 
235    * biased by one with respect to the bitmap.
236    */
237
238   if(bitmap_query( dptr_bmap, dptr->dnum - 1) != True) {
239     DEBUG(0,("dptr_close_internal : Error - closing dnum = %d and bitmap not set !\n",
240                         dptr->dnum ));
241   }
242
243   bitmap_clear(dptr_bmap, dptr->dnum - 1);
244
245   if (dptr->ptr) {
246     CloseDir(dptr->ptr);
247     dptrs_open--;
248   }
249
250   /* Lanman 2 specific code */
251   if (dptr->wcard)
252     free(dptr->wcard);
253   string_set(&dptr->path,"");
254   free((char *)dptr);
255 }
256
257 /****************************************************************************
258  Close a dptr given a key.
259 ****************************************************************************/
260
261 void dptr_close(int *key)
262 {
263   dptr_struct *dptr;
264
265   if(*key == INVALID_DPTR_KEY)
266     return;
267
268   /* OS/2 seems to use -1 to indicate "close all directories" */
269   if (*key == -1) {
270     dptr_struct *next;
271     for(dptr = dirptrs; dptr; dptr = next) {
272       next = dptr->next;
273       dptr_close_internal(dptr);
274     }
275     *key = INVALID_DPTR_KEY;
276     return;
277   }
278
279   dptr = dptr_get(*key, True);
280
281   if (!dptr) {
282     DEBUG(0,("Invalid key %d given to dptr_close\n", *key));
283     return;
284   }
285
286   dptr_close_internal(dptr);
287
288   *key = INVALID_DPTR_KEY;
289 }
290
291 /****************************************************************************
292  Close all dptrs for a cnum.
293 ****************************************************************************/
294
295 void dptr_closecnum(connection_struct *conn)
296 {
297   dptr_struct *dptr, *next;
298   for(dptr = dirptrs; dptr; dptr = next) {
299     next = dptr->next;
300     if (dptr->conn == conn)
301       dptr_close_internal(dptr);
302   }
303 }
304
305 /****************************************************************************
306  Idle all dptrs for a cnum.
307 ****************************************************************************/
308
309 void dptr_idlecnum(connection_struct *conn)
310 {
311   dptr_struct *dptr;
312   for(dptr = dirptrs; dptr; dptr = dptr->next) {
313     if (dptr->conn == conn && dptr->ptr)
314       dptr_idle(dptr);
315   }
316 }
317
318 /****************************************************************************
319  Close a dptr that matches a given path, only if it matches the spid also.
320 ****************************************************************************/
321
322 void dptr_closepath(char *path,uint16 spid)
323 {
324   dptr_struct *dptr, *next;
325   for(dptr = dirptrs; dptr; dptr = next) {
326     next = dptr->next;
327     if (spid == dptr->spid && strequal(dptr->path,path))
328       dptr_close_internal(dptr);
329   }
330 }
331
332 /****************************************************************************
333  Start a directory listing.
334 ****************************************************************************/
335
336 static BOOL start_dir(connection_struct *conn,char *directory)
337 {
338   DEBUG(5,("start_dir dir=%s\n",directory));
339
340   if (!check_name(directory,conn))
341     return(False);
342   
343   if (! *directory)
344     directory = ".";
345
346   conn->dirptr = OpenDir(conn, directory, True);
347   if (conn->dirptr) {    
348     dptrs_open++;
349     string_set(&conn->dirpath,directory);
350     return(True);
351   }
352   
353   return(False);
354 }
355
356 /****************************************************************************
357  Try and close the oldest handle not marked for
358  expect close in the hope that the client has
359  finished with that one.
360 ****************************************************************************/
361
362 static void dptr_close_oldest(BOOL old)
363 {
364   dptr_struct *dptr;
365
366   /*
367    * Go to the end of the list.
368    */
369   for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next)
370     ;
371
372   if(!dptr) {
373     DEBUG(0,("No old dptrs available to close oldest ?\n"));
374     return;
375   }
376
377   /*
378    * If 'old' is true, close the oldest oldhandle dnum (ie. 1 < dnum < 256) that
379    * does not have expect_close set. If 'old' is false, close
380    * one of the new dnum handles.
381    */
382
383   for(; dptr; dptr = dptr->prev) {
384     if ((old && (dptr->dnum < 256) && !dptr->expect_close) ||
385         (!old && (dptr->dnum > 255))) {
386       dptr_close_internal(dptr);
387       return;
388     }
389   }
390 }
391
392 /****************************************************************************
393  Create a new dir ptr. If the flag old_handle is true then we must allocate
394  from the bitmap range 0 - 255 as old SMBsearch directory handles are only
395  one byte long. If old_handle is false we allocate from the range
396  256 - MAX_DIRECTORY_HANDLES. We bias the number we return by 1 to ensure
397  a directory handle is never zero. All the above is folklore taught to
398  me at Andrew's knee.... :-) :-). JRA.
399 ****************************************************************************/
400
401 int dptr_create(connection_struct *conn,char *path, BOOL old_handle, BOOL expect_close,uint16 spid)
402 {
403   dptr_struct *dptr;
404
405   if (!start_dir(conn,path))
406     return(-2); /* Code to say use a unix error return code. */
407
408   if (dptrs_open >= MAX_OPEN_DIRECTORIES)
409     dptr_idleoldest();
410
411   dptr = (dptr_struct *)malloc(sizeof(dptr_struct));
412   if(!dptr) {
413     DEBUG(0,("malloc fail in dptr_create.\n"));
414     return -1;
415   }
416
417   ZERO_STRUCTP(dptr);
418
419   if(old_handle) {
420
421     /*
422      * This is an old-style SMBsearch request. Ensure the
423      * value we return will fit in the range 1-255.
424      */
425
426     dptr->dnum = bitmap_find(dptr_bmap, 0);
427
428     if(dptr->dnum == -1 || dptr->dnum > 254) {
429
430       /*
431        * Try and close the oldest handle not marked for
432        * expect close in the hope that the client has
433        * finished with that one.
434        */
435
436       dptr_close_oldest(True);
437
438       /* Now try again... */
439       dptr->dnum = bitmap_find(dptr_bmap, 0);
440
441       if(dptr->dnum == -1 || dptr->dnum > 254) {
442         DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum));
443         free((char *)dptr);
444         return -1;
445       }
446     }
447   } else {
448
449     /*
450      * This is a new-style trans2 request. Allocate from
451      * a range that will return 256 - MAX_DIRECTORY_HANDLES.
452      */
453
454     dptr->dnum = bitmap_find(dptr_bmap, 255);
455
456     if(dptr->dnum == -1 || dptr->dnum < 255) {
457
458       /*
459        * Try and close the oldest handle close in the hope that
460        * the client has finished with that one. This will only
461        * happen in the case of the Win98 client bug where it leaks
462        * directory handles.
463        */
464
465       dptr_close_oldest(False);
466
467       /* Now try again... */
468       dptr->dnum = bitmap_find(dptr_bmap, 255);
469
470       if(dptr->dnum == -1 || dptr->dnum < 255) {
471         DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum));
472         free((char *)dptr);
473         return -1;
474       }
475     }
476   }
477
478   bitmap_set(dptr_bmap, dptr->dnum);
479
480   dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */
481
482   dptr->ptr = conn->dirptr;
483   string_set(&dptr->path,path);
484   dptr->conn = conn;
485   dptr->spid = spid;
486   dptr->expect_close = expect_close;
487   dptr->wcard = NULL; /* Only used in lanman2 searches */
488   dptr->attr = 0; /* Only used in lanman2 searches */
489
490   DLIST_ADD(dirptrs, dptr);
491
492   DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
493            dptr->dnum,path,expect_close));  
494
495   return(dptr->dnum);
496 }
497
498 /****************************************************************************
499  Fill the 5 byte server reserved dptr field.
500 ****************************************************************************/
501
502 BOOL dptr_fill(char *buf1,unsigned int key)
503 {
504   unsigned char *buf = (unsigned char *)buf1;
505   void *p = dptr_ptr(key);
506   uint32 offset;
507   if (!p) {
508     DEBUG(1,("filling null dirptr %d\n",key));
509     return(False);
510   }
511   offset = TellDir(p);
512   DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key,
513            (long)p,(int)offset));
514   buf[0] = key;
515   SIVAL(buf,1,offset | DPTR_MASK);
516   return(True);
517 }
518
519 /****************************************************************************
520  Fetch the dir ptr and seek it given the 5 byte server field.
521 ****************************************************************************/
522
523 void *dptr_fetch(char *buf,int *num)
524 {
525   unsigned int key = *(unsigned char *)buf;
526   void *p = dptr_ptr(key);
527   uint32 offset;
528   if (!p) {
529     DEBUG(3,("fetched null dirptr %d\n",key));
530     return(NULL);
531   }
532   *num = key;
533   offset = IVAL(buf,1)&~DPTR_MASK;
534   SeekDir(p,offset);
535   DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
536            key,dptr_path(key),offset));
537   return(p);
538 }
539
540 /****************************************************************************
541  Fetch the dir ptr.
542 ****************************************************************************/
543
544 void *dptr_fetch_lanman2(int dptr_num)
545 {
546   void *p = dptr_ptr(dptr_num);
547
548   if (!p) {
549     DEBUG(3,("fetched null dirptr %d\n",dptr_num));
550     return(NULL);
551   }
552   DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
553   return(p);
554 }
555
556 /****************************************************************************
557  Check a filetype for being valid.
558 ****************************************************************************/
559
560 BOOL dir_check_ftype(connection_struct *conn,int mode,SMB_STRUCT_STAT *st,int dirtype)
561 {
562   if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
563     return False;
564   return True;
565 }
566
567 /****************************************************************************
568  Get an 8.3 directory entry.
569 ****************************************************************************/
570
571 BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype,char *fname,
572                    SMB_OFF_T *size,int *mode,time_t *date,BOOL check_descend)
573 {
574   char *dname;
575   BOOL found = False;
576   SMB_STRUCT_STAT sbuf;
577   pstring path;
578   pstring pathreal;
579   BOOL isrootdir;
580   pstring filename;
581   BOOL needslash;
582
583   *path = *pathreal = *filename = 0;
584
585   isrootdir = (strequal(conn->dirpath,"./") ||
586                strequal(conn->dirpath,".") ||
587                strequal(conn->dirpath,"/"));
588   
589   needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
590
591   if (!conn->dirptr)
592     return(False);
593   
594   while (!found)
595   {
596     dname = ReadDirName(conn->dirptr);
597
598     DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n",
599           (long)conn->dirptr,TellDir(conn->dirptr)));
600       
601     if (dname == NULL) 
602       return(False);
603       
604     pstrcpy(filename,dname);      
605
606     /* notice the special *.* handling. This appears to be the only difference
607        between the wildcard handling in this routine and in the trans2 routines.
608        see masktest for a demo
609     */
610     if ((strcmp(mask,"*.*") == 0) ||
611         mask_match(filename,mask,False) ||
612         (name_map_mangle(filename,True,False,SNUM(conn)) &&
613          mask_match(filename,mask,False)))
614     {
615       if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
616         continue;
617
618       if (!is_8_3(filename, False)) {
619               name_map_mangle(filename,True,False,SNUM(conn));
620       }
621
622       pstrcpy(fname,filename);
623       *path = 0;
624       pstrcpy(path,conn->dirpath);
625       if(needslash)
626         pstrcat(path,"/");
627       pstrcpy(pathreal,path);
628       pstrcat(path,fname);
629       pstrcat(pathreal,dname);
630       if (conn->vfs_ops.stat(conn,dos_to_unix(pathreal, False), &sbuf) != 0)
631       {
632         DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n",path, strerror(errno) ));
633         continue;
634       }
635           
636       *mode = dos_mode(conn,pathreal,&sbuf);
637
638       if (!dir_check_ftype(conn,*mode,&sbuf,dirtype)) 
639       {
640         DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
641         continue;
642       }
643
644       *size = sbuf.st_size;
645       *date = sbuf.st_mtime;
646
647       DEBUG(0,("get_dir_entry mask=[%s] found %s fname=%s\n",mask, pathreal,fname));
648           
649       found = True;
650     }
651   }
652
653   return(found);
654 }
655
656
657
658 typedef struct
659 {
660   int pos;
661   int numentries;
662   int mallocsize;
663   char *data;
664   char *current;
665 } Dir;
666
667
668 /*******************************************************************
669  Open a directory.
670 ********************************************************************/
671
672 void *OpenDir(connection_struct *conn, char *name, BOOL use_veto)
673 {
674   Dir *dirp;
675   char *n;
676   DIR *p = conn->vfs_ops.opendir(conn,dos_to_unix(name,False));
677   int used=0;
678
679   if (!p) return(NULL);
680   dirp = (Dir *)malloc(sizeof(Dir));
681   if (!dirp) {
682     conn->vfs_ops.closedir(conn,p);
683     return(NULL);
684   }
685   dirp->pos = dirp->numentries = dirp->mallocsize = 0;
686   dirp->data = dirp->current = NULL;
687
688   while ((n = vfs_readdirname(conn, p)))
689   {
690     int l;
691
692     l = strlen(n)+1;
693
694     /* Return value of vfs_readdirname has already gone through 
695        unix_to_dos() */
696
697     /* If it's a vetoed file, pretend it doesn't even exist */
698     if (use_veto && conn && IS_VETO_PATH(conn, n)) continue;
699
700     if (used + l > dirp->mallocsize) {
701       int s = MAX(used+l,used+2000);
702       char *r;
703       r = (char *)Realloc(dirp->data,s);
704       if (!r) {
705         DEBUG(0,("Out of memory in OpenDir\n"));
706         break;
707       }
708       dirp->data = r;
709       dirp->mallocsize = s;
710       dirp->current = dirp->data;
711     }
712     pstrcpy(dirp->data+used,n);
713     used += l;
714     dirp->numentries++;
715   }
716
717   conn->vfs_ops.closedir(conn,p);
718   return((void *)dirp);
719 }
720
721
722 /*******************************************************************
723  Close a directory.
724 ********************************************************************/
725
726 void CloseDir(void *p)
727 {
728   Dir *dirp = (Dir *)p;
729   if (!dirp) return;    
730   if (dirp->data) free(dirp->data);
731   free(dirp);
732 }
733
734 /*******************************************************************
735  Read from a directory.
736 ********************************************************************/
737
738 char *ReadDirName(void *p)
739 {
740   char *ret;
741   Dir *dirp = (Dir *)p;
742
743   if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
744
745   ret = dirp->current;
746   dirp->current = skip_string(dirp->current,1);
747   dirp->pos++;
748
749   return(ret);
750 }
751
752
753 /*******************************************************************
754  Seek a dir.
755 ********************************************************************/
756
757 BOOL SeekDir(void *p,int pos)
758 {
759   Dir *dirp = (Dir *)p;
760
761   if (!dirp) return(False);
762
763   if (pos < dirp->pos) {
764     dirp->current = dirp->data;
765     dirp->pos = 0;
766   }
767
768   while (dirp->pos < pos && ReadDirName(p)) ;
769
770   return(dirp->pos == pos);
771 }
772
773 /*******************************************************************
774  Tell a dir position.
775 ********************************************************************/
776
777 int TellDir(void *p)
778 {
779   Dir *dirp = (Dir *)p;
780
781   if (!dirp) return(-1);
782   
783   return(dirp->pos);
784 }
785
786 /*******************************************************************************
787  This section manages a global directory cache.
788  (It should probably be split into a separate module.  crh)
789 ********************************************************************************/
790
791 typedef struct {
792   ubi_dlNode  node;
793   char       *path;
794   char       *name;
795   char       *dname;
796   int         snum;
797 } dir_cache_entry;
798
799 static ubi_dlNewList( dir_cache );
800
801 /*****************************************************************************
802  Add an entry to the directory cache.
803  Input:  path  -
804          name  -
805          dname -
806          snum  -
807  Output: None.
808 *****************************************************************************/
809
810 void DirCacheAdd( char *path, char *name, char *dname, int snum )
811 {
812   int               pathlen;
813   int               namelen;
814   dir_cache_entry  *entry;
815
816   /* Allocate the structure & string space in one go so that it can be freed
817    * in one call to free().
818    */
819   pathlen = strlen( path ) +1;  /* Bytes required to store path (with nul). */
820   namelen = strlen( name ) +1;  /* Bytes required to store name (with nul). */
821   entry = (dir_cache_entry *)malloc( sizeof( dir_cache_entry )
822                                    + pathlen
823                                    + namelen
824                                    + strlen( dname ) +1 );
825   if( NULL == entry )   /* Not adding to the cache is not fatal,  */
826     return;             /* so just return as if nothing happened. */
827
828   /* Set pointers correctly and load values. */
829   entry->path  = pstrcpy( (char *)&entry[1],       path);
830   entry->name  = pstrcpy( &(entry->path[pathlen]), name);
831   entry->dname = pstrcpy( &(entry->name[namelen]), dname);
832   entry->snum  = snum;
833
834   /* Add the new entry to the linked list. */
835   (void)ubi_dlAddHead( dir_cache, entry );
836   DEBUG( 4, ("Added dir cache entry %s %s -> %s\n", path, name, dname ) );
837
838   /* Free excess cache entries. */
839   while( DIRCACHESIZE < dir_cache->count )
840     free( ubi_dlRemTail( dir_cache ) );
841
842 }
843
844 /*****************************************************************************
845  Search for an entry to the directory cache.
846  Input:  path  -
847          name  -
848          snum  -
849  Output: The dname string of the located entry, or NULL if the entry was
850          not found.
851
852  Notes:  This uses a linear search, which is is okay because of
853          the small size of the cache.  Use a splay tree or hash
854          for large caches.
855 *****************************************************************************/
856
857 char *DirCacheCheck( char *path, char *name, int snum )
858 {
859   dir_cache_entry *entry;
860
861   for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
862        NULL != entry;
863        entry = (dir_cache_entry *)ubi_dlNext( entry ) )
864     {
865     if( entry->snum == snum
866         && 0 == strcmp( name, entry->name )
867         && 0 == strcmp( path, entry->path ) )
868       {
869       DEBUG(4, ("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
870       return( entry->dname );
871       }
872     }
873
874   return(NULL);
875 }
876
877 /*****************************************************************************
878  Remove all cache entries which have an snum that matches the input.
879  Input:  snum  -
880  Output: None.
881 *****************************************************************************/
882
883 void DirCacheFlush(int snum)
884 {
885         dir_cache_entry *entry;
886         ubi_dlNodePtr    next;
887
888         for(entry = (dir_cache_entry *)ubi_dlFirst( dir_cache ); 
889             NULL != entry; )  {
890                 next = ubi_dlNext( entry );
891                 if( entry->snum == snum )
892                         free( ubi_dlRemThis( dir_cache, entry ) );
893                 entry = (dir_cache_entry *)next;
894         }
895 }
896
897 #undef OLD_NTDOMAIN