r3699: - split the delayed write testing out of RAW-WRITE, as it is not yet
[amitay/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    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "libcli/raw/libcliraw.h"
26 #include "system/time.h"
27
28 #define BASEDIR "\\delaywrite"
29
30 static BOOL test_delayed_write_update(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
31 {
32         union smb_fileinfo finfo1, finfo2;
33         const char *fname = BASEDIR "\\torture_file.txt";
34         NTSTATUS status;
35         int fnum1 = -1;
36         BOOL ret = True;
37         ssize_t written;
38         time_t t;
39
40         printf("Testing delayed update of write time\n");
41
42         if (!torture_setup_dir(cli, BASEDIR)) {
43                 return False;
44         }
45
46         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
47         if (fnum1 == -1) {
48                 printf("Failed to open %s\n", fname);
49                 return False;
50         }
51
52         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
53         finfo1.basic_info.in.fnum = fnum1;
54         finfo2 = finfo1;
55
56         status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1);
57
58         if (!NT_STATUS_IS_OK(status)) {
59                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
60                 return False;
61         }
62         
63         printf("Initial write time %s\n", 
64                nt_time_string(mem_ctx, finfo1.basic_info.out.write_time));
65
66         /* 3 second delay to ensure we get past any 2 second time
67            granularity (older systems may have that) */
68         sleep(3);
69
70         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
71
72         if (written != 1) {
73                 printf("write failed - wrote %d bytes (%s)\n", written, __location__);
74                 return False;
75         }
76
77         t = time(NULL);
78
79         while (time(NULL) < t+120) {
80                 status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2);
81
82                 if (!NT_STATUS_IS_OK(status)) {
83                         DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
84                         ret = False;
85                         break;
86                 }
87                 printf("write time %s\n", 
88                        nt_time_string(mem_ctx, finfo2.basic_info.out.write_time));
89                 if (finfo1.basic_info.out.write_time != finfo2.basic_info.out.write_time) {
90                         printf("Server updated write_time after %d seconds\n",
91                                (int)(time(NULL) - t));
92                         break;
93                 }
94                 sleep(1);
95                 fflush(stdout);
96         }
97         
98         if (finfo1.basic_info.out.write_time == finfo2.basic_info.out.write_time) {
99                 printf("Server did not update write time?!\n");
100                 ret = False;
101         }
102
103
104         if (fnum1 != -1)
105                 smbcli_close(cli->tree, fnum1);
106         smbcli_unlink(cli->tree, fname);
107         smbcli_deltree(cli->tree, BASEDIR);
108
109         return ret;
110 }
111
112
113 /* Windows does obviously not update the stat info during a write call. I
114  * *think* this is the problem causing a spurious Excel 2003 on XP error
115  * message when saving a file. Excel does a setfileinfo, writes, and then does
116  * a getpath(!)info. Or so... For Samba sometimes it displays an error message
117  * that the file might have been changed in between. What i've been able to
118  * trace down is that this happens if the getpathinfo after the write shows a
119  * different last write time than the setfileinfo showed. This is really
120  * nasty....
121  */
122
123 static BOOL test_finfo_after_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
124 {
125         union smb_fileinfo finfo1, finfo2;
126         const char *fname = BASEDIR "\\torture_file.txt";
127         NTSTATUS status;
128         int fnum1 = -1;
129         int fnum2;
130         BOOL ret = True;
131         ssize_t written;
132         struct smbcli_state *cli2=NULL;
133
134         printf("Testing finfo update on close\n");
135
136         if (!torture_setup_dir(cli, BASEDIR)) {
137                 return False;
138         }
139
140         fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
141         if (fnum1 == -1) {
142                 ret = False;
143                 goto done;
144         }
145
146         finfo1.basic_info.level = RAW_FILEINFO_BASIC_INFO;
147         finfo1.basic_info.in.fnum = fnum1;
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                 ret = False;
154                 goto done;
155         }
156
157         msleep(1000);
158
159         written =  smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
160
161         if (written != 1) {
162                 printf("(%s) written gave %d - should have been 1\n", 
163                        __location__, written);
164                 ret = False;
165                 goto done;
166         }
167
168         if (!torture_open_connection(&cli2)) {
169                 return False;
170         }
171
172         fnum2 = smbcli_open(cli2->tree, fname, O_RDWR, DENY_NONE);
173         if (fnum2 == -1) {
174                 printf("(%s) failed to open 2nd time - %s\n", 
175                        __location__, smbcli_errstr(cli2->tree));
176                 ret = False;
177                 goto done;
178         }
179         
180         written =  smbcli_write(cli2->tree, fnum2, 0, "x", 0, 1);
181         
182         if (written != 1) {
183                 printf("(%s) written gave %d - should have been 1\n", 
184                        __location__, written);
185                 ret = False;
186                 goto done;
187         }
188         
189         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
190         finfo2.basic_info.in.fname = fname;
191         
192         status = smb_raw_pathinfo(cli2->tree, mem_ctx, &finfo2);
193         
194         if (!NT_STATUS_IS_OK(status)) {
195                 DEBUG(0, ("(%s) fileinfo failed: %s\n", 
196                           __location__, nt_errstr(status)));
197                 ret = False;
198                 goto done;
199         }
200         
201         if (finfo1.basic_info.out.create_time !=
202             finfo2.basic_info.out.create_time) {
203                 printf("(%s) create_time changed\n", __location__);
204                 ret = False;
205                 goto done;
206         }
207         
208         if (finfo1.basic_info.out.access_time !=
209             finfo2.basic_info.out.access_time) {
210                 printf("(%s) access_time changed\n", __location__);
211                 ret = False;
212                 goto done;
213         }
214         
215         if (finfo1.basic_info.out.write_time !=
216             finfo2.basic_info.out.write_time) {
217                 printf("(%s) write_time changed\n", __location__);
218                 ret = False;
219                 goto done;
220         }
221         
222         if (finfo1.basic_info.out.change_time !=
223             finfo2.basic_info.out.change_time) {
224                 printf("(%s) change_time changed\n", __location__);
225                 ret = False;
226                 goto done;
227         }
228         
229         /* One of the two following calls updates the qpathinfo. */
230         
231         /* If you had skipped the smbcli_write on fnum2, it would
232          * *not* have updated the stat on disk */
233         
234         smbcli_close(cli2->tree, fnum2);
235         torture_close_connection(cli2);
236         cli2 = NULL;
237
238         /* This call is only for the people looking at ethereal :-) */
239         finfo2.basic_info.level = RAW_FILEINFO_BASIC_INFO;
240         finfo2.basic_info.in.fname = fname;
241
242         status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2);
243
244         if (!NT_STATUS_IS_OK(status)) {
245                 DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
246                 ret = False;
247                 goto done;
248         }
249
250  done:
251         if (fnum1 != -1)
252                 smbcli_close(cli->tree, fnum1);
253         smbcli_unlink(cli->tree, fname);
254         smbcli_deltree(cli->tree, BASEDIR);
255         if (cli2 != NULL) {
256                 torture_close_connection(cli2);
257         }
258
259         return ret;
260 }
261
262
263 /* 
264    testing of delayed update of write_time
265 */
266 BOOL torture_delay_write(void)
267 {
268         struct smbcli_state *cli;
269         BOOL ret = True;
270         TALLOC_CTX *mem_ctx;
271
272         if (!torture_open_connection(&cli)) {
273                 return False;
274         }
275
276         mem_ctx = talloc_init("torture_delay_write");
277
278         ret &= test_finfo_after_write(cli, mem_ctx);
279         ret &= test_delayed_write_update(cli, mem_ctx);
280
281         torture_close_connection(cli);
282         talloc_destroy(mem_ctx);
283         return ret;
284 }