73c4afd77e67c5914044a190982fd8a445bab7c3
[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
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
437   *path = *pathreal = *filename = 0;
438
439   isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
440                strequal(Connections[cnum].dirpath,".") ||
441                strequal(Connections[cnum].dirpath,"/"));
442   
443   if (!Connections[cnum].dirptr)
444     return(False);
445   
446   while (!found)
447     {
448       dname = ReadDirName(Connections[cnum].dirptr);
449
450       DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n",
451             Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
452       
453       if (dname == NULL) 
454         return(False);
455       
456       matched = False;
457
458       strcpy(filename,dname);      
459
460       if ((strcmp(filename,mask) == 0) ||
461           (name_map_mangle(filename,True,SNUM(cnum)) &&
462            mask_match(filename,mask,False,False)))
463         {
464           if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
465             continue;
466
467           strcpy(fname,filename);
468           *path = 0;
469           strcpy(path,Connections[cnum].dirpath);
470           strcat(path,"/");
471           strcpy(pathreal,path);
472           strcat(path,fname);
473           strcat(pathreal,dname);
474           if (sys_stat(pathreal,&sbuf) != 0) 
475             {
476               DEBUG(5,("Couldn't stat 1 [%s]\n",path));
477               continue;
478             }
479
480           if (check_descend &&
481               !strequal(fname,".") && !strequal(fname,".."))
482             continue;
483           
484           *mode = dos_mode(cnum,pathreal,&sbuf);
485
486           if (!dir_check_ftype(cnum,*mode,&sbuf,dirtype)) {
487             DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
488             continue;
489           }
490
491           *size = sbuf.st_size;
492           *date = sbuf.st_mtime;
493
494           DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname));
495           
496           found = True;
497         }
498     }
499
500   return(found);
501 }
502
503
504
505 typedef struct
506 {
507   int pos;
508   int numentries;
509   int mallocsize;
510   char *data;
511   char *current;
512 } Dir;
513
514
515 /*******************************************************************
516 open a directory
517 ********************************************************************/
518 void *OpenDir(char *name)
519 {
520   Dir *dirp;
521   char *n;
522   void *p = sys_opendir(name);
523   int used=0;
524
525   if (!p) return(NULL);
526   dirp = (Dir *)malloc(sizeof(Dir));
527   if (!dirp) {
528     closedir(p);
529     return(NULL);
530   }
531   dirp->pos = dirp->numentries = dirp->mallocsize = 0;
532   dirp->data = dirp->current = NULL;
533
534   while ((n = readdirname(p))) {
535     int l = strlen(n)+1;
536     if (used + l > dirp->mallocsize) {
537       int s = MAX(used+l,used+2000);
538       char *r;
539       r = (char *)Realloc(dirp->data,s);
540       if (!r) {
541         DEBUG(0,("Out of memory in OpenDir\n"));
542         break;
543       }
544       dirp->data = r;
545       dirp->mallocsize = s;
546       dirp->current = dirp->data;
547     }
548     strcpy(dirp->data+used,n);
549     used += l;
550     dirp->numentries++;
551   }
552
553   closedir(p);
554   return((void *)dirp);
555 }
556
557
558 /*******************************************************************
559 close a directory
560 ********************************************************************/
561 void CloseDir(void *p)
562 {
563   Dir *dirp = (Dir *)p;
564   if (!dirp) return;    
565   if (dirp->data) free(dirp->data);
566   free(dirp);
567 }
568
569 /*******************************************************************
570 read from a directory
571 ********************************************************************/
572 char *ReadDirName(void *p)
573 {
574   char *ret;
575   Dir *dirp = (Dir *)p;
576
577   if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
578
579   ret = dirp->current;
580   dirp->current = skip_string(dirp->current,1);
581   dirp->pos++;
582
583   return(ret);
584 }
585
586
587 /*******************************************************************
588 seek a dir
589 ********************************************************************/
590 BOOL SeekDir(void *p,int pos)
591 {
592   Dir *dirp = (Dir *)p;
593
594   if (!dirp) return(False);
595
596   if (pos < dirp->pos) {
597     dirp->current = dirp->data;
598     dirp->pos = 0;
599   }
600
601   while (dirp->pos < pos && ReadDirName(p)) ;
602
603   return(dirp->pos == pos);
604 }
605
606 /*******************************************************************
607 tell a dir position
608 ********************************************************************/
609 int TellDir(void *p)
610 {
611   Dir *dirp = (Dir *)p;
612
613   if (!dirp) return(-1);
614   
615   return(dirp->pos);
616 }
617
618
619 static int dir_cache_size = 0;
620 static struct dir_cache {
621   struct dir_cache *next;
622   struct dir_cache *prev;
623   char *path;
624   char *name;
625   char *dname;
626   int snum;
627 } *dir_cache = NULL;
628
629 /*******************************************************************
630 add an entry to the directory cache
631 ********************************************************************/
632 void DirCacheAdd(char *path,char *name,char *dname,int snum)
633 {
634   int count;
635   struct dir_cache *entry = (struct dir_cache *)malloc(sizeof(*entry));
636   if (!entry) return;
637   entry->path = strdup(path);
638   entry->name = strdup(name);
639   entry->dname = strdup(dname);
640   entry->snum = snum;
641   if (!entry->path || !entry->name || !entry->dname) return;
642
643   entry->next = dir_cache;
644   entry->prev = NULL;
645   if (entry->next) entry->next->prev = entry;
646   dir_cache = entry;
647
648   DEBUG(4,("Added dir cache entry %s %s -> %s\n",path,name,dname));
649   
650   if (dir_cache_size == DIRCACHESIZE) {
651     for (entry=dir_cache, count=1; 
652          entry->next && count < dir_cache_size + 1; 
653          entry=entry->next, count++) ;
654     if (entry->next || count != dir_cache_size + 1) {
655       DEBUG(0,("DirCache bug - please report %d %d\n",dir_cache_size,count));
656     }
657     free(entry->path);
658     free(entry->name);
659     free(entry->dname);
660     if (entry->prev) entry->prev->next = entry->next;
661     free(entry);
662   } else {
663     dir_cache_size++;
664   }
665 }
666
667
668 /*******************************************************************
669 check for an entry in the directory cache
670 ********************************************************************/
671 char *DirCacheCheck(char *path,char *name,int snum)
672 {
673   struct dir_cache *entry;
674
675   for (entry=dir_cache; entry; entry=entry->next) {
676     if (entry->snum == snum &&
677         strcmp(path,entry->path) == 0 &&
678         strcmp(name,entry->name) == 0) {
679       DEBUG(4,("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
680       return(entry->dname);
681     }
682   }
683
684   return(NULL);
685 }
686
687 /*******************************************************************
688 flush entries in the dir_cache
689 ********************************************************************/
690 void DirCacheFlush(int snum)
691 {
692   struct dir_cache *entry,*next;
693
694   for (entry=dir_cache; entry; entry=next) {
695     if (entry->snum == snum) {
696       free(entry->path);
697       free(entry->dname);
698       free(entry->name);
699       next = entry->next;
700       if (entry->prev) entry->prev->next = entry->next;
701       if (entry->next) entry->next->prev = entry->prev;
702       if (dir_cache == entry) dir_cache = entry->next; 
703       free(entry);
704       dir_cache_size--;
705     } else {
706       next = entry->next;
707     }
708   }
709 }
710
711
712 #ifdef REPLACE_GETWD
713 /* This is getcwd.c from bash.  It is needed in Interactive UNIX.  To
714  * add support for another OS you need to determine which of the
715  * conditional compilation macros you need to define.  All the options
716  * are defined for Interactive UNIX.
717  */
718 #ifdef ISC
719 #define HAVE_UNISTD_H
720 #define USGr3
721 #define USG
722 #endif
723
724 #if defined (HAVE_UNISTD_H)
725 #  include <unistd.h>
726 #endif
727
728 #if defined (__STDC__)
729 #  define CONST const
730 #  define PTR void *
731 #else /* !__STDC__ */
732 #  define CONST
733 #  define PTR char *
734 #endif /* !__STDC__ */
735
736 #if !defined (PATH_MAX)
737 #  if defined (MAXPATHLEN)
738 #    define PATH_MAX MAXPATHLEN
739 #  else /* !MAXPATHLEN */
740 #    define PATH_MAX 1024
741 #  endif /* !MAXPATHLEN */
742 #endif /* !PATH_MAX */
743
744 #if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H)
745 #  if !defined (HAVE_DIRENT)
746 #    define HAVE_DIRENT
747 #  endif /* !HAVE_DIRENT */
748 #endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */
749
750 #if defined (HAVE_DIRENT)
751 #  define D_NAMLEN(d)   (strlen ((d)->d_name))
752 #else
753 #  define D_NAMLEN(d)   ((d)->d_namlen)
754 #endif /* ! (_POSIX_VERSION || USGr3) */
755
756 #if defined (USG) || defined (USGr3)
757 #  define d_fileno d_ino
758 #endif
759
760 #if !defined (alloca)
761 extern char *alloca ();
762 #endif /* alloca */
763
764 /* Get the pathname of the current working directory,
765    and put it in SIZE bytes of BUF.  Returns NULL if the
766    directory couldn't be determined or SIZE was too small.
767    If successful, returns BUF.  In GNU, if BUF is NULL,
768    an array is allocated with `malloc'; the array is SIZE
769    bytes long, unless SIZE <= 0, in which case it is as
770    big as necessary.  */
771 #if defined (__STDC__)
772 char *
773 getcwd (char *buf, size_t size)
774 #else /* !__STDC__ */
775 char *
776 getcwd (buf, size)
777      char *buf;
778      int size;
779 #endif /* !__STDC__ */
780 {
781   static CONST char dots[]
782     = "../../../../../../../../../../../../../../../../../../../../../../../\
783 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
784 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
785   CONST char *dotp, *dotlist;
786   size_t dotsize;
787   dev_t rootdev, thisdev;
788   ino_t rootino, thisino;
789   char path[PATH_MAX + 1];
790   register char *pathp;
791   char *pathbuf;
792   size_t pathsize;
793   struct stat st;
794
795   if (buf != NULL && size == 0)
796     {
797       errno = EINVAL;
798       return ((char *)NULL);
799     }
800
801   pathsize = sizeof (path);
802   pathp = &path[pathsize];
803   *--pathp = '\0';
804   pathbuf = path;
805
806   if (stat (".", &st) < 0)
807     return ((char *)NULL);
808   thisdev = st.st_dev;
809   thisino = st.st_ino;
810
811   if (stat ("/", &st) < 0)
812     return ((char *)NULL);
813   rootdev = st.st_dev;
814   rootino = st.st_ino;
815
816   dotsize = sizeof (dots) - 1;
817   dotp = &dots[sizeof (dots)];
818   dotlist = dots;
819   while (!(thisdev == rootdev && thisino == rootino))
820     {
821       register DIR *dirstream;
822       register struct dirent *d;
823       dev_t dotdev;
824       ino_t dotino;
825       char mount_point;
826       int namlen;
827
828       /* Look at the parent directory.  */
829       if (dotp == dotlist)
830         {
831           /* My, what a deep directory tree you have, Grandma.  */
832           char *new;
833           if (dotlist == dots)
834             {
835               new = malloc (dotsize * 2 + 1);
836               if (new == NULL)
837                 goto lose;
838               memcpy (new, dots, dotsize);
839             }
840           else
841             {
842               new = realloc ((PTR) dotlist, dotsize * 2 + 1);
843               if (new == NULL)
844                 goto lose;
845             }
846           memcpy (&new[dotsize], new, dotsize);
847           dotp = &new[dotsize];
848           dotsize *= 2;
849           new[dotsize] = '\0';
850           dotlist = new;
851         }
852
853       dotp -= 3;
854
855       /* Figure out if this directory is a mount point.  */
856       if (stat (dotp, &st) < 0)
857         goto lose;
858       dotdev = st.st_dev;
859       dotino = st.st_ino;
860       mount_point = dotdev != thisdev;
861
862       /* Search for the last directory.  */
863       dirstream = opendir(dotp);
864       if (dirstream == NULL)
865         goto lose;
866       while ((d = (struct dirent *)readdir(dirstream)) != NULL)
867         {
868           if (d->d_name[0] == '.' &&
869               (d->d_name[1] == '\0' ||
870                 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
871             continue;
872           if (mount_point || d->d_fileno == thisino)
873             {
874               char *name;
875
876               namlen = D_NAMLEN(d);
877               name = (char *)
878                 alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
879               memcpy (name, dotp, dotlist + dotsize - dotp);
880               name[dotlist + dotsize - dotp] = '/';
881               memcpy (&name[dotlist + dotsize - dotp + 1],
882                       d->d_name, namlen + 1);
883               if (lstat (name, &st) < 0)
884                 {
885                   int save = errno;
886                   closedir(dirstream);
887                   errno = save;
888                   goto lose;
889                 }
890               if (st.st_dev == thisdev && st.st_ino == thisino)
891                 break;
892             }
893         }
894       if (d == NULL)
895         {
896           int save = errno;
897           closedir(dirstream);
898           errno = save;
899           goto lose;
900         }
901       else
902         {
903           size_t space;
904
905           while ((space = pathp - pathbuf) <= namlen)
906             {
907               char *new;
908
909               if (pathbuf == path)
910                 {
911                   new = malloc (pathsize * 2);
912                   if (!new)
913                     goto lose;
914                 }
915               else
916                 {
917                   new = realloc ((PTR) pathbuf, (pathsize * 2));
918                   if (!new)
919                     goto lose;
920                   pathp = new + space;
921                 }
922               (void) memcpy (new + pathsize + space, pathp, pathsize - space);
923               pathp = new + pathsize + space;
924               pathbuf = new;
925               pathsize *= 2;
926             }
927
928           pathp -= namlen;
929           (void) memcpy (pathp, d->d_name, namlen);
930           *--pathp = '/';
931           closedir(dirstream);
932         }
933
934       thisdev = dotdev;
935       thisino = dotino;
936     }
937
938   if (pathp == &path[sizeof(path) - 1])
939     *--pathp = '/';
940
941   if (dotlist != dots)
942     free ((PTR) dotlist);
943
944   {
945     size_t len = pathbuf + pathsize - pathp;
946     if (buf == NULL)
947       {
948         if (len < (size_t) size)
949           len = size;
950         buf = (char *) malloc (len);
951         if (buf == NULL)
952           goto lose2;
953       }
954     else if ((size_t) size < len)
955       {
956         errno = ERANGE;
957         goto lose2;
958       }
959     (void) memcpy((PTR) buf, (PTR) pathp, len);
960   }
961
962   if (pathbuf != path)
963     free (pathbuf);
964
965   return (buf);
966
967  lose:
968   if ((dotlist != dots) && dotlist)
969     {
970       int e = errno;
971       free ((PTR) dotlist);
972       errno = e;
973     }
974
975  lose2:
976   if ((pathbuf != path) && pathbuf)
977     {
978       int e = errno;
979       free ((PTR) pathbuf);
980       errno = e;
981     }
982   return ((char *)NULL);
983 }
984 #endif