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