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 if (dirptrs[key].valid) {
190 DEBUG(4,("closing dptr key %d\n",key));
191 if (dirptrs[key].ptr) {
192 CloseDir(dirptrs[key].ptr);
195 /* Lanman 2 specific code */
196 if (dirptrs[key].wcard)
197 free(dirptrs[key].wcard);
198 dirptrs[key].valid = False;
199 string_set(&dirptrs[key].path,"");
203 /****************************************************************************
204 close all dptrs for a cnum
205 ****************************************************************************/
206 void dptr_closecnum(int cnum)
209 for (i=0;i<NUMDIRPTRS;i++)
210 if (dirptrs[i].valid && dirptrs[i].cnum == cnum)
214 /****************************************************************************
215 idle all dptrs for a cnum
216 ****************************************************************************/
217 void dptr_idlecnum(int cnum)
220 for (i=0;i<NUMDIRPTRS;i++)
221 if (dirptrs[i].valid && dirptrs[i].cnum == cnum && dirptrs[i].ptr)
225 /****************************************************************************
226 close a dptr that matches a given path, only if it matches the pid also
227 ****************************************************************************/
228 void dptr_closepath(char *path,int pid)
231 for (i=0;i<NUMDIRPTRS;i++)
232 if (dirptrs[i].valid && pid == dirptrs[i].pid &&
233 strequal(dirptrs[i].path,path))
237 /****************************************************************************
238 start a directory listing
239 ****************************************************************************/
240 static BOOL start_dir(int cnum,char *directory)
242 DEBUG(5,("start_dir cnum=%d dir=%s\n",cnum,directory));
244 if (!check_name(directory,cnum))
250 Connections[cnum].dirptr = OpenDir(directory);
251 if (Connections[cnum].dirptr) {
253 string_set(&Connections[cnum].dirpath,directory);
261 /****************************************************************************
263 ****************************************************************************/
264 int dptr_create(int cnum,char *path, BOOL expect_close,int pid)
270 if (!start_dir(cnum,path))
273 if (dptrs_open >= MAXDIR)
276 for (i=0;i<NUMDIRPTRS;i++)
277 if (!dirptrs[i].valid)
279 if (i == NUMDIRPTRS) i = -1;
282 /* as a 2nd option, grab the oldest not marked for expect_close */
286 for (i=0;i<NUMDIRPTRS;i++)
287 if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) {
288 old = dirptrs[i].lastused;
294 /* a 3rd option - grab the oldest one */
298 for (i=0;i<NUMDIRPTRS;i++)
299 if (dirptrs[i].lastused < old) {
300 old = dirptrs[i].lastused;
307 DEBUG(0,("Error - all dirptrs in use??\n"));
311 if (dirptrs[i].valid)
314 dirptrs[i].ptr = Connections[cnum].dirptr;
315 string_set(&dirptrs[i].path,path);
316 dirptrs[i].lastused = dircounter++;
317 dirptrs[i].finished = False;
318 dirptrs[i].cnum = cnum;
319 dirptrs[i].pid = pid;
320 dirptrs[i].expect_close = expect_close;
321 dirptrs[i].wcard = NULL; /* Only used in lanman2 searches */
322 dirptrs[i].attr = 0; /* Only used in lanman2 searches */
323 dirptrs[i].valid = True;
325 DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
326 i,path,expect_close));
331 #define DPTR_MASK ((uint32)(((uint32)1)<<31))
333 /****************************************************************************
334 fill the 5 byte server reserved dptr field
335 ****************************************************************************/
336 BOOL dptr_fill(char *buf1,unsigned int key)
338 unsigned char *buf = (unsigned char *)buf1;
339 void *p = dptr_get(key,0);
342 DEBUG(1,("filling null dirptr %d\n",key));
346 DEBUG(6,("fill on key %d dirptr 0x%x now at %d\n",key,p,offset));
348 SIVAL(buf,1,offset | DPTR_MASK);
353 /****************************************************************************
354 return True is the offset is at zero
355 ****************************************************************************/
356 BOOL dptr_zero(char *buf)
358 return((IVAL(buf,1)&~DPTR_MASK) == 0);
361 /****************************************************************************
362 fetch the dir ptr and seek it given the 5 byte server field
363 ****************************************************************************/
364 void *dptr_fetch(char *buf,int *num)
366 unsigned int key = *(unsigned char *)buf;
367 void *p = dptr_get(key,dircounter++);
370 DEBUG(3,("fetched null dirptr %d\n",key));
374 offset = IVAL(buf,1)&~DPTR_MASK;
376 DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
377 key,dptr_path(key),offset));
381 /****************************************************************************
382 fetch the dir ptr and seek it given the lanman2 parameter block
383 ****************************************************************************/
384 void *dptr_fetch_lanman2(char *params,int dptr_num)
386 void *p = dptr_get(dptr_num,dircounter++);
387 uint32 resume_key = SVAL(params,6);
388 BOOL uses_resume_key = BITSETW(params+10,2);
389 BOOL continue_bit = BITSETW(params+10,3);
392 DEBUG(3,("fetched null dirptr %d\n",dptr_num));
395 if(uses_resume_key && !continue_bit)
396 SeekDir(p,resume_key);
397 DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
401 /****************************************************************************
402 get a directory entry
403 ****************************************************************************/
404 BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend)
415 *path = *pathreal = *filename = 0;
417 isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
418 strequal(Connections[cnum].dirpath,".") ||
419 strequal(Connections[cnum].dirpath,"/"));
421 if (!Connections[cnum].dirptr)
426 dname = ReadDirName(Connections[cnum].dirptr);
428 DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n",
429 Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
436 strcpy(filename,dname);
438 if ((strcmp(filename,mask) == 0) ||
439 (name_map_mangle(filename,True,SNUM(cnum)) &&
440 mask_match(filename,mask,False,False)))
442 if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
445 strcpy(fname,filename);
447 strcpy(path,Connections[cnum].dirpath);
449 strcpy(pathreal,path);
451 strcat(pathreal,dname);
452 if (sys_stat(pathreal,&sbuf) != 0)
454 DEBUG(5,("Couldn't stat 1 [%s]\n",path));
459 !strequal(fname,".") && !strequal(fname,".."))
462 *mode = dos_mode(cnum,pathreal,&sbuf);
464 if (((*mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
466 DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
469 *size = sbuf.st_size;
470 *date = sbuf.st_mtime;
472 DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname));
493 /*******************************************************************
495 ********************************************************************/
496 void *OpenDir(char *name)
500 void *p = sys_opendir(name);
503 if (!p) return(NULL);
504 dirp = (Dir *)malloc(sizeof(Dir));
509 dirp->pos = dirp->numentries = dirp->mallocsize = 0;
510 dirp->data = dirp->current = NULL;
512 while ((n = readdirname(p))) {
514 if (used + l > dirp->mallocsize) {
515 int s = MAX(used+l,used+2000);
517 r = (char *)Realloc(dirp->data,s);
519 DEBUG(0,("Out of memory in OpenDir\n"));
523 dirp->mallocsize = s;
524 dirp->current = dirp->data;
526 strcpy(dirp->data+used,n);
532 return((void *)dirp);
536 /*******************************************************************
538 ********************************************************************/
539 void CloseDir(void *p)
541 Dir *dirp = (Dir *)p;
543 if (dirp->data) free(dirp->data);
547 /*******************************************************************
548 read from a directory
549 ********************************************************************/
550 char *ReadDirName(void *p)
553 Dir *dirp = (Dir *)p;
555 if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
558 dirp->current = skip_string(dirp->current,1);
565 /*******************************************************************
567 ********************************************************************/
568 BOOL SeekDir(void *p,int pos)
570 Dir *dirp = (Dir *)p;
572 if (!dirp) return(False);
574 if (pos < dirp->pos) {
575 dirp->current = dirp->data;
579 while (dirp->pos < pos && ReadDirName(p)) ;
581 return(dirp->pos == pos);
584 /*******************************************************************
586 ********************************************************************/
589 Dir *dirp = (Dir *)p;
591 if (!dirp) return(-1);
597 static int dir_cache_size = 0;
598 static struct dir_cache {
599 struct dir_cache *next;
600 struct dir_cache *prev;
607 /*******************************************************************
608 add an entry to the directory cache
609 ********************************************************************/
610 void DirCacheAdd(char *path,char *name,char *dname,int snum)
612 struct dir_cache *entry = (struct dir_cache *)malloc(sizeof(*entry));
614 entry->path = strdup(path);
615 entry->name = strdup(name);
616 entry->dname = strdup(dname);
618 if (!entry->path || !entry->name || !entry->dname) return;
620 entry->next = dir_cache;
622 if (entry->next) entry->next->prev = entry;
625 DEBUG(4,("Added dir cache entry %s %s -> %s\n",path,name,dname));
627 if (dir_cache_size == DIRCACHESIZE) {
628 for (entry=dir_cache; entry->next; entry=entry->next) ;
632 if (entry->prev) entry->prev->next = entry->next;
640 /*******************************************************************
641 check for an entry in the directory cache
642 ********************************************************************/
643 char *DirCacheCheck(char *path,char *name,int snum)
645 struct dir_cache *entry;
647 for (entry=dir_cache; entry; entry=entry->next) {
648 if (entry->snum == snum &&
649 strcmp(path,entry->path) == 0 &&
650 strcmp(name,entry->name) == 0) {
651 DEBUG(4,("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
652 return(entry->dname);
659 /*******************************************************************
660 flush entries in the dir_cache
661 ********************************************************************/
662 void DirCacheFlush(int snum)
664 struct dir_cache *entry,*next;
666 for (entry=dir_cache; entry; entry=next) {
667 if (entry->snum == snum) {
672 if (entry->prev) entry->prev->next = entry->next;
673 if (entry->next) entry->next->prev = entry->prev;
674 if (dir_cache == entry) dir_cache = entry->next;
684 /* This is getcwd.c from bash. It is needed in Interactive UNIX. To
685 * add support for another OS you need to determine which of the
686 * conditional compilation macros you need to define. All the options
687 * are defined for Interactive UNIX.
690 #define HAVE_UNISTD_H
695 #if defined (HAVE_UNISTD_H)
699 #if defined (__STDC__)
702 #else /* !__STDC__ */
705 #endif /* !__STDC__ */
707 #if !defined (PATH_MAX)
708 # if defined (MAXPATHLEN)
709 # define PATH_MAX MAXPATHLEN
710 # else /* !MAXPATHLEN */
711 # define PATH_MAX 1024
712 # endif /* !MAXPATHLEN */
713 #endif /* !PATH_MAX */
715 #if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H)
716 # if !defined (HAVE_DIRENT)
718 # endif /* !HAVE_DIRENT */
719 #endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */
721 #if defined (HAVE_DIRENT)
722 # define D_NAMLEN(d) (strlen ((d)->d_name))
724 # define D_NAMLEN(d) ((d)->d_namlen)
725 #endif /* ! (_POSIX_VERSION || USGr3) */
727 #if defined (USG) || defined (USGr3)
728 # define d_fileno d_ino
731 #if !defined (alloca)
732 extern char *alloca ();
735 /* Get the pathname of the current working directory,
736 and put it in SIZE bytes of BUF. Returns NULL if the
737 directory couldn't be determined or SIZE was too small.
738 If successful, returns BUF. In GNU, if BUF is NULL,
739 an array is allocated with `malloc'; the array is SIZE
740 bytes long, unless SIZE <= 0, in which case it is as
742 #if defined (__STDC__)
744 getcwd (char *buf, size_t size)
745 #else /* !__STDC__ */
750 #endif /* !__STDC__ */
752 static CONST char dots[]
753 = "../../../../../../../../../../../../../../../../../../../../../../../\
754 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
755 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
756 CONST char *dotp, *dotlist;
758 dev_t rootdev, thisdev;
759 ino_t rootino, thisino;
760 char path[PATH_MAX + 1];
761 register char *pathp;
766 if (buf != NULL && size == 0)
769 return ((char *)NULL);
772 pathsize = sizeof (path);
773 pathp = &path[pathsize];
777 if (stat (".", &st) < 0)
778 return ((char *)NULL);
782 if (stat ("/", &st) < 0)
783 return ((char *)NULL);
787 dotsize = sizeof (dots) - 1;
788 dotp = &dots[sizeof (dots)];
790 while (!(thisdev == rootdev && thisino == rootino))
792 register DIR *dirstream;
793 register struct dirent *d;
799 /* Look at the parent directory. */
802 /* My, what a deep directory tree you have, Grandma. */
806 new = malloc (dotsize * 2 + 1);
809 memcpy (new, dots, dotsize);
813 new = realloc ((PTR) dotlist, dotsize * 2 + 1);
817 memcpy (&new[dotsize], new, dotsize);
818 dotp = &new[dotsize];
826 /* Figure out if this directory is a mount point. */
827 if (stat (dotp, &st) < 0)
831 mount_point = dotdev != thisdev;
833 /* Search for the last directory. */
834 dirstream = opendir(dotp);
835 if (dirstream == NULL)
837 while ((d = (struct dirent *)readdir(dirstream)) != NULL)
839 if (d->d_name[0] == '.' &&
840 (d->d_name[1] == '\0' ||
841 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
843 if (mount_point || d->d_fileno == thisino)
847 namlen = D_NAMLEN(d);
849 alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
850 memcpy (name, dotp, dotlist + dotsize - dotp);
851 name[dotlist + dotsize - dotp] = '/';
852 memcpy (&name[dotlist + dotsize - dotp + 1],
853 d->d_name, namlen + 1);
854 if (lstat (name, &st) < 0)
861 if (st.st_dev == thisdev && st.st_ino == thisino)
876 while ((space = pathp - pathbuf) <= namlen)
882 new = malloc (pathsize * 2);
888 new = realloc ((PTR) pathbuf, (pathsize * 2));
893 (void) memcpy (new + pathsize + space, pathp, pathsize - space);
894 pathp = new + pathsize + space;
900 (void) memcpy (pathp, d->d_name, namlen);
909 if (pathp == &path[sizeof(path) - 1])
913 free ((PTR) dotlist);
916 size_t len = pathbuf + pathsize - pathp;
919 if (len < (size_t) size)
921 buf = (char *) malloc (len);
925 else if ((size_t) size < len)
930 (void) memcpy((PTR) buf, (PTR) pathp, len);
939 if ((dotlist != dots) && dotlist)
942 free ((PTR) dotlist);
947 if ((pathbuf != path) && pathbuf)
950 free ((PTR) pathbuf);
953 return ((char *)NULL);