*/
#include "includes.h"
+#include "system/filesys.h"
+#include "smbd/smbd.h"
#include "smbd/globals.h"
+#include "libcli/security/security.h"
+#include "lib/util/bitmap.h"
+#include "../lib/util/memcache.h"
+#include "../librpc/gen_ndr/open_files.h"
/*
This module implements directory related functions for Samba.
#define START_OF_DIRECTORY_OFFSET ((long)0)
#define DOT_DOT_DIRECTORY_OFFSET ((long)0x80000000)
+/* "Special" directory offsets in 32-bit wire format. */
+#define WIRE_END_OF_DIRECTORY_OFFSET ((uint32_t)0xFFFFFFFF)
+#define WIRE_START_OF_DIRECTORY_OFFSET ((uint32_t)0)
+#define WIRE_DOT_DOT_DIRECTORY_OFFSET ((uint32_t)0x80000000)
+
/* Make directory handle internals available. */
struct name_cache_entry {
struct smb_Dir {
connection_struct *conn;
- SMB_STRUCT_DIR *dir;
+ 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;
+ files_struct *fsp; /* Back pointer to containing fsp, only
+ set from OpenDir_fsp(). */
};
struct dptr_struct {
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. */
+ bool priv; /* Directory handle opened with privilege. */
+ uint32_t counter;
+ struct memcache *dptr_cache;
};
-
-#define INVALID_DPTR_KEY (-3)
-
-/****************************************************************************
- Make a dir struct.
-****************************************************************************/
-
-bool make_dir_struct(TALLOC_CTX *ctx,
- char *buf,
+static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
+ files_struct *fsp,
const char *mask,
- const char *fname,
- SMB_OFF_T size,
- uint32 mode,
- time_t date,
- bool uc)
-{
- char *p;
- char *mask2 = talloc_strdup(ctx, mask);
+ uint32 attr);
- if (!mask2) {
- return False;
- }
-
- if ((mode & aDIR) != 0) {
- size = 0;
- }
+static void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset);
- memset(buf+1,' ',11);
- if ((p = strchr_m(mask2,'.')) != NULL) {
- *p = 0;
- push_ascii(buf+1,mask2,8, 0);
- push_ascii(buf+9,p+1,3, 0);
- *p = '.';
- } else {
- push_ascii(buf+1,mask2,11, 0);
- }
-
- memset(buf+21,'\0',DIR_STRUCT_SIZE-21);
- SCVAL(buf,21,mode);
- 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.
- Strange, but verified on W2K3. Needed for OS/2. JRA. */
- push_ascii(buf+30,fname,12, uc ? STR_UPPER : 0);
- DEBUG(8,("put name [%s] from [%s] into dir struct\n",buf+30, fname));
- return True;
-}
+#define INVALID_DPTR_KEY (-3)
/****************************************************************************
Initialise the dir bitmap.
bool init_dptrs(struct smbd_server_connection *sconn)
{
- if (sconn->smb1.searches.dptr_bmap) {
+ if (sconn->searches.dptr_bmap) {
return true;
}
- sconn->smb1.searches.dptr_bmap = bitmap_talloc(
+ sconn->searches.dptr_bmap = bitmap_talloc(
sconn, MAX_DIRECTORY_HANDLES);
- if (sconn->smb1.searches.dptr_bmap == NULL) {
+ if (sconn->searches.dptr_bmap == NULL) {
return false;
}
if (dptr->dir_hnd) {
DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum));
TALLOC_FREE(dptr->dir_hnd);
+ TALLOC_FREE(dptr->dptr_cache);
+ dptr->counter = 0;
}
}
/*
* Go to the end of the list.
*/
- dptr = DLIST_TAIL(sconn->smb1.searches.dirptrs);
+ dptr = DLIST_TAIL(sconn->searches.dirptrs);
if(!dptr) {
DEBUG(0,("No dptrs available to idle ?\n"));
{
struct dptr_struct *dptr;
- for(dptr = sconn->smb1.searches.dirptrs; dptr; dptr = dptr->next) {
+ for(dptr = sconn->searches.dirptrs; dptr; dptr = dptr->next) {
if(dptr->dnum == key) {
if (!forclose && !dptr->dir_hnd) {
- if (sconn->smb1.searches.dirhandles_open >= MAX_OPEN_DIRECTORIES)
+ if (sconn->searches.dirhandles_open >= MAX_OPEN_DIRECTORIES)
dptr_idleoldest(sconn);
DEBUG(4,("dptr_get: Reopening dptr key %d\n",key));
if (!(dptr->dir_hnd = OpenDir(
dptr->wcard, dptr->attr))) {
DEBUG(4,("dptr_get: Failed to open %s (%s)\n",dptr->path,
strerror(errno)));
- return False;
+ return NULL;
}
}
- DLIST_PROMOTE(sconn->smb1.searches.dirptrs,dptr);
+ DLIST_PROMOTE(sconn->searches.dirptrs,dptr);
return dptr;
}
}
Get the dir path for a dir index.
****************************************************************************/
-char *dptr_path(struct smbd_server_connection *sconn, int key)
+const char *dptr_path(struct smbd_server_connection *sconn, int key)
{
struct dptr_struct *dptr = dptr_get(sconn, key, false);
if (dptr)
Get the dir wcard for a dir index.
****************************************************************************/
-char *dptr_wcard(struct smbd_server_connection *sconn, int key)
+const char *dptr_wcard(struct smbd_server_connection *sconn, int key)
{
struct dptr_struct *dptr = dptr_get(sconn, key, false);
if (dptr)
goto done;
}
- DLIST_REMOVE(sconn->smb1.searches.dirptrs, dptr);
+ if (sconn->using_smb2) {
+ goto done;
+ }
+
+ DLIST_REMOVE(sconn->searches.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(sconn->smb1.searches.dptr_bmap, dptr->dnum - 1)) {
+ if (!bitmap_query(sconn->searches.dptr_bmap, dptr->dnum - 1)) {
DEBUG(0,("dptr_close_internal : Error - closing dnum = %d and bitmap not set !\n",
dptr->dnum ));
}
- bitmap_clear(sconn->smb1.searches.dptr_bmap, dptr->dnum - 1);
+ bitmap_clear(sconn->searches.dptr_bmap, dptr->dnum - 1);
done:
TALLOC_FREE(dptr->dir_hnd);
-
- /* Lanman 2 specific code */
- SAFE_FREE(dptr->wcard);
- string_set(&dptr->path,"");
- SAFE_FREE(dptr);
+ TALLOC_FREE(dptr);
}
/****************************************************************************
/* OS/2 seems to use -1 to indicate "close all directories" */
if (*key == -1) {
struct dptr_struct *next;
- for(dptr = sconn->smb1.searches.dirptrs; dptr; dptr = next) {
+ for(dptr = sconn->searches.dirptrs; dptr; dptr = next) {
next = dptr->next;
dptr_close_internal(dptr);
}
return;
}
- for(dptr = sconn->smb1.searches.dirptrs; dptr; dptr = next) {
+ for(dptr = sconn->searches.dirptrs; dptr; dptr = next) {
next = dptr->next;
if (dptr->conn == conn) {
dptr_close_internal(dptr);
return;
}
- for(dptr = sconn->smb1.searches.dirptrs; dptr; dptr = dptr->next) {
+ for(dptr = sconn->searches.dirptrs; dptr; dptr = dptr->next) {
if (dptr->conn == conn && dptr->dir_hnd) {
dptr_idle(dptr);
}
char *path,uint16 spid)
{
struct dptr_struct *dptr, *next;
- for(dptr = sconn->smb1.searches.dirptrs; dptr; dptr = next) {
+ for(dptr = sconn->searches.dirptrs; dptr; dptr = next) {
next = dptr->next;
if (spid == dptr->spid && strequal(dptr->path,path))
dptr_close_internal(dptr);
/*
* Go to the end of the list.
*/
- for(dptr = sconn->smb1.searches.dirptrs; dptr && dptr->next; dptr = dptr->next)
+ for(dptr = sconn->searches.dirptrs; dptr && dptr->next; dptr = dptr->next)
;
if(!dptr) {
}
}
+/****************************************************************************
+ Safely do an OpenDir as root, ensuring we're in the right place.
+****************************************************************************/
+
+static struct smb_Dir *open_dir_with_privilege(connection_struct *conn,
+ struct smb_request *req,
+ const char *path,
+ const char *wcard,
+ uint32_t attr)
+{
+ struct smb_Dir *dir_hnd = NULL;
+ struct smb_filename *smb_fname_cwd;
+ char *saved_dir = vfs_GetWd(talloc_tos(), conn);
+ struct privilege_paths *priv_paths = req->priv_paths;
+ int ret;
+
+ if (saved_dir == NULL) {
+ return NULL;
+ }
+
+ if (vfs_ChDir(conn, path) == -1) {
+ return NULL;
+ }
+
+ /* Now check the stat value is the same. */
+ smb_fname_cwd = synthetic_smb_fname(talloc_tos(), ".", NULL, NULL);
+
+ if (smb_fname_cwd == NULL) {
+ goto out;
+ }
+ ret = SMB_VFS_STAT(conn, smb_fname_cwd);
+ if (ret != 0) {
+ goto out;
+ }
+
+ if (!check_same_stat(&smb_fname_cwd->st, &priv_paths->parent_name.st)) {
+ DEBUG(0,("open_dir_with_privilege: stat mismatch between %s "
+ "and %s\n",
+ path,
+ smb_fname_str_dbg(&priv_paths->parent_name)));
+ goto out;
+ }
+
+ dir_hnd = OpenDir(NULL, conn, ".", wcard, attr);
+
+ out:
+
+ vfs_ChDir(conn, saved_dir);
+ return dir_hnd;
+}
+
/****************************************************************************
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
wcard must not be zero.
****************************************************************************/
-NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, bool expect_close,uint16 spid,
+NTSTATUS dptr_create(connection_struct *conn,
+ struct smb_request *req,
+ files_struct *fsp,
+ 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 smbd_server_connection *sconn = conn->sconn;
struct dptr_struct *dptr = NULL;
struct smb_Dir *dir_hnd;
- NTSTATUS status;
+
+ if (fsp && fsp->is_directory && fsp->fh->fd != -1) {
+ path = fsp->fsp_name->base_name;
+ }
DEBUG(5,("dptr_create dir=%s\n", path));
return NT_STATUS_INVALID_PARAMETER;
}
- status = check_name(conn,path);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ if (fsp) {
+ if (!(fsp->access_mask & SEC_DIR_LIST)) {
+ DEBUG(5,("dptr_create: directory %s "
+ "not open for LIST access\n",
+ path));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ dir_hnd = OpenDir_fsp(NULL, conn, fsp, wcard, attr);
+ } else {
+ int ret;
+ bool backup_intent = (req && req->priv_paths);
+ struct smb_filename *smb_dname;
+ NTSTATUS status;
+
+ smb_dname = synthetic_smb_fname(talloc_tos(), path,
+ NULL, NULL);
+ if (smb_dname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (lp_posix_pathnames()) {
+ ret = SMB_VFS_LSTAT(conn, smb_dname);
+ } else {
+ ret = SMB_VFS_STAT(conn, smb_dname);
+ }
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ if (!S_ISDIR(smb_dname->st.st_ex_mode)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+ status = smbd_check_access_rights(conn,
+ smb_dname,
+ backup_intent,
+ SEC_DIR_LIST);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (backup_intent) {
+ dir_hnd = open_dir_with_privilege(conn,
+ req,
+ path,
+ wcard,
+ attr);
+ } else {
+ dir_hnd = OpenDir(NULL, conn, path, wcard, attr);
+ }
}
- dir_hnd = OpenDir(NULL, conn, path, wcard, attr);
if (!dir_hnd) {
return map_nt_error_from_unix(errno);
}
- if (sconn->smb1.searches.dirhandles_open >= MAX_OPEN_DIRECTORIES) {
+ if (sconn->searches.dirhandles_open >= MAX_OPEN_DIRECTORIES) {
dptr_idleoldest(sconn);
}
- dptr = SMB_MALLOC_P(struct dptr_struct);
+ dptr = talloc(NULL, struct dptr_struct);
if(!dptr) {
- DEBUG(0,("malloc fail in dptr_create.\n"));
+ DEBUG(0,("talloc fail in dptr_create.\n"));
TALLOC_FREE(dir_hnd);
return NT_STATUS_NO_MEMORY;
}
ZERO_STRUCTP(dptr);
+ dptr->path = talloc_strdup(dptr, path);
+ if (!dptr->path) {
+ TALLOC_FREE(dptr);
+ TALLOC_FREE(dir_hnd);
+ return NT_STATUS_NO_MEMORY;
+ }
+ dptr->conn = conn;
+ dptr->dir_hnd = dir_hnd;
+ dptr->spid = spid;
+ dptr->expect_close = expect_close;
+ dptr->wcard = talloc_strdup(dptr, wcard);
+ if (!dptr->wcard) {
+ TALLOC_FREE(dptr);
+ TALLOC_FREE(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;
+
+ if (sconn->using_smb2) {
+ goto done;
+ }
+
if(old_handle) {
/*
* value we return will fit in the range 1-255.
*/
- dptr->dnum = bitmap_find(sconn->smb1.searches.dptr_bmap, 0);
+ dptr->dnum = bitmap_find(sconn->searches.dptr_bmap, 0);
if(dptr->dnum == -1 || dptr->dnum > 254) {
dptr_close_oldest(sconn, true);
/* Now try again... */
- dptr->dnum = bitmap_find(sconn->smb1.searches.dptr_bmap, 0);
+ dptr->dnum = bitmap_find(sconn->searches.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);
+ TALLOC_FREE(dptr);
TALLOC_FREE(dir_hnd);
return NT_STATUS_TOO_MANY_OPENED_FILES;
}
* a range that will return 256 - MAX_DIRECTORY_HANDLES.
*/
- dptr->dnum = bitmap_find(sconn->smb1.searches.dptr_bmap, 255);
+ dptr->dnum = bitmap_find(sconn->searches.dptr_bmap, 255);
if(dptr->dnum == -1 || dptr->dnum < 255) {
dptr_close_oldest(sconn, false);
/* Now try again... */
- dptr->dnum = bitmap_find(sconn->smb1.searches.dptr_bmap, 255);
+ dptr->dnum = bitmap_find(sconn->searches.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);
+ TALLOC_FREE(dptr);
TALLOC_FREE(dir_hnd);
return NT_STATUS_TOO_MANY_OPENED_FILES;
}
}
}
- bitmap_set(sconn->smb1.searches.dptr_bmap, dptr->dnum);
+ bitmap_set(sconn->searches.dptr_bmap, dptr->dnum);
dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */
- string_set(&dptr->path,path);
- dptr->conn = conn;
- dptr->dir_hnd = dir_hnd;
- dptr->spid = spid;
- dptr->expect_close = expect_close;
- dptr->wcard = SMB_STRDUP(wcard);
- if (!dptr->wcard) {
- bitmap_clear(sconn->smb1.searches.dptr_bmap, dptr->dnum - 1);
- SAFE_FREE(dptr);
- TALLOC_FREE(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(sconn->smb1.searches.dirptrs, dptr);
+ DLIST_ADD(sconn->searches.dirptrs, dptr);
+done:
DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
dptr->dnum,path,expect_close));
Wrapper functions to access the lower level directory handles.
****************************************************************************/
-int dptr_CloseDir(struct dptr_struct *dptr)
+void dptr_CloseDir(files_struct *fsp)
{
- dptr_close_internal(dptr);
- return 0;
+ if (fsp->dptr) {
+ /*
+ * The destructor for the struct smb_Dir
+ * (fsp->dptr->dir_hnd) now handles
+ * all resource deallocation.
+ */
+ dptr_close_internal(fsp->dptr);
+ fsp->dptr = NULL;
+ }
}
void dptr_SeekDir(struct dptr_struct *dptr, long offset)
return dptr->dnum;
}
+bool dptr_get_priv(struct dptr_struct *dptr)
+{
+ return dptr->priv;
+}
+
+void dptr_set_priv(struct dptr_struct *dptr)
+{
+ dptr->priv = true;
+}
+
/****************************************************************************
Return the next visible file name, skipping veto'd and invisible files.
****************************************************************************/
Return the next visible file name, skipping veto'd and invisible files.
****************************************************************************/
-char *dptr_ReadDirName(TALLOC_CTX *ctx,
- struct dptr_struct *dptr,
- long *poffset,
- SMB_STRUCT_STAT *pst)
+static char *dptr_ReadDirName(TALLOC_CTX *ctx,
+ struct dptr_struct *dptr,
+ long *poffset,
+ SMB_STRUCT_STAT *pst)
{
struct smb_filename smb_fname_base;
char *name = NULL;
}
/****************************************************************************
- Add the name we're returning into the underlying cache.
+ Initialize variables & state data at the beginning of all search SMB requests.
****************************************************************************/
-
-void dptr_DirCacheAdd(struct dptr_struct *dptr, const char *name, long offset)
+void dptr_init_search_op(struct dptr_struct *dptr)
{
- DirCacheAdd(dptr->dir_hnd, name, offset);
+ SMB_VFS_INIT_SEARCH_OP(dptr->conn, dptr->dir_hnd->dir);
}
/****************************************************************************
- Initialize variables & state data at the beginning of all search SMB requests.
+ Map a native directory offset to a 32-bit cookie.
****************************************************************************/
-void dptr_init_search_op(struct dptr_struct *dptr)
+
+static uint32_t map_dir_offset_to_wire(struct dptr_struct *dptr, long offset)
{
- SMB_VFS_INIT_SEARCH_OP(dptr->conn, dptr->dir_hnd->dir);
+ DATA_BLOB key;
+ DATA_BLOB val;
+
+ if (offset == END_OF_DIRECTORY_OFFSET) {
+ return WIRE_END_OF_DIRECTORY_OFFSET;
+ } else if(offset == START_OF_DIRECTORY_OFFSET) {
+ return WIRE_START_OF_DIRECTORY_OFFSET;
+ } else if (offset == DOT_DOT_DIRECTORY_OFFSET) {
+ return WIRE_DOT_DOT_DIRECTORY_OFFSET;
+ }
+ if (sizeof(long) == 4) {
+ /* 32-bit machine. We can cheat... */
+ return (uint32_t)offset;
+ }
+ if (dptr->dptr_cache == NULL) {
+ /* Lazy initialize cache. */
+ dptr->dptr_cache = memcache_init(dptr, 0);
+ if (dptr->dptr_cache == NULL) {
+ return WIRE_END_OF_DIRECTORY_OFFSET;
+ }
+ } else {
+ /* Have we seen this offset before ? */
+ key.data = (void *)&offset;
+ key.length = sizeof(offset);
+ if (memcache_lookup(dptr->dptr_cache,
+ SMB1_SEARCH_OFFSET_MAP,
+ key,
+ &val)) {
+ uint32_t wire_offset;
+ SMB_ASSERT(val.length == sizeof(wire_offset));
+ memcpy(&wire_offset, val.data, sizeof(wire_offset));
+ DEBUG(10,("found wire %u <-> offset %ld\n",
+ (unsigned int)wire_offset,
+ (long)offset));
+ return wire_offset;
+ }
+ }
+ /* Allocate a new wire cookie. */
+ do {
+ dptr->counter++;
+ } while (dptr->counter == WIRE_START_OF_DIRECTORY_OFFSET ||
+ dptr->counter == WIRE_END_OF_DIRECTORY_OFFSET ||
+ dptr->counter == WIRE_DOT_DOT_DIRECTORY_OFFSET);
+ /* Store it in the cache. */
+ key.data = (void *)&offset;
+ key.length = sizeof(offset);
+ val.data = (void *)&dptr->counter;
+ val.length = sizeof(dptr->counter); /* MUST BE uint32_t ! */
+ memcache_add(dptr->dptr_cache,
+ SMB1_SEARCH_OFFSET_MAP,
+ key,
+ val);
+ /* And the reverse mapping for lookup from
+ map_wire_to_dir_offset(). */
+ memcache_add(dptr->dptr_cache,
+ SMB1_SEARCH_OFFSET_MAP,
+ val,
+ key);
+ DEBUG(10,("stored wire %u <-> offset %ld\n",
+ (unsigned int)dptr->counter,
+ (long)offset));
+ return dptr->counter;
}
/****************************************************************************
{
unsigned char *buf = (unsigned char *)buf1;
struct dptr_struct *dptr = dptr_get(sconn, key, false);
- uint32 offset;
+ uint32_t wire_offset;
if (!dptr) {
DEBUG(1,("filling null dirptr %d\n",key));
return(False);
}
- offset = (uint32)TellDir(dptr->dir_hnd);
+ wire_offset = map_dir_offset_to_wire(dptr,TellDir(dptr->dir_hnd));
DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key,
- (long)dptr->dir_hnd,(int)offset));
+ (long)dptr->dir_hnd,(int)wire_offset));
buf[0] = key;
- SIVAL(buf,1,offset);
+ SIVAL(buf,1,wire_offset);
return(True);
}
+/****************************************************************************
+ Map a 32-bit wire cookie to a native directory offset.
+****************************************************************************/
+
+static long map_wire_to_dir_offset(struct dptr_struct *dptr, uint32_t wire_offset)
+{
+ DATA_BLOB key;
+ DATA_BLOB val;
+
+ if (wire_offset == WIRE_END_OF_DIRECTORY_OFFSET) {
+ return END_OF_DIRECTORY_OFFSET;
+ } else if(wire_offset == WIRE_START_OF_DIRECTORY_OFFSET) {
+ return START_OF_DIRECTORY_OFFSET;
+ } else if (wire_offset == WIRE_DOT_DOT_DIRECTORY_OFFSET) {
+ return DOT_DOT_DIRECTORY_OFFSET;
+ }
+ if (sizeof(long) == 4) {
+ /* 32-bit machine. We can cheat... */
+ return (long)wire_offset;
+ }
+ if (dptr->dptr_cache == NULL) {
+ /* Logic error, cache should be initialized. */
+ return END_OF_DIRECTORY_OFFSET;
+ }
+ key.data = (void *)&wire_offset;
+ key.length = sizeof(wire_offset);
+ if (memcache_lookup(dptr->dptr_cache,
+ SMB1_SEARCH_OFFSET_MAP,
+ key,
+ &val)) {
+ /* Found mapping. */
+ long offset;
+ SMB_ASSERT(val.length == sizeof(offset));
+ memcpy(&offset, val.data, sizeof(offset));
+ DEBUG(10,("lookup wire %u <-> offset %ld\n",
+ (unsigned int)wire_offset,
+ (long)offset));
+ return offset;
+ }
+ return END_OF_DIRECTORY_OFFSET;
+}
+
/****************************************************************************
Fetch the dir ptr and seek it given the 5 byte server field.
****************************************************************************/
{
unsigned int key = *(unsigned char *)buf;
struct dptr_struct *dptr = dptr_get(sconn, key, false);
- uint32 offset;
+ uint32_t wire_offset;
long seekoff;
if (!dptr) {
return(NULL);
}
*num = key;
- offset = IVAL(buf,1);
- if (offset == (uint32)-1) {
- seekoff = END_OF_DIRECTORY_OFFSET;
- } else {
- seekoff = (long)offset;
- }
+ wire_offset = IVAL(buf,1);
+ seekoff = map_wire_to_dir_offset(dptr, wire_offset);
SeekDir(dptr->dir_hnd,seekoff);
DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
key, dptr->path, (int)seekoff));
return(dptr);
}
-/****************************************************************************
- Check that a file matches a particular file type.
-****************************************************************************/
-
-bool dir_check_ftype(connection_struct *conn, uint32 mode, uint32 dirtype)
-{
- uint32 mask;
-
- /* Check the "may have" search bits. */
- if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
- return False;
-
- /* Check the "must have" bits, which are the may have bits shifted eight */
- /* If must have bit is set, the file/dir can not be returned in search unless the matching
- file attribute is set */
- mask = ((dirtype >> 8) & (aDIR|aARCH|aRONLY|aHIDDEN|aSYSTEM)); /* & 0x37 */
- if(mask) {
- if((mask & (mode & (aDIR|aARCH|aRONLY|aHIDDEN|aSYSTEM))) == mask) /* check if matching attribute present */
- return True;
- else
- return False;
- }
-
- return True;
-}
-
static bool mangle_mask_match(connection_struct *conn,
const char *filename,
const char *mask)
long *_prev_offset)
{
connection_struct *conn = dirptr->conn;
- bool needslash;
+ size_t slashlen;
+ size_t pathlen;
*_smb_fname = NULL;
*_mode = 0;
- needslash = ( dirptr->path[strlen(dirptr->path) -1] != '/');
+ pathlen = strlen(dirptr->path);
+ slashlen = ( dirptr->path[pathlen-1] != '/') ? 1 : 0;
while (true) {
long cur_offset;
long prev_offset;
- SMB_STRUCT_STAT sbuf;
+ SMB_STRUCT_STAT sbuf = { 0 };
char *dname = NULL;
bool isdots;
char *fname = NULL;
struct smb_filename smb_fname;
uint32_t mode = 0;
bool ok;
- NTSTATUS status;
cur_offset = dptr_TellDir(dirptr);
prev_offset = cur_offset;
continue;
}
- pathreal = talloc_asprintf(ctx, "%s%s%s",
- dirptr->path,
- needslash?"/":"",
- dname);
+ /*
+ * This used to be
+ * pathreal = talloc_asprintf(ctx, "%s%s%s", dirptr->path,
+ * needslash?"/":"", dname);
+ * but this was measurably slower than doing the memcpy.
+ */
+
+ pathreal = talloc_array(
+ ctx, char,
+ pathlen + slashlen + talloc_get_size(dname));
if (!pathreal) {
TALLOC_FREE(dname);
TALLOC_FREE(fname);
return false;
}
+ memcpy(pathreal, dirptr->path, pathlen);
+ pathreal[pathlen] = '/';
+ memcpy(pathreal + slashlen + pathlen, dname,
+ talloc_get_size(dname));
+
/* Create smb_fname with NULL stream_name. */
ZERO_STRUCT(smb_fname);
smb_fname.base_name = pathreal;
continue;
}
- if (!dir_check_ftype(conn, mode, dirtype)) {
+ if (!dir_check_ftype(mode, dirtype)) {
DEBUG(5,("[%s] attribs 0x%x didn't match 0x%x\n",
fname, (unsigned int)mode, (unsigned int)dirtype));
TALLOC_FREE(dname);
fileid = vfs_file_id_from_sbuf(conn,
&smb_fname.st);
- get_file_infos(fileid, NULL, &write_time_ts);
+ get_file_infos(fileid, 0, NULL, &write_time_ts);
if (!null_timespec(write_time_ts)) {
update_stat_ex_mtime(&smb_fname.st,
write_time_ts);
TALLOC_FREE(dname);
- status = copy_smb_filename(ctx, &smb_fname, _smb_fname);
+ *_smb_fname = cp_smb_filename(ctx, &smb_fname);
TALLOC_FREE(pathreal);
- if (!NT_STATUS_IS_OK(status)) {
+ if (*_smb_fname == NULL) {
return false;
}
*_fname = fname;
mangle_mask_match(conn, dname, mask)) {
char mname[13];
const char *fname;
+ /*
+ * Ensure we can push the original name as UCS2. If
+ * not, then just don't return this name.
+ */
+ NTSTATUS status;
+ size_t ret_len = 0;
+ size_t len = (strlen(dname) + 2) * 4; /* Allow enough space. */
+ uint8_t *tmp = talloc_array(talloc_tos(),
+ uint8,
+ len);
+
+ status = srvstr_push(NULL,
+ FLAGS2_UNICODE_STRINGS,
+ tmp,
+ dname,
+ len,
+ STR_TERMINATE,
+ &ret_len);
+
+ TALLOC_FREE(tmp);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
if (!mangle_is_8_3(dname, false, conn->params)) {
bool ok = name_to_8_3(dname, mname, false,
const char *mask,
uint32_t dirtype,
char **_fname,
- SMB_OFF_T *_size,
+ off_t *_size,
uint32_t *_mode,
struct timespec *_date,
bool check_descend,
return True;
}
- return can_access_file_acl(conn, smb_fname, FILE_READ_DATA);
+ return NT_STATUS_IS_OK(smbd_check_access_rights(conn,
+ smb_fname,
+ false,
+ FILE_READ_DATA));
}
/*******************************************************************
bool is_visible_file(connection_struct *conn, const char *dir_path,
const char *name, SMB_STRUCT_STAT *pst, bool use_veto)
{
- bool hide_unreadable = lp_hideunreadable(SNUM(conn));
- bool hide_unwriteable = lp_hideunwriteable_files(SNUM(conn));
+ bool hide_unreadable = lp_hide_unreadable(SNUM(conn));
+ bool hide_unwriteable = lp_hide_unwriteable_files(SNUM(conn));
bool hide_special = lp_hide_special_files(SNUM(conn));
char *entry = NULL;
struct smb_filename *smb_fname_base = NULL;
- NTSTATUS status;
bool ret = false;
if ((strcmp(".",name) == 0) || (strcmp("..",name) == 0)) {
}
/* Create an smb_filename with stream_name == NULL. */
- status = create_synthetic_smb_fname(talloc_tos(), entry, NULL,
- pst, &smb_fname_base);
- if (!NT_STATUS_IS_OK(status)) {
+ smb_fname_base = synthetic_smb_fname(talloc_tos(), entry, NULL,
+ pst);
+ if (smb_fname_base == NULL) {
ret = false;
goto out;
}
static int smb_Dir_destructor(struct smb_Dir *dirp)
{
- if (dirp->dir) {
+ if (dirp->dir != NULL) {
SMB_VFS_CLOSEDIR(dirp->conn,dirp->dir);
+ if (dirp->fsp != NULL) {
+ /*
+ * The SMB_VFS_CLOSEDIR above
+ * closes the underlying fd inside
+ * dirp->fsp.
+ */
+ dirp->fsp->fh->fd = -1;
+ if (dirp->fsp->dptr != NULL) {
+ SMB_ASSERT(dirp->fsp->dptr->dir_hnd == dirp);
+ dirp->fsp->dptr->dir_hnd = NULL;
+ }
+ dirp->fsp = NULL;
+ }
}
- if (dirp->conn->sconn) {
- dirp->conn->sconn->smb1.searches.dirhandles_open--;
+ if (dirp->conn->sconn && !dirp->conn->sconn->using_smb2) {
+ dirp->conn->sconn->searches.dirhandles_open--;
}
return 0;
}
********************************************************************/
struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn,
- const char *name, const char *mask, uint32 attr)
+ const char *name,
+ const char *mask,
+ uint32 attr)
{
- struct smb_Dir *dirp = TALLOC_ZERO_P(mem_ctx, struct smb_Dir);
+ struct smb_Dir *dirp = talloc_zero(mem_ctx, struct smb_Dir);
struct smbd_server_connection *sconn = conn->sconn;
if (!dirp) {
goto fail;
}
- if (sconn) {
- sconn->smb1.searches.dirhandles_open++;
+ if (sconn && !sconn->using_smb2) {
+ sconn->searches.dirhandles_open++;
}
talloc_set_destructor(dirp, smb_Dir_destructor);
return NULL;
}
+/*******************************************************************
+ Open a directory from an fsp.
+********************************************************************/
+
+static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
+ files_struct *fsp,
+ const char *mask,
+ uint32 attr)
+{
+ struct smb_Dir *dirp = talloc_zero(mem_ctx, struct smb_Dir);
+ struct smbd_server_connection *sconn = conn->sconn;
+
+ if (!dirp) {
+ return NULL;
+ }
+
+ dirp->conn = conn;
+ dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn));
+
+ dirp->dir_path = talloc_strdup(dirp, fsp->fsp_name->base_name);
+ if (!dirp->dir_path) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ if (sconn && !sconn->using_smb2) {
+ sconn->searches.dirhandles_open++;
+ }
+ talloc_set_destructor(dirp, smb_Dir_destructor);
+
+ if (fsp->is_directory && fsp->fh->fd != -1) {
+ dirp->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr);
+ if (dirp->dir != NULL) {
+ dirp->fsp = fsp;
+ } else {
+ DEBUG(10,("OpenDir_fsp: SMB_VFS_FDOPENDIR on %s returned "
+ "NULL (%s)\n",
+ dirp->dir_path,
+ strerror(errno)));
+ if (errno != ENOSYS) {
+ return NULL;
+ }
+ }
+ }
+
+ if (dirp->dir == NULL) {
+ /* FDOPENDIR didn't work. Use OPENDIR instead. */
+ dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr);
+ }
+
+ if (!dirp->dir) {
+ DEBUG(5,("OpenDir_fsp: Can't open %s. %s\n", dirp->dir_path,
+ strerror(errno) ));
+ goto fail;
+ }
+
+ return dirp;
+
+ fail:
+ TALLOC_FREE(dirp);
+ return NULL;
+}
+
+
/*******************************************************************
Read from a directory.
Return directory entry, current offset, and optional stat information.
Add an entry into the dcache.
********************************************************************/
-void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset)
+static void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset)
{
struct name_cache_entry *e;
}
if (dirp->name_cache == NULL) {
- dirp->name_cache = TALLOC_ZERO_ARRAY(
+ dirp->name_cache = talloc_zero_array(
dirp, struct name_cache_entry, dirp->name_cache_size);
if (dirp->name_cache == NULL) {
return False;
}
+struct files_below_forall_state {
+ char *dirpath;
+ size_t dirpath_len;
+ int (*fn)(struct file_id fid, const struct share_mode_data *data,
+ void *private_data);
+ void *private_data;
+};
+
+static int files_below_forall_fn(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data)
+{
+ struct files_below_forall_state *state = private_data;
+ char tmpbuf[PATH_MAX];
+ char *fullpath, *to_free;
+ size_t len;
+
+ len = full_path_tos(data->servicepath, data->base_name,
+ tmpbuf, sizeof(tmpbuf),
+ &fullpath, &to_free);
+ if (len == -1) {
+ return 0;
+ }
+ if (state->dirpath_len >= len) {
+ /*
+ * Filter files above dirpath
+ */
+ return 0;
+ }
+ if (fullpath[state->dirpath_len] != '/') {
+ /*
+ * Filter file that don't have a path separator at the end of
+ * dirpath's length
+ */
+ return 0;
+ }
+
+ if (memcmp(state->dirpath, fullpath, len) != 0) {
+ /*
+ * Not a parent
+ */
+ return 0;
+ }
+
+ return state->fn(fid, data, private_data);
+}
+
+static int files_below_forall(connection_struct *conn,
+ const struct smb_filename *dir_name,
+ int (*fn)(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data),
+ void *private_data)
+{
+ struct files_below_forall_state state = {};
+ int ret;
+ char tmpbuf[PATH_MAX];
+ char *to_free;
+
+ state.dirpath_len = full_path_tos(conn->connectpath,
+ dir_name->base_name,
+ tmpbuf, sizeof(tmpbuf),
+ &state.dirpath, &to_free);
+ if (state.dirpath_len == -1) {
+ return -1;
+
+ }
+
+ ret = share_mode_forall(files_below_forall_fn, &state);
+ TALLOC_FREE(to_free);
+ return ret;
+}
+
+struct have_file_open_below_state {
+ bool found_one;
+};
+
+static int have_file_open_below_fn(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data)
+{
+ struct have_file_open_below_state *state = private_data;
+ state->found_one = true;
+ return 1;
+}
+
+static bool have_file_open_below(connection_struct *conn,
+ const struct smb_filename *name)
+{
+ struct have_file_open_below_state state = {};
+ int ret;
+
+ if (!VALID_STAT(name->st)) {
+ return false;
+ }
+ if (!S_ISDIR(name->st.st_ex_mode)) {
+ return false;
+ }
+
+ ret = files_below_forall(conn, name, have_file_open_below_fn, &state);
+ if (ret == -1) {
+ return false;
+ }
+
+ return state.found_one;
+}
+
/*****************************************************************
Is this directory empty ?
*****************************************************************/
-NTSTATUS can_delete_directory(struct connection_struct *conn,
- const char *dirname)
+NTSTATUS can_delete_directory_fsp(files_struct *fsp)
{
NTSTATUS status = NT_STATUS_OK;
long dirpos = 0;
const char *dname = NULL;
+ const char *dirname = fsp->fsp_name->base_name;
char *talloced = NULL;
SMB_STRUCT_STAT st;
- struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn, dirname,
- NULL, 0);
+ struct connection_struct *conn = fsp->conn;
+ struct smb_Dir *dir_hnd = OpenDir_fsp(talloc_tos(),
+ conn,
+ fsp,
+ NULL,
+ 0);
if (!dir_hnd) {
return map_nt_error_from_unix(errno);
continue;
}
- DEBUG(10,("can_delete_directory: got name %s - can't delete\n",
+ DEBUG(10,("got name %s - can't delete\n",
dname ));
status = NT_STATUS_DIRECTORY_NOT_EMPTY;
break;
TALLOC_FREE(talloced);
TALLOC_FREE(dir_hnd);
- return status;
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!lp_posix_pathnames() &&
+ lp_strict_rename(SNUM(conn)) &&
+ have_file_open_below(fsp->conn, fsp->fsp_name))
+ {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
}