Changed the definition of the linked list header used in the directory
[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-1998
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 >= MAX_OPEN_DIRECTORIES)
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 >= MAX_OPEN_DIRECTORIES)
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.
397 ****************************************************************************/
398 void *dptr_fetch_lanman2(int dptr_num)
399 {
400   void *p = dptr_get(dptr_num,dircounter++);
401
402   if (!p) {
403     DEBUG(3,("fetched null dirptr %d\n",dptr_num));
404     return(NULL);
405   }
406   DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
407   return(p);
408 }
409
410 /****************************************************************************
411 check a filetype for being valid
412 ****************************************************************************/
413 BOOL dir_check_ftype(int cnum,int mode,struct stat *st,int dirtype)
414 {
415   if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
416     return False;
417   return True;
418 }
419
420 /****************************************************************************
421   get a directory entry
422 ****************************************************************************/
423 BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend)
424 {
425   char *dname;
426   BOOL found = False;
427   struct stat sbuf;
428   pstring path;
429   pstring pathreal;
430   BOOL isrootdir;
431   pstring filename;
432   BOOL matched;
433   BOOL needslash;
434
435   *path = *pathreal = *filename = 0;
436
437   isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
438                strequal(Connections[cnum].dirpath,".") ||
439                strequal(Connections[cnum].dirpath,"/"));
440   
441   needslash = 
442         ( Connections[cnum].dirpath[strlen(Connections[cnum].dirpath) -1] != '/');
443
444   if (!Connections[cnum].dirptr)
445     return(False);
446   
447   while (!found)
448     {
449       dname = ReadDirName(Connections[cnum].dirptr);
450
451       DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n",
452             Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
453       
454       if (dname == NULL) 
455         return(False);
456       
457       matched = False;
458
459       pstrcpy(filename,dname);      
460
461       if ((strcmp(filename,mask) == 0) ||
462           (name_map_mangle(filename,True,SNUM(cnum)) &&
463            mask_match(filename,mask,False,False)))
464         {
465           if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
466             continue;
467
468           pstrcpy(fname,filename);
469           *path = 0;
470           pstrcpy(path,Connections[cnum].dirpath);
471           if(needslash)
472             pstrcat(path,"/");
473           pstrcpy(pathreal,path);
474           pstrcat(path,fname);
475           pstrcat(pathreal,dname);
476           if (sys_stat(pathreal,&sbuf) != 0) 
477             {
478               DEBUG(5,("Couldn't stat 1 [%s]\n",path));
479               continue;
480             }
481
482           if (check_descend &&
483               !strequal(fname,".") && !strequal(fname,".."))
484             continue;
485           
486           *mode = dos_mode(cnum,pathreal,&sbuf);
487
488           if (!dir_check_ftype(cnum,*mode,&sbuf,dirtype)) {
489             DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
490             continue;
491           }
492
493           *size = sbuf.st_size;
494           *date = sbuf.st_mtime;
495
496           DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname));
497           
498           found = True;
499         }
500     }
501
502   return(found);
503 }
504
505
506
507 typedef struct
508 {
509   int pos;
510   int numentries;
511   int mallocsize;
512   char *data;
513   char *current;
514 } Dir;
515
516
517 /*******************************************************************
518 open a directory
519 ********************************************************************/
520 void *OpenDir(int cnum, char *name, BOOL use_veto)
521 {
522   Dir *dirp;
523   char *n;
524   void *p = sys_opendir(name);
525   int used=0;
526
527   if (!p) return(NULL);
528   dirp = (Dir *)malloc(sizeof(Dir));
529   if (!dirp) {
530     closedir(p);
531     return(NULL);
532   }
533   dirp->pos = dirp->numentries = dirp->mallocsize = 0;
534   dirp->data = dirp->current = NULL;
535
536   while ((n = readdirname(p)))
537   {
538     int l = strlen(n)+1;
539
540     /* If it's a vetoed file, pretend it doesn't even exist */
541     if (use_veto && IS_VETO_PATH(cnum, n)) continue;
542
543     if (used + l > dirp->mallocsize) {
544       int s = MAX(used+l,used+2000);
545       char *r;
546       r = (char *)Realloc(dirp->data,s);
547       if (!r) {
548         DEBUG(0,("Out of memory in OpenDir\n"));
549         break;
550       }
551       dirp->data = r;
552       dirp->mallocsize = s;
553       dirp->current = dirp->data;
554     }
555     pstrcpy(dirp->data+used,n);
556     used += l;
557     dirp->numentries++;
558   }
559
560   closedir(p);
561   return((void *)dirp);
562 }
563
564
565 /*******************************************************************
566 close a directory
567 ********************************************************************/
568 void CloseDir(void *p)
569 {
570   Dir *dirp = (Dir *)p;
571   if (!dirp) return;    
572   if (dirp->data) free(dirp->data);
573   free(dirp);
574 }
575
576 /*******************************************************************
577 read from a directory
578 ********************************************************************/
579 char *ReadDirName(void *p)
580 {
581   char *ret;
582   Dir *dirp = (Dir *)p;
583
584   if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
585
586   ret = dirp->current;
587   dirp->current = skip_string(dirp->current,1);
588   dirp->pos++;
589
590   return(ret);
591 }
592
593
594 /*******************************************************************
595 seek a dir
596 ********************************************************************/
597 BOOL SeekDir(void *p,int pos)
598 {
599   Dir *dirp = (Dir *)p;
600
601   if (!dirp) return(False);
602
603   if (pos < dirp->pos) {
604     dirp->current = dirp->data;
605     dirp->pos = 0;
606   }
607
608   while (dirp->pos < pos && ReadDirName(p)) ;
609
610   return(dirp->pos == pos);
611 }
612
613 /*******************************************************************
614 tell a dir position
615 ********************************************************************/
616 int TellDir(void *p)
617 {
618   Dir *dirp = (Dir *)p;
619
620   if (!dirp) return(-1);
621   
622   return(dirp->pos);
623 }
624
625
626 /* -------------------------------------------------------------------------- **
627  * This section manages a global directory cache.
628  * (It should probably be split into a separate module.  crh)
629  * -------------------------------------------------------------------------- **
630  */
631
632 typedef struct
633   {
634   ubi_dlNode  node;
635   char       *path;
636   char       *name;
637   char       *dname;
638   int         snum;
639   } dir_cache_entry;
640
641 static ubi_dlNewList( dir_cache );
642
643 void DirCacheAdd( char *path, char *name, char *dname, int snum )
644   /* ------------------------------------------------------------------------ **
645    * Add an entry to the directory cache.
646    *
647    *  Input:  path  -
648    *          name  -
649    *          dname -
650    *          snum  -
651    *
652    *  Output: None.
653    *
654    * ------------------------------------------------------------------------ **
655    */
656   {
657   int               pathlen;
658   int               namelen;
659   dir_cache_entry  *entry;
660
661   /* Allocate the structure & string space in one go so that it can be freed
662    * in one call to free().
663    */
664   pathlen = strlen( path ) +1;  /* Bytes required to store path (with nul). */
665   namelen = strlen( name ) +1;  /* Bytes required to store name (with nul). */
666   entry = (dir_cache_entry *)malloc( sizeof( dir_cache_entry )
667                                    + pathlen
668                                    + namelen
669                                    + strlen( dname ) +1 );
670   if( NULL == entry )   /* Not adding to the cache is not fatal,  */
671     return;             /* so just return as if nothing happened. */
672
673   /* Set pointers correctly and load values. */
674   entry->path  = pstrcpy( (char *)&entry[1],       path);
675   entry->name  = pstrcpy( &(entry->path[pathlen]), name);
676   entry->dname = pstrcpy( &(entry->name[namelen]), dname);
677   entry->snum  = snum;
678
679   /* Add the new entry to the linked list. */
680   (void)ubi_dlAddHead( dir_cache, entry );
681   DEBUG( 4, ("Added dir cache entry %s %s -> %s\n", path, name, dname ) );
682
683   /* Free excess cache entries. */
684   while( DIRCACHESIZE < dir_cache->count )
685     free( ubi_dlRemTail( dir_cache ) );
686
687   } /* DirCacheAdd */
688
689
690 char *DirCacheCheck( char *path, char *name, int snum )
691   /* ------------------------------------------------------------------------ **
692    * Search for an entry to the directory cache.
693    *
694    *  Input:  path  -
695    *          name  -
696    *          snum  -
697    *
698    *  Output: The dname string of the located entry, or NULL if the entry was
699    *          not found.
700    *
701    *  Notes:  This uses a linear search, which is is okay because of
702    *          the small size of the cache.  Use a splay tree or hash
703    *          for large caches.
704    *
705    * ------------------------------------------------------------------------ **
706    */
707   {
708   dir_cache_entry *entry;
709
710   for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
711        NULL != entry;
712        entry = (dir_cache_entry *)ubi_dlNext( entry ) )
713     {
714     if( entry->snum == snum
715         && 0 == strcmp( name, entry->name )
716         && 0 == strcmp( path, entry->path ) )
717       {
718       DEBUG(4, ("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
719       return( entry->dname );
720       }
721     }
722
723   return(NULL);
724   } /* DirCacheCheck */
725
726 void DirCacheFlush( int snum )
727   /* ------------------------------------------------------------------------ **
728    * Remove all cache entries which have an snum that matches the input.
729    *
730    *  Input:  snum  -
731    *
732    *  Output: None.
733    *
734    * ------------------------------------------------------------------------ **
735    */
736   {
737   dir_cache_entry *entry;
738   ubi_dlNodePtr    next;
739
740   for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache ); NULL != entry; )
741     {
742     next = ubi_dlNext( entry );
743     if( entry->snum == snum )
744       free( ubi_dlRemThis( dir_cache, entry ) );
745     entry = (dir_cache_entry *)next;
746     }
747   } /* DirCacheFlush */
748
749 /* -------------------------------------------------------------------------- **
750  * End of the section that manages the global directory cache.
751  * -------------------------------------------------------------------------- **
752  */
753
754
755 #ifdef REPLACE_GETWD
756 /* This is getcwd.c from bash.  It is needed in Interactive UNIX.  To
757  * add support for another OS you need to determine which of the
758  * conditional compilation macros you need to define.  All the options
759  * are defined for Interactive UNIX.
760  */
761 #ifdef ISC
762 #define HAVE_UNISTD_H
763 #define USGr3
764 #define USG
765 #endif
766
767 #if defined (HAVE_UNISTD_H)
768 #  include <unistd.h>
769 #endif
770
771 #if defined (__STDC__)
772 #  define CONST const
773 #  define PTR void *
774 #else /* !__STDC__ */
775 #  define CONST
776 #  define PTR char *
777 #endif /* !__STDC__ */
778
779 #if !defined (PATH_MAX)
780 #  if defined (MAXPATHLEN)
781 #    define PATH_MAX MAXPATHLEN
782 #  else /* !MAXPATHLEN */
783 #    define PATH_MAX 1024
784 #  endif /* !MAXPATHLEN */
785 #endif /* !PATH_MAX */
786
787 #if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H)
788 #  if !defined (HAVE_DIRENT)
789 #    define HAVE_DIRENT
790 #  endif /* !HAVE_DIRENT */
791 #endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */
792
793 #if defined (HAVE_DIRENT)
794 #  define D_NAMLEN(d)   (strlen ((d)->d_name))
795 #else
796 #  define D_NAMLEN(d)   ((d)->d_namlen)
797 #endif /* ! (_POSIX_VERSION || USGr3) */
798
799 #if defined (USG) || defined (USGr3)
800 #  define d_fileno d_ino
801 #endif
802
803 #if !defined (alloca)
804 extern char *alloca ();
805 #endif /* alloca */
806
807 /* Get the pathname of the current working directory,
808    and put it in SIZE bytes of BUF.  Returns NULL if the
809    directory couldn't be determined or SIZE was too small.
810    If successful, returns BUF.  In GNU, if BUF is NULL,
811    an array is allocated with `malloc'; the array is SIZE
812    bytes long, unless SIZE <= 0, in which case it is as
813    big as necessary.  */
814 #if defined (__STDC__)
815 char *
816 getcwd (char *buf, size_t size)
817 #else /* !__STDC__ */
818 char *
819 getcwd (buf, size)
820      char *buf;
821      int size;
822 #endif /* !__STDC__ */
823 {
824   static CONST char dots[]
825     = "../../../../../../../../../../../../../../../../../../../../../../../\
826 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
827 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
828   CONST char *dotp, *dotlist;
829   size_t dotsize;
830   dev_t rootdev, thisdev;
831   ino_t rootino, thisino;
832   char path[PATH_MAX + 1];
833   register char *pathp;
834   char *pathbuf;
835   size_t pathsize;
836   struct stat st;
837
838   if (buf != NULL && size == 0)
839     {
840       errno = EINVAL;
841       return ((char *)NULL);
842     }
843
844   pathsize = sizeof (path);
845   pathp = &path[pathsize];
846   *--pathp = '\0';
847   pathbuf = path;
848
849   if (stat (".", &st) < 0)
850     return ((char *)NULL);
851   thisdev = st.st_dev;
852   thisino = st.st_ino;
853
854   if (stat ("/", &st) < 0)
855     return ((char *)NULL);
856   rootdev = st.st_dev;
857   rootino = st.st_ino;
858
859   dotsize = sizeof (dots) - 1;
860   dotp = &dots[sizeof (dots)];
861   dotlist = dots;
862   while (!(thisdev == rootdev && thisino == rootino))
863     {
864       register DIR *dirstream;
865       register struct dirent *d;
866       dev_t dotdev;
867       ino_t dotino;
868       char mount_point;
869       int namlen;
870
871       /* Look at the parent directory.  */
872       if (dotp == dotlist)
873         {
874           /* My, what a deep directory tree you have, Grandma.  */
875           char *new;
876           if (dotlist == dots)
877             {
878               new = malloc (dotsize * 2 + 1);
879               if (new == NULL)
880                 goto lose;
881               memcpy (new, dots, dotsize);
882             }
883           else
884             {
885               new = realloc ((PTR) dotlist, dotsize * 2 + 1);
886               if (new == NULL)
887                 goto lose;
888             }
889           memcpy (&new[dotsize], new, dotsize);
890           dotp = &new[dotsize];
891           dotsize *= 2;
892           new[dotsize] = '\0';
893           dotlist = new;
894         }
895
896       dotp -= 3;
897
898       /* Figure out if this directory is a mount point.  */
899       if (stat (dotp, &st) < 0)
900         goto lose;
901       dotdev = st.st_dev;
902       dotino = st.st_ino;
903       mount_point = dotdev != thisdev;
904
905       /* Search for the last directory.  */
906       dirstream = opendir(dotp);
907       if (dirstream == NULL)
908         goto lose;
909       while ((d = (struct dirent *)readdir(dirstream)) != NULL)
910         {
911           if (d->d_name[0] == '.' &&
912               (d->d_name[1] == '\0' ||
913                 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
914             continue;
915           if (mount_point || d->d_fileno == thisino)
916             {
917               char *name;
918
919               namlen = D_NAMLEN(d);
920               name = (char *)
921                 alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
922               memcpy (name, dotp, dotlist + dotsize - dotp);
923               name[dotlist + dotsize - dotp] = '/';
924               memcpy (&name[dotlist + dotsize - dotp + 1],
925                       d->d_name, namlen + 1);
926               if (lstat (name, &st) < 0)
927                 {
928                   int save = errno;
929                   closedir(dirstream);
930                   errno = save;
931                   goto lose;
932                 }
933               if (st.st_dev == thisdev && st.st_ino == thisino)
934                 break;
935             }
936         }
937       if (d == NULL)
938         {
939           int save = errno;
940           closedir(dirstream);
941           errno = save;
942           goto lose;
943         }
944       else
945         {
946           size_t space;
947
948           while ((space = pathp - pathbuf) <= namlen)
949             {
950               char *new;
951
952               if (pathbuf == path)
953                 {
954                   new = malloc (pathsize * 2);
955                   if (!new)
956                     goto lose;
957                 }
958               else
959                 {
960                   new = realloc ((PTR) pathbuf, (pathsize * 2));
961                   if (!new)
962                     goto lose;
963                   pathp = new + space;
964                 }
965               (void) memcpy (new + pathsize + space, pathp, pathsize - space);
966               pathp = new + pathsize + space;
967               pathbuf = new;
968               pathsize *= 2;
969             }
970
971           pathp -= namlen;
972           (void) memcpy (pathp, d->d_name, namlen);
973           *--pathp = '/';
974           closedir(dirstream);
975         }
976
977       thisdev = dotdev;
978       thisino = dotino;
979     }
980
981   if (pathp == &path[sizeof(path) - 1])
982     *--pathp = '/';
983
984   if (dotlist != dots)
985     free ((PTR) dotlist);
986
987   {
988     size_t len = pathbuf + pathsize - pathp;
989     if (buf == NULL)
990       {
991         if (len < (size_t) size)
992           len = size;
993         buf = (char *) malloc (len);
994         if (buf == NULL)
995           goto lose2;
996       }
997     else if ((size_t) size < len)
998       {
999         errno = ERANGE;
1000         goto lose2;
1001       }
1002     (void) memcpy((PTR) buf, (PTR) pathp, len);
1003   }
1004
1005   if (pathbuf != path)
1006     free (pathbuf);
1007
1008   return (buf);
1009
1010  lose:
1011   if ((dotlist != dots) && dotlist)
1012     {
1013       int e = errno;
1014       free ((PTR) dotlist);
1015       errno = e;
1016     }
1017
1018  lose2:
1019   if ((pathbuf != path) && pathbuf)
1020     {
1021       int e = errno;
1022       free ((PTR) pathbuf);
1023       errno = e;
1024     }
1025   return ((char *)NULL);
1026 }
1027 #endif