From a926d0f4616e70d0e50222b5b6b03f4be374ff3e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 3 Dec 2013 17:22:19 -0800 Subject: [PATCH 1/2] smbd - allow updates on directory write times on open handles. If we set a non-null 'old timestamp' in the share mode database when creating a directory handle, this prevents mtime (write time) updates from being seen by clients, as we will always return the timestamp stored in the database whilst the handle is open. For files this is ok, as we update the stored timestamp ourselves when we write to the handle. For directories we should just rely on the mtime value from the underlying filesystem. Torture test to follow. BUG: https://bugzilla.samba.org/show_bug.cgi?id=9870 Signed-off-by: Jeremy Allison Reviewed-by: Volker Lendecke --- source3/smbd/open.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 2d866bb..200c1ce 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -3202,7 +3202,14 @@ static NTSTATUS open_directory(connection_struct *conn, return status; } - mtimespec = smb_dname->st.st_ex_mtime; + /* Don't store old timestamps for directory + handles in the internal database. We don't + update them in there if new objects + are creaded in the directory. Currently + we only update timestamps on file writes. + See bug #9870. + */ + ZERO_STRUCT(mtimespec); #ifdef O_DIRECTORY status = fd_open(conn, fsp, O_RDONLY|O_DIRECTORY, 0); -- 1.8.5.1 From 48f426b22de563f62c39a7aead47c53b7f7c578c Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 3 Dec 2013 17:26:26 -0800 Subject: [PATCH 2/2] smbtorture: New torture test for bug #9870. Not fetching the latest modification time on a folder if we have read locks on it. Prove we should just rely on the mtime value from the underlying filesystem, even with an open handle. BUG: https://bugzilla.samba.org/show_bug.cgi?id=9870 Signed-off-by: Jeremy Allison Reviewed-by: Volker Lendecke Autobuild-User(master): Volker Lendecke Autobuild-Date(master): Thu Dec 5 10:05:06 CET 2013 on sn-devel-104 --- selftest/knownfail | 1 + source4/torture/basic/delaywrite.c | 95 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/selftest/knownfail b/selftest/knownfail index 201ea6b..52fecfb 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -133,6 +133,7 @@ ^samba4.*base.delaywrite.*delayed update of write time 3c\(.*\)$ ^samba4.*base.delaywrite.*update of write time using SET_END_OF_FILE\(.*\)$ ^samba4.*base.delaywrite.*update of write time using SET_ALLOCATION_SIZE\(.*\)$ +^samba4.*base.delaywrite.*directory timestamp update test\(.*\)$ ^samba4.ldap.python \(dc\).Test add_ldif\(\) with BASE64 security descriptor input using WRONG domain SID\(.*\)$ # some operations don't work over the CIFS NTVFS backend yet (eg. root_fid) ^samba4.ntvfs.cifs.*.base.createx_sharemodes_dir diff --git a/source4/torture/basic/delaywrite.c b/source4/torture/basic/delaywrite.c index 23a17d5..da03b72 100644 --- a/source4/torture/basic/delaywrite.c +++ b/source4/torture/basic/delaywrite.c @@ -3059,6 +3059,100 @@ static bool test_delayed_write_update7(struct torture_context *tctx, struct smbc } /* + Test if creating a file in a directory with an open handle updates the + write timestamp (it should). +*/ +static bool test_directory_update8(struct torture_context *tctx, struct smbcli_state *cli) +{ + union smb_fileinfo dir_info1, dir_info2; + union smb_open open_parms; + const char *fname = BASEDIR "\\torture_file.txt"; + NTSTATUS status; + int fnum1 = -1; + int fnum2 = -1; + bool ret = true; + int used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000); + int normal_delay = 2000000; + double sec = ((double)used_delay) / ((double)normal_delay); + int msec = 1000 * sec; + TALLOC_CTX *mem_ctx = talloc_init("test_delayed_write_update8"); + + if (!mem_ctx) return false; + + torture_comment(tctx, "\nRunning test directory write update\n"); + + torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR); + + /* Open a handle on the directory - and leave it open. */ + ZERO_STRUCT(open_parms); + open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX; + open_parms.ntcreatex.in.flags = 0; + open_parms.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_READ; + open_parms.ntcreatex.in.file_attr = 0; + open_parms.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + open_parms.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + open_parms.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + open_parms.ntcreatex.in.fname = BASEDIR; + + status = smb_raw_open(cli->tree, mem_ctx, &open_parms); + talloc_free(mem_ctx); + + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, + "failed to open directory handle"); + ret = false; + goto done; + } + + fnum1 = open_parms.ntcreatex.out.file.fnum; + + /* Store the returned write time. */ + ZERO_STRUCT(dir_info1); + dir_info1.basic_info.out.write_time = open_parms.ntcreatex.out.write_time; + + torture_comment(tctx, "Initial write time %s\n", + nt_time_string(tctx, dir_info1.basic_info.out.write_time)); + + /* sleep */ + smb_msleep(3 * msec); + + /* Now create a file within the directory. */ + fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum2 == -1) { + torture_result(tctx, TORTURE_FAIL, "Failed to open %s", fname); + ret = false; + goto done; + } + smbcli_close(cli->tree, fnum2); + + /* Read the directory write time again. */ + ZERO_STRUCT(dir_info2); + dir_info2.basic_info.level = RAW_FILEINFO_BASIC_INFO; + dir_info2.basic_info.in.file.fnum = fnum1; + + status = smb_raw_fileinfo(cli->tree, tctx, &dir_info2); + + torture_assert_ntstatus_ok(tctx, status, "fileinfo failed"); + + /* Ensure it's been incremented. */ + COMPARE_WRITE_TIME_GREATER(dir_info2, dir_info1); + + torture_comment(tctx, "Updated write time %s\n", + nt_time_string(tctx, dir_info2.basic_info.out.write_time)); + + done: + + if (fnum1 != -1) + smbcli_close(cli->tree, fnum1); + smbcli_unlink(cli->tree, fname); + smbcli_deltree(cli->tree, BASEDIR); + + return ret; +} + +/* testing of delayed update of write_time */ struct torture_suite *torture_delay_write(void) @@ -3082,6 +3176,7 @@ struct torture_suite *torture_delay_write(void) torture_suite_add_2smb_test(suite, "delayed update of write time 6", test_delayed_write_update6); torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7); torture_suite_add_1smb_test(suite, "timestamp resolution test", test_delayed_write_update7); + torture_suite_add_1smb_test(suite, "directory timestamp update test", test_directory_update8); return suite; } -- 1.8.5.1