Line data Source code
1 : /*
2 : Test the SMB_QUERY_FILE_UNIX_INFO2 Unix extension.
3 :
4 : Copyright (C) 2007 James Peach
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "libcli/libcli.h"
22 : #include "libcli/raw/raw_proto.h"
23 : #include "torture/util.h"
24 : #include "torture/unix/proto.h"
25 : #include "lib/cmdline/cmdline.h"
26 : #include "libcli/resolve/resolve.h"
27 : #include "param/param.h"
28 :
29 : struct unix_info2 {
30 : uint64_t end_of_file;
31 : uint64_t num_bytes;
32 : NTTIME status_change_time;
33 : NTTIME access_time;
34 : NTTIME change_time;
35 : uint64_t uid;
36 : uint64_t gid;
37 : uint32_t file_type;
38 : uint64_t dev_major;
39 : uint64_t dev_minor;
40 : uint64_t unique_id;
41 : uint64_t permissions;
42 : uint64_t nlink;
43 : NTTIME create_time;
44 : uint32_t file_flags;
45 : uint32_t flags_mask;
46 : };
47 :
48 4 : static struct smbcli_state *connect_to_server(struct torture_context *tctx)
49 : {
50 0 : NTSTATUS status;
51 0 : struct smbcli_state *cli;
52 :
53 4 : const char *host = torture_setting_string(tctx, "host", NULL);
54 4 : const char *share = torture_setting_string(tctx, "share", NULL);
55 0 : struct smbcli_options options;
56 0 : struct smbcli_session_options session_options;
57 0 : struct smb_trans2 tp;
58 0 : uint16_t setup;
59 0 : uint8_t data[12];
60 0 : uint8_t params[4];
61 :
62 4 : lpcfg_smbcli_options(tctx->lp_ctx, &options);
63 4 : lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options);
64 :
65 4 : status = smbcli_full_connection(tctx, &cli, host,
66 : lpcfg_smb_ports(tctx->lp_ctx),
67 : share, NULL, lpcfg_socket_options(tctx->lp_ctx),
68 : samba_cmdline_get_creds(),
69 : lpcfg_resolve_context(tctx->lp_ctx),
70 : tctx->ev, &options, &session_options,
71 : lpcfg_gensec_settings(tctx, tctx->lp_ctx));
72 :
73 4 : if (!NT_STATUS_IS_OK(status)) {
74 0 : torture_comment(tctx, "failed to connect to //%s/%s: %s\n",
75 : host, share, nt_errstr(status));
76 0 : torture_result(tctx, TORTURE_FAIL, "Failed to connect to server");
77 0 : return NULL;
78 : }
79 :
80 : /* Setup POSIX on the server. */
81 4 : SSVAL(data, 0, CIFS_UNIX_MAJOR_VERSION);
82 4 : SSVAL(data, 2, CIFS_UNIX_MINOR_VERSION);
83 4 : SBVAL(data,4,((uint64_t)(
84 : CIFS_UNIX_POSIX_ACLS_CAP|
85 : CIFS_UNIX_POSIX_PATHNAMES_CAP|
86 : CIFS_UNIX_FCNTL_LOCKS_CAP|
87 : CIFS_UNIX_EXTATTR_CAP|
88 : CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP)));
89 4 : setup = TRANSACT2_SETFSINFO;
90 4 : tp.in.max_setup = 0;
91 4 : tp.in.flags = 0;
92 4 : tp.in.timeout = 0;
93 4 : tp.in.setup_count = 1;
94 4 : tp.in.max_param = 0;
95 4 : tp.in.max_data = 0;
96 4 : tp.in.setup = &setup;
97 4 : tp.in.trans_name = NULL;
98 4 : SSVAL(params, 0, 0);
99 4 : SSVAL(params, 2, SMB_SET_CIFS_UNIX_INFO);
100 4 : tp.in.params = data_blob_talloc(tctx, params, 4);
101 4 : tp.in.data = data_blob_talloc(tctx, data, 12);
102 :
103 4 : status = smb_raw_trans2(cli->tree, tctx, &tp);
104 4 : torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK,
105 : "doing SMB_SET_CIFS_UNIX_INFO");
106 :
107 4 : return cli;
108 : }
109 :
110 16 : static bool check_unix_info2(struct torture_context *torture,
111 : struct unix_info2 *info2)
112 : {
113 16 : printf("\tcreate_time=0x%016llu flags=0x%08x mask=0x%08x\n",
114 16 : (unsigned long long)info2->create_time,
115 : info2->file_flags, info2->flags_mask);
116 :
117 16 : if (info2->file_flags == 0) {
118 16 : return true;
119 : }
120 :
121 : /* If we have any file_flags set, they must be within the range
122 : * defined by flags_mask.
123 : */
124 0 : if ((info2->flags_mask & info2->file_flags) == 0) {
125 0 : torture_result(torture, TORTURE_FAIL,
126 : __location__": UNIX_INFO2 flags field 0x%08x, "
127 : "does not match mask 0x%08x\n",
128 : info2->file_flags, info2->flags_mask);
129 : }
130 :
131 0 : return true;
132 : }
133 :
134 132 : static NTSTATUS set_path_info2(void *mem_ctx,
135 : struct smbcli_state *cli,
136 : const char *fname,
137 : struct unix_info2 *info2)
138 : {
139 0 : union smb_setfileinfo sfinfo;
140 :
141 132 : ZERO_STRUCT(sfinfo.basic_info.in);
142 132 : sfinfo.generic.level = RAW_SFILEINFO_UNIX_INFO2;
143 132 : sfinfo.generic.in.file.path = fname;
144 :
145 132 : sfinfo.unix_info2.in.end_of_file = info2->end_of_file;
146 132 : sfinfo.unix_info2.in.num_bytes = info2->num_bytes;
147 132 : sfinfo.unix_info2.in.status_change_time = info2->status_change_time;
148 132 : sfinfo.unix_info2.in.access_time = info2->access_time;
149 132 : sfinfo.unix_info2.in.change_time = info2->change_time;
150 132 : sfinfo.unix_info2.in.uid = info2->uid;
151 132 : sfinfo.unix_info2.in.gid = info2->gid;
152 132 : sfinfo.unix_info2.in.file_type = info2->file_type;
153 132 : sfinfo.unix_info2.in.dev_major = info2->dev_major;
154 132 : sfinfo.unix_info2.in.dev_minor = info2->dev_minor;
155 132 : sfinfo.unix_info2.in.unique_id = info2->unique_id;
156 132 : sfinfo.unix_info2.in.permissions = info2->permissions;
157 132 : sfinfo.unix_info2.in.nlink = info2->nlink;
158 132 : sfinfo.unix_info2.in.create_time = info2->create_time;
159 132 : sfinfo.unix_info2.in.file_flags = info2->file_flags;
160 132 : sfinfo.unix_info2.in.flags_mask = info2->flags_mask;
161 :
162 132 : return smb_raw_setpathinfo(cli->tree, &sfinfo);
163 : }
164 :
165 12 : static bool query_file_path_info2(void *mem_ctx,
166 : struct torture_context *torture,
167 : struct smbcli_state *cli,
168 : int fnum,
169 : const char *fname,
170 : struct unix_info2 *info2)
171 : {
172 0 : NTSTATUS result;
173 0 : union smb_fileinfo finfo;
174 :
175 12 : finfo.generic.level = RAW_FILEINFO_UNIX_INFO2;
176 :
177 12 : if (fname) {
178 8 : finfo.generic.in.file.path = fname;
179 8 : result = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
180 : } else {
181 4 : finfo.generic.in.file.fnum = fnum;
182 4 : result = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo);
183 : }
184 :
185 12 : torture_assert_ntstatus_equal(torture, result, NT_STATUS_OK,
186 : smbcli_errstr(cli->tree));
187 :
188 12 : info2->end_of_file = finfo.unix_info2.out.end_of_file;
189 12 : info2->num_bytes = finfo.unix_info2.out.num_bytes;
190 12 : info2->status_change_time = finfo.unix_info2.out.status_change_time;
191 12 : info2->access_time = finfo.unix_info2.out.access_time;
192 12 : info2->change_time = finfo.unix_info2.out.change_time;
193 12 : info2->uid = finfo.unix_info2.out.uid;
194 12 : info2->gid = finfo.unix_info2.out.gid;
195 12 : info2->file_type = finfo.unix_info2.out.file_type;
196 12 : info2->dev_major = finfo.unix_info2.out.dev_major;
197 12 : info2->dev_minor = finfo.unix_info2.out.dev_minor;
198 12 : info2->unique_id = finfo.unix_info2.out.unique_id;
199 12 : info2->permissions = finfo.unix_info2.out.permissions;
200 12 : info2->nlink = finfo.unix_info2.out.nlink;
201 12 : info2->create_time = finfo.unix_info2.out.create_time;
202 12 : info2->file_flags = finfo.unix_info2.out.file_flags;
203 12 : info2->flags_mask = finfo.unix_info2.out.flags_mask;
204 :
205 12 : if (!check_unix_info2(torture, info2)) {
206 0 : return false;
207 : }
208 :
209 12 : return true;
210 : }
211 :
212 4 : static bool query_file_info2(void *mem_ctx,
213 : struct torture_context *torture,
214 : struct smbcli_state *cli,
215 : int fnum,
216 : struct unix_info2 *info2)
217 : {
218 4 : return query_file_path_info2(mem_ctx, torture, cli,
219 : fnum, NULL, info2);
220 : }
221 :
222 8 : static bool query_path_info2(void *mem_ctx,
223 : struct torture_context *torture,
224 : struct smbcli_state *cli,
225 : const char *fname,
226 : struct unix_info2 *info2)
227 : {
228 8 : return query_file_path_info2(mem_ctx, torture, cli,
229 : -1, fname, info2);
230 : }
231 :
232 4 : static bool search_callback(void *private_data, const union smb_search_data *fdata)
233 : {
234 4 : struct unix_info2 *info2 = (struct unix_info2 *)private_data;
235 :
236 4 : info2->end_of_file = fdata->unix_info2.end_of_file;
237 4 : info2->num_bytes = fdata->unix_info2.num_bytes;
238 4 : info2->status_change_time = fdata->unix_info2.status_change_time;
239 4 : info2->access_time = fdata->unix_info2.access_time;
240 4 : info2->change_time = fdata->unix_info2.change_time;
241 4 : info2->uid = fdata->unix_info2.uid;
242 4 : info2->gid = fdata->unix_info2.gid;
243 4 : info2->file_type = fdata->unix_info2.file_type;
244 4 : info2->dev_major = fdata->unix_info2.dev_major;
245 4 : info2->dev_minor = fdata->unix_info2.dev_minor;
246 4 : info2->unique_id = fdata->unix_info2.unique_id;
247 4 : info2->permissions = fdata->unix_info2.permissions;
248 4 : info2->nlink = fdata->unix_info2.nlink;
249 4 : info2->create_time = fdata->unix_info2.create_time;
250 4 : info2->file_flags = fdata->unix_info2.file_flags;
251 4 : info2->flags_mask = fdata->unix_info2.flags_mask;
252 :
253 4 : return true;
254 : }
255 :
256 4 : static bool find_single_info2(void *mem_ctx,
257 : struct torture_context *torture,
258 : struct smbcli_state *cli,
259 : const char *fname,
260 : struct unix_info2 *info2)
261 : {
262 0 : union smb_search_first search;
263 0 : NTSTATUS status;
264 :
265 : /* Set up a new search for a single item, not using resume keys. */
266 4 : ZERO_STRUCT(search);
267 4 : search.t2ffirst.level = RAW_SEARCH_TRANS2;
268 4 : search.t2ffirst.data_level = SMB_FIND_UNIX_INFO2;
269 4 : search.t2ffirst.in.max_count = 1;
270 4 : search.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE;
271 4 : search.t2ffirst.in.pattern = fname;
272 :
273 4 : status = smb_raw_search_first(cli->tree, mem_ctx,
274 : &search, info2, search_callback);
275 4 : torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
276 : smbcli_errstr(cli->tree));
277 :
278 4 : torture_assert_int_equal(torture, search.t2ffirst.out.count, 1,
279 : "expected exactly one result");
280 : /*
281 : * In smbd directory listings using POSIX extensions
282 : * always treat the search pathname as a wildcard,
283 : * so don't expect end_of_search to be set here. Wildcard
284 : * searches always need a findnext to end the search.
285 : */
286 4 : torture_assert_int_equal(torture, search.t2ffirst.out.end_of_search, 0,
287 : "expected end_of_search to be false");
288 :
289 4 : return check_unix_info2(torture, info2);
290 : }
291 :
292 : #define ASSERT_FLAGS_MATCH(info2, expected) \
293 : if ((info2)->file_flags != (1 << i)) { \
294 : torture_result(torture, TORTURE_FAIL, \
295 : __location__": INFO2 flags field was 0x%08x, "\
296 : "expected 0x%08x\n",\
297 : (info2)->file_flags, expected); \
298 : }
299 :
300 132 : static void set_no_metadata_change(struct unix_info2 *info2)
301 : {
302 132 : info2->uid = SMB_UID_NO_CHANGE;
303 132 : info2->gid = SMB_GID_NO_CHANGE;
304 132 : info2->permissions = SMB_MODE_NO_CHANGE;
305 :
306 132 : info2->end_of_file =
307 : ((uint64_t)SMB_SIZE_NO_CHANGE_HI << 32) | SMB_SIZE_NO_CHANGE_LO;
308 :
309 132 : info2->status_change_time =
310 132 : info2->access_time =
311 132 : info2->change_time =
312 132 : info2->create_time =
313 : ((uint64_t)SMB_SIZE_NO_CHANGE_HI << 32) | SMB_SIZE_NO_CHANGE_LO;
314 132 : }
315 :
316 4 : static bool verify_setinfo_flags(void *mem_ctx,
317 : struct torture_context *torture,
318 : struct smbcli_state *cli,
319 : const char *fname)
320 : {
321 0 : struct unix_info2 info2;
322 0 : uint32_t smb_fmask;
323 0 : int i;
324 :
325 4 : bool ret = true;
326 0 : NTSTATUS status;
327 :
328 4 : if (!query_path_info2(mem_ctx, torture, cli, fname, &info2)) {
329 0 : return false;
330 : }
331 :
332 4 : smb_fmask = info2.flags_mask;
333 :
334 : /* For each possible flag, ask to set exactly 1 flag, making sure
335 : * that flag is in our requested mask.
336 : */
337 132 : for (i = 0; i < 32; ++i) {
338 128 : info2.file_flags = ((uint32_t)1 << i);
339 128 : info2.flags_mask = smb_fmask | info2.file_flags;
340 :
341 128 : set_no_metadata_change(&info2);
342 128 : status = set_path_info2(mem_ctx, cli, fname, &info2);
343 :
344 128 : if (info2.file_flags & smb_fmask) {
345 0 : torture_assert_ntstatus_equal(torture,
346 : status, NT_STATUS_OK,
347 : "setting valid UNIX_INFO2 flag");
348 :
349 0 : if (!query_path_info2(mem_ctx, torture, cli,
350 : fname, &info2)) {
351 0 : return false;
352 : }
353 :
354 0 : ASSERT_FLAGS_MATCH(&info2, 1 << i);
355 :
356 :
357 : } else {
358 : /* We tried to set a flag the server doesn't
359 : * understand.
360 : */
361 128 : torture_assert_ntstatus_equal(torture,
362 : status, NT_STATUS_INVALID_PARAMETER,
363 : "setting invalid UNIX_INFO2 flag");
364 : }
365 : }
366 :
367 : /* Make sure that a zero flags field does nothing. */
368 4 : set_no_metadata_change(&info2);
369 4 : info2.file_flags = 0xFFFFFFFF;
370 4 : info2.flags_mask = 0;
371 4 : status = set_path_info2(mem_ctx, cli, fname, &info2);
372 4 : torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
373 : "setting empty flags mask");
374 :
375 4 : return ret;
376 :
377 : }
378 :
379 4 : static int create_file(struct smbcli_state *cli, const char * fname)
380 : {
381 :
382 4 : return smbcli_nt_create_full(cli->tree, fname, 0,
383 : SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL,
384 : NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF,
385 : 0, 0);
386 : }
387 :
388 8 : static bool match_info2(struct torture_context *torture,
389 : const struct unix_info2 *pinfo,
390 : const struct unix_info2 *finfo)
391 : {
392 8 : printf("checking results match\n");
393 :
394 8 : torture_assert_u64_equal(torture, finfo->end_of_file, 0,
395 : "end_of_file should be 0");
396 8 : torture_assert_u64_equal(torture, finfo->num_bytes, 0,
397 : "num_bytes should be 0");
398 :
399 8 : torture_assert_u64_equal(torture, finfo->end_of_file,
400 : pinfo->end_of_file, "end_of_file mismatch");
401 8 : torture_assert_u64_equal(torture, finfo->num_bytes, pinfo->num_bytes,
402 : "num_bytes mismatch");
403 :
404 : /* Don't match access_time. */
405 :
406 8 : torture_assert_u64_equal(torture, finfo->status_change_time,
407 : pinfo->status_change_time,
408 : "status_change_time mismatch");
409 8 : torture_assert_u64_equal(torture, finfo->change_time,
410 : pinfo->change_time, "change_time mismatch");
411 :
412 8 : torture_assert_u64_equal(torture, finfo->uid, pinfo->uid,
413 : "UID mismatch");
414 8 : torture_assert_u64_equal(torture, finfo->gid, pinfo->gid,
415 : "GID mismatch");
416 8 : torture_assert_int_equal(torture, finfo->file_type, pinfo->file_type,
417 : "file_type mismatch");
418 8 : torture_assert_u64_equal(torture, finfo->dev_major, pinfo->dev_major,
419 : "dev_major mismatch");
420 8 : torture_assert_u64_equal(torture, finfo->dev_minor, pinfo->dev_minor,
421 : "dev_minor mismatch");
422 8 : torture_assert_u64_equal(torture, finfo->unique_id, pinfo->unique_id,
423 : "unique_id mismatch");
424 8 : torture_assert_u64_equal(torture, finfo->permissions,
425 : pinfo->permissions, "permissions mismatch");
426 8 : torture_assert_u64_equal(torture, finfo->nlink, pinfo->nlink,
427 : "nlink mismatch");
428 8 : torture_assert_u64_equal(torture, finfo->create_time, pinfo->create_time,
429 : "create_time mismatch");
430 :
431 8 : return true;
432 : }
433 :
434 :
435 : #define FILENAME "\\smb_unix_info2.txt"
436 :
437 4 : bool unix_torture_unix_info2(struct torture_context *torture)
438 : {
439 0 : void *mem_ctx;
440 0 : struct smbcli_state *cli;
441 0 : int fnum;
442 :
443 0 : struct unix_info2 pinfo, finfo;
444 :
445 4 : mem_ctx = talloc_init("smb_query_unix_info2");
446 4 : torture_assert(torture, mem_ctx != NULL, "out of memory");
447 :
448 4 : if (!(cli = connect_to_server(torture))) {
449 0 : talloc_free(mem_ctx);
450 0 : return false;
451 : }
452 :
453 4 : smbcli_unlink(cli->tree, FILENAME);
454 :
455 4 : fnum = create_file(cli, FILENAME);
456 4 : torture_assert(torture, fnum != -1, smbcli_errstr(cli->tree));
457 :
458 4 : printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryFileInfo\n");
459 4 : if (!query_file_info2(mem_ctx, torture, cli, fnum, &finfo)) {
460 0 : goto fail;
461 : }
462 :
463 4 : printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryPathInfo\n");
464 4 : if (!query_path_info2(mem_ctx, torture, cli, FILENAME, &pinfo)) {
465 0 : goto fail;
466 : }
467 :
468 4 : if (!match_info2(torture, &pinfo, &finfo)) {
469 0 : goto fail;
470 : }
471 :
472 4 : printf("checking SMB_FIND_UNIX_INFO2 for FindFirst\n");
473 4 : if (!find_single_info2(mem_ctx, torture, cli, FILENAME, &pinfo)) {
474 0 : goto fail;
475 : }
476 :
477 4 : if (!match_info2(torture, &pinfo, &finfo)) {
478 0 : goto fail;
479 : }
480 :
481 : /* XXX: should repeat this test with SetFileInfo. */
482 4 : printf("checking SMB_SFILEINFO_UNIX_INFO2 for SetPathInfo\n");
483 4 : if (!verify_setinfo_flags(mem_ctx, torture, cli, FILENAME)) {
484 0 : goto fail;
485 : }
486 :
487 4 : smbcli_close(cli->tree, fnum);
488 4 : smbcli_unlink(cli->tree, FILENAME);
489 4 : torture_close_connection(cli);
490 4 : talloc_free(mem_ctx);
491 4 : return true;
492 :
493 0 : fail:
494 :
495 0 : smbcli_close(cli->tree, fnum);
496 0 : smbcli_unlink(cli->tree, FILENAME);
497 0 : torture_close_connection(cli);
498 0 : talloc_free(mem_ctx);
499 0 : return false;
500 :
501 : }
502 :
503 : /* vim: set sts=8 sw=8 : */
|