xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c (revision abdf5d9abf528d6c318fd8533e09bc3cac1f228b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <smbsrv/smb_vops.h>
27 #include <smbsrv/smb_incl.h>
28 #include <smbsrv/smb_fsops.h>
29 
30 /*
31  * Trans2 Query File/Path Information Levels:
32  *
33  * SMB_INFO_STANDARD
34  * SMB_INFO_QUERY_EA_SIZE
35  * SMB_INFO_QUERY_EAS_FROM_LIST
36  * SMB_INFO_QUERY_ALL_EAS - not valid for pipes
37  * SMB_INFO_IS_NAME_VALID - only valid when query is by path
38  *
39  * SMB_QUERY_FILE_BASIC_INFO
40  * SMB_QUERY_FILE_STANDARD_INFO
41  * SMB_QUERY_FILE_EA_INFO
42  * SMB_QUERY_FILE_NAME_INFO
43  * SMB_QUERY_FILE_ALL_INFO
44  * SMB_QUERY_FILE_ALT_NAME_INFO - not valid for pipes
45  * SMB_QUERY_FILE_STREAM_INFO - not valid for pipes
46  * SMB_QUERY_FILE_COMPRESSION_INFO - not valid for pipes
47  *
48  * Supported Passthrough levels:
49  * SMB_FILE_BASIC_INFORMATION
50  * SMB_FILE_STANDARD_INFORMATION
51  * SMB_FILE_INTERNAL_INFORMATION
52  * SMB_FILE_EA_INFORMATION
53  * SMB_FILE_ACCESS_INFORMATION - not yet supported quen query by path
54  * SMB_FILE_NAME_INFORMATION
55  * SMB_FILE_ALL_INFORMATION
56  * SMB_FILE_ALT_NAME_INFORMATION - not valid for pipes
57  * SMB_FILE_STREAM_INFORMATION - not valid for pipes
58  * SMB_FILE_COMPRESSION_INFORMATION - not valid for pipes
59  * SMB_FILE_ATTR_TAG_INFORMATION - not valid for pipes
60  *
61  * Internal levels representing non trans2 requests
62  * SMB_QUERY_INFORMATION
63  * SMB_QUERY_INFORMATION2
64  */
65 
66 typedef struct smb_queryinfo {
67 	smb_node_t	*qi_node;	/* NULL for pipes */
68 	smb_attr_t	qi_attr;
69 	boolean_t	qi_delete_on_close;
70 	uint32_t	qi_namelen;
71 	char		qi_name83[SMB_SHORTNAMELEN];
72 	char		qi_shortname[SMB_SHORTNAMELEN];
73 	char		qi_name[MAXPATHLEN];
74 } smb_queryinfo_t;
75 #define	qi_mtime	qi_attr.sa_vattr.va_mtime
76 #define	qi_ctime	qi_attr.sa_vattr.va_ctime
77 #define	qi_atime	qi_attr.sa_vattr.va_atime
78 #define	qi_crtime	qi_attr.sa_crtime
79 
80 static int smb_query_by_fid(smb_request_t *, smb_xa_t *, uint16_t);
81 static int smb_query_by_path(smb_request_t *, smb_xa_t *,
82     uint16_t, char *);
83 
84 static int smb_query_fileinfo(smb_request_t *, smb_node_t *,
85     uint16_t, smb_queryinfo_t *);
86 static int smb_query_pipeinfo(smb_request_t *, smb_opipe_t *,
87     uint16_t, smb_queryinfo_t *);
88 static boolean_t smb_query_pipe_valid_infolev(smb_request_t *, uint16_t);
89 
90 static int smb_query_encode_response(smb_request_t *, smb_xa_t *,
91     uint16_t, smb_queryinfo_t *);
92 static void smb_encode_stream_info(smb_request_t *, smb_xa_t *,
93     smb_queryinfo_t *);
94 static int smb_all_info_filename(smb_tree_t *, smb_node_t *, char *, size_t);
95 uint32_t smb_pad_align(uint32_t offset, uint32_t align);
96 
97 
98 /*
99  * smb_com_trans2_query_file_information
100  */
101 smb_sdrc_t
102 smb_com_trans2_query_file_information(struct smb_request *sr, struct smb_xa *xa)
103 {
104 	uint16_t infolev;
105 
106 	if (smb_mbc_decodef(&xa->req_param_mb, "ww",
107 	    &sr->smb_fid, &infolev) != 0)
108 		return (SDRC_ERROR);
109 
110 	if (smb_query_by_fid(sr, xa, infolev) != 0)
111 		return (SDRC_ERROR);
112 
113 	return (SDRC_SUCCESS);
114 }
115 
116 /*
117  * smb_com_trans2_query_path_information
118  */
119 smb_sdrc_t
120 smb_com_trans2_query_path_information(smb_request_t *sr, smb_xa_t *xa)
121 {
122 	uint16_t infolev;
123 	char *path;
124 
125 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
126 		smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST,
127 		    ERRDOS, ERROR_INVALID_FUNCTION);
128 		return (SDRC_ERROR);
129 	}
130 
131 	if (smb_mbc_decodef(&xa->req_param_mb, "%w4.u",
132 	    sr, &infolev, &path) != 0)
133 		return (SDRC_ERROR);
134 
135 	if (smb_query_by_path(sr, xa, infolev, path) != 0)
136 		return (SDRC_ERROR);
137 
138 	return (SDRC_SUCCESS);
139 }
140 
141 /*
142  * smb_com_query_information (aka getattr)
143  */
144 smb_sdrc_t
145 smb_pre_query_information(smb_request_t *sr)
146 {
147 	int rc;
148 	smb_fqi_t *fqi = &sr->arg.dirop.fqi;
149 
150 	rc = smbsr_decode_data(sr, "%S", sr, &fqi->fq_path.pn_path);
151 
152 	DTRACE_SMB_2(op__QueryInformation__start, smb_request_t *, sr,
153 	    smb_fqi_t *, fqi);
154 
155 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
156 }
157 
158 void
159 smb_post_query_information(smb_request_t *sr)
160 {
161 	DTRACE_SMB_1(op__QueryInformation__done, smb_request_t *, sr);
162 }
163 
164 smb_sdrc_t
165 smb_com_query_information(smb_request_t *sr)
166 {
167 	char *path = sr->arg.dirop.fqi.fq_path.pn_path;
168 	uint16_t infolev = SMB_QUERY_INFORMATION;
169 
170 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
171 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
172 		    ERRDOS, ERROR_ACCESS_DENIED);
173 		return (SDRC_ERROR);
174 	}
175 
176 	if (smb_query_by_path(sr, NULL, infolev, path) != 0)
177 		return (SDRC_ERROR);
178 
179 	return (SDRC_SUCCESS);
180 }
181 
182 /*
183  * smb_com_query_information2 (aka getattre)
184  */
185 smb_sdrc_t
186 smb_pre_query_information2(smb_request_t *sr)
187 {
188 	int rc;
189 	rc = smbsr_decode_vwv(sr, "w", &sr->smb_fid);
190 
191 	DTRACE_SMB_1(op__QueryInformation2__start, smb_request_t *, sr);
192 
193 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
194 }
195 
196 void
197 smb_post_query_information2(smb_request_t *sr)
198 {
199 	DTRACE_SMB_1(op__QueryInformation2__done, smb_request_t *, sr);
200 }
201 
202 smb_sdrc_t
203 smb_com_query_information2(smb_request_t *sr)
204 {
205 	uint16_t infolev = SMB_QUERY_INFORMATION2;
206 
207 	if (smb_query_by_fid(sr, NULL, infolev) != 0)
208 		return (SDRC_ERROR);
209 
210 	return (SDRC_SUCCESS);
211 }
212 
213 /*
214  * smb_query_by_fid
215  *
216  * Common code for querying file information by open file (or pipe) id.
217  * Use the id to identify the node / pipe object and request the
218  * smb_queryinfo_t data for that object.
219  */
220 static int
221 smb_query_by_fid(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
222 {
223 	int		rc;
224 	smb_queryinfo_t	*qinfo;
225 	smb_node_t	*node;
226 	smb_opipe_t	*opipe;
227 
228 	smbsr_lookup_file(sr);
229 
230 	if (sr->fid_ofile == NULL) {
231 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
232 		return (-1);
233 	}
234 
235 	if (infolev == SMB_INFO_IS_NAME_VALID) {
236 		smbsr_error(sr, 0, ERRDOS, ERRunknownlevel);
237 		smbsr_release_file(sr);
238 		return (-1);
239 	}
240 
241 	if ((sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE) &&
242 	    (!smb_query_pipe_valid_infolev(sr, infolev))) {
243 		smbsr_release_file(sr);
244 		return (-1);
245 	}
246 
247 	qinfo = kmem_alloc(sizeof (smb_queryinfo_t), KM_SLEEP);
248 
249 	switch (sr->fid_ofile->f_ftype) {
250 	case SMB_FTYPE_DISK:
251 		node = sr->fid_ofile->f_node;
252 		rc = smb_query_fileinfo(sr, node, infolev, qinfo);
253 		break;
254 	case SMB_FTYPE_MESG_PIPE:
255 		opipe = sr->fid_ofile->f_pipe;
256 		rc = smb_query_pipeinfo(sr, opipe, infolev, qinfo);
257 		break;
258 	default:
259 		smbsr_error(sr, 0, ERRDOS, ERRbadfile);
260 		rc = -1;
261 		break;
262 	}
263 
264 	if (rc == 0)
265 		rc = smb_query_encode_response(sr, xa, infolev, qinfo);
266 
267 	kmem_free(qinfo, sizeof (smb_queryinfo_t));
268 	smbsr_release_file(sr);
269 	return (rc);
270 }
271 
272 /*
273  * smb_query_by_path
274  *
275  * Common code for querying file information by file name.
276  * Use the file name to identify the node object and request the
277  * smb_queryinfo_t data for that node.
278  *
279  * Querying attributes on a named pipe by name is an error and
280  * is handled in the calling functions so that they can return
281  * the appropriate error status code (which differs by caller).
282  */
283 static int
284 smb_query_by_path(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev, char *path)
285 {
286 	smb_queryinfo_t	*qinfo;
287 	smb_node_t	*node, *dnode;
288 	int		rc;
289 	int		len;
290 
291 	/* VALID, but not yet supported */
292 	if (infolev == SMB_FILE_ACCESS_INFORMATION) {
293 		smbsr_error(sr, 0, ERRDOS, ERRunknownlevel);
294 		return (-1);
295 	}
296 
297 	/*
298 	 * Some MS clients pass NULL file names. NT interprets this as "\".
299 	 * Otherwise, if path is not "\\", remove the terminating slash.
300 	 */
301 	if ((len = strlen(path)) == 0)
302 		path = "\\";
303 	else {
304 		if ((len > 1) && (path[len - 1] == '\\')) {
305 			path[len - 1] = 0;
306 		}
307 	}
308 
309 	qinfo = kmem_alloc(sizeof (smb_queryinfo_t), KM_SLEEP);
310 
311 	rc = smb_pathname_reduce(sr, sr->user_cr, path, sr->tid_tree->t_snode,
312 	    sr->tid_tree->t_snode, &dnode, qinfo->qi_name);
313 	if (rc == 0) {
314 		rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS,
315 		    sr->tid_tree->t_snode, dnode, qinfo->qi_name, &node);
316 		smb_node_release(dnode);
317 	}
318 
319 	if (rc != 0) {
320 		if (rc == ENOENT)
321 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
322 			    ERRDOS, ERROR_FILE_NOT_FOUND);
323 		else
324 			smbsr_errno(sr, rc);
325 
326 		kmem_free(qinfo, sizeof (smb_queryinfo_t));
327 		return (-1);
328 	}
329 
330 	rc = smb_query_fileinfo(sr, node, infolev, qinfo);
331 	if (rc != 0) {
332 		kmem_free(qinfo, sizeof (smb_queryinfo_t));
333 		smb_node_release(node);
334 		return (rc);
335 	}
336 
337 	/* If delete_on_close - NT_STATUS_DELETE_PENDING */
338 	if (qinfo->qi_delete_on_close) {
339 		smbsr_error(sr, NT_STATUS_DELETE_PENDING,
340 		    ERRDOS, ERROR_ACCESS_DENIED);
341 		kmem_free(qinfo, sizeof (smb_queryinfo_t));
342 		smb_node_release(node);
343 		return (-1);
344 	}
345 
346 	rc = smb_query_encode_response(sr, xa, infolev, qinfo);
347 	kmem_free(qinfo, sizeof (smb_queryinfo_t));
348 	smb_node_release(node);
349 	return (rc);
350 }
351 
352 /*
353  * smb_size32
354  * Some responses only support 32 bit file sizes. If the file size
355  * exceeds UINT_MAX (32 bit) we return UINT_MAX in the response.
356  */
357 static uint32_t
358 smb_size32(u_offset_t size)
359 {
360 	return ((size > UINT_MAX) ? UINT_MAX : (uint32_t)size);
361 }
362 
363 /*
364  * smb_query_encode_response
365  *
366  * Encode the data from smb_queryinfo_t into client response
367  */
368 int
369 smb_query_encode_response(smb_request_t *sr, smb_xa_t *xa,
370     uint16_t infolev, smb_queryinfo_t *qinfo)
371 {
372 	uint16_t dattr;
373 	u_offset_t datasz, allocsz;
374 
375 	dattr = qinfo->qi_attr.sa_dosattr & FILE_ATTRIBUTE_MASK;
376 	datasz = qinfo->qi_attr.sa_vattr.va_size;
377 	allocsz = qinfo->qi_attr.sa_allocsz;
378 
379 	switch (infolev) {
380 	case SMB_QUERY_INFORMATION:
381 		(void) smbsr_encode_result(sr, 10, 0, "bwll10.w",
382 		    10,
383 		    dattr,
384 		    smb_time_gmt_to_local(sr, qinfo->qi_mtime.tv_sec),
385 		    smb_size32(datasz),
386 		    0);
387 		break;
388 
389 	case SMB_QUERY_INFORMATION2:
390 		(void) smbsr_encode_result(sr, 11, 0, "byyyllww",
391 		    11,
392 		    smb_time_gmt_to_local(sr, qinfo->qi_crtime.tv_sec),
393 		    smb_time_gmt_to_local(sr, qinfo->qi_atime.tv_sec),
394 		    smb_time_gmt_to_local(sr, qinfo->qi_mtime.tv_sec),
395 		    smb_size32(datasz), smb_size32(allocsz), dattr, 0);
396 	break;
397 
398 	case SMB_FILE_ACCESS_INFORMATION:
399 		ASSERT(sr->fid_ofile);
400 		(void) smb_mbc_encodef(&xa->rep_data_mb, "l",
401 		    sr->fid_ofile->f_granted_access);
402 		break;
403 
404 	case SMB_INFO_STANDARD:
405 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
406 		(void) smb_mbc_encodef(&xa->rep_data_mb,
407 		    ((sr->session->native_os == NATIVE_OS_WIN95) ?
408 		    "YYYllw" : "yyyllw"),
409 		    smb_time_gmt_to_local(sr, qinfo->qi_crtime.tv_sec),
410 		    smb_time_gmt_to_local(sr, qinfo->qi_atime.tv_sec),
411 		    smb_time_gmt_to_local(sr, qinfo->qi_mtime.tv_sec),
412 		    smb_size32(datasz), smb_size32(allocsz), dattr);
413 		break;
414 
415 	case SMB_INFO_QUERY_EA_SIZE:
416 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
417 		(void) smb_mbc_encodef(&xa->rep_data_mb,
418 		    ((sr->session->native_os == NATIVE_OS_WIN95) ?
419 		    "YYYllwl" : "yyyllwl"),
420 		    smb_time_gmt_to_local(sr, qinfo->qi_crtime.tv_sec),
421 		    smb_time_gmt_to_local(sr, qinfo->qi_atime.tv_sec),
422 		    smb_time_gmt_to_local(sr, qinfo->qi_mtime.tv_sec),
423 		    smb_size32(datasz), smb_size32(allocsz), dattr, 0);
424 		break;
425 
426 	case SMB_INFO_QUERY_ALL_EAS:
427 	case SMB_INFO_QUERY_EAS_FROM_LIST:
428 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
429 		(void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0);
430 		break;
431 
432 	case SMB_INFO_IS_NAME_VALID:
433 		break;
434 
435 	case SMB_QUERY_FILE_BASIC_INFO:
436 	case SMB_FILE_BASIC_INFORMATION:
437 		/*
438 		 * NT includes 6 bytes (spec says 4) at the end of this
439 		 * response, which are required by NetBench 5.01.
440 		 */
441 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
442 		(void) smb_mbc_encodef(&xa->rep_data_mb, "TTTTw6.",
443 		    &qinfo->qi_crtime,
444 		    &qinfo->qi_atime,
445 		    &qinfo->qi_mtime,
446 		    &qinfo->qi_ctime,
447 		    dattr);
448 		break;
449 
450 	case SMB_QUERY_FILE_STANDARD_INFO:
451 	case SMB_FILE_STANDARD_INFORMATION:
452 		/* 2-byte pad at end */
453 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
454 		(void) smb_mbc_encodef(&xa->rep_data_mb, "qqlbb2.",
455 		    (uint64_t)allocsz,
456 		    (uint64_t)datasz,
457 		    qinfo->qi_attr.sa_vattr.va_nlink,
458 		    qinfo->qi_delete_on_close,
459 		    (qinfo->qi_attr.sa_vattr.va_type == VDIR));
460 		break;
461 
462 	case SMB_QUERY_FILE_EA_INFO:
463 	case SMB_FILE_EA_INFORMATION:
464 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
465 		(void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0);
466 		break;
467 
468 	case SMB_QUERY_FILE_NAME_INFO:
469 	case SMB_FILE_NAME_INFORMATION:
470 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
471 		(void) smb_mbc_encodef(&xa->rep_data_mb, "%lu", sr,
472 		    qinfo->qi_namelen, qinfo->qi_name);
473 		break;
474 
475 	case SMB_QUERY_FILE_ALL_INFO:
476 	case SMB_FILE_ALL_INFORMATION:
477 		/*
478 		 * There is a 6-byte pad between Attributes and AllocationSize,
479 		 * and a 2-byte pad after the Directory field.
480 		 */
481 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
482 		(void) smb_mbc_encodef(&xa->rep_data_mb, "TTTTw6.qqlbb2.l",
483 		    &qinfo->qi_crtime,
484 		    &qinfo->qi_atime,
485 		    &qinfo->qi_mtime,
486 		    &qinfo->qi_ctime,
487 		    dattr,
488 		    (uint64_t)allocsz,
489 		    (uint64_t)datasz,
490 		    qinfo->qi_attr.sa_vattr.va_nlink,
491 		    qinfo->qi_delete_on_close,
492 		    (qinfo->qi_attr.sa_vattr.va_type == VDIR),
493 		    0);
494 
495 		(void) smb_mbc_encodef(&xa->rep_data_mb, "%lu",
496 		    sr, qinfo->qi_namelen, qinfo->qi_name);
497 		break;
498 
499 	case SMB_QUERY_FILE_ALT_NAME_INFO:
500 	case SMB_FILE_ALT_NAME_INFORMATION:
501 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
502 		(void) smb_mbc_encodef(&xa->rep_data_mb, "%lU", sr,
503 		    mts_wcequiv_strlen(qinfo->qi_shortname),
504 		    qinfo->qi_shortname);
505 		break;
506 
507 	case SMB_QUERY_FILE_STREAM_INFO:
508 	case SMB_FILE_STREAM_INFORMATION:
509 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
510 		smb_encode_stream_info(sr, xa, qinfo);
511 		break;
512 
513 	case SMB_QUERY_FILE_COMPRESSION_INFO:
514 	case SMB_FILE_COMPRESSION_INFORMATION:
515 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
516 		(void) smb_mbc_encodef(&xa->rep_data_mb, "qwbbb3.",
517 		    datasz, 0, 0, 0, 0);
518 		break;
519 
520 	case SMB_FILE_INTERNAL_INFORMATION:
521 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
522 		(void) smb_mbc_encodef(&xa->rep_data_mb, "q",
523 		    qinfo->qi_attr.sa_vattr.va_nodeid);
524 		break;
525 
526 	case SMB_FILE_ATTR_TAG_INFORMATION:
527 		/*
528 		 * If dattr includes FILE_ATTRIBUTE_REPARSE_POINT, the
529 		 * second dword should be the reparse tag.  Otherwise
530 		 * the tag value should be set to zero.
531 		 * We don't support reparse points, so we set the tag
532 		 * to zero.
533 		 */
534 		(void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0);
535 		(void) smb_mbc_encodef(&xa->rep_data_mb, "ll",
536 		    (uint32_t)dattr, 0);
537 		break;
538 
539 	default:
540 		smbsr_error(sr, 0, ERRDOS, ERRunknownlevel);
541 		return (-1);
542 	}
543 
544 	return (0);
545 }
546 
547 /*
548  * smb_encode_stream_info
549  *
550  * This function encodes the streams information.
551  * The following rules about how have been derived from observed NT
552  * behaviour.
553  *
554  * If the target is a file:
555  * 1. If there are no named streams, the response should still contain
556  *    an entry for the unnamed stream.
557  * 2. If there are named streams, the response should contain an entry
558  *    for the unnamed stream followed by the entries for the named
559  *    streams.
560  *
561  * If the target is a directory:
562  * 1. If there are no streams, the response is complete. Directories
563  *    do not report the unnamed stream.
564  * 2. If there are streams, the response should contain entries for
565  *    those streams but there should not be an entry for the unnamed
566  *    stream.
567  *
568  * Note that the stream name lengths exclude the null terminator but
569  * the field lengths (i.e. next offset calculations) need to include
570  * the null terminator and be padded to a multiple of 8 bytes. The
571  * last entry does not seem to need any padding.
572  *
573  * If an error is encountered when trying to read the stream entries
574  * (smb_odir_read_streaminfo) it is treated as if there are no [more]
575  * entries. The entries that have been read so far are returned and
576  * no error is reported.
577  *
578  * Offset calculation:
579  * 2 dwords + 2 quadwords => 4 + 4 + 8 + 8 => 24
580  */
581 static void
582 smb_encode_stream_info(smb_request_t *sr, smb_xa_t *xa, smb_queryinfo_t *qinfo)
583 {
584 	char *stream_name;
585 	uint32_t next_offset;
586 	uint32_t stream_nlen;
587 	uint32_t pad;
588 	u_offset_t datasz, allocsz;
589 	boolean_t is_dir;
590 	smb_streaminfo_t *sinfo, *sinfo_next;
591 	int rc = 0;
592 	boolean_t done = B_FALSE;
593 	boolean_t eos = B_FALSE;
594 	uint16_t odid;
595 	smb_odir_t *od = NULL;
596 
597 	smb_node_t *fnode = qinfo->qi_node;
598 	smb_attr_t *attr = &qinfo->qi_attr;
599 
600 	ASSERT(fnode);
601 	if (SMB_IS_STREAM(fnode)) {
602 		fnode = fnode->n_unode;
603 		ASSERT(fnode);
604 	}
605 	ASSERT(fnode->n_magic == SMB_NODE_MAGIC);
606 	ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING);
607 
608 	sinfo = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
609 	sinfo_next = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
610 	is_dir = (attr->sa_vattr.va_type == VDIR);
611 	datasz = attr->sa_vattr.va_size;
612 	allocsz = attr->sa_allocsz;
613 
614 	odid = smb_odir_openat(sr, fnode);
615 	if (odid != 0)
616 		od = smb_tree_lookup_odir(sr->tid_tree, odid);
617 	if (od != NULL)
618 		rc = smb_odir_read_streaminfo(sr, od, sinfo, &eos);
619 
620 	if ((od == NULL) || (rc != 0) || (eos))
621 		done = B_TRUE;
622 
623 	/* If not a directory, encode an entry for the unnamed stream. */
624 	if (!is_dir) {
625 		stream_name = "::$DATA";
626 		stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name);
627 
628 		if (done)
629 			next_offset = 0;
630 		else
631 			next_offset = 24 + stream_nlen +
632 			    smb_ascii_or_unicode_null_len(sr);
633 
634 		(void) smb_mbc_encodef(&xa->rep_data_mb, "%llqqu", sr,
635 		    next_offset, stream_nlen, datasz, allocsz, stream_name);
636 	}
637 
638 	/*
639 	 * Since last packet does not have a pad we need to check
640 	 * for the next stream before we encode the current one
641 	 */
642 	while (!done) {
643 		stream_nlen = smb_ascii_or_unicode_strlen(sr, sinfo->si_name);
644 		sinfo_next->si_name[0] = 0;
645 
646 		rc = smb_odir_read_streaminfo(sr, od, sinfo_next, &eos);
647 		if ((rc != 0) || (eos)) {
648 			done = B_TRUE;
649 			next_offset = 0;
650 			pad = 0;
651 		} else {
652 			next_offset = 24 + stream_nlen +
653 			    smb_ascii_or_unicode_null_len(sr);
654 			pad = smb_pad_align(next_offset, 8);
655 			next_offset += pad;
656 		}
657 		(void) smb_mbc_encodef(&xa->rep_data_mb, "%llqqu#.",
658 		    sr, next_offset, stream_nlen,
659 		    sinfo->si_size, sinfo->si_alloc_size,
660 		    sinfo->si_name, pad);
661 
662 		(void) memcpy(sinfo, sinfo_next, sizeof (smb_streaminfo_t));
663 	}
664 
665 	kmem_free(sinfo, sizeof (smb_streaminfo_t));
666 	kmem_free(sinfo_next, sizeof (smb_streaminfo_t));
667 	if (od) {
668 		smb_odir_close(od);
669 		smb_odir_release(od);
670 	}
671 }
672 
673 /*
674  * smb_pad_align
675  *
676  * Returns the number of bytes required to pad an offset to the
677  * specified alignment.
678  */
679 uint32_t
680 smb_pad_align(uint32_t offset, uint32_t align)
681 {
682 	uint32_t pad = offset % align;
683 
684 	if (pad != 0)
685 		pad = align - pad;
686 
687 	return (pad);
688 }
689 
690 /*
691  * smb_all_info_filename
692  *
693  * This format of filename is only used by the ALL_INFO levels.
694  *
695  * Determine the absolute pathname of 'node' within the share.
696  * For example if the node represents file "test1.txt" in directory
697  * "dir1" on share "share1", the path would be: \share1\dir1\test1.txt
698  *
699  * If node represents a named stream, construct the pathname for the
700  * associated unnamed stream then append the stream name.
701  */
702 static int
703 smb_all_info_filename(smb_tree_t *tree, smb_node_t *node,
704     char *buf, size_t buflen)
705 {
706 	char *sharename = tree->t_sharename;
707 	int rc;
708 	size_t len;
709 	vnode_t *vp;
710 
711 	len = snprintf(buf, buflen, "\\%s", sharename);
712 	if (len == (buflen - 1))
713 		return (ENAMETOOLONG);
714 
715 	buf += len;
716 	buflen -= len;
717 
718 	if (SMB_IS_STREAM(node))
719 		vp = node->n_unode->vp;
720 	else
721 		vp = node->vp;
722 
723 	rc = vnodetopath(tree->t_snode->vp, vp, buf, buflen, kcred);
724 	if (rc == 0) {
725 		(void) strsubst(buf, '/', '\\');
726 
727 		if (SMB_IS_STREAM(node))
728 			(void) strlcat(buf, node->od_name, buflen);
729 	}
730 
731 	return (rc);
732 }
733 
734 /*
735  * smb_query_fileinfo
736  *
737  * Populate smb_queryinfo_t structure for SMB_FTYPE_DISK
738  * (This should become an smb_ofile / smb_node function.)
739  */
740 int
741 smb_query_fileinfo(smb_request_t *sr, smb_node_t *node, uint16_t infolev,
742     smb_queryinfo_t *qinfo)
743 {
744 	char *namep = node->od_name;
745 	int rc;
746 
747 	(void) bzero(qinfo, sizeof (smb_queryinfo_t));
748 
749 	if (smb_node_getattr(sr, node, &qinfo->qi_attr) != 0) {
750 		smbsr_error(sr, NT_STATUS_INTERNAL_ERROR,
751 		    ERRDOS, ERROR_INTERNAL_ERROR);
752 		return (-1);
753 	}
754 
755 	qinfo->qi_node = node;
756 	qinfo->qi_delete_on_close =
757 	    (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0;
758 
759 	/*
760 	 * The number of links reported should be the number of
761 	 * non-deleted links. Thus if delete_on_close is set,
762 	 * decrement the link count.
763 	 */
764 	if (qinfo->qi_delete_on_close &&
765 	    qinfo->qi_attr.sa_vattr.va_nlink > 0) {
766 		--(qinfo->qi_attr.sa_vattr.va_nlink);
767 	}
768 
769 	/* populate name, namelen and shortname */
770 
771 	/* ALL_INFO levels are a special case for name field */
772 	if ((infolev == SMB_QUERY_FILE_ALL_INFO) ||
773 	    (infolev == SMB_FILE_ALL_INFORMATION)) {
774 		rc = smb_all_info_filename(sr->tid_tree, node,
775 		    qinfo->qi_name, MAXPATHLEN);
776 		if (rc != 0) {
777 			smbsr_errno(sr, rc);
778 			return (-1);
779 		}
780 		qinfo->qi_namelen =
781 		    smb_ascii_or_unicode_strlen(sr, qinfo->qi_name);
782 		return (0);
783 	}
784 
785 	/*
786 	 * It looks like NT doesn't know what to do with the name "."
787 	 * so we convert it to "\\" to indicate the root directory.
788 	 * If the leading \ is missing, add it.
789 	 */
790 	if (strcmp(namep, ".") == 0)
791 		(void) strlcpy(qinfo->qi_name, "\\", MAXNAMELEN);
792 	else if (*namep != '\\')
793 		(void) snprintf(qinfo->qi_name, MAXNAMELEN, "\\%s", namep);
794 	else
795 		(void) strlcpy(qinfo->qi_name, namep, MAXNAMELEN);
796 
797 	qinfo->qi_namelen = smb_ascii_or_unicode_strlen(sr, qinfo->qi_name);
798 
799 	/*
800 	 * For some reason NT will not show the security tab in the root
801 	 * directory of a mapped drive unless the filename length is
802 	 * greater than one. So we hack the length here to persuade NT
803 	 * to show the tab. It should be safe because of the null
804 	 * terminator character.
805 	 */
806 	if (qinfo->qi_namelen == 1)
807 		qinfo->qi_namelen = 2;
808 
809 	/*
810 	 * If the shortname is generated by smb_mangle_name()
811 	 * it will be returned as the alternative name.
812 	 * Otherwise, convert the original name to  upper-case
813 	 * and return it as the alternative name.
814 	 */
815 	(void) smb_mangle_name(qinfo->qi_attr.sa_vattr.va_nodeid,
816 	    namep, qinfo->qi_shortname, qinfo->qi_name83, 0);
817 	if (*qinfo->qi_shortname == 0) {
818 		(void) strlcpy(qinfo->qi_shortname, namep, SMB_SHORTNAMELEN);
819 		(void) utf8_strupr(qinfo->qi_shortname);
820 	}
821 
822 	return (0);
823 }
824 
825 /*
826  * smb_query_pipeinfo
827  *
828  * Populate smb_queryinfo_t structure for SMB_FTYPE_MESG_PIPE
829  * (This should become an smb_opipe function.)
830  */
831 static int
832 smb_query_pipeinfo(smb_request_t *sr, smb_opipe_t *opipe, uint16_t infolev,
833     smb_queryinfo_t *qinfo)
834 {
835 	char *namep = opipe->p_name;
836 
837 	(void) bzero(qinfo, sizeof (smb_queryinfo_t));
838 	qinfo->qi_node = NULL;
839 	qinfo->qi_attr.sa_vattr.va_nlink = 1;
840 	qinfo->qi_delete_on_close = 1;
841 
842 	if ((infolev == SMB_INFO_STANDARD) ||
843 	    (infolev == SMB_INFO_QUERY_EA_SIZE) ||
844 	    (infolev == SMB_QUERY_INFORMATION2)) {
845 		qinfo->qi_attr.sa_dosattr = 0;
846 	} else {
847 		qinfo->qi_attr.sa_dosattr = FILE_ATTRIBUTE_NORMAL;
848 	}
849 
850 	/* If the leading \ is missing from the pipe name, add it. */
851 	if (*namep != '\\')
852 		(void) snprintf(qinfo->qi_name, MAXNAMELEN, "\\%s", namep);
853 	else
854 		(void) strlcpy(qinfo->qi_name, namep, MAXNAMELEN);
855 
856 	qinfo->qi_namelen=
857 	    smb_ascii_or_unicode_strlen(sr, qinfo->qi_name);
858 
859 	return (0);
860 }
861 
862 /*
863  * smb_query_pipe_valid_infolev
864  *
865  * If the infolev is not valid for a message pipe, the error
866  * information is set in sr and B_FALSE is returned.
867  * Otherwise, returns B_TRUE.
868  */
869 static boolean_t
870 smb_query_pipe_valid_infolev(smb_request_t *sr, uint16_t infolev)
871 {
872 	switch (infolev) {
873 	case SMB_INFO_QUERY_ALL_EAS:
874 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
875 		    ERRDOS, ERROR_ACCESS_DENIED);
876 		return (B_FALSE);
877 
878 	case SMB_QUERY_FILE_ALT_NAME_INFO:
879 	case SMB_FILE_ALT_NAME_INFORMATION:
880 	case SMB_QUERY_FILE_STREAM_INFO:
881 	case SMB_FILE_STREAM_INFORMATION:
882 	case SMB_QUERY_FILE_COMPRESSION_INFO:
883 	case SMB_FILE_COMPRESSION_INFORMATION:
884 	case SMB_FILE_ATTR_TAG_INFORMATION:
885 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
886 		    ERRDOS, ERROR_INVALID_PARAMETER);
887 		return (B_FALSE);
888 	}
889 
890 	return (B_TRUE);
891 }
892