1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-25 23:21:54 +03:00
samba-mirror/source3/smbd/dir.c

995 lines
25 KiB
C
Raw Normal View History

/*
Unix SMB/Netbios implementation.
Version 1.9.
Directory handling routines
'The mother of all checkins' :-). Jeremy Allison (jallison@whistle.com) Wed May 7 1997: Update for 1.9.17alpha1 release - 'browsefix release' designed to make browsing across subnets work. byteorder.h: Updated copyright to 1997. charcnv.c: Updated copyright to 1997. charset.c Updated copyright to 1997. charset.h Updated copyright to 1997. client.c Updated copyright to 1997. clientutil.c Updated copyright to 1997. dir.c Updated copyright to 1997. fault.c Updated copyright to 1997. includes.h Updated copyright to 1997. interface.c Updated copyright to 1997. ipc.c Updated copyright to 1997. kanji.c Updated copyright to 1997. kanji.h Updated copyright to 1997. loadparm.c Updated copyright to 1997. locking.c Updated copyright to 1997. mangle.c Updated copyright to 1997. message.c Updated copyright to 1997. nameannounce.c Made use of WINS subnet explicit. Added reset_announce_timer() so announcement can be made immediately when we become a master. Expanded code to do sync with dmb. namebrowse.c Removed redundent checks for AM_MASTER in sync code. Made use of WINS subnet explicit. namedbname.c Made use of WINS subnet explicit. namedbresp.c Made use of WINS subnet explicit. namedbserver.c Made use of WINS subnet explicit. namedbsubnet.c Explicitly add workgroup to WINS subnet when we become a dmb. Made use of WINS subnet explicit. namedbwork.c Made use of WINS subnet explicit. Removed redundent check_work_servertype() function. nameelect.c Explicitly add workgroup to WINS subnet when we become a master browser. Made use of WINS subnet explicit. namelogon.c Updated copyright to 1997. namepacket.c Updated copyright to 1997. namequery.c Updated copyright to 1997. nameresp.c Made use of WINS subnet explicit. Made nmbd fail if configured as master browser and one exists already. nameserv.c Made use of WINS subnet explicit. Remove redundent logon server and domain master code. nameserv.h Add emumerate subnet macros. nameservreply.c Made use of WINS subnet explicit. nameservresp.c Updated copyright to 1997. namework.c Made use of WINS subnet explicit. Updated code to add sync browser entries to add subnet parameter. nmbd.c Added sanity check for misconfigured nmbd. nmblib.c Updated copyright to 1997. nmblookup.c Updated copyright to 1997. nmbsync.c Removed redundent AM_ANY_MASTER check. params.c Updated copyright to 1997. password.c Updated copyright to 1997. pipes.c Updated copyright to 1997. predict.c Updated copyright to 1997. printing.c Updated copyright to 1997. proto.h Changed protos for new nmbd code. quotas.c Updated copyright to 1997. replace.c Updated copyright to 1997. reply.c Updated copyright to 1997. server.c Updated copyright to 1997. shmem.c Updated copyright to 1997. smb.h Updated copyright to 1997. smbencrypt.c Updated copyright to 1997. smbpasswd.c Updated copyright to 1997. smbrun.c Updated copyright to 1997. status.c Updated copyright to 1997. system.c Updated copyright to 1997. testparm.c Updated copyright to 1997. testprns.c Updated copyright to 1997. time.c Updated copyright to 1997. trans2.c Updated copyright to 1997. trans2.h Updated copyright to 1997. uid.c Updated copyright to 1997. username.c Updated copyright to 1997. util.c Updated copyright to 1997. version.h Changed to 1.9.17alpha1. (This used to be commit cf23a155a1315f50d488794a2caf88402bf3e3e6)
1997-05-08 05:14:17 +04:00
Copyright (C) Andrew Tridgell 1992-1997
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"
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 snum, 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(snum, dirptrs[key].path, True)))
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)
{
/* OS/2 seems to use -1 to indicate "close all directories" */
if (key == -1) {
int i;
for (i=0;i<NUMDIRPTRS;i++)
dptr_close(i);
return;
}
if (key < 0 || key >= NUMDIRPTRS) {
DEBUG(3,("Invalid key %d given to dptr_close\n",key));
return;
}
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(SNUM(cnum), directory, True);
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(int snum, char *buf1,unsigned int key)
{
unsigned char *buf = (unsigned char *)buf1;
void *p = dptr_get(snum, 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(int snum, char *buf,int *num)
{
unsigned int key = *(unsigned char *)buf;
void *p = dptr_get(snum, 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(int snum, char *params,int dptr_num)
{
void *p = dptr_get(snum, 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);
}
/****************************************************************************
check a filetype for being valid
****************************************************************************/
BOOL dir_check_ftype(int cnum,int mode,struct stat *st,int dirtype)
{
if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
return False;
return True;
}
/****************************************************************************
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;
BOOL needslash;
*path = *pathreal = *filename = 0;
isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
strequal(Connections[cnum].dirpath,".") ||
strequal(Connections[cnum].dirpath,"/"));
needslash =
( Connections[cnum].dirpath[strlen(Connections[cnum].dirpath) -1] != '/');
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);
if(needslash)
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 (!dir_check_ftype(cnum,*mode,&sbuf,dirtype)) {
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(int snum, char *name, BOOL use_veto)
{
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 it's a vetoed file, pretend it doesn't even exist */
if (use_veto && is_vetoed_name(snum, n)) continue;
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)
{
int count;
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, count=1;
entry->next && count < dir_cache_size + 1;
entry=entry->next, count++) ;
if (entry->next || count != dir_cache_size + 1) {
DEBUG(0,("DirCache bug - please report %d %d\n",dir_cache_size,count));
}
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);
dir_cache_size--;
} 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