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"
/* Make directory handle internals available. */
-#define NAME_CACHE_SIZE 100
-
struct name_cache_entry {
char *name;
long offset;
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;
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;
wcard must not be zero.
****************************************************************************/
-int dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOOL expect_close,uint16 spid,
- const char *wcard, BOOL wcard_has_wild, uint32 attr)
+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 (!wcard) {
- return -1;
+ return NT_STATUS_INVALID_PARAMETER;
}
- if (!check_name(path,conn))
- return(-2); /* Code to say use a unix error return code. */
-
- /* 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, wcard, attr);
+ 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;
bitmap_clear(dptr_bmap, dptr->dnum - 1);
SAFE_FREE(dptr);
CloseDir(dir_hnd);
- return -1;
+ return NT_STATUS_NO_MEMORY;
}
if (lp_posix_pathnames() || (wcard[0] == '.' && wcard[1] == 0)) {
dptr->has_wild = True;
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) {
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 = END_OF_DIRECTORY_OFFSET;
- 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 = 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
+ 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 = 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 = END_OF_DIRECTORY_OFFSET;
- 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);
}
/****************************************************************************
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);
}
/****************************************************************************
}
/****************************************************************************
- Check a filetype for being valid.
+ Check that a file matches a particular file type.
****************************************************************************/
BOOL dir_check_ftype(connection_struct *conn, uint32 mode, uint32 dirtype)
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);
}
/****************************************************************************
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;
DEBUG(3,("get_dir_entry mask=[%s] found %s fname=%s\n",mask, pathreal,fname));
found = True;
+
+ DirCacheAdd(conn->dirptr->dir_hnd, dname, curoff);
}
}
/* If we can't stat it does not show it */
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,
+ status = open_directory(conn, NULL, name, pst,
READ_CONTROL_ACCESS,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_OPEN,
0, /* no create options. */
- NULL);
+ FILE_ATTRIBUTE_DIRECTORY,
+ NULL, &fsp);
} else {
- fsp = open_file_stat(conn, name, pst);
+ status = open_file_stat(conn, NULL, name, pst, &fsp);
}
- if (!fsp) {
+ if (!NT_STATUS_IS_OK(status)) {
return False;
}
if(S_ISDIR(pst->st_mode)) {
return True;
} else {
- fsp = open_file_ntcreate(conn, name, pst,
+ 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);
+ &info, &fsp);
}
- if (!fsp) {
+ if (!NT_STATUS_IS_OK(status)) {
return False;
}
/* 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;
}
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->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);
}
}
}
while ((n = vfs_readdirname(conn, dirp->dir))) {
- struct name_cache_entry *e;
/* Ignore . and .. - we've already returned them. */
if (*n == '.') {
if ((n[1] == '\0') || (n[1] == '.' && n[2] == '\0')) {
continue;
}
}
- 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;
+ *poffset = dirp->offset = SMB_VFS_TELLDIR(conn, dirp->dir);
dirp->file_number++;
- return e->name;
+ return n;
}
*poffset = dirp->offset = END_OF_DIRECTORY_OFFSET;
return NULL;
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;
+ }
}
}
}
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;
+}