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