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