da907b27a20eefe52b86d347e93bcbc8f3f8caaf
[samba.git] / source / smbd / dir.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Directory handling routines
5    Copyright (C) Andrew Tridgell 1992-1998
6    
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.
11    
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.
16    
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.
20 */
21
22 #include "includes.h"
23
24 extern int DEBUGLEVEL;
25
26 /*
27    This module implements directory related functions for Samba.
28 */
29
30
31
32 static uint32 dircounter = 0;
33
34
35 #define NUMDIRPTRS 256
36
37
38 static struct dptr_struct {
39         int pid;
40         connection_struct *conn;
41         uint32 lastused;
42         void *ptr;
43         BOOL valid;
44         BOOL finished;
45         BOOL expect_close;
46         char *wcard; /* Field only used for trans2_ searches */
47         uint16 attr; /* Field only used for trans2_ searches */
48         char *path;
49 }
50 dirptrs[NUMDIRPTRS];
51
52
53 static int dptrs_open = 0;
54
55 /****************************************************************************
56 initialise the dir array
57 ****************************************************************************/
58 void init_dptrs(void)
59 {
60   static BOOL dptrs_init=False;
61   int i;
62
63   if (dptrs_init) return;
64   for (i=0;i<NUMDIRPTRS;i++)    
65     {
66       dirptrs[i].valid = False;
67       dirptrs[i].wcard = NULL;
68       dirptrs[i].ptr = NULL;
69       string_init(&dirptrs[i].path,"");
70     }
71   dptrs_init = True;
72 }
73
74 /****************************************************************************
75 idle a dptr - the directory is closed but the control info is kept
76 ****************************************************************************/
77 static void dptr_idle(int key)
78 {
79   if (dirptrs[key].valid && dirptrs[key].ptr) {
80     DEBUG(4,("Idling dptr key %d\n",key));
81     dptrs_open--;
82     CloseDir(dirptrs[key].ptr);
83     dirptrs[key].ptr = NULL;
84   }    
85 }
86
87 /****************************************************************************
88 idle the oldest dptr
89 ****************************************************************************/
90 static void dptr_idleoldest(void)
91 {
92   int i;
93   uint32 old=dircounter+1;
94   int oldi= -1;
95   for (i=0;i<NUMDIRPTRS;i++)
96     if (dirptrs[i].valid && dirptrs[i].ptr && dirptrs[i].lastused < old) {
97       old = dirptrs[i].lastused;
98       oldi = i;
99     }
100   if (oldi != -1)
101     dptr_idle(oldi);
102   else
103     DEBUG(0,("No dptrs available to idle??\n"));
104 }
105
106 /****************************************************************************
107 get the dir ptr for a dir index
108 ****************************************************************************/
109 static void *dptr_get(int key,uint32 lastused)
110 {
111         struct dptr_struct *dp = &dirptrs[key];
112
113         if (dp->valid) {
114                 if (lastused) dp->lastused = lastused;
115                 if (!dp->ptr) {
116                         if (dptrs_open >= MAX_OPEN_DIRECTORIES)
117                                 dptr_idleoldest();
118                         DEBUG(4,("Reopening dptr key %d\n",key));
119                         if ((dp->ptr = OpenDir(dp->conn, dp->path, True)))
120                                 dptrs_open++;
121                 }
122                 return(dp->ptr);
123         }
124         return(NULL);
125 }
126
127 /****************************************************************************
128 get the dir path for a dir index
129 ****************************************************************************/
130 char *dptr_path(int key)
131 {
132   if (dirptrs[key].valid)
133     return(dirptrs[key].path);
134   return(NULL);
135 }
136
137 /****************************************************************************
138 get the dir wcard for a dir index (lanman2 specific)
139 ****************************************************************************/
140 char *dptr_wcard(int key)
141 {
142   if (dirptrs[key].valid)
143     return(dirptrs[key].wcard);
144   return(NULL);
145 }
146
147 /****************************************************************************
148 set the dir wcard for a dir index (lanman2 specific)
149 Returns 0 on ok, 1 on fail.
150 ****************************************************************************/
151 BOOL dptr_set_wcard(int key, char *wcard)
152 {
153   if (dirptrs[key].valid) {
154     dirptrs[key].wcard = wcard;
155     return True;
156   }
157   return False;
158 }
159
160 /****************************************************************************
161 set the dir attrib for a dir index (lanman2 specific)
162 Returns 0 on ok, 1 on fail.
163 ****************************************************************************/
164 BOOL dptr_set_attr(int key, uint16 attr)
165 {
166   if (dirptrs[key].valid) {
167     dirptrs[key].attr = attr;
168     return True;
169   }
170   return False;
171 }
172
173 /****************************************************************************
174 get the dir attrib for a dir index (lanman2 specific)
175 ****************************************************************************/
176 uint16 dptr_attr(int key)
177 {
178   if (dirptrs[key].valid)
179     return(dirptrs[key].attr);
180   return(0);
181 }
182
183 /****************************************************************************
184 close a dptr
185 ****************************************************************************/
186 void dptr_close(int key)
187 {
188   /* OS/2 seems to use -1 to indicate "close all directories" */
189   if (key == -1) {
190     int i;
191     for (i=0;i<NUMDIRPTRS;i++) 
192       dptr_close(i);
193     return;
194   }
195
196   if (key < 0 || key >= NUMDIRPTRS) {
197     DEBUG(3,("Invalid key %d given to dptr_close\n",key));
198     return;
199   }
200
201   if (dirptrs[key].valid) {
202     DEBUG(4,("closing dptr key %d\n",key));
203     if (dirptrs[key].ptr) {
204       CloseDir(dirptrs[key].ptr);
205       dptrs_open--;
206     }
207     /* Lanman 2 specific code */
208     if (dirptrs[key].wcard)
209       free(dirptrs[key].wcard);
210     dirptrs[key].valid = False;
211     string_set(&dirptrs[key].path,"");
212   }
213 }
214
215 /****************************************************************************
216 close all dptrs for a cnum
217 ****************************************************************************/
218 void dptr_closecnum(connection_struct *conn)
219 {
220   int i;
221   for (i=0;i<NUMDIRPTRS;i++)
222     if (dirptrs[i].valid && dirptrs[i].conn == conn)
223       dptr_close(i);
224 }
225
226 /****************************************************************************
227 idle all dptrs for a cnum
228 ****************************************************************************/
229 void dptr_idlecnum(connection_struct *conn)
230 {
231   int i;
232   for (i=0;i<NUMDIRPTRS;i++)
233     if (dirptrs[i].valid && dirptrs[i].conn == conn && dirptrs[i].ptr)
234       dptr_idle(i);
235 }
236
237 /****************************************************************************
238 close a dptr that matches a given path, only if it matches the pid also
239 ****************************************************************************/
240 void dptr_closepath(char *path,int pid)
241 {
242   int i;
243   for (i=0;i<NUMDIRPTRS;i++)
244     if (dirptrs[i].valid && pid == dirptrs[i].pid &&
245         strequal(dirptrs[i].path,path))
246       dptr_close(i);
247 }
248
249 /****************************************************************************
250   start a directory listing
251 ****************************************************************************/
252 static BOOL start_dir(connection_struct *conn,char *directory)
253 {
254         DEBUG(5,("start_dir dir=%s\n",directory));
255
256         if (!check_name(directory,conn))
257                 return(False);
258   
259         if (! *directory)
260                 directory = ".";
261
262         conn->dirptr = OpenDir(conn, directory, True);
263         if (conn->dirptr) {    
264                 dptrs_open++;
265                 string_set(&conn->dirpath,directory);
266                 return(True);
267         }
268   
269         return(False);
270 }
271
272
273 /****************************************************************************
274 create a new dir ptr
275 ****************************************************************************/
276 int dptr_create(connection_struct *conn,char *path, BOOL expect_close,int pid)
277 {
278   int i;
279   uint32 old;
280   int oldi;
281
282   if (!start_dir(conn,path))
283     return(-2); /* Code to say use a unix error return code. */
284
285   if (dptrs_open >= MAX_OPEN_DIRECTORIES)
286     dptr_idleoldest();
287
288   for (i=0;i<NUMDIRPTRS;i++)
289     if (!dirptrs[i].valid)
290       break;
291   if (i == NUMDIRPTRS) i = -1;
292
293
294   /* as a 2nd option, grab the oldest not marked for expect_close */
295   if (i == -1) {
296     old=dircounter+1;
297     oldi= -1;
298     for (i=0;i<NUMDIRPTRS;i++)
299       if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) {
300         old = dirptrs[i].lastused;
301         oldi = i;
302       }
303     i = oldi;
304   }
305
306   /* a 3rd option - grab the oldest one */
307   if (i == -1) {
308     old=dircounter+1;
309     oldi= -1;
310     for (i=0;i<NUMDIRPTRS;i++)
311       if (dirptrs[i].lastused < old) {
312         old = dirptrs[i].lastused;
313         oldi = i;
314       }
315     i = oldi;
316   }
317
318   if (i == -1) {
319     DEBUG(0,("Error - all dirptrs in use??\n"));
320     return(-1);
321   }
322
323   if (dirptrs[i].valid)
324     dptr_close(i);
325
326   dirptrs[i].ptr = conn->dirptr;
327   string_set(&dirptrs[i].path,path);
328   dirptrs[i].lastused = dircounter++;
329   dirptrs[i].finished = False;
330   dirptrs[i].conn = conn;
331   dirptrs[i].pid = pid;
332   dirptrs[i].expect_close = expect_close;
333   dirptrs[i].wcard = NULL; /* Only used in lanman2 searches */
334   dirptrs[i].attr = 0; /* Only used in lanman2 searches */
335   dirptrs[i].valid = True;
336
337   DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
338            i,path,expect_close));  
339
340   return(i);
341 }
342
343 #define DPTR_MASK ((uint32)(((uint32)1)<<31))
344
345 /****************************************************************************
346 fill the 5 byte server reserved dptr field
347 ****************************************************************************/
348 BOOL dptr_fill(char *buf1,unsigned int key)
349 {
350   unsigned char *buf = (unsigned char *)buf1;
351   void *p = dptr_get(key,0);
352   uint32 offset;
353   if (!p) {
354     DEBUG(1,("filling null dirptr %d\n",key));
355     return(False);
356   }
357   offset = TellDir(p);
358   DEBUG(6,("fill on key %ld dirptr 0x%x now at %d\n",key,
359            (long)p,offset));
360   buf[0] = key;
361   SIVAL(buf,1,offset | DPTR_MASK);
362   return(True);
363 }
364
365
366 /****************************************************************************
367 return True is the offset is at zero
368 ****************************************************************************/
369 BOOL dptr_zero(char *buf)
370 {
371   return((IVAL(buf,1)&~DPTR_MASK) == 0);
372 }
373
374 /****************************************************************************
375 fetch the dir ptr and seek it given the 5 byte server field
376 ****************************************************************************/
377 void *dptr_fetch(char *buf,int *num)
378 {
379   unsigned int key = *(unsigned char *)buf;
380   void *p = dptr_get(key,dircounter++);
381   uint32 offset;
382   if (!p) {
383     DEBUG(3,("fetched null dirptr %d\n",key));
384     return(NULL);
385   }
386   *num = key;
387   offset = IVAL(buf,1)&~DPTR_MASK;
388   SeekDir(p,offset);
389   DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
390            key,dptr_path(key),offset));
391   return(p);
392 }
393
394 /****************************************************************************
395 fetch the dir ptr.
396 ****************************************************************************/
397 void *dptr_fetch_lanman2(int dptr_num)
398 {
399   void *p = dptr_get(dptr_num,dircounter++);
400
401   if (!p) {
402     DEBUG(3,("fetched null dirptr %d\n",dptr_num));
403     return(NULL);
404   }
405   DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
406   return(p);
407 }
408
409 /****************************************************************************
410 check a filetype for being valid
411 ****************************************************************************/
412 BOOL dir_check_ftype(connection_struct *conn,int mode,SMB_STRUCT_STAT *st,int dirtype)
413 {
414   if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
415     return False;
416   return True;
417 }
418
419 /****************************************************************************
420   get a directory entry
421 ****************************************************************************/
422 BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype,char *fname,
423                    SMB_OFF_T *size,int *mode,time_t *date,BOOL check_descend)
424 {
425   char *dname;
426   BOOL found = False;
427   SMB_STRUCT_STAT sbuf;
428   pstring path;
429   pstring pathreal;
430   BOOL isrootdir;
431   pstring filename;
432   BOOL needslash;
433
434   *path = *pathreal = *filename = 0;
435
436   isrootdir = (strequal(conn->dirpath,"./") ||
437                strequal(conn->dirpath,".") ||
438                strequal(conn->dirpath,"/"));
439   
440   needslash = 
441         ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
442
443   if (!conn->dirptr)
444     return(False);
445   
446   while (!found)
447     {
448       dname = ReadDirName(conn->dirptr);
449
450       DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n",
451                (long)conn->dirptr,TellDir(conn->dirptr)));
452       
453       if (dname == NULL) 
454         return(False);
455       
456       pstrcpy(filename,dname);      
457
458       if ((strcmp(filename,mask) == 0) ||
459           (name_map_mangle(filename,True,SNUM(conn)) &&
460            mask_match(filename,mask,False,False)))
461         {
462           if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
463             continue;
464
465           pstrcpy(fname,filename);
466           *path = 0;
467           pstrcpy(path,conn->dirpath);
468           if(needslash)
469             pstrcat(path,"/");
470           pstrcpy(pathreal,path);
471           pstrcat(path,fname);
472           pstrcat(pathreal,dname);
473           if (dos_stat(pathreal,&sbuf) != 0) 
474             {
475               DEBUG(5,("Couldn't stat 1 [%s]\n",path));
476               continue;
477             }
478
479           if (check_descend &&
480               !strequal(fname,".") && !strequal(fname,".."))
481             continue;
482           
483           *mode = dos_mode(conn,pathreal,&sbuf);
484
485           if (!dir_check_ftype(conn,*mode,&sbuf,dirtype)) {
486             DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
487             continue;
488           }
489
490           *size = sbuf.st_size;
491           *date = sbuf.st_mtime;
492
493           DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname));
494           
495           found = True;
496         }
497     }
498
499   return(found);
500 }
501
502
503
504 typedef struct
505 {
506   int pos;
507   int numentries;
508   int mallocsize;
509   char *data;
510   char *current;
511 } Dir;
512
513
514 /*******************************************************************
515 open a directory
516 ********************************************************************/
517 void *OpenDir(connection_struct *conn, char *name, BOOL use_veto)
518 {
519   Dir *dirp;
520   char *n;
521   void *p = dos_opendir(name);
522   int used=0;
523
524   if (!p) return(NULL);
525   dirp = (Dir *)malloc(sizeof(Dir));
526   if (!dirp) {
527     closedir(p);
528     return(NULL);
529   }
530   dirp->pos = dirp->numentries = dirp->mallocsize = 0;
531   dirp->data = dirp->current = NULL;
532
533   while ((n = readdirname(p)))
534   {
535     int l = strlen(n)+1;
536
537     /* If it's a vetoed file, pretend it doesn't even exist */
538     if (use_veto && conn && IS_VETO_PATH(conn, n)) continue;
539
540     if (used + l > dirp->mallocsize) {
541       int s = MAX(used+l,used+2000);
542       char *r;
543       r = (char *)Realloc(dirp->data,s);
544       if (!r) {
545         DEBUG(0,("Out of memory in OpenDir\n"));
546         break;
547       }
548       dirp->data = r;
549       dirp->mallocsize = s;
550       dirp->current = dirp->data;
551     }
552     pstrcpy(dirp->data+used,n);
553     used += l;
554     dirp->numentries++;
555   }
556
557   closedir(p);
558   return((void *)dirp);
559 }
560
561
562 /*******************************************************************
563 close a directory
564 ********************************************************************/
565 void CloseDir(void *p)
566 {
567   Dir *dirp = (Dir *)p;
568   if (!dirp) return;    
569   if (dirp->data) free(dirp->data);
570   free(dirp);
571 }
572
573 /*******************************************************************
574 read from a directory
575 ********************************************************************/
576 char *ReadDirName(void *p)
577 {
578   char *ret;
579   Dir *dirp = (Dir *)p;
580
581   if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
582
583   ret = dirp->current;
584   dirp->current = skip_string(dirp->current,1);
585   dirp->pos++;
586
587   return(ret);
588 }
589
590
591 /*******************************************************************
592 seek a dir
593 ********************************************************************/
594 BOOL SeekDir(void *p,int pos)
595 {
596   Dir *dirp = (Dir *)p;
597
598   if (!dirp) return(False);
599
600   if (pos < dirp->pos) {
601     dirp->current = dirp->data;
602     dirp->pos = 0;
603   }
604
605   while (dirp->pos < pos && ReadDirName(p)) ;
606
607   return(dirp->pos == pos);
608 }
609
610 /*******************************************************************
611 tell a dir position
612 ********************************************************************/
613 int TellDir(void *p)
614 {
615   Dir *dirp = (Dir *)p;
616
617   if (!dirp) return(-1);
618   
619   return(dirp->pos);
620 }
621
622
623 /* -------------------------------------------------------------------------- **
624  * This section manages a global directory cache.
625  * (It should probably be split into a separate module.  crh)
626  * -------------------------------------------------------------------------- **
627  */
628
629 typedef struct
630   {
631   ubi_dlNode  node;
632   char       *path;
633   char       *name;
634   char       *dname;
635   int         snum;
636   } dir_cache_entry;
637
638 static ubi_dlNewList( dir_cache );
639
640 void DirCacheAdd( char *path, char *name, char *dname, int snum )
641   /* ------------------------------------------------------------------------ **
642    * Add an entry to the directory cache.
643    *
644    *  Input:  path  -
645    *          name  -
646    *          dname -
647    *          snum  -
648    *
649    *  Output: None.
650    *
651    * ------------------------------------------------------------------------ **
652    */
653   {
654   int               pathlen;
655   int               namelen;
656   dir_cache_entry  *entry;
657
658   /* Allocate the structure & string space in one go so that it can be freed
659    * in one call to free().
660    */
661   pathlen = strlen( path ) +1;  /* Bytes required to store path (with nul). */
662   namelen = strlen( name ) +1;  /* Bytes required to store name (with nul). */
663   entry = (dir_cache_entry *)malloc( sizeof( dir_cache_entry )
664                                    + pathlen
665                                    + namelen
666                                    + strlen( dname ) +1 );
667   if( NULL == entry )   /* Not adding to the cache is not fatal,  */
668     return;             /* so just return as if nothing happened. */
669
670   /* Set pointers correctly and load values. */
671   entry->path  = pstrcpy( (char *)&entry[1],       path);
672   entry->name  = pstrcpy( &(entry->path[pathlen]), name);
673   entry->dname = pstrcpy( &(entry->name[namelen]), dname);
674   entry->snum  = snum;
675
676   /* Add the new entry to the linked list. */
677   (void)ubi_dlAddHead( dir_cache, entry );
678   DEBUG( 4, ("Added dir cache entry %s %s -> %s\n", path, name, dname ) );
679
680   /* Free excess cache entries. */
681   while( DIRCACHESIZE < dir_cache->count )
682     free( ubi_dlRemTail( dir_cache ) );
683
684   } /* DirCacheAdd */
685
686
687 char *DirCacheCheck( char *path, char *name, int snum )
688   /* ------------------------------------------------------------------------ **
689    * Search for an entry to the directory cache.
690    *
691    *  Input:  path  -
692    *          name  -
693    *          snum  -
694    *
695    *  Output: The dname string of the located entry, or NULL if the entry was
696    *          not found.
697    *
698    *  Notes:  This uses a linear search, which is is okay because of
699    *          the small size of the cache.  Use a splay tree or hash
700    *          for large caches.
701    *
702    * ------------------------------------------------------------------------ **
703    */
704   {
705   dir_cache_entry *entry;
706
707   for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
708        NULL != entry;
709        entry = (dir_cache_entry *)ubi_dlNext( entry ) )
710     {
711     if( entry->snum == snum
712         && 0 == strcmp( name, entry->name )
713         && 0 == strcmp( path, entry->path ) )
714       {
715       DEBUG(4, ("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
716       return( entry->dname );
717       }
718     }
719
720   return(NULL);
721   } /* DirCacheCheck */
722
723 void DirCacheFlush(int snum)
724   /* ------------------------------------------------------------------------ **
725    * Remove all cache entries which have an snum that matches the input.
726    *
727    *  Input:  snum  -
728    *
729    *  Output: None.
730    *
731    * ------------------------------------------------------------------------ **
732    */
733 {
734         dir_cache_entry *entry;
735         ubi_dlNodePtr    next;
736
737         for(entry = (dir_cache_entry *)ubi_dlFirst( dir_cache ); 
738             NULL != entry; )  {
739                 next = ubi_dlNext( entry );
740                 if( entry->snum == snum )
741                         free( ubi_dlRemThis( dir_cache, entry ) );
742                 entry = (dir_cache_entry *)next;
743         }
744 } /* DirCacheFlush */
745
746 /* -------------------------------------------------------------------------- **
747  * End of the section that manages the global directory cache.
748  * -------------------------------------------------------------------------- **
749  */
750
751