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