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