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 * Copyright 2014 Nexenta Systems, Inc. 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
53 /*
54 * smb_quota_init_sids
55 *
56 * If the query is of type SMB_QUOTA_QUERY_SIDLIST or
57 * SMB_QUOTA_QUERY_STARTSID decode the list of sids from
58 * the client request into request->qq_sid_list.
59 * Otherwise (type SMB_QUOTA_QUERY_ALL) find the resume sid
60 * and insert it into request->qq_sid_list, or reset the
61 * resume sid to NULL if request->qq_restart.
62 *
63 * Returns: NT_STATUS codes
64 */
65 uint32_t
smb_quota_init_sids(mbuf_chain_t * mbc,smb_quota_query_t * request,smb_ofile_t * ofile)66 smb_quota_init_sids(mbuf_chain_t *mbc, smb_quota_query_t *request,
67 smb_ofile_t *ofile)
68 {
69 smb_quota_sid_t *sid;
70 list_t *sid_list;
71 uint32_t status = NT_STATUS_SUCCESS;
72
73 sid_list = &request->qq_sid_list;
74 list_create(sid_list, sizeof (smb_quota_sid_t),
75 offsetof(smb_quota_sid_t, qs_list_node));
76
77 switch (request->qq_query_op) {
78 case SMB_QUOTA_QUERY_SIDLIST:
79 case SMB_QUOTA_QUERY_STARTSID:
80 status = smb_quota_decode_sids(mbc, sid_list);
81 break;
82 case SMB_QUOTA_QUERY_ALL:
83 if (request->qq_restart)
84 smb_ofile_set_quota_resume(ofile, NULL);
85 else {
86 sid = kmem_zalloc(sizeof (smb_quota_sid_t), KM_SLEEP);
87 list_insert_tail(sid_list, sid);
88 smb_ofile_get_quota_resume(ofile, sid->qs_sidstr,
89 SMB_SID_STRSZ);
90 if (*sid->qs_sidstr == '\0')
91 status = NT_STATUS_INVALID_PARAMETER;
92 }
93 break;
94 default:
95 status = NT_STATUS_INVALID_PARAMETER;
96 break;
97 }
98
99 return (status);
100 }
101
102 /*
103 * smb_quota_free_sids
104 */
105 void
smb_quota_free_sids(smb_quota_query_t * request)106 smb_quota_free_sids(smb_quota_query_t *request)
107 {
108 list_t *sid_list;
109 smb_quota_sid_t *sid;
110
111 sid_list = &request->qq_sid_list;
112
113 while ((sid = list_head(sid_list)) != NULL) {
114 list_remove(sid_list, sid);
115 kmem_free(sid, sizeof (smb_quota_sid_t));
116 }
117
118 list_destroy(sid_list);
119 }
120
121 /*
122 * smb_quota_decode_sids
123 *
124 * Decode the SIDs from the data block and stores them in string form in list.
125 * Eaxh sid entry comprises:
126 * next_offset (4 bytes) - offset of next entry
127 * sid length (4 bytes)
128 * sid (variable length = sidlen)
129 * The last entry will have a next_offset value of 0.
130 *
131 * Returns NT_STATUS codes.
132 */
133 uint32_t
smb_quota_decode_sids(mbuf_chain_t * mbc,list_t * list)134 smb_quota_decode_sids(mbuf_chain_t *mbc, list_t *list)
135 {
136 uint32_t offset, mb_offset, sid_offset, bytes_left;
137 uint32_t next_offset, sidlen;
138 smb_sid_t *sid;
139 smb_quota_sid_t *qsid;
140 uint32_t status = NT_STATUS_SUCCESS;
141 struct mbuf_chain sidbuf;
142 int rc;
143
144 offset = 0;
145 do {
146 mb_offset = offset + mbc->chain_offset;
147 bytes_left = mbc->max_bytes - mb_offset;
148 rc = MBC_SHADOW_CHAIN(&sidbuf, mbc,
149 mb_offset, bytes_left);
150 if (rc != 0) {
151 status = NT_STATUS_INVALID_PARAMETER;
152 break;
153 }
154
155 if (smb_mbc_decodef(&sidbuf, "ll", &next_offset, &sidlen)) {
156 status = NT_STATUS_INVALID_PARAMETER;
157 break;
158 }
159
160 sid_offset = offset + (2 * sizeof (uint32_t));
161 sid = smb_decode_sid(mbc, sid_offset);
162 if (sid == NULL) {
163 status = NT_STATUS_INVALID_PARAMETER;
164 break;
165 }
166
167 qsid = kmem_zalloc(sizeof (smb_quota_sid_t), KM_SLEEP);
168 smb_sid_tostr(sid, qsid->qs_sidstr);
169 smb_sid_free(sid);
170 sid = NULL;
171
172 list_insert_tail(list, qsid);
173 offset += next_offset;
174 } while ((next_offset != 0) && (bytes_left > 0));
175
176 return (status);
177 }
178
179 /*
180 * smb_quota_max_quota
181 *
182 * If the query is if type SMB_QUOTA_QUERY_SIDLIST a quota entry
183 * is returned for each sid in the sidlist. request->qr_max_quota
184 * is set to 0 and is unused.
185 * Otherwise (for SMB_QUOTA_QUERY_STARTSID and SMB_QUOTA_QUERY_ALL)
186 * max_quota is the maximum number of quota entries requested from
187 * the file system (via door call smb_quota_query()).
188 * If single is set max_quota is set to 1. If single is not set
189 * max quota is calculated as the number of quotas of size
190 * SMB_QUOTA_EST_SIZE that would fit in the response buffer.
191 */
192 void
smb_quota_max_quota(mbuf_chain_t * mbc,smb_quota_query_t * request)193 smb_quota_max_quota(mbuf_chain_t *mbc, smb_quota_query_t *request)
194 {
195 if (request->qq_query_op == SMB_QUOTA_QUERY_SIDLIST)
196 request->qq_max_quota = 0;
197 else if (request->qq_single)
198 request->qq_max_quota = 1;
199 else
200 request->qq_max_quota = (mbc->max_bytes / SMB_QUOTA_EST_SIZE);
201 }
202
203 /*
204 * smb_quota_decode_quotas
205 *
206 * Decode the quota entries into a list_t of smb_quota_t.
207 * SMB_QUOTA_SIZE_NO_SID is the size of a quota entry,
208 * excluding the sid.
209 * The last entry will have a next_offset value of 0.
210 *
211 * Returns NT_STATUS codes.
212 */
213 uint32_t
smb_quota_decode_quotas(mbuf_chain_t * mbc,list_t * list)214 smb_quota_decode_quotas(mbuf_chain_t *mbc, list_t *list)
215 {
216 uint32_t offset, mb_offset, sid_offset, bytes_left;
217 uint32_t next_offset, sidlen;
218 uint64_t mtime;
219 smb_sid_t *sid;
220 smb_quota_t *quota;
221 uint32_t status = NT_STATUS_SUCCESS;
222 struct mbuf_chain quotabuf;
223 int rc;
224
225 offset = 0;
226 do {
227 mb_offset = offset + mbc->chain_offset;
228 bytes_left = mbc->max_bytes - mb_offset;
229 rc = MBC_SHADOW_CHAIN("abuf, mbc,
230 mb_offset, bytes_left);
231 if (rc != 0) {
232 status = NT_STATUS_INVALID_PARAMETER;
233 break;
234 }
235
236 quota = kmem_zalloc(sizeof (smb_quota_t), KM_SLEEP);
237
238 if (smb_mbc_decodef("abuf, "llqqqq",
239 &next_offset, &sidlen, &mtime,
240 "a->q_used, "a->q_thresh, "a->q_limit)) {
241 kmem_free(quota, sizeof (smb_quota_t));
242 status = NT_STATUS_INVALID_PARAMETER;
243 break;
244 }
245
246 sid_offset = offset + SMB_QUOTA_SIZE_NO_SID;
247 sid = smb_decode_sid(mbc, sid_offset);
248 if (sid == NULL) {
249 kmem_free(quota, sizeof (smb_quota_t));
250 status = NT_STATUS_INVALID_PARAMETER;
251 break;
252 }
253
254 bzero(quota->q_sidstr, SMB_SID_STRSZ);
255 smb_sid_tostr(sid, quota->q_sidstr);
256 smb_sid_free(sid);
257 sid = NULL;
258
259 list_insert_tail(list, quota);
260 offset += next_offset;
261 } while ((next_offset != 0) && (bytes_left > 0));
262
263 return (status);
264 }
265
266 /*
267 * smb_quota_free_quotas
268 */
269 void
smb_quota_free_quotas(list_t * list)270 smb_quota_free_quotas(list_t *list)
271 {
272 smb_quota_t *quota;
273
274 while ((quota = list_head(list)) != NULL) {
275 list_remove(list, quota);
276 kmem_free(quota, sizeof (smb_quota_t));
277 }
278
279 list_destroy(list);
280 }
281
282 /*
283 * smb_quota_encode_quotas
284 *
285 * Encode the quota entries from a list_t of smb_quota_t.
286 * SMB_QUOTA_SIZE_NO_SID is the size of a quota entry,
287 * excluding the sid.
288 * The last entry will have a next_offset value of 0.
289 * Sets the last encoded SID as the resume sid.
290 */
291 uint32_t
smb_quota_encode_quotas(mbuf_chain_t * mbc,smb_quota_query_t * request,smb_quota_response_t * reply,smb_ofile_t * ofile)292 smb_quota_encode_quotas(mbuf_chain_t *mbc, smb_quota_query_t *request,
293 smb_quota_response_t *reply, smb_ofile_t *ofile)
294 {
295 uint32_t next_offset, sid_offset;
296 uint64_t mtime = 0;
297 uint32_t sidlen, pad;
298 smb_sid_t *sid;
299 char *sidstr = NULL, *resume = NULL;
300 smb_quota_t *quota, *next_quota;
301 list_t *list = &reply->qr_quota_list;
302
303 int rc;
304 uint32_t status = NT_STATUS_SUCCESS;
305
306 quota = list_head(list);
307 while (quota) {
308 next_quota = list_next(list, quota);
309 sidstr = quota->q_sidstr;
310 if ((sid = smb_sid_fromstr(sidstr)) == NULL) {
311 quota = next_quota;
312 continue;
313 }
314
315 sidlen = smb_sid_len(sid);
316 sid_offset = SMB_QUOTA_SIZE_NO_SID;
317 next_offset = sid_offset + sidlen;
318 pad = smb_pad_align(next_offset, 8);
319 next_offset += pad;
320
321 if (!MBC_ROOM_FOR(mbc, next_offset)) {
322 smb_sid_free(sid);
323 break;
324 }
325 if (!MBC_ROOM_FOR(mbc,
326 next_offset + SMB_QUOTA_MAX_SIZE)) {
327 next_quota = NULL;
328 }
329
330 rc = smb_mbc_encodef(mbc, "llqqqq",
331 next_quota ? next_offset : 0, sidlen, mtime,
332 quota->q_used, quota->q_thresh, quota->q_limit);
333 if (rc == 0) {
334 smb_encode_sid(mbc, sid);
335 rc = smb_mbc_encodef(mbc, "#.", pad);
336 }
337
338 smb_sid_free(sid);
339
340 if (rc != 0) {
341 status = NT_STATUS_INTERNAL_ERROR;
342 break;
343 }
344
345 resume = sidstr;
346 quota = next_quota;
347 }
348
349 if ((status == NT_STATUS_SUCCESS) &&
350 ((request->qq_query_op == SMB_QUOTA_QUERY_STARTSID) ||
351 (request->qq_query_op == SMB_QUOTA_QUERY_ALL))) {
352 smb_ofile_set_quota_resume(ofile, resume);
353 }
354
355 return (status);
356 }
357
358 /*
359 * smb_quota_query_user_quota
360 *
361 * Get user quota information for a single user (uid)
362 * for the current file system.
363 * Find the user's sid, insert it in the sidlist of a
364 * smb_quota_query_t request and invoke the door call
365 * smb_quota_query() to obtain the quota information.
366 *
367 * Returns: NT_STATUS codes.
368 */
369 uint32_t
smb_quota_query_user_quota(smb_request_t * sr,uid_t uid,smb_quota_t * quota)370 smb_quota_query_user_quota(smb_request_t *sr, uid_t uid, smb_quota_t *quota)
371 {
372 smb_sid_t *sid;
373 smb_quota_sid_t qsid;
374 smb_quota_query_t request;
375 smb_quota_response_t reply;
376 list_t *sid_list;
377 smb_quota_t *q;
378 smb_node_t *tnode;
379 uint32_t status = NT_STATUS_SUCCESS;
380
381 if (smb_idmap_getsid(uid, SMB_IDMAP_USER, &sid) != IDMAP_SUCCESS)
382 return (NT_STATUS_INTERNAL_ERROR);
383
384 smb_sid_tostr(sid, qsid.qs_sidstr);
385 smb_sid_free(sid);
386
387 bzero(&request, sizeof (smb_quota_query_t));
388 bzero(&reply, sizeof (smb_quota_response_t));
389
390 tnode = sr->tid_tree->t_snode;
391 request.qq_root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
392 if (smb_node_getmntpath(tnode, request.qq_root_path, MAXPATHLEN) != 0) {
393 kmem_free(request.qq_root_path, MAXPATHLEN);
394 return (NT_STATUS_INTERNAL_ERROR);
395 }
396
397 sid_list = &request.qq_sid_list;
398 list_create(sid_list, sizeof (smb_quota_sid_t),
399 offsetof(smb_quota_sid_t, qs_list_node));
400 list_insert_tail(sid_list, &qsid);
401
402 request.qq_query_op = SMB_QUOTA_QUERY_SIDLIST;
403 request.qq_single = B_TRUE;
404
405 if (smb_quota_query(sr->sr_server, &request, &reply) != 0) {
406 status = NT_STATUS_INTERNAL_ERROR;
407 } else {
408 if (reply.qr_status != NT_STATUS_SUCCESS) {
409 status = reply.qr_status;
410 } else {
411 q = list_head(&reply.qr_quota_list);
412 if ((q == NULL) ||
413 (strcmp(qsid.qs_sidstr, q->q_sidstr) != 0)) {
414 /* should never happen */
415 status = NT_STATUS_INTERNAL_ERROR;
416 } else {
417 bcopy(q, quota, sizeof (smb_quota_t));
418 }
419 }
420 xdr_free(smb_quota_response_xdr, (char *)&reply);
421 }
422
423 kmem_free(request.qq_root_path, MAXPATHLEN);
424 list_remove(sid_list, &qsid);
425 list_destroy(sid_list);
426
427 return (status);
428 }
429
430 /*
431 * smb_quota_query
432 *
433 * Door call to query quotas for the provided filesystem path.
434 * Returns: -1 - door call (or encode/decode) failure.
435 * 0 - success. Status set in reply.
436 */
437 int
smb_quota_query(smb_server_t * sv,smb_quota_query_t * request,smb_quota_response_t * reply)438 smb_quota_query(smb_server_t *sv, smb_quota_query_t *request,
439 smb_quota_response_t *reply)
440 {
441 int rc;
442
443 rc = smb_kdoor_upcall(sv, SMB_DR_QUOTA_QUERY,
444 request, smb_quota_query_xdr, reply, smb_quota_response_xdr);
445
446 return (rc);
447 }
448
449 /*
450 * smb_quota_set
451 *
452 * Door call to set quotas for the provided filesystem path.
453 * Returns: -1 - door call (or encode/decode) failure.
454 * 0 - success. Status set in reply.
455 */
456 int
smb_quota_set(smb_server_t * sv,smb_quota_set_t * request,uint32_t * reply)457 smb_quota_set(smb_server_t *sv, smb_quota_set_t *request, uint32_t *reply)
458 {
459 int rc;
460
461 rc = smb_kdoor_upcall(sv, SMB_DR_QUOTA_SET,
462 request, smb_quota_set_xdr, reply, xdr_uint32_t);
463
464 return (rc);
465 }
466