2 Unix SMB/CIFS implementation.
4 test suite for delayed write update
6 Copyright (C) Volker Lendecke 2004
7 Copyright (C) Andrew Tridgell 2004
8 Copyright (C) Jeremy Allison 2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "torture/torture.h"
27 #include "libcli/raw/libcliraw.h"
28 #include "system/time.h"
29 #include "system/filesys.h"
30 #include "libcli/libcli.h"
31 #include "torture/util.h"
33 #define BASEDIR "\\delaywrite"
35 static BOOL test_delayed_write_update(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
37 union smb_fileinfo finfo1, finfo2;
38 const char *fname = BASEDIR "\\torture_file.txt";
45 printf("Testing delayed update of write time\n");
47 if (!torture_setup_dir(cli, BASEDIR)) {
51 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
53 printf("Failed to open %s\n", fname);
57 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
58 finfo1.basic_info.in.file.fnum = fnum1;
61 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
63 if (!NT_STATUS_IS_OK(status)) {
64 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
68 printf("Initial write time %s\n",
69 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
71 /* 3 second delay to ensure we get past any 2 second time
72 granularity (older systems may have that) */
75 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
78 printf("write failed - wrote %d bytes (%s)\n",
79 (int)written, __location__);
85 while (time(NULL) < t+120) {
86 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
88 if (!NT_STATUS_IS_OK(status)) {
89 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
93 printf("write time %s\n",
94 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
95 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
96 printf("Server updated write_time after %d seconds\n",
97 (int)(time(NULL) - t));
104 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
105 printf("Server did not update write time?!\n");
111 smbcli_close(cli->tree, fnum1);
112 smbcli_unlink(cli->tree, fname);
113 smbcli_deltree(cli->tree, BASEDIR);
119 * Do as above, but using 2 connections.
122 static BOOL test_delayed_write_update2(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
124 struct smbcli_state *cli2=NULL;
125 union smb_fileinfo finfo1, finfo2;
126 const char *fname = BASEDIR "\\torture_file.txt";
133 union smb_flush flsh;
135 printf("Testing delayed update of write time using 2 connections\n");
137 if (!torture_open_connection(&cli2)) {
141 if (!torture_setup_dir(cli, BASEDIR)) {
145 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
147 printf("Failed to open %s\n", fname);
151 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
152 finfo1.basic_info.in.file.fnum = fnum1;
155 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
157 if (!NT_STATUS_IS_OK(status)) {
158 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
162 printf("Initial write time %s\n",
163 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
165 /* 3 second delay to ensure we get past any 2 second time
166 granularity (older systems may have that) */
170 /* Try using setfileinfo instead of write to update write time. */
171 union smb_setfileinfo sfinfo;
172 time_t t_set = time(NULL);
173 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
174 sfinfo.basic_info.in.file.fnum = fnum1;
175 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
176 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
178 /* I tried this with both + and - ve to see if it makes a different.
179 It doesn't - once the filetime is set via setfileinfo it stays that way. */
181 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
183 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
185 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
186 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
188 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
190 if (!NT_STATUS_IS_OK(status)) {
191 DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
198 while (time(NULL) < t+120) {
199 finfo2.basic_info.in.file.path = fname;
201 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
203 if (!NT_STATUS_IS_OK(status)) {
204 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
208 printf("write time %s\n",
209 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
210 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
211 printf("Server updated write_time after %d seconds\n",
212 (int)(time(NULL) - t));
219 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
220 printf("Server did not update write time?!\n");
224 /* Now try a write to see if the write time gets reset. */
226 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
227 finfo1.basic_info.in.file.fnum = fnum1;
230 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
232 if (!NT_STATUS_IS_OK(status)) {
233 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
237 printf("Modified write time %s\n",
238 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
241 printf("Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
243 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
246 printf("write failed - wrote %d bytes (%s)\n",
247 (int)written, __location__);
251 /* Just to prove to tridge that the an smbflush has no effect on
252 the write time :-). The setfileinfo IS STICKY. JRA. */
254 printf("Doing flush after write\n");
256 flsh.flush.in.file.fnum = fnum1;
257 status = smb_raw_flush(cli->tree, &flsh);
258 if (!NT_STATUS_IS_OK(status)) {
259 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
265 /* Once the time was set using setfileinfo then it stays set - writes
266 don't have any effect. But make sure. */
268 while (time(NULL) < t+15) {
269 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
271 if (!NT_STATUS_IS_OK(status)) {
272 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
276 printf("write time %s\n",
277 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
278 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
279 printf("Server updated write_time after %d seconds\n",
280 (int)(time(NULL) - t));
287 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
288 printf("Server did not update write time\n");
291 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
293 printf("Failed to open %s\n", fname);
297 printf("Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
299 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
302 printf("write failed - wrote %d bytes (%s)\n",
303 (int)written, __location__);
307 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
309 if (!NT_STATUS_IS_OK(status)) {
310 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
313 printf("write time %s\n",
314 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
315 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
316 printf("Server updated write_time\n");
319 printf("Closing the first fd to see if write time updated.\n");
320 smbcli_close(cli->tree, fnum1);
323 printf("Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
325 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
328 printf("write failed - wrote %d bytes (%s)\n",
329 (int)written, __location__);
333 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
334 finfo1.basic_info.in.file.fnum = fnum2;
336 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
338 if (!NT_STATUS_IS_OK(status)) {
339 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
342 printf("write time %s\n",
343 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
344 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
345 printf("Server updated write_time\n");
350 /* Once the time was set using setfileinfo then it stays set - writes
351 don't have any effect. But make sure. */
353 while (time(NULL) < t+15) {
354 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
356 if (!NT_STATUS_IS_OK(status)) {
357 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
361 printf("write time %s\n",
362 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
363 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
364 printf("Server updated write_time after %d seconds\n",
365 (int)(time(NULL) - t));
372 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
373 printf("Server did not update write time\n");
376 printf("Closing both fd's to see if write time updated.\n");
378 smbcli_close(cli->tree, fnum2);
381 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
383 printf("Failed to open %s\n", fname);
387 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
388 finfo1.basic_info.in.file.fnum = fnum1;
391 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
393 if (!NT_STATUS_IS_OK(status)) {
394 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
398 printf("Second open initial write time %s\n",
399 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
402 printf("Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
404 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
407 printf("write failed - wrote %d bytes (%s)\n",
408 (int)written, __location__);
412 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
413 finfo1.basic_info.in.file.fnum = fnum1;
415 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
417 if (!NT_STATUS_IS_OK(status)) {
418 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
421 printf("write time %s\n",
422 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
423 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
424 printf("Server updated write_time\n");
429 /* Once the time was set using setfileinfo then it stays set - writes
430 don't have any effect. But make sure. */
432 while (time(NULL) < t+15) {
433 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
435 if (!NT_STATUS_IS_OK(status)) {
436 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
440 printf("write time %s\n",
441 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
442 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
443 printf("Server updated write_time after %d seconds\n",
444 (int)(time(NULL) - t));
451 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
452 printf("Server did not update write time\n");
456 /* One more test to do. We should read the filetime via findfirst on the
457 second connection to ensure it's the same. This is very easy for a Windows
458 server but a bastard to get right on a POSIX server. JRA. */
461 torture_close_connection(cli2);
464 smbcli_close(cli->tree, fnum1);
465 smbcli_unlink(cli->tree, fname);
466 smbcli_deltree(cli->tree, BASEDIR);
472 /* Windows does obviously not update the stat info during a write call. I
473 * *think* this is the problem causing a spurious Excel 2003 on XP error
474 * message when saving a file. Excel does a setfileinfo, writes, and then does
475 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
476 * that the file might have been changed in between. What i've been able to
477 * trace down is that this happens if the getpathinfo after the write shows a
478 * different last write time than the setfileinfo showed. This is really
482 static BOOL test_finfo_after_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
484 union smb_fileinfo finfo1, finfo2;
485 const char *fname = BASEDIR "\\torture_file.txt";
491 struct smbcli_state *cli2=NULL;
493 printf("Testing finfo update on close\n");
495 if (!torture_setup_dir(cli, BASEDIR)) {
499 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
505 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
506 finfo1.basic_info.in.file.fnum = fnum1;
508 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
510 if (!NT_STATUS_IS_OK(status)) {
511 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
518 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
521 printf("(%s) written gave %d - should have been 1\n",
522 __location__, (int)written);
527 if (!torture_open_connection(&cli2)) {
531 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
533 printf("(%s) failed to open 2nd time - %s\n",
534 __location__, smbcli_errstr(cli2->tree));
539 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
542 printf("(%s) written gave %d - should have been 1\n",
543 __location__, (int)written);
548 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
549 finfo2.basic_info.in.file.path = fname;
551 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
553 if (!NT_STATUS_IS_OK(status)) {
554 DEBUG(0, ("(%s) fileinfo failed: %s\n",
555 __location__, nt_errstr(status)));
560 if (finfo1.basic_info.out.create_time !=
561 finfo2.basic_info.out.create_time) {
562 printf("(%s) create_time changed\n", __location__);
567 if (finfo1.basic_info.out.access_time !=
568 finfo2.basic_info.out.access_time) {
569 printf("(%s) access_time changed\n", __location__);
574 if (finfo1.basic_info.out.write_time !=
575 finfo2.basic_info.out.write_time) {
576 printf("(%s) write_time changed\n", __location__);
577 printf("write time conn 1 = %s, conn 2 = %s\n",
578 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time),
579 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
584 if (finfo1.basic_info.out.change_time !=
585 finfo2.basic_info.out.change_time) {
586 printf("(%s) change_time changed\n", __location__);
591 /* One of the two following calls updates the qpathinfo. */
593 /* If you had skipped the smbcli_write on fnum2, it would
594 * *not* have updated the stat on disk */
596 smbcli_close(cli2->tree, fnum2);
597 torture_close_connection(cli2);
600 /* This call is only for the people looking at ethereal :-) */
601 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
602 finfo2.basic_info.in.file.path = fname;
604 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2);
606 if (!NT_STATUS_IS_OK(status)) {
607 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
614 smbcli_close(cli->tree, fnum1);
615 smbcli_unlink(cli->tree, fname);
616 smbcli_deltree(cli->tree, BASEDIR);
618 torture_close_connection(cli2);
626 testing of delayed update of write_time
628 BOOL torture_delay_write(void)
630 struct smbcli_state *cli;
634 if (!torture_open_connection(&cli)) {
638 mem_ctx = talloc_init("torture_delay_write");
640 ret &= test_finfo_after_write(cli, mem_ctx);
641 ret &= test_delayed_write_update(cli, mem_ctx);
642 ret &= test_delayed_write_update2(cli, mem_ctx);
644 torture_close_connection(cli);
645 talloc_free(mem_ctx);