From: Steven Danneman Date: Tue, 27 Jan 2009 04:14:32 +0000 (-0800) Subject: s3: OneFS bulk directory enumeration support X-Git-Tag: samba-4.0.0alpha7~380 X-Git-Url: http://git.samba.org/samba.git/?p=ira%2Fwip.git;a=commitdiff_plain;h=11f60a62a1d7633e9a8ec62da18ed9ababa694df;hp=6272f4c2f453c509b8a3893d4c2ac5fc356b348d;ds=sidebyside s3: OneFS bulk directory enumeration support OneFS provides the bulk directory enumeration syscall readdirplus(). This syscall has the same semantics as the NFSv3 READDIRPLUS command, returning a batch of directory entries with prefetched stat information via one syscall. This commit wraps the readdirplus() call in the existing POSIX readdir/seekdir VFS interface. By default a batch of 128 directory entries are optimistically read from the kernel into a global cache, and fed to iterative calls of VFS_OP_READDIR. The global buffers could be avoided in the future by hanging connection specific buffers off the conn struct. Added new parameter "onefs:use readdirplus" which toggles usage of this code on or off. --- diff --git a/source3/Makefile.in b/source3/Makefile.in index 20499532848..6e453c9a862 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -664,7 +664,7 @@ VFS_ACL_XATTR_OBJ = modules/vfs_acl_xattr.o VFS_ACL_TDB_OBJ = modules/vfs_acl_tdb.o VFS_SMB_TRAFFIC_ANALYZER_OBJ = modules/vfs_smb_traffic_analyzer.o VFS_ONEFS_OBJ = modules/vfs_onefs.o modules/onefs_acl.o modules/onefs_system.o \ - modules/onefs_open.o modules/onefs_streams.o + modules/onefs_open.o modules/onefs_streams.o modules/onefs_dir.c PERFCOUNT_ONEFS_OBJ = modules/perfcount_onefs.o PLAINTEXT_AUTH_OBJ = auth/pampass.o auth/pass_check.o diff --git a/source3/include/vfs.h b/source3/include/vfs.h index 99af30b1c5e..f944c899c64 100644 --- a/source3/include/vfs.h +++ b/source3/include/vfs.h @@ -252,7 +252,7 @@ typedef enum _vfs_op_type { SMB_VFS_OP_SYS_ACL_FREE_TEXT, SMB_VFS_OP_SYS_ACL_FREE_ACL, SMB_VFS_OP_SYS_ACL_FREE_QUALIFIER, - + /* EA operations. */ SMB_VFS_OP_GETXATTR, SMB_VFS_OP_LGETXATTR, diff --git a/source3/modules/onefs.h b/source3/modules/onefs.h index c8f19f4b31b..72be682c212 100644 --- a/source3/modules/onefs.h +++ b/source3/modules/onefs.h @@ -41,26 +41,28 @@ enum onefs_acl_wire_format #define PARM_ONEFS_TYPE "onefs" #define PARM_ACL_WIRE_FORMAT "acl wire format" #define PARM_ACL_WIRE_FORMAT_DEFAULT ACL_FORMAT_WINDOWS_SD -#define PARM_SIMPLE_FILE_SHARING_COMPATIBILITY_MODE "simple file sharing compatibility mode" -#define PARM_SIMPLE_FILE_SHARING_COMPATIBILITY_MODE_DEFAULT false +#define PARM_ATIME_NOW "atime now files" +#define PARM_ATIME_STATIC "atime static files" +#define PARM_ATIME_SLOP "atime now slop" #define PARM_CREATOR_OWNER_GETS_FULL_CONTROL "creator owner gets full control" #define PARM_CREATOR_OWNER_GETS_FULL_CONTROL_DEFAULT true +#define PARM_CTIME_NOW "ctime now files" +#define PARM_CTIME_SLOP "ctime now slop" +#define PARM_IGNORE_SACL "ignore sacl" +#define PARM_IGNORE_SACL_DEFAULT false +#define PARM_MTIME_NOW "mtime now files" +#define PARM_MTIME_STATIC "mtime static files" +#define PARM_MTIME_SLOP "mtime now slop" +#define PARM_USE_READDIRPLUS "use readdirplus" +#define PARM_USE_READDIRPLUS_DEFAULT true +#define PARM_SIMPLE_FILE_SHARING_COMPATIBILITY_MODE "simple file sharing compatibility mode" +#define PARM_SIMPLE_FILE_SHARING_COMPATIBILITY_MODE_DEFAULT false #define PARM_UNMAPPABLE_SIDS_DENY_EVERYONE "unmappable sids deny everyone" #define PARM_UNMAPPABLE_SIDS_DENY_EVERYONE_DEFAULT false #define PARM_UNMAPPABLE_SIDS_IGNORE "ignore unmappable sids" #define PARM_UNMAPPABLE_SIDS_IGNORE_DEFAULT false #define PARM_UNMAPPABLE_SIDS_IGNORE_LIST "unmappable sids ignore list" #define PARM_UNMAPPABLE_SIDS_IGNORE_LIST_DEFAULT NULL -#define PARM_IGNORE_SACL "ignore sacl" -#define PARM_IGNORE_SACL_DEFAULT false -#define PARM_ATIME_NOW "atime now files" -#define PARM_ATIME_SLOP "atime now slop" -#define PARM_CTIME_NOW "ctime now files" -#define PARM_CTIME_SLOP "ctime now slop" -#define PARM_MTIME_NOW "mtime now files" -#define PARM_MTIME_SLOP "mtime now slop" -#define PARM_MTIME_STATIC "mtime static files" -#define PARM_ATIME_STATIC "atime static files" #define IS_CTIME_NOW_PATH(conn,cfg,path) ((conn) && is_in_path((path),\ (cfg)->ctime_now_list,(conn)->case_sensitive)) @@ -81,7 +83,6 @@ enum onefs_acl_wire_format #define ONEFS_VFS_CONFIG_FAKETIMESTAMPS 0x00000001 - struct onefs_vfs_config { int32 init_flags; @@ -113,6 +114,25 @@ struct onefs_vfs_config /* * vfs interface handlers */ +SMB_STRUCT_DIR *onefs_opendir(struct vfs_handle_struct *handle, + const char *fname, const char *mask, + uint32 attributes); + +SMB_STRUCT_DIRENT *onefs_readdir(struct vfs_handle_struct *handle, + SMB_STRUCT_DIR *dirp, SMB_STRUCT_STAT *sbuf); + +void onefs_seekdir(struct vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp, + long offset); + +long onefs_telldir(struct vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp); + +void onefs_rewinddir(struct vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp); + +int onefs_closedir(struct vfs_handle_struct *handle, SMB_STRUCT_DIR *dir); + +void onefs_init_search_op(struct vfs_handle_struct *handle, + SMB_STRUCT_DIR *dirp); + NTSTATUS onefs_create_file(vfs_handle_struct *handle, struct smb_request *req, uint16_t root_dir_fid, diff --git a/source3/modules/onefs_dir.c b/source3/modules/onefs_dir.c new file mode 100644 index 00000000000..3c1a8364beb --- /dev/null +++ b/source3/modules/onefs_dir.c @@ -0,0 +1,636 @@ +/* + * Unix SMB/CIFS implementation. + * + * Support for OneFS bulk directory enumeration API + * + * Copyright (C) Steven Danneman, 2009 + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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, see . + */ + +#include "onefs.h" + +#include + +/* The OneFS filesystem provides a readdirplus() syscall, equivalent to the + * NFSv3 PDU, which retrieves bulk directory listings with stat information + * in a single syscall. + * + * This file hides this bulk interface underneath Samba's very POSIX like + * opendir/readdir/telldir VFS interface. This is done to provide a + * significant performance improvement when listing the contents of large + * directories, which also require file meta information. ie a typical + * Windows Explorer request. + */ + +#define RDP_RESUME_KEY_START 0x1 + +#define RDP_BATCH_SIZE 128 +#define RDP_DIRENTRIES_SIZE ((size_t)(RDP_BATCH_SIZE * sizeof(struct dirent))) + +static char *rdp_direntries = NULL; +static SMB_STRUCT_STAT *rdp_stats = NULL; +static uint64_t *rdp_cookies = NULL; + +struct rdp_dir_state { + struct rdp_dir_state *next, *prev; + SMB_STRUCT_DIR *dirp; + char *direntries_cursor; /* cursor to current direntry in the cache */ + size_t stat_count; /* number of entries stored in the cache */ + size_t stat_cursor; /* cursor to current stat in the cache */ + uint64_t resume_cookie; /* last cookie returned from the cache */ + long location; /* absolute location of direnty in DIR */ +}; + +static struct rdp_dir_state *dirstatelist = NULL; + +SMB_STRUCT_DIR *rdp_last_dirp = NULL; + +/** + * Given a DIR pointer, return our internal state. + * + * This function also tells us whether the given DIR is the same as we saw + * during the last call. Because we use a single globally allocated buffer + * for readdirplus entries we must check every call into this API to see if + * it's for the same directory listing, or a new one. If it's the same we can + * maintain our current cached entries, otherwise we must go to the kernel. + * + * @return 0 on success, 1 on failure + */ +static int +rdp_retrieve_dir_state(SMB_STRUCT_DIR *dirp, struct rdp_dir_state **dir_state, + bool *same_as_last) +{ + struct rdp_dir_state *dsp; + + /* Is this directory the same as the last call */ + *same_as_last = (dirp == rdp_last_dirp); + + for(dsp = dirstatelist; dsp; dsp = dsp->next) + if (dsp->dirp == dirp) { + *dir_state = dsp; + return 0; + } + + /* Couldn't find existing dir_state for the given directory + * pointer. */ + return 1; +} + +/** + * Initialize the global readdirplus buffers. + * + * These same buffers are used for all calls into readdirplus. + * + * @return 0 on success, errno value on failure + */ +static int +rdp_init(struct rdp_dir_state *dsp) +{ + /* Unfortunately, there is no good way to free these buffers. If we + * allocated and freed for every DIR handle performance would be + * adversely affected. For now these buffers will be leaked and only + * freed when the smbd process dies. */ + if (!rdp_direntries) { + rdp_direntries = SMB_MALLOC(RDP_DIRENTRIES_SIZE); + if (!rdp_direntries) + return ENOMEM; + } + + if (!rdp_stats) { + rdp_stats = + SMB_MALLOC(RDP_BATCH_SIZE * sizeof(SMB_STRUCT_STAT)); + if (!rdp_stats) + return ENOMEM; + } + + if (!rdp_cookies) { + rdp_cookies = SMB_MALLOC(RDP_BATCH_SIZE * sizeof(uint64_t)); + if (!rdp_cookies) + return ENOMEM; + } + + dsp->direntries_cursor = rdp_direntries + RDP_DIRENTRIES_SIZE; + dsp->stat_count = RDP_BATCH_SIZE; + dsp->stat_cursor = RDP_BATCH_SIZE; + dsp->resume_cookie = RDP_RESUME_KEY_START; + dsp->location = 0; + + return 0; +} + +/** + * Call into readdirplus() to refill our global dirent cache. + * + * This function also resets all cursors back to the beginning of the cache. + * All stat buffers are retrieved by following symlinks. + * + * @return number of entries retrieved, -1 on error + */ +static int +rdp_fill_cache(struct rdp_dir_state *dsp) +{ + int nread, dirfd; + + dirfd = dirfd(dsp->dirp); + if (dirfd < 0) { + DEBUG(1, ("Could not retrieve fd for DIR\n")); + return -1; + } + + /* Resize the stat_count to grab as many entries as possible */ + dsp->stat_count = RDP_BATCH_SIZE; + + DEBUG(9, ("Calling readdirplus() with DIR %p, dirfd: %d, " + "resume_cookie 0x%llx, location %u, size_to_read: %zu, " + "direntries_size: %zu, stat_count: %u\n", + dsp->dirp, dirfd, dsp->resume_cookie, dsp->location, + RDP_BATCH_SIZE, RDP_DIRENTRIES_SIZE, dsp->stat_count)); + + nread = readdirplus(dirfd, + RDP_FOLLOW, + &dsp->resume_cookie, + RDP_BATCH_SIZE, + rdp_direntries, + RDP_DIRENTRIES_SIZE, + &dsp->stat_count, + rdp_stats, + rdp_cookies); + if (nread < 0) { + DEBUG(1, ("Error calling readdirplus(): %s\n", + strerror(errno))); + return -1; + } + + DEBUG(9, ("readdirplus() returned %u entries from DIR %p\n", + dsp->stat_count, dsp->dirp)); + + dsp->direntries_cursor = rdp_direntries; + dsp->stat_cursor = 0; + + return nread; +} + +/** + * Open a directory for enumeration. + * + * Create a state struct to track the state of this directory for the life + * of this open. + * + * @param[in] handle vfs handle given in most VFS calls + * @param[in] fname filename of the directory to open + * @param[in] mask unused + * @param[in] attr unused + * + * @return DIR pointer, NULL if directory does not exist, NULL on error + */ +SMB_STRUCT_DIR * +onefs_opendir(vfs_handle_struct *handle, const char *fname, const char *mask, + uint32 attr) +{ + int ret = 0; + SMB_STRUCT_DIR *ret_dirp; + struct rdp_dir_state *dsp = NULL; + + /* Fallback to default system routines if readdirplus is disabled */ + if (!lp_parm_bool(SNUM(handle->conn), PARM_ONEFS_TYPE, + PARM_USE_READDIRPLUS, PARM_USE_READDIRPLUS_DEFAULT)) + { + return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr); + } + + /* Create a struct dir_state */ + dsp = SMB_MALLOC_P(struct rdp_dir_state); + if (!dsp) { + DEBUG(0, ("Error allocating struct rdp_dir_state.\n")); + return NULL; + } + + /* Open the directory */ + ret_dirp = SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr); + if (!ret_dirp) { + DEBUG(3, ("Unable to open directory: %s\n", fname)); + return NULL; + } + + /* Initialize the dir_state structure and add it to the list */ + ret = rdp_init(dsp); + if (ret) { + DEBUG(0, ("Error initializing readdirplus() buffers: %s\n", + strerror(ret))); + return NULL; + } + + /* Set the SMB_STRUCT_DIR in the dsp */ + dsp->dirp = ret_dirp; + + DLIST_ADD(dirstatelist, dsp); + + DEBUG(9, ("Opened handle on directory: \"%s\", DIR %p\n", + fname, dsp->dirp)); + + return ret_dirp; +} + +/** + * Retrieve one direntry and optional stat buffer from our readdir cache. + * + * Increment the internal resume cookie, and refresh the cache from the + * kernel if necessary. + * + * @param[in] handle vfs handle given in most VFS calls + * @param[in] dirp system DIR handle to retrieve direntries from + * @param[in/out] sbuf optional stat buffer to fill, this can be NULL + * + * @return dirent structure, NULL if at the end of the directory, NULL on error + */ +SMB_STRUCT_DIRENT * +onefs_readdir(vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp, + SMB_STRUCT_STAT *sbuf) +{ + struct rdp_dir_state *dsp = NULL; + SMB_STRUCT_DIRENT *ret_direntp; + bool same_as_last; + int ret = -1; + + /* Set stat invalid in-case we error out */ + if (sbuf) + SET_STAT_INVALID(*sbuf); + + /* Fallback to default system routines if readdirplus is disabled */ + if (!lp_parm_bool(SNUM(handle->conn), PARM_ONEFS_TYPE, + PARM_USE_READDIRPLUS, PARM_USE_READDIRPLUS_DEFAULT)) + { + return sys_readdir(dirp); + } + + /* Retrieve state based off DIR handle */ + ret = rdp_retrieve_dir_state(dirp, &dsp, &same_as_last); + if (ret) { + DEBUG(1, ("Could not retrieve dir_state struct for " + "SMB_STRUCT_DIR pointer.\n")); + ret_direntp = NULL; + goto end; + } + + /* DIR is the same, current buffer and cursors are valid. + * Grab the next direntry from our cache. */ + if (same_as_last) { + if ((dsp->direntries_cursor >= + rdp_direntries + RDP_DIRENTRIES_SIZE) || + (dsp->stat_cursor == dsp->stat_count)) + { + /* Cache is empty, refill from kernel */ + ret = rdp_fill_cache(dsp); + if (ret <= 0) { + ret_direntp = NULL; + goto end; + } + } + } else { + /* DIR is different from last call, reset all buffers and + * cursors, and refill the global cache from the new DIR */ + ret = rdp_fill_cache(dsp); + if (ret <= 0) { + ret_direntp = NULL; + goto end; + } + DEBUG(8, ("Switched global rdp cache to new DIR entry.\n")); + } + + /* Return next entry from cache */ + ret_direntp = ((SMB_STRUCT_DIRENT *)dsp->direntries_cursor); + dsp->direntries_cursor += + ((SMB_STRUCT_DIRENT *)dsp->direntries_cursor)->d_reclen; + if (sbuf) { + *sbuf = rdp_stats[dsp->stat_cursor]; + /* readdirplus() sets st_ino field to 0, if it was + * unable to retrieve stat information for that + * particular directory entry. */ + if (sbuf->st_ino == 0) + SET_STAT_INVALID(*sbuf); + } + + DEBUG(9, ("Read from DIR %p, direntry: \"%s\", location: %ld, " + "resume cookie: 0x%llx, cache cursor: %zu, cache count: %zu\n", + dsp->dirp, ret_direntp->d_name, dsp->location, + dsp->resume_cookie, dsp->stat_cursor, dsp->stat_count)); + + dsp->resume_cookie = rdp_cookies[dsp->stat_cursor]; + dsp->stat_cursor++; + dsp->location++; + + /* FALLTHROUGH */ +end: + /* Set rdp_last_dirp at the end of every VFS call where the cache was + * reloaded */ + rdp_last_dirp = dirp; + return ret_direntp; +} + +/** + * Set the location of the next direntry to be read via onefs_readdir(). + * + * This function should only pass in locations retrieved from onefs_telldir(). + * + * Ideally the seek point will still be in the readdirplus cache, and we'll + * just update our cursors. If the seek location is outside of the current + * cache we must do an expensive re-enumeration of the entire directory up + * to the offset. + * + * @param[in] handle vfs handle given in most VFS calls + * @param[in] dirp system DIR handle to set offset on + * @param[in] offset from the start of the directory where the next read + * will take place + * + * @return no return value + */ +void +onefs_seekdir(vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp, long offset) +{ + struct rdp_dir_state *dsp = NULL; + bool same_as_last; + bool outside_cache = false; + int ret = -1, i; + + /* Fallback to default system routines if readdirplus is disabled */ + if (!lp_parm_bool(SNUM(handle->conn), PARM_ONEFS_TYPE, + PARM_USE_READDIRPLUS, PARM_USE_READDIRPLUS_DEFAULT)) + { + return sys_seekdir(dirp, offset); + } + + /* Validate inputs */ + if (offset < 0) { + DEBUG(1, ("Invalid offset %ld passed.\n", offset)); + return; + } + + /* Retrieve state based off DIR handle */ + ret = rdp_retrieve_dir_state(dirp, &dsp, &same_as_last); + if (ret) { + DEBUG(1, ("Could not retrieve dir_state struct for " + "SMB_STRUCT_DIR pointer.\n")); + /* XXX: we can't return an error, should we ABORT rather than + * return without actually seeking? */ + return; + } + + /* Short cut if no work needs to be done */ + if (offset == dsp->location) + return; + + /* If DIR is different from last call, reset all buffers and cursors, + * and refill the global cache from the new DIR */ + if (!same_as_last) { + ret = rdp_fill_cache(dsp); + if (ret <= 0) + goto out; + DEBUG(8, ("Switched global rdp cache to new DIR entry.\n")); + } + + /* Check if location is outside the currently cached entries */ + if (offset < dsp->location - dsp->stat_cursor) { + /* offset is before the current cache */ + /* reset to the beginning of the directory */ + ret = rdp_init(dsp); + if (ret) { + DEBUG(0, ("Error initializing readdirplus() buffers: " + "%s\n", strerror(ret))); + goto out; + } + outside_cache = true; + } else if (offset > + dsp->location + (dsp->stat_count - 1 - dsp->stat_cursor)) + { + /* offset is after the current cache + * advance the cookie to the end of the cache */ + dsp->resume_cookie = rdp_cookies[dsp->stat_count - 1]; + outside_cache = true; + } + + if (outside_cache) { + /* start reading from the directory, until we have the + * specified offset in our cache */ + do { + dsp->location += dsp->stat_count - dsp->stat_cursor; + ret = rdp_fill_cache(dsp); + if (ret <= 0) { + DEBUG(1, ("Error seeking to offset outside the " + "cached directory entries. Offset " + "%ld \n", dsp->location)); + goto out; + } + dsp->resume_cookie = rdp_cookies[dsp->stat_count - 1]; + } while (offset >= dsp->location + dsp->stat_count); + } + + /* Location should be within the currently cached entries */ + if (offset < dsp->location && + offset >= dsp->location - dsp->stat_cursor) + { + /* offset is within the current cache, before the cursor. + * update cursors to the new location */ + int new_cursor = dsp->stat_cursor - (dsp->location - offset); + + dsp->direntries_cursor = rdp_direntries; + for (i=0; i < new_cursor; i++) { + dsp->direntries_cursor += + ((SMB_STRUCT_DIRENT *) + dsp->direntries_cursor)->d_reclen; + } + dsp->stat_cursor = new_cursor; + dsp->resume_cookie = rdp_cookies[dsp->stat_cursor]; + dsp->location = offset; + } else if (offset >= dsp->location && + offset <= dsp->location + (dsp->stat_count - 1 - dsp->stat_cursor)) + { + /* offset is within the current cache, at or after the cursor. + * update cursors to the new location */ + int add_to_cursor = offset - dsp->location - 1; + + for (i=0; i < add_to_cursor; i++) { + dsp->direntries_cursor += + ((SMB_STRUCT_DIRENT *) + dsp->direntries_cursor)->d_reclen; + } + dsp->stat_cursor += add_to_cursor; + dsp->resume_cookie = rdp_cookies[dsp->stat_cursor]; + dsp->location = offset; + } + + DEBUG(9, ("Seek DIR %p, location: %ld, cache cursor: %zu\n", + dsp->dirp, dsp->location, dsp->stat_cursor)); + + /* FALLTHROUGH */ +out: + /* Set rdp_last_dirp at the end of every VFS call where the cache was + * reloaded */ + rdp_last_dirp = dirp; + return; +} + +/** + * Returns the location of the next direntry to be read via onefs_readdir(). + * + * This value can be passed into onefs_seekdir(). + * + * @param[in] handle vfs handle given in most VFS calls + * @param[in] dirp system DIR handle to set offset on + * + * @return offset from the start of the directory where the next read + * will take place + */ +long +onefs_telldir(vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp) +{ + struct rdp_dir_state *dsp = NULL; + bool same_as_last; + int ret = -1; + + /* Fallback to default system routines if readdirplus is disabled */ + if (!lp_parm_bool(SNUM(handle->conn), PARM_ONEFS_TYPE, + PARM_USE_READDIRPLUS, PARM_USE_READDIRPLUS_DEFAULT)) + { + return sys_telldir(dirp); + } + + /* Retrieve state based off DIR handle */ + ret = rdp_retrieve_dir_state(dirp, &dsp, &same_as_last); + if (ret) { + DEBUG(1, ("Could not retrieve dir_state struct for " + "SMB_STRUCT_DIR pointer.\n")); + return -1; + } + + DEBUG(9, ("Tell DIR %p, location: %ld, cache cursor: %zu\n", + dsp->dirp, dsp->location, dsp->stat_cursor)); + + return dsp->location; +} + +/** + * Set the next direntry to be read via onefs_readdir() to the beginning of the + * directory. + * + * @param[in] handle vfs handle given in most VFS calls + * @param[in] dirp system DIR handle to set offset on + * + * @return no return value + */ +void +onefs_rewinddir(vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp) +{ + struct rdp_dir_state *dsp = NULL; + bool same_as_last; + int ret = -1; + + /* Fallback to default system routines if readdirplus is disabled */ + if (!lp_parm_bool(SNUM(handle->conn), PARM_ONEFS_TYPE, + PARM_USE_READDIRPLUS, PARM_USE_READDIRPLUS_DEFAULT)) + { + return sys_rewinddir(dirp); + } + + /* Retrieve state based off DIR handle */ + ret = rdp_retrieve_dir_state(dirp, &dsp, &same_as_last); + if (ret) { + DEBUG(1, ("Could not retrieve dir_state struct for " + "SMB_STRUCT_DIR pointer.\n")); + return; + } + + /* Reset location and resume key to beginning */ + ret = rdp_init(dsp); + if (ret) { + DEBUG(0, ("Error re-initializing rdp cursors: %s\n", + strerror(ret))); + return; + } + + DEBUG(9, ("Rewind DIR: %p, to location: %ld\n", dsp->dirp, + dsp->location)); + + return; +} + +/** + * Close DIR pointer and remove all state for that directory open. + * + * @param[in] handle vfs handle given in most VFS calls + * @param[in] dirp system DIR handle to set offset on + * + * @return -1 on failure, setting errno + */ +int +onefs_closedir(vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp) +{ + struct rdp_dir_state *dsp = NULL; + bool same_as_last; + int ret_val = -1; + int ret = -1; + + /* Fallback to default system routines if readdirplus is disabled */ + if (!lp_parm_bool(SNUM(handle->conn), PARM_ONEFS_TYPE, + PARM_USE_READDIRPLUS, PARM_USE_READDIRPLUS_DEFAULT)) + { + return SMB_VFS_NEXT_CLOSEDIR(handle, dirp); + } + + /* Retrieve state based off DIR handle */ + ret = rdp_retrieve_dir_state(dirp, &dsp, &same_as_last); + if (ret) { + DEBUG(1, ("Could not retrieve dir_state struct for " + "SMB_STRUCT_DIR pointer.\n")); + errno = ENOENT; + return -1; + } + + /* Close DIR pointer */ + ret_val = SMB_VFS_NEXT_CLOSEDIR(handle, dsp->dirp); + + DEBUG(9, ("Closed handle on DIR %p\n", dsp->dirp)); + + /* Tear down state struct */ + DLIST_REMOVE(dirstatelist, dsp); + SAFE_FREE(dsp); + + /* Set lastp to NULL, as cache is no longer valid */ + rdp_last_dirp = NULL; + + return ret_val; +} + +/** + * Initialize cache data at the beginning of every SMB search operation + * + * Since filesystem operations, such as delete files or meta data + * updates can occur to files in the directory we're searching + * between FIND_FIRST and FIND_NEXT calls we must refresh the cache + * from the kernel on every new search SMB. + * + * @param[in] handle vfs handle given in most VFS calls + * @param[in] dirp system DIR handle for the current search + * + * @return nothing + */ +void +onefs_init_search_op(vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp) +{ + /* Setting the rdp_last_dirp to NULL will cause the next readdir operation + * to refill the cache. */ + rdp_last_dirp = NULL; + + return; +} diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index cb56690dbb8..4b123ab03ed 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -210,7 +210,6 @@ static void vfswrap_init_search_op(vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp) { /* Default behavior is a NOOP */ - return; } /* File operations */ @@ -1456,7 +1455,7 @@ static vfs_op_tuple vfs_default_ops[] = { SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(vfswrap_closedir), SMB_VFS_OP_CLOSEDIR, SMB_VFS_LAYER_OPAQUE}, - {SMB_VFS_OP(vfswrap_init_search_op), SMB_VFS_OP_INIT_SEARCH_OP, + {SMB_VFS_OP(vfswrap_init_search_op), SMB_VFS_OP_INIT_SEARCH_OP, SMB_VFS_LAYER_OPAQUE}, /* File operations */ diff --git a/source3/modules/vfs_onefs.c b/source3/modules/vfs_onefs.c index 1f11f691a49..af52c713a83 100644 --- a/source3/modules/vfs_onefs.c +++ b/source3/modules/vfs_onefs.c @@ -91,8 +91,9 @@ static int onefs_load_config(struct vfs_handle_struct *handle) if (share_count <= ONEFS_DATA_FASTBUF) pshare_config = share_config; else { - pshare_config = SMB_MALLOC_ARRAY(struct onefs_vfs_config, - share_count); + pshare_config = + SMB_MALLOC_ARRAY(struct onefs_vfs_config, + share_count); if (!pshare_config) { errno = ENOMEM; return -1; @@ -152,8 +153,9 @@ static int onefs_open(vfs_handle_struct *handle, const char *fname, return SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode); } -static uint64_t onefs_get_alloc_size(struct vfs_handle_struct *handle, files_struct *fsp, - const SMB_STRUCT_STAT *sbuf) +static uint64_t onefs_get_alloc_size(struct vfs_handle_struct *handle, + files_struct *fsp, + const SMB_STRUCT_STAT *sbuf) { uint64_t result; @@ -246,8 +248,22 @@ static vfs_op_tuple onefs_ops[] = { SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(onefs_fs_capabilities), SMB_VFS_OP_FS_CAPABILITIES, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_opendir), SMB_VFS_OP_OPENDIR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_readdir), SMB_VFS_OP_READDIR, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(onefs_seekdir), SMB_VFS_OP_SEEKDIR, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(onefs_telldir), SMB_VFS_OP_TELLDIR, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(onefs_rewinddir), SMB_VFS_OP_REWINDDIR, + SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(onefs_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(onefs_closedir), SMB_VFS_OP_CLOSEDIR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_init_search_op), SMB_VFS_OP_INIT_SEARCH_OP, + SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(onefs_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(onefs_create_file), SMB_VFS_OP_CREATE_FILE,