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 "libcli/raw/interfaces.h"
23 #include "libcli/raw/raw_proto.h"
24 #include "torture/torture.h"
25 #include "torture/util.h"
26 #include "torture/basic/proto.h"
27 #include "lib/cmdline/popt_common.h"
28 #include "auth/credentials/credentials.h"
29 #include "libcli/resolve/resolve.h"
30 #include "param/param.h"
35 NTTIME status_change_time
;
51 static struct smbcli_state
*connect_to_server(struct torture_context
*tctx
)
54 struct smbcli_state
*cli
;
56 const char *host
= torture_setting_string(tctx
, "host", NULL
);
57 const char *share
= torture_setting_string(tctx
, "share", NULL
);
58 struct smbcli_options options
;
59 struct smbcli_session_options session_options
;
61 lp_smbcli_options(tctx
->lp_ctx
, &options
);
62 lp_smbcli_session_options(tctx
->lp_ctx
, &session_options
);
64 status
= smbcli_full_connection(tctx
, &cli
, host
,
65 lp_smb_ports(tctx
->lp_ctx
),
66 share
, NULL
, lp_socket_options(tctx
->lp_ctx
),
68 lp_resolve_context(tctx
->lp_ctx
),
69 tctx
->ev
, &options
, &session_options
,
70 lp_iconv_convenience(tctx
->lp_ctx
),
71 lp_gensec_settings(tctx
, tctx
->lp_ctx
));
73 if (!NT_STATUS_IS_OK(status
)) {
74 printf("failed to connect to //%s/%s: %s\n",
75 host
, share
, nt_errstr(status
));
82 static bool check_unix_info2(struct torture_context
*torture
,
83 struct unix_info2
*info2
)
85 printf("\tcreate_time=0x%016llu flags=0x%08x mask=0x%08x\n",
86 (unsigned long long)info2
->create_time
,
87 info2
->file_flags
, info2
->flags_mask
);
89 if (info2
->file_flags
== 0) {
93 /* If we have any file_flags set, they must be within the range
94 * defined by flags_mask.
96 if ((info2
->flags_mask
& info2
->file_flags
) == 0) {
97 torture_result(torture
, TORTURE_FAIL
,
98 __location__
": UNIX_INFO2 flags field 0x%08x, "
99 "does not match mask 0x%08x\n",
100 info2
->file_flags
, info2
->flags_mask
);
106 static NTSTATUS
set_path_info2(void *mem_ctx
,
107 struct smbcli_state
*cli
,
109 struct unix_info2
*info2
)
111 union smb_setfileinfo sfinfo
;
113 ZERO_STRUCT(sfinfo
.basic_info
.in
);
114 sfinfo
.generic
.level
= RAW_SFILEINFO_UNIX_INFO2
;
115 sfinfo
.generic
.in
.file
.path
= fname
;
117 sfinfo
.unix_info2
.in
.end_of_file
= info2
->end_of_file
;
118 sfinfo
.unix_info2
.in
.num_bytes
= info2
->num_bytes
;
119 sfinfo
.unix_info2
.in
.status_change_time
= info2
->status_change_time
;
120 sfinfo
.unix_info2
.in
.access_time
= info2
->access_time
;
121 sfinfo
.unix_info2
.in
.change_time
= info2
->change_time
;
122 sfinfo
.unix_info2
.in
.uid
= info2
->uid
;
123 sfinfo
.unix_info2
.in
.gid
= info2
->gid
;
124 sfinfo
.unix_info2
.in
.file_type
= info2
->file_type
;
125 sfinfo
.unix_info2
.in
.dev_major
= info2
->dev_major
;
126 sfinfo
.unix_info2
.in
.dev_minor
= info2
->dev_minor
;
127 sfinfo
.unix_info2
.in
.unique_id
= info2
->unique_id
;
128 sfinfo
.unix_info2
.in
.permissions
= info2
->permissions
;
129 sfinfo
.unix_info2
.in
.nlink
= info2
->nlink
;
130 sfinfo
.unix_info2
.in
.create_time
= info2
->create_time
;
131 sfinfo
.unix_info2
.in
.file_flags
= info2
->file_flags
;
132 sfinfo
.unix_info2
.in
.flags_mask
= info2
->flags_mask
;
134 return smb_raw_setpathinfo(cli
->tree
, &sfinfo
);
137 static bool query_file_path_info2(void *mem_ctx
,
138 struct torture_context
*torture
,
139 struct smbcli_state
*cli
,
142 struct unix_info2
*info2
)
145 union smb_fileinfo finfo
;
147 finfo
.generic
.level
= RAW_FILEINFO_UNIX_INFO2
;
150 finfo
.generic
.in
.file
.path
= fname
;
151 result
= smb_raw_pathinfo(cli
->tree
, mem_ctx
, &finfo
);
153 finfo
.generic
.in
.file
.fnum
= fnum
;
154 result
= smb_raw_fileinfo(cli
->tree
, mem_ctx
, &finfo
);
157 torture_assert_ntstatus_equal(torture
, result
, NT_STATUS_OK
,
158 smbcli_errstr(cli
->tree
));
160 info2
->end_of_file
= finfo
.unix_info2
.out
.end_of_file
;
161 info2
->num_bytes
= finfo
.unix_info2
.out
.num_bytes
;
162 info2
->status_change_time
= finfo
.unix_info2
.out
.status_change_time
;
163 info2
->access_time
= finfo
.unix_info2
.out
.access_time
;
164 info2
->change_time
= finfo
.unix_info2
.out
.change_time
;
165 info2
->uid
= finfo
.unix_info2
.out
.uid
;
166 info2
->gid
= finfo
.unix_info2
.out
.gid
;
167 info2
->file_type
= finfo
.unix_info2
.out
.file_type
;
168 info2
->dev_major
= finfo
.unix_info2
.out
.dev_major
;
169 info2
->dev_minor
= finfo
.unix_info2
.out
.dev_minor
;
170 info2
->unique_id
= finfo
.unix_info2
.out
.unique_id
;
171 info2
->permissions
= finfo
.unix_info2
.out
.permissions
;
172 info2
->nlink
= finfo
.unix_info2
.out
.nlink
;
173 info2
->create_time
= finfo
.unix_info2
.out
.create_time
;
174 info2
->file_flags
= finfo
.unix_info2
.out
.file_flags
;
175 info2
->flags_mask
= finfo
.unix_info2
.out
.flags_mask
;
177 if (!check_unix_info2(torture
, info2
)) {
184 static bool query_file_info2(void *mem_ctx
,
185 struct torture_context
*torture
,
186 struct smbcli_state
*cli
,
188 struct unix_info2
*info2
)
190 return query_file_path_info2(mem_ctx
, torture
, cli
,
194 static bool query_path_info2(void *mem_ctx
,
195 struct torture_context
*torture
,
196 struct smbcli_state
*cli
,
198 struct unix_info2
*info2
)
200 return query_file_path_info2(mem_ctx
, torture
, cli
,
204 static bool search_callback(void *private_data
, const union smb_search_data
*fdata
)
206 struct unix_info2
*info2
= (struct unix_info2
*)private_data
;
208 info2
->end_of_file
= fdata
->unix_info2
.end_of_file
;
209 info2
->num_bytes
= fdata
->unix_info2
.num_bytes
;
210 info2
->status_change_time
= fdata
->unix_info2
.status_change_time
;
211 info2
->access_time
= fdata
->unix_info2
.access_time
;
212 info2
->change_time
= fdata
->unix_info2
.change_time
;
213 info2
->uid
= fdata
->unix_info2
.uid
;
214 info2
->gid
= fdata
->unix_info2
.gid
;
215 info2
->file_type
= fdata
->unix_info2
.file_type
;
216 info2
->dev_major
= fdata
->unix_info2
.dev_major
;
217 info2
->dev_minor
= fdata
->unix_info2
.dev_minor
;
218 info2
->unique_id
= fdata
->unix_info2
.unique_id
;
219 info2
->permissions
= fdata
->unix_info2
.permissions
;
220 info2
->nlink
= fdata
->unix_info2
.nlink
;
221 info2
->create_time
= fdata
->unix_info2
.create_time
;
222 info2
->file_flags
= fdata
->unix_info2
.file_flags
;
223 info2
->flags_mask
= fdata
->unix_info2
.flags_mask
;
228 static bool find_single_info2(void *mem_ctx
,
229 struct torture_context
*torture
,
230 struct smbcli_state
*cli
,
232 struct unix_info2
*info2
)
234 union smb_search_first search
;
237 /* Set up a new search for a single item, not using resume keys. */
239 search
.t2ffirst
.level
= RAW_SEARCH_TRANS2
;
240 search
.t2ffirst
.data_level
= SMB_FIND_UNIX_INFO2
;
241 search
.t2ffirst
.in
.max_count
= 1;
242 search
.t2ffirst
.in
.flags
= FLAG_TRANS2_FIND_CLOSE
;
243 search
.t2ffirst
.in
.pattern
= fname
;
245 status
= smb_raw_search_first(cli
->tree
, mem_ctx
,
246 &search
, info2
, search_callback
);
247 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
248 smbcli_errstr(cli
->tree
));
250 torture_assert_int_equal(torture
, search
.t2ffirst
.out
.count
, 1,
251 "expected exactly one result");
252 torture_assert_int_equal(torture
, search
.t2ffirst
.out
.end_of_search
, 1,
253 "expected end_of_search to be true");
255 return check_unix_info2(torture
, info2
);
258 #define ASSERT_FLAGS_MATCH(info2, expected) \
259 if ((info2)->file_flags != (1 << i)) { \
260 torture_result(torture, TORTURE_FAIL, \
261 __location__": INFO2 flags field was 0x%08x, "\
262 "expected 0x%08x\n",\
263 (info2)->file_flags, expected); \
266 static void set_no_metadata_change(struct unix_info2
*info2
)
268 info2
->uid
= SMB_UID_NO_CHANGE
;
269 info2
->gid
= SMB_GID_NO_CHANGE
;
270 info2
->permissions
= SMB_MODE_NO_CHANGE
;
273 ((uint64_t)SMB_SIZE_NO_CHANGE_HI
<< 32) | SMB_SIZE_NO_CHANGE_LO
;
275 info2
->status_change_time
=
279 ((uint64_t)SMB_SIZE_NO_CHANGE_HI
<< 32) | SMB_SIZE_NO_CHANGE_LO
;
282 static bool verify_setinfo_flags(void *mem_ctx
,
283 struct torture_context
*torture
,
284 struct smbcli_state
*cli
,
287 struct unix_info2 info2
;
294 if (!query_path_info2(mem_ctx
, torture
, cli
, fname
, &info2
)) {
298 smb_fmask
= info2
.flags_mask
;
300 /* For each possible flag, ask to set exactly 1 flag, making sure
301 * that flag is in our requested mask.
303 for (i
= 0; i
< 32; ++i
) {
304 info2
.file_flags
= (1 << i
);
305 info2
.flags_mask
= smb_fmask
| info2
.file_flags
;
307 set_no_metadata_change(&info2
);
308 status
= set_path_info2(mem_ctx
, cli
, fname
, &info2
);
310 if (info2
.file_flags
& smb_fmask
) {
311 torture_assert_ntstatus_equal(torture
,
312 status
, NT_STATUS_OK
,
313 "setting valid UNIX_INFO2 flag");
315 if (!query_path_info2(mem_ctx
, torture
, cli
,
320 ASSERT_FLAGS_MATCH(&info2
, 1 << i
);
324 /* We tried to set a flag the server doesn't
327 torture_assert_ntstatus_equal(torture
,
328 status
, NT_STATUS_INVALID_PARAMETER
,
329 "setting invalid UNIX_INFO2 flag");
333 /* Make sure that a zero flags field does nothing. */
334 set_no_metadata_change(&info2
);
335 info2
.file_flags
= 0xFFFFFFFF;
336 info2
.flags_mask
= 0;
337 status
= set_path_info2(mem_ctx
, cli
, fname
, &info2
);
338 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
339 "setting empty flags mask");
345 static int create_file(struct smbcli_state
*cli
, const char * fname
)
348 return smbcli_nt_create_full(cli
->tree
, fname
, 0,
349 SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
, FILE_ATTRIBUTE_NORMAL
,
350 NTCREATEX_SHARE_ACCESS_NONE
, NTCREATEX_DISP_OPEN_IF
,
354 static bool match_info2(struct torture_context
*torture
,
355 const struct unix_info2
*pinfo
,
356 const struct unix_info2
*finfo
)
358 printf("checking results match\n");
360 torture_assert_u64_equal(torture
, finfo
->end_of_file
, 0,
361 "end_of_file should be 0");
362 torture_assert_u64_equal(torture
, finfo
->num_bytes
, 0,
363 "num_bytes should be 0");
365 torture_assert_u64_equal(torture
, finfo
->end_of_file
,
366 pinfo
->end_of_file
, "end_of_file mismatch");
367 torture_assert_u64_equal(torture
, finfo
->num_bytes
, pinfo
->num_bytes
,
368 "num_bytes mismatch");
370 /* Don't match access_time. */
372 torture_assert_u64_equal(torture
, finfo
->status_change_time
,
373 pinfo
->status_change_time
,
374 "status_change_time mismatch");
375 torture_assert_u64_equal(torture
, finfo
->change_time
,
376 pinfo
->change_time
, "change_time mismatch");
378 torture_assert_u64_equal(torture
, finfo
->uid
, pinfo
->uid
,
380 torture_assert_u64_equal(torture
, finfo
->gid
, pinfo
->gid
,
382 torture_assert_int_equal(torture
, finfo
->file_type
, pinfo
->file_type
,
383 "file_type mismatch");
384 torture_assert_u64_equal(torture
, finfo
->dev_major
, pinfo
->dev_major
,
385 "dev_major mismatch");
386 torture_assert_u64_equal(torture
, finfo
->dev_minor
, pinfo
->dev_minor
,
387 "dev_minor mismatch");
388 torture_assert_u64_equal(torture
, finfo
->unique_id
, pinfo
->unique_id
,
389 "unique_id mismatch");
390 torture_assert_u64_equal(torture
, finfo
->permissions
,
391 pinfo
->permissions
, "permissions mismatch");
392 torture_assert_u64_equal(torture
, finfo
->nlink
, pinfo
->nlink
,
394 torture_assert_u64_equal(torture
, finfo
->create_time
, pinfo
->create_time
,
395 "create_time mismatch");
401 #define FILENAME "\\smb_unix_info2.txt"
403 bool unix_torture_unix_info2(struct torture_context
*torture
)
406 struct smbcli_state
*cli
;
409 struct unix_info2 pinfo
, finfo
;
411 mem_ctx
= talloc_init("smb_query_unix_info2");
412 torture_assert(torture
, mem_ctx
!= NULL
, "out of memory");
414 if (!(cli
= connect_to_server(torture
))) {
415 talloc_free(mem_ctx
);
419 smbcli_unlink(cli
->tree
, FILENAME
);
421 fnum
= create_file(cli
, FILENAME
);
422 torture_assert(torture
, fnum
!= -1, smbcli_errstr(cli
->tree
));
424 printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryFileInfo\n");
425 if (!query_file_info2(mem_ctx
, torture
, cli
, fnum
, &finfo
)) {
429 printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryPathInfo\n");
430 if (!query_path_info2(mem_ctx
, torture
, cli
, FILENAME
, &pinfo
)) {
434 if (!match_info2(torture
, &pinfo
, &finfo
)) {
438 printf("checking SMB_FIND_UNIX_INFO2 for FindFirst\n");
439 if (!find_single_info2(mem_ctx
, torture
, cli
, FILENAME
, &pinfo
)) {
443 if (!match_info2(torture
, &pinfo
, &finfo
)) {
447 /* XXX: should repeat this test with SetFileInfo. */
448 printf("checking SMB_SFILEINFO_UNIX_INFO2 for SetPathInfo\n");
449 if (!verify_setinfo_flags(mem_ctx
, torture
, cli
, FILENAME
)) {
453 smbcli_close(cli
->tree
, fnum
);
454 smbcli_unlink(cli
->tree
, FILENAME
);
455 torture_close_connection(cli
);
456 talloc_free(mem_ctx
);
461 smbcli_close(cli
->tree
, fnum
);
462 smbcli_unlink(cli
->tree
, FILENAME
);
463 torture_close_connection(cli
);
464 talloc_free(mem_ctx
);
469 /* vim: set sts=8 sw=8 : */