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
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
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.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
extern struct current_user current_user;
-/* Make directory handle internals available. */
+/* "Special" directory offsets. */
+#define END_OF_DIRECTORY_OFFSET ((long)-1)
+#define START_OF_DIRECTORY_OFFSET ((long)0)
+#define DOT_DOT_DIRECTORY_OFFSET ((long)0x80000000)
-#define NAME_CACHE_SIZE 100
+/* Make directory handle internals available. */
struct name_cache_entry {
char *name;
struct smb_Dir {
connection_struct *conn;
- DIR *dir;
+ SMB_STRUCT_DIR *dir;
long offset;
char *dir_path;
+ size_t name_cache_size;
struct name_cache_entry *name_cache;
unsigned int name_cache_index;
+ unsigned int file_number;
};
struct dptr_struct {
struct smb_Dir *dir_hnd;
BOOL expect_close;
char *wcard;
- uint16 attr;
+ uint32 attr;
char *path;
BOOL has_wild; /* Set to true if the wcard entry has MS wildcard characters in it. */
+ BOOL did_stat; /* Optimisation for non-wcard searches. */
};
static struct bitmap *dptr_bmap;
Make a dir struct.
****************************************************************************/
-void make_dir_struct(char *buf, const char *mask, const char *fname,SMB_OFF_T size,int mode,time_t date, BOOL uc)
+void make_dir_struct(char *buf, const char *mask, const char *fname,SMB_OFF_T size,uint32 mode,time_t date, BOOL uc)
{
char *p;
pstring mask2;
memset(buf+21,'\0',DIR_STRUCT_SIZE-21);
SCVAL(buf,21,mode);
- put_dos_date(buf,22,date);
+ srv_put_dos_date(buf,22,date);
SSVAL(buf,26,size & 0xFFFF);
SSVAL(buf,28,(size >> 16)&0xFFFF);
/* We only uppercase if FLAGS2_LONG_PATH_COMPONENTS is zero in the input buf.
if (dirhandles_open >= MAX_OPEN_DIRECTORIES)
dptr_idleoldest();
DEBUG(4,("dptr_get: Reopening dptr key %d\n",key));
- if (!(dptr->dir_hnd = OpenDir(dptr->conn, dptr->path))) {
+ if (!(dptr->dir_hnd = OpenDir(dptr->conn, dptr->path, dptr->wcard, dptr->attr))) {
DEBUG(4,("dptr_get: Failed to open %s (%s)\n",dptr->path,
strerror(errno)));
return False;
return(0);
}
-/****************************************************************************
- Set the dir wcard for a dir index.
- Returns 0 on ok, 1 on fail.
-****************************************************************************/
-
-BOOL dptr_set_wcard_and_attributes(int key, const char *wcard, uint16 attr)
-{
- struct dptr_struct *dptr = dptr_get(key, False);
-
- if (dptr) {
- dptr->attr = attr;
- dptr->wcard = SMB_STRDUP(wcard);
- if (!dptr->wcard)
- return False;
- if (wcard[0] == '.' && wcard[1] == 0) {
- dptr->has_wild = True;
- } else {
- dptr->has_wild = ms_has_wild(wcard);
- }
- return True;
- }
- return False;
-}
-
/****************************************************************************
Close a dptr (internal func).
****************************************************************************/
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.
+ wcard must not be zero.
****************************************************************************/
-int dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOOL expect_close,uint16 spid)
+NTSTATUS dptr_create(connection_struct *conn, const char *path, BOOL old_handle, BOOL expect_close,uint16 spid,
+ const char *wcard, BOOL wcard_has_wild, uint32 attr, struct dptr_struct **dptr_ret)
{
struct dptr_struct *dptr = NULL;
struct smb_Dir *dir_hnd;
- const char *dir2;
+ NTSTATUS status;
DEBUG(5,("dptr_create dir=%s\n", path));
- if (!check_name(path,conn))
- return(-2); /* Code to say use a unix error return code. */
+ if (!wcard) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
- /* use a const pointer from here on */
- dir2 = path;
- if (!*dir2)
- dir2 = ".";
+ status = check_name(conn,path);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
- dir_hnd = OpenDir(conn, dir2);
+ dir_hnd = OpenDir(conn, path, wcard, attr);
if (!dir_hnd) {
- return (-2);
+ return map_nt_error_from_unix(errno);
}
- string_set(&conn->dirpath,dir2);
+ string_set(&conn->dirpath,path);
- if (dirhandles_open >= MAX_OPEN_DIRECTORIES)
+ if (dirhandles_open >= MAX_OPEN_DIRECTORIES) {
dptr_idleoldest();
+ }
dptr = SMB_MALLOC_P(struct dptr_struct);
if(!dptr) {
DEBUG(0,("malloc fail in dptr_create.\n"));
CloseDir(dir_hnd);
- return -1;
+ return NT_STATUS_NO_MEMORY;
}
ZERO_STRUCTP(dptr);
DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum));
SAFE_FREE(dptr);
CloseDir(dir_hnd);
- return -1;
+ return NT_STATUS_TOO_MANY_OPENED_FILES;
}
}
} else {
DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum));
SAFE_FREE(dptr);
CloseDir(dir_hnd);
- return -1;
+ return NT_STATUS_TOO_MANY_OPENED_FILES;
}
}
}
dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */
- string_set(&dptr->path,dir2);
+ string_set(&dptr->path,path);
dptr->conn = conn;
dptr->dir_hnd = dir_hnd;
dptr->spid = spid;
dptr->expect_close = expect_close;
- dptr->wcard = NULL; /* Only used in lanman2 searches */
- dptr->attr = 0; /* Only used in lanman2 searches */
- dptr->has_wild = True; /* Only used in lanman2 searches */
+ dptr->wcard = SMB_STRDUP(wcard);
+ if (!dptr->wcard) {
+ bitmap_clear(dptr_bmap, dptr->dnum - 1);
+ SAFE_FREE(dptr);
+ CloseDir(dir_hnd);
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (lp_posix_pathnames() || (wcard[0] == '.' && wcard[1] == 0)) {
+ dptr->has_wild = True;
+ } else {
+ dptr->has_wild = wcard_has_wild;
+ }
+
+ dptr->attr = attr;
DLIST_ADD(dirptrs, dptr);
DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
dptr->dnum,path,expect_close));
- conn->dirptr = dptr;
+ *dptr_ret = dptr;
- return(dptr->dnum);
+ return NT_STATUS_OK;
}
int dptr_CloseDir(struct dptr_struct *dptr)
{
+ DLIST_REMOVE(dirptrs, dptr);
return CloseDir(dptr->dir_hnd);
}
return TellDir(dptr->dir_hnd);
}
+BOOL dptr_has_wild(struct dptr_struct *dptr)
+{
+ return dptr->has_wild;
+}
+
+int dptr_dnum(struct dptr_struct *dptr)
+{
+ return dptr->dnum;
+}
+
/****************************************************************************
Return the next visible file name, skipping veto'd and invisible files.
****************************************************************************/
const char *dptr_ReadDirName(struct dptr_struct *dptr, long *poffset, SMB_STRUCT_STAT *pst)
{
- pstring pathreal;
-
SET_STAT_INVALID(*pst);
if (dptr->has_wild) {
/* If poffset is -1 then we know we returned this name before and we have
no wildcards. We're at the end of the directory. */
- if (*poffset == -1) {
+ if (*poffset == END_OF_DIRECTORY_OFFSET) {
return NULL;
}
- /* We know the stored wcard contains no wildcard characters. See if we can match
- with a stat call. If we can't, then set has_wild to true to
- prevent us from doing this on every call. */
+ if (!dptr->did_stat) {
+ pstring pathreal;
- /* First check if it should be visible. */
- if (!is_visible_file(dptr->conn, dptr->path, dptr->wcard, pst, True)) {
- dptr->has_wild = True;
- return dptr_normal_ReadDirName(dptr, poffset, pst);
- }
+ /* We know the stored wcard contains no wildcard characters. See if we can match
+ with a stat call. If we can't, then set did_stat to true to
+ ensure we only do this once and keep searching. */
- if (VALID_STAT(*pst)) {
- /* We need to set the underlying dir_hdn offset to -1 also as
- this function is usually called with the output from TellDir. */
- dptr->dir_hnd->offset = *poffset = -1;
- return dptr->wcard;
- }
+ dptr->did_stat = True;
- pstrcpy(pathreal,dptr->path);
- pstrcat(pathreal,"/");
- pstrcat(pathreal,dptr->wcard);
+ /* First check if it should be visible. */
+ if (!is_visible_file(dptr->conn, dptr->path, dptr->wcard, pst, True)) {
+ /* This only returns False if the file was found, but
+ is explicitly not visible. Set us to end of directory,
+ but return NULL as we know we can't ever find it. */
+ dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET;
+ return NULL;
+ }
- if (SMB_VFS_STAT(dptr->conn,pathreal,pst) == 0) {
- /* We need to set the underlying dir_hdn offset to -1 also as
- this function is usually called with the output from TellDir. */
- dptr->dir_hnd->offset = *poffset = -1;
- return dptr->wcard;
- } else {
- /* If we get any other error than ENOENT or ENOTDIR
- then the file exists we just can't stat it. */
- if (errno != ENOENT && errno != ENOTDIR) {
- /* We need to set the underlying dir_hdn offset to -1 also as
+ if (VALID_STAT(*pst)) {
+ /* We need to set the underlying dir_hnd offset to -1 also as
this function is usually called with the output from TellDir. */
- dptr->dir_hnd->offset = *poffset = -1;
+ dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET;
return dptr->wcard;
}
- }
- /* In case sensitive mode we don't search - we know if it doesn't exist
- with a stat we will fail. */
+ pstrcpy(pathreal,dptr->path);
+ pstrcat(pathreal,"/");
+ pstrcat(pathreal,dptr->wcard);
- if (dptr->conn->case_sensitive) {
- /* We need to set the underlying dir_hdn offset to -1 also as
- this function is usually called with the output from TellDir. */
- dptr->dir_hnd->offset = *poffset = -1;
- return NULL;
- } else {
- dptr->has_wild = True;
- return dptr_normal_ReadDirName(dptr, poffset, pst);
+ if (SMB_VFS_STAT(dptr->conn,pathreal,pst) == 0) {
+ /* We need to set the underlying dir_hnd offset to -1 also as
+ this function is usually called with the output from TellDir. */
+ dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET;
+ return dptr->wcard;
+ } else {
+ /* If we get any other error than ENOENT or ENOTDIR
+ then the file exists we just can't stat it. */
+ if (errno != ENOENT && errno != ENOTDIR) {
+ /* We need to set the underlying dir_hdn offset to -1 also as
+ this function is usually called with the output from TellDir. */
+ dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET;
+ return dptr->wcard;
+ }
+ }
+
+ /* In case sensitive mode we don't search - we know if it doesn't exist
+ with a stat we will fail. */
+
+ if (dptr->conn->case_sensitive) {
+ /* We need to set the underlying dir_hnd offset to -1 also as
+ this function is usually called with the output from TellDir. */
+ dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET;
+ return NULL;
+ }
}
+ return dptr_normal_ReadDirName(dptr, poffset, pst);
}
/****************************************************************************
{
SET_STAT_INVALID(*pst);
- if (!dptr->has_wild && (dptr->dir_hnd->offset == -1)) {
+ if (!dptr->has_wild && (dptr->dir_hnd->offset == END_OF_DIRECTORY_OFFSET)) {
/* This is a singleton directory and we're already at the end. */
- *poffset = -1;
+ *poffset = END_OF_DIRECTORY_OFFSET;
return False;
}
- if (SearchDir(dptr->dir_hnd, name, poffset)) {
- if (is_visible_file(dptr->conn, dptr->path, name, pst, True)) {
- return True;
- }
- }
- return False;
+ return SearchDir(dptr->dir_hnd, name, poffset);
+}
+
+/****************************************************************************
+ Add the name we're returning into the underlying cache.
+****************************************************************************/
+
+void dptr_DirCacheAdd(struct dptr_struct *dptr, const char *name, long offset)
+{
+ DirCacheAdd(dptr->dir_hnd, name, offset);
}
/****************************************************************************
*num = key;
offset = IVAL(buf,1);
if (offset == (uint32)-1) {
- seekoff = -1;
+ seekoff = END_OF_DIRECTORY_OFFSET;
} else {
seekoff = (long)offset;
}
}
/****************************************************************************
- Check a filetype for being valid.
+ Check that a file matches a particular file type.
****************************************************************************/
-BOOL dir_check_ftype(connection_struct *conn,int mode,int dirtype)
+BOOL dir_check_ftype(connection_struct *conn, uint32 mode, uint32 dirtype)
{
- int mask;
+ uint32 mask;
/* Check the "may have" search bits. */
if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
return True;
}
-static BOOL mangle_mask_match(connection_struct *conn, fstring filename, char *mask)
+static BOOL mangle_mask_match(connection_struct *conn, const char *filename,
+ char *mask)
{
- mangle_map(filename,True,False,SNUM(conn));
- return mask_match_search(filename,mask,False);
+ char mname[13];
+
+ if (!name_to_8_3(filename,mname,False,conn->params)) {
+ return False;
+ }
+ return mask_match_search(mname,mask,False);
}
/****************************************************************************
Get an 8.3 directory entry.
****************************************************************************/
-BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype, pstring fname,
- SMB_OFF_T *size,int *mode,time_t *date,BOOL check_descend)
+BOOL get_dir_entry(connection_struct *conn,char *mask,uint32 dirtype, pstring fname,
+ SMB_OFF_T *size,uint32 *mode,time_t *date,BOOL check_descend)
{
const char *dname;
BOOL found = False;
mask_match_search(filename,mask,False) ||
mangle_mask_match(conn,filename,mask)) {
- if (!mangle_is_8_3(filename, False, SNUM(conn)))
- mangle_map(filename,True,False,SNUM(conn));
+ if (!mangle_is_8_3(filename, False, conn->params)) {
+ char mname[13];
+ if (!name_to_8_3(filename,mname,False,
+ conn->params)) {
+ continue;
+ }
+ pstrcpy(filename,mname);
+ }
pstrcpy(fname,filename);
*path = 0;
*mode = dos_mode(conn,pathreal,&sbuf);
if (!dir_check_ftype(conn,*mode,dirtype)) {
- DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
+ DEBUG(5,("[%s] attribs 0x%x didn't match 0x%x\n",filename,(unsigned int)*mode,(unsigned int)dirtype));
continue;
}
DEBUG(3,("get_dir_entry mask=[%s] found %s fname=%s\n",mask, pathreal,fname));
found = True;
+
+ DirCacheAdd(conn->dirptr->dir_hnd, dname, curoff);
}
}
SEC_DESC *psd = NULL;
size_t sd_size;
files_struct *fsp;
- int smb_action;
NTSTATUS status;
uint32 access_granted;
* we never hide files from them.
*/
- if (conn->admin_user)
+ if (conn->admin_user) {
return True;
+ }
/* If we can't stat it does not show it */
- if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0))
+ if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0)) {
+ DEBUG(10,("user_can_read_file: SMB_VFS_STAT failed for file %s with error %s\n",
+ name, strerror(errno) ));
return False;
+ }
/* Pseudo-open the file (note - no fd's created). */
- if(S_ISDIR(pst->st_mode))
- fsp = open_directory(conn, name, pst, 0, SET_DENY_MODE(DENY_NONE), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
- &smb_action);
- else
- fsp = open_file_stat(conn, name, pst);
+ if(S_ISDIR(pst->st_mode)) {
+ status = open_directory(conn, NULL, name, pst,
+ READ_CONTROL_ACCESS,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN,
+ 0, /* no create options. */
+ FILE_ATTRIBUTE_DIRECTORY,
+ NULL, &fsp);
+ } else {
+ status = open_file_stat(conn, NULL, name, pst, &fsp);
+ }
- if (!fsp)
+ if (!NT_STATUS_IS_OK(status)) {
return False;
+ }
/* Get NT ACL -allocated in main loop talloc context. No free needed here. */
- sd_size = SMB_VFS_FGET_NT_ACL(fsp, fsp->fd,
+ sd_size = SMB_VFS_FGET_NT_ACL(fsp, fsp->fh->fd,
(OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd);
- close_file(fsp, True);
+ close_file(fsp, NORMAL_CLOSE);
/* No access if SD get failed. */
- if (!sd_size)
+ if (!sd_size) {
return False;
+ }
return se_access_check(psd, current_user.nt_user_token, FILE_READ_DATA,
&access_granted, &status);
SEC_DESC *psd = NULL;
size_t sd_size;
files_struct *fsp;
- int smb_action;
- int access_mode;
+ int info;
NTSTATUS status;
uint32 access_granted;
* we never hide files from them.
*/
- if (conn->admin_user)
+ if (conn->admin_user) {
return True;
+ }
/* If we can't stat it does not show it */
- if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0))
+ if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0)) {
return False;
+ }
- /* Pseudo-open the file (note - no fd's created). */
+ /* Pseudo-open the file */
- if(S_ISDIR(pst->st_mode))
+ if(S_ISDIR(pst->st_mode)) {
return True;
- else
- fsp = open_file_shared1(conn, name, pst, FILE_WRITE_ATTRIBUTES, SET_DENY_MODE(DENY_NONE),
- (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY,
- &access_mode, &smb_action);
+ } else {
+ status = open_file_ntcreate(conn, NULL, name, pst,
+ FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN,
+ 0,
+ FILE_ATTRIBUTE_NORMAL,
+ INTERNAL_OPEN_ONLY,
+ &info, &fsp);
+ }
- if (!fsp)
+ if (!NT_STATUS_IS_OK(status)) {
return False;
+ }
/* Get NT ACL -allocated in main loop talloc context. No free needed here. */
- sd_size = SMB_VFS_FGET_NT_ACL(fsp, fsp->fd,
+ sd_size = SMB_VFS_FGET_NT_ACL(fsp, fsp->fh->fd,
(OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd);
- close_file(fsp, False);
+ close_file(fsp, NORMAL_CLOSE);
/* No access if SD get failed. */
if (!sd_size)
/* If it's a vetoed file, pretend it doesn't even exist */
if (use_veto && IS_VETO_PATH(conn, name)) {
+ DEBUG(10,("is_visible_file: file %s is vetoed.\n", name ));
return False;
}
if (hide_unreadable || hide_unwriteable || hide_special) {
+ pstring link_target;
char *entry = NULL;
if (asprintf(&entry, "%s/%s", dir_path, name) == -1) {
return False;
}
+
+ /* If it's a dfs symlink, ignore _hide xxxx_ options */
+ if (lp_host_msdfs() &&
+ lp_msdfs_root(SNUM(conn)) &&
+ is_msdfs_link(conn, entry, link_target, NULL)) {
+ SAFE_FREE(entry);
+ return True;
+ }
+
/* Honour _hide unreadable_ option */
if (hide_unreadable && !user_can_read_file(conn, entry, pst)) {
+ DEBUG(10,("is_visible_file: file %s is unreadable.\n", entry ));
SAFE_FREE(entry);
return False;
}
/* Honour _hide unwriteable_ option */
if (hide_unwriteable && !user_can_write_file(conn, entry, pst)) {
+ DEBUG(10,("is_visible_file: file %s is unwritable.\n", entry ));
SAFE_FREE(entry);
return False;
}
/* Honour _hide_special_ option */
if (hide_special && file_is_special(conn, entry, pst)) {
+ DEBUG(10,("is_visible_file: file %s is special.\n", entry ));
SAFE_FREE(entry);
return False;
}
Open a directory.
********************************************************************/
-struct smb_Dir *OpenDir(connection_struct *conn, const char *name)
+struct smb_Dir *OpenDir(connection_struct *conn, const char *name, const char *mask, uint32 attr)
{
struct smb_Dir *dirp = SMB_MALLOC_P(struct smb_Dir);
+
if (!dirp) {
return NULL;
}
ZERO_STRUCTP(dirp);
dirp->conn = conn;
+ dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn));
dirp->dir_path = SMB_STRDUP(name);
if (!dirp->dir_path) {
goto fail;
}
- dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path);
+ dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr);
if (!dirp->dir) {
DEBUG(5,("OpenDir: Can't open %s. %s\n", dirp->dir_path, strerror(errno) ));
goto fail;
}
- dirp->name_cache = SMB_CALLOC_ARRAY(struct name_cache_entry, NAME_CACHE_SIZE);
- if (!dirp->name_cache) {
- goto fail;
+ if (dirp->name_cache_size) {
+ dirp->name_cache = SMB_CALLOC_ARRAY(struct name_cache_entry,
+ dirp->name_cache_size);
+ if (!dirp->name_cache) {
+ goto fail;
+ }
+ } else {
+ dirp->name_cache = NULL;
}
dirhandles_open++;
}
SAFE_FREE(dirp->dir_path);
if (dirp->name_cache) {
- for (i = 0; i < NAME_CACHE_SIZE; i++) {
+ for (i = 0; i < dirp->name_cache_size; i++) {
SAFE_FREE(dirp->name_cache[i].name);
}
}
const char *n;
connection_struct *conn = dirp->conn;
- SeekDir(dirp, *poffset);
+ /* Cheat to allow . and .. to be the first entries returned. */
+ if (((*poffset == START_OF_DIRECTORY_OFFSET) || (*poffset == DOT_DOT_DIRECTORY_OFFSET)) && (dirp->file_number < 2)) {
+ if (dirp->file_number == 0) {
+ n = ".";
+ *poffset = dirp->offset = START_OF_DIRECTORY_OFFSET;
+ } else {
+ *poffset = dirp->offset = DOT_DOT_DIRECTORY_OFFSET;
+ n = "..";
+ }
+ dirp->file_number++;
+ return n;
+ } else if (*poffset == END_OF_DIRECTORY_OFFSET) {
+ *poffset = dirp->offset = END_OF_DIRECTORY_OFFSET;
+ return NULL;
+ } else {
+ /* A real offset, seek to it. */
+ SeekDir(dirp, *poffset);
+ }
+
while ((n = vfs_readdirname(conn, dirp->dir))) {
- struct name_cache_entry *e;
- dirp->offset = SMB_VFS_TELLDIR(conn, dirp->dir);
- dirp->name_cache_index = (dirp->name_cache_index+1) % NAME_CACHE_SIZE;
- e = &dirp->name_cache[dirp->name_cache_index];
- SAFE_FREE(e->name);
- e->name = SMB_STRDUP(n);
- *poffset = e->offset= dirp->offset;
- return e->name;
+ /* Ignore . and .. - we've already returned them. */
+ if (*n == '.') {
+ if ((n[1] == '\0') || (n[1] == '.' && n[2] == '\0')) {
+ continue;
+ }
+ }
+ *poffset = dirp->offset = SMB_VFS_TELLDIR(conn, dirp->dir);
+ dirp->file_number++;
+ return n;
}
- dirp->offset = -1;
+ *poffset = dirp->offset = END_OF_DIRECTORY_OFFSET;
return NULL;
}
+/*******************************************************************
+ Rewind to the start.
+********************************************************************/
+
+void RewindDir(struct smb_Dir *dirp, long *poffset)
+{
+ SMB_VFS_REWINDDIR(dirp->conn, dirp->dir);
+ dirp->file_number = 0;
+ dirp->offset = START_OF_DIRECTORY_OFFSET;
+ *poffset = START_OF_DIRECTORY_OFFSET;
+}
+
/*******************************************************************
Seek a dir.
********************************************************************/
void SeekDir(struct smb_Dir *dirp, long offset)
{
if (offset != dirp->offset) {
- SMB_VFS_SEEKDIR(dirp->conn, dirp->dir, offset);
+ if (offset == START_OF_DIRECTORY_OFFSET) {
+ RewindDir(dirp, &offset);
+ /*
+ * Ok we should really set the file number here
+ * to 1 to enable ".." to be returned next. Trouble
+ * is I'm worried about callers using SeekDir(dirp,0)
+ * as equivalent to RewindDir(). So leave this alone
+ * for now.
+ */
+ } else if (offset == DOT_DOT_DIRECTORY_OFFSET) {
+ RewindDir(dirp, &offset);
+ /*
+ * Set the file number to 2 - we want to get the first
+ * real file entry (the one we return after "..")
+ * on the next ReadDir.
+ */
+ dirp->file_number = 2;
+ } else if (offset == END_OF_DIRECTORY_OFFSET) {
+ ; /* Don't seek in this case. */
+ } else {
+ SMB_VFS_SEEKDIR(dirp->conn, dirp->dir, offset);
+ }
dirp->offset = offset;
}
}
return(dirp->offset);
}
+/*******************************************************************
+ Add an entry into the dcache.
+********************************************************************/
+
+void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset)
+{
+ struct name_cache_entry *e;
+
+ if (!dirp->name_cache_size || !dirp->name_cache) {
+ return;
+ }
+
+ dirp->name_cache_index = (dirp->name_cache_index+1) %
+ dirp->name_cache_size;
+ e = &dirp->name_cache[dirp->name_cache_index];
+ SAFE_FREE(e->name);
+ e->name = SMB_STRDUP(name);
+ e->offset = offset;
+}
+
/*******************************************************************
Find an entry by name. Leave us at the offset after it.
Don't check for veto or invisible files.
connection_struct *conn = dirp->conn;
/* Search back in the name cache. */
- for (i = dirp->name_cache_index; i >= 0; i--) {
- struct name_cache_entry *e = &dirp->name_cache[i];
- if (e->name && (conn->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) {
- *poffset = e->offset;
- SeekDir(dirp, e->offset);
- return True;
+ if (dirp->name_cache_size && dirp->name_cache) {
+ for (i = dirp->name_cache_index; i >= 0; i--) {
+ struct name_cache_entry *e = &dirp->name_cache[i];
+ if (e->name && (conn->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) {
+ *poffset = e->offset;
+ SeekDir(dirp, e->offset);
+ return True;
+ }
}
- }
- for (i = NAME_CACHE_SIZE-1; i > dirp->name_cache_index; i--) {
- struct name_cache_entry *e = &dirp->name_cache[i];
- if (e->name && (conn->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) {
- *poffset = e->offset;
- SeekDir(dirp, e->offset);
- return True;
+ for (i = dirp->name_cache_size - 1; i > dirp->name_cache_index; i--) {
+ struct name_cache_entry *e = &dirp->name_cache[i];
+ if (e->name && (conn->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) {
+ *poffset = e->offset;
+ SeekDir(dirp, e->offset);
+ return True;
+ }
}
}
/* Not found in the name cache. Rewind directory and start from scratch. */
SMB_VFS_REWINDDIR(conn, dirp->dir);
- *poffset = 0;
+ dirp->file_number = 0;
+ *poffset = START_OF_DIRECTORY_OFFSET;
while ((entry = ReadDirName(dirp, poffset))) {
if (conn->case_sensitive ? (strcmp(entry, name) == 0) : strequal(entry, name)) {
return True;
}
return False;
}
+
+/*****************************************************************
+ Is this directory empty ?
+*****************************************************************/
+
+NTSTATUS can_delete_directory(struct connection_struct *conn,
+ const char *dirname)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ long dirpos = 0;
+ const char *dname;
+ struct smb_Dir *dir_hnd = OpenDir(conn, dirname, NULL, 0);
+
+ if (!dir_hnd) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ while ((dname = ReadDirName(dir_hnd,&dirpos))) {
+ SMB_STRUCT_STAT st;
+
+ /* Quick check for "." and ".." */
+ if (dname[0] == '.') {
+ if (!dname[1] || (dname[1] == '.' && !dname[2])) {
+ continue;
+ }
+ }
+
+ if (!is_visible_file(conn, dirname, dname, &st, True)) {
+ continue;
+ }
+
+ DEBUG(10,("can_delete_directory: got name %s - can't delete\n", dname ));
+ status = NT_STATUS_DIRECTORY_NOT_EMPTY;
+ break;
+ }
+ CloseDir(dir_hnd);
+
+ return status;
+}