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.
24 extern int DEBUGLEVEL
;
25 extern connection_struct Connections
[];
28 This module implements directory related functions for Samba.
33 uint32 dircounter
= 0;
36 #define NUMDIRPTRS 256
39 static struct dptr_struct
48 char *wcard
; /* Field only used for lanman2 trans2_findfirst/next searches */
49 uint16 attr
; /* Field only used for lanman2 trans2_findfirst/next searches */
55 static int dptrs_open
= 0;
57 /****************************************************************************
58 initialise the dir array
59 ****************************************************************************/
62 static BOOL dptrs_init
=False
;
65 if (dptrs_init
) return;
66 for (i
=0;i
<NUMDIRPTRS
;i
++)
68 dirptrs
[i
].valid
= False
;
69 dirptrs
[i
].wcard
= NULL
;
70 dirptrs
[i
].ptr
= NULL
;
71 string_init(&dirptrs
[i
].path
,"");
76 /****************************************************************************
77 idle a dptr - the directory is closed but the control info is kept
78 ****************************************************************************/
79 static void dptr_idle(int key
)
81 if (dirptrs
[key
].valid
&& dirptrs
[key
].ptr
) {
82 DEBUG(4,("Idling dptr key %d\n",key
));
84 CloseDir(dirptrs
[key
].ptr
);
85 dirptrs
[key
].ptr
= NULL
;
89 /****************************************************************************
91 ****************************************************************************/
92 static void dptr_idleoldest(void)
95 uint32 old
=dircounter
+1;
97 for (i
=0;i
<NUMDIRPTRS
;i
++)
98 if (dirptrs
[i
].valid
&& dirptrs
[i
].ptr
&& dirptrs
[i
].lastused
< old
) {
99 old
= dirptrs
[i
].lastused
;
105 DEBUG(0,("No dptrs available to idle??\n"));
108 /****************************************************************************
109 get the dir ptr for a dir index
110 ****************************************************************************/
111 static void *dptr_get(int key
,uint32 lastused
)
113 if (dirptrs
[key
].valid
) {
114 if (lastused
) dirptrs
[key
].lastused
= lastused
;
115 if (!dirptrs
[key
].ptr
) {
116 if (dptrs_open
>= MAXDIR
)
118 DEBUG(4,("Reopening dptr key %d\n",key
));
119 if ((dirptrs
[key
].ptr
= OpenDir(dirptrs
[key
].path
)))
122 return(dirptrs
[key
].ptr
);
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(int cnum
)
221 for (i
=0;i
<NUMDIRPTRS
;i
++)
222 if (dirptrs
[i
].valid
&& dirptrs
[i
].cnum
== cnum
)
226 /****************************************************************************
227 idle all dptrs for a cnum
228 ****************************************************************************/
229 void dptr_idlecnum(int cnum
)
232 for (i
=0;i
<NUMDIRPTRS
;i
++)
233 if (dirptrs
[i
].valid
&& dirptrs
[i
].cnum
== cnum
&& 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(int cnum
,char *directory
)
254 DEBUG(5,("start_dir cnum=%d dir=%s\n",cnum
,directory
));
256 if (!check_name(directory
,cnum
))
262 Connections
[cnum
].dirptr
= OpenDir(directory
);
263 if (Connections
[cnum
].dirptr
) {
265 string_set(&Connections
[cnum
].dirpath
,directory
);
273 /****************************************************************************
275 ****************************************************************************/
276 int dptr_create(int cnum
,char *path
, BOOL expect_close
,int pid
)
282 if (!start_dir(cnum
,path
))
285 if (dptrs_open
>= MAXDIR
)
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
= Connections
[cnum
].dirptr
;
327 string_set(&dirptrs
[i
].path
,path
);
328 dirptrs
[i
].lastused
= dircounter
++;
329 dirptrs
[i
].finished
= False
;
330 dirptrs
[i
].cnum
= cnum
;
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 %d dirptr 0x%x now at %d\n",key
,p
,offset
));
360 SIVAL(buf
,1,offset
| DPTR_MASK
);
365 /****************************************************************************
366 return True is the offset is at zero
367 ****************************************************************************/
368 BOOL
dptr_zero(char *buf
)
370 return((IVAL(buf
,1)&~DPTR_MASK
) == 0);
373 /****************************************************************************
374 fetch the dir ptr and seek it given the 5 byte server field
375 ****************************************************************************/
376 void *dptr_fetch(char *buf
,int *num
)
378 unsigned int key
= *(unsigned char *)buf
;
379 void *p
= dptr_get(key
,dircounter
++);
382 DEBUG(3,("fetched null dirptr %d\n",key
));
386 offset
= IVAL(buf
,1)&~DPTR_MASK
;
388 DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
389 key
,dptr_path(key
),offset
));
393 /****************************************************************************
394 fetch the dir ptr and seek it given the lanman2 parameter block
395 ****************************************************************************/
396 void *dptr_fetch_lanman2(char *params
,int dptr_num
)
398 void *p
= dptr_get(dptr_num
,dircounter
++);
399 uint32 resume_key
= SVAL(params
,6);
400 BOOL uses_resume_key
= BITSETW(params
+10,2);
401 BOOL continue_bit
= BITSETW(params
+10,3);
404 DEBUG(3,("fetched null dirptr %d\n",dptr_num
));
407 if(uses_resume_key
&& !continue_bit
)
408 SeekDir(p
,resume_key
);
409 DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num
,dptr_path(dptr_num
)));
413 /****************************************************************************
414 check a filetype for being valid
415 ****************************************************************************/
416 BOOL
dir_check_ftype(int cnum
,int mode
,struct stat
*st
,int dirtype
)
418 if (((mode
& ~dirtype
) & (aHIDDEN
| aSYSTEM
| aDIR
)) != 0)
423 /****************************************************************************
424 get a directory entry
425 ****************************************************************************/
426 BOOL
get_dir_entry(int cnum
,char *mask
,int dirtype
,char *fname
,int *size
,int *mode
,time_t *date
,BOOL check_descend
)
437 *path
= *pathreal
= *filename
= 0;
439 isrootdir
= (strequal(Connections
[cnum
].dirpath
,"./") ||
440 strequal(Connections
[cnum
].dirpath
,".") ||
441 strequal(Connections
[cnum
].dirpath
,"/"));
443 if (!Connections
[cnum
].dirptr
)
448 dname
= ReadDirName(Connections
[cnum
].dirptr
);
450 DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n",
451 Connections
[cnum
].dirptr
,TellDir(Connections
[cnum
].dirptr
)));
458 strcpy(filename
,dname
);
460 if ((strcmp(filename
,mask
) == 0) ||
461 (name_map_mangle(filename
,True
,SNUM(cnum
)) &&
462 mask_match(filename
,mask
,False
,False
)))
464 if (isrootdir
&& (strequal(filename
,"..") || strequal(filename
,".")))
467 strcpy(fname
,filename
);
469 strcpy(path
,Connections
[cnum
].dirpath
);
471 strcpy(pathreal
,path
);
473 strcat(pathreal
,dname
);
474 if (sys_stat(pathreal
,&sbuf
) != 0)
476 DEBUG(5,("Couldn't stat 1 [%s]\n",path
));
481 !strequal(fname
,".") && !strequal(fname
,".."))
484 *mode
= dos_mode(cnum
,pathreal
,&sbuf
);
486 if (!dir_check_ftype(cnum
,*mode
,&sbuf
,dirtype
)) {
487 DEBUG(5,("[%s] attribs didn't match %x\n",filename
,dirtype
));
491 *size
= sbuf
.st_size
;
492 *date
= sbuf
.st_mtime
;
494 DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal
,fname
));
515 /*******************************************************************
517 ********************************************************************/
518 void *OpenDir(char *name
)
522 void *p
= sys_opendir(name
);
525 if (!p
) return(NULL
);
526 dirp
= (Dir
*)malloc(sizeof(Dir
));
531 dirp
->pos
= dirp
->numentries
= dirp
->mallocsize
= 0;
532 dirp
->data
= dirp
->current
= NULL
;
534 while ((n
= readdirname(p
))) {
536 if (used
+ l
> dirp
->mallocsize
) {
537 int s
= MAX(used
+l
,used
+2000);
539 r
= (char *)Realloc(dirp
->data
,s
);
541 DEBUG(0,("Out of memory in OpenDir\n"));
545 dirp
->mallocsize
= s
;
546 dirp
->current
= dirp
->data
;
548 strcpy(dirp
->data
+used
,n
);
554 return((void *)dirp
);
558 /*******************************************************************
560 ********************************************************************/
561 void CloseDir(void *p
)
563 Dir
*dirp
= (Dir
*)p
;
565 if (dirp
->data
) free(dirp
->data
);
569 /*******************************************************************
570 read from a directory
571 ********************************************************************/
572 char *ReadDirName(void *p
)
575 Dir
*dirp
= (Dir
*)p
;
577 if (!dirp
|| !dirp
->current
|| dirp
->pos
>= dirp
->numentries
) return(NULL
);
580 dirp
->current
= skip_string(dirp
->current
,1);
587 /*******************************************************************
589 ********************************************************************/
590 BOOL
SeekDir(void *p
,int pos
)
592 Dir
*dirp
= (Dir
*)p
;
594 if (!dirp
) return(False
);
596 if (pos
< dirp
->pos
) {
597 dirp
->current
= dirp
->data
;
601 while (dirp
->pos
< pos
&& ReadDirName(p
)) ;
603 return(dirp
->pos
== pos
);
606 /*******************************************************************
608 ********************************************************************/
611 Dir
*dirp
= (Dir
*)p
;
613 if (!dirp
) return(-1);
619 static int dir_cache_size
= 0;
620 static struct dir_cache
{
621 struct dir_cache
*next
;
622 struct dir_cache
*prev
;
629 /*******************************************************************
630 add an entry to the directory cache
631 ********************************************************************/
632 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
, count
=1;
652 entry
->next
&& count
< dir_cache_size
;
653 entry
=entry
->next
, count
++) ;
654 if (entry
->next
|| count
!= dir_cache_size
) {
655 DEBUG(0,("DirCache bug - please report\n"));
660 if (entry
->prev
) entry
->prev
->next
= entry
->next
;
668 /*******************************************************************
669 check for an entry in the directory cache
670 ********************************************************************/
671 char *DirCacheCheck(char *path
,char *name
,int snum
)
673 struct dir_cache
*entry
;
675 for (entry
=dir_cache
; entry
; entry
=entry
->next
) {
676 if (entry
->snum
== snum
&&
677 strcmp(path
,entry
->path
) == 0 &&
678 strcmp(name
,entry
->name
) == 0) {
679 DEBUG(4,("Got dir cache hit on %s %s -> %s\n",path
,name
,entry
->dname
));
680 return(entry
->dname
);
687 /*******************************************************************
688 flush entries in the dir_cache
689 ********************************************************************/
690 void DirCacheFlush(int snum
)
692 struct dir_cache
*entry
,*next
;
694 for (entry
=dir_cache
; entry
; entry
=next
) {
695 if (entry
->snum
== snum
) {
700 if (entry
->prev
) entry
->prev
->next
= entry
->next
;
701 if (entry
->next
) entry
->next
->prev
= entry
->prev
;
702 if (dir_cache
== entry
) dir_cache
= entry
->next
;
713 /* This is getcwd.c from bash. It is needed in Interactive UNIX. To
714 * add support for another OS you need to determine which of the
715 * conditional compilation macros you need to define. All the options
716 * are defined for Interactive UNIX.
719 #define HAVE_UNISTD_H
724 #if defined (HAVE_UNISTD_H)
728 #if defined (__STDC__)
731 #else /* !__STDC__ */
734 #endif /* !__STDC__ */
736 #if !defined (PATH_MAX)
737 # if defined (MAXPATHLEN)
738 # define PATH_MAX MAXPATHLEN
739 # else /* !MAXPATHLEN */
740 # define PATH_MAX 1024
741 # endif /* !MAXPATHLEN */
742 #endif /* !PATH_MAX */
744 #if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H)
745 # if !defined (HAVE_DIRENT)
747 # endif /* !HAVE_DIRENT */
748 #endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */
750 #if defined (HAVE_DIRENT)
751 # define D_NAMLEN(d) (strlen ((d)->d_name))
753 # define D_NAMLEN(d) ((d)->d_namlen)
754 #endif /* ! (_POSIX_VERSION || USGr3) */
756 #if defined (USG) || defined (USGr3)
757 # define d_fileno d_ino
760 #if !defined (alloca)
761 extern char *alloca ();
764 /* Get the pathname of the current working directory,
765 and put it in SIZE bytes of BUF. Returns NULL if the
766 directory couldn't be determined or SIZE was too small.
767 If successful, returns BUF. In GNU, if BUF is NULL,
768 an array is allocated with `malloc'; the array is SIZE
769 bytes long, unless SIZE <= 0, in which case it is as
771 #if defined (__STDC__)
773 getcwd (char *buf
, size_t size
)
774 #else /* !__STDC__ */
779 #endif /* !__STDC__ */
781 static CONST
char dots
[]
782 = "../../../../../../../../../../../../../../../../../../../../../../../\
783 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
784 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
785 CONST
char *dotp
, *dotlist
;
787 dev_t rootdev
, thisdev
;
788 ino_t rootino
, thisino
;
789 char path
[PATH_MAX
+ 1];
790 register char *pathp
;
795 if (buf
!= NULL
&& size
== 0)
798 return ((char *)NULL
);
801 pathsize
= sizeof (path
);
802 pathp
= &path
[pathsize
];
806 if (stat (".", &st
) < 0)
807 return ((char *)NULL
);
811 if (stat ("/", &st
) < 0)
812 return ((char *)NULL
);
816 dotsize
= sizeof (dots
) - 1;
817 dotp
= &dots
[sizeof (dots
)];
819 while (!(thisdev
== rootdev
&& thisino
== rootino
))
821 register DIR *dirstream
;
822 register struct dirent
*d
;
828 /* Look at the parent directory. */
831 /* My, what a deep directory tree you have, Grandma. */
835 new = malloc (dotsize
* 2 + 1);
838 memcpy (new, dots
, dotsize
);
842 new = realloc ((PTR
) dotlist
, dotsize
* 2 + 1);
846 memcpy (&new[dotsize
], new, dotsize
);
847 dotp
= &new[dotsize
];
855 /* Figure out if this directory is a mount point. */
856 if (stat (dotp
, &st
) < 0)
860 mount_point
= dotdev
!= thisdev
;
862 /* Search for the last directory. */
863 dirstream
= opendir(dotp
);
864 if (dirstream
== NULL
)
866 while ((d
= (struct dirent
*)readdir(dirstream
)) != NULL
)
868 if (d
->d_name
[0] == '.' &&
869 (d
->d_name
[1] == '\0' ||
870 (d
->d_name
[1] == '.' && d
->d_name
[2] == '\0')))
872 if (mount_point
|| d
->d_fileno
== thisino
)
876 namlen
= D_NAMLEN(d
);
878 alloca (dotlist
+ dotsize
- dotp
+ 1 + namlen
+ 1);
879 memcpy (name
, dotp
, dotlist
+ dotsize
- dotp
);
880 name
[dotlist
+ dotsize
- dotp
] = '/';
881 memcpy (&name
[dotlist
+ dotsize
- dotp
+ 1],
882 d
->d_name
, namlen
+ 1);
883 if (lstat (name
, &st
) < 0)
890 if (st
.st_dev
== thisdev
&& st
.st_ino
== thisino
)
905 while ((space
= pathp
- pathbuf
) <= namlen
)
911 new = malloc (pathsize
* 2);
917 new = realloc ((PTR
) pathbuf
, (pathsize
* 2));
922 (void) memcpy (new + pathsize
+ space
, pathp
, pathsize
- space
);
923 pathp
= new + pathsize
+ space
;
929 (void) memcpy (pathp
, d
->d_name
, namlen
);
938 if (pathp
== &path
[sizeof(path
) - 1])
942 free ((PTR
) dotlist
);
945 size_t len
= pathbuf
+ pathsize
- pathp
;
948 if (len
< (size_t) size
)
950 buf
= (char *) malloc (len
);
954 else if ((size_t) size
< len
)
959 (void) memcpy((PTR
) buf
, (PTR
) pathp
, len
);
968 if ((dotlist
!= dots
) && dotlist
)
971 free ((PTR
) dotlist
);
976 if ((pathbuf
!= path
) && pathbuf
)
979 free ((PTR
) pathbuf
);
982 return ((char *)NULL
);