connection_struct *conn;
DIR *dir;
long offset;
- char *dir_path;
+ struct smb_filename *dir_smb_fname;
size_t name_cache_size;
struct name_cache_entry *name_cache;
unsigned int name_cache_index;
struct dptr_struct {
struct dptr_struct *next, *prev;
int dnum;
- uint16 spid;
+ uint16_t spid;
struct connection_struct *conn;
struct smb_Dir *dir_hnd;
bool expect_close;
char *wcard;
- uint32 attr;
- char *path;
+ uint32_t attr;
+ struct smb_filename *smb_dname;
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. */
static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
files_struct *fsp,
const char *mask,
- uint32 attr);
+ uint32_t attr);
-static void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset);
+static void DirCacheAdd(struct smb_Dir *dir_hnd, const char *name, long offset);
+
+static int smb_Dir_destructor(struct smb_Dir *dir_hnd);
#define INVALID_DPTR_KEY (-3)
return true;
}
-/****************************************************************************
- Idle a dptr - the directory is closed but the control info is kept.
-****************************************************************************/
-
-static void dptr_idle(struct dptr_struct *dptr)
-{
- 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;
- }
-}
-
-/****************************************************************************
- Idle the oldest dptr.
-****************************************************************************/
-
-static void dptr_idleoldest(struct smbd_server_connection *sconn)
-{
- struct dptr_struct *dptr;
-
- /*
- * Go to the end of the list.
- */
- dptr = DLIST_TAIL(sconn->searches.dirptrs);
-
- if(!dptr) {
- DEBUG(0,("No dptrs available to idle ?\n"));
- return;
- }
-
- /*
- * Idle the oldest pointer.
- */
-
- for(; dptr; dptr = DLIST_PREV(dptr)) {
- if (dptr->dir_hnd) {
- dptr_idle(dptr);
- return;
- }
- }
-}
-
/****************************************************************************
Get the struct dptr_struct for a dir index.
****************************************************************************/
static struct dptr_struct *dptr_get(struct smbd_server_connection *sconn,
- int key, bool forclose)
+ int key)
{
struct dptr_struct *dptr;
- for(dptr = sconn->searches.dirptrs; dptr; dptr = dptr->next) {
- if(dptr->dnum == key) {
- if (!forclose && !dptr->dir_hnd) {
- 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(
- NULL, dptr->conn, dptr->path,
- dptr->wcard, dptr->attr))) {
- DEBUG(4,("dptr_get: Failed to open %s (%s)\n",dptr->path,
- strerror(errno)));
- return NULL;
- }
- }
- DLIST_PROMOTE(sconn->searches.dirptrs,dptr);
- return dptr;
+ for (dptr = sconn->searches.dirptrs; dptr != NULL; dptr = dptr->next) {
+ if(dptr->dnum != key) {
+ continue;
}
+ DLIST_PROMOTE(sconn->searches.dirptrs, dptr);
+ return dptr;
}
return(NULL);
}
const char *dptr_path(struct smbd_server_connection *sconn, int key)
{
- struct dptr_struct *dptr = dptr_get(sconn, key, false);
+ struct dptr_struct *dptr = dptr_get(sconn, key);
if (dptr)
- return(dptr->path);
+ return(dptr->smb_dname->base_name);
return(NULL);
}
const char *dptr_wcard(struct smbd_server_connection *sconn, int key)
{
- struct dptr_struct *dptr = dptr_get(sconn, key, false);
+ struct dptr_struct *dptr = dptr_get(sconn, key);
if (dptr)
return(dptr->wcard);
return(NULL);
Get the dir attrib for a dir index.
****************************************************************************/
-uint16 dptr_attr(struct smbd_server_connection *sconn, int key)
+uint16_t dptr_attr(struct smbd_server_connection *sconn, int key)
{
- struct dptr_struct *dptr = dptr_get(sconn, key, false);
+ struct dptr_struct *dptr = dptr_get(sconn, key);
if (dptr)
return(dptr->attr);
return(0);
}
-/****************************************************************************
- Close a dptr (internal func).
-****************************************************************************/
-
-static void dptr_close_internal(struct dptr_struct *dptr)
-{
- struct smbd_server_connection *sconn = dptr->conn->sconn;
-
- DEBUG(4,("closing dptr key %d\n",dptr->dnum));
-
- if (sconn == NULL) {
- goto done;
- }
-
- 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->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->searches.dptr_bmap, dptr->dnum - 1);
-
-done:
- TALLOC_FREE(dptr->dir_hnd);
- TALLOC_FREE(dptr);
-}
-
-/****************************************************************************
- Close a dptr given a key.
-****************************************************************************/
-
-void dptr_close(struct smbd_server_connection *sconn, int *key)
-{
- struct dptr_struct *dptr;
-
- if(*key == INVALID_DPTR_KEY)
- return;
-
- /* OS/2 seems to use -1 to indicate "close all directories" */
- if (*key == -1) {
- struct dptr_struct *next;
- for(dptr = sconn->searches.dirptrs; dptr; dptr = next) {
- next = dptr->next;
- dptr_close_internal(dptr);
- }
- *key = INVALID_DPTR_KEY;
- return;
- }
-
- dptr = dptr_get(sconn, *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.
****************************************************************************/
for(dptr = sconn->searches.dirptrs; dptr; dptr = next) {
next = dptr->next;
if (dptr->conn == conn) {
- dptr_close_internal(dptr);
- }
- }
-}
-
-/****************************************************************************
- Idle all dptrs for a cnum.
-****************************************************************************/
-
-void dptr_idlecnum(connection_struct *conn)
-{
- struct dptr_struct *dptr;
- struct smbd_server_connection *sconn = conn->sconn;
-
- if (sconn == NULL) {
- return;
- }
-
- for(dptr = sconn->searches.dirptrs; dptr; dptr = dptr->next) {
- if (dptr->conn == conn && dptr->dir_hnd) {
- dptr_idle(dptr);
- }
- }
-}
-
-/****************************************************************************
- Close a dptr that matches a given path, only if it matches the spid also.
-****************************************************************************/
-
-void dptr_closepath(struct smbd_server_connection *sconn,
- char *path,uint16 spid)
-{
- struct dptr_struct *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);
- }
-}
-
-/****************************************************************************
- 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(struct smbd_server_connection *sconn,
- bool old)
-{
- struct dptr_struct *dptr;
-
- /*
- * Go to the end of the list.
- */
- for(dptr = sconn->searches.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 = DLIST_PREV(dptr)) {
- if ((old && (dptr->dnum < 256) && !dptr->expect_close) ||
- (!old && (dptr->dnum > 255))) {
- dptr_close_internal(dptr);
- return;
+ files_struct *fsp = dptr->dir_hnd->fsp;
+ close_file(NULL, fsp, NORMAL_CLOSE);
+ fsp = NULL;
}
}
}
-/****************************************************************************
- 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
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)
+ bool old_handle,
+ bool expect_close,
+ uint16_t spid,
+ const char *wcard,
+ bool wcard_has_wild,
+ uint32_t attr,
+ struct dptr_struct **dptr_ret)
{
struct smbd_server_connection *sconn = conn->sconn;
struct dptr_struct *dptr = NULL;
struct smb_Dir *dir_hnd;
- if (fsp && fsp->is_directory && fsp->fh->fd != -1) {
- path = fsp->fsp_name->base_name;
- }
-
- DEBUG(5,("dptr_create dir=%s\n", path));
+ DBG_INFO("dir=%s\n", fsp_str_dbg(fsp));
if (sconn == NULL) {
DEBUG(0,("dptr_create: called with fake connection_struct\n"));
return NT_STATUS_INVALID_PARAMETER;
}
- 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);
- }
+ if (!(fsp->access_mask & SEC_DIR_LIST)) {
+ DBG_INFO("dptr_create: directory %s "
+ "not open for LIST access\n",
+ fsp_str_dbg(fsp));
+ return NT_STATUS_ACCESS_DENIED;
}
-
+ dir_hnd = OpenDir_fsp(NULL, conn, fsp, wcard, attr);
if (!dir_hnd) {
return map_nt_error_from_unix(errno);
}
- if (sconn->searches.dirhandles_open >= MAX_OPEN_DIRECTORIES) {
- dptr_idleoldest(sconn);
- }
-
dptr = talloc_zero(NULL, struct dptr_struct);
if(!dptr) {
DEBUG(0,("talloc fail in dptr_create.\n"));
return NT_STATUS_NO_MEMORY;
}
- dptr->path = talloc_strdup(dptr, path);
- if (!dptr->path) {
+ dptr->smb_dname = cp_smb_filename(dptr, fsp->fsp_name);
+ if (dptr->smb_dname == NULL) {
TALLOC_FREE(dptr);
TALLOC_FREE(dir_hnd);
return NT_STATUS_NO_MEMORY;
TALLOC_FREE(dir_hnd);
return NT_STATUS_NO_MEMORY;
}
- if (lp_posix_pathnames() || (wcard[0] == '.' && wcard[1] == 0)) {
+ if ((req != NULL && req->posix_pathnames) ||
+ (wcard[0] == '.' && wcard[1] == 0)) {
dptr->has_wild = True;
} else {
dptr->has_wild = wcard_has_wild;
dptr->dnum = bitmap_find(sconn->searches.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(sconn, true);
-
- /* Now try again... */
- 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));
- TALLOC_FREE(dptr);
- TALLOC_FREE(dir_hnd);
- return NT_STATUS_TOO_MANY_OPENED_FILES;
- }
+ DBG_ERR("returned %d: Error - all old "
+ "dirptrs in use ?\n",
+ dptr->dnum);
+ TALLOC_FREE(dptr);
+ TALLOC_FREE(dir_hnd);
+ return NT_STATUS_TOO_MANY_OPENED_FILES;
}
} else {
dptr->dnum = bitmap_find(sconn->searches.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(sconn, false);
-
- /* Now try again... */
- 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));
- TALLOC_FREE(dptr);
- TALLOC_FREE(dir_hnd);
- return NT_STATUS_TOO_MANY_OPENED_FILES;
- }
+ DBG_ERR("returned %d: Error - all new "
+ "dirptrs in use ?\n",
+ dptr->dnum);
+ TALLOC_FREE(dptr);
+ TALLOC_FREE(dir_hnd);
+ return NT_STATUS_TOO_MANY_OPENED_FILES;
}
}
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));
+ DBG_INFO("creating new dirptr [%d] for path [%s], expect_close = %d\n",
+ dptr->dnum, fsp_str_dbg(fsp), expect_close);
*dptr_ret = dptr;
void dptr_CloseDir(files_struct *fsp)
{
- if (fsp->dptr) {
+ struct smbd_server_connection *sconn = NULL;
+
+ if (fsp->dptr == NULL) {
+ return;
+ }
+ sconn = fsp->dptr->conn->sconn;
+
+ /*
+ * The destructor for the struct smb_Dir (fsp->dptr->dir_hnd)
+ * now handles all resource deallocation.
+ */
+
+ DBG_INFO("closing dptr key %d\n", fsp->dptr->dnum);
+
+ if (sconn != NULL && !sconn->using_smb2) {
+ DLIST_REMOVE(sconn->searches.dirptrs, fsp->dptr);
+
/*
- * The destructor for the struct smb_Dir
- * (fsp->dptr->dir_hnd) now handles
- * all resource deallocation.
+ * Free the dnum in the bitmap. Remember the dnum value is
+ * always biased by one with respect to the bitmap.
*/
- dptr_close_internal(fsp->dptr);
- fsp->dptr = NULL;
+
+ if (!bitmap_query(sconn->searches.dptr_bmap,
+ fsp->dptr->dnum - 1))
+ {
+ DBG_ERR("closing dnum = %d and bitmap not set !\n",
+ fsp->dptr->dnum);
+ }
+
+ bitmap_clear(sconn->searches.dptr_bmap, fsp->dptr->dnum - 1);
}
+
+ TALLOC_FREE(fsp->dptr->dir_hnd);
+ TALLOC_FREE(fsp->dptr);
}
void dptr_SeekDir(struct dptr_struct *dptr, long offset)
while ((name = ReadDirName(dptr->dir_hnd, poffset, pst, &talloced))
!= NULL) {
- if (is_visible_file(dptr->conn, dptr->path, name, pst, True)) {
+ if (is_visible_file(dptr->conn,
+ dptr->dir_hnd,
+ name,
+ pst,
+ true)) {
*ptalloced = talloced;
return name;
}
dptr->did_stat = true;
/* First check if it should be visible. */
- if (!is_visible_file(dptr->conn, dptr->path, dptr->wcard,
- pst, true))
- {
+ if (!is_visible_file(dptr->conn,
+ dptr->dir_hnd,
+ 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
pathreal = talloc_asprintf(ctx,
"%s/%s",
- dptr->path,
+ dptr->smb_dname->base_name,
dptr->wcard);
if (!pathreal)
return NULL;
* Try case-insensitive stat if the fs has the ability. This avoids
* scanning the whole directory.
*/
- ret = SMB_VFS_GET_REAL_FILENAME(dptr->conn, dptr->path, dptr->wcard,
- ctx, &found_name);
+ ret = SMB_VFS_GET_REAL_FILENAME(dptr->conn,
+ dptr->smb_dname->base_name,
+ dptr->wcard,
+ ctx,
+ &found_name);
if (ret == 0) {
name = found_name;
goto clean;
return SearchDir(dptr->dir_hnd, name, poffset);
}
-/****************************************************************************
- Initialize variables & state data at the beginning of all search SMB requests.
-****************************************************************************/
-void dptr_init_search_op(struct dptr_struct *dptr)
-{
- SMB_VFS_INIT_SEARCH_OP(dptr->conn, dptr->dir_hnd->dir);
-}
-
/****************************************************************************
Map a native directory offset to a 32-bit cookie.
****************************************************************************/
char *buf1,unsigned int key)
{
unsigned char *buf = (unsigned char *)buf1;
- struct dptr_struct *dptr = dptr_get(sconn, key, false);
+ struct dptr_struct *dptr = dptr_get(sconn, key);
uint32_t wire_offset;
if (!dptr) {
DEBUG(1,("filling null dirptr %d\n",key));
}
/****************************************************************************
- Fetch the dir ptr and seek it given the 5 byte server field.
+ Return the associated fsp and seek the dir_hnd on it it given the 5 byte
+ server field.
****************************************************************************/
-struct dptr_struct *dptr_fetch(struct smbd_server_connection *sconn,
+files_struct *dptr_fetch_fsp(struct smbd_server_connection *sconn,
char *buf, int *num)
{
unsigned int key = *(unsigned char *)buf;
- struct dptr_struct *dptr = dptr_get(sconn, key, false);
+ struct dptr_struct *dptr = dptr_get(sconn, key);
uint32_t wire_offset;
long seekoff;
- if (!dptr) {
+ if (dptr == NULL) {
DEBUG(3,("fetched null dirptr %d\n",key));
return(NULL);
}
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);
+ key, dptr->smb_dname->base_name, (int)seekoff));
+ return dptr->dir_hnd->fsp;
}
/****************************************************************************
- Fetch the dir ptr.
+ Fetch the fsp associated with the dptr_num.
****************************************************************************/
-struct dptr_struct *dptr_fetch_lanman2(struct smbd_server_connection *sconn,
+files_struct *dptr_fetch_lanman2_fsp(struct smbd_server_connection *sconn,
int dptr_num)
{
- struct dptr_struct *dptr = dptr_get(sconn, dptr_num, false);
-
- if (!dptr) {
- DEBUG(3,("fetched null dirptr %d\n",dptr_num));
- return(NULL);
+ struct dptr_struct *dptr = dptr_get(sconn, dptr_num);
+ if (dptr == NULL) {
+ return NULL;
}
- DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr->path));
- return(dptr);
+ DBG_NOTICE("fetching dirptr %d for path %s\n",
+ dptr_num,
+ dptr->smb_dname->base_name);
+ return dptr->dir_hnd->fsp;
}
static bool mangle_mask_match(connection_struct *conn,
uint32_t dirtype,
bool dont_descend,
bool ask_sharemode,
+ bool get_dosmode,
bool (*match_fn)(TALLOC_CTX *ctx,
void *private_data,
const char *dname,
bool (*mode_fn)(TALLOC_CTX *ctx,
void *private_data,
struct smb_filename *smb_fname,
+ bool get_dosmode,
uint32_t *_mode),
void *private_data,
char **_fname,
connection_struct *conn = dirptr->conn;
size_t slashlen;
size_t pathlen;
+ const char *dpath = dirptr->smb_dname->base_name;
+ bool dirptr_path_is_dot = ISDOT(dpath);
*_smb_fname = NULL;
*_mode = 0;
- pathlen = strlen(dirptr->path);
- slashlen = ( dirptr->path[pathlen-1] != '/') ? 1 : 0;
+ pathlen = strlen(dpath);
+ slashlen = ( dpath[pathlen-1] != '/') ? 1 : 0;
while (true) {
long cur_offset;
return false;
}
- memcpy(pathreal, dirptr->path, pathlen);
- pathreal[pathlen] = '/';
- memcpy(pathreal + slashlen + pathlen, dname,
- talloc_get_size(dname));
+ /*
+ * We don't want to pass ./xxx to modules below us so don't
+ * add the path if it is just . by itself.
+ */
+ if (dirptr_path_is_dot) {
+ memcpy(pathreal, dname, talloc_get_size(dname));
+ } else {
+ memcpy(pathreal, dpath, pathlen);
+ pathreal[pathlen] = '/';
+ memcpy(pathreal + slashlen + pathlen, dname,
+ talloc_get_size(dname));
+ }
/* Create smb_fname with NULL stream_name. */
smb_fname = (struct smb_filename) {
.base_name = pathreal, .st = sbuf
};
- ok = mode_fn(ctx, private_data, &smb_fname, &mode);
+ ok = mode_fn(ctx, private_data, &smb_fname, get_dosmode, &mode);
if (!ok) {
TALLOC_FREE(dname);
TALLOC_FREE(fname);
continue;
}
- if (ask_sharemode) {
+ if (ask_sharemode && !S_ISDIR(smb_fname.st.st_ex_mode)) {
struct timespec write_time_ts;
struct file_id fileid;
fileid = vfs_file_id_from_sbuf(conn,
&smb_fname.st);
get_file_infos(fileid, 0, NULL, &write_time_ts);
- if (!null_timespec(write_time_ts)) {
+ if (!is_omit_timespec(&write_time_ts)) {
update_stat_ex_mtime(&smb_fname.st,
write_time_ts);
}
mask, smb_fname_str_dbg(&smb_fname),
dname, fname));
- DirCacheAdd(dirptr->dir_hnd, dname, cur_offset);
+ if (!conn->sconn->using_smb2) {
+ /*
+ * The dircache is only needed for SMB1 because SMB1
+ * uses a name for the resume wheras SMB2 always
+ * continues from the next position (unless it's told to
+ * restart or close-and-reopen the listing).
+ */
+ DirCacheAdd(dirptr->dir_hnd, dname, cur_offset);
+ }
TALLOC_FREE(dname);
size_t ret_len = 0;
size_t len = (strlen(dname) + 2) * 4; /* Allow enough space. */
uint8_t *tmp = talloc_array(talloc_tos(),
- uint8,
+ uint8_t,
len);
status = srvstr_push(NULL,
static bool smbd_dirptr_8_3_mode_fn(TALLOC_CTX *ctx,
void *private_data,
struct smb_filename *smb_fname,
+ bool get_dosmode,
uint32_t *_mode)
{
connection_struct *conn = (connection_struct *)private_data;
dirtype,
check_descend,
ask_sharemode,
+ true,
smbd_dirptr_8_3_match_fn,
smbd_dirptr_8_3_mode_fn,
conn,
static bool user_can_read_file(connection_struct *conn,
struct smb_filename *smb_fname)
{
+ NTSTATUS status;
+ uint32_t rejected_share_access = 0;
+ uint32_t rejected_mask = 0;
+ struct security_descriptor *sd = NULL;
+ uint32_t access_mask = FILE_READ_DATA|
+ FILE_READ_EA|
+ FILE_READ_ATTRIBUTES|
+ SEC_STD_READ_CONTROL;
+
/*
* Never hide files from the root user.
* We use (uid_t)0 here not sec_initial_uid()
return True;
}
- return NT_STATUS_IS_OK(smbd_check_access_rights(conn,
- smb_fname,
+ /*
+ * We can't directly use smbd_check_access_rights()
+ * here, as this implicitly grants FILE_READ_ATTRIBUTES
+ * which the Windows access-based-enumeration code
+ * explicitly checks for on the file security descriptor.
+ * See bug:
+ *
+ * https://bugzilla.samba.org/show_bug.cgi?id=10252
+ *
+ * and the smb2.acl2.ACCESSBASED test for details.
+ */
+
+ rejected_share_access = access_mask & ~(conn->share_access);
+ if (rejected_share_access) {
+ DEBUG(10, ("rejected share access 0x%x "
+ "on %s (0x%x)\n",
+ (unsigned int)access_mask,
+ smb_fname_str_dbg(smb_fname),
+ (unsigned int)rejected_share_access ));
+ return false;
+ }
+
+ status = SMB_VFS_GET_NT_ACL(conn,
+ smb_fname,
+ (SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL),
+ talloc_tos(),
+ &sd);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Could not get acl "
+ "on %s: %s\n",
+ smb_fname_str_dbg(smb_fname),
+ nt_errstr(status)));
+ return false;
+ }
+
+ status = se_file_access_check(sd,
+ get_current_nttok(conn),
false,
- FILE_READ_DATA));
+ access_mask,
+ &rejected_mask);
+
+ TALLOC_FREE(sd);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ DEBUG(10,("rejected bits 0x%x read access for %s\n",
+ (unsigned int)rejected_mask,
+ smb_fname_str_dbg(smb_fname) ));
+ return false;
+ }
+ return true;
}
/*******************************************************************
NOTE: A successful return is no guarantee of the file's existence.
********************************************************************/
-bool is_visible_file(connection_struct *conn, const char *dir_path,
- const char *name, SMB_STRUCT_STAT *pst, bool use_veto)
+bool is_visible_file(connection_struct *conn,
+ struct smb_Dir *dir_hnd,
+ const char *name,
+ SMB_STRUCT_STAT *pst,
+ bool use_veto)
{
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));
+ int hide_new_files_timeout = lp_hide_new_files_timeout(SNUM(conn));
char *entry = NULL;
+ struct smb_filename *dir_path = dir_hnd->fsp->fsp_name;
struct smb_filename *smb_fname_base = NULL;
bool ret = false;
return False;
}
- if (hide_unreadable || hide_unwriteable || hide_special) {
- entry = talloc_asprintf(talloc_tos(), "%s/%s", dir_path, name);
+ if (hide_unreadable ||
+ hide_unwriteable ||
+ hide_special ||
+ (hide_new_files_timeout != 0))
+ {
+ entry = talloc_asprintf(talloc_tos(),
+ "%s/%s",
+ dir_path->base_name,
+ name);
if (!entry) {
ret = false;
goto out;
}
/* Create an smb_filename with stream_name == NULL. */
- smb_fname_base = synthetic_smb_fname(talloc_tos(), entry, NULL,
- pst);
+ smb_fname_base = synthetic_smb_fname(talloc_tos(),
+ entry,
+ NULL,
+ pst,
+ 0);
if (smb_fname_base == NULL) {
ret = false;
goto out;
if (SMB_VFS_STAT(conn, smb_fname_base) != 0) {
ret = true;
goto out;
- } else {
- *pst = smb_fname_base->st;
}
+ *pst = smb_fname_base->st;
}
/* Honour _hide unreadable_ option */
goto out;
}
/* Honour _hide unwriteable_ option */
- if (hide_unwriteable && !user_can_write_file(conn,
- smb_fname_base)) {
+ if (hide_unwriteable &&
+ !user_can_write_file(conn,
+ smb_fname_base))
+ {
DEBUG(10,("is_visible_file: file %s is unwritable.\n",
entry ));
ret = false;
ret = false;
goto out;
}
+
+ if (hide_new_files_timeout != 0) {
+
+ double age = timespec_elapsed(
+ &smb_fname_base->st.st_ex_mtime);
+
+ if (age < (double)hide_new_files_timeout) {
+ ret = false;
+ goto out;
+ }
+ }
}
ret = true;
return ret;
}
-static int smb_Dir_destructor(struct smb_Dir *dirp)
+static int smb_Dir_destructor(struct smb_Dir *dir_hnd)
{
- 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->using_smb2) {
- dirp->conn->sconn->searches.dirhandles_open--;
+ files_struct *fsp = dir_hnd->fsp;
+
+ SMB_VFS_CLOSEDIR(dir_hnd->conn, dir_hnd->dir);
+ fsp->fh->fd = -1;
+ if (fsp->dptr != NULL) {
+ SMB_ASSERT(fsp->dptr->dir_hnd == dir_hnd);
+ fsp->dptr->dir_hnd = NULL;
}
+ dir_hnd->fsp = NULL;
return 0;
}
Open a directory.
********************************************************************/
-struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn,
- const char *name,
- const char *mask,
- uint32 attr)
+static int smb_Dir_OpenDir_destructor(struct smb_Dir *dir_hnd)
{
- struct smb_Dir *dirp = talloc_zero(mem_ctx, struct smb_Dir);
- struct smbd_server_connection *sconn = conn->sconn;
+ files_struct *fsp = dir_hnd->fsp;
- if (!dirp) {
- return NULL;
- }
-
- dirp->conn = conn;
- dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn));
+ smb_Dir_destructor(dir_hnd);
+ file_free(NULL, fsp);
+ return 0;
+}
- dirp->dir_path = talloc_strdup(dirp, name);
- if (!dirp->dir_path) {
- errno = ENOMEM;
- goto fail;
- }
+struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx,
+ connection_struct *conn,
+ const struct smb_filename *smb_dname,
+ const char *mask,
+ uint32_t attr)
+{
+ struct files_struct *fsp = NULL;
+ struct smb_Dir *dir_hnd = NULL;
+ NTSTATUS status;
- if (sconn && !sconn->using_smb2) {
- sconn->searches.dirhandles_open++;
+ status = open_internal_dirfsp_at(conn, conn->cwd_fsp, smb_dname, &fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
}
- talloc_set_destructor(dirp, smb_Dir_destructor);
- 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;
+ dir_hnd = OpenDir_fsp(mem_ctx, conn, fsp, mask, attr);
+ if (dir_hnd == NULL) {
+ return NULL;
}
- return dirp;
-
- fail:
- TALLOC_FREE(dirp);
- return NULL;
+ /*
+ * This overwrites the destructor set by smb_Dir_OpenDir_destructor(),
+ * but smb_Dir_OpenDir_destructor() calls the OpenDir_fsp() destructor.
+ */
+ talloc_set_destructor(dir_hnd, smb_Dir_OpenDir_destructor);
+ return dir_hnd;
}
/*******************************************************************
static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
files_struct *fsp,
const char *mask,
- uint32 attr)
+ uint32_t attr)
{
- struct smb_Dir *dirp = talloc_zero(mem_ctx, struct smb_Dir);
- struct smbd_server_connection *sconn = conn->sconn;
+ struct smb_Dir *dir_hnd = talloc_zero(mem_ctx, struct smb_Dir);
- if (!dirp) {
- return NULL;
+ if (!dir_hnd) {
+ goto fail;
}
- 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;
+ if (!fsp->fsp_flags.is_directory) {
+ errno = EBADF;
goto fail;
}
- if (sconn && !sconn->using_smb2) {
- sconn->searches.dirhandles_open++;
+ if (fsp->fh->fd == -1) {
+ errno = EBADF;
+ goto fail;
}
- 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;
- }
- }
+ dir_hnd->conn = conn;
+
+ if (!conn->sconn->using_smb2) {
+ /*
+ * The dircache is only needed for SMB1 because SMB1 uses a name
+ * for the resume wheras SMB2 always continues from the next
+ * position (unless it's told to restart or close-and-reopen the
+ * listing).
+ */
+ dir_hnd->name_cache_size =
+ lp_directory_name_cache_size(SNUM(conn));
}
- if (dirp->dir == NULL) {
- /* FDOPENDIR didn't work. Use OPENDIR instead. */
- dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr);
+ dir_hnd->dir_smb_fname = cp_smb_filename(dir_hnd, fsp->fsp_name);
+ if (!dir_hnd->dir_smb_fname) {
+ errno = ENOMEM;
+ goto fail;
}
- if (!dirp->dir) {
- DEBUG(5,("OpenDir_fsp: Can't open %s. %s\n", dirp->dir_path,
- strerror(errno) ));
+ dir_hnd->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr);
+ if (dir_hnd->dir == NULL) {
goto fail;
}
+ dir_hnd->fsp = fsp;
- return dirp;
+ talloc_set_destructor(dir_hnd, smb_Dir_destructor);
+
+ return dir_hnd;
fail:
- TALLOC_FREE(dirp);
+ TALLOC_FREE(dir_hnd);
return NULL;
}
Don't check for veto or invisible files.
********************************************************************/
-const char *ReadDirName(struct smb_Dir *dirp, long *poffset,
+const char *ReadDirName(struct smb_Dir *dir_hnd, long *poffset,
SMB_STRUCT_STAT *sbuf, char **ptalloced)
{
const char *n;
char *talloced = NULL;
- connection_struct *conn = dirp->conn;
+ connection_struct *conn = dir_hnd->conn;
/* 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))
+ (*poffset == DOT_DOT_DIRECTORY_OFFSET)) && (dir_hnd->file_number < 2))
{
- if (dirp->file_number == 0) {
+ if (dir_hnd->file_number == 0) {
n = ".";
- *poffset = dirp->offset = START_OF_DIRECTORY_OFFSET;
+ *poffset = dir_hnd->offset = START_OF_DIRECTORY_OFFSET;
} else {
n = "..";
- *poffset = dirp->offset = DOT_DOT_DIRECTORY_OFFSET;
+ *poffset = dir_hnd->offset = DOT_DOT_DIRECTORY_OFFSET;
}
- dirp->file_number++;
+ dir_hnd->file_number++;
*ptalloced = NULL;
return n;
}
if (*poffset == END_OF_DIRECTORY_OFFSET) {
- *poffset = dirp->offset = END_OF_DIRECTORY_OFFSET;
+ *poffset = dir_hnd->offset = END_OF_DIRECTORY_OFFSET;
return NULL;
}
/* A real offset, seek to it. */
- SeekDir(dirp, *poffset);
+ SeekDir(dir_hnd, *poffset);
- while ((n = vfs_readdirname(conn, dirp->dir, sbuf, &talloced))) {
+ while ((n = vfs_readdirname(conn, dir_hnd->dir, sbuf, &talloced))) {
/* 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);
+ *poffset = dir_hnd->offset = SMB_VFS_TELLDIR(conn, dir_hnd->dir);
*ptalloced = talloced;
- dirp->file_number++;
+ dir_hnd->file_number++;
return n;
}
- *poffset = dirp->offset = END_OF_DIRECTORY_OFFSET;
+ *poffset = dir_hnd->offset = END_OF_DIRECTORY_OFFSET;
*ptalloced = NULL;
return NULL;
}
Rewind to the start.
********************************************************************/
-void RewindDir(struct smb_Dir *dirp, long *poffset)
+void RewindDir(struct smb_Dir *dir_hnd, long *poffset)
{
- SMB_VFS_REWINDDIR(dirp->conn, dirp->dir);
- dirp->file_number = 0;
- dirp->offset = START_OF_DIRECTORY_OFFSET;
+ SMB_VFS_REWINDDIR(dir_hnd->conn, dir_hnd->dir);
+ dir_hnd->file_number = 0;
+ dir_hnd->offset = START_OF_DIRECTORY_OFFSET;
*poffset = START_OF_DIRECTORY_OFFSET;
}
Tell a dir position.
********************************************************************/
-long TellDir(struct smb_Dir *dirp)
+long TellDir(struct smb_Dir *dir_hnd)
{
- return(dirp->offset);
+ return(dir_hnd->offset);
}
/*******************************************************************
Add an entry into the dcache.
********************************************************************/
-static void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset)
+static void DirCacheAdd(struct smb_Dir *dir_hnd, const char *name, long offset)
{
struct name_cache_entry *e;
- if (dirp->name_cache_size == 0) {
+ if (dir_hnd->name_cache_size == 0) {
return;
}
- if (dirp->name_cache == NULL) {
- dirp->name_cache = talloc_zero_array(
- dirp, struct name_cache_entry, dirp->name_cache_size);
+ if (dir_hnd->name_cache == NULL) {
+ dir_hnd->name_cache = talloc_zero_array(dir_hnd,
+ struct name_cache_entry,
+ dir_hnd->name_cache_size);
- if (dirp->name_cache == NULL) {
+ if (dir_hnd->name_cache == NULL) {
return;
}
}
- dirp->name_cache_index = (dirp->name_cache_index+1) %
- dirp->name_cache_size;
- e = &dirp->name_cache[dirp->name_cache_index];
+ dir_hnd->name_cache_index = (dir_hnd->name_cache_index+1) %
+ dir_hnd->name_cache_size;
+ e = &dir_hnd->name_cache[dir_hnd->name_cache_index];
TALLOC_FREE(e->name);
- e->name = talloc_strdup(dirp, name);
+ e->name = talloc_strdup(dir_hnd, name);
e->offset = offset;
}
Don't check for veto or invisible files.
********************************************************************/
-bool SearchDir(struct smb_Dir *dirp, const char *name, long *poffset)
+bool SearchDir(struct smb_Dir *dir_hnd, const char *name, long *poffset)
{
int i;
const char *entry = NULL;
char *talloced = NULL;
- connection_struct *conn = dirp->conn;
+ connection_struct *conn = dir_hnd->conn;
/* Search back in the name cache. */
- 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 (dir_hnd->name_cache_size && dir_hnd->name_cache) {
+ for (i = dir_hnd->name_cache_index; i >= 0; i--) {
+ struct name_cache_entry *e = &dir_hnd->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);
+ SeekDir(dir_hnd, 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];
+ for (i = dir_hnd->name_cache_size - 1;
+ i > dir_hnd->name_cache_index; i--) {
+ struct name_cache_entry *e = &dir_hnd->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);
+ SeekDir(dir_hnd, e->offset);
return True;
}
}
}
/* Not found in the name cache. Rewind directory and start from scratch. */
- SMB_VFS_REWINDDIR(conn, dirp->dir);
- dirp->file_number = 0;
+ SMB_VFS_REWINDDIR(conn, dir_hnd->dir);
+ dir_hnd->file_number = 0;
*poffset = START_OF_DIRECTORY_OFFSET;
- while ((entry = ReadDirName(dirp, poffset, NULL, &talloced))) {
+ while ((entry = ReadDirName(dir_hnd, poffset, NULL, &talloced))) {
if (conn->case_sensitive ? (strcmp(entry, name) == 0) : strequal(entry, name)) {
TALLOC_FREE(talloced);
return True;
/*
* Filter files above dirpath
*/
- return 0;
+ goto out;
}
if (fullpath[state->dirpath_len] != '/') {
/*
* Filter file that don't have a path separator at the end of
* dirpath's length
*/
- return 0;
+ goto out;
}
- if (memcmp(state->dirpath, fullpath, len) != 0) {
+ if (memcmp(state->dirpath, fullpath, state->dirpath_len) != 0) {
/*
* Not a parent
*/
- return 0;
+ goto out;
}
- return state->fn(fid, data, private_data);
+ TALLOC_FREE(to_free);
+ return state->fn(fid, data, state->private_data);
+
+out:
+ TALLOC_FREE(to_free);
+ return 0;
}
static int files_below_forall(connection_struct *conn,
void *private_data),
void *private_data)
{
- struct files_below_forall_state state = {};
+ struct files_below_forall_state state = {
+ .fn = fn,
+ .private_data = private_data,
+ };
int ret;
char tmpbuf[PATH_MAX];
char *to_free;
&state.dirpath, &to_free);
if (state.dirpath_len == -1) {
return -1;
-
}
ret = share_mode_forall(files_below_forall_fn, &state);
return 1;
}
-static bool have_file_open_below(connection_struct *conn,
+bool have_file_open_below(connection_struct *conn,
const struct smb_filename *name)
{
- struct have_file_open_below_state state = {};
+ struct have_file_open_below_state state = {
+ .found_one = false,
+ };
int ret;
if (!VALID_STAT(name->st)) {
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 connection_struct *conn = fsp->conn;
- struct smb_Dir *dir_hnd = OpenDir_fsp(talloc_tos(),
+ struct smb_Dir *dir_hnd = OpenDir(talloc_tos(),
conn,
- fsp,
+ fsp->fsp_name,
NULL,
0);
}
}
- if (!is_visible_file(conn, dirname, dname, &st, True)) {
+ if (!is_visible_file(conn,
+ dir_hnd,
+ dname,
+ &st,
+ True)) {
TALLOC_FREE(talloced);
continue;
}
return status;
}
- if (!lp_posix_pathnames() &&
+ if (!(fsp->posix_flags & FSP_POSIX_FLAGS_RENAME) &&
lp_strict_rename(SNUM(conn)) &&
have_file_open_below(fsp->conn, fsp->fsp_name))
{