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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <smbsrv/smb_kproto.h>
27 #include <smbsrv/smb_fsops.h>
28 #include <smbsrv/smb_share.h>
29 #include <smbsrv/string.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
smb_nt_transact_query_quota(smb_request_t * sr,smb_xa_t * xa)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_ENTRIES) {
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
smb_nt_transact_set_quota(smb_request_t * sr,smb_xa_t * xa)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
smb_quota_init_sids(smb_xa_t * xa,smb_quota_query_t * request,smb_ofile_t * ofile)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
smb_quota_free_sids(smb_quota_query_t * request)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
smb_quota_decode_sids(smb_xa_t * xa,list_t * list)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
smb_quota_max_quota(smb_xa_t * xa,smb_quota_query_t * request)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
smb_quota_decode_quotas(smb_xa_t * xa,list_t * list)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("abuf, &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("abuf, "llqqqq",
512 &next_offset, &sidlen, &mtime,
513 "a->q_used, "a->q_thresh, "a->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
smb_quota_free_quotas(list_t * list)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
smb_quota_encode_quotas(smb_xa_t * xa,smb_quota_query_t * request,smb_quota_response_t * reply,smb_ofile_t * ofile)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
smb_quota_query_user_quota(smb_request_t * sr,uid_t uid,smb_quota_t * quota)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
smb_quota_query(smb_quota_query_t * request,smb_quota_response_t * reply)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
smb_quota_set(smb_quota_set_t * request,uint32_t * reply)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