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