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";
128 printf("Testing delayed update of write time using 2 connections\n");
130 if (!torture_open_connection(&cli2)) {
134 if (!torture_setup_dir(cli, BASEDIR)) {
138 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
140 printf("Failed to open %s\n", fname);
144 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
145 finfo1.basic_info.in.fnum = fnum1;
148 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
150 if (!NT_STATUS_IS_OK(status)) {
151 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
155 printf("Initial write time %s\n",
156 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
158 /* 3 second delay to ensure we get past any 2 second time
159 granularity (older systems may have that) */
163 /* Try using setfileinfo instead of write to update write time. */
164 union smb_setfileinfo sfinfo;
165 time_t t_set = time(NULL);
166 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
167 sfinfo.basic_info.file.fnum = fnum1;
168 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
169 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
171 /* I tried this with both + and - ve to see if it makes a different.
172 It doesn't - once the filetime is set via setfileinfo it stays that way. */
174 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
176 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
178 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
179 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
181 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
183 if (!NT_STATUS_IS_OK(status)) {
184 DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
191 while (time(NULL) < t+120) {
192 finfo2.basic_info.in.fname = fname;
194 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
196 if (!NT_STATUS_IS_OK(status)) {
197 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
201 printf("write time %s\n",
202 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
203 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
204 printf("Server updated write_time after %d seconds\n",
205 (int)(time(NULL) - t));
212 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
213 printf("Server did not update write time?!\n");
217 /* Now try a write to see if the write time gets reset. */
219 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
220 finfo1.basic_info.in.fnum = fnum1;
223 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
225 if (!NT_STATUS_IS_OK(status)) {
226 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
230 printf("Modified write time %s\n",
231 nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
234 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
237 printf("write failed - wrote %d bytes (%s)\n", written, __location__);
243 /* Once the time was set using setfileinfo then it stays set - writes
244 don't have any effect. But make sure. */
246 while (time(NULL) < t+40) {
247 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
249 if (!NT_STATUS_IS_OK(status)) {
250 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
254 printf("write time %s\n",
255 nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
256 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
257 printf("Server updated write_time after %d seconds\n",
258 (int)(time(NULL) - t));
265 if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
266 printf("Server did not update write time?!\n");
269 /* One more test to do. We should read the filetime via findfirst on the
270 second connection to ensure it's the same. This is very easy for a Windows
271 server but a bastard to get right on a POSIX server. JRA. */
274 torture_close_connection(cli2);
277 smbcli_close(cli->tree, fnum1);
278 smbcli_unlink(cli->tree, fname);
279 smbcli_deltree(cli->tree, BASEDIR);
285 /* Windows does obviously not update the stat info during a write call. I
286 * *think* this is the problem causing a spurious Excel 2003 on XP error
287 * message when saving a file. Excel does a setfileinfo, writes, and then does
288 * a getpath(!)info. Or so... For Samba sometimes it displays an error message
289 * that the file might have been changed in between. What i've been able to
290 * trace down is that this happens if the getpathinfo after the write shows a
291 * different last write time than the setfileinfo showed. This is really
295 static BOOL test_finfo_after_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
297 union smb_fileinfo finfo1, finfo2;
298 const char *fname = BASEDIR "\\torture_file.txt";
304 struct smbcli_state *cli2=NULL;
306 printf("Testing finfo update on close\n");
308 if (!torture_setup_dir(cli, BASEDIR)) {
312 fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
318 finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
319 finfo1.basic_info.in.fnum = fnum1;
321 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
323 if (!NT_STATUS_IS_OK(status)) {
324 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
331 written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
334 printf("(%s) written gave %d - should have been 1\n",
335 __location__, written);
340 if (!torture_open_connection(&cli2)) {
344 fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
346 printf("(%s) failed to open 2nd time - %s\n",
347 __location__, smbcli_errstr(cli2->tree));
352 written = smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
355 printf("(%s) written gave %d - should have been 1\n",
356 __location__, written);
361 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
362 finfo2.basic_info.in.fname = fname;
364 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
366 if (!NT_STATUS_IS_OK(status)) {
367 DEBUG(0, ("(%s) fileinfo failed: %s\n",
368 __location__, nt_errstr(status)));
373 if (finfo1.basic_info.out.create_time !=
374 finfo2.basic_info.out.create_time) {
375 printf("(%s) create_time changed\n", __location__);
380 if (finfo1.basic_info.out.access_time !=
381 finfo2.basic_info.out.access_time) {
382 printf("(%s) access_time changed\n", __location__);
387 if (finfo1.basic_info.out.write_time !=
388 finfo2.basic_info.out.write_time) {
389 printf("(%s) write_time changed\n", __location__);
394 if (finfo1.basic_info.out.change_time !=
395 finfo2.basic_info.out.change_time) {
396 printf("(%s) change_time changed\n", __location__);
401 /* One of the two following calls updates the qpathinfo. */
403 /* If you had skipped the smbcli_write on fnum2, it would
404 * *not* have updated the stat on disk */
406 smbcli_close(cli2->tree, fnum2);
407 torture_close_connection(cli2);
410 /* This call is only for the people looking at ethereal :-) */
411 finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
412 finfo2.basic_info.in.fname = fname;
414 status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2);
416 if (!NT_STATUS_IS_OK(status)) {
417 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
424 smbcli_close(cli->tree, fnum1);
425 smbcli_unlink(cli->tree, fname);
426 smbcli_deltree(cli->tree, BASEDIR);
428 torture_close_connection(cli2);
436 testing of delayed update of write_time
438 BOOL torture_delay_write(void)
440 struct smbcli_state *cli;
444 if (!torture_open_connection(&cli)) {
448 mem_ctx = talloc_init("torture_delay_write");
450 ret &= test_finfo_after_write(cli, mem_ctx);
451 ret &= test_delayed_write_update(cli, mem_ctx);
452 ret &= test_delayed_write_update2(cli, mem_ctx);
454 torture_close_connection(cli);
455 talloc_destroy(mem_ctx);