810667134a011f3c2a7f9c5fcd2dafdea52627e8
[jelmer/samba4-debian.git] / source / torture / raw / eas.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    test DOS extended attributes
5
6    Copyright (C) Andrew Tridgell 2004
7    Copyright (C) Guenter Kukkukk 2005
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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "torture/torture.h"
25 #include "libcli/raw/libcliraw.h"
26 #include "libcli/libcli.h"
27 #include "torture/util.h"
28 #include "param/param.h"
29
30 #define BASEDIR "\\testeas"
31
32 #define CHECK_STATUS(status, correct) do { \
33         if (!NT_STATUS_EQUAL(status, correct)) { \
34                 printf("(%s) Incorrect status %s - should be %s\n", \
35                        __location__, nt_errstr(status), nt_errstr(correct)); \
36                 ret = False; \
37                 goto done; \
38         }} while (0)
39
40 static  BOOL maxeadebug; /* need that here, to allow no file delete in debug case */
41
42 static BOOL check_ea(struct smbcli_state *cli, 
43                      const char *fname, const char *eaname, const char *value)
44 {
45         NTSTATUS status = torture_check_ea(cli, fname, eaname, value);
46         return NT_STATUS_IS_OK(status);
47 }
48
49 static BOOL test_eas(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
50 {
51         NTSTATUS status;
52         union smb_setfileinfo setfile;
53         union smb_open io;
54         const char *fname = BASEDIR "\\ea.txt";
55         BOOL ret = True;
56         int fnum = -1;
57
58         printf("TESTING SETFILEINFO EA_SET\n");
59
60         io.generic.level = RAW_OPEN_NTCREATEX;
61         io.ntcreatex.in.root_fid = 0;
62         io.ntcreatex.in.flags = 0;
63         io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
64         io.ntcreatex.in.create_options = 0;
65         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
66         io.ntcreatex.in.share_access = 
67                 NTCREATEX_SHARE_ACCESS_READ | 
68                 NTCREATEX_SHARE_ACCESS_WRITE;
69         io.ntcreatex.in.alloc_size = 0;
70         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
71         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
72         io.ntcreatex.in.security_flags = 0;
73         io.ntcreatex.in.fname = fname;
74         status = smb_raw_open(cli->tree, mem_ctx, &io);
75         CHECK_STATUS(status, NT_STATUS_OK);
76         fnum = io.ntcreatex.out.file.fnum;
77         
78         ret &= check_ea(cli, fname, "EAONE", NULL);
79
80         printf("Adding first two EAs\n");
81         setfile.generic.level = RAW_SFILEINFO_EA_SET;
82         setfile.generic.in.file.fnum = fnum;
83         setfile.ea_set.in.num_eas = 2;
84         setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
85         setfile.ea_set.in.eas[0].flags = 0;
86         setfile.ea_set.in.eas[0].name.s = "EAONE";
87         setfile.ea_set.in.eas[0].value = data_blob_string_const("VALUE1");
88         setfile.ea_set.in.eas[1].flags = 0;
89         setfile.ea_set.in.eas[1].name.s = "SECONDEA";
90         setfile.ea_set.in.eas[1].value = data_blob_string_const("ValueTwo");
91
92         status = smb_raw_setfileinfo(cli->tree, &setfile);
93         CHECK_STATUS(status, NT_STATUS_OK);
94
95         ret &= check_ea(cli, fname, "EAONE", "VALUE1");
96         ret &= check_ea(cli, fname, "SECONDEA", "ValueTwo");
97
98         printf("Modifying 2nd EA\n");
99         setfile.ea_set.in.num_eas = 1;
100         setfile.ea_set.in.eas[0].name.s = "SECONDEA";
101         setfile.ea_set.in.eas[0].value = data_blob_string_const(" Changed Value");
102         status = smb_raw_setfileinfo(cli->tree, &setfile);
103         CHECK_STATUS(status, NT_STATUS_OK);
104
105         ret &= check_ea(cli, fname, "EAONE", "VALUE1");
106         ret &= check_ea(cli, fname, "SECONDEA", " Changed Value");
107
108         printf("Setting a NULL EA\n");
109         setfile.ea_set.in.eas[0].value = data_blob(NULL, 0);
110         setfile.ea_set.in.eas[0].name.s = "NULLEA";
111         status = smb_raw_setfileinfo(cli->tree, &setfile);
112         CHECK_STATUS(status, NT_STATUS_OK);
113
114         ret &= check_ea(cli, fname, "EAONE", "VALUE1");
115         ret &= check_ea(cli, fname, "SECONDEA", " Changed Value");
116         ret &= check_ea(cli, fname, "NULLEA", NULL);
117
118         printf("Deleting first EA\n");
119         setfile.ea_set.in.eas[0].flags = 0;
120         setfile.ea_set.in.eas[0].name.s = "EAONE";
121         setfile.ea_set.in.eas[0].value = data_blob(NULL, 0);
122         status = smb_raw_setfileinfo(cli->tree, &setfile);
123         CHECK_STATUS(status, NT_STATUS_OK);
124
125         ret &= check_ea(cli, fname, "EAONE", NULL);
126         ret &= check_ea(cli, fname, "SECONDEA", " Changed Value");
127
128         printf("Deleting second EA\n");
129         setfile.ea_set.in.eas[0].flags = 0;
130         setfile.ea_set.in.eas[0].name.s = "SECONDEA";
131         setfile.ea_set.in.eas[0].value = data_blob(NULL, 0);
132         status = smb_raw_setfileinfo(cli->tree, &setfile);
133         CHECK_STATUS(status, NT_STATUS_OK);
134
135         ret &= check_ea(cli, fname, "EAONE", NULL);
136         ret &= check_ea(cli, fname, "SECONDEA", NULL);
137
138 done:
139         smbcli_close(cli->tree, fnum);
140         return ret;
141 }
142
143
144 /*
145  * Helper function to retrieve the max. ea size for one ea name
146  */
147 static int test_one_eamax(struct smbcli_state *cli, const int fnum, 
148                           const char *eaname, DATA_BLOB eablob, 
149                           const int eastart, const int eadebug) 
150 {
151         NTSTATUS status;
152         struct ea_struct eastruct;
153         union smb_setfileinfo setfile;
154         int i, high, low, maxeasize;
155
156         setfile.generic.level = RAW_SFILEINFO_EA_SET;
157         setfile.generic.in.file.fnum = fnum;
158         setfile.ea_set.in.num_eas = 1;
159         setfile.ea_set.in.eas = &eastruct;
160         setfile.ea_set.in.eas->flags = 0;
161         setfile.ea_set.in.eas->name.s = eaname;
162         setfile.ea_set.in.eas->value = eablob;
163
164         maxeasize = eablob.length;
165         i = eastart;
166         low = 0;
167         high = maxeasize;
168
169         do {
170                 if (eadebug) {
171                         printf ("Testing EA size: %d\n", i);
172                 }
173                 setfile.ea_set.in.eas->value.length = i;
174
175                 status = smb_raw_setfileinfo(cli->tree, &setfile);
176
177                 if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
178                         if (eadebug) {
179                                 printf ("[%s] EA size %d succeeded! "
180                                         "(high=%d low=%d)\n", 
181                                         eaname, i, high, low);
182                         }
183                         low = i;
184                         if (low == maxeasize) {
185                                 printf ("Max. EA size for \"%s\"=%d "
186                                         "[but could be possibly larger]\n", 
187                                         eaname, low);
188                                 break;
189                         }
190                         if (high - low == 1 && high != maxeasize) {
191                                 printf ("Max. EA size for \"%s\"=%d\n", 
192                                         eaname, low);
193                                 break;
194                         }
195                         i += (high - low + 1) / 2;
196                 } else {
197                         if (eadebug) {
198                                 printf ("[%s] EA size %d failed!    "
199                                         "(high=%d low=%d) [%s]\n", 
200                                         eaname, i, high, low, 
201                                         nt_errstr(status));
202                         }
203                         high = i;
204                         if (high - low <= 1) {
205                                 printf ("Max. EA size for \"%s\"=%d\n", 
206                                         eaname, low);
207                                 break;
208                         }
209                         i -= (high - low + 1) / 2;
210                 }
211         } while (True);
212
213         return low;
214 }
215
216 /*
217  * Test for maximum ea size - more than one ea name is checked.
218  *
219  * Additional parameters can be passed, to allow further testing:
220  *
221  *             default
222  * maxeasize    65536   limit the max. size for a single EA name
223  * maxeanames     101   limit of the number of tested names
224  * maxeastart       1   this EA size is used to test for the 1st EA (atm)
225  * maxeadebug       0   if set True, further debug output is done - in addition
226  *                      the testfile is not deleted for further inspection!
227  *
228  * Set some/all of these options on the cmdline with:
229  * --option torture:maxeasize=1024 --option torture:maxeadebug=1 ...
230  *
231  */
232 static BOOL test_max_eas(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
233 {
234         NTSTATUS status;
235         union smb_open io;
236         const char *fname = BASEDIR "\\ea_max.txt";
237         int fnum = -1;
238         BOOL ret = True;
239         BOOL err = False;
240
241         int       i, j, k, last, total;
242         DATA_BLOB eablob;
243         char      *eaname = NULL;
244         int       maxeasize;
245         int       maxeanames;
246         int       maxeastart;
247
248         printf("TESTING SETFILEINFO MAX. EA_SET\n");
249
250         maxeasize  = lp_parm_int(NULL, "torture", "maxeasize", 65536);
251         maxeanames = lp_parm_int(NULL, "torture", "maxeanames", 101);
252         maxeastart = lp_parm_int(NULL, "torture", "maxeastart", 1);
253         maxeadebug = lp_parm_int(NULL, "torture", "maxeadebug", 0);
254
255         /* Do some sanity check on possibly passed parms */
256         if (maxeasize <= 0) {
257                 printf("Invalid parameter 'maxeasize=%d'",maxeasize);
258                 err = True;
259         }
260         if (maxeanames <= 0) {
261                 printf("Invalid parameter 'maxeanames=%d'",maxeanames);
262                 err = True;
263         }
264         if (maxeastart <= 0) {
265                 printf("Invalid parameter 'maxeastart=%d'",maxeastart);
266                 err = True;
267         }
268         if (maxeadebug < 0) {
269                 printf("Invalid parameter 'maxeadebug=%d'",maxeadebug);
270                 err = True;
271         }
272         if (err) {
273           printf("\n\n");
274           goto done;
275         }
276         if (maxeastart > maxeasize) {
277                 maxeastart = maxeasize;
278                 printf ("'maxeastart' outside range - corrected to %d\n", 
279                         maxeastart);
280         }
281         printf("MAXEA parms: maxeasize=%d maxeanames=%d maxeastart=%d"
282                " maxeadebug=%d\n", maxeasize, maxeanames, maxeastart, 
283                maxeadebug);
284
285         io.generic.level = RAW_OPEN_NTCREATEX;
286         io.ntcreatex.in.root_fid = 0;
287         io.ntcreatex.in.flags = 0;
288         io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
289         io.ntcreatex.in.create_options = 0;
290         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
291         io.ntcreatex.in.share_access = 
292                 NTCREATEX_SHARE_ACCESS_READ | 
293                 NTCREATEX_SHARE_ACCESS_WRITE;
294         io.ntcreatex.in.alloc_size = 0;
295         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
296         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
297         io.ntcreatex.in.security_flags = 0;
298         io.ntcreatex.in.fname = fname;
299         status = smb_raw_open(cli->tree, mem_ctx, &io);
300         CHECK_STATUS(status, NT_STATUS_OK);
301         fnum = io.ntcreatex.out.file.fnum;
302         
303         eablob = data_blob_talloc(mem_ctx, NULL, maxeasize);
304         if (eablob.data == NULL) {
305                 goto done;
306         }
307         /* 
308          * Fill in some EA data - the offset could be easily checked 
309          * during a hexdump.
310          */
311         for (i = 0, k = 0; i < eablob.length / 4; i++, k+=4) {
312                 eablob.data[k]   = k & 0xff;
313                 eablob.data[k+1] = (k >>  8) & 0xff;
314                 eablob.data[k+2] = (k >> 16) & 0xff;
315                 eablob.data[k+3] = (k >> 24) & 0xff;
316         }
317
318         i = eablob.length % 4;
319         if (i-- > 0) { 
320                 eablob.data[k] = k & 0xff;
321                 if (i-- > 0) { 
322                         eablob.data[k+1] = (k >>  8) & 0xff;
323                         if (i-- > 0) { 
324                                 eablob.data[k+2] = (k >> 16) & 0xff;
325                         }
326                 }
327         }
328         /*
329          * Filesystems might allow max. EAs data for different EA names.
330          * So more than one EA name should be checked.
331          */
332         total = 0;
333         last  = maxeastart;
334
335         for (i = 0; i < maxeanames; i++) {
336                 if (eaname != NULL) {
337                         talloc_free(eaname);
338                 }
339                 eaname = talloc_asprintf(mem_ctx, "MAX%d", i);
340                 if(eaname == NULL) {
341                         goto done;
342                 }
343                 j = test_one_eamax(cli, fnum, eaname, eablob, last, maxeadebug);
344                 if (j <= 0) {
345                         break;
346                 }
347                 total += j;
348                 last = j;
349         }
350
351         printf("Total EA size:%d\n", total);
352         if (i == maxeanames) {
353                 printf ("NOTE: More EAs could be available!\n");
354         } 
355         if (total == 0) {
356                 ret = False;
357         }
358 done:
359         smbcli_close(cli->tree, fnum);
360         return ret;
361 }
362
363 /*
364   test using NTTRANS CREATE to create a file with an initial EA set
365 */
366 static BOOL test_nttrans_create(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
367 {
368         NTSTATUS status;
369         union smb_open io;
370         const char *fname = BASEDIR "\\ea2.txt";
371         BOOL ret = True;
372         int fnum = -1;
373         struct ea_struct eas[3];
374         struct smb_ea_list ea_list;
375
376         printf("TESTING NTTRANS CREATE WITH EAS\n");
377
378         io.generic.level = RAW_OPEN_NTTRANS_CREATE;
379         io.ntcreatex.in.root_fid = 0;
380         io.ntcreatex.in.flags = 0;
381         io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
382         io.ntcreatex.in.create_options = 0;
383         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
384         io.ntcreatex.in.share_access = 
385                 NTCREATEX_SHARE_ACCESS_READ | 
386                 NTCREATEX_SHARE_ACCESS_WRITE;
387         io.ntcreatex.in.alloc_size = 0;
388         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
389         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
390         io.ntcreatex.in.security_flags = 0;
391         io.ntcreatex.in.fname = fname;
392
393         ea_list.num_eas = 3;
394         ea_list.eas = eas;
395
396         eas[0].flags = 0;
397         eas[0].name.s = "1st EA";
398         eas[0].value = data_blob_string_const("Value One");
399
400         eas[1].flags = 0;
401         eas[1].name.s = "2nd EA";
402         eas[1].value = data_blob_string_const("Second Value");
403
404         eas[2].flags = 0;
405         eas[2].name.s = "and 3rd";
406         eas[2].value = data_blob_string_const("final value");
407
408         io.ntcreatex.in.ea_list = &ea_list;
409         io.ntcreatex.in.sec_desc = NULL;
410
411         status = smb_raw_open(cli->tree, mem_ctx, &io);
412         CHECK_STATUS(status, NT_STATUS_OK);
413         fnum = io.ntcreatex.out.file.fnum;
414         
415         ret &= check_ea(cli, fname, "EAONE", NULL);
416         ret &= check_ea(cli, fname, "1st EA", "Value One");
417         ret &= check_ea(cli, fname, "2nd EA", "Second Value");
418         ret &= check_ea(cli, fname, "and 3rd", "final value");
419
420         smbcli_close(cli->tree, fnum);
421
422         printf("Trying to add EAs on non-create\n");
423         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
424         io.ntcreatex.in.fname = fname;
425
426         ea_list.num_eas = 1;
427         eas[0].flags = 0;
428         eas[0].name.s = "Fourth EA";
429         eas[0].value = data_blob_string_const("Value Four");
430
431         status = smb_raw_open(cli->tree, mem_ctx, &io);
432         CHECK_STATUS(status, NT_STATUS_OK);
433         fnum = io.ntcreatex.out.file.fnum;
434         
435         ret &= check_ea(cli, fname, "1st EA", "Value One");
436         ret &= check_ea(cli, fname, "2nd EA", "Second Value");
437         ret &= check_ea(cli, fname, "and 3rd", "final value");
438         ret &= check_ea(cli, fname, "Fourth EA", NULL);
439
440 done:
441         smbcli_close(cli->tree, fnum);
442         return ret;
443 }
444
445 /* 
446    basic testing of EA calls
447 */
448 bool torture_raw_eas(struct torture_context *torture, struct smbcli_state *cli)
449 {
450         bool ret = true;
451
452         if (!torture_setup_dir(cli, BASEDIR)) {
453                 return False;
454         }
455
456         ret &= test_eas(cli, torture);
457         ret &= test_nttrans_create(cli, torture);
458
459         smb_raw_exit(cli->session);
460
461         return ret;
462 }
463
464 /* 
465    test max EA size
466 */
467 bool torture_max_eas(struct torture_context *torture)
468 {
469         struct smbcli_state *cli;
470         BOOL ret = True;
471         TALLOC_CTX *mem_ctx;
472
473         if (!torture_open_connection(&cli, 0)) {
474                 return False;
475         }
476
477         mem_ctx = talloc_init("torture_raw_eas");
478
479         if (!torture_setup_dir(cli, BASEDIR)) {
480                 return False;
481         }
482
483         ret &= test_max_eas(cli, mem_ctx);
484
485         smb_raw_exit(cli->session);
486         if (!maxeadebug) {
487                 /* in no ea debug case, all files are gone now */
488                 smbcli_deltree(cli->tree, BASEDIR);
489         }
490
491         torture_close_connection(cli);
492         talloc_free(mem_ctx);
493         return ret;
494 }