r14527: Fix build problems.
[kai/samba.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 "torture/torture.h"
27 #include "libcli/raw/libcliraw.h"
28 #include "system/time.h"
29 #include "system/filesys.h"
30 #include "libcli/libcli.h"
31 #include "torture/util.h"
32
33 #define BASEDIR "\\delaywrite"
34
35 static BOOL test_delayed_write_update(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
36 {
37         union smb_fileinfo finfo1, finfo2;
38         const char *fname = BASEDIR "\\torture_file.txt";
39         NTSTATUS status;
40         int fnum1 = -1;
41         BOOL ret = True;
42         ssize_t written;
43         time_t t;
44
45         printf("Testing delayed update of write time\n");
46
47         if (!torture_setup_dir(cli, BASEDIR)) {
48                 return False;
49         }
50
51         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
52         if (fnum1 == -1) {
53                 printf("Failed to open %s\n", fname);
54                 return False;
55         }
56
57         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
58         finfo1.basic_info.in.file.fnum = fnum1;
59         finfo2 = finfo1;
60
61         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
62
63         if (!NT_STATUS_IS_OK(status)) {
64                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
65                 return False;
66         }
67         
68         printf("Initial write time %s\n", 
69                nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
70
71         /* 3 second delay to ensure we get past any 2 second time
72            granularity (older systems may have that) */
73         sleep(3);
74
75         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
76
77         if (written != 1) {
78                 printf("write failed - wrote %d bytes (%s)\n", 
79                        (int)written, __location__);
80                 return False;
81         }
82
83         t = time(NULL);
84
85         while (time(NULL) < t+120) {
86                 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
87
88                 if (!NT_STATUS_IS_OK(status)) {
89                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
90                         ret = False;
91                         break;
92                 }
93                 printf("write time %s\n", 
94                        nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
95                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
96                         printf("Server updated write_time after %d seconds\n",
97                                (int)(time(NULL) - t));
98                         break;
99                 }
100                 sleep(1);
101                 fflush(stdout);
102         }
103         
104         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
105                 printf("Server did not update write time?!\n");
106                 ret = False;
107         }
108
109
110         if (fnum1 != -1)
111                 smbcli_close(cli->tree, fnum1);
112         smbcli_unlink(cli->tree, fname);
113         smbcli_deltree(cli->tree, BASEDIR);
114
115         return ret;
116 }
117
118 /* 
119  * Do as above, but using 2 connections.
120  */
121
122 static BOOL test_delayed_write_update2(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
123 {
124         struct smbcli_state *cli2=NULL;
125         union smb_fileinfo finfo1, finfo2;
126         const char *fname = BASEDIR "\\torture_file.txt";
127         NTSTATUS status;
128         int fnum1 = -1;
129         int fnum2 = -1;
130         BOOL ret = True;
131         ssize_t written;
132         time_t t;
133         union smb_flush flsh;
134
135         printf("Testing delayed update of write time using 2 connections\n");
136
137         if (!torture_open_connection(&cli2)) {
138                 return False;
139         }
140
141         if (!torture_setup_dir(cli, BASEDIR)) {
142                 return False;
143         }
144
145         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
146         if (fnum1 == -1) {
147                 printf("Failed to open %s\n", fname);
148                 return False;
149         }
150
151         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
152         finfo1.basic_info.in.file.fnum = fnum1;
153         finfo2 = finfo1;
154
155         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
156
157         if (!NT_STATUS_IS_OK(status)) {
158                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
159                 return False;
160         }
161         
162         printf("Initial write time %s\n", 
163                nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
164
165         /* 3 second delay to ensure we get past any 2 second time
166            granularity (older systems may have that) */
167         sleep(3);
168
169         {
170                 /* Try using setfileinfo instead of write to update write time. */
171                 union smb_setfileinfo sfinfo;
172                 time_t t_set = time(NULL);
173                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
174                 sfinfo.basic_info.in.file.fnum = fnum1;
175                 sfinfo.basic_info.in.create_time = finfo1.basic_info.out.create_time;
176                 sfinfo.basic_info.in.access_time = finfo1.basic_info.out.access_time;
177
178                 /* I tried this with both + and - ve to see if it makes a different.
179                    It doesn't - once the filetime is set via setfileinfo it stays that way. */
180 #if 1
181                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set - 30000);
182 #else
183                 unix_to_nt_time(&sfinfo.basic_info.in.write_time, t_set + 30000);
184 #endif
185                 sfinfo.basic_info.in.change_time = finfo1.basic_info.out.change_time;
186                 sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib;
187
188                 status = smb_raw_setfileinfo(cli->tree, &sfinfo);
189
190                 if (!NT_STATUS_IS_OK(status)) {
191                         DEBUG(0, ("sfileinfo failed: %s\n", nt_errstr(status)));
192                         return False;
193                 }
194         }
195
196         t = time(NULL);
197
198         while (time(NULL) < t+120) {
199                 finfo2.basic_info.in.file.path = fname;
200         
201                 status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
202
203                 if (!NT_STATUS_IS_OK(status)) {
204                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
205                         ret = False;
206                         break;
207                 }
208                 printf("write time %s\n", 
209                        nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
210                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
211                         printf("Server updated write_time after %d seconds\n",
212                                (int)(time(NULL) - t));
213                         break;
214                 }
215                 sleep(1);
216                 fflush(stdout);
217         }
218         
219         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
220                 printf("Server did not update write time?!\n");
221                 ret = False;
222         }
223
224         /* Now try a write to see if the write time gets reset. */
225
226         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
227         finfo1.basic_info.in.file.fnum = fnum1;
228         finfo2 = finfo1;
229
230         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
231
232         if (!NT_STATUS_IS_OK(status)) {
233                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
234                 return False;
235         }
236         
237         printf("Modified write time %s\n", 
238                nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
239
240
241         printf("Doing a 10 byte write to extend the file and see if this changes the last write time.\n");
242
243         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 1, 10);
244
245         if (written != 10) {
246                 printf("write failed - wrote %d bytes (%s)\n", 
247                        (int)written, __location__);
248                 return False;
249         }
250
251         /* Just to prove to tridge that the an smbflush has no effect on
252            the write time :-). The setfileinfo IS STICKY. JRA. */
253
254         printf("Doing flush after write\n");
255
256         flsh.flush.in.file.fnum = fnum1;
257         status = smb_raw_flush(cli->tree, &flsh);
258         if (!NT_STATUS_IS_OK(status)) {
259                 DEBUG(0, ("smbflush failed: %s\n", nt_errstr(status)));
260                 return False;
261         }
262
263         t = time(NULL);
264
265         /* Once the time was set using setfileinfo then it stays set - writes
266            don't have any effect. But make sure. */
267
268         while (time(NULL) < t+15) {
269                 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
270
271                 if (!NT_STATUS_IS_OK(status)) {
272                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
273                         ret = False;
274                         break;
275                 }
276                 printf("write time %s\n", 
277                        nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
278                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
279                         printf("Server updated write_time after %d seconds\n",
280                                (int)(time(NULL) - t));
281                         break;
282                 }
283                 sleep(1);
284                 fflush(stdout);
285         }
286         
287         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
288                 printf("Server did not update write time\n");
289         }
290
291         fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
292         if (fnum2 == -1) {
293                 printf("Failed to open %s\n", fname);
294                 return False;
295         }
296         
297         printf("Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
298
299         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 11, 10);
300
301         if (written != 10) {
302                 printf("write failed - wrote %d bytes (%s)\n", 
303                        (int)written, __location__);
304                 return False;
305         }
306
307         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
308
309         if (!NT_STATUS_IS_OK(status)) {
310                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
311                 return False;
312         }
313         printf("write time %s\n", 
314                nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
315         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
316                 printf("Server updated write_time\n");
317         }
318
319         printf("Closing the first fd to see if write time updated.\n");
320         smbcli_close(cli->tree, fnum1);
321         fnum1 = -1;
322
323         printf("Doing a 10 byte write to extend the file via second fd and see if this changes the last write time.\n");
324
325         written =  smbcli_write(cli->tree, fnum2, 0, "0123456789", 21, 10);
326
327         if (written != 10) {
328                 printf("write failed - wrote %d bytes (%s)\n", 
329                        (int)written, __location__);
330                 return False;
331         }
332
333         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
334         finfo1.basic_info.in.file.fnum = fnum2;
335         finfo2 = finfo1;
336         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
337
338         if (!NT_STATUS_IS_OK(status)) {
339                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
340                 return False;
341         }
342         printf("write time %s\n", 
343                nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
344         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
345                 printf("Server updated write_time\n");
346         }
347
348         t = time(NULL);
349
350         /* Once the time was set using setfileinfo then it stays set - writes
351            don't have any effect. But make sure. */
352
353         while (time(NULL) < t+15) {
354                 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
355
356                 if (!NT_STATUS_IS_OK(status)) {
357                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
358                         ret = False;
359                         break;
360                 }
361                 printf("write time %s\n", 
362                        nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
363                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
364                         printf("Server updated write_time after %d seconds\n",
365                                (int)(time(NULL) - t));
366                         break;
367                 }
368                 sleep(1);
369                 fflush(stdout);
370         }
371         
372         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
373                 printf("Server did not update write time\n");
374         }
375
376         printf("Closing both fd's to see if write time updated.\n");
377
378         smbcli_close(cli->tree, fnum2);
379         fnum2 = -1;
380
381         fnum1 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
382         if (fnum1 == -1) {
383                 printf("Failed to open %s\n", fname);
384                 return False;
385         }
386
387         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
388         finfo1.basic_info.in.file.fnum = fnum1;
389         finfo2 = finfo1;
390
391         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
392
393         if (!NT_STATUS_IS_OK(status)) {
394                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
395                 return False;
396         }
397         
398         printf("Second open initial write time %s\n", 
399                nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
400
401         sleep(10);
402         printf("Doing a 10 byte write to extend the file to see if this changes the last write time.\n");
403
404         written =  smbcli_write(cli->tree, fnum1, 0, "0123456789", 31, 10);
405
406         if (written != 10) {
407                 printf("write failed - wrote %d bytes (%s)\n", 
408                        (int)written, __location__);
409                 return False;
410         }
411
412         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
413         finfo1.basic_info.in.file.fnum = fnum1;
414         finfo2 = finfo1;
415         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
416
417         if (!NT_STATUS_IS_OK(status)) {
418                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
419                 return False;
420         }
421         printf("write time %s\n", 
422                nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
423         if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
424                 printf("Server updated write_time\n");
425         }
426
427         t = time(NULL);
428
429         /* Once the time was set using setfileinfo then it stays set - writes
430            don't have any effect. But make sure. */
431
432         while (time(NULL) < t+15) {
433                 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
434
435                 if (!NT_STATUS_IS_OK(status)) {
436                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
437                         ret = False;
438                         break;
439                 }
440                 printf("write time %s\n", 
441                        nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
442                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
443                         printf("Server updated write_time after %d seconds\n",
444                                (int)(time(NULL) - t));
445                         break;
446                 }
447                 sleep(1);
448                 fflush(stdout);
449         }
450         
451         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
452                 printf("Server did not update write time\n");
453         }
454
455
456         /* One more test to do. We should read the filetime via findfirst on the
457            second connection to ensure it's the same. This is very easy for a Windows
458            server but a bastard to get right on a POSIX server. JRA. */
459
460         if (cli2 != NULL) {
461                 torture_close_connection(cli2);
462         }
463         if (fnum1 != -1)
464                 smbcli_close(cli->tree, fnum1);
465         smbcli_unlink(cli->tree, fname);
466         smbcli_deltree(cli->tree, BASEDIR);
467
468         return ret;
469 }
470
471
472 /* Windows does obviously not update the stat info during a write call. I
473  * *think* this is the problem causing a spurious Excel 2003 on XP error
474  * message when saving a file. Excel does a setfileinfo, writes, and then does
475  * a getpath(!)info. Or so... For Samba sometimes it displays an error message
476  * that the file might have been changed in between. What i've been able to
477  * trace down is that this happens if the getpathinfo after the write shows a
478  * different last write time than the setfileinfo showed. This is really
479  * nasty....
480  */
481
482 static BOOL test_finfo_after_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
483 {
484         union smb_fileinfo finfo1, finfo2;
485         const char *fname = BASEDIR "\\torture_file.txt";
486         NTSTATUS status;
487         int fnum1 = -1;
488         int fnum2;
489         BOOL ret = True;
490         ssize_t written;
491         struct smbcli_state *cli2=NULL;
492
493         printf("Testing finfo update on close\n");
494
495         if (!torture_setup_dir(cli, BASEDIR)) {
496                 return False;
497         }
498
499         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
500         if (fnum1 == -1) {
501                 ret = False;
502                 goto done;
503         }
504
505         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
506         finfo1.basic_info.in.file.fnum = fnum1;
507
508         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
509
510         if (!NT_STATUS_IS_OK(status)) {
511                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
512                 ret = False;
513                 goto done;
514         }
515
516         msleep(1000);
517
518         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
519
520         if (written != 1) {
521                 printf("(%s) written gave %d - should have been 1\n", 
522                        __location__, (int)written);
523                 ret = False;
524                 goto done;
525         }
526
527         if (!torture_open_connection(&cli2)) {
528                 return False;
529         }
530
531         fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
532         if (fnum2 == -1) {
533                 printf("(%s) failed to open 2nd time - %s\n", 
534                        __location__, smbcli_errstr(cli2->tree));
535                 ret = False;
536                 goto done;
537         }
538         
539         written =  smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
540         
541         if (written != 1) {
542                 printf("(%s) written gave %d - should have been 1\n", 
543                        __location__, (int)written);
544                 ret = False;
545                 goto done;
546         }
547         
548         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
549         finfo2.basic_info.in.file.path = fname;
550         
551         status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
552         
553         if (!NT_STATUS_IS_OK(status)) {
554                 DEBUG(0, ("(%s) fileinfo failed: %s\n", 
555                           __location__, nt_errstr(status)));
556                 ret = False;
557                 goto done;
558         }
559         
560         if (finfo1.basic_info.out.create_time !=
561             finfo2.basic_info.out.create_time) {
562                 printf("(%s) create_time changed\n", __location__);
563                 ret = False;
564                 goto done;
565         }
566         
567         if (finfo1.basic_info.out.access_time !=
568             finfo2.basic_info.out.access_time) {
569                 printf("(%s) access_time changed\n", __location__);
570                 ret = False;
571                 goto done;
572         }
573         
574         if (finfo1.basic_info.out.write_time !=
575             finfo2.basic_info.out.write_time) {
576                 printf("(%s) write_time changed\n", __location__);
577                 printf("write time conn 1 = %s, conn 2 = %s\n", 
578                        nt_time_string(mem_ctx, finfo1.basic_info.out.write_time),
579                        nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
580                 ret = False;
581                 goto done;
582         }
583         
584         if (finfo1.basic_info.out.change_time !=
585             finfo2.basic_info.out.change_time) {
586                 printf("(%s) change_time changed\n", __location__);
587                 ret = False;
588                 goto done;
589         }
590         
591         /* One of the two following calls updates the qpathinfo. */
592         
593         /* If you had skipped the smbcli_write on fnum2, it would
594          * *not* have updated the stat on disk */
595         
596         smbcli_close(cli2->tree, fnum2);
597         torture_close_connection(cli2);
598         cli2 = NULL;
599
600         /* This call is only for the people looking at ethereal :-) */
601         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
602         finfo2.basic_info.in.file.path = fname;
603
604         status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2);
605
606         if (!NT_STATUS_IS_OK(status)) {
607                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
608                 ret = False;
609                 goto done;
610         }
611
612  done:
613         if (fnum1 != -1)
614                 smbcli_close(cli->tree, fnum1);
615         smbcli_unlink(cli->tree, fname);
616         smbcli_deltree(cli->tree, BASEDIR);
617         if (cli2 != NULL) {
618                 torture_close_connection(cli2);
619         }
620
621         return ret;
622 }
623
624
625 /* 
626    testing of delayed update of write_time
627 */
628 BOOL torture_delay_write(void)
629 {
630         struct smbcli_state *cli;
631         BOOL ret = True;
632         TALLOC_CTX *mem_ctx;
633
634         if (!torture_open_connection(&cli)) {
635                 return False;
636         }
637
638         mem_ctx = talloc_init("torture_delay_write");
639
640         ret &= test_finfo_after_write(cli, mem_ctx);
641         ret &= test_delayed_write_update(cli, mem_ctx);
642         ret &= test_delayed_write_update2(cli, mem_ctx);
643
644         torture_close_connection(cli);
645         talloc_free(mem_ctx);
646         return ret;
647 }