r5037: got rid of all of the TALLOC_DEPRECATED stuff. My apologies for the
[gd/samba-autobuild/.git] / source4 / torture / basic / delaywrite.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    test suite for delayed write update 
5
6    Copyright (C) Volker Lendecke 2004
7    Copyright (C) Andrew Tridgell 2004
8    Copyright (C) Jeremy Allison 2004
9    
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.
14    
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.
19    
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.
23 */
24
25 #include "includes.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "system/time.h"
28
29 #define BASEDIR "\\delaywrite"
30
31 static BOOL test_delayed_write_update(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
32 {
33         union smb_fileinfo finfo1, finfo2;
34         const char *fname = BASEDIR "\\torture_file.txt";
35         NTSTATUS status;
36         int fnum1 = -1;
37         BOOL ret = True;
38         ssize_t written;
39         time_t t;
40
41         printf("Testing delayed update of write time\n");
42
43         if (!torture_setup_dir(cli, BASEDIR)) {
44                 return False;
45         }
46
47         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
48         if (fnum1 == -1) {
49                 printf("Failed to open %s\n", fname);
50                 return False;
51         }
52
53         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
54         finfo1.basic_info.in.fnum = fnum1;
55         finfo2 = finfo1;
56
57         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
58
59         if (!NT_STATUS_IS_OK(status)) {
60                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
61                 return False;
62         }
63         
64         printf("Initial write time %s\n", 
65                nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
66
67         /* 3 second delay to ensure we get past any 2 second time
68            granularity (older systems may have that) */
69         sleep(3);
70
71         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
72
73         if (written != 1) {
74                 printf("write failed - wrote %d bytes (%s)\n", written, __location__);
75                 return False;
76         }
77
78         t = time(NULL);
79
80         while (time(NULL) < t+120) {
81                 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
82
83                 if (!NT_STATUS_IS_OK(status)) {
84                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
85                         ret = False;
86                         break;
87                 }
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));
93                         break;
94                 }
95                 sleep(1);
96                 fflush(stdout);
97         }
98         
99         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
100                 printf("Server did not update write time?!\n");
101                 ret = False;
102         }
103
104
105         if (fnum1 != -1)
106                 smbcli_close(cli->tree, fnum1);
107         smbcli_unlink(cli->tree, fname);
108         smbcli_deltree(cli->tree, BASEDIR);
109
110         return ret;
111 }
112
113 /* 
114  * Do as above, but using 2 connections.
115  */
116
117 static BOOL test_delayed_write_update2(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
118 {
119         struct smbcli_state *cli2=NULL;
120         union smb_fileinfo finfo1, finfo2;
121         const char *fname = BASEDIR "\\torture_file.txt";
122         NTSTATUS status;
123         int fnum1 = -1;
124         BOOL ret = True;
125         ssize_t written;
126         time_t t;
127         struct smb_flush flsh;
128
129         printf("Testing delayed update of write time using 2 connections\n");
130
131         if (!torture_open_connection(&cli2)) {
132                 return False;
133         }
134
135         if (!torture_setup_dir(cli, BASEDIR)) {
136                 return False;
137         }
138
139         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
140         if (fnum1 == -1) {
141                 printf("Failed to open %s\n", fname);
142                 return False;
143         }
144
145         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
146         finfo1.basic_info.in.fnum = fnum1;
147         finfo2 = finfo1;
148
149         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
150
151         if (!NT_STATUS_IS_OK(status)) {
152                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
153                 return False;
154         }
155         
156         printf("Initial write time %s\n", 
157                nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
158
159         /* 3 second delay to ensure we get past any 2 second time
160            granularity (older systems may have that) */
161         sleep(3);
162
163         {
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;
171
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. */
174 #if 1
175                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
176 #else
177                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
178 #endif
179                 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
180                 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
181
182                 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
183
184                 if (!NT_STATUS_IS_OK(status)) {
185                         DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
186                         return False;
187                 }
188         }
189
190         t = time(NULL);
191
192         while (time(NULL) < t+120) {
193                 finfo2.basic_info.in.fname = fname;
194         
195                 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
196
197                 if (!NT_STATUS_IS_OK(status)) {
198                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
199                         ret = False;
200                         break;
201                 }
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));
207                         break;
208                 }
209                 sleep(1);
210                 fflush(stdout);
211         }
212         
213         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
214                 printf("Server did not update write time?!\n");
215                 ret = False;
216         }
217
218         /* Now try a write to see if the write time gets reset. */
219
220         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
221         finfo1.basic_info.in.fnum = fnum1;
222         finfo2 = finfo1;
223
224         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
225
226         if (!NT_STATUS_IS_OK(status)) {
227                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
228                 return False;
229         }
230         
231         printf("Modified write time %s\n", 
232                nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
233
234
235         printf("Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
236
237         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
238
239         if (written != 10) {
240                 printf("write failed - wrote %d bytes (%s)\n", written, __location__);
241                 return False;
242         }
243
244         /* Just to prove to tridge that the an smbflush has no effect on
245            the write time :-). The setfileinfo IS STICKY. JRA. */
246
247         printf("Doing flush after write\n");
248
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)));
253                 return False;
254         }
255
256         t = time(NULL);
257
258         /* Once the time was set using setfileinfo then it stays set - writes
259            don't have any effect. But make sure. */
260
261         while (time(NULL) < t+40) {
262                 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
263
264                 if (!NT_STATUS_IS_OK(status)) {
265                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
266                         ret = False;
267                         break;
268                 }
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));
274                         break;
275                 }
276                 sleep(1);
277                 fflush(stdout);
278         }
279         
280         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
281                 printf("Server did not update write time?!\n");
282         }
283
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. */
287
288         if (cli2 != NULL) {
289                 torture_close_connection(cli2);
290         }
291         if (fnum1 != -1)
292                 smbcli_close(cli->tree, fnum1);
293         smbcli_unlink(cli->tree, fname);
294         smbcli_deltree(cli->tree, BASEDIR);
295
296         return ret;
297 }
298
299
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
307  * nasty....
308  */
309
310 static BOOL test_finfo_after_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
311 {
312         union smb_fileinfo finfo1, finfo2;
313         const char *fname = BASEDIR "\\torture_file.txt";
314         NTSTATUS status;
315         int fnum1 = -1;
316         int fnum2;
317         BOOL ret = True;
318         ssize_t written;
319         struct smbcli_state *cli2=NULL;
320
321         printf("Testing finfo update on close\n");
322
323         if (!torture_setup_dir(cli, BASEDIR)) {
324                 return False;
325         }
326
327         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
328         if (fnum1 == -1) {
329                 ret = False;
330                 goto done;
331         }
332
333         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
334         finfo1.basic_info.in.fnum = fnum1;
335
336         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
337
338         if (!NT_STATUS_IS_OK(status)) {
339                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
340                 ret = False;
341                 goto done;
342         }
343
344         msleep(1000);
345
346         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
347
348         if (written != 1) {
349                 printf("(%s) written gave %d - should have been 1\n", 
350                        __location__, written);
351                 ret = False;
352                 goto done;
353         }
354
355         if (!torture_open_connection(&cli2)) {
356                 return False;
357         }
358
359         fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
360         if (fnum2 == -1) {
361                 printf("(%s) failed to open 2nd time - %s\n", 
362                        __location__, smbcli_errstr(cli2->tree));
363                 ret = False;
364                 goto done;
365         }
366         
367         written =  smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
368         
369         if (written != 1) {
370                 printf("(%s) written gave %d - should have been 1\n", 
371                        __location__, written);
372                 ret = False;
373                 goto done;
374         }
375         
376         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
377         finfo2.basic_info.in.fname = fname;
378         
379         status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
380         
381         if (!NT_STATUS_IS_OK(status)) {
382                 DEBUG(0, ("(%s) fileinfo failed: %s\n", 
383                           __location__, nt_errstr(status)));
384                 ret = False;
385                 goto done;
386         }
387         
388         if (finfo1.basic_info.out.create_time !=
389             finfo2.basic_info.out.create_time) {
390                 printf("(%s) create_time changed\n", __location__);
391                 ret = False;
392                 goto done;
393         }
394         
395         if (finfo1.basic_info.out.access_time !=
396             finfo2.basic_info.out.access_time) {
397                 printf("(%s) access_time changed\n", __location__);
398                 ret = False;
399                 goto done;
400         }
401         
402         if (finfo1.basic_info.out.write_time !=
403             finfo2.basic_info.out.write_time) {
404                 printf("(%s) write_time changed\n", __location__);
405                 ret = False;
406                 goto done;
407         }
408         
409         if (finfo1.basic_info.out.change_time !=
410             finfo2.basic_info.out.change_time) {
411                 printf("(%s) change_time changed\n", __location__);
412                 ret = False;
413                 goto done;
414         }
415         
416         /* One of the two following calls updates the qpathinfo. */
417         
418         /* If you had skipped the smbcli_write on fnum2, it would
419          * *not* have updated the stat on disk */
420         
421         smbcli_close(cli2->tree, fnum2);
422         torture_close_connection(cli2);
423         cli2 = NULL;
424
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;
428
429         status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2);
430
431         if (!NT_STATUS_IS_OK(status)) {
432                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
433                 ret = False;
434                 goto done;
435         }
436
437  done:
438         if (fnum1 != -1)
439                 smbcli_close(cli->tree, fnum1);
440         smbcli_unlink(cli->tree, fname);
441         smbcli_deltree(cli->tree, BASEDIR);
442         if (cli2 != NULL) {
443                 torture_close_connection(cli2);
444         }
445
446         return ret;
447 }
448
449
450 /* 
451    testing of delayed update of write_time
452 */
453 BOOL torture_delay_write(void)
454 {
455         struct smbcli_state *cli;
456         BOOL ret = True;
457         TALLOC_CTX *mem_ctx;
458
459         if (!torture_open_connection(&cli)) {
460                 return False;
461         }
462
463         mem_ctx = talloc_init("torture_delay_write");
464
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);
468
469         torture_close_connection(cli);
470         talloc_free(mem_ctx);
471         return ret;
472 }