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