xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_nt_transact_quota.c (revision 45818ee124adeaaf947698996b4f4c722afc6d1f)
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 2011 Nexenta Systems, Inc.  All rights reserved.
24  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
25  */
26 
27 #include <smbsrv/smb_kproto.h>
28 #include <smbsrv/smb_fsops.h>
29 #include <smbsrv/smb_share.h>
30 #include <smbsrv/string.h>
31 #include <sys/fs/zfs.h>
32 #include <smbsrv/smb_xdr.h>
33 #include <smbsrv/smb_door.h>
34 #include <smbsrv/smb_idmap.h>
35 
36 /*
37  * A user/group quota entry passed over the wire consists of:
38  * - next offset (uint32_t)
39  * - length of SID (uint32_t)
40  * - last modified time (uint64_t)
41  * - quota used (uint64_t)
42  * - quota limit (uint64_t)
43  * - quota threahold (uint64_t)
44  * - variable length sid - max = 32 bytes
45  * SMB_QUOTA_SIZE_NO_SID is the size of the above, excluding the sid.
46  */
47 #define	SMB_QUOTA_SIZE_NO_SID \
48 	((2 * sizeof (uint32_t)) + (4 * sizeof (uint64_t)))
49 #define	SMB_QUOTA_EST_SIZE (SMB_QUOTA_SIZE_NO_SID + SMB_EST_SID_SIZE)
50 #define	SMB_QUOTA_MAX_SIZE (SMB_QUOTA_SIZE_NO_SID + SMB_MAX_SID_SIZE)
51 
52 static int smb_quota_query(smb_server_t *, smb_quota_query_t *,
53     smb_quota_response_t *);
54 static int smb_quota_set(smb_server_t *, smb_quota_set_t *, uint32_t *);
55 static uint32_t smb_quota_init_sids(smb_xa_t *, smb_quota_query_t *,
56     smb_ofile_t *);
57 static uint32_t smb_quota_decode_sids(smb_xa_t *, list_t *);
58 static void smb_quota_free_sids(smb_quota_query_t *);
59 static void smb_quota_max_quota(smb_xa_t *, smb_quota_query_t *);
60 static uint32_t smb_quota_decode_quotas(smb_xa_t *, list_t *);
61 static uint32_t smb_quota_encode_quotas(smb_xa_t *, smb_quota_query_t *,
62     smb_quota_response_t *, smb_ofile_t *);
63 static void smb_quota_free_quotas(list_t *);
64 
65 /*
66  * smb_nt_transact_query_quota
67  *
68  * This method allows the client to retrieve quota information from
69  * the server. The result of the call is returned to the client in the
70  * Data part of the transaction response.
71  *
72  * On entry, the 'TotalParameterCount' field must be equal to 16, and the
73  * client parameter block must be encoded with the following parameters:
74  *
75  * Request                    Description
76  * ========================== ==================================
77  * WORD fid                   SMB file identifier of the target directory
78  * BYTE ReturnSingleEntry     A boolean indicating whether to return
79  *                            a single entry (TRUE) or multiple entries (FALSE).
80  * BYTE RestartScan           A boolean indicating whether to continue from
81  *                            the previous request (FALSE) or restart a new
82  *                            sequence (TRUE).
83  * DWORD SidListLength        The length, in bytes, of the SidList in the
84  *                            data block or 0 if there is no SidList.
85  * DWORD StartSidLength       If SidListLength is 0 (i.e. there is no SidList
86  *                            in the data block), then this is either:
87  *                                 1) the (non-zero) length in bytes of the
88  *                                    StartSid in the parameter buffer, or
89  *                                 2) if 0, there is no StartSid in the
90  *                                    parameter buffer, in which case, all SIDs
91  *                                    are to be enumerated as if they were
92  *                                    passed in the SidList.
93  *                            Otherwise, StartSidLength is ignored.
94  * DWORD StartSidOffset       The offset, in bytes, to the StartSid in the
95  *                            parameter block (if one exists).
96  *
97  * One of SidListLength and StartSidLength must be 0.
98  *
99  * An SMB_COM_NT_TRANSACTION response is sent in reply when the request
100  * is successful.  The 'TotalParameterCount' is set to 4, and the parameter
101  * block in the server response contains a 32-bit unsigned integer
102  * indicating the length, in bytes, of the returned quota information.
103  * The 'TotalDataCount' is set to indicate the length of the data buffer,
104  * and the data buffer contains the following quota information:
105  *
106  *  Data Block Encoding                Description
107  *  ================================== =================================
108  *  ULONG NextEntryOffset;             Offset to start of next entry from
109  *                                     start of this entry, or 0 for the
110  *                                     final entry
111  *  ULONG SidLength;                   Length (bytes) of SID
112  *  SMB_TIME ChangeTime;               Time that the quota was last changed
113  *  LARGE_INTEGER QuotaUsed;           Amount of quota (bytes) used by user
114  *  LARGE_INTEGER QuotaThreshold;      Quota warning limit (bytes) for user
115  *  LARGE_INTEGER QuotaLimit;          The quota limit (bytes) for this user
116  *  USHORT Sid;                        Search handle
117  */
118 smb_sdrc_t
119 smb_nt_transact_query_quota(smb_request_t *sr, smb_xa_t *xa)
120 {
121 	uint8_t		single, restart;
122 	uint32_t	sidlistlen, startsidlen, startsidoff;
123 	smb_node_t	*tnode;
124 	smb_ofile_t	*ofile;
125 	smb_quota_query_t request;
126 	smb_quota_response_t reply;
127 	uint32_t status = NT_STATUS_SUCCESS;
128 
129 	bzero(&request, sizeof (smb_quota_query_t));
130 	bzero(&reply, sizeof (smb_quota_response_t));
131 
132 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
133 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, 0, 0);
134 		return (SDRC_ERROR);
135 	}
136 
137 	if (xa->smb_tpscnt != 16) {
138 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
139 		return (SDRC_ERROR);
140 	}
141 
142 	if (smb_mbc_decodef(&xa->req_param_mb, "%wbblll", sr, &sr->smb_fid,
143 	    &single, &restart, &sidlistlen, &startsidlen, &startsidoff)) {
144 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
145 		return (SDRC_ERROR);
146 	}
147 
148 	if ((sidlistlen != 0) && (startsidlen != 0)) {
149 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
150 		return (SDRC_ERROR);
151 	}
152 
153 	smbsr_lookup_file(sr);
154 	ofile = sr->fid_ofile;
155 	if (ofile == NULL) {
156 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
157 		return (SDRC_ERROR);
158 	}
159 
160 	if ((ofile->f_node == NULL) || (ofile->f_ftype != SMB_FTYPE_DISK)) {
161 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
162 		    ERROR_ACCESS_DENIED);
163 		smbsr_release_file(sr);
164 		return (SDRC_ERROR);
165 	}
166 
167 	tnode = sr->tid_tree->t_snode;
168 	request.qq_root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
169 	if (smb_node_getmntpath(tnode, request.qq_root_path, MAXPATHLEN) != 0) {
170 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS,
171 		    ERROR_INVALID_PARAMETER);
172 		smbsr_release_file(sr);
173 		kmem_free(request.qq_root_path, MAXPATHLEN);
174 		return (SDRC_ERROR);
175 	}
176 
177 	if (sidlistlen != 0)
178 		request.qq_query_op = SMB_QUOTA_QUERY_SIDLIST;
179 	else if (startsidlen != 0)
180 		request.qq_query_op = SMB_QUOTA_QUERY_STARTSID;
181 	else
182 		request.qq_query_op = SMB_QUOTA_QUERY_ALL;
183 
184 	request.qq_single = single;
185 	request.qq_restart = restart;
186 	smb_quota_max_quota(xa, &request);
187 
188 	status = smb_quota_init_sids(xa, &request, ofile);
189 
190 	if (status == NT_STATUS_SUCCESS) {
191 		if (smb_quota_query(sr->sr_server, &request, &reply) != 0) {
192 			status = NT_STATUS_INTERNAL_ERROR;
193 		} else {
194 			status = reply.qr_status;
195 			if (status == NT_STATUS_SUCCESS) {
196 				status = smb_quota_encode_quotas(xa,
197 				    &request, &reply, ofile);
198 			}
199 			xdr_free(smb_quota_response_xdr, (char *)&reply);
200 		}
201 	}
202 
203 	kmem_free(request.qq_root_path, MAXPATHLEN);
204 	smb_quota_free_sids(&request);
205 
206 	if (status != NT_STATUS_SUCCESS) {
207 		if (status == NT_STATUS_NO_MORE_ENTRIES) {
208 			smb_ofile_set_quota_resume(ofile, NULL);
209 			smbsr_warn(sr, status, 0, 0);
210 			status = NT_STATUS_SUCCESS;
211 		} else {
212 			smbsr_error(sr, status, 0, 0);
213 		}
214 		(void) smb_mbc_encodef(&xa->rep_param_mb, "l", 0);
215 	}
216 
217 	smbsr_release_file(sr);
218 	return ((status == NT_STATUS_SUCCESS) ? SDRC_SUCCESS : SDRC_ERROR);
219 }
220 
221 /*
222  * smb_nt_transact_set_quota
223  *
224  * This method allows the client to set quota information on the server.
225  * The result status of the call is returned to the client in the
226  * 'status' field of the SMB response header.
227  *
228  * On entry, the 'TotalParameterCount' field must be equal to 2, and the
229  * client parameter block must be encoded with the following parameters:
230  *
231  *  Data Block Encoding                Description
232  *  ================================== =================================
233  *  ULONG NextEntryOffset;             Offset to start of next entry from
234  *                                     start of this entry, or 0 for the
235  *                                     final entry
236  *  ULONG SidLength;                   Length (bytes) of SID
237  *  SMB_TIME ChangeTime;               Time that the quota was last changed
238  *  LARGE_INTEGER QuotaUsed;           Amount of quota (bytes) used by user
239  *  LARGE_INTEGER QuotaThreshold;      Quota warning limit (bytes) for user
240  *  LARGE_INTEGER QuotaLimit;          The quota limit (bytes) for this user
241  *  VARIABLE Sid;                      Security identifier of the user
242  *
243  * An SMB_COM_NT_TRANSACTION response is sent in reply when the request
244  * is successful.  The 'TotalParameterCount' and the 'TotalDataCount' are set
245  * to 0, and the parameter block 'Status' field in the server SMB response
246  * header contains a 32-bit unsigned integer indicating the result status
247  * (NT_STATUS_SUCCESS if successful).
248  *
249  * Only users with Admin privileges (i.e. of the BUILTIN/Administrators
250  * group) will be allowed to set quotas.
251  */
252 smb_sdrc_t
253 smb_nt_transact_set_quota(smb_request_t *sr, smb_xa_t *xa)
254 {
255 	char		*root_path;
256 	uint32_t	status = NT_STATUS_SUCCESS;
257 	smb_node_t	*tnode;
258 	smb_ofile_t	*ofile;
259 	smb_quota_set_t request;
260 	uint32_t	reply;
261 	list_t 		*quota_list;
262 
263 	bzero(&request, sizeof (smb_quota_set_t));
264 
265 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
266 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, 0, 0);
267 		return (SDRC_ERROR);
268 	}
269 
270 	if (!smb_user_is_admin(sr->uid_user)) {
271 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
272 		return (-1);
273 	}
274 
275 	if (xa->smb_tpscnt != 2) {
276 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
277 		return (SDRC_ERROR);
278 	}
279 
280 	if (smb_mbc_decodef(&xa->req_param_mb, "%w", sr,
281 	    &sr->smb_fid)) {
282 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
283 		return (SDRC_ERROR);
284 	}
285 
286 	smbsr_lookup_file(sr);
287 	ofile = sr->fid_ofile;
288 	if (ofile == NULL) {
289 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
290 		return (SDRC_ERROR);
291 	}
292 
293 	if ((ofile->f_node == NULL) || (ofile->f_ftype != SMB_FTYPE_DISK)) {
294 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
295 		    ERROR_ACCESS_DENIED);
296 		smbsr_release_file(sr);
297 		return (SDRC_ERROR);
298 	}
299 
300 	tnode = sr->tid_tree->t_snode;
301 	root_path  = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
302 	if (smb_node_getmntpath(tnode, root_path, MAXPATHLEN) != 0) {
303 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS,
304 		    ERROR_INVALID_PARAMETER);
305 		smbsr_release_file(sr);
306 		kmem_free(root_path, MAXPATHLEN);
307 		return (SDRC_ERROR);
308 	}
309 
310 	quota_list = &request.qs_quota_list;
311 	list_create(quota_list, sizeof (smb_quota_t),
312 	    offsetof(smb_quota_t, q_list_node));
313 
314 	status = smb_quota_decode_quotas(xa, quota_list);
315 	if (status == NT_STATUS_SUCCESS) {
316 		request.qs_root_path = root_path;
317 		if (smb_quota_set(sr->sr_server, &request, &reply) != 0) {
318 			status = NT_STATUS_INTERNAL_ERROR;
319 		} else {
320 			status = reply;
321 			xdr_free(xdr_uint32_t, (char *)&reply);
322 		}
323 	}
324 
325 	kmem_free(root_path, MAXPATHLEN);
326 	smb_quota_free_quotas(&request.qs_quota_list);
327 	smbsr_release_file(sr);
328 
329 	if (status != NT_STATUS_SUCCESS) {
330 		smbsr_error(sr, status, 0, 0);
331 		(void) smb_mbc_encodef(&xa->rep_param_mb, "l", 0);
332 		return (SDRC_ERROR);
333 	}
334 
335 	return (SDRC_SUCCESS);
336 }
337 
338 /*
339  * smb_quota_init_sids
340  *
341  * If the query is of type SMB_QUOTA_QUERY_SIDLIST or
342  * SMB_QUOTA_QUERY_STARTSID decode the list of sids from
343  * the client request into request->qq_sid_list.
344  * Otherwise (type SMB_QUOTA_QUERY_ALL) find the resume sid
345  * and insert it into request->qq_sid_list, or reset the
346  * resume sid to NULL if request->qq_restart.
347  *
348  * Returns: NT_STATUS codes
349  */
350 static uint32_t
351 smb_quota_init_sids(smb_xa_t *xa, smb_quota_query_t *request,
352     smb_ofile_t *ofile)
353 {
354 	smb_quota_sid_t *sid;
355 	list_t *sid_list;
356 	uint32_t status = NT_STATUS_SUCCESS;
357 
358 	sid_list = &request->qq_sid_list;
359 	list_create(sid_list, sizeof (smb_quota_sid_t),
360 	    offsetof(smb_quota_sid_t, qs_list_node));
361 
362 	switch (request->qq_query_op) {
363 	case SMB_QUOTA_QUERY_SIDLIST:
364 	case SMB_QUOTA_QUERY_STARTSID:
365 		status = smb_quota_decode_sids(xa, sid_list);
366 		break;
367 	case SMB_QUOTA_QUERY_ALL:
368 		if (request->qq_restart)
369 			smb_ofile_set_quota_resume(ofile, NULL);
370 		else {
371 			sid = kmem_zalloc(sizeof (smb_quota_sid_t), KM_SLEEP);
372 			list_insert_tail(sid_list, sid);
373 			smb_ofile_get_quota_resume(ofile, sid->qs_sidstr,
374 			    SMB_SID_STRSZ);
375 			if (*sid->qs_sidstr == '\0')
376 				status = NT_STATUS_INVALID_PARAMETER;
377 		}
378 		break;
379 	default:
380 		status = NT_STATUS_INVALID_PARAMETER;
381 		break;
382 	}
383 
384 	return (status);
385 }
386 
387 /*
388  * smb_quota_free_sids
389  */
390 static void
391 smb_quota_free_sids(smb_quota_query_t *request)
392 {
393 	list_t *sid_list;
394 	smb_quota_sid_t *sid;
395 
396 	sid_list = &request->qq_sid_list;
397 
398 	while ((sid = list_head(sid_list)) != NULL) {
399 		list_remove(sid_list, sid);
400 		kmem_free(sid, sizeof (smb_quota_sid_t));
401 	}
402 
403 	list_destroy(sid_list);
404 }
405 
406 /*
407  * smb_quota_decode_sids
408  *
409  * Decode the SIDs from the data block and stores them in string form in list.
410  * Eaxh sid entry comprises:
411  *	next_offset (4 bytes) - offset of next entry
412  *	sid length (4 bytes)
413  *	sid (variable length = sidlen)
414  * The last entry will have a next_offset value of 0.
415  *
416  * Returns NT_STATUS codes.
417  */
418 static uint32_t
419 smb_quota_decode_sids(smb_xa_t *xa, list_t *list)
420 {
421 	uint32_t	offset, mb_offset, sid_offset, bytes_left;
422 	uint32_t	next_offset, sidlen;
423 	smb_sid_t	*sid;
424 	smb_quota_sid_t	*qsid;
425 	uint32_t status = NT_STATUS_SUCCESS;
426 	struct mbuf_chain sidbuf;
427 
428 	offset = 0;
429 	do {
430 		mb_offset = offset + xa->req_data_mb.chain_offset;
431 		bytes_left = xa->req_data_mb.max_bytes - mb_offset;
432 		(void) MBC_SHADOW_CHAIN(&sidbuf, &xa->req_data_mb,
433 		    mb_offset, bytes_left);
434 
435 		if (smb_mbc_decodef(&sidbuf, "ll", &next_offset, &sidlen)) {
436 			status = NT_STATUS_INVALID_PARAMETER;
437 			break;
438 		}
439 
440 		sid_offset = offset + (2 * sizeof (uint32_t));
441 		sid = smb_decode_sid(xa, sid_offset);
442 		if (sid == NULL) {
443 			status = NT_STATUS_INVALID_PARAMETER;
444 			break;
445 		}
446 
447 		qsid = kmem_zalloc(sizeof (smb_quota_sid_t), KM_SLEEP);
448 		smb_sid_tostr(sid, qsid->qs_sidstr);
449 		smb_sid_free(sid);
450 		sid = NULL;
451 
452 		list_insert_tail(list, qsid);
453 		offset += next_offset;
454 	} while ((next_offset != 0) && (bytes_left > 0));
455 
456 	return (status);
457 }
458 
459 /*
460  * smb_quota_max_quota
461  *
462  * If the query is if type SMB_QUOTA_QUERY_SIDLIST a quota entry
463  * is returned for each sid in the sidlist. request->qr_max_quota
464  * is set to 0 and is unused.
465  * Otherwise (for SMB_QUOTA_QUERY_STARTSID and SMB_QUOTA_QUERY_ALL)
466  * max_quota is the maximum number of quota entries requested from
467  * the file system (via door call smb_quota_query()).
468  * If single is set max_quota is set to 1. If single is not set
469  * max quota is calculated as the number of quotas of size
470  * SMB_QUOTA_EST_SIZE that would fit in the response buffer.
471  */
472 static void
473 smb_quota_max_quota(smb_xa_t *xa, smb_quota_query_t *request)
474 {
475 	if (request->qq_query_op == SMB_QUOTA_QUERY_SIDLIST)
476 		request->qq_max_quota = 0;
477 	else if (request->qq_single)
478 		request->qq_max_quota = 1;
479 	else
480 		request->qq_max_quota = (xa->smb_mdrcnt / SMB_QUOTA_EST_SIZE);
481 }
482 
483 /*
484  * smb_quota_decode_quotas
485  *
486  * Decode the quota entries into a list_t of smb_quota_t.
487  * SMB_QUOTA_SIZE_NO_SID is the size of a quota entry,
488  * excluding the sid.
489  * The last entry will have a next_offset value of 0.
490  *
491  * Returns NT_STATUS codes.
492  */
493 static uint32_t
494 smb_quota_decode_quotas(smb_xa_t *xa, list_t *list)
495 {
496 	uint32_t	offset, mb_offset, sid_offset, bytes_left;
497 	uint32_t	next_offset, sidlen;
498 	uint64_t	mtime;
499 	smb_sid_t	*sid;
500 	smb_quota_t	*quota;
501 	uint32_t	status = NT_STATUS_SUCCESS;
502 	struct mbuf_chain quotabuf;
503 
504 	offset = 0;
505 	do {
506 		mb_offset = offset + xa->req_data_mb.chain_offset;
507 		bytes_left = xa->req_data_mb.max_bytes - mb_offset;
508 		(void) MBC_SHADOW_CHAIN(&quotabuf, &xa->req_data_mb,
509 		    mb_offset, bytes_left);
510 
511 		quota = kmem_zalloc(sizeof (smb_quota_t), KM_SLEEP);
512 
513 		if (smb_mbc_decodef(&quotabuf, "llqqqq",
514 		    &next_offset, &sidlen, &mtime,
515 		    &quota->q_used, &quota->q_thresh, &quota->q_limit)) {
516 			kmem_free(quota, sizeof (smb_quota_t));
517 			status = NT_STATUS_INVALID_PARAMETER;
518 			break;
519 		}
520 
521 		sid_offset = offset + SMB_QUOTA_SIZE_NO_SID;
522 		sid = smb_decode_sid(xa, sid_offset);
523 		if (sid == NULL) {
524 			kmem_free(quota, sizeof (smb_quota_t));
525 			status = NT_STATUS_INVALID_PARAMETER;
526 			break;
527 		}
528 
529 		bzero(quota->q_sidstr, SMB_SID_STRSZ);
530 		smb_sid_tostr(sid, quota->q_sidstr);
531 		smb_sid_free(sid);
532 		sid = NULL;
533 
534 		list_insert_tail(list, quota);
535 		offset += next_offset;
536 	} while ((next_offset != 0) && (bytes_left > 0));
537 
538 	return (status);
539 }
540 
541 /*
542  * smb_quota_free_quotas
543  */
544 static void
545 smb_quota_free_quotas(list_t *list)
546 {
547 	smb_quota_t *quota;
548 
549 	while ((quota = list_head(list)) != NULL) {
550 		list_remove(list, quota);
551 		kmem_free(quota, sizeof (smb_quota_t));
552 	}
553 
554 	list_destroy(list);
555 }
556 
557 /*
558  * smb_quota_encode_quotas
559  *
560  * Encode the quota entries from a list_t of smb_quota_t.
561  * SMB_QUOTA_SIZE_NO_SID is the size of a quota entry,
562  * excluding the sid.
563  * The last entry will have a next_offset value of 0.
564  * Sets the last encoded SID as the resume sid.
565  */
566 static uint32_t
567 smb_quota_encode_quotas(smb_xa_t *xa, smb_quota_query_t *request,
568     smb_quota_response_t *reply, smb_ofile_t *ofile)
569 {
570 	uint32_t next_offset, sid_offset, total_bytes;
571 	uint64_t mtime = 0;
572 	uint32_t sidlen, pad;
573 	smb_sid_t *sid;
574 	char *sidstr = NULL, *resume = NULL;
575 	smb_quota_t *quota, *next_quota;
576 	list_t *list = &reply->qr_quota_list;
577 
578 	int rc;
579 	uint32_t status = NT_STATUS_SUCCESS;
580 
581 	total_bytes = 0;
582 	quota = list_head(list);
583 	while (quota) {
584 		next_quota = list_next(list, quota);
585 		sidstr = quota->q_sidstr;
586 		if ((sid = smb_sid_fromstr(sidstr)) == NULL) {
587 			quota = next_quota;
588 			continue;
589 		}
590 
591 		sidlen = smb_sid_len(sid);
592 		sid_offset = SMB_QUOTA_SIZE_NO_SID;
593 		next_offset = sid_offset + sidlen;
594 		pad = smb_pad_align(next_offset, 8);
595 		next_offset += pad;
596 
597 		if (!MBC_ROOM_FOR(&xa->rep_data_mb, next_offset)) {
598 			smb_sid_free(sid);
599 			break;
600 		}
601 		if (!MBC_ROOM_FOR(&xa->rep_data_mb,
602 		    next_offset + SMB_QUOTA_MAX_SIZE)) {
603 			next_quota = NULL;
604 		}
605 
606 		rc = smb_mbc_encodef(&xa->rep_data_mb, "llqqqq",
607 		    next_quota ? next_offset : 0, sidlen, mtime,
608 		    quota->q_used, quota->q_thresh, quota->q_limit);
609 		if (rc == 0) {
610 			smb_encode_sid(xa, sid);
611 			rc = smb_mbc_encodef(&xa->rep_data_mb, "#.", pad);
612 		}
613 
614 		smb_sid_free(sid);
615 
616 		if (rc != 0) {
617 			status = NT_STATUS_INTERNAL_ERROR;
618 			break;
619 		}
620 
621 		resume = sidstr;
622 		total_bytes += next_offset;
623 		quota = next_quota;
624 	}
625 
626 	rc = smb_mbc_encodef(&xa->rep_param_mb, "l", total_bytes);
627 
628 	if ((status == NT_STATUS_SUCCESS) &&
629 	    ((request->qq_query_op == SMB_QUOTA_QUERY_STARTSID) ||
630 	    (request->qq_query_op == SMB_QUOTA_QUERY_ALL))) {
631 		smb_ofile_set_quota_resume(ofile, resume);
632 	}
633 
634 	return (status);
635 }
636 
637 /*
638  * smb_quota_query_user_quota
639  *
640  * Get user quota information for a single user (uid)
641  * for the current file system.
642  * Find the user's sid, insert it in the sidlist of a
643  * smb_quota_query_t request and invoke the door call
644  * smb_quota_query() to obtain the quota information.
645  *
646  * Returns: NT_STATUS codes.
647  */
648 uint32_t
649 smb_quota_query_user_quota(smb_request_t *sr, uid_t uid, smb_quota_t *quota)
650 {
651 	smb_sid_t *sid;
652 	smb_quota_sid_t qsid;
653 	smb_quota_query_t request;
654 	smb_quota_response_t reply;
655 	list_t *sid_list;
656 	smb_quota_t *q;
657 	smb_node_t *tnode;
658 	uint32_t status = NT_STATUS_SUCCESS;
659 
660 	if (smb_idmap_getsid(uid, SMB_IDMAP_USER, &sid) != IDMAP_SUCCESS)
661 		return (NT_STATUS_INTERNAL_ERROR);
662 
663 	smb_sid_tostr(sid, qsid.qs_sidstr);
664 	smb_sid_free(sid);
665 
666 	bzero(&request, sizeof (smb_quota_query_t));
667 	bzero(&reply, sizeof (smb_quota_response_t));
668 
669 	tnode = sr->tid_tree->t_snode;
670 	request.qq_root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
671 	if (smb_node_getmntpath(tnode, request.qq_root_path, MAXPATHLEN) != 0) {
672 		kmem_free(request.qq_root_path, MAXPATHLEN);
673 		return (NT_STATUS_INTERNAL_ERROR);
674 	}
675 
676 	sid_list = &request.qq_sid_list;
677 	list_create(sid_list, sizeof (smb_quota_sid_t),
678 	    offsetof(smb_quota_sid_t, qs_list_node));
679 	list_insert_tail(sid_list, &qsid);
680 
681 	request.qq_query_op = SMB_QUOTA_QUERY_SIDLIST;
682 	request.qq_single = B_TRUE;
683 
684 	if (smb_quota_query(sr->sr_server, &request, &reply) != 0) {
685 		status = NT_STATUS_INTERNAL_ERROR;
686 	} else {
687 		if (reply.qr_status != NT_STATUS_SUCCESS) {
688 			status = reply.qr_status;
689 		} else {
690 			q = list_head(&reply.qr_quota_list);
691 			if ((q == NULL) ||
692 			    (strcmp(qsid.qs_sidstr, q->q_sidstr) != 0)) {
693 				/* should never happen */
694 				status = NT_STATUS_INTERNAL_ERROR;
695 			} else {
696 				bcopy(q, quota, sizeof (smb_quota_t));
697 			}
698 		}
699 		xdr_free(smb_quota_response_xdr, (char *)&reply);
700 	}
701 
702 	kmem_free(request.qq_root_path, MAXPATHLEN);
703 	list_remove(sid_list, &qsid);
704 	list_destroy(sid_list);
705 
706 	return (status);
707 }
708 
709 /*
710  * smb_quota_query
711  *
712  * Door call to query quotas for the provided filesystem path.
713  * Returns: -1 - door call (or encode/decode) failure.
714  *	     0 - success. Status set in reply.
715  */
716 static int
717 smb_quota_query(smb_server_t *sv, smb_quota_query_t *request,
718     smb_quota_response_t *reply)
719 {
720 	int	rc;
721 
722 	rc = smb_kdoor_upcall(sv, SMB_DR_QUOTA_QUERY,
723 	    request, smb_quota_query_xdr, reply, smb_quota_response_xdr);
724 
725 	return (rc);
726 }
727 
728 /*
729  * smb_quota_set
730  *
731  * Door call to set quotas for the provided filesystem path.
732  * Returns: -1 - door call (or encode/decode) failure.
733  *	     0 - success. Status set in reply.
734  */
735 static int
736 smb_quota_set(smb_server_t *sv, smb_quota_set_t *request, uint32_t *reply)
737 {
738 	int	rc;
739 
740 	rc = smb_kdoor_upcall(sv, SMB_DR_QUOTA_SET,
741 	    request, smb_quota_set_xdr, reply, xdr_uint32_t);
742 
743 	return (rc);
744 }
745