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
smb_nt_transact_query_quota(smb_request_t * sr,smb_xa_t * xa)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
smb_nt_transact_set_quota(smb_request_t * sr,smb_xa_t * xa)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
smb_quota_init_sids(smb_xa_t * xa,smb_quota_query_t * request,smb_ofile_t * ofile)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
smb_quota_free_sids(smb_quota_query_t * request)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
smb_quota_decode_sids(smb_xa_t * xa,list_t * list)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
smb_quota_max_quota(smb_xa_t * xa,smb_quota_query_t * request)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
smb_quota_decode_quotas(smb_xa_t * xa,list_t * list)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("abuf, &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("abuf, "llqqqq",
514 &next_offset, &sidlen, &mtime,
515 "a->q_used, "a->q_thresh, "a->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
smb_quota_free_quotas(list_t * list)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
smb_quota_encode_quotas(smb_xa_t * xa,smb_quota_query_t * request,smb_quota_response_t * reply,smb_ofile_t * ofile)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
smb_quota_query_user_quota(smb_request_t * sr,uid_t uid,smb_quota_t * quota)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
smb_quota_query(smb_server_t * sv,smb_quota_query_t * request,smb_quota_response_t * reply)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
smb_quota_set(smb_server_t * sv,smb_quota_set_t * request,uint32_t * reply)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