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 "libcli/raw/libcliraw.h"
27 #include "system/time.h"
28 #include "system/filesys.h"
30 #define BASEDIR "\\delaywrite"
32 static BOOL test_delayed_write_update(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
34 union smb_fileinfo finfo1, finfo2;
35 const char *fname = BASEDIR "\\torture_file.txt";
42 printf("Testing delayed update of write time\n");
44 if (!torture_setup_dir(cli, BASEDIR)) {
48 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
50 printf("Failed to open %s\n", fname);
54 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
55 finfo1.basic_info.in.fnum = fnum1;
58 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
60 if (!NT_STATUS_IS_OK(status)) {
61 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
65 printf("Initial write time %s\n",
66 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
68 /* 3 second delay to ensure we get past any 2 second time
69 granularity (older systems may have that) */
72 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
75 printf("write failed - wrote %d bytes (%s)\n",
76 (int)written, __location__);
82 while (time(NULL) < t+120) {
83 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
85 if (!NT_STATUS_IS_OK(status)) {
86 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
90 printf("write time %s\n",
91 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
92 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
93 printf("Server updated write_time after %d seconds\n",
94 (int)(time(NULL) - t));
101 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
102 printf("Server did not update write time?!\n");
108 smbcli_close(cli->tree, fnum1);
109 smbcli_unlink(cli->tree, fname);
110 smbcli_deltree(cli->tree, BASEDIR);
116 * Do as above, but using 2 connections.
119 static BOOL test_delayed_write_update2(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
121 struct smbcli_state *cli2=NULL;
122 union smb_fileinfo finfo1, finfo2;
123 const char *fname = BASEDIR "\\torture_file.txt";
130 struct smb_flush flsh;
132 printf("Testing delayed update of write time using 2 connections\n");
134 if (!torture_open_connection(&cli2)) {
138 if (!torture_setup_dir(cli, BASEDIR)) {
142 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
144 printf("Failed to open %s\n", fname);
148 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
149 finfo1.basic_info.in.fnum = fnum1;
152 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
154 if (!NT_STATUS_IS_OK(status)) {
155 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
159 printf("Initial write time %s\n",
160 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
162 /* 3 second delay to ensure we get past any 2 second time
163 granularity (older systems may have that) */
167 /* Try using setfileinfo instead of write to update write time. */
168 union smb_setfileinfo sfinfo;
169 time_t t_set = time(NULL);
170 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
171 sfinfo.basic_info.file.fnum = fnum1;
172 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
173 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
175 /* I tried this with both + and - ve to see if it makes a different.
176 It doesn't - once the filetime is set via setfileinfo it stays that way. */
178 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
180 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
182 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
183 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
185 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
187 if (!NT_STATUS_IS_OK(status)) {
188 DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
195 while (time(NULL) < t+120) {
196 finfo2.basic_info.in.fname = fname;
198 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
200 if (!NT_STATUS_IS_OK(status)) {
201 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
205 printf("write time %s\n",
206 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
207 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
208 printf("Server updated write_time after %d seconds\n",
209 (int)(time(NULL) - t));
216 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
217 printf("Server did not update write time?!\n");
221 /* Now try a write to see if the write time gets reset. */
223 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
224 finfo1.basic_info.in.fnum = fnum1;
227 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
229 if (!NT_STATUS_IS_OK(status)) {
230 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
234 printf("Modified write time %s\n",
235 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
238 printf("Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
240 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
243 printf("write failed - wrote %d bytes (%s)\n",
244 (int)written, __location__);
248 /* Just to prove to tridge that the an smbflush has no effect on
249 the write time :-). The setfileinfo IS STICKY. JRA. */
251 printf("Doing flush after write\n");
253 flsh.in.fnum = fnum1;
254 status = smb_raw_flush(cli->tree, &flsh);
255 if (!NT_STATUS_IS_OK(status)) {
256 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
262 /* Once the time was set using setfileinfo then it stays set - writes
263 don't have any effect. But make sure. */
265 while (time(NULL) < t+15) {
266 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
268 if (!NT_STATUS_IS_OK(status)) {
269 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
273 printf("write time %s\n",
274 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
275 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
276 printf("Server updated write_time after %d seconds\n",
277 (int)(time(NULL) - t));
284 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
285 printf("Server did not update write time\n");
288 fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
290 printf("Failed to open %s\n", fname);
294 printf("Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
296 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
299 printf("write failed - wrote %d bytes (%s)\n",
300 (int)written, __location__);
304 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
306 if (!NT_STATUS_IS_OK(status)) {
307 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
310 printf("write time %s\n",
311 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
312 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
313 printf("Server updated write_time\n");
316 printf("Closing the first fd to see if write time updated.\n");
317 smbcli_close(cli->tree, fnum1);
320 printf("Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
322 written = smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
325 printf("write failed - wrote %d bytes (%s)\n",
326 (int)written, __location__);
330 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
331 finfo1.basic_info.in.fnum = fnum2;
333 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
335 if (!NT_STATUS_IS_OK(status)) {
336 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
339 printf("write time %s\n",
340 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
341 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
342 printf("Server updated write_time\n");
347 /* Once the time was set using setfileinfo then it stays set - writes
348 don't have any effect. But make sure. */
350 while (time(NULL) < t+15) {
351 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
353 if (!NT_STATUS_IS_OK(status)) {
354 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
358 printf("write time %s\n",
359 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
360 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
361 printf("Server updated write_time after %d seconds\n",
362 (int)(time(NULL) - t));
369 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
370 printf("Server did not update write time\n");
373 printf("Closing both fd's to see if write time updated.\n");
375 smbcli_close(cli->tree, fnum2);
378 fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
380 printf("Failed to open %s\n", fname);
384 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
385 finfo1.basic_info.in.fnum = fnum1;
388 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
390 if (!NT_STATUS_IS_OK(status)) {
391 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
395 printf("Second open initial write time %s\n",
396 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
399 printf("Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
401 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
404 printf("write failed - wrote %d bytes (%s)\n",
405 (int)written, __location__);
409 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
410 finfo1.basic_info.in.fnum = fnum1;
412 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
414 if (!NT_STATUS_IS_OK(status)) {
415 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
418 printf("write time %s\n",
419 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
420 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
421 printf("Server updated write_time\n");
426 /* Once the time was set using setfileinfo then it stays set - writes
427 don't have any effect. But make sure. */
429 while (time(NULL) < t+15) {
430 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
432 if (!NT_STATUS_IS_OK(status)) {
433 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
437 printf("write time %s\n",
438 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
439 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
440 printf("Server updated write_time after %d seconds\n",
441 (int)(time(NULL) - t));
448 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
449 printf("Server did not update write time\n");
453 /* One more test to do. We should read the filetime via findfirst on the
454 second connection to ensure it's the same. This is very easy for a Windows
455 server but a bastard to get right on a POSIX server. JRA. */
458 torture_close_connection(cli2);
461 smbcli_close(cli->tree, fnum1);
462 smbcli_unlink(cli->tree, fname);
463 smbcli_deltree(cli->tree, BASEDIR);
469 /* Windows does obviously not update the stat info during a write call. I
470 * *think* this is the problem causing a spurious Excel 2003 on XP error
471 * message when saving a file. Excel does a setfileinfo, writes, and then does
472 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
473 * that the file might have been changed in between. What i've been able to
474 * trace down is that this happens if the getpathinfo after the write shows a
475 * different last write time than the setfileinfo showed. This is really
479 static BOOL test_finfo_after_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
481 union smb_fileinfo finfo1, finfo2;
482 const char *fname = BASEDIR "\\torture_file.txt";
488 struct smbcli_state *cli2=NULL;
490 printf("Testing finfo update on close\n");
492 if (!torture_setup_dir(cli, BASEDIR)) {
496 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
502 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
503 finfo1.basic_info.in.fnum = fnum1;
505 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
507 if (!NT_STATUS_IS_OK(status)) {
508 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
515 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
518 printf("(%s) written gave %d - should have been 1\n",
519 __location__, (int)written);
524 if (!torture_open_connection(&cli2)) {
528 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
530 printf("(%s) failed to open 2nd time - %s\n",
531 __location__, smbcli_errstr(cli2->tree));
536 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
539 printf("(%s) written gave %d - should have been 1\n",
540 __location__, (int)written);
545 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
546 finfo2.basic_info.in.fname = fname;
548 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
550 if (!NT_STATUS_IS_OK(status)) {
551 DEBUG(0, ("(%s) fileinfo failed: %s\n",
552 __location__, nt_errstr(status)));
557 if (finfo1.basic_info.out.create_time !=
558 finfo2.basic_info.out.create_time) {
559 printf("(%s) create_time changed\n", __location__);
564 if (finfo1.basic_info.out.access_time !=
565 finfo2.basic_info.out.access_time) {
566 printf("(%s) access_time changed\n", __location__);
571 if (finfo1.basic_info.out.write_time !=
572 finfo2.basic_info.out.write_time) {
573 printf("(%s) write_time changed\n", __location__);
574 printf("write time conn 1 = %s, conn 2 = %s\n",
575 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time),
576 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
581 if (finfo1.basic_info.out.change_time !=
582 finfo2.basic_info.out.change_time) {
583 printf("(%s) change_time changed\n", __location__);
588 /* One of the two following calls updates the qpathinfo. */
590 /* If you had skipped the smbcli_write on fnum2, it would
591 * *not* have updated the stat on disk */
593 smbcli_close(cli2->tree, fnum2);
594 torture_close_connection(cli2);
597 /* This call is only for the people looking at ethereal :-) */
598 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
599 finfo2.basic_info.in.fname = fname;
601 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2);
603 if (!NT_STATUS_IS_OK(status)) {
604 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
611 smbcli_close(cli->tree, fnum1);
612 smbcli_unlink(cli->tree, fname);
613 smbcli_deltree(cli->tree, BASEDIR);
615 torture_close_connection(cli2);
623 testing of delayed update of write_time
625 BOOL torture_delay_write(void)
627 struct smbcli_state *cli;
631 if (!torture_open_connection(&cli)) {
635 mem_ctx = talloc_init("torture_delay_write");
637 ret &= test_finfo_after_write(cli, mem_ctx);
638 ret &= test_delayed_write_update(cli, mem_ctx);
639 ret &= test_delayed_write_update2(cli, mem_ctx);
641 torture_close_connection(cli);
642 talloc_free(mem_ctx);