Made changes to the dir cache functions:
[samba.git] / source3 / smbd / dir.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Directory handling routines
5    Copyright (C) Andrew Tridgell 1992-1997
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 extern connection_struct Connections[];
26
27 /*
28    This module implements directory related functions for Samba.
29 */
30
31
32
33 uint32 dircounter = 0;
34
35
36 #define NUMDIRPTRS 256
37
38
39 static struct dptr_struct
40 {
41   int pid;
42   int cnum;
43   uint32 lastused;
44   void *ptr;
45   BOOL valid;
46   BOOL finished;
47   BOOL expect_close;
48   char *wcard; /* Field only used for lanman2 trans2_findfirst/next searches */
49   uint16 attr; /* Field only used for lanman2 trans2_findfirst/next searches */
50   char *path;
51 }
52 dirptrs[NUMDIRPTRS];
53
54
55 static int dptrs_open = 0;
56
57 /****************************************************************************
58 initialise the dir array
59 ****************************************************************************/
60 void init_dptrs(void)
61 {
62   static BOOL dptrs_init=False;
63   int i;
64
65   if (dptrs_init) return;
66   for (i=0;i<NUMDIRPTRS;i++)    
67     {
68       dirptrs[i].valid = False;
69       dirptrs[i].wcard = NULL;
70       dirptrs[i].ptr = NULL;
71       string_init(&dirptrs[i].path,"");
72     }
73   dptrs_init = True;
74 }
75
76 /****************************************************************************
77 idle a dptr - the directory is closed but the control info is kept
78 ****************************************************************************/
79 static void dptr_idle(int key)
80 {
81   if (dirptrs[key].valid && dirptrs[key].ptr) {
82     DEBUG(4,("Idling dptr key %d\n",key));
83     dptrs_open--;
84     CloseDir(dirptrs[key].ptr);
85     dirptrs[key].ptr = NULL;
86   }    
87 }
88
89 /****************************************************************************
90 idle the oldest dptr
91 ****************************************************************************/
92 static void dptr_idleoldest(void)
93 {
94   int i;
95   uint32 old=dircounter+1;
96   int oldi= -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;
100       oldi = i;
101     }
102   if (oldi != -1)
103     dptr_idle(oldi);
104   else
105     DEBUG(0,("No dptrs available to idle??\n"));
106 }
107
108 /****************************************************************************
109 get the dir ptr for a dir index
110 ****************************************************************************/
111 static void *dptr_get(int key,uint32 lastused)
112 {
113   struct dptr_struct *dp = &dirptrs[key];
114
115   if (dp->valid) {
116     if (lastused) dp->lastused = lastused;
117     if (!dp->ptr) {
118       if (dptrs_open >= MAXDIR)
119         dptr_idleoldest();
120       DEBUG(4,("Reopening dptr key %d\n",key));
121       if ((dp->ptr = OpenDir(dp->cnum, dp->path, True)))
122         dptrs_open++;
123     }
124     return(dp->ptr);
125   }
126   return(NULL);
127 }
128
129 /****************************************************************************
130 get the dir path for a dir index
131 ****************************************************************************/
132 char *dptr_path(int key)
133 {
134   if (dirptrs[key].valid)
135     return(dirptrs[key].path);
136   return(NULL);
137 }
138
139 /****************************************************************************
140 get the dir wcard for a dir index (lanman2 specific)
141 ****************************************************************************/
142 char *dptr_wcard(int key)
143 {
144   if (dirptrs[key].valid)
145     return(dirptrs[key].wcard);
146   return(NULL);
147 }
148
149 /****************************************************************************
150 set the dir wcard for a dir index (lanman2 specific)
151 Returns 0 on ok, 1 on fail.
152 ****************************************************************************/
153 BOOL dptr_set_wcard(int key, char *wcard)
154 {
155   if (dirptrs[key].valid) {
156     dirptrs[key].wcard = wcard;
157     return True;
158   }
159   return False;
160 }
161
162 /****************************************************************************
163 set the dir attrib for a dir index (lanman2 specific)
164 Returns 0 on ok, 1 on fail.
165 ****************************************************************************/
166 BOOL dptr_set_attr(int key, uint16 attr)
167 {
168   if (dirptrs[key].valid) {
169     dirptrs[key].attr = attr;
170     return True;
171   }
172   return False;
173 }
174
175 /****************************************************************************
176 get the dir attrib for a dir index (lanman2 specific)
177 ****************************************************************************/
178 uint16 dptr_attr(int key)
179 {
180   if (dirptrs[key].valid)
181     return(dirptrs[key].attr);
182   return(0);
183 }
184
185 /****************************************************************************
186 close a dptr
187 ****************************************************************************/
188 void dptr_close(int key)
189 {
190   /* OS/2 seems to use -1 to indicate "close all directories" */
191   if (key == -1) {
192     int i;
193     for (i=0;i<NUMDIRPTRS;i++) 
194       dptr_close(i);
195     return;
196   }
197
198   if (key < 0 || key >= NUMDIRPTRS) {
199     DEBUG(3,("Invalid key %d given to dptr_close\n",key));
200     return;
201   }
202
203   if (dirptrs[key].valid) {
204     DEBUG(4,("closing dptr key %d\n",key));
205     if (dirptrs[key].ptr) {
206       CloseDir(dirptrs[key].ptr);
207       dptrs_open--;
208     }
209     /* Lanman 2 specific code */
210     if (dirptrs[key].wcard)
211       free(dirptrs[key].wcard);
212     dirptrs[key].valid = False;
213     string_set(&dirptrs[key].path,"");
214   }
215 }
216
217 /****************************************************************************
218 close all dptrs for a cnum
219 ****************************************************************************/
220 void dptr_closecnum(int cnum)
221 {
222   int i;
223   for (i=0;i<NUMDIRPTRS;i++)
224     if (dirptrs[i].valid && dirptrs[i].cnum == cnum)
225       dptr_close(i);
226 }
227
228 /****************************************************************************
229 idle all dptrs for a cnum
230 ****************************************************************************/
231 void dptr_idlecnum(int cnum)
232 {
233   int i;
234   for (i=0;i<NUMDIRPTRS;i++)
235     if (dirptrs[i].valid && dirptrs[i].cnum == cnum && dirptrs[i].ptr)
236       dptr_idle(i);
237 }
238
239 /****************************************************************************
240 close a dptr that matches a given path, only if it matches the pid also
241 ****************************************************************************/
242 void dptr_closepath(char *path,int pid)
243 {
244   int i;
245   for (i=0;i<NUMDIRPTRS;i++)
246     if (dirptrs[i].valid && pid == dirptrs[i].pid &&
247         strequal(dirptrs[i].path,path))
248       dptr_close(i);
249 }
250
251 /****************************************************************************
252   start a directory listing
253 ****************************************************************************/
254 static BOOL start_dir(int cnum,char *directory)
255 {
256   DEBUG(5,("start_dir cnum=%d dir=%s\n",cnum,directory));
257
258   if (!check_name(directory,cnum))
259     return(False);
260   
261   if (! *directory)
262     directory = ".";
263
264   Connections[cnum].dirptr = OpenDir(cnum, directory, True);
265   if (Connections[cnum].dirptr) {    
266     dptrs_open++;
267     string_set(&Connections[cnum].dirpath,directory);
268     return(True);
269   }
270   
271   return(False);
272 }
273
274
275 /****************************************************************************
276 create a new dir ptr
277 ****************************************************************************/
278 int dptr_create(int cnum,char *path, BOOL expect_close,int pid)
279 {
280   int i;
281   uint32 old;
282   int oldi;
283
284   if (!start_dir(cnum,path))
285     return(-2); /* Code to say use a unix error return code. */
286
287   if (dptrs_open >= MAXDIR)
288     dptr_idleoldest();
289
290   for (i=0;i<NUMDIRPTRS;i++)
291     if (!dirptrs[i].valid)
292       break;
293   if (i == NUMDIRPTRS) i = -1;
294
295
296   /* as a 2nd option, grab the oldest not marked for expect_close */
297   if (i == -1) {
298     old=dircounter+1;
299     oldi= -1;
300     for (i=0;i<NUMDIRPTRS;i++)
301       if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) {
302         old = dirptrs[i].lastused;
303         oldi = i;
304       }
305     i = oldi;
306   }
307
308   /* a 3rd option - grab the oldest one */
309   if (i == -1) {
310     old=dircounter+1;
311     oldi= -1;
312     for (i=0;i<NUMDIRPTRS;i++)
313       if (dirptrs[i].lastused < old) {
314         old = dirptrs[i].lastused;
315         oldi = i;
316       }
317     i = oldi;
318   }
319
320   if (i == -1) {
321     DEBUG(0,("Error - all dirptrs in use??\n"));
322     return(-1);
323   }
324
325   if (dirptrs[i].valid)
326     dptr_close(i);
327
328   dirptrs[i].ptr = Connections[cnum].dirptr;
329   string_set(&dirptrs[i].path,path);
330   dirptrs[i].lastused = dircounter++;
331   dirptrs[i].finished = False;
332   dirptrs[i].cnum = cnum;
333   dirptrs[i].pid = pid;
334   dirptrs[i].expect_close = expect_close;
335   dirptrs[i].wcard = NULL; /* Only used in lanman2 searches */
336   dirptrs[i].attr = 0; /* Only used in lanman2 searches */
337   dirptrs[i].valid = True;
338
339   DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
340            i,path,expect_close));  
341
342   return(i);
343 }
344
345 #define DPTR_MASK ((uint32)(((uint32)1)<<31))
346
347 /****************************************************************************
348 fill the 5 byte server reserved dptr field
349 ****************************************************************************/
350 BOOL dptr_fill(char *buf1,unsigned int key)
351 {
352   unsigned char *buf = (unsigned char *)buf1;
353   void *p = dptr_get(key,0);
354   uint32 offset;
355   if (!p) {
356     DEBUG(1,("filling null dirptr %d\n",key));
357     return(False);
358   }
359   offset = TellDir(p);
360   DEBUG(6,("fill on key %d dirptr 0x%x now at %d\n",key,p,offset));
361   buf[0] = key;
362   SIVAL(buf,1,offset | DPTR_MASK);
363   return(True);
364 }
365
366
367 /****************************************************************************
368 return True is the offset is at zero
369 ****************************************************************************/
370 BOOL dptr_zero(char *buf)
371 {
372   return((IVAL(buf,1)&~DPTR_MASK) == 0);
373 }
374
375 /****************************************************************************
376 fetch the dir ptr and seek it given the 5 byte server field
377 ****************************************************************************/
378 void *dptr_fetch(char *buf,int *num)
379 {
380   unsigned int key = *(unsigned char *)buf;
381   void *p = dptr_get(key,dircounter++);
382   uint32 offset;
383   if (!p) {
384     DEBUG(3,("fetched null dirptr %d\n",key));
385     return(NULL);
386   }
387   *num = key;
388   offset = IVAL(buf,1)&~DPTR_MASK;
389   SeekDir(p,offset);
390   DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
391            key,dptr_path(key),offset));
392   return(p);
393 }
394
395 /****************************************************************************
396 fetch the dir ptr and seek it given the lanman2 parameter block
397 ****************************************************************************/
398 void *dptr_fetch_lanman2(char *params,int dptr_num)
399 {
400   void *p = dptr_get(dptr_num,dircounter++);
401   uint32 resume_key = SVAL(params,6);
402   BOOL uses_resume_key = BITSETW(params+10,2);
403   BOOL continue_bit = BITSETW(params+10,3);
404
405   if (!p) {
406     DEBUG(3,("fetched null dirptr %d\n",dptr_num));
407     return(NULL);
408   }
409   if(uses_resume_key && !continue_bit)
410     SeekDir(p,resume_key);
411   DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
412   return(p);
413 }
414
415 /****************************************************************************
416 check a filetype for being valid
417 ****************************************************************************/
418 BOOL dir_check_ftype(int cnum,int mode,struct stat *st,int dirtype)
419 {
420   if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
421     return False;
422   return True;
423 }
424
425 /****************************************************************************
426   get a directory entry
427 ****************************************************************************/
428 BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend)
429 {
430   char *dname;
431   BOOL found = False;
432   struct stat sbuf;
433   pstring path;
434   pstring pathreal;
435   BOOL isrootdir;
436   pstring filename;
437   BOOL matched;
438   BOOL needslash;
439
440   *path = *pathreal = *filename = 0;
441
442   isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
443                strequal(Connections[cnum].dirpath,".") ||
444                strequal(Connections[cnum].dirpath,"/"));
445   
446   needslash = 
447         ( Connections[cnum].dirpath[strlen(Connections[cnum].dirpath) -1] != '/');
448
449   if (!Connections[cnum].dirptr)
450     return(False);
451   
452   while (!found)
453     {
454       dname = ReadDirName(Connections[cnum].dirptr);
455
456       DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n",
457             Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
458       
459       if (dname == NULL) 
460         return(False);
461       
462       matched = False;
463
464       pstrcpy(filename,dname);      
465
466       if ((strcmp(filename,mask) == 0) ||
467           (name_map_mangle(filename,True,SNUM(cnum)) &&
468            mask_match(filename,mask,False,False)))
469         {
470           if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
471             continue;
472
473           pstrcpy(fname,filename);
474           *path = 0;
475           pstrcpy(path,Connections[cnum].dirpath);
476           if(needslash)
477             strcat(path,"/");
478           pstrcpy(pathreal,path);
479           strcat(path,fname);
480           strcat(pathreal,dname);
481           if (sys_stat(pathreal,&sbuf) != 0) 
482             {
483               DEBUG(5,("Couldn't stat 1 [%s]\n",path));
484               continue;
485             }
486
487           if (check_descend &&
488               !strequal(fname,".") && !strequal(fname,".."))
489             continue;
490           
491           *mode = dos_mode(cnum,pathreal,&sbuf);
492
493           if (!dir_check_ftype(cnum,*mode,&sbuf,dirtype)) {
494             DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
495             continue;
496           }
497
498           *size = sbuf.st_size;
499           *date = sbuf.st_mtime;
500
501           DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname));
502           
503           found = True;
504         }
505     }
506
507   return(found);
508 }
509
510
511
512 typedef struct
513 {
514   int pos;
515   int numentries;
516   int mallocsize;
517   char *data;
518   char *current;
519 } Dir;
520
521
522 /*******************************************************************
523 open a directory
524 ********************************************************************/
525 void *OpenDir(int cnum, char *name, BOOL use_veto)
526 {
527   Dir *dirp;
528   char *n;
529   void *p = sys_opendir(name);
530   int used=0;
531
532   if (!p) return(NULL);
533   dirp = (Dir *)malloc(sizeof(Dir));
534   if (!dirp) {
535     closedir(p);
536     return(NULL);
537   }
538   dirp->pos = dirp->numentries = dirp->mallocsize = 0;
539   dirp->data = dirp->current = NULL;
540
541   while ((n = readdirname(p)))
542   {
543     int l = strlen(n)+1;
544
545     /* If it's a vetoed file, pretend it doesn't even exist */
546     if (use_veto && IS_VETO_PATH(cnum, n)) continue;
547
548     if (used + l > dirp->mallocsize) {
549       int s = MAX(used+l,used+2000);
550       char *r;
551       r = (char *)Realloc(dirp->data,s);
552       if (!r) {
553         DEBUG(0,("Out of memory in OpenDir\n"));
554         break;
555       }
556       dirp->data = r;
557       dirp->mallocsize = s;
558       dirp->current = dirp->data;
559     }
560     strcpy(dirp->data+used,n);
561     used += l;
562     dirp->numentries++;
563   }
564
565   closedir(p);
566   return((void *)dirp);
567 }
568
569
570 /*******************************************************************
571 close a directory
572 ********************************************************************/
573 void CloseDir(void *p)
574 {
575   Dir *dirp = (Dir *)p;
576   if (!dirp) return;    
577   if (dirp->data) free(dirp->data);
578   free(dirp);
579 }
580
581 /*******************************************************************
582 read from a directory
583 ********************************************************************/
584 char *ReadDirName(void *p)
585 {
586   char *ret;
587   Dir *dirp = (Dir *)p;
588
589   if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
590
591   ret = dirp->current;
592   dirp->current = skip_string(dirp->current,1);
593   dirp->pos++;
594
595   return(ret);
596 }
597
598
599 /*******************************************************************
600 seek a dir
601 ********************************************************************/
602 BOOL SeekDir(void *p,int pos)
603 {
604   Dir *dirp = (Dir *)p;
605
606   if (!dirp) return(False);
607
608   if (pos < dirp->pos) {
609     dirp->current = dirp->data;
610     dirp->pos = 0;
611   }
612
613   while (dirp->pos < pos && ReadDirName(p)) ;
614
615   return(dirp->pos == pos);
616 }
617
618 /*******************************************************************
619 tell a dir position
620 ********************************************************************/
621 int TellDir(void *p)
622 {
623   Dir *dirp = (Dir *)p;
624
625   if (!dirp) return(-1);
626   
627   return(dirp->pos);
628 }
629
630
631 /* -------------------------------------------------------------------------- **
632  * This section manages a global directory cache.
633  * (It should probably be split into a separate module.  crh)
634  * -------------------------------------------------------------------------- **
635  */
636
637 #include "ubi_dLinkList.h"
638
639 typedef struct
640   {
641   ubi_dlNode  node;
642   char       *path;
643   char       *name;
644   char       *dname;
645   int         snum;
646   } dir_cache_entry;
647
648 static ubi_dlList dir_cache[1] = { { NULL, NULL, 0 } };
649
650 void DirCacheAdd( char *path, char *name, char *dname, int snum )
651   /* ------------------------------------------------------------------------ **
652    * Add an entry to the directory cache.
653    *
654    *  Input:  path  -
655    *          name  -
656    *          dname -
657    *          snum  -
658    *
659    *  Output: None.
660    *
661    * ------------------------------------------------------------------------ **
662    */
663   {
664   int               pathlen;
665   int               namelen;
666   dir_cache_entry  *entry;
667
668   /* Allocate the structure & string space in one go so that it can be freed
669    * in one call to free().
670    */
671   pathlen = strlen( path ) +1;  /* Bytes required to store path (with nul). */
672   namelen = strlen( name ) +1;  /* Bytes required to store name (with nul). */
673   entry = (dir_cache_entry *)malloc( sizeof( dir_cache_entry )
674                                    + pathlen
675                                    + namelen
676                                    + strlen( dname ) +1 );
677   if( NULL == entry )   /* Not adding to the cache is not fatal,  */
678     return;             /* so just return as if nothing happened. */
679
680   /* Set pointers correctly and load values. */
681   entry->path  = strcpy( (char *)&entry[1],       path);
682   entry->name  = strcpy( &(entry->path[pathlen]), name);
683   entry->dname = strcpy( &(entry->name[namelen]), dname);
684   entry->snum  = snum;
685
686   /* Add the new entry to the linked list. */
687   (void)ubi_dlAddHead( dir_cache, entry );
688   DEBUG( 4, ("Added dir cache entry %s %s -> %s\n", path, name, dname ) );
689
690   /* Free excess cache entries. */
691   while( DIRCACHESIZE < dir_cache->count )
692     free( ubi_dlRemTail( dir_cache ) );
693
694   } /* DirCacheAdd */
695
696
697 char *DirCacheCheck( char *path, char *name, int snum )
698   /* ------------------------------------------------------------------------ **
699    * Search for an entry to the directory cache.
700    *
701    *  Input:  path  -
702    *          name  -
703    *          snum  -
704    *
705    *  Output: The dname string of the located entry, or NULL if the entry was
706    *          not found.
707    *
708    *  Notes:  This uses a linear search, which is is okay because of
709    *          the small size of the cache.  Use a splay tree or hash
710    *          for large caches.
711    *
712    * ------------------------------------------------------------------------ **
713    */
714   {
715   dir_cache_entry *entry;
716
717   for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
718        NULL != entry;
719        entry = (dir_cache_entry *)ubi_dlNext( entry ) )
720     {
721     if( entry->snum == snum
722         && 0 == strcmp( name, entry->name )
723         && 0 == strcmp( path, entry->path ) )
724       {
725       DEBUG(4, ("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
726       return( entry->dname );
727       }
728     }
729
730   return(NULL);
731   } /* DirCacheCheck */
732
733 void DirCacheFlush( int snum )
734   /* ------------------------------------------------------------------------ **
735    * Remove all cache entries which have an snum that matches the input.
736    *
737    *  Input:  snum  -
738    *
739    *  Output: None.
740    *
741    * ------------------------------------------------------------------------ **
742    */
743   {
744   dir_cache_entry *entry;
745   ubi_dlNodePtr    next;
746
747   for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache ); NULL != entry; )
748     {
749     next = ubi_dlNext( entry );
750     if( entry->snum == snum )
751       free( ubi_dlRemThis( dir_cache, entry ) );
752     entry = (dir_cache_entry *)next;
753     }
754   } /* DirCacheFlush */
755
756 /* -------------------------------------------------------------------------- **
757  * End of the section that manages the global directory cache.
758  * -------------------------------------------------------------------------- **
759  */
760
761
762 #ifdef REPLACE_GETWD
763 /* This is getcwd.c from bash.  It is needed in Interactive UNIX.  To
764  * add support for another OS you need to determine which of the
765  * conditional compilation macros you need to define.  All the options
766  * are defined for Interactive UNIX.
767  */
768 #ifdef ISC
769 #define HAVE_UNISTD_H
770 #define USGr3
771 #define USG
772 #endif
773
774 #if defined (HAVE_UNISTD_H)
775 #  include <unistd.h>
776 #endif
777
778 #if defined (__STDC__)
779 #  define CONST const
780 #  define PTR void *
781 #else /* !__STDC__ */
782 #  define CONST
783 #  define PTR char *
784 #endif /* !__STDC__ */
785
786 #if !defined (PATH_MAX)
787 #  if defined (MAXPATHLEN)
788 #    define PATH_MAX MAXPATHLEN
789 #  else /* !MAXPATHLEN */
790 #    define PATH_MAX 1024
791 #  endif /* !MAXPATHLEN */
792 #endif /* !PATH_MAX */
793
794 #if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H)
795 #  if !defined (HAVE_DIRENT)
796 #    define HAVE_DIRENT
797 #  endif /* !HAVE_DIRENT */
798 #endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */
799
800 #if defined (HAVE_DIRENT)
801 #  define D_NAMLEN(d)   (strlen ((d)->d_name))
802 #else
803 #  define D_NAMLEN(d)   ((d)->d_namlen)
804 #endif /* ! (_POSIX_VERSION || USGr3) */
805
806 #if defined (USG) || defined (USGr3)
807 #  define d_fileno d_ino
808 #endif
809
810 #if !defined (alloca)
811 extern char *alloca ();
812 #endif /* alloca */
813
814 /* Get the pathname of the current working directory,
815    and put it in SIZE bytes of BUF.  Returns NULL if the
816    directory couldn't be determined or SIZE was too small.
817    If successful, returns BUF.  In GNU, if BUF is NULL,
818    an array is allocated with `malloc'; the array is SIZE
819    bytes long, unless SIZE <= 0, in which case it is as
820    big as necessary.  */
821 #if defined (__STDC__)
822 char *
823 getcwd (char *buf, size_t size)
824 #else /* !__STDC__ */
825 char *
826 getcwd (buf, size)
827      char *buf;
828      int size;
829 #endif /* !__STDC__ */
830 {
831   static CONST char dots[]
832     = "../../../../../../../../../../../../../../../../../../../../../../../\
833 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
834 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
835   CONST char *dotp, *dotlist;
836   size_t dotsize;
837   dev_t rootdev, thisdev;
838   ino_t rootino, thisino;
839   char path[PATH_MAX + 1];
840   register char *pathp;
841   char *pathbuf;
842   size_t pathsize;
843   struct stat st;
844
845   if (buf != NULL && size == 0)
846     {
847       errno = EINVAL;
848       return ((char *)NULL);
849     }
850
851   pathsize = sizeof (path);
852   pathp = &path[pathsize];
853   *--pathp = '\0';
854   pathbuf = path;
855
856   if (stat (".", &st) < 0)
857     return ((char *)NULL);
858   thisdev = st.st_dev;
859   thisino = st.st_ino;
860
861   if (stat ("/", &st) < 0)
862     return ((char *)NULL);
863   rootdev = st.st_dev;
864   rootino = st.st_ino;
865
866   dotsize = sizeof (dots) - 1;
867   dotp = &dots[sizeof (dots)];
868   dotlist = dots;
869   while (!(thisdev == rootdev && thisino == rootino))
870     {
871       register DIR *dirstream;
872       register struct dirent *d;
873       dev_t dotdev;
874       ino_t dotino;
875       char mount_point;
876       int namlen;
877
878       /* Look at the parent directory.  */
879       if (dotp == dotlist)
880         {
881           /* My, what a deep directory tree you have, Grandma.  */
882           char *new;
883           if (dotlist == dots)
884             {
885               new = malloc (dotsize * 2 + 1);
886               if (new == NULL)
887                 goto lose;
888               memcpy (new, dots, dotsize);
889             }
890           else
891             {
892               new = realloc ((PTR) dotlist, dotsize * 2 + 1);
893               if (new == NULL)
894                 goto lose;
895             }
896           memcpy (&new[dotsize], new, dotsize);
897           dotp = &new[dotsize];
898           dotsize *= 2;
899           new[dotsize] = '\0';
900           dotlist = new;
901         }
902
903       dotp -= 3;
904
905       /* Figure out if this directory is a mount point.  */
906       if (stat (dotp, &st) < 0)
907         goto lose;
908       dotdev = st.st_dev;
909       dotino = st.st_ino;
910       mount_point = dotdev != thisdev;
911
912       /* Search for the last directory.  */
913       dirstream = opendir(dotp);
914       if (dirstream == NULL)
915         goto lose;
916       while ((d = (struct dirent *)readdir(dirstream)) != NULL)
917         {
918           if (d->d_name[0] == '.' &&
919               (d->d_name[1] == '\0' ||
920                 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
921             continue;
922           if (mount_point || d->d_fileno == thisino)
923             {
924               char *name;
925
926               namlen = D_NAMLEN(d);
927               name = (char *)
928                 alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
929               memcpy (name, dotp, dotlist + dotsize - dotp);
930               name[dotlist + dotsize - dotp] = '/';
931               memcpy (&name[dotlist + dotsize - dotp + 1],
932                       d->d_name, namlen + 1);
933               if (lstat (name, &st) < 0)
934                 {
935                   int save = errno;
936                   closedir(dirstream);
937                   errno = save;
938                   goto lose;
939                 }
940               if (st.st_dev == thisdev && st.st_ino == thisino)
941                 break;
942             }
943         }
944       if (d == NULL)
945         {
946           int save = errno;
947           closedir(dirstream);
948           errno = save;
949           goto lose;
950         }
951       else
952         {
953           size_t space;
954
955           while ((space = pathp - pathbuf) <= namlen)
956             {
957               char *new;
958
959               if (pathbuf == path)
960                 {
961                   new = malloc (pathsize * 2);
962                   if (!new)
963                     goto lose;
964                 }
965               else
966                 {
967                   new = realloc ((PTR) pathbuf, (pathsize * 2));
968                   if (!new)
969                     goto lose;
970                   pathp = new + space;
971                 }
972               (void) memcpy (new + pathsize + space, pathp, pathsize - space);
973               pathp = new + pathsize + space;
974               pathbuf = new;
975               pathsize *= 2;
976             }
977
978           pathp -= namlen;
979           (void) memcpy (pathp, d->d_name, namlen);
980           *--pathp = '/';
981           closedir(dirstream);
982         }
983
984       thisdev = dotdev;
985       thisino = dotino;
986     }
987
988   if (pathp == &path[sizeof(path) - 1])
989     *--pathp = '/';
990
991   if (dotlist != dots)
992     free ((PTR) dotlist);
993
994   {
995     size_t len = pathbuf + pathsize - pathp;
996     if (buf == NULL)
997       {
998         if (len < (size_t) size)
999           len = size;
1000         buf = (char *) malloc (len);
1001         if (buf == NULL)
1002           goto lose2;
1003       }
1004     else if ((size_t) size < len)
1005       {
1006         errno = ERANGE;
1007         goto lose2;
1008       }
1009     (void) memcpy((PTR) buf, (PTR) pathp, len);
1010   }
1011
1012   if (pathbuf != path)
1013     free (pathbuf);
1014
1015   return (buf);
1016
1017  lose:
1018   if ((dotlist != dots) && dotlist)
1019     {
1020       int e = errno;
1021       free ((PTR) dotlist);
1022       errno = e;
1023     }
1024
1025  lose2:
1026   if ((pathbuf != path) && pathbuf)
1027     {
1028       int e = errno;
1029       free ((PTR) pathbuf);
1030       errno = e;
1031     }
1032   return ((char *)NULL);
1033 }
1034 #endif