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"
29 #define BASEDIR "\\delaywrite"
31 static BOOL test_delayed_write_update(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
33 union smb_fileinfo finfo1, finfo2;
34 const char *fname = BASEDIR "\\torture_file.txt";
41 printf("Testing delayed update of write time\n");
43 if (!torture_setup_dir(cli, BASEDIR)) {
47 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
49 printf("Failed to open %s\n", fname);
53 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
54 finfo1.basic_info.in.fnum = fnum1;
57 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
59 if (!NT_STATUS_IS_OK(status)) {
60 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
64 printf("Initial write time %s\n",
65 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
67 /* 3 second delay to ensure we get past any 2 second time
68 granularity (older systems may have that) */
71 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
74 printf("write failed - wrote %d bytes (%s)\n", written, __location__);
80 while (time(NULL) < t+120) {
81 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
83 if (!NT_STATUS_IS_OK(status)) {
84 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
88 printf("write time %s\n",
89 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
90 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
91 printf("Server updated write_time after %d seconds\n",
92 (int)(time(NULL) - t));
99 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
100 printf("Server did not update write time?!\n");
106 smbcli_close(cli->tree, fnum1);
107 smbcli_unlink(cli->tree, fname);
108 smbcli_deltree(cli->tree, BASEDIR);
114 * Do as above, but using 2 connections.
117 static BOOL test_delayed_write_update2(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
119 struct smbcli_state *cli2=NULL;
120 union smb_fileinfo finfo1, finfo2;
121 const char *fname = BASEDIR "\\torture_file.txt";
127 struct smb_flush flsh;
129 printf("Testing delayed update of write time using 2 connections\n");
131 if (!torture_open_connection(&cli2)) {
135 if (!torture_setup_dir(cli, BASEDIR)) {
139 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
141 printf("Failed to open %s\n", fname);
145 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
146 finfo1.basic_info.in.fnum = fnum1;
149 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
151 if (!NT_STATUS_IS_OK(status)) {
152 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
156 printf("Initial write time %s\n",
157 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
159 /* 3 second delay to ensure we get past any 2 second time
160 granularity (older systems may have that) */
164 /* Try using setfileinfo instead of write to update write time. */
165 union smb_setfileinfo sfinfo;
166 time_t t_set = time(NULL);
167 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
168 sfinfo.basic_info.file.fnum = fnum1;
169 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
170 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
172 /* I tried this with both + and - ve to see if it makes a different.
173 It doesn't - once the filetime is set via setfileinfo it stays that way. */
175 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
177 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
179 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
180 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
182 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
184 if (!NT_STATUS_IS_OK(status)) {
185 DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
192 while (time(NULL) < t+120) {
193 finfo2.basic_info.in.fname = fname;
195 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
197 if (!NT_STATUS_IS_OK(status)) {
198 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
202 printf("write time %s\n",
203 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
204 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
205 printf("Server updated write_time after %d seconds\n",
206 (int)(time(NULL) - t));
213 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
214 printf("Server did not update write time?!\n");
218 /* Now try a write to see if the write time gets reset. */
220 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
221 finfo1.basic_info.in.fnum = fnum1;
224 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
226 if (!NT_STATUS_IS_OK(status)) {
227 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
231 printf("Modified write time %s\n",
232 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
235 printf("Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
237 written = smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
240 printf("write failed - wrote %d bytes (%s)\n", written, __location__);
244 /* Just to prove to tridge that the an smbflush has no effect on
245 the write time :-). The setfileinfo IS STICKY. JRA. */
247 printf("Doing flush after write\n");
249 flsh.in.fnum = fnum1;
250 status = smb_raw_flush(cli->tree, &flsh);
251 if (!NT_STATUS_IS_OK(status)) {
252 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
258 /* Once the time was set using setfileinfo then it stays set - writes
259 don't have any effect. But make sure. */
261 while (time(NULL) < t+40) {
262 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
264 if (!NT_STATUS_IS_OK(status)) {
265 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
269 printf("write time %s\n",
270 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
271 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
272 printf("Server updated write_time after %d seconds\n",
273 (int)(time(NULL) - t));
280 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
281 printf("Server did not update write time?!\n");
284 /* One more test to do. We should read the filetime via findfirst on the
285 second connection to ensure it's the same. This is very easy for a Windows
286 server but a bastard to get right on a POSIX server. JRA. */
289 torture_close_connection(cli2);
292 smbcli_close(cli->tree, fnum1);
293 smbcli_unlink(cli->tree, fname);
294 smbcli_deltree(cli->tree, BASEDIR);
300 /* Windows does obviously not update the stat info during a write call. I
301 * *think* this is the problem causing a spurious Excel 2003 on XP error
302 * message when saving a file. Excel does a setfileinfo, writes, and then does
303 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
304 * that the file might have been changed in between. What i've been able to
305 * trace down is that this happens if the getpathinfo after the write shows a
306 * different last write time than the setfileinfo showed. This is really
310 static BOOL test_finfo_after_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
312 union smb_fileinfo finfo1, finfo2;
313 const char *fname = BASEDIR "\\torture_file.txt";
319 struct smbcli_state *cli2=NULL;
321 printf("Testing finfo update on close\n");
323 if (!torture_setup_dir(cli, BASEDIR)) {
327 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
333 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
334 finfo1.basic_info.in.fnum = fnum1;
336 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
338 if (!NT_STATUS_IS_OK(status)) {
339 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
346 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
349 printf("(%s) written gave %d - should have been 1\n",
350 __location__, written);
355 if (!torture_open_connection(&cli2)) {
359 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
361 printf("(%s) failed to open 2nd time - %s\n",
362 __location__, smbcli_errstr(cli2->tree));
367 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
370 printf("(%s) written gave %d - should have been 1\n",
371 __location__, written);
376 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
377 finfo2.basic_info.in.fname = fname;
379 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
381 if (!NT_STATUS_IS_OK(status)) {
382 DEBUG(0, ("(%s) fileinfo failed: %s\n",
383 __location__, nt_errstr(status)));
388 if (finfo1.basic_info.out.create_time !=
389 finfo2.basic_info.out.create_time) {
390 printf("(%s) create_time changed\n", __location__);
395 if (finfo1.basic_info.out.access_time !=
396 finfo2.basic_info.out.access_time) {
397 printf("(%s) access_time changed\n", __location__);
402 if (finfo1.basic_info.out.write_time !=
403 finfo2.basic_info.out.write_time) {
404 printf("(%s) write_time changed\n", __location__);
409 if (finfo1.basic_info.out.change_time !=
410 finfo2.basic_info.out.change_time) {
411 printf("(%s) change_time changed\n", __location__);
416 /* One of the two following calls updates the qpathinfo. */
418 /* If you had skipped the smbcli_write on fnum2, it would
419 * *not* have updated the stat on disk */
421 smbcli_close(cli2->tree, fnum2);
422 torture_close_connection(cli2);
425 /* This call is only for the people looking at ethereal :-) */
426 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
427 finfo2.basic_info.in.fname = fname;
429 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2);
431 if (!NT_STATUS_IS_OK(status)) {
432 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
439 smbcli_close(cli->tree, fnum1);
440 smbcli_unlink(cli->tree, fname);
441 smbcli_deltree(cli->tree, BASEDIR);
443 torture_close_connection(cli2);
451 testing of delayed update of write_time
453 BOOL torture_delay_write(void)
455 struct smbcli_state *cli;
459 if (!torture_open_connection(&cli)) {
463 mem_ctx = talloc_init("torture_delay_write");
465 ret &= test_finfo_after_write(cli, mem_ctx);
466 ret &= test_delayed_write_update(cli, mem_ctx);
467 ret &= test_delayed_write_update2(cli, mem_ctx);
469 torture_close_connection(cli);
470 talloc_free(mem_ctx);