From 72266942b63ccf79a583ee9d32b3cdbce374070b Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 26 Aug 2019 09:54:06 -0700 Subject: [PATCH 1/5] s3: libsmbclient: Ensure SMBC_readdir_ctx() also updates the readdirplus pointers. If we are returning file entries, we have a duplicate list in dirplus. Update dirplus_next also so readdir and readdirplus are kept in sync. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14094 Signed-off-by: Jeremy Allison --- source3/libsmb/libsmb_dir.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source3/libsmb/libsmb_dir.c b/source3/libsmb/libsmb_dir.c index 886aa626509..a3ec9a8ff71 100644 --- a/source3/libsmb/libsmb_dir.c +++ b/source3/libsmb/libsmb_dir.c @@ -1174,6 +1174,17 @@ SMBC_readdir_ctx(SMBCCTX *context, dir->dir_next = dir->dir_next->next; + /* + * If we are returning file entries, we + * have a duplicate list in dirplus. + * + * Update dirplus_next also so readdir and + * readdirplus are kept in sync. + */ + if (dir->dirplus_list != NULL) { + dir->dirplus_next = dir->dirplus_next->next; + } + TALLOC_FREE(frame); return dirp; } -- 2.23.0.187.g17f5b7556c-goog From 98051ad01912f8c2eb08448dc6600f520da896b7 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 26 Aug 2019 10:02:47 -0700 Subject: [PATCH 2/5] s3: libsmbclient: Ensure SMBC_readdirplus_ctx() also updates the readdir pointers. If we are returning file entries, we have a duplicate list in dir_list. Update dir_next also so readdir and readdirplus are kept in sync. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14094 Signed-off-by: Jeremy Allison --- source3/libsmb/libsmb_dir.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source3/libsmb/libsmb_dir.c b/source3/libsmb/libsmb_dir.c index a3ec9a8ff71..2f2117e8131 100644 --- a/source3/libsmb/libsmb_dir.c +++ b/source3/libsmb/libsmb_dir.c @@ -1231,6 +1231,17 @@ SMBC_readdirplus_ctx(SMBCCTX *context, } dir->dirplus_next = dir->dirplus_next->next; + /* + * If we are returning file entries, we + * have a duplicate list in dir_list + * + * Update dir_next also so readdir and + * readdirplus are kept in sync. + */ + if (dir->dir_list) { + dir->dir_next = dir->dir_next->next; + } + TALLOC_FREE(frame); return smb_finfo; } -- 2.23.0.187.g17f5b7556c-goog From 339a82680b26d330a4fb943429236085462b0706 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 26 Aug 2019 10:07:32 -0700 Subject: [PATCH 3/5] s3: libsmbclient: Ensure SMBC_getdents_ctx() also updates the readdirplus pointers. If we are returning file entries, we have a duplicate list in dirplus. Update dirplus_next also so readdir and readdirplus are kept in sync. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14094 Signed-off-by: Jeremy Allison --- source3/libsmb/libsmb_dir.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source3/libsmb/libsmb_dir.c b/source3/libsmb/libsmb_dir.c index 2f2117e8131..4447806c108 100644 --- a/source3/libsmb/libsmb_dir.c +++ b/source3/libsmb/libsmb_dir.c @@ -1358,6 +1358,17 @@ SMBC_getdents_ctx(SMBCCTX *context, } dir->dir_next = dirlist = dirlist -> next; + + /* + * If we are returning file entries, we + * have a duplicate list in dirplus. + * + * Update dirplus_next also so readdir and + * readdirplus are kept in sync. + */ + if (dir->dirplus_list != NULL) { + dir->dirplus_next = dir->dirplus_next->next; + } } TALLOC_FREE(frame); -- 2.23.0.187.g17f5b7556c-goog From 958e56371fe7745cbf259513cade67d2300e31b7 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 26 Aug 2019 10:18:28 -0700 Subject: [PATCH 4/5] s3: libsmbclient: Fix smbc_lseekdir() to work with smbc_readdirplus(). If returning files the dir_list and the dirplus_list have exactly the same entries, we just need to keep the next pointers in sync on seek. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14094 Signed-off-by: Jeremy Allison --- source3/libsmb/libsmb_dir.c | 69 +++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/source3/libsmb/libsmb_dir.c b/source3/libsmb/libsmb_dir.c index 4447806c108..df606c4adfe 100644 --- a/source3/libsmb/libsmb_dir.c +++ b/source3/libsmb/libsmb_dir.c @@ -1672,35 +1672,43 @@ SMBC_telldir_ctx(SMBCCTX *context, /* * A routine to run down the list and see if the entry is OK + * Modifies the dir list and the dirplus list (if it exists) + * to point at the correct next entry on success. */ -static struct smbc_dir_list * -check_dir_ent(struct smbc_dir_list *list, - struct smbc_dirent *dirent) +static bool update_dir_ents(SMBCFILE *dir, struct smbc_dirent *dirent) { + struct smbc_dir_list *tmp_dir = dir->dir_list; + struct smbc_dirplus_list *tmp_dirplus = dir->dirplus_list; - /* Run down the list looking for what we want */ - - if (dirent) { - - struct smbc_dir_list *tmp = list; - - while (tmp) { - - if (tmp->dirent == dirent) - return tmp; - - tmp = tmp->next; + /* + * Run down the list looking for what we want. + * If we're enumerating files both dir_list + * and dirplus_list contain the same entry + * list, as they were seeded from the same + * cli_list callback. + * + * If we're enumerating servers then + * dirplus_list will be NULL, so don't + * update in that case. + */ + while (tmp_dir != NULL) { + if (tmp_dir->dirent == dirent) { + dir->dir_next = tmp_dir; + if (tmp_dirplus != NULL) { + dir->dirplus_next = tmp_dirplus; + } + return true; + } + tmp_dir = tmp_dir->next; + if (tmp_dirplus != NULL) { + tmp_dirplus = tmp_dirplus->next; } - } - - return NULL; /* Not found, or an error */ - + return false; } - /* * Routine to seek on a directory */ @@ -1712,8 +1720,8 @@ SMBC_lseekdir_ctx(SMBCCTX *context, { long int l_offset = offset; /* Handle problems of size */ struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset; - struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL; TALLOC_CTX *frame = talloc_stackframe(); + bool ok; if (!context || !context->internal->initialized) { @@ -1736,6 +1744,10 @@ SMBC_lseekdir_ctx(SMBCCTX *context, if (dirent == NULL) { /* Seek to the begining of the list */ dir->dir_next = dir->dir_list; + + /* Do the same for dirplus. */ + dir->dirplus_next = dir->dirplus_list; + TALLOC_FREE(frame); return 0; @@ -1743,21 +1755,26 @@ SMBC_lseekdir_ctx(SMBCCTX *context, if (offset == -1) { /* Seek to the end of the list */ dir->dir_next = NULL; + + /* Do the same for dirplus. */ + dir->dirplus_next = NULL; + TALLOC_FREE(frame); return 0; } - /* Now, run down the list and make sure that the entry is OK */ - /* This may need to be changed if we change the format of the list */ + /* + * Run down the list and make sure that the entry is OK. + * Update the position of both dir and dirplus lists. + */ - if ((list_ent = check_dir_ent(dir->dir_list, dirent)) == NULL) { + ok = update_dir_ents(dir, dirent); + if (!ok) { errno = EINVAL; /* Bad entry */ TALLOC_FREE(frame); return -1; } - dir->dir_next = list_ent; - TALLOC_FREE(frame); return 0; } -- 2.23.0.187.g17f5b7556c-goog From 981b7c62a43c89feb826bbe574831c7ed28c32df Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 26 Aug 2019 11:22:35 -0700 Subject: [PATCH 5/5] s3/4: libsmbclient test. Test using smbc_telldir/smbc_lseekdir with smbc_readdir/smbc_readdirplus/smbc_getdents. Ensure that for file access you can mix any of these three access methods for directory entries and the returned names/structs stay in sync across telldir/seekdir changes. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14094 Signed-off-by: Jeremy Allison --- source3/selftest/tests.py | 3 +- source4/torture/libsmbclient/libsmbclient.c | 340 ++++++++++++++++++++ 2 files changed, 342 insertions(+), 1 deletion(-) diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index 8bdc7038362..589c3347b84 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -509,7 +509,8 @@ nbt = ["nbt.dgram"] libsmbclient = ["libsmbclient.version", "libsmbclient.initialize", "libsmbclient.configuration", "libsmbclient.setConfiguration", "libsmbclient.options", "libsmbclient.opendir", - "libsmbclient.list_shares", "libsmbclient.readdirplus"] + "libsmbclient.list_shares", "libsmbclient.readdirplus", + "libsmbclient.readdirplus_seek"] vfs = [ "vfs.fruit", diff --git a/source4/torture/libsmbclient/libsmbclient.c b/source4/torture/libsmbclient/libsmbclient.c index f9154e8a19c..7bc407ac905 100644 --- a/source4/torture/libsmbclient/libsmbclient.c +++ b/source4/torture/libsmbclient/libsmbclient.c @@ -18,6 +18,7 @@ */ #include "includes.h" +#include "system/dir.h" #include "torture/smbtorture.h" #include "auth/credentials/credentials.h" #include "lib/cmdline/popt_common.h" @@ -375,6 +376,343 @@ static bool torture_libsmbclient_readdirplus(struct torture_context *tctx) return true; } +static bool torture_libsmbclient_readdirplus_seek(struct torture_context *tctx) +{ + SMBCCTX *ctx; + int ret = -1; + int dhandle = -1; + int fhandle = -1; + const char *dname = NULL; + const char *full_filename[100] = {0}; + const char *filename[100] = {0}; + const struct libsmb_file_info *direntries[102] = {0}; + unsigned int i = 0; + const char *smburl = torture_setting_string(tctx, "smburl", NULL); + bool success = false; + off_t telldir_50 = (off_t)-1; + off_t telldir_20 = (off_t)-1; + size_t getdentries_size = 0; + struct smbc_dirent *getdentries = NULL; + struct smbc_dirent *dirent_20 = NULL; + const struct libsmb_file_info *direntries_20 = NULL; + + if (smburl == NULL) { + torture_fail(tctx, + "option --option=torture:smburl=" + "smb://user:password@server/share missing\n"); + } + + DEBUG(0,("torture_libsmbclient_readdirplus_seek start\n")); + + torture_assert(tctx, torture_libsmbclient_init_context(tctx, &ctx), ""); + smbc_set_context(ctx); + + dname = talloc_asprintf(tctx, + "%s/rd_seek", + smburl); + if (dname == NULL) { + torture_fail_goto(tctx, + done, + "talloc fail\n"); + } + + /* Ensure the files don't exist. */ + for (i = 0; i < 100; i++) { + filename[i] = talloc_asprintf(tctx, + "test_readdirplus_%u.txt", + i); + if (full_filename == NULL) { + torture_fail_goto(tctx, + done, + "talloc fail\n"); + } + full_filename[i] = talloc_asprintf(tctx, + "%s/%s", + dname, + filename[i]); + if (full_filename == NULL) { + torture_fail_goto(tctx, + done, + "talloc fail\n"); + } + (void)smbc_unlink(full_filename[i]); + } + /* Ensure the directory doesn't exist. */ + (void)smbc_rmdir(dname); + + /* Create containing directory. */ + ret = smbc_mkdir(dname, 0777); + if (ret != 0) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "failed to create directory '%s': %s", + dname, + strerror(errno))); + } + + DEBUG(0,("torture_libsmbclient_readdirplus_seek create\n")); + + /* Create them. */ + for (i = 0; i < 100; i++) { + fhandle = smbc_creat(full_filename[i], 0666); + if (fhandle < 0) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "failed to create file '%s': %s", + full_filename[i], + strerror(errno))); + } + ret = smbc_close(fhandle); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to close handle for '%s'", + full_filename[i])); + } + + DEBUG(0,("torture_libsmbclient_readdirplus_seek enum\n")); + + /* Now enumerate the directory. */ + dhandle = smbc_opendir(dname); + if (dhandle < 0) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "failed to obtain " + "directory handle for '%s' : %s", + dname, + strerror(errno))); + } + + /* Read all the files. 100 we created plus . and .. */ + for (i = 0; i < 102; i++) { + bool found = false; + unsigned int j; + + direntries[i] = smbc_readdirplus(dhandle); + if (direntries[i] == NULL) { + break; + } + + /* Store at offset 50. */ + if (i == 50) { + telldir_50 = smbc_telldir(dhandle); + if (telldir_50 == (off_t)-1) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "telldir failed file %s\n", + direntries[i]->name)); + } + } + + if (ISDOT(direntries[i]->name)) { + continue; + } + if (ISDOTDOT(direntries[i]->name)) { + continue; + } + + /* Ensure all our files exist. */ + for (j = 0; j < 100; j++) { + if (strcmp(direntries[i]->name, + filename[j]) == 0) { + found = true; + } + } + if (!found) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "failed to find file %s\n", + direntries[i]->name)); + } + } + + /* + * We're seeking on in-memory lists here, so + * whilst the handle is open we really should + * get the same files back in the same order. + */ + + ret = smbc_lseekdir(dhandle, telldir_50); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to seek (50) directory handle for '%s'", + dname)); + + DEBUG(0,("torture_libsmbclient_readdirplus_seek seek\n")); + + for (i = 51; i < 102; i++) { + const struct libsmb_file_info *entry = + smbc_readdirplus(dhandle); + if (entry != direntries[i]) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "after seek - failed to find " + "file %s - got %s\n", + direntries[i]->name, + entry->name)); + } + } + + /* Seek back to the start. */ + ret = smbc_lseekdir(dhandle, 0); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to seek directory handle to start for '%s'", + dname)); + + /* + * Mix getdents/readdir/readdirplus with lseek to ensure + * we get the same result. + */ + + /* Allocate the space for 20 entries. + * Tricky as we need to allocate 20 struct smbc_dirent's + space + * for the name lengths. + */ + getdentries_size = 20 * (sizeof(struct smbc_dirent) + + strlen("test_readdirplus_1000.txt") + 1); + + getdentries = (struct smbc_dirent *)talloc_array_size(tctx, + getdentries_size, + 1); + + ret = smbc_getdents(dhandle, getdentries, getdentries_size); + torture_assert_goto(tctx, + (ret != -1), + success, + done, + talloc_asprintf(tctx, + "smbd_getdents(1) for '%s' failed\n", + dname)); + + telldir_20 = smbc_telldir(dhandle); + if (telldir_20 == (off_t)-1) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "telldir (20) failed\n")); + } + /* Read another 20. */ + ret = smbc_getdents(dhandle, getdentries, getdentries_size); + torture_assert_goto(tctx, + (ret != -1), + success, + done, + talloc_asprintf(tctx, + "smbd_getdents(2) for '%s' failed\n", + dname)); + + /* Seek back to 20. */ + ret = smbc_lseekdir(dhandle, telldir_20); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to seek (20) directory handle for '%s'", + dname)); + + /* Read with readdir. */ + dirent_20 = smbc_readdir(dhandle); + if (dirent_20 == NULL) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "smbc_readdir (20) failed\n")); + } + + /* Ensure the getdents and readdir names are the same. */ + ret = strcmp(dirent_20->name, getdentries[0].name); + if (ret != 0) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "after seek (20) readdir name missmatch " + "file %s - got %s\n", + dirent_20->name, + getdentries[0].name)); + } + + /* Seek back to 20. */ + ret = smbc_lseekdir(dhandle, telldir_20); + torture_assert_int_equal_goto(tctx, + ret, + 0, + success, + done, + talloc_asprintf(tctx, + "failed to seek (20) directory handle for '%s'", + dname)); + /* Read with readdirplus. */ + direntries_20 = smbc_readdirplus(dhandle); + if (direntries_20 == NULL) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "smbc_readdirplus (20) failed\n")); + } + + /* Ensure the readdirplus and readdir names are the same. */ + ret = strcmp(dirent_20->name, direntries_20->name); + if (ret != 0) { + torture_fail_goto(tctx, + done, + talloc_asprintf(tctx, + "after seek (20) readdirplus name missmatch " + "file %s - got %s\n", + dirent_20->name, + direntries_20->name)); + } + + ret = smbc_closedir(dhandle); + torture_assert_int_equal(tctx, + ret, + 0, + talloc_asprintf(tctx, + "failed to close directory handle for '%s'", + dname)); + + dhandle = -1; + success = true; + + done: + + /* Clean up. */ + if (dhandle != -1) { + smbc_closedir(dhandle); + } + for (i = 0; i < 100; i++) { + if (full_filename[i] != NULL) { + smbc_unlink(full_filename[i]); + } + } + if (dname != NULL) { + smbc_rmdir(dname); + } + + smbc_free_context(ctx, 1); + + return success; +} + bool torture_libsmbclient_configuration(struct torture_context *tctx) { SMBCCTX *ctx; @@ -635,6 +973,8 @@ NTSTATUS torture_libsmbclient_init(TALLOC_CTX *ctx) torture_suite_add_simple_test(suite, "list_shares", torture_libsmbclient_list_shares); torture_suite_add_simple_test(suite, "readdirplus", torture_libsmbclient_readdirplus); + torture_suite_add_simple_test(suite, "readdirplus_seek", + torture_libsmbclient_readdirplus_seek); suite->description = talloc_strdup(suite, "libsmbclient interface tests"); -- 2.23.0.187.g17f5b7556c-goog