dir.c: Fixed double slash issue.
[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 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(dirptrs[key].path)))
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(directory);
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(char *buf1,unsigned int key)
349 {
350   unsigned char *buf = (unsigned char *)buf1;
351   void *p = dptr_get(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(char *buf,int *num)
377 {
378   unsigned int key = *(unsigned char *)buf;
379   void *p = dptr_get(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(char *params,int dptr_num)
397 {
398   void *p = dptr_get(dptr_num,dircounter++);
399   uint32 resume_key = SVAL(params,6);
400   BOOL uses_resume_key = BITSETW(params+10,2);
401   BOOL continue_bit = BITSETW(params+10,3);
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(char *name)
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     int l = strlen(n)+1;
541     /* If it's a vetoed file, pretend it doesn't even exist */
542     if(is_vetoed_name(n))
543       continue;
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     strcpy(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 static int dir_cache_size = 0;
628 static struct dir_cache {
629   struct dir_cache *next;
630   struct dir_cache *prev;
631   char *path;
632   char *name;
633   char *dname;
634   int snum;
635 } *dir_cache = NULL;
636
637 /*******************************************************************
638 add an entry to the directory cache
639 ********************************************************************/
640 void DirCacheAdd(char *path,char *name,char *dname,int snum)
641 {
642   int count;
643   struct dir_cache *entry = (struct dir_cache *)malloc(sizeof(*entry));
644   if (!entry) return;
645   entry->path = strdup(path);
646   entry->name = strdup(name);
647   entry->dname = strdup(dname);
648   entry->snum = snum;
649   if (!entry->path || !entry->name || !entry->dname) return;
650
651   entry->next = dir_cache;
652   entry->prev = NULL;
653   if (entry->next) entry->next->prev = entry;
654   dir_cache = entry;
655
656   DEBUG(4,("Added dir cache entry %s %s -> %s\n",path,name,dname));
657   
658   if (dir_cache_size == DIRCACHESIZE) {
659     for (entry=dir_cache, count=1; 
660          entry->next && count < dir_cache_size + 1; 
661          entry=entry->next, count++) ;
662     if (entry->next || count != dir_cache_size + 1) {
663       DEBUG(0,("DirCache bug - please report %d %d\n",dir_cache_size,count));
664     }
665     free(entry->path);
666     free(entry->name);
667     free(entry->dname);
668     if (entry->prev) entry->prev->next = entry->next;
669     free(entry);
670   } else {
671     dir_cache_size++;
672   }
673 }
674
675
676 /*******************************************************************
677 check for an entry in the directory cache
678 ********************************************************************/
679 char *DirCacheCheck(char *path,char *name,int snum)
680 {
681   struct dir_cache *entry;
682
683   for (entry=dir_cache; entry; entry=entry->next) {
684     if (entry->snum == snum &&
685         strcmp(path,entry->path) == 0 &&
686         strcmp(name,entry->name) == 0) {
687       DEBUG(4,("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
688       return(entry->dname);
689     }
690   }
691
692   return(NULL);
693 }
694
695 /*******************************************************************
696 flush entries in the dir_cache
697 ********************************************************************/
698 void DirCacheFlush(int snum)
699 {
700   struct dir_cache *entry,*next;
701
702   for (entry=dir_cache; entry; entry=next) {
703     if (entry->snum == snum) {
704       free(entry->path);
705       free(entry->dname);
706       free(entry->name);
707       next = entry->next;
708       if (entry->prev) entry->prev->next = entry->next;
709       if (entry->next) entry->next->prev = entry->prev;
710       if (dir_cache == entry) dir_cache = entry->next; 
711       free(entry);
712       dir_cache_size--;
713     } else {
714       next = entry->next;
715     }
716   }
717 }
718
719
720 #ifdef REPLACE_GETWD
721 /* This is getcwd.c from bash.  It is needed in Interactive UNIX.  To
722  * add support for another OS you need to determine which of the
723  * conditional compilation macros you need to define.  All the options
724  * are defined for Interactive UNIX.
725  */
726 #ifdef ISC
727 #define HAVE_UNISTD_H
728 #define USGr3
729 #define USG
730 #endif
731
732 #if defined (HAVE_UNISTD_H)
733 #  include <unistd.h>
734 #endif
735
736 #if defined (__STDC__)
737 #  define CONST const
738 #  define PTR void *
739 #else /* !__STDC__ */
740 #  define CONST
741 #  define PTR char *
742 #endif /* !__STDC__ */
743
744 #if !defined (PATH_MAX)
745 #  if defined (MAXPATHLEN)
746 #    define PATH_MAX MAXPATHLEN
747 #  else /* !MAXPATHLEN */
748 #    define PATH_MAX 1024
749 #  endif /* !MAXPATHLEN */
750 #endif /* !PATH_MAX */
751
752 #if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H)
753 #  if !defined (HAVE_DIRENT)
754 #    define HAVE_DIRENT
755 #  endif /* !HAVE_DIRENT */
756 #endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */
757
758 #if defined (HAVE_DIRENT)
759 #  define D_NAMLEN(d)   (strlen ((d)->d_name))
760 #else
761 #  define D_NAMLEN(d)   ((d)->d_namlen)
762 #endif /* ! (_POSIX_VERSION || USGr3) */
763
764 #if defined (USG) || defined (USGr3)
765 #  define d_fileno d_ino
766 #endif
767
768 #if !defined (alloca)
769 extern char *alloca ();
770 #endif /* alloca */
771
772 /* Get the pathname of the current working directory,
773    and put it in SIZE bytes of BUF.  Returns NULL if the
774    directory couldn't be determined or SIZE was too small.
775    If successful, returns BUF.  In GNU, if BUF is NULL,
776    an array is allocated with `malloc'; the array is SIZE
777    bytes long, unless SIZE <= 0, in which case it is as
778    big as necessary.  */
779 #if defined (__STDC__)
780 char *
781 getcwd (char *buf, size_t size)
782 #else /* !__STDC__ */
783 char *
784 getcwd (buf, size)
785      char *buf;
786      int size;
787 #endif /* !__STDC__ */
788 {
789   static CONST char dots[]
790     = "../../../../../../../../../../../../../../../../../../../../../../../\
791 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
792 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
793   CONST char *dotp, *dotlist;
794   size_t dotsize;
795   dev_t rootdev, thisdev;
796   ino_t rootino, thisino;
797   char path[PATH_MAX + 1];
798   register char *pathp;
799   char *pathbuf;
800   size_t pathsize;
801   struct stat st;
802
803   if (buf != NULL && size == 0)
804     {
805       errno = EINVAL;
806       return ((char *)NULL);
807     }
808
809   pathsize = sizeof (path);
810   pathp = &path[pathsize];
811   *--pathp = '\0';
812   pathbuf = path;
813
814   if (stat (".", &st) < 0)
815     return ((char *)NULL);
816   thisdev = st.st_dev;
817   thisino = st.st_ino;
818
819   if (stat ("/", &st) < 0)
820     return ((char *)NULL);
821   rootdev = st.st_dev;
822   rootino = st.st_ino;
823
824   dotsize = sizeof (dots) - 1;
825   dotp = &dots[sizeof (dots)];
826   dotlist = dots;
827   while (!(thisdev == rootdev && thisino == rootino))
828     {
829       register DIR *dirstream;
830       register struct dirent *d;
831       dev_t dotdev;
832       ino_t dotino;
833       char mount_point;
834       int namlen;
835
836       /* Look at the parent directory.  */
837       if (dotp == dotlist)
838         {
839           /* My, what a deep directory tree you have, Grandma.  */
840           char *new;
841           if (dotlist == dots)
842             {
843               new = malloc (dotsize * 2 + 1);
844               if (new == NULL)
845                 goto lose;
846               memcpy (new, dots, dotsize);
847             }
848           else
849             {
850               new = realloc ((PTR) dotlist, dotsize * 2 + 1);
851               if (new == NULL)
852                 goto lose;
853             }
854           memcpy (&new[dotsize], new, dotsize);
855           dotp = &new[dotsize];
856           dotsize *= 2;
857           new[dotsize] = '\0';
858           dotlist = new;
859         }
860
861       dotp -= 3;
862
863       /* Figure out if this directory is a mount point.  */
864       if (stat (dotp, &st) < 0)
865         goto lose;
866       dotdev = st.st_dev;
867       dotino = st.st_ino;
868       mount_point = dotdev != thisdev;
869
870       /* Search for the last directory.  */
871       dirstream = opendir(dotp);
872       if (dirstream == NULL)
873         goto lose;
874       while ((d = (struct dirent *)readdir(dirstream)) != NULL)
875         {
876           if (d->d_name[0] == '.' &&
877               (d->d_name[1] == '\0' ||
878                 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
879             continue;
880           if (mount_point || d->d_fileno == thisino)
881             {
882               char *name;
883
884               namlen = D_NAMLEN(d);
885               name = (char *)
886                 alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
887               memcpy (name, dotp, dotlist + dotsize - dotp);
888               name[dotlist + dotsize - dotp] = '/';
889               memcpy (&name[dotlist + dotsize - dotp + 1],
890                       d->d_name, namlen + 1);
891               if (lstat (name, &st) < 0)
892                 {
893                   int save = errno;
894                   closedir(dirstream);
895                   errno = save;
896                   goto lose;
897                 }
898               if (st.st_dev == thisdev && st.st_ino == thisino)
899                 break;
900             }
901         }
902       if (d == NULL)
903         {
904           int save = errno;
905           closedir(dirstream);
906           errno = save;
907           goto lose;
908         }
909       else
910         {
911           size_t space;
912
913           while ((space = pathp - pathbuf) <= namlen)
914             {
915               char *new;
916
917               if (pathbuf == path)
918                 {
919                   new = malloc (pathsize * 2);
920                   if (!new)
921                     goto lose;
922                 }
923               else
924                 {
925                   new = realloc ((PTR) pathbuf, (pathsize * 2));
926                   if (!new)
927                     goto lose;
928                   pathp = new + space;
929                 }
930               (void) memcpy (new + pathsize + space, pathp, pathsize - space);
931               pathp = new + pathsize + space;
932               pathbuf = new;
933               pathsize *= 2;
934             }
935
936           pathp -= namlen;
937           (void) memcpy (pathp, d->d_name, namlen);
938           *--pathp = '/';
939           closedir(dirstream);
940         }
941
942       thisdev = dotdev;
943       thisino = dotino;
944     }
945
946   if (pathp == &path[sizeof(path) - 1])
947     *--pathp = '/';
948
949   if (dotlist != dots)
950     free ((PTR) dotlist);
951
952   {
953     size_t len = pathbuf + pathsize - pathp;
954     if (buf == NULL)
955       {
956         if (len < (size_t) size)
957           len = size;
958         buf = (char *) malloc (len);
959         if (buf == NULL)
960           goto lose2;
961       }
962     else if ((size_t) size < len)
963       {
964         errno = ERANGE;
965         goto lose2;
966       }
967     (void) memcpy((PTR) buf, (PTR) pathp, len);
968   }
969
970   if (pathbuf != path)
971     free (pathbuf);
972
973   return (buf);
974
975  lose:
976   if ((dotlist != dots) && dotlist)
977     {
978       int e = errno;
979       free ((PTR) dotlist);
980       errno = e;
981     }
982
983  lose2:
984   if ((pathbuf != path) && pathbuf)
985     {
986       int e = errno;
987       free ((PTR) pathbuf);
988       errno = e;
989     }
990   return ((char *)NULL);
991 }
992 #endif