From 3c75e482ac2d5e0910295b9f7a25eaaffc285250 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. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Ralph Böhme (cherry picked from commit 4bca8e097f5a909c628daa4dbfa932ddc1725ebc) --- 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 ff19ed57f14..35215fdfbb5 100644 --- a/source3/libsmb/libsmb_dir.c +++ b/source3/libsmb/libsmb_dir.c @@ -1173,6 +1173,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 491244d47e5788f0c9556ebb6f663598921c4de3 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. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Ralph Böhme (cherry picked from commit 3d82b7d11cd7b78adc6b3642e64e3a8f251de869) --- 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 35215fdfbb5..b51a4c13775 100644 --- a/source3/libsmb/libsmb_dir.c +++ b/source3/libsmb/libsmb_dir.c @@ -1230,6 +1230,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 592175f8820cca3c081421572ccfba1024b276dd 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. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Ralph Böhme (cherry picked from commit 754cec7756b2ddb1cfcc3984265f01cb366beb76) --- 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 b51a4c13775..ff2948c9b69 100644 --- a/source3/libsmb/libsmb_dir.c +++ b/source3/libsmb/libsmb_dir.c @@ -1357,6 +1357,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 73e7eb62bbf1ebf82843d6ae1901f0d43bef1cb3 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(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Ralph Böhme (cherry picked from commit 0d9b1645499ce12a79a137d3482434aa5d2eb47c) --- 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 ff2948c9b69..5b0a62418a1 100644 --- a/source3/libsmb/libsmb_dir.c +++ b/source3/libsmb/libsmb_dir.c @@ -1671,35 +1671,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 */ @@ -1711,8 +1719,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) { @@ -1735,6 +1743,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; @@ -1742,21 +1754,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 d7eaa5477b95d34679205d0513df9590c435dbe9 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. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Back-ported from master 3355601fe8541994cc41f5ed800aab9b6a2294f4. Signed-off-by: Jeremy Allison Reviewed-by: Ralph Böhme --- source4/torture/libsmbclient/libsmbclient.c | 340 ++++++++++++++++++++ 1 file changed, 340 insertions(+) diff --git a/source4/torture/libsmbclient/libsmbclient.c b/source4/torture/libsmbclient/libsmbclient.c index e84ce018a69..aabd8b301d0 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" @@ -316,6 +317,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; @@ -519,6 +857,8 @@ NTSTATUS torture_libsmbclient_init(TALLOC_CTX *ctx) torture_suite_add_simple_test(suite, "opendir", torture_libsmbclient_opendir); 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