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 torture_comment(tctx
, "failed to connect to //%s/%s: %s\n",
69 host
, share
, nt_errstr(status
));
70 torture_fail(tctx
, "Failed to connect to server");
77 static bool check_unix_info2(struct torture_context
*torture
,
78 struct unix_info2
*info2
)
80 printf("\tcreate_time=0x%016llu flags=0x%08x mask=0x%08x\n",
81 (unsigned long long)info2
->create_time
,
82 info2
->file_flags
, info2
->flags_mask
);
84 if (info2
->file_flags
== 0) {
88 /* If we have any file_flags set, they must be within the range
89 * defined by flags_mask.
91 if ((info2
->flags_mask
& info2
->file_flags
) == 0) {
92 torture_result(torture
, TORTURE_FAIL
,
93 __location__
": UNIX_INFO2 flags field 0x%08x, "
94 "does not match mask 0x%08x\n",
95 info2
->file_flags
, info2
->flags_mask
);
101 static NTSTATUS
set_path_info2(void *mem_ctx
,
102 struct smbcli_state
*cli
,
104 struct unix_info2
*info2
)
106 union smb_setfileinfo sfinfo
;
108 ZERO_STRUCT(sfinfo
.basic_info
.in
);
109 sfinfo
.generic
.level
= RAW_SFILEINFO_UNIX_INFO2
;
110 sfinfo
.generic
.in
.file
.path
= fname
;
112 sfinfo
.unix_info2
.in
.end_of_file
= info2
->end_of_file
;
113 sfinfo
.unix_info2
.in
.num_bytes
= info2
->num_bytes
;
114 sfinfo
.unix_info2
.in
.status_change_time
= info2
->status_change_time
;
115 sfinfo
.unix_info2
.in
.access_time
= info2
->access_time
;
116 sfinfo
.unix_info2
.in
.change_time
= info2
->change_time
;
117 sfinfo
.unix_info2
.in
.uid
= info2
->uid
;
118 sfinfo
.unix_info2
.in
.gid
= info2
->gid
;
119 sfinfo
.unix_info2
.in
.file_type
= info2
->file_type
;
120 sfinfo
.unix_info2
.in
.dev_major
= info2
->dev_major
;
121 sfinfo
.unix_info2
.in
.dev_minor
= info2
->dev_minor
;
122 sfinfo
.unix_info2
.in
.unique_id
= info2
->unique_id
;
123 sfinfo
.unix_info2
.in
.permissions
= info2
->permissions
;
124 sfinfo
.unix_info2
.in
.nlink
= info2
->nlink
;
125 sfinfo
.unix_info2
.in
.create_time
= info2
->create_time
;
126 sfinfo
.unix_info2
.in
.file_flags
= info2
->file_flags
;
127 sfinfo
.unix_info2
.in
.flags_mask
= info2
->flags_mask
;
129 return smb_raw_setpathinfo(cli
->tree
, &sfinfo
);
132 static bool query_file_path_info2(void *mem_ctx
,
133 struct torture_context
*torture
,
134 struct smbcli_state
*cli
,
137 struct unix_info2
*info2
)
140 union smb_fileinfo finfo
;
142 finfo
.generic
.level
= RAW_FILEINFO_UNIX_INFO2
;
145 finfo
.generic
.in
.file
.path
= fname
;
146 result
= smb_raw_pathinfo(cli
->tree
, mem_ctx
, &finfo
);
148 finfo
.generic
.in
.file
.fnum
= fnum
;
149 result
= smb_raw_fileinfo(cli
->tree
, mem_ctx
, &finfo
);
152 torture_assert_ntstatus_equal(torture
, result
, NT_STATUS_OK
,
153 smbcli_errstr(cli
->tree
));
155 info2
->end_of_file
= finfo
.unix_info2
.out
.end_of_file
;
156 info2
->num_bytes
= finfo
.unix_info2
.out
.num_bytes
;
157 info2
->status_change_time
= finfo
.unix_info2
.out
.status_change_time
;
158 info2
->access_time
= finfo
.unix_info2
.out
.access_time
;
159 info2
->change_time
= finfo
.unix_info2
.out
.change_time
;
160 info2
->uid
= finfo
.unix_info2
.out
.uid
;
161 info2
->gid
= finfo
.unix_info2
.out
.gid
;
162 info2
->file_type
= finfo
.unix_info2
.out
.file_type
;
163 info2
->dev_major
= finfo
.unix_info2
.out
.dev_major
;
164 info2
->dev_minor
= finfo
.unix_info2
.out
.dev_minor
;
165 info2
->unique_id
= finfo
.unix_info2
.out
.unique_id
;
166 info2
->permissions
= finfo
.unix_info2
.out
.permissions
;
167 info2
->nlink
= finfo
.unix_info2
.out
.nlink
;
168 info2
->create_time
= finfo
.unix_info2
.out
.create_time
;
169 info2
->file_flags
= finfo
.unix_info2
.out
.file_flags
;
170 info2
->flags_mask
= finfo
.unix_info2
.out
.flags_mask
;
172 if (!check_unix_info2(torture
, info2
)) {
179 static bool query_file_info2(void *mem_ctx
,
180 struct torture_context
*torture
,
181 struct smbcli_state
*cli
,
183 struct unix_info2
*info2
)
185 return query_file_path_info2(mem_ctx
, torture
, cli
,
189 static bool query_path_info2(void *mem_ctx
,
190 struct torture_context
*torture
,
191 struct smbcli_state
*cli
,
193 struct unix_info2
*info2
)
195 return query_file_path_info2(mem_ctx
, torture
, cli
,
199 static bool search_callback(void *private_data
, const union smb_search_data
*fdata
)
201 struct unix_info2
*info2
= (struct unix_info2
*)private_data
;
203 info2
->end_of_file
= fdata
->unix_info2
.end_of_file
;
204 info2
->num_bytes
= fdata
->unix_info2
.num_bytes
;
205 info2
->status_change_time
= fdata
->unix_info2
.status_change_time
;
206 info2
->access_time
= fdata
->unix_info2
.access_time
;
207 info2
->change_time
= fdata
->unix_info2
.change_time
;
208 info2
->uid
= fdata
->unix_info2
.uid
;
209 info2
->gid
= fdata
->unix_info2
.gid
;
210 info2
->file_type
= fdata
->unix_info2
.file_type
;
211 info2
->dev_major
= fdata
->unix_info2
.dev_major
;
212 info2
->dev_minor
= fdata
->unix_info2
.dev_minor
;
213 info2
->unique_id
= fdata
->unix_info2
.unique_id
;
214 info2
->permissions
= fdata
->unix_info2
.permissions
;
215 info2
->nlink
= fdata
->unix_info2
.nlink
;
216 info2
->create_time
= fdata
->unix_info2
.create_time
;
217 info2
->file_flags
= fdata
->unix_info2
.file_flags
;
218 info2
->flags_mask
= fdata
->unix_info2
.flags_mask
;
223 static bool find_single_info2(void *mem_ctx
,
224 struct torture_context
*torture
,
225 struct smbcli_state
*cli
,
227 struct unix_info2
*info2
)
229 union smb_search_first search
;
232 /* Set up a new search for a single item, not using resume keys. */
234 search
.t2ffirst
.level
= RAW_SEARCH_TRANS2
;
235 search
.t2ffirst
.data_level
= SMB_FIND_UNIX_INFO2
;
236 search
.t2ffirst
.in
.max_count
= 1;
237 search
.t2ffirst
.in
.flags
= FLAG_TRANS2_FIND_CLOSE
;
238 search
.t2ffirst
.in
.pattern
= fname
;
240 status
= smb_raw_search_first(cli
->tree
, mem_ctx
,
241 &search
, info2
, search_callback
);
242 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
243 smbcli_errstr(cli
->tree
));
245 torture_assert_int_equal(torture
, search
.t2ffirst
.out
.count
, 1,
246 "expected exactly one result");
247 torture_assert_int_equal(torture
, search
.t2ffirst
.out
.end_of_search
, 1,
248 "expected end_of_search to be true");
250 return check_unix_info2(torture
, info2
);
253 #define ASSERT_FLAGS_MATCH(info2, expected) \
254 if ((info2)->file_flags != (1 << i)) { \
255 torture_result(torture, TORTURE_FAIL, \
256 __location__": INFO2 flags field was 0x%08x, "\
257 "expected 0x%08x\n",\
258 (info2)->file_flags, expected); \
261 static void set_no_metadata_change(struct unix_info2
*info2
)
263 info2
->uid
= SMB_UID_NO_CHANGE
;
264 info2
->gid
= SMB_GID_NO_CHANGE
;
265 info2
->permissions
= SMB_MODE_NO_CHANGE
;
268 ((uint64_t)SMB_SIZE_NO_CHANGE_HI
<< 32) | SMB_SIZE_NO_CHANGE_LO
;
270 info2
->status_change_time
=
274 ((uint64_t)SMB_SIZE_NO_CHANGE_HI
<< 32) | SMB_SIZE_NO_CHANGE_LO
;
277 static bool verify_setinfo_flags(void *mem_ctx
,
278 struct torture_context
*torture
,
279 struct smbcli_state
*cli
,
282 struct unix_info2 info2
;
289 if (!query_path_info2(mem_ctx
, torture
, cli
, fname
, &info2
)) {
293 smb_fmask
= info2
.flags_mask
;
295 /* For each possible flag, ask to set exactly 1 flag, making sure
296 * that flag is in our requested mask.
298 for (i
= 0; i
< 32; ++i
) {
299 info2
.file_flags
= (1 << i
);
300 info2
.flags_mask
= smb_fmask
| info2
.file_flags
;
302 set_no_metadata_change(&info2
);
303 status
= set_path_info2(mem_ctx
, cli
, fname
, &info2
);
305 if (info2
.file_flags
& smb_fmask
) {
306 torture_assert_ntstatus_equal(torture
,
307 status
, NT_STATUS_OK
,
308 "setting valid UNIX_INFO2 flag");
310 if (!query_path_info2(mem_ctx
, torture
, cli
,
315 ASSERT_FLAGS_MATCH(&info2
, 1 << i
);
319 /* We tried to set a flag the server doesn't
322 torture_assert_ntstatus_equal(torture
,
323 status
, NT_STATUS_INVALID_PARAMETER
,
324 "setting invalid UNIX_INFO2 flag");
328 /* Make sure that a zero flags field does nothing. */
329 set_no_metadata_change(&info2
);
330 info2
.file_flags
= 0xFFFFFFFF;
331 info2
.flags_mask
= 0;
332 status
= set_path_info2(mem_ctx
, cli
, fname
, &info2
);
333 torture_assert_ntstatus_equal(torture
, status
, NT_STATUS_OK
,
334 "setting empty flags mask");
340 static int create_file(struct smbcli_state
*cli
, const char * fname
)
343 return smbcli_nt_create_full(cli
->tree
, fname
, 0,
344 SEC_FILE_READ_DATA
|SEC_FILE_WRITE_DATA
, FILE_ATTRIBUTE_NORMAL
,
345 NTCREATEX_SHARE_ACCESS_NONE
, NTCREATEX_DISP_OPEN_IF
,
349 static bool match_info2(struct torture_context
*torture
,
350 const struct unix_info2
*pinfo
,
351 const struct unix_info2
*finfo
)
353 printf("checking results match\n");
355 torture_assert_u64_equal(torture
, finfo
->end_of_file
, 0,
356 "end_of_file should be 0");
357 torture_assert_u64_equal(torture
, finfo
->num_bytes
, 0,
358 "num_bytes should be 0");
360 torture_assert_u64_equal(torture
, finfo
->end_of_file
,
361 pinfo
->end_of_file
, "end_of_file mismatch");
362 torture_assert_u64_equal(torture
, finfo
->num_bytes
, pinfo
->num_bytes
,
363 "num_bytes mismatch");
365 /* Don't match access_time. */
367 torture_assert_u64_equal(torture
, finfo
->status_change_time
,
368 pinfo
->status_change_time
,
369 "status_change_time mismatch");
370 torture_assert_u64_equal(torture
, finfo
->change_time
,
371 pinfo
->change_time
, "change_time mismatch");
373 torture_assert_u64_equal(torture
, finfo
->uid
, pinfo
->uid
,
375 torture_assert_u64_equal(torture
, finfo
->gid
, pinfo
->gid
,
377 torture_assert_int_equal(torture
, finfo
->file_type
, pinfo
->file_type
,
378 "file_type mismatch");
379 torture_assert_u64_equal(torture
, finfo
->dev_major
, pinfo
->dev_major
,
380 "dev_major mismatch");
381 torture_assert_u64_equal(torture
, finfo
->dev_minor
, pinfo
->dev_minor
,
382 "dev_minor mismatch");
383 torture_assert_u64_equal(torture
, finfo
->unique_id
, pinfo
->unique_id
,
384 "unique_id mismatch");
385 torture_assert_u64_equal(torture
, finfo
->permissions
,
386 pinfo
->permissions
, "permissions mismatch");
387 torture_assert_u64_equal(torture
, finfo
->nlink
, pinfo
->nlink
,
389 torture_assert_u64_equal(torture
, finfo
->create_time
, pinfo
->create_time
,
390 "create_time mismatch");
396 #define FILENAME "\\smb_unix_info2.txt"
398 bool unix_torture_unix_info2(struct torture_context
*torture
)
401 struct smbcli_state
*cli
;
404 struct unix_info2 pinfo
, finfo
;
406 mem_ctx
= talloc_init("smb_query_unix_info2");
407 torture_assert(torture
, mem_ctx
!= NULL
, "out of memory");
409 if (!(cli
= connect_to_server(torture
))) {
410 talloc_free(mem_ctx
);
414 smbcli_unlink(cli
->tree
, FILENAME
);
416 fnum
= create_file(cli
, FILENAME
);
417 torture_assert(torture
, fnum
!= -1, smbcli_errstr(cli
->tree
));
419 printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryFileInfo\n");
420 if (!query_file_info2(mem_ctx
, torture
, cli
, fnum
, &finfo
)) {
424 printf("checking SMB_QFILEINFO_UNIX_INFO2 for QueryPathInfo\n");
425 if (!query_path_info2(mem_ctx
, torture
, cli
, FILENAME
, &pinfo
)) {
429 if (!match_info2(torture
, &pinfo
, &finfo
)) {
433 printf("checking SMB_FIND_UNIX_INFO2 for FindFirst\n");
434 if (!find_single_info2(mem_ctx
, torture
, cli
, FILENAME
, &pinfo
)) {
438 if (!match_info2(torture
, &pinfo
, &finfo
)) {
442 /* XXX: should repeat this test with SetFileInfo. */
443 printf("checking SMB_SFILEINFO_UNIX_INFO2 for SetPathInfo\n");
444 if (!verify_setinfo_flags(mem_ctx
, torture
, cli
, FILENAME
)) {
448 smbcli_close(cli
->tree
, fnum
);
449 smbcli_unlink(cli
->tree
, FILENAME
);
450 torture_close_connection(cli
);
451 talloc_free(mem_ctx
);
456 smbcli_close(cli
->tree
, fnum
);
457 smbcli_unlink(cli
->tree
, FILENAME
);
458 torture_close_connection(cli
);
459 talloc_free(mem_ctx
);
464 /* vim: set sts=8 sw=8 : */