2 Unix SMB/Netbios implementation.
4 Directory handling routines
5 Copyright (C) Andrew Tridgell 1992-1995
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.
25 extern int DEBUGLEVEL;
26 extern connection_struct Connections[];
29 This module implements directory related functions for Samba.
34 uint32 dircounter = 0;
37 #define NUMDIRPTRS 256
40 static struct dptr_struct
49 char *wcard; /* Field only used for lanman2 trans2_findfirst/next searches */
50 uint16 attr; /* Field only used for lanman2 trans2_findfirst/next searches */
56 static int dptrs_open = 0;
58 /****************************************************************************
59 initialise the dir array
60 ****************************************************************************/
63 static BOOL dptrs_init=False;
66 if (dptrs_init) return;
67 for (i=0;i<NUMDIRPTRS;i++)
69 dirptrs[i].valid = False;
70 dirptrs[i].wcard = NULL;
71 dirptrs[i].ptr = NULL;
72 string_init(&dirptrs[i].path,"");
77 /****************************************************************************
78 idle a dptr - the directory is closed but the control info is kept
79 ****************************************************************************/
80 static void dptr_idle(int key)
82 if (dirptrs[key].valid && dirptrs[key].ptr) {
83 DEBUG(4,("Idling dptr key %d\n",key));
85 CloseDir(dirptrs[key].ptr);
86 dirptrs[key].ptr = NULL;
90 /****************************************************************************
92 ****************************************************************************/
93 static void dptr_idleoldest(void)
96 uint32 old=dircounter+1;
98 for (i=0;i<NUMDIRPTRS;i++)
99 if (dirptrs[i].valid && dirptrs[i].ptr && dirptrs[i].lastused < old) {
100 old = dirptrs[i].lastused;
106 DEBUG(0,("No dptrs available to idle??\n"));
109 /****************************************************************************
110 get the dir ptr for a dir index
111 ****************************************************************************/
112 static void *dptr_get(int key,uint32 lastused)
114 if (dirptrs[key].valid) {
115 if (lastused) dirptrs[key].lastused = lastused;
116 if (!dirptrs[key].ptr) {
117 if (dptrs_open >= MAXDIR)
119 DEBUG(4,("Reopening dptr key %d\n",key));
120 if ((dirptrs[key].ptr = OpenDir(dirptrs[key].path)))
123 return(dirptrs[key].ptr);
128 /****************************************************************************
129 get the dir path for a dir index
130 ****************************************************************************/
131 char *dptr_path(int key)
133 if (dirptrs[key].valid)
134 return(dirptrs[key].path);
138 /****************************************************************************
139 get the dir wcard for a dir index (lanman2 specific)
140 ****************************************************************************/
141 char *dptr_wcard(int key)
143 if (dirptrs[key].valid)
144 return(dirptrs[key].wcard);
148 /****************************************************************************
149 set the dir wcard for a dir index (lanman2 specific)
150 Returns 0 on ok, 1 on fail.
151 ****************************************************************************/
152 BOOL dptr_set_wcard(int key, char *wcard)
154 if (dirptrs[key].valid) {
155 dirptrs[key].wcard = wcard;
161 /****************************************************************************
162 set the dir attrib for a dir index (lanman2 specific)
163 Returns 0 on ok, 1 on fail.
164 ****************************************************************************/
165 BOOL dptr_set_attr(int key, uint16 attr)
167 if (dirptrs[key].valid) {
168 dirptrs[key].attr = attr;
174 /****************************************************************************
175 get the dir attrib for a dir index (lanman2 specific)
176 ****************************************************************************/
177 uint16 dptr_attr(int key)
179 if (dirptrs[key].valid)
180 return(dirptrs[key].attr);
184 /****************************************************************************
186 ****************************************************************************/
187 void dptr_close(int key)
189 /* OS/2 seems to use -1 to indicate "close all directories" */
192 for (i=0;i<NUMDIRPTRS;i++)
197 if (key < 0 || key >= NUMDIRPTRS) {
198 DEBUG(3,("Invalid key %d given to dptr_close\n",key));
202 if (dirptrs[key].valid) {
203 DEBUG(4,("closing dptr key %d\n",key));
204 if (dirptrs[key].ptr) {
205 CloseDir(dirptrs[key].ptr);
208 /* Lanman 2 specific code */
209 if (dirptrs[key].wcard)
210 free(dirptrs[key].wcard);
211 dirptrs[key].valid = False;
212 string_set(&dirptrs[key].path,"");
216 /****************************************************************************
217 close all dptrs for a cnum
218 ****************************************************************************/
219 void dptr_closecnum(int cnum)
222 for (i=0;i<NUMDIRPTRS;i++)
223 if (dirptrs[i].valid && dirptrs[i].cnum == cnum)
227 /****************************************************************************
228 idle all dptrs for a cnum
229 ****************************************************************************/
230 void dptr_idlecnum(int cnum)
233 for (i=0;i<NUMDIRPTRS;i++)
234 if (dirptrs[i].valid && dirptrs[i].cnum == cnum && dirptrs[i].ptr)
238 /****************************************************************************
239 close a dptr that matches a given path, only if it matches the pid also
240 ****************************************************************************/
241 void dptr_closepath(char *path,int pid)
244 for (i=0;i<NUMDIRPTRS;i++)
245 if (dirptrs[i].valid && pid == dirptrs[i].pid &&
246 strequal(dirptrs[i].path,path))
250 /****************************************************************************
251 start a directory listing
252 ****************************************************************************/
253 static BOOL start_dir(int cnum,char *directory)
255 DEBUG(5,("start_dir cnum=%d dir=%s\n",cnum,directory));
257 if (!check_name(directory,cnum))
263 Connections[cnum].dirptr = OpenDir(directory);
264 if (Connections[cnum].dirptr) {
266 string_set(&Connections[cnum].dirpath,directory);
274 /****************************************************************************
276 ****************************************************************************/
277 int dptr_create(int cnum,char *path, BOOL expect_close,int pid)
283 if (!start_dir(cnum,path))
286 if (dptrs_open >= MAXDIR)
289 for (i=0;i<NUMDIRPTRS;i++)
290 if (!dirptrs[i].valid)
292 if (i == NUMDIRPTRS) i = -1;
295 /* as a 2nd option, grab the oldest not marked for expect_close */
299 for (i=0;i<NUMDIRPTRS;i++)
300 if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) {
301 old = dirptrs[i].lastused;
307 /* a 3rd option - grab the oldest one */
311 for (i=0;i<NUMDIRPTRS;i++)
312 if (dirptrs[i].lastused < old) {
313 old = dirptrs[i].lastused;
320 DEBUG(0,("Error - all dirptrs in use??\n"));
324 if (dirptrs[i].valid)
327 dirptrs[i].ptr = Connections[cnum].dirptr;
328 string_set(&dirptrs[i].path,path);
329 dirptrs[i].lastused = dircounter++;
330 dirptrs[i].finished = False;
331 dirptrs[i].cnum = cnum;
332 dirptrs[i].pid = pid;
333 dirptrs[i].expect_close = expect_close;
334 dirptrs[i].wcard = NULL; /* Only used in lanman2 searches */
335 dirptrs[i].attr = 0; /* Only used in lanman2 searches */
336 dirptrs[i].valid = True;
338 DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
339 i,path,expect_close));
344 #define DPTR_MASK ((uint32)(((uint32)1)<<31))
346 /****************************************************************************
347 fill the 5 byte server reserved dptr field
348 ****************************************************************************/
349 BOOL dptr_fill(char *buf1,unsigned int key)
351 unsigned char *buf = (unsigned char *)buf1;
352 void *p = dptr_get(key,0);
355 DEBUG(1,("filling null dirptr %d\n",key));
359 DEBUG(6,("fill on key %d dirptr 0x%x now at %d\n",key,p,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 /****************************************************************************
395 fetch the dir ptr and seek it given the lanman2 parameter block
396 ****************************************************************************/
397 void *dptr_fetch_lanman2(char *params,int dptr_num)
399 void *p = dptr_get(dptr_num,dircounter++);
400 uint32 resume_key = SVAL(params,6);
401 BOOL uses_resume_key = BITSETW(params+10,2);
402 BOOL continue_bit = BITSETW(params+10,3);
405 DEBUG(3,("fetched null dirptr %d\n",dptr_num));
408 if(uses_resume_key && !continue_bit)
409 SeekDir(p,resume_key);
410 DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
414 /****************************************************************************
415 check a filetype for being valid
416 ****************************************************************************/
417 BOOL dir_check_ftype(int cnum,int mode,struct stat *st,int dirtype)
419 if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
424 /****************************************************************************
425 get a directory entry
426 ****************************************************************************/
427 BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend)
438 *path = *pathreal = *filename = 0;
440 isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
441 strequal(Connections[cnum].dirpath,".") ||
442 strequal(Connections[cnum].dirpath,"/"));
444 if (!Connections[cnum].dirptr)
449 dname = ReadDirName(Connections[cnum].dirptr);
451 DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n",
452 Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
459 strcpy(filename,dname);
461 if ((strcmp(filename,mask) == 0) ||
462 (name_map_mangle(filename,True,SNUM(cnum)) &&
463 mask_match(filename,mask,False,False)))
465 if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
468 strcpy(fname,filename);
470 strcpy(path,Connections[cnum].dirpath);
472 strcpy(pathreal,path);
474 strcat(pathreal,dname);
475 if (sys_stat(pathreal,&sbuf) != 0)
477 DEBUG(5,("Couldn't stat 1 [%s]\n",path));
482 !strequal(fname,".") && !strequal(fname,".."))
485 *mode = dos_mode(cnum,pathreal,&sbuf);
487 if (!dir_check_ftype(cnum,*mode,&sbuf,dirtype)) {
488 DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
492 *size = sbuf.st_size;
493 *date = sbuf.st_mtime;
495 DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname));
516 /*******************************************************************
518 ********************************************************************/
519 void *OpenDir(char *name)
523 void *p = sys_opendir(name);
526 if (!p) return(NULL);
527 dirp = (Dir *)malloc(sizeof(Dir));
532 dirp->pos = dirp->numentries = dirp->mallocsize = 0;
533 dirp->data = dirp->current = NULL;
535 while ((n = readdirname(p))) {
537 if (used + l > dirp->mallocsize) {
538 int s = MAX(used+l,used+2000);
540 r = (char *)Realloc(dirp->data,s);
542 DEBUG(0,("Out of memory in OpenDir\n"));
546 dirp->mallocsize = s;
547 dirp->current = dirp->data;
549 strcpy(dirp->data+used,n);
555 return((void *)dirp);
559 /*******************************************************************
561 ********************************************************************/
562 void CloseDir(void *p)
564 Dir *dirp = (Dir *)p;
566 if (dirp->data) free(dirp->data);
570 /*******************************************************************
571 read from a directory
572 ********************************************************************/
573 char *ReadDirName(void *p)
576 Dir *dirp = (Dir *)p;
578 if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
581 dirp->current = skip_string(dirp->current,1);
588 /*******************************************************************
590 ********************************************************************/
591 BOOL SeekDir(void *p,int pos)
593 Dir *dirp = (Dir *)p;
595 if (!dirp) return(False);
597 if (pos < dirp->pos) {
598 dirp->current = dirp->data;
602 while (dirp->pos < pos && ReadDirName(p)) ;
604 return(dirp->pos == pos);
607 /*******************************************************************
609 ********************************************************************/
612 Dir *dirp = (Dir *)p;
614 if (!dirp) return(-1);
620 static int dir_cache_size = 0;
621 static struct dir_cache {
622 struct dir_cache *next;
623 struct dir_cache *prev;
630 /*******************************************************************
631 add an entry to the directory cache
632 ********************************************************************/
633 void DirCacheAdd(char *path,char *name,char *dname,int snum)
635 struct dir_cache *entry = (struct dir_cache *)malloc(sizeof(*entry));
637 entry->path = strdup(path);
638 entry->name = strdup(name);
639 entry->dname = strdup(dname);
641 if (!entry->path || !entry->name || !entry->dname) return;
643 entry->next = dir_cache;
645 if (entry->next) entry->next->prev = entry;
648 DEBUG(4,("Added dir cache entry %s %s -> %s\n",path,name,dname));
650 if (dir_cache_size == DIRCACHESIZE) {
651 for (entry=dir_cache; entry->next; entry=entry->next) ;
655 if (entry->prev) entry->prev->next = entry->next;
663 /*******************************************************************
664 check for an entry in the directory cache
665 ********************************************************************/
666 char *DirCacheCheck(char *path,char *name,int snum)
668 struct dir_cache *entry;
670 for (entry=dir_cache; entry; entry=entry->next) {
671 if (entry->snum == snum &&
672 strcmp(path,entry->path) == 0 &&
673 strcmp(name,entry->name) == 0) {
674 DEBUG(4,("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
675 return(entry->dname);
682 /*******************************************************************
683 flush entries in the dir_cache
684 ********************************************************************/
685 void DirCacheFlush(int snum)
687 struct dir_cache *entry,*next;
689 for (entry=dir_cache; entry; entry=next) {
690 if (entry->snum == snum) {
695 if (entry->prev) entry->prev->next = entry->next;
696 if (entry->next) entry->next->prev = entry->prev;
697 if (dir_cache == entry) dir_cache = entry->next;
707 /* This is getcwd.c from bash. It is needed in Interactive UNIX. To
708 * add support for another OS you need to determine which of the
709 * conditional compilation macros you need to define. All the options
710 * are defined for Interactive UNIX.
713 #define HAVE_UNISTD_H
718 #if defined (HAVE_UNISTD_H)
722 #if defined (__STDC__)
725 #else /* !__STDC__ */
728 #endif /* !__STDC__ */
730 #if !defined (PATH_MAX)
731 # if defined (MAXPATHLEN)
732 # define PATH_MAX MAXPATHLEN
733 # else /* !MAXPATHLEN */
734 # define PATH_MAX 1024
735 # endif /* !MAXPATHLEN */
736 #endif /* !PATH_MAX */
738 #if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H)
739 # if !defined (HAVE_DIRENT)
741 # endif /* !HAVE_DIRENT */
742 #endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */
744 #if defined (HAVE_DIRENT)
745 # define D_NAMLEN(d) (strlen ((d)->d_name))
747 # define D_NAMLEN(d) ((d)->d_namlen)
748 #endif /* ! (_POSIX_VERSION || USGr3) */
750 #if defined (USG) || defined (USGr3)
751 # define d_fileno d_ino
754 #if !defined (alloca)
755 extern char *alloca ();
758 /* Get the pathname of the current working directory,
759 and put it in SIZE bytes of BUF. Returns NULL if the
760 directory couldn't be determined or SIZE was too small.
761 If successful, returns BUF. In GNU, if BUF is NULL,
762 an array is allocated with `malloc'; the array is SIZE
763 bytes long, unless SIZE <= 0, in which case it is as
765 #if defined (__STDC__)
767 getcwd (char *buf, size_t size)
768 #else /* !__STDC__ */
773 #endif /* !__STDC__ */
775 static CONST char dots[]
776 = "../../../../../../../../../../../../../../../../../../../../../../../\
777 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
778 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
779 CONST char *dotp, *dotlist;
781 dev_t rootdev, thisdev;
782 ino_t rootino, thisino;
783 char path[PATH_MAX + 1];
784 register char *pathp;
789 if (buf != NULL && size == 0)
792 return ((char *)NULL);
795 pathsize = sizeof (path);
796 pathp = &path[pathsize];
800 if (stat (".", &st) < 0)
801 return ((char *)NULL);
805 if (stat ("/", &st) < 0)
806 return ((char *)NULL);
810 dotsize = sizeof (dots) - 1;
811 dotp = &dots[sizeof (dots)];
813 while (!(thisdev == rootdev && thisino == rootino))
815 register DIR *dirstream;
816 register struct dirent *d;
822 /* Look at the parent directory. */
825 /* My, what a deep directory tree you have, Grandma. */
829 new = malloc (dotsize * 2 + 1);
832 memcpy (new, dots, dotsize);
836 new = realloc ((PTR) dotlist, dotsize * 2 + 1);
840 memcpy (&new[dotsize], new, dotsize);
841 dotp = &new[dotsize];
849 /* Figure out if this directory is a mount point. */
850 if (stat (dotp, &st) < 0)
854 mount_point = dotdev != thisdev;
856 /* Search for the last directory. */
857 dirstream = opendir(dotp);
858 if (dirstream == NULL)
860 while ((d = (struct dirent *)readdir(dirstream)) != NULL)
862 if (d->d_name[0] == '.' &&
863 (d->d_name[1] == '\0' ||
864 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
866 if (mount_point || d->d_fileno == thisino)
870 namlen = D_NAMLEN(d);
872 alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
873 memcpy (name, dotp, dotlist + dotsize - dotp);
874 name[dotlist + dotsize - dotp] = '/';
875 memcpy (&name[dotlist + dotsize - dotp + 1],
876 d->d_name, namlen + 1);
877 if (lstat (name, &st) < 0)
884 if (st.st_dev == thisdev && st.st_ino == thisino)
899 while ((space = pathp - pathbuf) <= namlen)
905 new = malloc (pathsize * 2);
911 new = realloc ((PTR) pathbuf, (pathsize * 2));
916 (void) memcpy (new + pathsize + space, pathp, pathsize - space);
917 pathp = new + pathsize + space;
923 (void) memcpy (pathp, d->d_name, namlen);
932 if (pathp == &path[sizeof(path) - 1])
936 free ((PTR) dotlist);
939 size_t len = pathbuf + pathsize - pathp;
942 if (len < (size_t) size)
944 buf = (char *) malloc (len);
948 else if ((size_t) size < len)
953 (void) memcpy((PTR) buf, (PTR) pathp, len);
962 if ((dotlist != dots) && dotlist)
965 free ((PTR) dotlist);
970 if ((pathbuf != path) && pathbuf)
973 free ((PTR) pathbuf);
976 return ((char *)NULL);