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