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 */
65uint32_t
66smb_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 */
105void
106smb_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 */
133uint32_t
134smb_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 */
192void
193smb_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 */
213uint32_t
214smb_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(&quotabuf, 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(&quotabuf, "llqqqq",
239		    &next_offset, &sidlen, &mtime,
240		    &quota->q_used, &quota->q_thresh, &quota->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 */
269void
270smb_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 */
291uint32_t
292smb_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 */
369uint32_t
370smb_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 */
437int
438smb_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 */
456int
457smb_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