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