diff options
Diffstat (limited to 'source3/smbd/dir.c')
-rw-r--r-- | source3/smbd/dir.c | 1226 |
1 files changed, 615 insertions, 611 deletions
diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index ac6f918b9d..f56e0e9ef0 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -1,8 +1,7 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Directory handling routines - Copyright (C) Andrew Tridgell 1992-1995 + Copyright (C) Andrew Tridgell 1992-1998 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 @@ -20,351 +19,505 @@ */ #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]; - +typedef struct _dptr_struct { + struct _dptr_struct *next, *prev; + int dnum; + uint16 spid; + connection_struct *conn; + void *ptr; + BOOL expect_close; + char *wcard; /* Field only used for trans2_ searches */ + uint16 attr; /* Field only used for trans2_ searches */ + char *path; +} dptr_struct; + +static struct bitmap *dptr_bmap; +static dptr_struct *dirptrs; static int dptrs_open = 0; +#define INVALID_DPTR_KEY (-3) + /**************************************************************************** -initialise the dir array + Initialise the dir bitmap. ****************************************************************************/ + 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,""); - } + if (dptrs_init) + return; + + dptr_bmap = bitmap_allocate(MAX_DIRECTORY_HANDLES); + + if (!dptr_bmap) + exit_server("out of memory in init_dptrs"); + dptrs_init = True; } /**************************************************************************** -idle a dptr - the directory is closed but the control info is kept + Idle a dptr - the directory is closed but the control info is kept. ****************************************************************************/ -static void dptr_idle(int key) + +static void dptr_idle(dptr_struct *dptr) { - if (dirptrs[key].valid && dirptrs[key].ptr) { - DEBUG(4,("Idling dptr key %d\n",key)); + if (dptr->ptr) { + DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum)); dptrs_open--; - CloseDir(dirptrs[key].ptr); - dirptrs[key].ptr = NULL; - } + CloseDir(dptr->ptr); + dptr->ptr = NULL; + } } /**************************************************************************** -idle the oldest dptr + 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; + dptr_struct *dptr; + + /* + * Go to the end of the list. + */ + for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next) + ; + + if(!dptr) { + DEBUG(0,("No dptrs available to idle ?\n")); + return; + } + + /* + * Idle the oldest pointer. + */ + + for(; dptr; dptr = dptr->prev) { + if (dptr->ptr) { + dptr_idle(dptr); + return; } - if (oldi != -1) - dptr_idle(oldi); - else - DEBUG(0,("No dptrs available to idle??\n")); + } } /**************************************************************************** -get the dir ptr for a dir index + Get the dptr_struct 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++; + +static dptr_struct *dptr_get(int key, BOOL forclose) +{ + dptr_struct *dptr; + + for(dptr = dirptrs; dptr; dptr = dptr->next) { + if(dptr->dnum == key) { + if (!forclose && !dptr->ptr) { + if (dptrs_open >= MAX_OPEN_DIRECTORIES) + dptr_idleoldest(); + DEBUG(4,("Reopening dptr key %d\n",key)); + if ((dptr->ptr = OpenDir(dptr->conn, dptr->path, True))) + dptrs_open++; + } + DLIST_PROMOTE(dirptrs,dptr); + return dptr; } - return(dirptrs[key].ptr); } return(NULL); } /**************************************************************************** -get the dir path for a dir index + Get the dptr ptr for a dir index. +****************************************************************************/ + +static void *dptr_ptr(int key) +{ + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->ptr); + return(NULL); +} + +/**************************************************************************** + Get the dir path for a dir index. ****************************************************************************/ + char *dptr_path(int key) { - if (dirptrs[key].valid) - return(dirptrs[key].path); + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->path); return(NULL); } /**************************************************************************** -get the dir wcard for a dir index (lanman2 specific) + Get the dir wcard for a dir index (lanman2 specific). ****************************************************************************/ + char *dptr_wcard(int key) { - if (dirptrs[key].valid) - return(dirptrs[key].wcard); + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->wcard); return(NULL); } /**************************************************************************** -set the dir wcard for a dir index (lanman2 specific) -Returns 0 on ok, 1 on fail. + 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; + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) { + dptr->wcard = wcard; return True; } return False; } /**************************************************************************** -set the dir attrib for a dir index (lanman2 specific) -Returns 0 on ok, 1 on fail. + 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; + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) { + dptr->attr = attr; return True; } return False; } /**************************************************************************** -get the dir attrib for a dir index (lanman2 specific) + Get the dir attrib for a dir index (lanman2 specific) ****************************************************************************/ + uint16 dptr_attr(int key) { - if (dirptrs[key].valid) - return(dirptrs[key].attr); + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->attr); return(0); } /**************************************************************************** -close a dptr + Close a dptr (internal func). +****************************************************************************/ + +static void dptr_close_internal(dptr_struct *dptr) +{ + DEBUG(4,("closing dptr key %d\n",dptr->dnum)); + + DLIST_REMOVE(dirptrs, dptr); + + /* + * Free the dnum in the bitmap. Remember the dnum value is always + * biased by one with respect to the bitmap. + */ + + if(bitmap_query( dptr_bmap, dptr->dnum - 1) != True) { + DEBUG(0,("dptr_close_internal : Error - closing dnum = %d and bitmap not set !\n", + dptr->dnum )); + } + + bitmap_clear(dptr_bmap, dptr->dnum - 1); + + if (dptr->ptr) { + CloseDir(dptr->ptr); + dptrs_open--; + } + + /* Lanman 2 specific code */ + SAFE_FREE(dptr->wcard); + string_set(&dptr->path,""); + SAFE_FREE(dptr); +} + +/**************************************************************************** + Close a dptr given a key. ****************************************************************************/ -void dptr_close(int key) + +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--; + dptr_struct *dptr; + + if(*key == INVALID_DPTR_KEY) + return; + + /* OS/2 seems to use -1 to indicate "close all directories" */ + if (*key == -1) { + dptr_struct *next; + for(dptr = dirptrs; dptr; dptr = next) { + next = dptr->next; + dptr_close_internal(dptr); } - /* Lanman 2 specific code */ - if (dirptrs[key].wcard) - free(dirptrs[key].wcard); - dirptrs[key].valid = False; - string_set(&dirptrs[key].path,""); + *key = INVALID_DPTR_KEY; + return; } + + dptr = dptr_get(*key, True); + + if (!dptr) { + DEBUG(0,("Invalid key %d given to dptr_close\n", *key)); + return; + } + + dptr_close_internal(dptr); + + *key = INVALID_DPTR_KEY; } /**************************************************************************** -close all dptrs for a cnum + Close all dptrs for a cnum. ****************************************************************************/ -void dptr_closecnum(int cnum) + +void dptr_closecnum(connection_struct *conn) { - int i; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].valid && dirptrs[i].cnum == cnum) - dptr_close(i); + dptr_struct *dptr, *next; + for(dptr = dirptrs; dptr; dptr = next) { + next = dptr->next; + if (dptr->conn == conn) + dptr_close_internal(dptr); + } } /**************************************************************************** -idle all dptrs for a cnum + Idle all dptrs for a cnum. ****************************************************************************/ -void dptr_idlecnum(int cnum) + +void dptr_idlecnum(connection_struct *conn) { - int i; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].valid && dirptrs[i].cnum == cnum && dirptrs[i].ptr) - dptr_idle(i); + dptr_struct *dptr; + for(dptr = dirptrs; dptr; dptr = dptr->next) { + if (dptr->conn == conn && dptr->ptr) + dptr_idle(dptr); + } } /**************************************************************************** -close a dptr that matches a given path, only if it matches the pid also + Close a dptr that matches a given path, only if it matches the spid also. ****************************************************************************/ -void dptr_closepath(char *path,int pid) + +void dptr_closepath(char *path,uint16 spid) { - int i; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].valid && pid == dirptrs[i].pid && - strequal(dirptrs[i].path,path)) - dptr_close(i); + dptr_struct *dptr, *next; + for(dptr = dirptrs; dptr; dptr = next) { + next = dptr->next; + if (spid == dptr->spid && strequal(dptr->path,path)) + dptr_close_internal(dptr); + } } /**************************************************************************** - start a directory listing + Start a directory listing. ****************************************************************************/ -static BOOL start_dir(int cnum,char *directory) + +static BOOL start_dir(connection_struct *conn,char *directory) { - DEBUG(5,("start_dir cnum=%d dir=%s\n",cnum,directory)); + DEBUG(5,("start_dir dir=%s\n",directory)); - if (!check_name(directory,cnum)) + if (!check_name(directory,conn)) return(False); if (! *directory) directory = "."; - Connections[cnum].dirptr = OpenDir(directory); - if (Connections[cnum].dirptr) { + conn->dirptr = OpenDir(conn, directory, True); + if (conn->dirptr) { dptrs_open++; - string_set(&Connections[cnum].dirpath,directory); + string_set(&conn->dirpath,directory); return(True); } return(False); } +/**************************************************************************** + Try and close the oldest handle not marked for + expect close in the hope that the client has + finished with that one. +****************************************************************************/ + +static void dptr_close_oldest(BOOL old) +{ + dptr_struct *dptr; + + /* + * Go to the end of the list. + */ + for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next) + ; + + if(!dptr) { + DEBUG(0,("No old dptrs available to close oldest ?\n")); + return; + } + + /* + * If 'old' is true, close the oldest oldhandle dnum (ie. 1 < dnum < 256) that + * does not have expect_close set. If 'old' is false, close + * one of the new dnum handles. + */ + + for(; dptr; dptr = dptr->prev) { + if ((old && (dptr->dnum < 256) && !dptr->expect_close) || + (!old && (dptr->dnum > 255))) { + dptr_close_internal(dptr); + return; + } + } +} /**************************************************************************** -create a new dir ptr + Create a new dir ptr. If the flag old_handle is true then we must allocate + from the bitmap range 0 - 255 as old SMBsearch directory handles are only + one byte long. If old_handle is false we allocate from the range + 256 - MAX_DIRECTORY_HANDLES. We bias the number we return by 1 to ensure + a directory handle is never zero. All the above is folklore taught to + me at Andrew's knee.... :-) :-). JRA. ****************************************************************************/ -int dptr_create(int cnum,char *path, BOOL expect_close,int pid) + +int dptr_create(connection_struct *conn,char *path, BOOL old_handle, BOOL expect_close,uint16 spid) { - int i; - uint32 old; - int oldi; + dptr_struct *dptr; - if (!start_dir(cnum,path)) - return(-1); + if (!start_dir(conn,path)) + return(-2); /* Code to say use a unix error return code. */ - if (dptrs_open >= MAXDIR) + if (dptrs_open >= MAX_OPEN_DIRECTORIES) dptr_idleoldest(); - for (i=0;i<NUMDIRPTRS;i++) - if (!dirptrs[i].valid) - break; - if (i == NUMDIRPTRS) i = -1; + dptr = (dptr_struct *)malloc(sizeof(dptr_struct)); + if(!dptr) { + DEBUG(0,("malloc fail in dptr_create.\n")); + return -1; + } + + ZERO_STRUCTP(dptr); + if(old_handle) { - /* 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; + /* + * This is an old-style SMBsearch request. Ensure the + * value we return will fit in the range 1-255. + */ + + dptr->dnum = bitmap_find(dptr_bmap, 0); + + if(dptr->dnum == -1 || dptr->dnum > 254) { + + /* + * Try and close the oldest handle not marked for + * expect close in the hope that the client has + * finished with that one. + */ + + dptr_close_oldest(True); + + /* Now try again... */ + dptr->dnum = bitmap_find(dptr_bmap, 0); + + if(dptr->dnum == -1 || dptr->dnum > 254) { + DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum)); + SAFE_FREE(dptr); + return -1; } - i = oldi; - } + } + } else { + + /* + * This is a new-style trans2 request. Allocate from + * a range that will return 256 - MAX_DIRECTORY_HANDLES. + */ + + dptr->dnum = bitmap_find(dptr_bmap, 255); + + if(dptr->dnum == -1 || dptr->dnum < 255) { + + /* + * Try and close the oldest handle close in the hope that + * the client has finished with that one. This will only + * happen in the case of the Win98 client bug where it leaks + * directory handles. + */ + + dptr_close_oldest(False); - /* 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; + /* Now try again... */ + dptr->dnum = bitmap_find(dptr_bmap, 255); + + if(dptr->dnum == -1 || dptr->dnum < 255) { + DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum)); + SAFE_FREE(dptr); + return -1; } - i = oldi; + } } - if (i == -1) { - DEBUG(0,("Error - all dirptrs in use??\n")); - return(-1); - } + bitmap_set(dptr_bmap, dptr->dnum); + + dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */ - if (dirptrs[i].valid) - dptr_close(i); + dptr->ptr = conn->dirptr; + string_set(&dptr->path,path); + dptr->conn = conn; + dptr->spid = spid; + dptr->expect_close = expect_close; + dptr->wcard = NULL; /* Only used in lanman2 searches */ + dptr->attr = 0; /* Only used in lanman2 searches */ - 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; + DLIST_ADD(dirptrs, dptr); DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n", - i,path,expect_close)); + dptr->dnum,path,expect_close)); - return(i); + return(dptr->dnum); } -#define DPTR_MASK ((uint32)(((uint32)1)<<31)) - /**************************************************************************** -fill the 5 byte server reserved dptr field + 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); + void *p = dptr_ptr(key); 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)); + DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key, + (long)p,(int)offset)); buf[0] = key; SIVAL(buf,1,offset | DPTR_MASK); return(True); } - /**************************************************************************** -return True is the offset is at zero + Fetch the dir ptr and seek it given the 5 byte server field. ****************************************************************************/ -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++); + void *p = dptr_ptr(key); uint32 offset; if (!p) { DEBUG(3,("fetched null dirptr %d\n",key)); @@ -379,101 +532,117 @@ void *dptr_fetch(char *buf,int *num) } /**************************************************************************** -fetch the dir ptr and seek it given the lanman2 parameter block + Fetch the dir ptr. ****************************************************************************/ -void *dptr_fetch_lanman2(char *params,int dptr_num) + +void *dptr_fetch_lanman2(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); + void *p = dptr_ptr(dptr_num); 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 + Check a filetype for being valid. +****************************************************************************/ + +BOOL dir_check_ftype(connection_struct *conn,int mode,SMB_STRUCT_STAT *st,int dirtype) +{ + if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0) + return False; + return True; +} + +/**************************************************************************** + Get an 8.3 directory entry. ****************************************************************************/ -BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend) + +BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype,char *fname, + SMB_OFF_T *size,int *mode,time_t *date,BOOL check_descend) { char *dname; BOOL found = False; - struct stat sbuf; + SMB_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,"/")); + isrootdir = (strequal(conn->dirpath,"./") || + strequal(conn->dirpath,".") || + strequal(conn->dirpath,"/")); - if (!Connections[cnum].dirptr) + needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/'); + + if (!conn->dirptr) return(False); - + while (!found) - { - dname = ReadDirName(Connections[cnum].dirptr); + { + dname = ReadDirName(conn->dirptr); - DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n", - Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr))); + DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n", + (long)conn->dirptr,TellDir(conn->dirptr))); - if (dname == NULL) - return(False); + 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; - } + pstrcpy(filename,dname); + + /* notice the special *.* handling. This appears to be the only difference + between the wildcard handling in this routine and in the trans2 routines. + see masktest for a demo + */ + if ((strcmp(mask,"*.*") == 0) || + mask_match(filename,mask,False) || + (mangle_map(filename,True,False,SNUM(conn)) && + mask_match(filename,mask,False))) + { + if (isrootdir && (strequal(filename,"..") || strequal(filename,"."))) + continue; + + if (!mangle_is_8_3(filename, False)) { + mangle_map(filename,True,False,SNUM(conn)); + } - if (check_descend && - !strequal(fname,".") && !strequal(fname,"..")) - continue; + pstrcpy(fname,filename); + *path = 0; + pstrcpy(path,conn->dirpath); + if(needslash) + pstrcat(path,"/"); + pstrcpy(pathreal,path); + pstrcat(path,fname); + pstrcat(pathreal,dname); + if (conn->vfs_ops.stat(conn, pathreal, &sbuf) != 0) + { + DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n",path, strerror(errno) )); + continue; + } - *mode = dos_mode(cnum,pathreal,&sbuf); + *mode = dos_mode(conn,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; + if (!dir_check_ftype(conn,*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)); + DEBUG(3,("get_dir_entry mask=[%s] found %s fname=%s\n",mask, pathreal,fname)); - found = True; - } + found = True; } + } return(found); } @@ -490,27 +659,109 @@ typedef struct } Dir; + +/******************************************************************* +check to see if a user can read a file. This is only approximate, +it is used as part of the "hide unreadable" option. Don't +use it for anything security sensitive +********************************************************************/ + +static BOOL user_can_read_file(connection_struct *conn, char *name) +{ + extern struct current_user current_user; + SMB_STRUCT_STAT ste; + SEC_DESC *psd = NULL; + size_t sd_size; + files_struct *fsp; + int smb_action; + int access_mode; + NTSTATUS status; + uint32 access_granted; + + ZERO_STRUCT(ste); + + /* if we can't stat it does not show it */ + if (vfs_stat(conn, name, &ste) != 0) + return False; + + /* Pseudo-open the file (note - no fd's created). */ + + if(S_ISDIR(ste.st_mode)) + fsp = open_directory(conn, name, &ste, 0, SET_DENY_MODE(DENY_NONE), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + unix_mode(conn,aRONLY|aDIR, name), &smb_action); + else + fsp = open_file_shared1(conn, name, &ste, FILE_READ_ATTRIBUTES, SET_DENY_MODE(DENY_NONE), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &smb_action); + + if (!fsp) + return False; + + /* Get NT ACL -allocated in main loop talloc context. No free needed here. */ + sd_size = conn->vfs_ops.fget_nt_acl(fsp, fsp->fd, &psd); + close_file(fsp, False); + + /* No access if SD get failed. */ + if (!sd_size) + return False; + + return se_access_check(psd, current_user.nt_user_token, FILE_READ_DATA, + &access_granted, &status); +} + /******************************************************************* -open a directory + Open a directory. ********************************************************************/ -void *OpenDir(char *name) + +void *OpenDir(connection_struct *conn, char *name, BOOL use_veto) { Dir *dirp; char *n; - void *p = sys_opendir(name); + DIR *p = conn->vfs_ops.opendir(conn,name); int used=0; if (!p) return(NULL); dirp = (Dir *)malloc(sizeof(Dir)); if (!dirp) { - closedir(p); + DEBUG(0,("Out of memory in OpenDir\n")); + conn->vfs_ops.closedir(conn,p); return(NULL); } dirp->pos = dirp->numentries = dirp->mallocsize = 0; dirp->data = dirp->current = NULL; - while ((n = readdirname(p))) { - int l = strlen(n)+1; + while (True) + { + int l; + + if (used == 0) { + n = "."; + } else if (used == 2) { + n = ".."; + } else { + n = vfs_readdirname(conn, p); + if (n == NULL) + break; + if ((strcmp(".",n) == 0) ||(strcmp("..",n) == 0)) + continue; + } + + l = strlen(n)+1; + + /* If it's a vetoed file, pretend it doesn't even exist */ + if (use_veto && conn && IS_VETO_PATH(conn, n)) continue; + + /* Honour _hide unreadable_ option */ + if (conn && lp_hideunreadable(SNUM(conn))) { + char *entry; + int ret=0; + + if (asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) { + ret = user_can_read_file(conn, entry); + SAFE_FREE(entry); + } + if (!ret) continue; + } + if (used + l > dirp->mallocsize) { int s = MAX(used+l,used+2000); char *r; @@ -523,30 +774,31 @@ void *OpenDir(char *name) dirp->mallocsize = s; dirp->current = dirp->data; } - strcpy(dirp->data+used,n); + pstrcpy(dirp->data+used,n); used += l; dirp->numentries++; } - closedir(p); + conn->vfs_ops.closedir(conn,p); return((void *)dirp); } /******************************************************************* -close a directory + Close a directory. ********************************************************************/ + void CloseDir(void *p) { - Dir *dirp = (Dir *)p; - if (!dirp) return; - if (dirp->data) free(dirp->data); - free(dirp); + if (!p) return; + SAFE_FREE(((Dir *)p)->data); + SAFE_FREE(p); } /******************************************************************* -read from a directory + Read from a directory. ********************************************************************/ + char *ReadDirName(void *p) { char *ret; @@ -563,8 +815,9 @@ char *ReadDirName(void *p) /******************************************************************* -seek a dir + Seek a dir. ********************************************************************/ + BOOL SeekDir(void *p,int pos) { Dir *dirp = (Dir *)p; @@ -582,8 +835,9 @@ BOOL SeekDir(void *p,int pos) } /******************************************************************* -tell a dir position + Tell a dir position. ********************************************************************/ + int TellDir(void *p) { Dir *dirp = (Dir *)p; @@ -593,363 +847,113 @@ int TellDir(void *p) return(dirp->pos); } +/******************************************************************************* + This section manages a global directory cache. + (It should probably be split into a separate module. crh) +********************************************************************************/ + +typedef struct { + ubi_dlNode node; + char *path; + char *name; + char *dname; + int snum; +} dir_cache_entry; + +static ubi_dlNewList( dir_cache ); + +/***************************************************************************** + Add an entry to the directory cache. + Input: path - + name - + dname - + snum - + Output: None. +*****************************************************************************/ + +void DirCacheAdd( char *path, char *name, char *dname, int snum ) +{ + int pathlen; + int namelen; + dir_cache_entry *entry; + + /* Allocate the structure & string space in one go so that it can be freed + * in one call to free(). + */ + pathlen = strlen( path ) +1; /* Bytes required to store path (with nul). */ + namelen = strlen( name ) +1; /* Bytes required to store name (with nul). */ + entry = (dir_cache_entry *)malloc( sizeof( dir_cache_entry ) + + pathlen + + namelen + + strlen( dname ) +1 ); + if( NULL == entry ) /* Not adding to the cache is not fatal, */ + return; /* so just return as if nothing happened. */ + + /* Set pointers correctly and load values. */ + entry->path = pstrcpy( (char *)&entry[1], path); + entry->name = pstrcpy( &(entry->path[pathlen]), name); + entry->dname = pstrcpy( &(entry->name[namelen]), dname); + entry->snum = snum; + + /* Add the new entry to the linked list. */ + (void)ubi_dlAddHead( dir_cache, entry ); + DEBUG( 4, ("Added dir cache entry %s %s -> %s\n", path, name, dname ) ); + + /* Free excess cache entries. */ + while( DIRCACHESIZE < dir_cache->count ) + safe_free( ubi_dlRemTail( dir_cache ) ); -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++; - } } +/***************************************************************************** + Search for an entry to the directory cache. + Input: path - + name - + snum - + Output: The dname string of the located entry, or NULL if the entry was + not found. -/******************************************************************* -check for an entry in the directory cache -********************************************************************/ -char *DirCacheCheck(char *path,char *name,int snum) + Notes: This uses a linear search, which is is okay because of + the small size of the cache. Use a splay tree or hash + for large caches. +*****************************************************************************/ + +char *DirCacheCheck( char *path, char *name, int snum ) { - struct dir_cache *entry; + dir_cache_entry *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); + for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache ); + NULL != entry; + entry = (dir_cache_entry *)ubi_dlNext( entry ) ) + { + if( entry->snum == snum + && 0 == strcmp( name, entry->name ) + && 0 == strcmp( path, entry->path ) ) + { + 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 -********************************************************************/ +/***************************************************************************** + Remove all cache entries which have an snum that matches the input. + Input: snum - + Output: None. +*****************************************************************************/ + 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; + dir_cache_entry *entry; + ubi_dlNodePtr next; + + for(entry = (dir_cache_entry *)ubi_dlFirst( dir_cache ); + NULL != entry; ) { + next = ubi_dlNext( entry ); + if( entry->snum == snum ) + safe_free( ubi_dlRemThis( dir_cache, entry ) ); + entry = (dir_cache_entry *)next; } - - 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 |