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