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