xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_query_dir.c (revision dcd633c7016ed64471a83aee2275fe5d60b48cb3)
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 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
26  * Copyright 2022 RackTop Systems, Inc.
27  */
28 
29 /*
30  * Dispatch function for SMB2_QUERY_DIRECTORY
31  *
32  * Similar to smb_trans2_find.c (from SMB1)
33  */
34 
35 #include <smbsrv/smb2_kproto.h>
36 #include <smbsrv/smb2_aapl.h>
37 
38 /*
39  * Internally defined info. level for MacOS support.
40  * Make sure this does not conflict with real values in
41  * FILE_INFORMATION_CLASS, and that it fits in 8-bits.
42  */
43 #define	FileIdMacOsDirectoryInformation (FileMaximumInformation + 10)
44 
45 /*
46  * Args (and other state) that we carry around among the
47  * various functions involved in SMB2 Query Directory.
48  */
49 typedef struct smb2_find_args {
50 	uint32_t fa_maxdata;
51 	uint8_t fa_infoclass;
52 	uint8_t fa_fflags;
53 	uint16_t fa_maxcount;
54 	uint16_t fa_eos;	/* End Of Search */
55 	uint16_t fa_fixedsize;	/* size of fixed part of a returned entry */
56 	uint32_t fa_lastkey;	/* Last resume key */
57 	int fa_last_entry;	/* offset of last entry */
58 
59 	/* Normal info, per dir. entry */
60 	smb_fileinfo_t fa_fi;
61 
62 	/* MacOS AAPL extension stuff. */
63 	smb_macinfo_t fa_mi;
64 } smb2_find_args_t;
65 
66 static uint32_t smb2_find_entries(smb_request_t *,
67     smb_odir_t *, smb2_find_args_t *);
68 static uint32_t smb2_find_mbc_encode(smb_request_t *, smb2_find_args_t *);
69 
70 /*
71  * Tunable parameter to limit the maximum
72  * number of entries to be returned.
73  */
74 uint16_t smb2_find_max = 1024;
75 
76 smb_sdrc_t
77 smb2_query_dir(smb_request_t *sr)
78 {
79 	smb2_find_args_t args;
80 	smb_odir_resume_t odir_resume;
81 	smb_ofile_t *of = NULL;
82 	smb_odir_t *od = NULL;
83 	char *pattern = NULL;
84 	uint16_t StructSize;
85 	uint32_t FileIndex;
86 	uint16_t NameOffset;
87 	uint16_t NameLength;
88 	smb2fid_t smb2fid;
89 	uint16_t sattr = SMB_SEARCH_ATTRIBUTES;
90 	uint16_t DataOff;
91 	uint32_t DataLen;
92 	uint32_t status;
93 	int skip, rc = 0;
94 
95 	bzero(&args, sizeof (args));
96 	bzero(&odir_resume, sizeof (odir_resume));
97 
98 	/*
99 	 * SMB2 Query Directory request
100 	 */
101 	rc = smb_mbc_decodef(
102 	    &sr->smb_data, "wbblqqwwl",
103 	    &StructSize,		/* w */
104 	    &args.fa_infoclass,		/* b */
105 	    &args.fa_fflags,		/* b */
106 	    &FileIndex,			/* l */
107 	    &smb2fid.persistent,	/* q */
108 	    &smb2fid.temporal,		/* q */
109 	    &NameOffset,		/* w */
110 	    &NameLength,		/* w */
111 	    &args.fa_maxdata);		/* l */
112 	if (rc || StructSize != 33)
113 		return (SDRC_ERROR);
114 
115 	status = smb2sr_lookup_fid(sr, &smb2fid);
116 	of = sr->fid_ofile;
117 
118 	DTRACE_SMB2_START(op__QueryDirectory, smb_request_t *, sr);
119 
120 	if (status)
121 		goto errout;
122 
123 	/*
124 	 * If there's an input buffer (search pattern), decode it.
125 	 * Two times MAXNAMELEN because it represents the UNICODE string
126 	 * length in bytes.
127 	 */
128 	if (NameLength >= (2 * MAXNAMELEN)) {
129 		status = NT_STATUS_OBJECT_PATH_INVALID;
130 		goto errout;
131 	}
132 	if (NameLength != 0) {
133 		/*
134 		 * We're normally positioned at the pattern now,
135 		 * but there could be some padding before it.
136 		 */
137 		skip = (sr->smb2_cmd_hdr + NameOffset) -
138 		    sr->smb_data.chain_offset;
139 		if (skip < 0) {
140 			status = NT_STATUS_OBJECT_PATH_INVALID;
141 			goto errout;
142 		}
143 		if (skip > 0)
144 			(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
145 		rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr,
146 		    NameLength, &pattern);
147 		if (rc || pattern == NULL) {
148 			status = NT_STATUS_OBJECT_PATH_INVALID;
149 			goto errout;
150 		}
151 	} else
152 		pattern = "*";
153 
154 	/*
155 	 * Setup the output buffer.
156 	 */
157 	if (args.fa_maxdata > smb2_max_trans)
158 		args.fa_maxdata = smb2_max_trans;
159 	sr->raw_data.max_bytes = args.fa_maxdata;
160 
161 	/*
162 	 * Get the fixed size of entries we will return, which
163 	 * lets us estimate the number of entries we'll need.
164 	 *
165 	 * Also use this opportunity to validate fa_infoclass.
166 	 */
167 
168 	switch (args.fa_infoclass) {
169 	case FileDirectoryInformation:		/* 1 */
170 		args.fa_fixedsize = 64;
171 		break;
172 	case FileFullDirectoryInformation:	/* 2 */
173 		args.fa_fixedsize = 68;
174 		break;
175 	case FileBothDirectoryInformation:	/* 3 */
176 		args.fa_fixedsize = 94;
177 		break;
178 	case FileNamesInformation:		/* 12 */
179 		args.fa_fixedsize = 12;
180 		break;
181 	case FileIdBothDirectoryInformation:	/* 37 */
182 		args.fa_fixedsize = 96;
183 		break;
184 	case FileIdFullDirectoryInformation:	/* 38 */
185 		args.fa_fixedsize = 84;
186 		break;
187 	default:
188 		status = NT_STATUS_INVALID_INFO_CLASS;
189 		goto errout;
190 	}
191 
192 	/*
193 	 * MacOS, when using the AAPL CreateContext extensions
194 	 * and the "read dir attr" feature, uses a non-standard
195 	 * information format for directory entries.  Internally
196 	 * we'll use a fake info level to represent this case.
197 	 * (Wish they had just defined a new info level.)
198 	 */
199 	if ((sr->session->s_flags & SMB_SSN_AAPL_READDIR) != 0 &&
200 	    args.fa_infoclass == FileIdBothDirectoryInformation) {
201 		args.fa_infoclass = FileIdMacOsDirectoryInformation;
202 		args.fa_fixedsize = 96; /* yes, same size */
203 	}
204 
205 	args.fa_maxcount = args.fa_maxdata / (args.fa_fixedsize + 4);
206 	if (args.fa_maxcount == 0)
207 		args.fa_maxcount = 1;
208 	if ((smb2_find_max != 0) && (args.fa_maxcount > smb2_find_max))
209 		args.fa_maxcount = smb2_find_max;
210 	if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE)
211 		args.fa_maxcount = 1;
212 
213 	/*
214 	 * If this ofile does not have an odir yet, get one.
215 	 */
216 	mutex_enter(&of->f_mutex);
217 	if ((od = of->f_odir) == NULL) {
218 		status = smb_odir_openfh(sr, pattern, sattr, &od);
219 		of->f_odir = od;
220 	}
221 	mutex_exit(&of->f_mutex);
222 	if (od == NULL) {
223 		if (status == 0)
224 			status = NT_STATUS_INTERNAL_ERROR;
225 		goto errout;
226 	}
227 
228 	/*
229 	 * "Reopen" sets a new pattern and restart.
230 	 */
231 	if (args.fa_fflags & SMB2_QDIR_FLAG_REOPEN) {
232 		smb_odir_reopen(od, pattern, sattr);
233 	}
234 
235 	/*
236 	 * Set the correct position in the directory.
237 	 */
238 	if (args.fa_fflags & SMB2_QDIR_FLAG_RESTART) {
239 		odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
240 		odir_resume.or_cookie = 0;
241 	} else if (args.fa_fflags & SMB2_QDIR_FLAG_INDEX) {
242 		odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
243 		odir_resume.or_cookie = FileIndex;
244 	} else {
245 		odir_resume.or_type = SMB_ODIR_RESUME_CONT;
246 	}
247 	smb_odir_resume_at(od, &odir_resume);
248 	of->f_seek_pos = od->d_offset;
249 
250 	/*
251 	 * The real work of readdir and format conversion.
252 	 */
253 	status = smb2_find_entries(sr, od, &args);
254 
255 	of->f_seek_pos = od->d_offset;
256 
257 	if ((args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) &&
258 	    status == NT_STATUS_NO_MORE_FILES) {
259 		status = NT_STATUS_NO_SUCH_FILE;
260 	}
261 
262 errout:
263 	sr->smb2_status = status;
264 	DTRACE_SMB2_DONE(op__QueryDirectory, smb_request_t *, sr);
265 
266 	/*
267 	 * Note: NT_STATUS_NO_MORE_FILES is a warning
268 	 * used to tell the client that this data return
269 	 * is the last of the enumeration.  Returning this
270 	 * warning now (with the data) saves the client a
271 	 * round trip that would otherwise be needed to
272 	 * find out it's at the end.
273 	 */
274 	if (status != 0 &&
275 	    status != NT_STATUS_NO_MORE_FILES) {
276 		smb2sr_put_error(sr, status);
277 		return (SDRC_SUCCESS);
278 	}
279 
280 	/*
281 	 * SMB2 Query Directory reply
282 	 */
283 	StructSize = 9;
284 	DataOff = SMB2_HDR_SIZE + 8;
285 	DataLen = MBC_LENGTH(&sr->raw_data);
286 	rc = smb_mbc_encodef(
287 	    &sr->reply, "wwlC",
288 	    StructSize,		/* w */
289 	    DataOff,		/* w */
290 	    DataLen,		/* l */
291 	    &sr->raw_data);	/* C */
292 	if (DataLen == 0)
293 		(void) smb_mbc_encodef(&sr->reply, ".");
294 
295 	if (rc)
296 		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
297 
298 	return (SDRC_SUCCESS);
299 }
300 
301 /*
302  * smb2_find_entries
303  *
304  * Find and encode up to args->fa_maxcount directory entries.
305  *
306  * Returns:
307  *   NT status
308  */
309 static uint32_t
310 smb2_find_entries(smb_request_t *sr, smb_odir_t *od, smb2_find_args_t *args)
311 {
312 	smb_odir_resume_t odir_resume;
313 	char		*tbuf = NULL;
314 	size_t		tbuflen = 0;
315 	uint16_t	count;
316 	uint16_t	minsize;
317 	uint32_t	status = 0;
318 	int		rc = -1;
319 
320 	/*
321 	 * Let's stop when the remaining space will not hold a
322 	 * minimum size entry.  That's the fixed part plus the
323 	 * storage size of a 1 char unicode string.
324 	 */
325 	minsize = args->fa_fixedsize + 2;
326 
327 	/*
328 	 * FileIdMacOsDirectoryInformation needs some buffer space
329 	 * for composing directory entry + stream name for lookup.
330 	 * Get the buffer now to avoid alloc/free per entry.
331 	 */
332 	if (args->fa_infoclass == FileIdMacOsDirectoryInformation) {
333 		tbuflen = 2 * MAXNAMELEN;
334 		tbuf = kmem_alloc(tbuflen, KM_SLEEP);
335 	}
336 
337 	count = 0;
338 	while (count < args->fa_maxcount) {
339 
340 		if (!MBC_ROOM_FOR(&sr->raw_data, minsize)) {
341 			status = NT_STATUS_BUFFER_OVERFLOW;
342 			break;
343 		}
344 
345 		rc = smb_odir_read_fileinfo(sr, od,
346 		    &args->fa_fi, &args->fa_eos);
347 		if (rc == ENOENT) {
348 			status = NT_STATUS_NO_MORE_FILES;
349 			break;
350 		}
351 		if (rc != 0) {
352 			status = smb_errno2status(rc);
353 			break;
354 		}
355 		if (args->fa_eos != 0) {
356 			/* The readdir call hit the end. */
357 			status = NT_STATUS_NO_MORE_FILES;
358 			break;
359 		}
360 
361 		if (args->fa_infoclass == FileIdMacOsDirectoryInformation)
362 			(void) smb2_aapl_get_macinfo(sr, od,
363 			    &args->fa_fi, &args->fa_mi, tbuf, tbuflen);
364 
365 		if (smb2_aapl_use_file_ids == 0 &&
366 		    (sr->session->s_flags & SMB_SSN_AAPL_CCEXT) != 0)
367 			args->fa_fi.fi_nodeid = 0;
368 
369 		status = smb2_find_mbc_encode(sr, args);
370 		if (status) {
371 			/*
372 			 * We read a directory entry but failed to
373 			 * copy it into the output buffer.  Rewind
374 			 * the directory pointer so this will be
375 			 * the first entry read next time.
376 			 */
377 			bzero(&odir_resume, sizeof (odir_resume));
378 			odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
379 			odir_resume.or_cookie = args->fa_lastkey;
380 			smb_odir_resume_at(od, &odir_resume);
381 			break;
382 		}
383 
384 		/*
385 		 * Save the offset of the next entry we'll read.
386 		 * If we fail copying, we'll need this offset.
387 		 */
388 		args->fa_lastkey = args->fa_fi.fi_cookie;
389 		++count;
390 	}
391 
392 	if (count == 0) {
393 		ASSERT(status != 0);
394 	} else {
395 		/*
396 		 * We copied some directory entries, but stopped for
397 		 * NT_STATUS_NO_MORE_FILES, or something.
398 		 *
399 		 * Per [MS-FSCC] sec. 2.4, the last entry in the
400 		 * enumeration MUST have its NextEntryOffset value
401 		 * set to zero.  Overwrite that in the last entry.
402 		 */
403 		(void) smb_mbc_poke(&sr->raw_data,
404 		    args->fa_last_entry, "l", 0);
405 		status = 0;
406 	}
407 
408 	if (tbuf != NULL)
409 		kmem_free(tbuf, tbuflen);
410 
411 	return (status);
412 }
413 
414 /*
415  * smb2_mbc_encode
416  *
417  * This function encodes the mbc for one directory entry.
418  *
419  * The function returns -1 when the max data requested by client
420  * is reached. If the entry is valid and successful encoded, 0
421  * will be returned; otherwise, 1 will be returned.
422  *
423  * We always null terminate the filename. The space for the null
424  * is included in the maxdata calculation and is therefore included
425  * in the next_entry_offset. namelen is the unterminated length of
426  * the filename. For levels except STANDARD and EA_SIZE, if the
427  * filename is ascii the name length returned to the client should
428  * include the null terminator. Otherwise the length returned to
429  * the client should not include the terminator.
430  *
431  * Returns: 0 - data successfully encoded
432  *      NT status
433  */
434 static uint32_t
435 smb2_find_mbc_encode(smb_request_t *sr, smb2_find_args_t *args)
436 {
437 	smb_fileinfo_t	*fileinfo = &args->fa_fi;
438 	smb_macinfo_t	*macinfo = &args->fa_mi;
439 	uint8_t		buf83[26];
440 	smb_msgbuf_t	mb;
441 	int		namelen;
442 	int		shortlen = 0;
443 	int		rc, starting_offset;
444 	uint32_t	next_entry_offset;
445 	uint32_t	mb_flags = SMB_MSGBUF_UNICODE | SMB_MSGBUF_NOTERM;
446 	uint32_t	resume_key;
447 
448 	namelen = smb_wcequiv_strlen(fileinfo->fi_name);
449 	if (namelen == -1)
450 		return (NT_STATUS_INTERNAL_ERROR);
451 
452 	/*
453 	 * Keep track of where the last entry starts so we can
454 	 * come back and poke the NextEntryOffset field.  Also,
455 	 * after enumeration finishes, the caller uses this to
456 	 * poke the last entry again with zero to mark it as
457 	 * the end of the enumeration.
458 	 */
459 	starting_offset = sr->raw_data.chain_offset;
460 
461 	/*
462 	 * Technically (per MS-SMB2) resume keys are optional.
463 	 * Windows doesn't need them, but MacOS does.
464 	 */
465 	resume_key = fileinfo->fi_cookie;
466 
467 	/*
468 	 * This switch handles all the "information levels" (formats)
469 	 * that we support.  Note that all formats have the file name
470 	 * placed after some fixed-size data, and the code to write
471 	 * the file name is factored out at the end of this switch.
472 	 */
473 	switch (args->fa_infoclass) {
474 
475 	/* See also: SMB_FIND_FILE_DIRECTORY_INFO */
476 	case FileDirectoryInformation:		/* 1 */
477 		rc = smb_mbc_encodef(
478 		    &sr->raw_data, "llTTTTqqll",
479 		    0,	/* NextEntryOffset (set later) */
480 		    resume_key,
481 		    &fileinfo->fi_crtime,
482 		    &fileinfo->fi_atime,
483 		    &fileinfo->fi_mtime,
484 		    &fileinfo->fi_ctime,
485 		    fileinfo->fi_size,
486 		    fileinfo->fi_alloc_size,
487 		    fileinfo->fi_dosattr,
488 		    namelen);
489 		break;
490 
491 	/* See also: SMB_FIND_FILE_FULL_DIRECTORY_INFO */
492 	case FileFullDirectoryInformation:	/* 2 */
493 		rc = smb_mbc_encodef(
494 		    &sr->raw_data, "llTTTTqqlll",
495 		    0,	/* NextEntryOffset (set later) */
496 		    resume_key,
497 		    &fileinfo->fi_crtime,
498 		    &fileinfo->fi_atime,
499 		    &fileinfo->fi_mtime,
500 		    &fileinfo->fi_ctime,
501 		    fileinfo->fi_size,
502 		    fileinfo->fi_alloc_size,
503 		    fileinfo->fi_dosattr,
504 		    namelen,
505 		    0L);	/* EaSize */
506 		break;
507 
508 	/* See also: SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO */
509 	case FileIdFullDirectoryInformation:	/* 38 */
510 		rc = smb_mbc_encodef(
511 		    &sr->raw_data, "llTTTTqqllllq",
512 		    0,	/* NextEntryOffset (set later) */
513 		    resume_key,
514 		    &fileinfo->fi_crtime,
515 		    &fileinfo->fi_atime,
516 		    &fileinfo->fi_mtime,
517 		    &fileinfo->fi_ctime,
518 		    fileinfo->fi_size,
519 		    fileinfo->fi_alloc_size,
520 		    fileinfo->fi_dosattr,
521 		    namelen,
522 		    0L,		/* EaSize */
523 		    0L,		/* reserved */
524 		    fileinfo->fi_nodeid);
525 		break;
526 
527 	/* See also: SMB_FIND_FILE_BOTH_DIRECTORY_INFO */
528 	case FileBothDirectoryInformation:	/* 3 */
529 		bzero(buf83, sizeof (buf83));
530 		smb_msgbuf_init(&mb, buf83, sizeof (buf83), mb_flags);
531 		shortlen = smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname);
532 		if (shortlen < 0) {
533 			shortlen = 0;
534 			bzero(buf83, sizeof (buf83));
535 		}
536 
537 		rc = smb_mbc_encodef(
538 		    &sr->raw_data, "llTTTTqqlllb.24c",
539 		    0,	/* NextEntryOffset (set later) */
540 		    resume_key,
541 		    &fileinfo->fi_crtime,
542 		    &fileinfo->fi_atime,
543 		    &fileinfo->fi_mtime,
544 		    &fileinfo->fi_ctime,
545 		    fileinfo->fi_size,
546 		    fileinfo->fi_alloc_size,
547 		    fileinfo->fi_dosattr,
548 		    namelen,
549 		    0L,		/* EaSize */
550 		    shortlen,
551 		    buf83);
552 
553 		smb_msgbuf_term(&mb);
554 		break;
555 
556 	/* See also: SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO */
557 	case FileIdBothDirectoryInformation:	/* 37 */
558 		bzero(buf83, sizeof (buf83));
559 		smb_msgbuf_init(&mb, buf83, sizeof (buf83), mb_flags);
560 		shortlen = smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname);
561 		if (shortlen < 0) {
562 			shortlen = 0;
563 			bzero(buf83, sizeof (buf83));
564 		}
565 
566 		rc = smb_mbc_encodef(
567 		    &sr->raw_data, "llTTTTqqlllb.24c..q",
568 		    0,	/* NextEntryOffset (set later) */
569 		    resume_key,
570 		    &fileinfo->fi_crtime,
571 		    &fileinfo->fi_atime,
572 		    &fileinfo->fi_mtime,
573 		    &fileinfo->fi_ctime,
574 		    fileinfo->fi_size,		/* q */
575 		    fileinfo->fi_alloc_size,	/* q */
576 		    fileinfo->fi_dosattr,	/* l */
577 		    namelen,			/* l */
578 		    0L,		/* EaSize	   l */
579 		    shortlen,			/* b. */
580 		    buf83,			/* 24c */
581 		    /* reserved			   .. */
582 		    fileinfo->fi_nodeid);	/* q */
583 
584 		smb_msgbuf_term(&mb);
585 		break;
586 
587 	/*
588 	 * MacOS, when using the AAPL extensions (see smb2_create)
589 	 * uses modified directory listing responses where the
590 	 * "EA size" field is replaced with "maximum access".
591 	 * This avoids the need for MacOS Finder to come back
592 	 * N times to get the maximum access for every file.
593 	 */
594 	case FileIdMacOsDirectoryInformation:
595 		rc = smb_mbc_encodef(
596 		    &sr->raw_data, "llTTTTqqll",
597 		    0,	/* NextEntryOffset (set later) */
598 		    resume_key,		/* a.k.a. file index */
599 		    &fileinfo->fi_crtime,
600 		    &fileinfo->fi_atime,
601 		    &fileinfo->fi_mtime,
602 		    &fileinfo->fi_ctime,
603 		    fileinfo->fi_size,		/* q */
604 		    fileinfo->fi_alloc_size,	/* q */
605 		    fileinfo->fi_dosattr,	/* l */
606 		    namelen);			/* l */
607 		if (rc != 0)
608 			break;
609 		/*
610 		 * This where FileIdMacOsDirectoryInformation
611 		 * differs from FileIdBothDirectoryInformation
612 		 * Instead of: EaSize, ShortNameLen, ShortName;
613 		 * MacOS wants: MaxAccess, ResourceForkSize, and
614 		 * 16 bytes of "compressed finder info".
615 		 * mi_rforksize + mi_finderinfo falls where
616 		 * the 24 byte shortname would normally be.
617 		 */
618 		rc = smb_mbc_encodef(
619 		    &sr->raw_data, "l..q16cwq",
620 		    macinfo->mi_maxaccess,	/* l */
621 		    /* short_name_len, reserved  (..) */
622 		    macinfo->mi_rforksize,	/* q */
623 		    macinfo->mi_finderinfo,	/* 16c */
624 		    macinfo->mi_unixmode,	/* w */
625 		    fileinfo->fi_nodeid);	/* q */
626 		break;
627 
628 	/* See also: SMB_FIND_FILE_NAMES_INFO */
629 	case FileNamesInformation:		/* 12 */
630 		rc = smb_mbc_encodef(
631 		    &sr->raw_data, "lll",
632 		    0,	/* NextEntryOffset (set later) */
633 		    resume_key,
634 		    namelen);
635 		break;
636 
637 	default:
638 		return (NT_STATUS_INVALID_INFO_CLASS);
639 	}
640 	if (rc)	/* smb_mbc_encodef failed */
641 		return (NT_STATUS_BUFFER_OVERFLOW);
642 
643 	/*
644 	 * At this point we have written all the fixed-size data
645 	 * for the specified info. class.  Now put the name and
646 	 * alignment padding, and then patch the NextEntryOffset.
647 	 * Also store this offset for the caller so they can
648 	 * patch this (again) to zero on the very last entry.
649 	 */
650 	rc = smb_mbc_encodef(
651 	    &sr->raw_data, "U",
652 	    fileinfo->fi_name);
653 	if (rc)
654 		return (NT_STATUS_BUFFER_OVERFLOW);
655 
656 	/* Next entry needs to be 8-byte aligned. */
657 	(void) smb_mbc_put_align(&sr->raw_data, 8);
658 
659 	next_entry_offset = sr->raw_data.chain_offset -	starting_offset;
660 	(void) smb_mbc_poke(&sr->raw_data, starting_offset, "l",
661 	    next_entry_offset);
662 	args->fa_last_entry = starting_offset;
663 
664 	return (0);
665 }
666