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