xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c (revision 76c08ae9d10f4e0b653a6ea98c06a7868246164b)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
14  * Copyright 2022 RackTop Systems, Inc.
15  */
16 
17 /*
18  * Dispatch function for SMB2_QUERY_INFO
19  *
20  * [MS-FSCC 2.4] If a file system does not support ...
21  * an Information Classs, NT_STATUS_INVALID_PARAMETER...
22  */
23 
24 #include <smbsrv/smb2_kproto.h>
25 #include <smbsrv/smb_fsops.h>
26 #include <smbsrv/ntifs.h>
27 
28 static uint32_t smb2_qif_all(smb_request_t *, smb_queryinfo_t *);
29 static uint32_t smb2_qif_basic(smb_request_t *, smb_queryinfo_t *);
30 static uint32_t smb2_qif_standard(smb_request_t *, smb_queryinfo_t *);
31 static uint32_t smb2_qif_internal(smb_request_t *, smb_queryinfo_t *);
32 static uint32_t smb2_qif_ea_size(smb_request_t *, smb_queryinfo_t *);
33 static uint32_t smb2_qif_access(smb_request_t *, smb_queryinfo_t *);
34 static uint32_t smb2_qif_name(smb_request_t *, smb_queryinfo_t *);
35 static uint32_t smb2_qif_position(smb_request_t *, smb_queryinfo_t *);
36 static uint32_t smb2_qif_full_ea(smb_request_t *, smb_queryinfo_t *);
37 static uint32_t smb2_qif_mode(smb_request_t *, smb_queryinfo_t *);
38 static uint32_t smb2_qif_alignment(smb_request_t *, smb_queryinfo_t *);
39 static uint32_t smb2_qif_all(smb_request_t *, smb_queryinfo_t *);
40 static uint32_t smb2_qif_altname(smb_request_t *, smb_queryinfo_t *);
41 static uint32_t smb2_qif_stream(smb_request_t *, smb_queryinfo_t *);
42 static uint32_t smb2_qif_pipe(smb_request_t *, smb_queryinfo_t *);
43 static uint32_t smb2_qif_pipe_lcl(smb_request_t *, smb_queryinfo_t *);
44 static uint32_t smb2_qif_pipe_rem(smb_request_t *, smb_queryinfo_t *);
45 static uint32_t smb2_qif_compr(smb_request_t *, smb_queryinfo_t *);
46 static uint32_t smb2_qif_opens(smb_request_t *, smb_queryinfo_t *);
47 static uint32_t smb2_qif_tags(smb_request_t *, smb_queryinfo_t *);
48 
49 
50 uint32_t
51 smb2_qinfo_file(smb_request_t *sr, smb_queryinfo_t *qi)
52 {
53 	smb_ofile_t *of = sr->fid_ofile;
54 	uint_t mask = 0;
55 	boolean_t getstd = B_FALSE;
56 	boolean_t getname = B_FALSE;
57 	uint32_t status;
58 
59 	/*
60 	 * Which attributes do we need from the FS?
61 	 */
62 	switch (qi->qi_InfoClass) {
63 	case FileBasicInformation:
64 		mask = SMB_AT_BASIC;
65 		break;
66 	case FileStandardInformation:
67 		mask = SMB_AT_STANDARD;
68 		getstd = B_TRUE;
69 		break;
70 	case FileInternalInformation:
71 		mask = SMB_AT_NODEID;
72 		break;
73 	case FileAllInformation:
74 		mask = SMB_AT_ALL;
75 		getstd = B_TRUE;
76 		if (sr->session->dialect < SMB_VERS_3_11) {
77 			/* See smb2_qif_all() */
78 			getname = B_TRUE;
79 		}
80 		break;
81 
82 	case FileNameInformation:
83 		getname = B_TRUE;
84 		break;
85 
86 	case FileAlternateNameInformation:
87 		mask = SMB_AT_NODEID;
88 		getname = B_TRUE;
89 		break;
90 
91 	case FileStreamInformation:
92 		mask = SMB_AT_STANDARD;
93 		getstd = B_TRUE;
94 		break;
95 
96 	case FileCompressionInformation:
97 		mask = SMB_AT_SIZE | SMB_AT_ALLOCSZ;
98 		break;
99 
100 	case FileNetworkOpenInformation:
101 		mask = SMB_AT_BASIC | SMB_AT_STANDARD;
102 
103 	default:
104 		break;
105 	}
106 
107 	qi->qi_attr.sa_mask = mask;
108 	qi->qi_node = of->f_node;
109 	if (mask & SMB_AT_ALL) {
110 		status = smb2_ofile_getattr(sr, of, &qi->qi_attr);
111 		if (status)
112 			return (status);
113 	}
114 	if (getstd) {
115 		status = smb2_ofile_getstd(of, qi);
116 		if (status)
117 			return (status);
118 	}
119 	if (getname) {
120 		status = smb2_ofile_getname(of, qi);
121 		if (status)
122 			return (status);
123 	}
124 
125 	switch (qi->qi_InfoClass) {
126 	case FileBasicInformation:
127 		status = smb2_qif_basic(sr, qi);
128 		break;
129 	case FileStandardInformation:
130 		status = smb2_qif_standard(sr, qi);
131 		break;
132 	case FileInternalInformation:
133 		status = smb2_qif_internal(sr, qi);
134 		break;
135 	case FileEaInformation:
136 		status = smb2_qif_ea_size(sr, qi);
137 		break;
138 	case FileAccessInformation:
139 		status = smb2_qif_access(sr, qi);
140 		break;
141 	case FileNameInformation:
142 		status = smb2_qif_name(sr, qi);
143 		break;
144 	case FilePositionInformation:
145 		status = smb2_qif_position(sr, qi);
146 		break;
147 	case FileFullEaInformation:
148 		status = smb2_qif_full_ea(sr, qi);
149 		break;
150 	case FileModeInformation:
151 		status = smb2_qif_mode(sr, qi);
152 		break;
153 	case FileAlignmentInformation:
154 		status = smb2_qif_alignment(sr, qi);
155 		break;
156 	case FileAllInformation:
157 		status = smb2_qif_all(sr, qi);
158 		break;
159 	case FileAlternateNameInformation:
160 		status = smb2_qif_altname(sr, qi);
161 		break;
162 	case FileStreamInformation:
163 		status = smb2_qif_stream(sr, qi);
164 		break;
165 	case FilePipeInformation:
166 		status = smb2_qif_pipe(sr, qi);
167 		break;
168 	case FilePipeLocalInformation:
169 		status = smb2_qif_pipe_lcl(sr, qi);
170 		break;
171 	case FilePipeRemoteInformation:
172 		status = smb2_qif_pipe_rem(sr, qi);
173 		break;
174 	case FileCompressionInformation:
175 		status = smb2_qif_compr(sr, qi);
176 		break;
177 	case FileNetworkOpenInformation:
178 		status = smb2_qif_opens(sr, qi);
179 		break;
180 	case FileAttributeTagInformation:
181 		status = smb2_qif_tags(sr, qi);
182 		break;
183 	default:
184 		status = NT_STATUS_INVALID_INFO_CLASS;
185 		break;
186 	}
187 
188 	return (status);
189 }
190 
191 /*
192  * FileAllInformation
193  *
194  * This returns a concatenation of:
195  *	FileBasicInformation
196  *	FileStandardInformation
197  *	FileInternalInformation
198  *	FileEaInformation
199  *	FilePositionInformation
200  *	FileModeInformation
201  *	FileAlignmentInformation
202  *	FileNameInformation
203  *
204  * Note: FileNameInformation is all zero on Win2016 and later.
205  */
206 static uint32_t
207 smb2_qif_all(smb_request_t *sr, smb_queryinfo_t *qi)
208 {
209 	uint32_t status;
210 
211 	status = smb2_qif_basic(sr, qi);
212 	if (status)
213 		return (status);
214 	status = smb2_qif_standard(sr, qi);
215 	if (status)
216 		return (status);
217 	status = smb2_qif_internal(sr, qi);
218 	if (status)
219 		return (status);
220 	status = smb2_qif_ea_size(sr, qi);
221 	if (status)
222 		return (status);
223 	status = smb2_qif_position(sr, qi);
224 	if (status)
225 		return (status);
226 	status = smb2_qif_mode(sr, qi);
227 	if (status)
228 		return (status);
229 	status = smb2_qif_alignment(sr, qi);
230 	if (status)
231 		return (status);
232 
233 	/*
234 	 * MS-SMB2 3.3.5.20.1 says (in a windows behavior note) that
235 	 * 2012R2 and older fill in the FileNameInformation.
236 	 * We could let this depend on sr->sr_cfg->skc_version
237 	 * but doing it based on dialect is a lot easier and
238 	 * has nearly the same effect.
239 	 */
240 	if (sr->session->dialect < SMB_VERS_3_11) {
241 		/* Win2012r2 and earlier fill it in. (SMB 3.0) */
242 		status = smb2_qif_name(sr, qi);
243 	} else {
244 		/* Win2016 and later just put zeros (SMB 3.11) */
245 		int rc = smb_mbc_encodef(&sr->raw_data, "10.");
246 		status = (rc == 0) ? 0 : NT_STATUS_BUFFER_OVERFLOW;
247 	}
248 
249 	return (status);
250 }
251 
252 /*
253  * FileBasicInformation
254  * See also:
255  *	case SMB_QUERY_FILE_BASIC_INFO:
256  *	case SMB_FILE_BASIC_INFORMATION:
257  */
258 static uint32_t
259 smb2_qif_basic(smb_request_t *sr, smb_queryinfo_t *qi)
260 {
261 	smb_attr_t *sa = &qi->qi_attr;
262 	int rc;
263 
264 	ASSERT((sa->sa_mask & SMB_AT_BASIC) == SMB_AT_BASIC);
265 
266 	rc = smb_mbc_encodef(
267 	    &sr->raw_data, "TTTTll",
268 	    &sa->sa_crtime,		/* T */
269 	    &sa->sa_vattr.va_atime,	/* T */
270 	    &sa->sa_vattr.va_mtime,	/* T */
271 	    &sa->sa_vattr.va_ctime,	/* T */
272 	    sa->sa_dosattr,		/* l */
273 	    0); /* reserved */		/* l */
274 	if (rc != 0)
275 		return (NT_STATUS_BUFFER_OVERFLOW);
276 
277 	return (0);
278 }
279 
280 /*
281  * FileStandardInformation
282  * See also:
283  *	SMB_QUERY_FILE_STANDARD_INFO
284  *	SMB_FILE_STANDARD_INFORMATION
285  */
286 static uint32_t
287 smb2_qif_standard(smb_request_t *sr, smb_queryinfo_t *qi)
288 {
289 	smb_attr_t *sa = &qi->qi_attr;
290 	int rc;
291 
292 	ASSERT((sa->sa_mask & SMB_AT_STANDARD) == SMB_AT_STANDARD);
293 
294 	rc = smb_mbc_encodef(
295 	    &sr->raw_data, "qqlbbw",
296 	    sa->sa_allocsz,		/* q */
297 	    sa->sa_vattr.va_size,	/* q */
298 	    sa->sa_vattr.va_nlink,	/* l */
299 	    qi->qi_delete_on_close,	/* b */
300 	    qi->qi_isdir,		/* b */
301 	    0); /* reserved */		/* w */
302 	if (rc != 0)
303 		return (NT_STATUS_BUFFER_OVERFLOW);
304 
305 	return (0);
306 }
307 
308 /*
309  * FileInternalInformation
310  * See also:
311  *	SMB_FILE_INTERNAL_INFORMATION
312  */
313 static uint32_t
314 smb2_qif_internal(smb_request_t *sr, smb_queryinfo_t *qi)
315 {
316 	smb_attr_t *sa = &qi->qi_attr;
317 	u_longlong_t nodeid;
318 	int rc;
319 
320 	ASSERT((sa->sa_mask & SMB_AT_NODEID) == SMB_AT_NODEID);
321 	nodeid = sa->sa_vattr.va_nodeid;
322 
323 	if (smb2_aapl_use_file_ids == 0 &&
324 	    (sr->session->s_flags & SMB_SSN_AAPL_CCEXT) != 0)
325 		nodeid = 0;
326 
327 	rc = smb_mbc_encodef(
328 	    &sr->raw_data, "q",
329 	    nodeid);	/* q */
330 	if (rc != 0)
331 		return (NT_STATUS_BUFFER_OVERFLOW);
332 
333 	return (0);
334 }
335 
336 /*
337  * FileEaInformation
338  * See also:
339  *	SMB_QUERY_FILE_EA_INFO
340  *	SMB_FILE_EA_INFORMATION
341  */
342 static uint32_t
343 smb2_qif_ea_size(smb_request_t *sr, smb_queryinfo_t *qi)
344 {
345 	_NOTE(ARGUNUSED(qi))
346 	int rc;
347 
348 	rc = smb_mbc_encodef(
349 	    &sr->raw_data, "l", 0);
350 	if (rc != 0)
351 		return (NT_STATUS_BUFFER_OVERFLOW);
352 
353 	return (0);
354 }
355 
356 /*
357  * FileFullEaInformation
358  * We could put EAs in a named stream...
359  */
360 /* ARGSUSED */
361 static uint32_t
362 smb2_qif_full_ea(smb_request_t *sr, smb_queryinfo_t *qi)
363 {
364 	return (NT_STATUS_NO_EAS_ON_FILE);
365 }
366 
367 /*
368  * FileAccessInformation
369  */
370 static uint32_t
371 smb2_qif_access(smb_request_t *sr, smb_queryinfo_t *qi)
372 {
373 	_NOTE(ARGUNUSED(qi))
374 	smb_ofile_t *of = sr->fid_ofile;
375 	int rc;
376 
377 	rc = smb_mbc_encodef(
378 	    &sr->raw_data, "l",
379 	    of->f_granted_access);
380 	if (rc != 0)
381 		return (NT_STATUS_BUFFER_OVERFLOW);
382 
383 	return (0);
384 }
385 
386 /*
387  * FileNameInformation
388  * See also:
389  *	SMB_QUERY_FILE_NAME_INFO
390  *	SMB_FILE_NAME_INFORMATION
391  */
392 static uint32_t
393 smb2_qif_name(smb_request_t *sr, smb_queryinfo_t *qi)
394 {
395 	int rc;
396 
397 	ASSERT(qi->qi_namelen > 0);
398 
399 	rc = smb_mbc_encodef(
400 	    &sr->raw_data, "llU",
401 	    0, /* FileIndex	 (l) */
402 	    qi->qi_namelen,	/* l */
403 	    qi->qi_name);	/* U */
404 	if (rc != 0)
405 		return (NT_STATUS_BUFFER_OVERFLOW);
406 
407 	return (0);
408 }
409 
410 /*
411  * FilePositionInformation
412  */
413 static uint32_t
414 smb2_qif_position(smb_request_t *sr, smb_queryinfo_t *qi)
415 {
416 	_NOTE(ARGUNUSED(qi))
417 	smb_ofile_t *of = sr->fid_ofile;
418 	uint64_t pos;
419 	int rc;
420 
421 	mutex_enter(&of->f_mutex);
422 	pos = of->f_seek_pos;
423 	mutex_exit(&of->f_mutex);
424 
425 	rc = smb_mbc_encodef(
426 	    &sr->raw_data, "q", pos);
427 	if (rc != 0)
428 		return (NT_STATUS_BUFFER_OVERFLOW);
429 
430 	return (0);
431 }
432 
433 /*
434  * FileModeInformation [MS-FSA 2.4.24]
435  * XXX: These mode flags are supposed to be on the open handle,
436  * XXX: or I think so.  Not yet...  (just put zero for now)
437  */
438 static uint32_t
439 smb2_qif_mode(smb_request_t *sr, smb_queryinfo_t *qi)
440 {
441 	_NOTE(ARGUNUSED(qi))
442 	int rc;
443 
444 	rc = smb_mbc_encodef(
445 	    &sr->raw_data, "l", 0);
446 	if (rc != 0)
447 		return (NT_STATUS_BUFFER_OVERFLOW);
448 
449 	return (0);
450 }
451 
452 /*
453  * FileAlignmentInformation
454  */
455 static uint32_t
456 smb2_qif_alignment(smb_request_t *sr, smb_queryinfo_t *qi)
457 {
458 	_NOTE(ARGUNUSED(qi))
459 	int rc;
460 
461 	rc = smb_mbc_encodef(
462 	    &sr->raw_data, "l", 0);
463 	if (rc != 0)
464 		return (NT_STATUS_BUFFER_OVERFLOW);
465 
466 	return (0);
467 }
468 
469 /*
470  * FileAlternateNameInformation
471  * See also:
472  *	SMB_QUERY_FILE_ALT_NAME_INFO
473  *	SMB_FILE_ALT_NAME_INFORMATION
474  */
475 static uint32_t
476 smb2_qif_altname(smb_request_t *sr, smb_queryinfo_t *qi)
477 {
478 	smb_ofile_t *of = sr->fid_ofile;
479 	int rc;
480 
481 	ASSERT(qi->qi_namelen > 0);
482 	ASSERT(qi->qi_attr.sa_mask & SMB_AT_NODEID);
483 
484 	if (of->f_ftype != SMB_FTYPE_DISK)
485 		return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
486 	if ((of->f_tree->t_flags & SMB_TREE_SHORTNAMES) == 0)
487 		return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
488 
489 	/* fill in qi->qi_shortname */
490 	smb_query_shortname(of->f_node, qi);
491 
492 	rc = smb_mbc_encodef(
493 	    &sr->raw_data, "%lU", sr,
494 	    smb_wcequiv_strlen(qi->qi_shortname),
495 	    qi->qi_shortname);
496 	if (rc != 0)
497 		return (NT_STATUS_BUFFER_OVERFLOW);
498 
499 	return (0);
500 }
501 
502 /*
503  * FileStreamInformation
504  */
505 static uint32_t
506 smb2_qif_stream(smb_request_t *sr, smb_queryinfo_t *qi)
507 {
508 	smb_ofile_t *of = sr->fid_ofile;
509 	smb_attr_t *attr = &qi->qi_attr;
510 	uint32_t status;
511 
512 	ASSERT((attr->sa_mask & SMB_AT_STANDARD) == SMB_AT_STANDARD);
513 	if (of->f_ftype != SMB_FTYPE_DISK) {
514 		(void) smb_mbc_encodef(
515 		    &sr->raw_data, "l", 0);
516 		return (0);
517 	}
518 
519 	status = smb_query_stream_info(sr, &sr->raw_data, qi);
520 	return (status);
521 }
522 
523 /*
524  * FilePipeInformation
525  */
526 static uint32_t
527 smb2_qif_pipe(smb_request_t *sr, smb_queryinfo_t *qi)
528 {
529 	_NOTE(ARGUNUSED(qi))
530 	smb_ofile_t *of = sr->fid_ofile;
531 	uint32_t	pipe_mode;
532 	uint32_t	nonblock;
533 	int		rc;
534 
535 	switch (of->f_ftype) {
536 	case SMB_FTYPE_BYTE_PIPE:
537 		pipe_mode = 0;	/* FILE_PIPE_BYTE_STREAM_MODE */
538 		break;
539 	case SMB_FTYPE_MESG_PIPE:
540 		pipe_mode = 1;	/* FILE_PIPE_MESSAGE_MODE */
541 		break;
542 	case SMB_FTYPE_DISK:
543 	case SMB_FTYPE_PRINTER:
544 	default:
545 		return (NT_STATUS_INVALID_PARAMETER);
546 	}
547 	nonblock = 0;	/* XXX todo: Get this from the pipe handle. */
548 
549 	rc = smb_mbc_encodef(
550 	    &sr->raw_data, "ll",
551 	    pipe_mode, nonblock);
552 	if (rc != 0)
553 		return (NT_STATUS_BUFFER_OVERFLOW);
554 
555 	return (0);
556 }
557 
558 /*
559  * FilePipeLocalInformation
560  */
561 /* ARGSUSED */
562 static uint32_t
563 smb2_qif_pipe_lcl(smb_request_t *sr, smb_queryinfo_t *qi)
564 {
565 	return (NT_STATUS_INVALID_PARAMETER); /* XXX todo */
566 }
567 
568 /*
569  * FilePipeRemoteInformation
570  */
571 /* ARGSUSED */
572 static uint32_t
573 smb2_qif_pipe_rem(smb_request_t *sr, smb_queryinfo_t *qi)
574 {
575 	return (NT_STATUS_INVALID_PARAMETER); /* XXX todo */
576 }
577 
578 /*
579  * FileCompressionInformation
580  * XXX: For now, just say "not compressed".
581  */
582 static uint32_t
583 smb2_qif_compr(smb_request_t *sr, smb_queryinfo_t *qi)
584 {
585 	smb_attr_t *sa = &qi->qi_attr;
586 	uint16_t CompressionFormat = 0;	/* COMPRESSION_FORMAT_NONE */
587 	int rc;
588 
589 	ASSERT(sa->sa_mask & SMB_AT_SIZE);
590 
591 	rc = smb_mbc_encodef(
592 	    &sr->raw_data, "qw6.",
593 	    sa->sa_vattr.va_size,	/* q */
594 	    CompressionFormat);		/* w */
595 	if (rc != 0)
596 		return (NT_STATUS_BUFFER_OVERFLOW);
597 
598 	return (0);
599 }
600 
601 /*
602  * FileNetworkOpenInformation
603  */
604 static uint32_t
605 smb2_qif_opens(smb_request_t *sr, smb_queryinfo_t *qi)
606 {
607 	smb_attr_t *sa = &qi->qi_attr;
608 	int rc;
609 
610 	rc = smb_mbc_encodef(
611 	    &sr->raw_data, "TTTTqqll",
612 	    &sa->sa_crtime,		/* T */
613 	    &sa->sa_vattr.va_atime,	/* T */
614 	    &sa->sa_vattr.va_mtime,	/* T */
615 	    &sa->sa_vattr.va_ctime,	/* T */
616 	    sa->sa_allocsz,		/* q */
617 	    sa->sa_vattr.va_size,	/* q */
618 	    sa->sa_dosattr,		/* l */
619 	    0); /* reserved */		/* l */
620 	if (rc != 0)
621 		return (NT_STATUS_BUFFER_OVERFLOW);
622 
623 	return (0);
624 }
625 
626 /*
627  * FileAttributeTagInformation
628  *
629  * If dattr includes FILE_ATTRIBUTE_REPARSE_POINT, the
630  * second dword should be the reparse tag.  Otherwise
631  * the tag value should be set to zero.
632  * We don't support reparse points, so we set the tag
633  * to zero.
634  */
635 static uint32_t
636 smb2_qif_tags(smb_request_t *sr, smb_queryinfo_t *qi)
637 {
638 	_NOTE(ARGUNUSED(qi))
639 	int rc;
640 
641 	rc = smb_mbc_encodef(
642 	    &sr->raw_data, "ll", 0, 0);
643 	if (rc != 0)
644 		return (NT_STATUS_BUFFER_OVERFLOW);
645 
646 	return (0);
647 }
648