summaryrefslogtreecommitdiff
path: root/source/smbd/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/smbd/dir.c')
-rw-r--r--source/smbd/dir.c955
1 files changed, 955 insertions, 0 deletions
diff --git a/source/smbd/dir.c b/source/smbd/dir.c
new file mode 100644
index 00000000000..ac6f918b9da
--- /dev/null
+++ b/source/smbd/dir.c
@@ -0,0 +1,955 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Directory handling routines
+ Copyright (C) Andrew Tridgell 1992-1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+
+extern int DEBUGLEVEL;
+extern connection_struct Connections[];
+
+/*
+ This module implements directory related functions for Samba.
+*/
+
+
+
+uint32 dircounter = 0;
+
+
+#define NUMDIRPTRS 256
+
+
+static struct dptr_struct
+{
+ int pid;
+ int cnum;
+ uint32 lastused;
+ void *ptr;
+ BOOL valid;
+ BOOL finished;
+ BOOL expect_close;
+ char *wcard; /* Field only used for lanman2 trans2_findfirst/next searches */
+ uint16 attr; /* Field only used for lanman2 trans2_findfirst/next searches */
+ char *path;
+}
+dirptrs[NUMDIRPTRS];
+
+
+static int dptrs_open = 0;
+
+/****************************************************************************
+initialise the dir array
+****************************************************************************/
+void init_dptrs(void)
+{
+ static BOOL dptrs_init=False;
+ int i;
+
+ if (dptrs_init) return;
+ for (i=0;i<NUMDIRPTRS;i++)
+ {
+ dirptrs[i].valid = False;
+ dirptrs[i].wcard = NULL;
+ dirptrs[i].ptr = NULL;
+ string_init(&dirptrs[i].path,"");
+ }
+ dptrs_init = True;
+}
+
+/****************************************************************************
+idle a dptr - the directory is closed but the control info is kept
+****************************************************************************/
+static void dptr_idle(int key)
+{
+ if (dirptrs[key].valid && dirptrs[key].ptr) {
+ DEBUG(4,("Idling dptr key %d\n",key));
+ dptrs_open--;
+ CloseDir(dirptrs[key].ptr);
+ dirptrs[key].ptr = NULL;
+ }
+}
+
+/****************************************************************************
+idle the oldest dptr
+****************************************************************************/
+static void dptr_idleoldest(void)
+{
+ int i;
+ uint32 old=dircounter+1;
+ int oldi= -1;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (dirptrs[i].valid && dirptrs[i].ptr && dirptrs[i].lastused < old) {
+ old = dirptrs[i].lastused;
+ oldi = i;
+ }
+ if (oldi != -1)
+ dptr_idle(oldi);
+ else
+ DEBUG(0,("No dptrs available to idle??\n"));
+}
+
+/****************************************************************************
+get the dir ptr for a dir index
+****************************************************************************/
+static void *dptr_get(int key,uint32 lastused)
+{
+ if (dirptrs[key].valid) {
+ if (lastused) dirptrs[key].lastused = lastused;
+ if (!dirptrs[key].ptr) {
+ if (dptrs_open >= MAXDIR)
+ dptr_idleoldest();
+ DEBUG(4,("Reopening dptr key %d\n",key));
+ if ((dirptrs[key].ptr = OpenDir(dirptrs[key].path)))
+ dptrs_open++;
+ }
+ return(dirptrs[key].ptr);
+ }
+ return(NULL);
+}
+
+/****************************************************************************
+get the dir path for a dir index
+****************************************************************************/
+char *dptr_path(int key)
+{
+ if (dirptrs[key].valid)
+ return(dirptrs[key].path);
+ return(NULL);
+}
+
+/****************************************************************************
+get the dir wcard for a dir index (lanman2 specific)
+****************************************************************************/
+char *dptr_wcard(int key)
+{
+ if (dirptrs[key].valid)
+ return(dirptrs[key].wcard);
+ return(NULL);
+}
+
+/****************************************************************************
+set the dir wcard for a dir index (lanman2 specific)
+Returns 0 on ok, 1 on fail.
+****************************************************************************/
+BOOL dptr_set_wcard(int key, char *wcard)
+{
+ if (dirptrs[key].valid) {
+ dirptrs[key].wcard = wcard;
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+set the dir attrib for a dir index (lanman2 specific)
+Returns 0 on ok, 1 on fail.
+****************************************************************************/
+BOOL dptr_set_attr(int key, uint16 attr)
+{
+ if (dirptrs[key].valid) {
+ dirptrs[key].attr = attr;
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+get the dir attrib for a dir index (lanman2 specific)
+****************************************************************************/
+uint16 dptr_attr(int key)
+{
+ if (dirptrs[key].valid)
+ return(dirptrs[key].attr);
+ return(0);
+}
+
+/****************************************************************************
+close a dptr
+****************************************************************************/
+void dptr_close(int key)
+{
+ if (dirptrs[key].valid) {
+ DEBUG(4,("closing dptr key %d\n",key));
+ if (dirptrs[key].ptr) {
+ CloseDir(dirptrs[key].ptr);
+ dptrs_open--;
+ }
+ /* Lanman 2 specific code */
+ if (dirptrs[key].wcard)
+ free(dirptrs[key].wcard);
+ dirptrs[key].valid = False;
+ string_set(&dirptrs[key].path,"");
+ }
+}
+
+/****************************************************************************
+close all dptrs for a cnum
+****************************************************************************/
+void dptr_closecnum(int cnum)
+{
+ int i;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (dirptrs[i].valid && dirptrs[i].cnum == cnum)
+ dptr_close(i);
+}
+
+/****************************************************************************
+idle all dptrs for a cnum
+****************************************************************************/
+void dptr_idlecnum(int cnum)
+{
+ int i;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (dirptrs[i].valid && dirptrs[i].cnum == cnum && dirptrs[i].ptr)
+ dptr_idle(i);
+}
+
+/****************************************************************************
+close a dptr that matches a given path, only if it matches the pid also
+****************************************************************************/
+void dptr_closepath(char *path,int pid)
+{
+ int i;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (dirptrs[i].valid && pid == dirptrs[i].pid &&
+ strequal(dirptrs[i].path,path))
+ dptr_close(i);
+}
+
+/****************************************************************************
+ start a directory listing
+****************************************************************************/
+static BOOL start_dir(int cnum,char *directory)
+{
+ DEBUG(5,("start_dir cnum=%d dir=%s\n",cnum,directory));
+
+ if (!check_name(directory,cnum))
+ return(False);
+
+ if (! *directory)
+ directory = ".";
+
+ Connections[cnum].dirptr = OpenDir(directory);
+ if (Connections[cnum].dirptr) {
+ dptrs_open++;
+ string_set(&Connections[cnum].dirpath,directory);
+ return(True);
+ }
+
+ return(False);
+}
+
+
+/****************************************************************************
+create a new dir ptr
+****************************************************************************/
+int dptr_create(int cnum,char *path, BOOL expect_close,int pid)
+{
+ int i;
+ uint32 old;
+ int oldi;
+
+ if (!start_dir(cnum,path))
+ return(-1);
+
+ if (dptrs_open >= MAXDIR)
+ dptr_idleoldest();
+
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (!dirptrs[i].valid)
+ break;
+ if (i == NUMDIRPTRS) i = -1;
+
+
+ /* as a 2nd option, grab the oldest not marked for expect_close */
+ if (i == -1) {
+ old=dircounter+1;
+ oldi= -1;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) {
+ old = dirptrs[i].lastused;
+ oldi = i;
+ }
+ i = oldi;
+ }
+
+ /* a 3rd option - grab the oldest one */
+ if (i == -1) {
+ old=dircounter+1;
+ oldi= -1;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (dirptrs[i].lastused < old) {
+ old = dirptrs[i].lastused;
+ oldi = i;
+ }
+ i = oldi;
+ }
+
+ if (i == -1) {
+ DEBUG(0,("Error - all dirptrs in use??\n"));
+ return(-1);
+ }
+
+ if (dirptrs[i].valid)
+ dptr_close(i);
+
+ dirptrs[i].ptr = Connections[cnum].dirptr;
+ string_set(&dirptrs[i].path,path);
+ dirptrs[i].lastused = dircounter++;
+ dirptrs[i].finished = False;
+ dirptrs[i].cnum = cnum;
+ dirptrs[i].pid = pid;
+ dirptrs[i].expect_close = expect_close;
+ dirptrs[i].wcard = NULL; /* Only used in lanman2 searches */
+ dirptrs[i].attr = 0; /* Only used in lanman2 searches */
+ dirptrs[i].valid = True;
+
+ DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
+ i,path,expect_close));
+
+ return(i);
+}
+
+#define DPTR_MASK ((uint32)(((uint32)1)<<31))
+
+/****************************************************************************
+fill the 5 byte server reserved dptr field
+****************************************************************************/
+BOOL dptr_fill(char *buf1,unsigned int key)
+{
+ unsigned char *buf = (unsigned char *)buf1;
+ void *p = dptr_get(key,0);
+ uint32 offset;
+ if (!p) {
+ DEBUG(1,("filling null dirptr %d\n",key));
+ return(False);
+ }
+ offset = TellDir(p);
+ DEBUG(6,("fill on key %d dirptr 0x%x now at %d\n",key,p,offset));
+ buf[0] = key;
+ SIVAL(buf,1,offset | DPTR_MASK);
+ return(True);
+}
+
+
+/****************************************************************************
+return True is the offset is at zero
+****************************************************************************/
+BOOL dptr_zero(char *buf)
+{
+ return((IVAL(buf,1)&~DPTR_MASK) == 0);
+}
+
+/****************************************************************************
+fetch the dir ptr and seek it given the 5 byte server field
+****************************************************************************/
+void *dptr_fetch(char *buf,int *num)
+{
+ unsigned int key = *(unsigned char *)buf;
+ void *p = dptr_get(key,dircounter++);
+ uint32 offset;
+ if (!p) {
+ DEBUG(3,("fetched null dirptr %d\n",key));
+ return(NULL);
+ }
+ *num = key;
+ offset = IVAL(buf,1)&~DPTR_MASK;
+ SeekDir(p,offset);
+ DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
+ key,dptr_path(key),offset));
+ return(p);
+}
+
+/****************************************************************************
+fetch the dir ptr and seek it given the lanman2 parameter block
+****************************************************************************/
+void *dptr_fetch_lanman2(char *params,int dptr_num)
+{
+ void *p = dptr_get(dptr_num,dircounter++);
+ uint32 resume_key = SVAL(params,6);
+ BOOL uses_resume_key = BITSETW(params+10,2);
+ BOOL continue_bit = BITSETW(params+10,3);
+
+ if (!p) {
+ DEBUG(3,("fetched null dirptr %d\n",dptr_num));
+ return(NULL);
+ }
+ if(uses_resume_key && !continue_bit)
+ SeekDir(p,resume_key);
+ DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
+ return(p);
+}
+
+/****************************************************************************
+ get a directory entry
+****************************************************************************/
+BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend)
+{
+ char *dname;
+ BOOL found = False;
+ struct stat sbuf;
+ pstring path;
+ pstring pathreal;
+ BOOL isrootdir;
+ pstring filename;
+ BOOL matched;
+
+ *path = *pathreal = *filename = 0;
+
+ isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
+ strequal(Connections[cnum].dirpath,".") ||
+ strequal(Connections[cnum].dirpath,"/"));
+
+ if (!Connections[cnum].dirptr)
+ return(False);
+
+ while (!found)
+ {
+ dname = ReadDirName(Connections[cnum].dirptr);
+
+ DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n",
+ Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
+
+ if (dname == NULL)
+ return(False);
+
+ matched = False;
+
+ strcpy(filename,dname);
+
+ if ((strcmp(filename,mask) == 0) ||
+ (name_map_mangle(filename,True,SNUM(cnum)) &&
+ mask_match(filename,mask,False,False)))
+ {
+ if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
+ continue;
+
+ strcpy(fname,filename);
+ *path = 0;
+ strcpy(path,Connections[cnum].dirpath);
+ strcat(path,"/");
+ strcpy(pathreal,path);
+ strcat(path,fname);
+ strcat(pathreal,dname);
+ if (sys_stat(pathreal,&sbuf) != 0)
+ {
+ DEBUG(5,("Couldn't stat 1 [%s]\n",path));
+ continue;
+ }
+
+ if (check_descend &&
+ !strequal(fname,".") && !strequal(fname,".."))
+ continue;
+
+ *mode = dos_mode(cnum,pathreal,&sbuf);
+
+ if (((*mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
+ {
+ DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
+ continue;
+ }
+ *size = sbuf.st_size;
+ *date = sbuf.st_mtime;
+
+ DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname));
+
+ found = True;
+ }
+ }
+
+ return(found);
+}
+
+
+
+typedef struct
+{
+ int pos;
+ int numentries;
+ int mallocsize;
+ char *data;
+ char *current;
+} Dir;
+
+
+/*******************************************************************
+open a directory
+********************************************************************/
+void *OpenDir(char *name)
+{
+ Dir *dirp;
+ char *n;
+ void *p = sys_opendir(name);
+ int used=0;
+
+ if (!p) return(NULL);
+ dirp = (Dir *)malloc(sizeof(Dir));
+ if (!dirp) {
+ closedir(p);
+ return(NULL);
+ }
+ dirp->pos = dirp->numentries = dirp->mallocsize = 0;
+ dirp->data = dirp->current = NULL;
+
+ while ((n = readdirname(p))) {
+ int l = strlen(n)+1;
+ if (used + l > dirp->mallocsize) {
+ int s = MAX(used+l,used+2000);
+ char *r;
+ r = (char *)Realloc(dirp->data,s);
+ if (!r) {
+ DEBUG(0,("Out of memory in OpenDir\n"));
+ break;
+ }
+ dirp->data = r;
+ dirp->mallocsize = s;
+ dirp->current = dirp->data;
+ }
+ strcpy(dirp->data+used,n);
+ used += l;
+ dirp->numentries++;
+ }
+
+ closedir(p);
+ return((void *)dirp);
+}
+
+
+/*******************************************************************
+close a directory
+********************************************************************/
+void CloseDir(void *p)
+{
+ Dir *dirp = (Dir *)p;
+ if (!dirp) return;
+ if (dirp->data) free(dirp->data);
+ free(dirp);
+}
+
+/*******************************************************************
+read from a directory
+********************************************************************/
+char *ReadDirName(void *p)
+{
+ char *ret;
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
+
+ ret = dirp->current;
+ dirp->current = skip_string(dirp->current,1);
+ dirp->pos++;
+
+ return(ret);
+}
+
+
+/*******************************************************************
+seek a dir
+********************************************************************/
+BOOL SeekDir(void *p,int pos)
+{
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp) return(False);
+
+ if (pos < dirp->pos) {
+ dirp->current = dirp->data;
+ dirp->pos = 0;
+ }
+
+ while (dirp->pos < pos && ReadDirName(p)) ;
+
+ return(dirp->pos == pos);
+}
+
+/*******************************************************************
+tell a dir position
+********************************************************************/
+int TellDir(void *p)
+{
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp) return(-1);
+
+ return(dirp->pos);
+}
+
+
+static int dir_cache_size = 0;
+static struct dir_cache {
+ struct dir_cache *next;
+ struct dir_cache *prev;
+ char *path;
+ char *name;
+ char *dname;
+ int snum;
+} *dir_cache = NULL;
+
+/*******************************************************************
+add an entry to the directory cache
+********************************************************************/
+void DirCacheAdd(char *path,char *name,char *dname,int snum)
+{
+ struct dir_cache *entry = (struct dir_cache *)malloc(sizeof(*entry));
+ if (!entry) return;
+ entry->path = strdup(path);
+ entry->name = strdup(name);
+ entry->dname = strdup(dname);
+ entry->snum = snum;
+ if (!entry->path || !entry->name || !entry->dname) return;
+
+ entry->next = dir_cache;
+ entry->prev = NULL;
+ if (entry->next) entry->next->prev = entry;
+ dir_cache = entry;
+
+ DEBUG(4,("Added dir cache entry %s %s -> %s\n",path,name,dname));
+
+ if (dir_cache_size == DIRCACHESIZE) {
+ for (entry=dir_cache; entry->next; entry=entry->next) ;
+ free(entry->path);
+ free(entry->name);
+ free(entry->dname);
+ if (entry->prev) entry->prev->next = entry->next;
+ free(entry);
+ } else {
+ dir_cache_size++;
+ }
+}
+
+
+/*******************************************************************
+check for an entry in the directory cache
+********************************************************************/
+char *DirCacheCheck(char *path,char *name,int snum)
+{
+ struct dir_cache *entry;
+
+ for (entry=dir_cache; entry; entry=entry->next) {
+ if (entry->snum == snum &&
+ strcmp(path,entry->path) == 0 &&
+ strcmp(name,entry->name) == 0) {
+ DEBUG(4,("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
+ return(entry->dname);
+ }
+ }
+
+ return(NULL);
+}
+
+/*******************************************************************
+flush entries in the dir_cache
+********************************************************************/
+void DirCacheFlush(int snum)
+{
+ struct dir_cache *entry,*next;
+
+ for (entry=dir_cache; entry; entry=next) {
+ if (entry->snum == snum) {
+ free(entry->path);
+ free(entry->dname);
+ free(entry->name);
+ next = entry->next;
+ if (entry->prev) entry->prev->next = entry->next;
+ if (entry->next) entry->next->prev = entry->prev;
+ if (dir_cache == entry) dir_cache = entry->next;
+ free(entry);
+ } else {
+ next = entry->next;
+ }
+ }
+}
+
+
+#ifdef REPLACE_GETWD
+/* This is getcwd.c from bash. It is needed in Interactive UNIX. To
+ * add support for another OS you need to determine which of the
+ * conditional compilation macros you need to define. All the options
+ * are defined for Interactive UNIX.
+ */
+#ifdef ISC
+#define HAVE_UNISTD_H
+#define USGr3
+#define USG
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#if defined (__STDC__)
+# define CONST const
+# define PTR void *
+#else /* !__STDC__ */
+# define CONST
+# define PTR char *
+#endif /* !__STDC__ */
+
+#if !defined (PATH_MAX)
+# if defined (MAXPATHLEN)
+# define PATH_MAX MAXPATHLEN
+# else /* !MAXPATHLEN */
+# define PATH_MAX 1024
+# endif /* !MAXPATHLEN */
+#endif /* !PATH_MAX */
+
+#if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H)
+# if !defined (HAVE_DIRENT)
+# define HAVE_DIRENT
+# endif /* !HAVE_DIRENT */
+#endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */
+
+#if defined (HAVE_DIRENT)
+# define D_NAMLEN(d) (strlen ((d)->d_name))
+#else
+# define D_NAMLEN(d) ((d)->d_namlen)
+#endif /* ! (_POSIX_VERSION || USGr3) */
+
+#if defined (USG) || defined (USGr3)
+# define d_fileno d_ino
+#endif
+
+#if !defined (alloca)
+extern char *alloca ();
+#endif /* alloca */
+
+/* Get the pathname of the current working directory,
+ and put it in SIZE bytes of BUF. Returns NULL if the
+ directory couldn't be determined or SIZE was too small.
+ If successful, returns BUF. In GNU, if BUF is NULL,
+ an array is allocated with `malloc'; the array is SIZE
+ bytes long, unless SIZE <= 0, in which case it is as
+ big as necessary. */
+#if defined (__STDC__)
+char *
+getcwd (char *buf, size_t size)
+#else /* !__STDC__ */
+char *
+getcwd (buf, size)
+ char *buf;
+ int size;
+#endif /* !__STDC__ */
+{
+ static CONST char dots[]
+ = "../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../..";
+ CONST char *dotp, *dotlist;
+ size_t dotsize;
+ dev_t rootdev, thisdev;
+ ino_t rootino, thisino;
+ char path[PATH_MAX + 1];
+ register char *pathp;
+ char *pathbuf;
+ size_t pathsize;
+ struct stat st;
+
+ if (buf != NULL && size == 0)
+ {
+ errno = EINVAL;
+ return ((char *)NULL);
+ }
+
+ pathsize = sizeof (path);
+ pathp = &path[pathsize];
+ *--pathp = '\0';
+ pathbuf = path;
+
+ if (stat (".", &st) < 0)
+ return ((char *)NULL);
+ thisdev = st.st_dev;
+ thisino = st.st_ino;
+
+ if (stat ("/", &st) < 0)
+ return ((char *)NULL);
+ rootdev = st.st_dev;
+ rootino = st.st_ino;
+
+ dotsize = sizeof (dots) - 1;
+ dotp = &dots[sizeof (dots)];
+ dotlist = dots;
+ while (!(thisdev == rootdev && thisino == rootino))
+ {
+ register DIR *dirstream;
+ register struct dirent *d;
+ dev_t dotdev;
+ ino_t dotino;
+ char mount_point;
+ int namlen;
+
+ /* Look at the parent directory. */
+ if (dotp == dotlist)
+ {
+ /* My, what a deep directory tree you have, Grandma. */
+ char *new;
+ if (dotlist == dots)
+ {
+ new = malloc (dotsize * 2 + 1);
+ if (new == NULL)
+ goto lose;
+ memcpy (new, dots, dotsize);
+ }
+ else
+ {
+ new = realloc ((PTR) dotlist, dotsize * 2 + 1);
+ if (new == NULL)
+ goto lose;
+ }
+ memcpy (&new[dotsize], new, dotsize);
+ dotp = &new[dotsize];
+ dotsize *= 2;
+ new[dotsize] = '\0';
+ dotlist = new;
+ }
+
+ dotp -= 3;
+
+ /* Figure out if this directory is a mount point. */
+ if (stat (dotp, &st) < 0)
+ goto lose;
+ dotdev = st.st_dev;
+ dotino = st.st_ino;
+ mount_point = dotdev != thisdev;
+
+ /* Search for the last directory. */
+ dirstream = opendir(dotp);
+ if (dirstream == NULL)
+ goto lose;
+ while ((d = (struct dirent *)readdir(dirstream)) != NULL)
+ {
+ if (d->d_name[0] == '.' &&
+ (d->d_name[1] == '\0' ||
+ (d->d_name[1] == '.' && d->d_name[2] == '\0')))
+ continue;
+ if (mount_point || d->d_fileno == thisino)
+ {
+ char *name;
+
+ namlen = D_NAMLEN(d);
+ name = (char *)
+ alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
+ memcpy (name, dotp, dotlist + dotsize - dotp);
+ name[dotlist + dotsize - dotp] = '/';
+ memcpy (&name[dotlist + dotsize - dotp + 1],
+ d->d_name, namlen + 1);
+ if (lstat (name, &st) < 0)
+ {
+ int save = errno;
+ closedir(dirstream);
+ errno = save;
+ goto lose;
+ }
+ if (st.st_dev == thisdev && st.st_ino == thisino)
+ break;
+ }
+ }
+ if (d == NULL)
+ {
+ int save = errno;
+ closedir(dirstream);
+ errno = save;
+ goto lose;
+ }
+ else
+ {
+ size_t space;
+
+ while ((space = pathp - pathbuf) <= namlen)
+ {
+ char *new;
+
+ if (pathbuf == path)
+ {
+ new = malloc (pathsize * 2);
+ if (!new)
+ goto lose;
+ }
+ else
+ {
+ new = realloc ((PTR) pathbuf, (pathsize * 2));
+ if (!new)
+ goto lose;
+ pathp = new + space;
+ }
+ (void) memcpy (new + pathsize + space, pathp, pathsize - space);
+ pathp = new + pathsize + space;
+ pathbuf = new;
+ pathsize *= 2;
+ }
+
+ pathp -= namlen;
+ (void) memcpy (pathp, d->d_name, namlen);
+ *--pathp = '/';
+ closedir(dirstream);
+ }
+
+ thisdev = dotdev;
+ thisino = dotino;
+ }
+
+ if (pathp == &path[sizeof(path) - 1])
+ *--pathp = '/';
+
+ if (dotlist != dots)
+ free ((PTR) dotlist);
+
+ {
+ size_t len = pathbuf + pathsize - pathp;
+ if (buf == NULL)
+ {
+ if (len < (size_t) size)
+ len = size;
+ buf = (char *) malloc (len);
+ if (buf == NULL)
+ goto lose2;
+ }
+ else if ((size_t) size < len)
+ {
+ errno = ERANGE;
+ goto lose2;
+ }
+ (void) memcpy((PTR) buf, (PTR) pathp, len);
+ }
+
+ if (pathbuf != path)
+ free (pathbuf);
+
+ return (buf);
+
+ lose:
+ if ((dotlist != dots) && dotlist)
+ {
+ int e = errno;
+ free ((PTR) dotlist);
+ errno = e;
+ }
+
+ lose2:
+ if ((pathbuf != path) && pathbuf)
+ {
+ int e = errno;
+ free ((PTR) pathbuf);
+ errno = e;
+ }
+ return ((char *)NULL);
+}
+#endif