From 395cd3f8aedb951f9589d3e96d0a8458a24da2d7 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 29 Jan 2014 17:01:30 -0800 Subject: [PATCH] s3: vfs_dirsort module. Allow dirsort to work when multiple simultaneous directories are open. The old code only keeps one active private data pointer on the connection struct, opening a second directory on the same connection will overwrite it. This modification turns the private data pointer into a linked list of open directories on the connection struct, and finds the correct one by searching on the passed in DIR *. With this code in place, smbd passes raw.search torture test on a share definition with: vfs objects = dirsort https://bugzilla.samba.org/show_bug.cgi?id=10406 Signed-off-by: Jeremy Allison --- source3/modules/vfs_dirsort.c | 119 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 112 insertions(+), 7 deletions(-) diff --git a/source3/modules/vfs_dirsort.c b/source3/modules/vfs_dirsort.c index 2c25765..98109c2 100644 --- a/source3/modules/vfs_dirsort.c +++ b/source3/modules/vfs_dirsort.c @@ -28,6 +28,7 @@ static int compare_dirent (const struct dirent *da, const struct dirent *db) } struct dirsort_privates { + struct dirsort_privates *prev, *next; long pos; struct dirent *directory_list; unsigned int number_of_entries; @@ -37,10 +38,6 @@ struct dirsort_privates { struct smb_filename *smb_fname; /* If open via OPENDIR */ }; -static void free_dirsort_privates(void **datap) { - TALLOC_FREE(*datap); -} - static bool get_sorted_dir_mtime(vfs_handle_struct *handle, struct dirsort_privates *data, struct timespec *ret_mtime) @@ -119,8 +116,15 @@ static DIR *dirsort_opendir(vfs_handle_struct *handle, const char *fname, const char *mask, uint32 attr) { + struct dirsort_privates *list_head = NULL; struct dirsort_privates *data = NULL; + if (SMB_VFS_HANDLE_TEST_DATA(handle)) { + /* Find the list head of all open directories. */ + SMB_VFS_HANDLE_GET_DATA(handle, list_head, struct dirsort_privates, + return NULL); + } + /* set up our private data about this directory */ data = talloc_zero(handle->conn, struct dirsort_privates); if (!data) { @@ -148,7 +152,9 @@ static DIR *dirsort_opendir(vfs_handle_struct *handle, return NULL; } - SMB_VFS_HANDLE_SET_DATA(handle, data, free_dirsort_privates, + /* Add to the private list of all open directories. */ + DLIST_ADD(list_head, data); + SMB_VFS_HANDLE_SET_DATA(handle, list_head, NULL, struct dirsort_privates, return NULL); return data->source_directory; @@ -159,8 +165,15 @@ static DIR *dirsort_fdopendir(vfs_handle_struct *handle, const char *mask, uint32 attr) { + struct dirsort_privates *list_head = NULL; struct dirsort_privates *data = NULL; + if (SMB_VFS_HANDLE_TEST_DATA(handle)) { + /* Find the list head of all open directories. */ + SMB_VFS_HANDLE_GET_DATA(handle, list_head, struct dirsort_privates, + return NULL); + } + /* set up our private data about this directory */ data = talloc_zero(handle->conn, struct dirsort_privates); if (!data) { @@ -186,7 +199,9 @@ static DIR *dirsort_fdopendir(vfs_handle_struct *handle, return NULL; } - SMB_VFS_HANDLE_SET_DATA(handle, data, free_dirsort_privates, + /* Add to the private list of all open directories. */ + DLIST_ADD(list_head, data); + SMB_VFS_HANDLE_SET_DATA(handle, list_head, NULL, struct dirsort_privates, return NULL); return data->source_directory; @@ -202,12 +217,20 @@ static struct dirent *dirsort_readdir(vfs_handle_struct *handle, SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, return NULL); + while(data && (data->source_directory != dirp)) { + data = data->next; + } + if (data == NULL) { + return NULL; + } + if (get_sorted_dir_mtime(handle, data, ¤t_mtime) == false) { return NULL; } /* throw away cache and re-read the directory if we've changed */ - if (timespec_compare(¤t_mtime, &data->mtime) > 1) { + if (timespec_compare(¤t_mtime, &data->mtime)) { + SMB_VFS_NEXT_REWINDDIR(handle, data->source_directory); open_and_sort_dir(handle, data); } @@ -221,10 +244,53 @@ static struct dirent *dirsort_readdir(vfs_handle_struct *handle, static void dirsort_seekdir(vfs_handle_struct *handle, DIR *dirp, long offset) { + struct timespec current_mtime; struct dirsort_privates *data = NULL; + SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, return); + /* Find the entry holding dirp. */ + while(data && (data->source_directory != dirp)) { + data = data->next; + } + if (data == NULL) { + return; + } + if (offset > data->number_of_entries) { + return; + } data->pos = offset; + + if (get_sorted_dir_mtime(handle, data, ¤t_mtime) == false) { + return; + } + + if (timespec_compare(¤t_mtime, &data->mtime)) { + /* Directory changed. We must re-read the + cache and search for the name that was + previously stored at the offset being + requested, otherwise after the re-sort + we will point to the wrong entry. The + OS/2 incremental delete code relies on + this. */ + unsigned int i; + char *wanted_name = talloc_strdup(handle->conn, + data->directory_list[offset].d_name); + if (wanted_name == NULL) { + return; + } + SMB_VFS_NEXT_REWINDDIR(handle, data->source_directory); + open_and_sort_dir(handle, data); + /* Now search for where we were. */ + data->pos = 0; + for (i = 0; i < data->number_of_entries; i++) { + if(strcmp(wanted_name, data->directory_list[i].d_name) == 0) { + data->pos = i; + break; + } + } + TALLOC_FREE(wanted_name); + } } static long dirsort_telldir(vfs_handle_struct *handle, DIR *dirp) @@ -233,6 +299,13 @@ static long dirsort_telldir(vfs_handle_struct *handle, DIR *dirp) SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, return -1); + /* Find the entry holding dirp. */ + while(data && (data->source_directory != dirp)) { + data = data->next; + } + if (data == NULL) { + return -1; + } return data->pos; } @@ -241,9 +314,40 @@ static void dirsort_rewinddir(vfs_handle_struct *handle, DIR *dirp) struct dirsort_privates *data = NULL; SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, return); + /* Find the entry holding dirp. */ + while(data && (data->source_directory != dirp)) { + data = data->next; + } + if (data == NULL) { + return; + } data->pos = 0; } +static int dirsort_closedir(vfs_handle_struct *handle, DIR *dirp) +{ + struct dirsort_privates *list_head = NULL; + struct dirsort_privates *data = NULL; + int ret; + + SMB_VFS_HANDLE_GET_DATA(handle, list_head, struct dirsort_privates, return -1); + /* Find the entry holding dirp. */ + for(data = list_head; data && (data->source_directory != dirp); data = data->next) { + ; + } + if (data == NULL) { + return -1; + } + /* Remove from the list and re-store the list head. */ + DLIST_REMOVE(list_head, data); + SMB_VFS_HANDLE_SET_DATA(handle, list_head, NULL, + struct dirsort_privates, return -1); + + ret = SMB_VFS_NEXT_CLOSEDIR(handle, dirp); + TALLOC_FREE(data); + return ret; +} + static struct vfs_fn_pointers vfs_dirsort_fns = { .opendir_fn = dirsort_opendir, .fdopendir_fn = dirsort_fdopendir, @@ -251,6 +355,7 @@ static struct vfs_fn_pointers vfs_dirsort_fns = { .seekdir_fn = dirsort_seekdir, .telldir_fn = dirsort_telldir, .rewind_dir_fn = dirsort_rewinddir, + .closedir_fn = dirsort_closedir, }; NTSTATUS vfs_dirsort_init(void) -- 1.8.5.3