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