2 Test the SMB_QUERY_FILE_UNIX_INFO2 Unix extension.
4 Copyright (C) 2007 James Peach
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.
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.
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/>.
21 #include "libcli/libcli.h"
22 #include "torture/util.h"
23 #include "lib/cmdline/popt_common.h"
24 #include "libcli/resolve/resolve.h"
25 #include "param/param.h"
30 NTTIME status_change_time
;
46 static struct smbcli_state
*connect_to_server(struct torture_context
*tctx
)
49 struct smbcli_state
*cli
;
51 const char *host
= torture_setting_string(tctx
, "host", NULL
);
52 const char *share
= torture_setting_string(tctx
, "share", NULL
);
53 struct smbcli_options options
;
54 struct smbcli_session_options session_options
;
56 lpcfg_smbcli_options(tctx
->lp_ctx
, &options
);
57 lpcfg_smbcli_session_options(tctx
->lp_ctx
, &session_options
);
59 status
= smbcli_full_connection(tctx
, &cli
, host
,
60 lpcfg_smb_ports(tctx
->lp_ctx
),
61 share
, NULL
, lpcfg_socket_options(tctx
->lp_ctx
),
63 lpcfg_resolve_context(tctx
->lp_ctx
),
64 tctx
->ev
, &options
, &session_options
,
65 lpcfg_gensec_settings(tctx
, tctx
->lp_ctx
));
67 if (!NT_STATUS_IS_OK(status
)) {
68 printf("failed to connect to //%s/%s: %s\n",
69 host
, share
, nt_errstr(status
));
76 static bool check_unix_info2(struct torture_context
*torture
,
77 struct unix_info2
*info2
)
79 printf("\tcreate_time=0x%016llu flags=0x%08x mask=0x%08x\n",
80 (unsigned long long)info2
->create_time
,
81 info2
->file_flags
, info2
->flags_mask
);
83 if (info2
->file_flags
== 0) {
87 /* If we have any file_flags set, they must be within the range
88 * defined by flags_mask.
90 if ((info2
->flags_mask
& info2
->file_flags
) == 0) {
91 torture_result(torture
, TORTURE_FAIL
,
92 __location__
": UNIX_INFO2 flags field 0x%08x, "
93 "does not match mask 0x%08x\n",
94 info2
->file_flags
, info2
->flags_mask
);
100 static NTSTATUS
set_path_info2(void *mem_ctx
,
101 struct smbcli_state
*cli
,
103 struct unix_info2
*info2
)
105 union smb_setfileinfo sfinfo
;
107 ZERO_STRUCT(sfinfo
.basic_info
.in
);
108 sfinfo
.generic
.level
= RAW_SFILEINFO_UNIX_INFO2
;
109 sfinfo
.generic
.in
.file
.path
= fname
;
111 sfinfo
.unix_info2
.in
.end_of_file
= info2
->end_of_file
;
112 sfinfo
.unix_info2
.in
.num_bytes
= info2
->num_bytes
;
113 sfinfo
.unix_info2
.in
.status_change_time
= info2
->status_change_time
;
114 sfinfo
.unix_info2
.in
.access_time
= info2
->access_time
;
115 sfinfo
.unix_info2
.in
.change_time
= info2
->change_time
;
116 sfinfo
.unix_info2
.in
.uid
= info2
->uid
;
117 sfinfo
.unix_info2
.in
.gid
= info2
->gid
;
118 sfinfo
.unix_info2
.in
.file_type
= info2
->file_type
;
119 sfinfo
.unix_info2
.in
.dev_major
= info2
->dev_major
;
120 sfinfo
.unix_info2
.in
.dev_minor
= info2
->dev_minor
;
121 sfinfo
.unix_info2
.in
.unique_id
= info2
->unique_id
;
122 sfinfo
.unix_info2
.in
.permissions
= info2
->permissions
;
123 sfinfo
.unix_info2
.in
.nlink
= info2
->nlink
;
124 sfinfo
.unix_info2
.in
.create_time
= info2
->create_time
;
125 sfinfo
.unix_info2
.in
.file_flags
= info2
->file_flags
;
126 sfinfo
.unix_info2
.in
.flags_mask
= info2
->flags_mask
;
128 return smb_raw_setpathinfo(cli
->tree
, &sfinfo
);
131 static bool query_file_path_info2(void *mem_ctx
,
132 struct torture_context
*torture
,
133 struct smbcli_state
*cli
,
136 struct unix_info2
*info2
)
139 union smb_fileinfo finfo
;
141 finfo
.generic
.level
= RAW_FILEINFO_UNIX_INFO2
;
144 finfo
.generic
.in
.file
.path
= fname
;
145 result
= smb_raw_pathinfo(cli
->tree
, mem_ctx
, &finfo
);
147 finfo
.generic
.in
.file
.fnum
= fnum
;
148 result
= smb_raw_fileinfo(cli
->tree
, mem_ctx
, &finfo
);
151 torture_assert_ntstatus_equal(torture
, result
, NT_STATUS_OK
,
152 smbcli_errstr(cli
->tree
));
154 info2
->end_of_file
= finfo
.unix_info2
.out
.end_of_file
;
155 info2
->num_bytes
= finfo
.unix_info2
.out
.num_bytes
;
156 info2
->status_change_time
= finfo
.unix_info2
.out
.status_change_time
;
157 info2
->access_time
= finfo
.unix_info2
.out
.access_time
;
158 info2
->change_time
= finfo
.unix_info2
.out
.change_time
;
159 info2
->uid
= finfo
.unix_info2
.out
.uid
;
160 info2
->gid
= finfo
.unix_info2
.out
.gid
;
161 info2
->file_type
= finfo
.unix_info2
.out
.file_type
;
162 info2
->dev_major
= finfo
.unix_info2
.out
.dev_major
;
163 info2
->dev_minor
= finfo
.unix_info2
.out
.dev_minor
;
164 info2
->unique_id
= finfo
.unix_info2
.out
.unique_id
;
165 info2
->permissions
= finfo
.unix_info2
.out
.permissions
;
166 info2
->nlink
= finfo
.unix_info2
.out
.nlink
;
167 info2
->create_time
= finfo
.unix_info2
.out
.create_time
;
168 info2
->file_flags
= finfo
.unix_info2
.out
.file_flags
;
169 info2
->flags_mask
= finfo
.unix_info2
.out
.flags_mask
;
171 if (!check_unix_info2(torture
, info2
)) {
178 static bool query_file_info2(void *mem_ctx
,
179 struct torture_context
*torture
,
180 struct smbcli_state
*cli
,
182 struct unix_info2
*info2
)
184 return query_file_path_info2(mem_ctx
, torture
, cli
,
188 static bool query_path_info2(void *mem_ctx
,
189 struct torture_context
*torture
,
190 struct smbcli_state
*cli
,
192 struct unix_info2
*info2
)
194 return query_file_path_info2(mem_ctx
, torture
, cli
,
198 static bool search_callback(void *private_data
, const union smb_search_data
*fdata
)
200 struct unix_info2
*info2
= (struct unix_info2
*)private_data
;
202 info2
->end_of_file
= fdata
->unix_info2
.end_of_file
;
203 info2
->num_bytes
= fdata
->unix_info2
.num_bytes
;
204 info2
->status_change_time
= fdata
->unix_info2
.status_change_time
;
205 info2
->access_time
= fdata
->unix_info2
.access_time
;
206 info2
->change_time
= fdata
->unix_info2
.change_time
;
207 info2
->uid
= fdata
->unix_info2
.uid
;
208 info2
->gid
= fdata
->unix_info2
.gid
;
209 info2
->file_type
= fdata
->unix_info2
.file_type
;
210 info2
->dev_major
= fdata
->unix_info2
.dev_major
;
211 info2
->dev_minor
= fdata
->unix_info2
.dev_minor
;
212 info2
->unique_id
= fdata
->unix_info2
.unique_id
;
213 info2
->permissions
= fdata
->unix_info2
.permissions
;
214 info2
->nlink
= fdata
->unix_info2
.nlink
;
215 info2
->create_time
= fdata
->unix_info2
.create_time
;
216 info2
->file_flags
= fdata
->unix_info2
.file_flags
;
217 info2
->flags_mask
= fdata
->unix_info2
.flags_mask
;
222 static bool find_single_info2(void *mem_ctx
,
223 struct torture_context
*torture
,
224 struct smbcli_state
*cli
,
226 struct unix_info2
*info2
)
228 union smb_search_first search
;
231 /* Set up a new search for a single item, not using resume keys. */
233 search
.t2ffirst
.level
= RAW_SEARCH_TRANS2
;
234 search
.t2ffirst
.data_level
= SMB_FIND_UNIX_INFO2
;
235 search
.t2ffirst
.in
.max_count
= 1;
236 search
.t2ffirst
.in
.flags
= FLAG_TRANS2_FIND_CLOSE
;
237 search
.t2ffirst
.in
.pattern
= fname
;
239 status
= smb_raw_search_first(cli
->tree
, mem_ctx
,
240 &search
, info2
, search_callback
);
241 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
242 smbcli_errstr(cli
->tree
));
244 torture_assert_int_equal(torture
, search
.t2ffirst
.out
.count
, 1,
245 "expected exactly one result");
246 torture_assert_int_equal(torture
, search
.t2ffirst
.out
.end_of_search
, 1,
247 "expected end_of_search to be true");
249 return check_unix_info2(torture
, info2
);
252 #define ASSERT_FLAGS_MATCH(info2, expected) \
253 if ((info2)->file_flags != (1 << i)) { \
254 torture_result(torture, TORTURE_FAIL, \
255 __location__": INFO2 flags field was 0x%08x, "\
256 "expected 0x%08x\n",\
257 (info2)->file_flags, expected); \
260 static void set_no_metadata_change(struct unix_info2
*info2
)
262 info2
->uid
= SMB_UID_NO_CHANGE
;
263 info2
->gid
= SMB_GID_NO_CHANGE
;
264 info2
->permissions
= SMB_MODE_NO_CHANGE
;
267 ((uint64_t)SMB_SIZE_NO_CHANGE_HI
<< 32) | SMB_SIZE_NO_CHANGE_LO
;
269 info2
->status_change_time
=
273 ((uint64_t)SMB_SIZE_NO_CHANGE_HI
<< 32) | SMB_SIZE_NO_CHANGE_LO
;
276 static bool verify_setinfo_flags(void *mem_ctx
,
277 struct torture_context
*torture
,
278 struct smbcli_state
*cli
,
281 struct unix_info2 info2
;
288 if (!query_path_info2(mem_ctx
, torture
, cli
, fname
, &info2
)) {
292 smb_fmask
= info2
.flags_mask
;
294 /* For each possible flag, ask to set exactly 1 flag, making sure
295 * that flag is in our requested mask.
297 for (i
= 0; i
< 32; ++i
) {
298 info2
.file_flags
= (1 << i
);
299 info2
.flags_mask
= smb_fmask
| info2
.file_flags
;
301 set_no_metadata_change(&info2
);
302 status
= set_path_info2(mem_ctx
, cli
, fname
, &info2
);
304 if (info2
.file_flags
& smb_fmask
) {
305 torture_assert_ntstatus_equal(torture
,
306 status
, NT_STATUS_OK
,
307 "setting valid UNIX_INFO2 flag");
309 if (!query_path_info2(mem_ctx
, torture
, cli
,
314 ASSERT_FLAGS_MATCH(&info2
, 1 << i
);
318 /* We tried to set a flag the server doesn't
321 torture_assert_ntstatus_equal(torture
,
322 status
, NT_STATUS_INVALID_PARAMETER
,
323 "setting invalid UNIX_INFO2 flag");
327 /* Make sure that a zero flags field does nothing. */
328 set_no_metadata_change(&info2
);
329 info2
.file_flags
= 0xFFFFFFFF;
330 info2
.flags_mask
= 0;
331 status
= set_path_info2(mem_ctx
, cli
, fname
, &info2
);
332 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
333 "setting empty flags mask");
339 static int create_file(struct smbcli_state
*cli
, const char * fname
)
342 return smbcli_nt_create_full(cli
->tree
, fname
, 0,
343 SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
, FILE_ATTRIBUTE_NORMAL
,
344 NTCREATEX_SHARE_ACCESS_NONE
, NTCREATEX_DISP_OPEN_IF
,
348 static bool match_info2(struct torture_context
*torture
,
349 const struct unix_info2
*pinfo
,
350 const struct unix_info2
*finfo
)
352 printf("checking results match\n");
354 torture_assert_u64_equal(torture
, finfo
->end_of_file
, 0,
355 "end_of_file should be 0");
356 torture_assert_u64_equal(torture
, finfo
->num_bytes
, 0,
357 "num_bytes should be 0");
359 torture_assert_u64_equal(torture
, finfo
->end_of_file
,
360 pinfo
->end_of_file
, "end_of_file mismatch");
361 torture_assert_u64_equal(torture
, finfo
->num_bytes
, pinfo
->num_bytes
,
362 "num_bytes mismatch");
364 /* Don't match access_time. */
366 torture_assert_u64_equal(torture
, finfo
->status_change_time
,
367 pinfo
->status_change_time
,
368 "status_change_time mismatch");
369 torture_assert_u64_equal(torture
, finfo
->change_time
,
370 pinfo
->change_time
, "change_time mismatch");
372 torture_assert_u64_equal(torture
, finfo
->uid
, pinfo
->uid
,
374 torture_assert_u64_equal(torture
, finfo
->gid
, pinfo
->gid
,
376 torture_assert_int_equal(torture
, finfo
->file_type
, pinfo
->file_type
,
377 "file_type mismatch");
378 torture_assert_u64_equal(torture
, finfo
->dev_major
, pinfo
->dev_major
,
379 "dev_major mismatch");
380 torture_assert_u64_equal(torture
, finfo
->dev_minor
, pinfo
->dev_minor
,
381 "dev_minor mismatch");
382 torture_assert_u64_equal(torture
, finfo
->unique_id
, pinfo
->unique_id
,
383 "unique_id mismatch");
384 torture_assert_u64_equal(torture
, finfo
->permissions
,
385 pinfo
->permissions
, "permissions mismatch");
386 torture_assert_u64_equal(torture
, finfo
->nlink
, pinfo
->nlink
,
388 torture_assert_u64_equal(torture
, finfo
->create_time
, pinfo
->create_time
,
389 "create_time mismatch");
395 #define FILENAME "\\smb_unix_info2.txt"
397 bool unix_torture_unix_info2(struct torture_context
*torture
)
400 struct smbcli_state
*cli
;
403 struct unix_info2 pinfo
, finfo
;
405 mem_ctx
= talloc_init("smb_query_unix_info2");
406 torture_assert(torture
, mem_ctx
!= NULL
, "out of memory");
408 if (!(cli
= connect_to_server(torture
))) {
409 talloc_free(mem_ctx
);
413 smbcli_unlink(cli
->tree
, FILENAME
);
415 fnum
= create_file(cli
, FILENAME
);
416 torture_assert(torture
, fnum
!= -1, smbcli_errstr(cli
->tree
));
418 printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryFileInfo\n");
419 if (!query_file_info2(mem_ctx
, torture
, cli
, fnum
, &finfo
)) {
423 printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryPathInfo\n");
424 if (!query_path_info2(mem_ctx
, torture
, cli
, FILENAME
, &pinfo
)) {
428 if (!match_info2(torture
, &pinfo
, &finfo
)) {
432 printf("checking SMB_FIND_UNIX_INFO2 for FindFirst\n");
433 if (!find_single_info2(mem_ctx
, torture
, cli
, FILENAME
, &pinfo
)) {
437 if (!match_info2(torture
, &pinfo
, &finfo
)) {
441 /* XXX: should repeat this test with SetFileInfo. */
442 printf("checking SMB_SFILEINFO_UNIX_INFO2 for SetPathInfo\n");
443 if (!verify_setinfo_flags(mem_ctx
, torture
, cli
, FILENAME
)) {
447 smbcli_close(cli
->tree
, fnum
);
448 smbcli_unlink(cli
->tree
, FILENAME
);
449 torture_close_connection(cli
);
450 talloc_free(mem_ctx
);
455 smbcli_close(cli
->tree
, fnum
);
456 smbcli_unlink(cli
->tree
, FILENAME
);
457 torture_close_connection(cli
);
458 talloc_free(mem_ctx
);
463 /* vim: set sts=8 sw=8 : */