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 "torture/unix/proto.h"
24 #include "lib/cmdline/popt_common.h"
25 #include "libcli/resolve/resolve.h"
26 #include "param/param.h"
31 NTTIME status_change_time
;
47 static struct smbcli_state
*connect_to_server(struct torture_context
*tctx
)
50 struct smbcli_state
*cli
;
52 const char *host
= torture_setting_string(tctx
, "host", NULL
);
53 const char *share
= torture_setting_string(tctx
, "share", NULL
);
54 struct smbcli_options options
;
55 struct smbcli_session_options session_options
;
57 lpcfg_smbcli_options(tctx
->lp_ctx
, &options
);
58 lpcfg_smbcli_session_options(tctx
->lp_ctx
, &session_options
);
60 status
= smbcli_full_connection(tctx
, &cli
, host
,
61 lpcfg_smb_ports(tctx
->lp_ctx
),
62 share
, NULL
, lpcfg_socket_options(tctx
->lp_ctx
),
64 lpcfg_resolve_context(tctx
->lp_ctx
),
65 tctx
->ev
, &options
, &session_options
,
66 lpcfg_gensec_settings(tctx
, tctx
->lp_ctx
));
68 if (!NT_STATUS_IS_OK(status
)) {
69 torture_comment(tctx
, "failed to connect to //%s/%s: %s\n",
70 host
, share
, nt_errstr(status
));
71 torture_fail(tctx
, "Failed to connect to server");
78 static bool check_unix_info2(struct torture_context
*torture
,
79 struct unix_info2
*info2
)
81 printf("\tcreate_time=0x%016llu flags=0x%08x mask=0x%08x\n",
82 (unsigned long long)info2
->create_time
,
83 info2
->file_flags
, info2
->flags_mask
);
85 if (info2
->file_flags
== 0) {
89 /* If we have any file_flags set, they must be within the range
90 * defined by flags_mask.
92 if ((info2
->flags_mask
& info2
->file_flags
) == 0) {
93 torture_result(torture
, TORTURE_FAIL
,
94 __location__
": UNIX_INFO2 flags field 0x%08x, "
95 "does not match mask 0x%08x\n",
96 info2
->file_flags
, info2
->flags_mask
);
102 static NTSTATUS
set_path_info2(void *mem_ctx
,
103 struct smbcli_state
*cli
,
105 struct unix_info2
*info2
)
107 union smb_setfileinfo sfinfo
;
109 ZERO_STRUCT(sfinfo
.basic_info
.in
);
110 sfinfo
.generic
.level
= RAW_SFILEINFO_UNIX_INFO2
;
111 sfinfo
.generic
.in
.file
.path
= fname
;
113 sfinfo
.unix_info2
.in
.end_of_file
= info2
->end_of_file
;
114 sfinfo
.unix_info2
.in
.num_bytes
= info2
->num_bytes
;
115 sfinfo
.unix_info2
.in
.status_change_time
= info2
->status_change_time
;
116 sfinfo
.unix_info2
.in
.access_time
= info2
->access_time
;
117 sfinfo
.unix_info2
.in
.change_time
= info2
->change_time
;
118 sfinfo
.unix_info2
.in
.uid
= info2
->uid
;
119 sfinfo
.unix_info2
.in
.gid
= info2
->gid
;
120 sfinfo
.unix_info2
.in
.file_type
= info2
->file_type
;
121 sfinfo
.unix_info2
.in
.dev_major
= info2
->dev_major
;
122 sfinfo
.unix_info2
.in
.dev_minor
= info2
->dev_minor
;
123 sfinfo
.unix_info2
.in
.unique_id
= info2
->unique_id
;
124 sfinfo
.unix_info2
.in
.permissions
= info2
->permissions
;
125 sfinfo
.unix_info2
.in
.nlink
= info2
->nlink
;
126 sfinfo
.unix_info2
.in
.create_time
= info2
->create_time
;
127 sfinfo
.unix_info2
.in
.file_flags
= info2
->file_flags
;
128 sfinfo
.unix_info2
.in
.flags_mask
= info2
->flags_mask
;
130 return smb_raw_setpathinfo(cli
->tree
, &sfinfo
);
133 static bool query_file_path_info2(void *mem_ctx
,
134 struct torture_context
*torture
,
135 struct smbcli_state
*cli
,
138 struct unix_info2
*info2
)
141 union smb_fileinfo finfo
;
143 finfo
.generic
.level
= RAW_FILEINFO_UNIX_INFO2
;
146 finfo
.generic
.in
.file
.path
= fname
;
147 result
= smb_raw_pathinfo(cli
->tree
, mem_ctx
, &finfo
);
149 finfo
.generic
.in
.file
.fnum
= fnum
;
150 result
= smb_raw_fileinfo(cli
->tree
, mem_ctx
, &finfo
);
153 torture_assert_ntstatus_equal(torture
, result
, NT_STATUS_OK
,
154 smbcli_errstr(cli
->tree
));
156 info2
->end_of_file
= finfo
.unix_info2
.out
.end_of_file
;
157 info2
->num_bytes
= finfo
.unix_info2
.out
.num_bytes
;
158 info2
->status_change_time
= finfo
.unix_info2
.out
.status_change_time
;
159 info2
->access_time
= finfo
.unix_info2
.out
.access_time
;
160 info2
->change_time
= finfo
.unix_info2
.out
.change_time
;
161 info2
->uid
= finfo
.unix_info2
.out
.uid
;
162 info2
->gid
= finfo
.unix_info2
.out
.gid
;
163 info2
->file_type
= finfo
.unix_info2
.out
.file_type
;
164 info2
->dev_major
= finfo
.unix_info2
.out
.dev_major
;
165 info2
->dev_minor
= finfo
.unix_info2
.out
.dev_minor
;
166 info2
->unique_id
= finfo
.unix_info2
.out
.unique_id
;
167 info2
->permissions
= finfo
.unix_info2
.out
.permissions
;
168 info2
->nlink
= finfo
.unix_info2
.out
.nlink
;
169 info2
->create_time
= finfo
.unix_info2
.out
.create_time
;
170 info2
->file_flags
= finfo
.unix_info2
.out
.file_flags
;
171 info2
->flags_mask
= finfo
.unix_info2
.out
.flags_mask
;
173 if (!check_unix_info2(torture
, info2
)) {
180 static bool query_file_info2(void *mem_ctx
,
181 struct torture_context
*torture
,
182 struct smbcli_state
*cli
,
184 struct unix_info2
*info2
)
186 return query_file_path_info2(mem_ctx
, torture
, cli
,
190 static bool query_path_info2(void *mem_ctx
,
191 struct torture_context
*torture
,
192 struct smbcli_state
*cli
,
194 struct unix_info2
*info2
)
196 return query_file_path_info2(mem_ctx
, torture
, cli
,
200 static bool search_callback(void *private_data
, const union smb_search_data
*fdata
)
202 struct unix_info2
*info2
= (struct unix_info2
*)private_data
;
204 info2
->end_of_file
= fdata
->unix_info2
.end_of_file
;
205 info2
->num_bytes
= fdata
->unix_info2
.num_bytes
;
206 info2
->status_change_time
= fdata
->unix_info2
.status_change_time
;
207 info2
->access_time
= fdata
->unix_info2
.access_time
;
208 info2
->change_time
= fdata
->unix_info2
.change_time
;
209 info2
->uid
= fdata
->unix_info2
.uid
;
210 info2
->gid
= fdata
->unix_info2
.gid
;
211 info2
->file_type
= fdata
->unix_info2
.file_type
;
212 info2
->dev_major
= fdata
->unix_info2
.dev_major
;
213 info2
->dev_minor
= fdata
->unix_info2
.dev_minor
;
214 info2
->unique_id
= fdata
->unix_info2
.unique_id
;
215 info2
->permissions
= fdata
->unix_info2
.permissions
;
216 info2
->nlink
= fdata
->unix_info2
.nlink
;
217 info2
->create_time
= fdata
->unix_info2
.create_time
;
218 info2
->file_flags
= fdata
->unix_info2
.file_flags
;
219 info2
->flags_mask
= fdata
->unix_info2
.flags_mask
;
224 static bool find_single_info2(void *mem_ctx
,
225 struct torture_context
*torture
,
226 struct smbcli_state
*cli
,
228 struct unix_info2
*info2
)
230 union smb_search_first search
;
233 /* Set up a new search for a single item, not using resume keys. */
235 search
.t2ffirst
.level
= RAW_SEARCH_TRANS2
;
236 search
.t2ffirst
.data_level
= SMB_FIND_UNIX_INFO2
;
237 search
.t2ffirst
.in
.max_count
= 1;
238 search
.t2ffirst
.in
.flags
= FLAG_TRANS2_FIND_CLOSE
;
239 search
.t2ffirst
.in
.pattern
= fname
;
241 status
= smb_raw_search_first(cli
->tree
, mem_ctx
,
242 &search
, info2
, search_callback
);
243 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
244 smbcli_errstr(cli
->tree
));
246 torture_assert_int_equal(torture
, search
.t2ffirst
.out
.count
, 1,
247 "expected exactly one result");
248 torture_assert_int_equal(torture
, search
.t2ffirst
.out
.end_of_search
, 1,
249 "expected end_of_search to be true");
251 return check_unix_info2(torture
, info2
);
254 #define ASSERT_FLAGS_MATCH(info2, expected) \
255 if ((info2)->file_flags != (1 << i)) { \
256 torture_result(torture, TORTURE_FAIL, \
257 __location__": INFO2 flags field was 0x%08x, "\
258 "expected 0x%08x\n",\
259 (info2)->file_flags, expected); \
262 static void set_no_metadata_change(struct unix_info2
*info2
)
264 info2
->uid
= SMB_UID_NO_CHANGE
;
265 info2
->gid
= SMB_GID_NO_CHANGE
;
266 info2
->permissions
= SMB_MODE_NO_CHANGE
;
269 ((uint64_t)SMB_SIZE_NO_CHANGE_HI
<< 32) | SMB_SIZE_NO_CHANGE_LO
;
271 info2
->status_change_time
=
275 ((uint64_t)SMB_SIZE_NO_CHANGE_HI
<< 32) | SMB_SIZE_NO_CHANGE_LO
;
278 static bool verify_setinfo_flags(void *mem_ctx
,
279 struct torture_context
*torture
,
280 struct smbcli_state
*cli
,
283 struct unix_info2 info2
;
290 if (!query_path_info2(mem_ctx
, torture
, cli
, fname
, &info2
)) {
294 smb_fmask
= info2
.flags_mask
;
296 /* For each possible flag, ask to set exactly 1 flag, making sure
297 * that flag is in our requested mask.
299 for (i
= 0; i
< 32; ++i
) {
300 info2
.file_flags
= (1 << i
);
301 info2
.flags_mask
= smb_fmask
| info2
.file_flags
;
303 set_no_metadata_change(&info2
);
304 status
= set_path_info2(mem_ctx
, cli
, fname
, &info2
);
306 if (info2
.file_flags
& smb_fmask
) {
307 torture_assert_ntstatus_equal(torture
,
308 status
, NT_STATUS_OK
,
309 "setting valid UNIX_INFO2 flag");
311 if (!query_path_info2(mem_ctx
, torture
, cli
,
316 ASSERT_FLAGS_MATCH(&info2
, 1 << i
);
320 /* We tried to set a flag the server doesn't
323 torture_assert_ntstatus_equal(torture
,
324 status
, NT_STATUS_INVALID_PARAMETER
,
325 "setting invalid UNIX_INFO2 flag");
329 /* Make sure that a zero flags field does nothing. */
330 set_no_metadata_change(&info2
);
331 info2
.file_flags
= 0xFFFFFFFF;
332 info2
.flags_mask
= 0;
333 status
= set_path_info2(mem_ctx
, cli
, fname
, &info2
);
334 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
335 "setting empty flags mask");
341 static int create_file(struct smbcli_state
*cli
, const char * fname
)
344 return smbcli_nt_create_full(cli
->tree
, fname
, 0,
345 SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
, FILE_ATTRIBUTE_NORMAL
,
346 NTCREATEX_SHARE_ACCESS_NONE
, NTCREATEX_DISP_OPEN_IF
,
350 static bool match_info2(struct torture_context
*torture
,
351 const struct unix_info2
*pinfo
,
352 const struct unix_info2
*finfo
)
354 printf("checking results match\n");
356 torture_assert_u64_equal(torture
, finfo
->end_of_file
, 0,
357 "end_of_file should be 0");
358 torture_assert_u64_equal(torture
, finfo
->num_bytes
, 0,
359 "num_bytes should be 0");
361 torture_assert_u64_equal(torture
, finfo
->end_of_file
,
362 pinfo
->end_of_file
, "end_of_file mismatch");
363 torture_assert_u64_equal(torture
, finfo
->num_bytes
, pinfo
->num_bytes
,
364 "num_bytes mismatch");
366 /* Don't match access_time. */
368 torture_assert_u64_equal(torture
, finfo
->status_change_time
,
369 pinfo
->status_change_time
,
370 "status_change_time mismatch");
371 torture_assert_u64_equal(torture
, finfo
->change_time
,
372 pinfo
->change_time
, "change_time mismatch");
374 torture_assert_u64_equal(torture
, finfo
->uid
, pinfo
->uid
,
376 torture_assert_u64_equal(torture
, finfo
->gid
, pinfo
->gid
,
378 torture_assert_int_equal(torture
, finfo
->file_type
, pinfo
->file_type
,
379 "file_type mismatch");
380 torture_assert_u64_equal(torture
, finfo
->dev_major
, pinfo
->dev_major
,
381 "dev_major mismatch");
382 torture_assert_u64_equal(torture
, finfo
->dev_minor
, pinfo
->dev_minor
,
383 "dev_minor mismatch");
384 torture_assert_u64_equal(torture
, finfo
->unique_id
, pinfo
->unique_id
,
385 "unique_id mismatch");
386 torture_assert_u64_equal(torture
, finfo
->permissions
,
387 pinfo
->permissions
, "permissions mismatch");
388 torture_assert_u64_equal(torture
, finfo
->nlink
, pinfo
->nlink
,
390 torture_assert_u64_equal(torture
, finfo
->create_time
, pinfo
->create_time
,
391 "create_time mismatch");
397 #define FILENAME "\\smb_unix_info2.txt"
399 bool unix_torture_unix_info2(struct torture_context
*torture
)
402 struct smbcli_state
*cli
;
405 struct unix_info2 pinfo
, finfo
;
407 mem_ctx
= talloc_init("smb_query_unix_info2");
408 torture_assert(torture
, mem_ctx
!= NULL
, "out of memory");
410 if (!(cli
= connect_to_server(torture
))) {
411 talloc_free(mem_ctx
);
415 smbcli_unlink(cli
->tree
, FILENAME
);
417 fnum
= create_file(cli
, FILENAME
);
418 torture_assert(torture
, fnum
!= -1, smbcli_errstr(cli
->tree
));
420 printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryFileInfo\n");
421 if (!query_file_info2(mem_ctx
, torture
, cli
, fnum
, &finfo
)) {
425 printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryPathInfo\n");
426 if (!query_path_info2(mem_ctx
, torture
, cli
, FILENAME
, &pinfo
)) {
430 if (!match_info2(torture
, &pinfo
, &finfo
)) {
434 printf("checking SMB_FIND_UNIX_INFO2 for FindFirst\n");
435 if (!find_single_info2(mem_ctx
, torture
, cli
, FILENAME
, &pinfo
)) {
439 if (!match_info2(torture
, &pinfo
, &finfo
)) {
443 /* XXX: should repeat this test with SetFileInfo. */
444 printf("checking SMB_SFILEINFO_UNIX_INFO2 for SetPathInfo\n");
445 if (!verify_setinfo_flags(mem_ctx
, torture
, cli
, FILENAME
)) {
449 smbcli_close(cli
->tree
, fnum
);
450 smbcli_unlink(cli
->tree
, FILENAME
);
451 torture_close_connection(cli
);
452 talloc_free(mem_ctx
);
457 smbcli_close(cli
->tree
, fnum
);
458 smbcli_unlink(cli
->tree
, FILENAME
);
459 torture_close_connection(cli
);
460 talloc_free(mem_ctx
);
465 /* vim: set sts=8 sw=8 : */