From 587c0a9f23cd68dc85b6cb69f8d8e2066e3a2b22 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 29 Jan 2014 17:01:30 -0800 Subject: [PATCH 1/2] 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 Reviewed-by: Andrew Bartlett (back-ported from commit fd79652b0e598882e0c4d156bd897c29dae8ec04) --- 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 64d74d5..72b46c9 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) @@ -120,8 +117,15 @@ static DIR *dirsort_opendir(vfs_handle_struct *handle, uint32 attr) { NTSTATUS status; + 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) { @@ -153,7 +157,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; @@ -164,8 +170,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) { @@ -191,7 +204,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; @@ -207,12 +222,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); } @@ -226,10 +249,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) @@ -238,6 +304,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; } @@ -246,9 +319,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, @@ -256,6 +360,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.9.0.rc1.175.g0b1dcb5 From 76183716c85114ff3cd9e603eead1148645f69b8 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 11 Feb 2014 10:39:04 -0800 Subject: [PATCH 2/2] s3: vfs_dirsort module. Add 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 Reviewed-by: Andrew Bartlett (cherry picked from commit 30cc8f41da58b87ff575860b7cde640520829923) --- selftest/target/Samba3.pm | 4 ++++ source3/selftest/tests.py | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index 792bbca..59a9f79 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -933,6 +933,10 @@ sub provision($$$$$$) [tmp] path = $shrdir comment = smb username is [%U] +[tmpsort] + path = $shrdir + comment = Load dirsort module + vfs objects = dirsort acl_xattr fake_acls xattr_tdb streams_depot [tmpenc] path = $shrdir comment = encrypt smb username is [%U] diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index a968665..2965aae 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -340,6 +340,11 @@ for t in tests: plansmbtorture4testsuite(t, "s3dc", '//$SERVER_IP/aio -U$USERNAME%$PASSWORD', 'aio') plansmbtorture4testsuite(t, "s3dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD') plansmbtorture4testsuite(t, "plugin_s4_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD') + elif t == "raw.search": + plansmbtorture4testsuite(t, "s3dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD') +# test the dirsort module. + plansmbtorture4testsuite(t, "s3dc", '//$SERVER_IP/tmpsort -U$USERNAME%$PASSWORD') + plansmbtorture4testsuite(t, "plugin_s4_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD') else: plansmbtorture4testsuite(t, "s3dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD') plansmbtorture4testsuite(t, "plugin_s4_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD') -- 1.9.0.rc1.175.g0b1dcb5