2 Unix SMB/Netbios implementation.
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.
24 extern int DEBUGLEVEL;
27 This module implements directory related functions for Samba.
32 static uint32 dircounter = 0;
35 #define NUMDIRPTRS 256
38 static struct dptr_struct {
40 connection_struct *conn;
46 char *wcard; /* Field only used for trans2_ searches */
47 uint16 attr; /* Field only used for trans2_ searches */
53 static int dptrs_open = 0;
55 /****************************************************************************
56 initialise the dir array
57 ****************************************************************************/
60 static BOOL dptrs_init=False;
63 if (dptrs_init) return;
64 for (i=0;i<NUMDIRPTRS;i++)
66 dirptrs[i].valid = False;
67 dirptrs[i].wcard = NULL;
68 dirptrs[i].ptr = NULL;
69 string_init(&dirptrs[i].path,"");
74 /****************************************************************************
75 idle a dptr - the directory is closed but the control info is kept
76 ****************************************************************************/
77 static void dptr_idle(int key)
79 if (dirptrs[key].valid && dirptrs[key].ptr) {
80 DEBUG(4,("Idling dptr key %d\n",key));
82 CloseDir(dirptrs[key].ptr);
83 dirptrs[key].ptr = NULL;
87 /****************************************************************************
89 ****************************************************************************/
90 static void dptr_idleoldest(void)
93 uint32 old=dircounter+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;
103 DEBUG(0,("No dptrs available to idle??\n"));
106 /****************************************************************************
107 get the dir ptr for a dir index
108 ****************************************************************************/
109 static void *dptr_get(int key,uint32 lastused)
111 struct dptr_struct *dp = &dirptrs[key];
114 if (lastused) dp->lastused = lastused;
116 if (dptrs_open >= MAX_OPEN_DIRECTORIES)
118 DEBUG(4,("Reopening dptr key %d\n",key));
119 if ((dp->ptr = OpenDir(dp->conn, dp->path, True)))
127 /****************************************************************************
128 get the dir path for a dir index
129 ****************************************************************************/
130 char *dptr_path(int key)
132 if (dirptrs[key].valid)
133 return(dirptrs[key].path);
137 /****************************************************************************
138 get the dir wcard for a dir index (lanman2 specific)
139 ****************************************************************************/
140 char *dptr_wcard(int key)
142 if (dirptrs[key].valid)
143 return(dirptrs[key].wcard);
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)
153 if (dirptrs[key].valid) {
154 dirptrs[key].wcard = wcard;
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)
166 if (dirptrs[key].valid) {
167 dirptrs[key].attr = attr;
173 /****************************************************************************
174 get the dir attrib for a dir index (lanman2 specific)
175 ****************************************************************************/
176 uint16 dptr_attr(int key)
178 if (dirptrs[key].valid)
179 return(dirptrs[key].attr);
183 /****************************************************************************
185 ****************************************************************************/
186 void dptr_close(int key)
188 /* OS/2 seems to use -1 to indicate "close all directories" */
191 for (i=0;i<NUMDIRPTRS;i++)
196 if (key < 0 || key >= NUMDIRPTRS) {
197 DEBUG(3,("Invalid key %d given to dptr_close\n",key));
201 if (dirptrs[key].valid) {
202 DEBUG(4,("closing dptr key %d\n",key));
203 if (dirptrs[key].ptr) {
204 CloseDir(dirptrs[key].ptr);
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,"");
215 /****************************************************************************
216 close all dptrs for a cnum
217 ****************************************************************************/
218 void dptr_closecnum(connection_struct *conn)
221 for (i=0;i<NUMDIRPTRS;i++)
222 if (dirptrs[i].valid && dirptrs[i].conn == conn)
226 /****************************************************************************
227 idle all dptrs for a cnum
228 ****************************************************************************/
229 void dptr_idlecnum(connection_struct *conn)
232 for (i=0;i<NUMDIRPTRS;i++)
233 if (dirptrs[i].valid && dirptrs[i].conn == conn && dirptrs[i].ptr)
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)
243 for (i=0;i<NUMDIRPTRS;i++)
244 if (dirptrs[i].valid && pid == dirptrs[i].pid &&
245 strequal(dirptrs[i].path,path))
249 /****************************************************************************
250 start a directory listing
251 ****************************************************************************/
252 static BOOL start_dir(connection_struct *conn,char *directory)
254 DEBUG(5,("start_dir dir=%s\n",directory));
256 if (!check_name(directory,conn))
262 conn->dirptr = OpenDir(conn, directory, True);
265 string_set(&conn->dirpath,directory);
273 /****************************************************************************
275 ****************************************************************************/
276 int dptr_create(connection_struct *conn,char *path, BOOL expect_close,int pid)
282 if (!start_dir(conn,path))
283 return(-2); /* Code to say use a unix error return code. */
285 if (dptrs_open >= MAX_OPEN_DIRECTORIES)
288 for (i=0;i<NUMDIRPTRS;i++)
289 if (!dirptrs[i].valid)
291 if (i == NUMDIRPTRS) i = -1;
294 /* as a 2nd option, grab the oldest not marked for expect_close */
298 for (i=0;i<NUMDIRPTRS;i++)
299 if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) {
300 old = dirptrs[i].lastused;
306 /* a 3rd option - grab the oldest one */
310 for (i=0;i<NUMDIRPTRS;i++)
311 if (dirptrs[i].lastused < old) {
312 old = dirptrs[i].lastused;
319 DEBUG(0,("Error - all dirptrs in use??\n"));
323 if (dirptrs[i].valid)
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;
337 DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
338 i,path,expect_close));
343 #define DPTR_MASK ((uint32)(((uint32)1)<<31))
345 /****************************************************************************
346 fill the 5 byte server reserved dptr field
347 ****************************************************************************/
348 BOOL dptr_fill(char *buf1,unsigned int key)
350 unsigned char *buf = (unsigned char *)buf1;
351 void *p = dptr_get(key,0);
354 DEBUG(1,("filling null dirptr %d\n",key));
358 DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key,
359 (long)p,(int)offset));
361 SIVAL(buf,1,offset | DPTR_MASK);
366 /****************************************************************************
367 return True is the offset is at zero
368 ****************************************************************************/
369 BOOL dptr_zero(char *buf)
371 return((IVAL(buf,1)&~DPTR_MASK) == 0);
374 /****************************************************************************
375 fetch the dir ptr and seek it given the 5 byte server field
376 ****************************************************************************/
377 void *dptr_fetch(char *buf,int *num)
379 unsigned int key = *(unsigned char *)buf;
380 void *p = dptr_get(key,dircounter++);
383 DEBUG(3,("fetched null dirptr %d\n",key));
387 offset = IVAL(buf,1)&~DPTR_MASK;
389 DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
390 key,dptr_path(key),offset));
394 /****************************************************************************
396 ****************************************************************************/
397 void *dptr_fetch_lanman2(int dptr_num)
399 void *p = dptr_get(dptr_num,dircounter++);
402 DEBUG(3,("fetched null dirptr %d\n",dptr_num));
405 DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
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)
414 if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
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)
427 SMB_STRUCT_STAT sbuf;
434 *path = *pathreal = *filename = 0;
436 isrootdir = (strequal(conn->dirpath,"./") ||
437 strequal(conn->dirpath,".") ||
438 strequal(conn->dirpath,"/"));
441 ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
448 dname = ReadDirName(conn->dirptr);
450 DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n",
451 (long)conn->dirptr,TellDir(conn->dirptr)));
456 pstrcpy(filename,dname);
458 if ((strcmp(filename,mask) == 0) ||
459 (name_map_mangle(filename,True,SNUM(conn)) &&
460 mask_match(filename,mask,False,False)))
462 if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
465 pstrcpy(fname,filename);
467 pstrcpy(path,conn->dirpath);
470 pstrcpy(pathreal,path);
472 pstrcat(pathreal,dname);
473 if (dos_stat(pathreal,&sbuf) != 0)
475 DEBUG(5,("Couldn't stat 1 [%s]\n",path));
480 !strequal(fname,".") && !strequal(fname,".."))
483 *mode = dos_mode(conn,pathreal,&sbuf);
485 if (!dir_check_ftype(conn,*mode,&sbuf,dirtype)) {
486 DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
490 *size = sbuf.st_size;
491 *date = sbuf.st_mtime;
493 DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname));
514 /*******************************************************************
516 ********************************************************************/
517 void *OpenDir(connection_struct *conn, char *name, BOOL use_veto)
521 void *p = dos_opendir(name);
524 if (!p) return(NULL);
525 dirp = (Dir *)malloc(sizeof(Dir));
530 dirp->pos = dirp->numentries = dirp->mallocsize = 0;
531 dirp->data = dirp->current = NULL;
533 while ((n = readdirname(p)))
537 /* If it's a vetoed file, pretend it doesn't even exist */
538 if (use_veto && conn && IS_VETO_PATH(conn, n)) continue;
540 if (used + l > dirp->mallocsize) {
541 int s = MAX(used+l,used+2000);
543 r = (char *)Realloc(dirp->data,s);
545 DEBUG(0,("Out of memory in OpenDir\n"));
549 dirp->mallocsize = s;
550 dirp->current = dirp->data;
552 pstrcpy(dirp->data+used,n);
558 return((void *)dirp);
562 /*******************************************************************
564 ********************************************************************/
565 void CloseDir(void *p)
567 Dir *dirp = (Dir *)p;
569 if (dirp->data) free(dirp->data);
573 /*******************************************************************
574 read from a directory
575 ********************************************************************/
576 char *ReadDirName(void *p)
579 Dir *dirp = (Dir *)p;
581 if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
584 dirp->current = skip_string(dirp->current,1);
591 /*******************************************************************
593 ********************************************************************/
594 BOOL SeekDir(void *p,int pos)
596 Dir *dirp = (Dir *)p;
598 if (!dirp) return(False);
600 if (pos < dirp->pos) {
601 dirp->current = dirp->data;
605 while (dirp->pos < pos && ReadDirName(p)) ;
607 return(dirp->pos == pos);
610 /*******************************************************************
612 ********************************************************************/
615 Dir *dirp = (Dir *)p;
617 if (!dirp) return(-1);
623 /* -------------------------------------------------------------------------- **
624 * This section manages a global directory cache.
625 * (It should probably be split into a separate module. crh)
626 * -------------------------------------------------------------------------- **
638 static ubi_dlNewList( dir_cache );
640 void DirCacheAdd( char *path, char *name, char *dname, int snum )
641 /* ------------------------------------------------------------------------ **
642 * Add an entry to the directory cache.
651 * ------------------------------------------------------------------------ **
656 dir_cache_entry *entry;
658 /* Allocate the structure & string space in one go so that it can be freed
659 * in one call to free().
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 )
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. */
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);
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 ) );
680 /* Free excess cache entries. */
681 while( DIRCACHESIZE < dir_cache->count )
682 free( ubi_dlRemTail( dir_cache ) );
687 char *DirCacheCheck( char *path, char *name, int snum )
688 /* ------------------------------------------------------------------------ **
689 * Search for an entry to the directory cache.
695 * Output: The dname string of the located entry, or NULL if the entry was
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
702 * ------------------------------------------------------------------------ **
705 dir_cache_entry *entry;
707 for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
709 entry = (dir_cache_entry *)ubi_dlNext( entry ) )
711 if( entry->snum == snum
712 && 0 == strcmp( name, entry->name )
713 && 0 == strcmp( path, entry->path ) )
715 DEBUG(4, ("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
716 return( entry->dname );
721 } /* DirCacheCheck */
723 void DirCacheFlush(int snum)
724 /* ------------------------------------------------------------------------ **
725 * Remove all cache entries which have an snum that matches the input.
731 * ------------------------------------------------------------------------ **
734 dir_cache_entry *entry;
737 for(entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
739 next = ubi_dlNext( entry );
740 if( entry->snum == snum )
741 free( ubi_dlRemThis( dir_cache, entry ) );
742 entry = (dir_cache_entry *)next;
744 } /* DirCacheFlush */
746 /* -------------------------------------------------------------------------- **
747 * End of the section that manages the global directory cache.
748 * -------------------------------------------------------------------------- **