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"
32 #define BASEDIR "\\delaywrite"
34 static BOOL test_delayed_write_update(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
36 union smb_fileinfo finfo1, finfo2;
37 const char *fname = BASEDIR "\\torture_file.txt";
44 printf("Testing delayed update of write time\n");
46 if (!torture_setup_dir(cli, BASEDIR)) {
50 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
52 printf("Failed to open %s\n", fname);
56 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
57 finfo1.basic_info.in.fnum = fnum1;
60 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
62 if (!NT_STATUS_IS_OK(status)) {
63 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
67 printf("Initial write time %s\n",
68 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
70 /* 3 second delay to ensure we get past any 2 second time
71 granularity (older systems may have that) */
74 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
77 printf("write failed - wrote %d bytes (%s)\n",
78 (int)written, __location__);
84 while (time(NULL) < t+120) {
85 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
87 if (!NT_STATUS_IS_OK(status)) {
88 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
92 printf("write time %s\n",
93 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
94 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
95 printf("Server updated write_time after %d seconds\n",
96 (int)(time(NULL) - t));
103 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
104 printf("Server did not update write time?!\n");
110 smbcli_close(cli->tree, fnum1);
111 smbcli_unlink(cli->tree, fname);
112 smbcli_deltree(cli->tree, BASEDIR);
118 * Do as above, but using 2 connections.
121 static BOOL test_delayed_write_update2(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
123 struct smbcli_state *cli2=NULL;
124 union smb_fileinfo finfo1, finfo2;
125 const char *fname = BASEDIR "\\torture_file.txt";
132 struct smb_flush flsh;
134 printf("Testing delayed update of write time using 2 connections\n");
136 if (!torture_open_connection(&cli2)) {
140 if (!torture_setup_dir(cli, BASEDIR)) {
144 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
146 printf("Failed to open %s\n", fname);
150 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
151 finfo1.basic_info.in.fnum = fnum1;
154 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
156 if (!NT_STATUS_IS_OK(status)) {
157 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
161 printf("Initial write time %s\n",
162 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
164 /* 3 second delay to ensure we get past any 2 second time
165 granularity (older systems may have that) */
169 /* Try using setfileinfo instead of write to update write time. */
170 union smb_setfileinfo sfinfo;
171 time_t t_set = time(NULL);
172 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
173 sfinfo.basic_info.file.fnum = fnum1;
174 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
175 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
177 /* I tried this with both + and - ve to see if it makes a different.
178 It doesn't - once the filetime is set via setfileinfo it stays that way. */
180 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
182 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
184 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
185 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
187 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
189 if (!NT_STATUS_IS_OK(status)) {
190 DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
197 while (time(NULL) < t+120) {
198 finfo2.basic_info.in.fname = fname;
200 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
202 if (!NT_STATUS_IS_OK(status)) {
203 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
207 printf("write time %s\n",
208 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
209 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
210 printf("Server updated write_time after %d seconds\n",
211 (int)(time(NULL) - t));
218 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
219 printf("Server did not update write time?!\n");
223 /* Now try a write to see if the write time gets reset. */
225 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
226 finfo1.basic_info.in.fnum = fnum1;
229 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
231 if (!NT_STATUS_IS_OK(status)) {
232 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
236 printf("Modified write time %s\n",
237 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
240 printf("Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
242 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
245 printf("write failed - wrote %d bytes (%s)\n",
246 (int)written, __location__);
250 /* Just to prove to tridge that the an smbflush has no effect on
251 the write time :-). The setfileinfo IS STICKY. JRA. */
253 printf("Doing flush after write\n");
255 flsh.in.fnum = fnum1;
256 status = smb_raw_flush(cli->tree, &flsh);
257 if (!NT_STATUS_IS_OK(status)) {
258 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
264 /* Once the time was set using setfileinfo then it stays set - writes
265 don't have any effect. But make sure. */
267 while (time(NULL) < t+15) {
268 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
270 if (!NT_STATUS_IS_OK(status)) {
271 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
275 printf("write time %s\n",
276 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
277 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
278 printf("Server updated write_time after %d seconds\n",
279 (int)(time(NULL) - t));
286 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
287 printf("Server did not update write time\n");
290 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
292 printf("Failed to open %s\n", fname);
296 printf("Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
298 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
301 printf("write failed - wrote %d bytes (%s)\n",
302 (int)written, __location__);
306 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
308 if (!NT_STATUS_IS_OK(status)) {
309 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
312 printf("write time %s\n",
313 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
314 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
315 printf("Server updated write_time\n");
318 printf("Closing the first fd to see if write time updated.\n");
319 smbcli_close(cli->tree, fnum1);
322 printf("Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
324 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
327 printf("write failed - wrote %d bytes (%s)\n",
328 (int)written, __location__);
332 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
333 finfo1.basic_info.in.fnum = fnum2;
335 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
337 if (!NT_STATUS_IS_OK(status)) {
338 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
341 printf("write time %s\n",
342 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
343 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
344 printf("Server updated write_time\n");
349 /* Once the time was set using setfileinfo then it stays set - writes
350 don't have any effect. But make sure. */
352 while (time(NULL) < t+15) {
353 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
355 if (!NT_STATUS_IS_OK(status)) {
356 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
360 printf("write time %s\n",
361 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
362 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
363 printf("Server updated write_time after %d seconds\n",
364 (int)(time(NULL) - t));
371 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
372 printf("Server did not update write time\n");
375 printf("Closing both fd's to see if write time updated.\n");
377 smbcli_close(cli->tree, fnum2);
380 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
382 printf("Failed to open %s\n", fname);
386 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
387 finfo1.basic_info.in.fnum = fnum1;
390 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
392 if (!NT_STATUS_IS_OK(status)) {
393 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
397 printf("Second open initial write time %s\n",
398 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
401 printf("Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
403 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
406 printf("write failed - wrote %d bytes (%s)\n",
407 (int)written, __location__);
411 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
412 finfo1.basic_info.in.fnum = fnum1;
414 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
416 if (!NT_STATUS_IS_OK(status)) {
417 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
420 printf("write time %s\n",
421 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
422 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
423 printf("Server updated write_time\n");
428 /* Once the time was set using setfileinfo then it stays set - writes
429 don't have any effect. But make sure. */
431 while (time(NULL) < t+15) {
432 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
434 if (!NT_STATUS_IS_OK(status)) {
435 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
439 printf("write time %s\n",
440 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
441 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
442 printf("Server updated write_time after %d seconds\n",
443 (int)(time(NULL) - t));
450 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
451 printf("Server did not update write time\n");
455 /* One more test to do. We should read the filetime via findfirst on the
456 second connection to ensure it's the same. This is very easy for a Windows
457 server but a bastard to get right on a POSIX server. JRA. */
460 torture_close_connection(cli2);
463 smbcli_close(cli->tree, fnum1);
464 smbcli_unlink(cli->tree, fname);
465 smbcli_deltree(cli->tree, BASEDIR);
471 /* Windows does obviously not update the stat info during a write call. I
472 * *think* this is the problem causing a spurious Excel 2003 on XP error
473 * message when saving a file. Excel does a setfileinfo, writes, and then does
474 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
475 * that the file might have been changed in between. What i've been able to
476 * trace down is that this happens if the getpathinfo after the write shows a
477 * different last write time than the setfileinfo showed. This is really
481 static BOOL test_finfo_after_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
483 union smb_fileinfo finfo1, finfo2;
484 const char *fname = BASEDIR "\\torture_file.txt";
490 struct smbcli_state *cli2=NULL;
492 printf("Testing finfo update on close\n");
494 if (!torture_setup_dir(cli, BASEDIR)) {
498 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
504 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
505 finfo1.basic_info.in.fnum = fnum1;
507 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
509 if (!NT_STATUS_IS_OK(status)) {
510 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
517 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
520 printf("(%s) written gave %d - should have been 1\n",
521 __location__, (int)written);
526 if (!torture_open_connection(&cli2)) {
530 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
532 printf("(%s) failed to open 2nd time - %s\n",
533 __location__, smbcli_errstr(cli2->tree));
538 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
541 printf("(%s) written gave %d - should have been 1\n",
542 __location__, (int)written);
547 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
548 finfo2.basic_info.in.fname = fname;
550 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
552 if (!NT_STATUS_IS_OK(status)) {
553 DEBUG(0, ("(%s) fileinfo failed: %s\n",
554 __location__, nt_errstr(status)));
559 if (finfo1.basic_info.out.create_time !=
560 finfo2.basic_info.out.create_time) {
561 printf("(%s) create_time changed\n", __location__);
566 if (finfo1.basic_info.out.access_time !=
567 finfo2.basic_info.out.access_time) {
568 printf("(%s) access_time changed\n", __location__);
573 if (finfo1.basic_info.out.write_time !=
574 finfo2.basic_info.out.write_time) {
575 printf("(%s) write_time changed\n", __location__);
576 printf("write time conn 1 = %s, conn 2 = %s\n",
577 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time),
578 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
583 if (finfo1.basic_info.out.change_time !=
584 finfo2.basic_info.out.change_time) {
585 printf("(%s) change_time changed\n", __location__);
590 /* One of the two following calls updates the qpathinfo. */
592 /* If you had skipped the smbcli_write on fnum2, it would
593 * *not* have updated the stat on disk */
595 smbcli_close(cli2->tree, fnum2);
596 torture_close_connection(cli2);
599 /* This call is only for the people looking at ethereal :-) */
600 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
601 finfo2.basic_info.in.fname = fname;
603 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2);
605 if (!NT_STATUS_IS_OK(status)) {
606 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
613 smbcli_close(cli->tree, fnum1);
614 smbcli_unlink(cli->tree, fname);
615 smbcli_deltree(cli->tree, BASEDIR);
617 torture_close_connection(cli2);
625 testing of delayed update of write_time
627 BOOL torture_delay_write(void)
629 struct smbcli_state *cli;
633 if (!torture_open_connection(&cli)) {
637 mem_ctx = talloc_init("torture_delay_write");
639 ret &= test_finfo_after_write(cli, mem_ctx);
640 ret &= test_delayed_write_update(cli, mem_ctx);
641 ret &= test_delayed_write_update2(cli, mem_ctx);
643 torture_close_connection(cli);
644 talloc_free(mem_ctx);