xref: /onnv-gate/usr/src/lib/smbsrv/libmlsvc/common/smb_quota.c (revision 11963:061945695ce1)
1*11963SAfshin.Ardakani@Sun.COM /*
2*11963SAfshin.Ardakani@Sun.COM  * CDDL HEADER START
3*11963SAfshin.Ardakani@Sun.COM  *
4*11963SAfshin.Ardakani@Sun.COM  * The contents of this file are subject to the terms of the
5*11963SAfshin.Ardakani@Sun.COM  * Common Development and Distribution License (the "License").
6*11963SAfshin.Ardakani@Sun.COM  * You may not use this file except in compliance with the License.
7*11963SAfshin.Ardakani@Sun.COM  *
8*11963SAfshin.Ardakani@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*11963SAfshin.Ardakani@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*11963SAfshin.Ardakani@Sun.COM  * See the License for the specific language governing permissions
11*11963SAfshin.Ardakani@Sun.COM  * and limitations under the License.
12*11963SAfshin.Ardakani@Sun.COM  *
13*11963SAfshin.Ardakani@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*11963SAfshin.Ardakani@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*11963SAfshin.Ardakani@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*11963SAfshin.Ardakani@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*11963SAfshin.Ardakani@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*11963SAfshin.Ardakani@Sun.COM  *
19*11963SAfshin.Ardakani@Sun.COM  * CDDL HEADER END
20*11963SAfshin.Ardakani@Sun.COM  */
21*11963SAfshin.Ardakani@Sun.COM /*
22*11963SAfshin.Ardakani@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23*11963SAfshin.Ardakani@Sun.COM  * Use is subject to license terms.
24*11963SAfshin.Ardakani@Sun.COM  */
25*11963SAfshin.Ardakani@Sun.COM #include <stdio.h>
26*11963SAfshin.Ardakani@Sun.COM #include <stdlib.h>
27*11963SAfshin.Ardakani@Sun.COM #include <fcntl.h>
28*11963SAfshin.Ardakani@Sun.COM #include <attr.h>
29*11963SAfshin.Ardakani@Sun.COM #include <unistd.h>
30*11963SAfshin.Ardakani@Sun.COM #include <libuutil.h>
31*11963SAfshin.Ardakani@Sun.COM #include <libzfs.h>
32*11963SAfshin.Ardakani@Sun.COM #include <assert.h>
33*11963SAfshin.Ardakani@Sun.COM #include <stddef.h>
34*11963SAfshin.Ardakani@Sun.COM #include <strings.h>
35*11963SAfshin.Ardakani@Sun.COM #include <errno.h>
36*11963SAfshin.Ardakani@Sun.COM #include <synch.h>
37*11963SAfshin.Ardakani@Sun.COM #include <smbsrv/smb_xdr.h>
38*11963SAfshin.Ardakani@Sun.COM #include <smbsrv/ntstatus.h>
39*11963SAfshin.Ardakani@Sun.COM #include <smbsrv/libmlsvc.h>
40*11963SAfshin.Ardakani@Sun.COM #include <smbsrv/smb_idmap.h>
41*11963SAfshin.Ardakani@Sun.COM #include <mlsvc.h>
42*11963SAfshin.Ardakani@Sun.COM #include <sys/avl.h>
43*11963SAfshin.Ardakani@Sun.COM 
44*11963SAfshin.Ardakani@Sun.COM /*
45*11963SAfshin.Ardakani@Sun.COM  * smb_quota subsystem interface - mlsvc.h
46*11963SAfshin.Ardakani@Sun.COM  * ---------------------------------------
47*11963SAfshin.Ardakani@Sun.COM  * Management of the smb_quota_fs_list (see below).
48*11963SAfshin.Ardakani@Sun.COM  * smb_quota_init
49*11963SAfshin.Ardakani@Sun.COM  * smb_quota_fini
50*11963SAfshin.Ardakani@Sun.COM  * smb_quota_add_fs
51*11963SAfshin.Ardakani@Sun.COM  * smb_quota_remove_fs
52*11963SAfshin.Ardakani@Sun.COM  *
53*11963SAfshin.Ardakani@Sun.COM  * smb_quota public interface - libmlsvc.h
54*11963SAfshin.Ardakani@Sun.COM  * ---------------------------------------
55*11963SAfshin.Ardakani@Sun.COM  * Handling of requests to query and set quota data on a filesystem.
56*11963SAfshin.Ardakani@Sun.COM  * smb_quota_query - query user/group quotas on a filesystem
57*11963SAfshin.Ardakani@Sun.COM  * smb_quota_set - set user/group quotas ona filesystem
58*11963SAfshin.Ardakani@Sun.COM  * smb_quota_free - delete the quota list created in smb_quota_query
59*11963SAfshin.Ardakani@Sun.COM  */
60*11963SAfshin.Ardakani@Sun.COM 
61*11963SAfshin.Ardakani@Sun.COM /*
62*11963SAfshin.Ardakani@Sun.COM  * Querying user & group quotas - smb_quota_query
63*11963SAfshin.Ardakani@Sun.COM  *
64*11963SAfshin.Ardakani@Sun.COM  * In order to fulfill the quota query requests that can be received
65*11963SAfshin.Ardakani@Sun.COM  * from clients, it is required that the quota data can be provided in
66*11963SAfshin.Ardakani@Sun.COM  * a well defined and consistent order, and that a request can specify
67*11963SAfshin.Ardakani@Sun.COM  * at which quota entry to begin the query.
68*11963SAfshin.Ardakani@Sun.COM  *
69*11963SAfshin.Ardakani@Sun.COM  * Quota Tree
70*11963SAfshin.Ardakani@Sun.COM  * Since the file system does not support the above, an avl tree is
71*11963SAfshin.Ardakani@Sun.COM  * populated with the file system's user and group quota data, and
72*11963SAfshin.Ardakani@Sun.COM  * then used to provide the data to respond to query requests. The
73*11963SAfshin.Ardakani@Sun.COM  * avl tree is indexed by the SID.
74*11963SAfshin.Ardakani@Sun.COM  * Each node of the avl tree is an smb_quota_t structure.
75*11963SAfshin.Ardakani@Sun.COM  *
76*11963SAfshin.Ardakani@Sun.COM  * Quota List
77*11963SAfshin.Ardakani@Sun.COM  * There is a list of avl trees, one per file system.
78*11963SAfshin.Ardakani@Sun.COM  * Each node in the list is an smb_quota_tree_t structure.
79*11963SAfshin.Ardakani@Sun.COM  * The list is created via a call to smb_quota_init() when the library
80*11963SAfshin.Ardakani@Sun.COM  * is initialized, and destroyed via a call to smb_quota_fini() when
81*11963SAfshin.Ardakani@Sun.COM  * the library is fini'd.
82*11963SAfshin.Ardakani@Sun.COM  *
83*11963SAfshin.Ardakani@Sun.COM  * An avl tree for a specific file system is created and added to the
84*11963SAfshin.Ardakani@Sun.COM  * list via a call to smb_quota_add_fs() when the file system is shared,
85*11963SAfshin.Ardakani@Sun.COM  * and removed from the list via a call to smb_quota_remove_fs() when
86*11963SAfshin.Ardakani@Sun.COM  * the file system is unshared.
87*11963SAfshin.Ardakani@Sun.COM  *
88*11963SAfshin.Ardakani@Sun.COM  * An avl tree is (re)populated, if required, whenever a quota request
89*11963SAfshin.Ardakani@Sun.COM  * (EXCLUDING a resume request) is received for its filesystem. The
90*11963SAfshin.Ardakani@Sun.COM  * avl tree is considered to be expired (needs to be repopulated) if
91*11963SAfshin.Ardakani@Sun.COM  * either of the following have occurred since it was last (re)populated:
92*11963SAfshin.Ardakani@Sun.COM  * - SMB_QUOTA_REFRESH seconds have elapsed OR
93*11963SAfshin.Ardakani@Sun.COM  * - a quota set operation has been performed on its file system
94*11963SAfshin.Ardakani@Sun.COM  *
95*11963SAfshin.Ardakani@Sun.COM  * In order to perform a smb_quota_query/set operation on a file system
96*11963SAfshin.Ardakani@Sun.COM  * the appropriate quota tree must be identified and locked via a call
97*11963SAfshin.Ardakani@Sun.COM  * to smb_quota_tree_lookup(), The quota tree is locked (qt_locked == B_TRUE)
98*11963SAfshin.Ardakani@Sun.COM  * until the caller releases it via a call to smb_quota_tree_release().
99*11963SAfshin.Ardakani@Sun.COM  */
100*11963SAfshin.Ardakani@Sun.COM 
101*11963SAfshin.Ardakani@Sun.COM /*
102*11963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_t
103*11963SAfshin.Ardakani@Sun.COM  * Represents an avl tree of user quotas for a file system.
104*11963SAfshin.Ardakani@Sun.COM  *
105*11963SAfshin.Ardakani@Sun.COM  * qt_refcnt - a count of the number of users of the tree.
106*11963SAfshin.Ardakani@Sun.COM  * qt_refcnt is also incremented and decremented when the tree is
107*11963SAfshin.Ardakani@Sun.COM  * added to and removed from the quota list.
108*11963SAfshin.Ardakani@Sun.COM  * The tree cannot be deleted until this count is zero.
109*11963SAfshin.Ardakani@Sun.COM  *
110*11963SAfshin.Ardakani@Sun.COM  * qt_sharecnt - a count of the shares of the file system which the
111*11963SAfshin.Ardakani@Sun.COM  * tree represents.  smb_quota_remove_fs() cannot remove the tree from
112*11963SAfshin.Ardakani@Sun.COM  * removed from the quota list until this count is zero.
113*11963SAfshin.Ardakani@Sun.COM  *
114*11963SAfshin.Ardakani@Sun.COM  * qt_locked - B_TRUE if someone is currently using the tree, in
115*11963SAfshin.Ardakani@Sun.COM  * which case a lookup will wait for the tree to become available.
116*11963SAfshin.Ardakani@Sun.COM  */
117*11963SAfshin.Ardakani@Sun.COM typedef struct smb_quota_tree {
118*11963SAfshin.Ardakani@Sun.COM 	list_node_t	qt_node;
119*11963SAfshin.Ardakani@Sun.COM 	char		*qt_path;
120*11963SAfshin.Ardakani@Sun.COM 	time_t		qt_timestamp;
121*11963SAfshin.Ardakani@Sun.COM 	uint32_t	qt_refcnt;
122*11963SAfshin.Ardakani@Sun.COM 	uint32_t	qt_sharecnt;
123*11963SAfshin.Ardakani@Sun.COM 	boolean_t	qt_locked;
124*11963SAfshin.Ardakani@Sun.COM 	avl_tree_t	qt_avl;
125*11963SAfshin.Ardakani@Sun.COM 	mutex_t		qt_mutex;
126*11963SAfshin.Ardakani@Sun.COM }smb_quota_tree_t;
127*11963SAfshin.Ardakani@Sun.COM 
128*11963SAfshin.Ardakani@Sun.COM /*
129*11963SAfshin.Ardakani@Sun.COM  * smb_quota_fs_list
130*11963SAfshin.Ardakani@Sun.COM  * list of quota trees; one per shared file system.
131*11963SAfshin.Ardakani@Sun.COM  */
132*11963SAfshin.Ardakani@Sun.COM static list_t smb_quota_fs_list;
133*11963SAfshin.Ardakani@Sun.COM static boolean_t smb_quota_list_init = B_FALSE;
134*11963SAfshin.Ardakani@Sun.COM static boolean_t smb_quota_shutdown = B_FALSE;
135*11963SAfshin.Ardakani@Sun.COM static mutex_t smb_quota_list_mutex;
136*11963SAfshin.Ardakani@Sun.COM static cond_t smb_quota_list_condvar;
137*11963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_tree_cnt = 0;
138*11963SAfshin.Ardakani@Sun.COM static int smb_quota_fini_timeout = 1; /* seconds */
139*11963SAfshin.Ardakani@Sun.COM 
140*11963SAfshin.Ardakani@Sun.COM /*
141*11963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_handle_t
142*11963SAfshin.Ardakani@Sun.COM  * handle to zfs library and dataset
143*11963SAfshin.Ardakani@Sun.COM  */
144*11963SAfshin.Ardakani@Sun.COM typedef struct smb_quota_zfs_handle {
145*11963SAfshin.Ardakani@Sun.COM 	libzfs_handle_t *z_lib;
146*11963SAfshin.Ardakani@Sun.COM 	zfs_handle_t *z_fs;
147*11963SAfshin.Ardakani@Sun.COM } smb_quota_zfs_handle_t;
148*11963SAfshin.Ardakani@Sun.COM 
149*11963SAfshin.Ardakani@Sun.COM /*
150*11963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_arg_t
151*11963SAfshin.Ardakani@Sun.COM  * arg passed to zfs callback when querying quota properties
152*11963SAfshin.Ardakani@Sun.COM  */
153*11963SAfshin.Ardakani@Sun.COM typedef struct smb_quota_zfs_arg {
154*11963SAfshin.Ardakani@Sun.COM 	zfs_userquota_prop_t qa_prop;
155*11963SAfshin.Ardakani@Sun.COM 	avl_tree_t *qa_avl;
156*11963SAfshin.Ardakani@Sun.COM } smb_quota_zfs_arg_t;
157*11963SAfshin.Ardakani@Sun.COM 
158*11963SAfshin.Ardakani@Sun.COM static void smb_quota_add_ctrldir(const char *);
159*11963SAfshin.Ardakani@Sun.COM static void smb_quota_remove_ctrldir(const char *);
160*11963SAfshin.Ardakani@Sun.COM 
161*11963SAfshin.Ardakani@Sun.COM static smb_quota_tree_t *smb_quota_tree_create(const char *);
162*11963SAfshin.Ardakani@Sun.COM static void smb_quota_tree_delete(smb_quota_tree_t *);
163*11963SAfshin.Ardakani@Sun.COM 
164*11963SAfshin.Ardakani@Sun.COM static smb_quota_tree_t *smb_quota_tree_lookup(const char *);
165*11963SAfshin.Ardakani@Sun.COM static void smb_quota_tree_release(smb_quota_tree_t *);
166*11963SAfshin.Ardakani@Sun.COM static boolean_t smb_quota_tree_match(smb_quota_tree_t *, const char *);
167*11963SAfshin.Ardakani@Sun.COM static int smb_quota_sid_cmp(const void *, const void *);
168*11963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_tree_populate(smb_quota_tree_t *);
169*11963SAfshin.Ardakani@Sun.COM static boolean_t smb_quota_tree_expired(smb_quota_tree_t *);
170*11963SAfshin.Ardakani@Sun.COM static void smb_quota_tree_set_expired(smb_quota_tree_t *);
171*11963SAfshin.Ardakani@Sun.COM 
172*11963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_zfs_init(const char *, smb_quota_zfs_handle_t *);
173*11963SAfshin.Ardakani@Sun.COM static void smb_quota_zfs_fini(smb_quota_zfs_handle_t *);
174*11963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_zfs_get_quotas(smb_quota_tree_t *);
175*11963SAfshin.Ardakani@Sun.COM static int smb_quota_zfs_callback(void *, const char *, uid_t, uint64_t);
176*11963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_zfs_set_quotas(smb_quota_tree_t *, smb_quota_set_t *);
177*11963SAfshin.Ardakani@Sun.COM static int smb_quota_sidstr(uint32_t, zfs_userquota_prop_t, char *);
178*11963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_sidtype(smb_quota_tree_t *, char *);
179*11963SAfshin.Ardakani@Sun.COM static int smb_quota_getid(char *, uint32_t, uint32_t *);
180*11963SAfshin.Ardakani@Sun.COM 
181*11963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_query_all(smb_quota_tree_t *,
182*11963SAfshin.Ardakani@Sun.COM     smb_quota_query_t *, smb_quota_response_t *);
183*11963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_query_list(smb_quota_tree_t *,
184*11963SAfshin.Ardakani@Sun.COM     smb_quota_query_t *, smb_quota_response_t *);
185*11963SAfshin.Ardakani@Sun.COM 
186*11963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_REFRESH		2
187*11963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CMD_LENGTH		21
188*11963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CMD_STR_LENGTH	SMB_SID_STRSZ+SMB_QUOTA_CMD_LENGTH
189*11963SAfshin.Ardakani@Sun.COM 
190*11963SAfshin.Ardakani@Sun.COM /*
191*11963SAfshin.Ardakani@Sun.COM  * In order to display the quota properties tab, windows clients
192*11963SAfshin.Ardakani@Sun.COM  * check for the existence of the quota control file.
193*11963SAfshin.Ardakani@Sun.COM  */
194*11963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CNTRL_DIR		".$EXTEND"
195*11963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CNTRL_FILE		"$QUOTA"
196*11963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CNTRL_INDEX_XATTR	"SUNWsmb:$Q:$INDEX_ALLOCATION"
197*11963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CNTRL_PERM		"everyone@:rwpaARWc::allow"
198*11963SAfshin.Ardakani@Sun.COM 
199*11963SAfshin.Ardakani@Sun.COM /*
200*11963SAfshin.Ardakani@Sun.COM  * smb_quota_init
201*11963SAfshin.Ardakani@Sun.COM  * Initialize the list to hold the quota trees.
202*11963SAfshin.Ardakani@Sun.COM  */
203*11963SAfshin.Ardakani@Sun.COM void
204*11963SAfshin.Ardakani@Sun.COM smb_quota_init(void)
205*11963SAfshin.Ardakani@Sun.COM {
206*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
207*11963SAfshin.Ardakani@Sun.COM 	if (!smb_quota_list_init) {
208*11963SAfshin.Ardakani@Sun.COM 		list_create(&smb_quota_fs_list, sizeof (smb_quota_tree_t),
209*11963SAfshin.Ardakani@Sun.COM 		    offsetof(smb_quota_tree_t, qt_node));
210*11963SAfshin.Ardakani@Sun.COM 		smb_quota_list_init = B_TRUE;
211*11963SAfshin.Ardakani@Sun.COM 		smb_quota_shutdown = B_FALSE;
212*11963SAfshin.Ardakani@Sun.COM 	}
213*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
214*11963SAfshin.Ardakani@Sun.COM }
215*11963SAfshin.Ardakani@Sun.COM 
216*11963SAfshin.Ardakani@Sun.COM /*
217*11963SAfshin.Ardakani@Sun.COM  * smb_quota_fini
218*11963SAfshin.Ardakani@Sun.COM  *
219*11963SAfshin.Ardakani@Sun.COM  * Wait for each quota tree to not be in use (qt_refcnt == 1)
220*11963SAfshin.Ardakani@Sun.COM  * then remove it from the list and delete it.
221*11963SAfshin.Ardakani@Sun.COM  */
222*11963SAfshin.Ardakani@Sun.COM void
223*11963SAfshin.Ardakani@Sun.COM smb_quota_fini(void)
224*11963SAfshin.Ardakani@Sun.COM {
225*11963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree, *qtree_next;
226*11963SAfshin.Ardakani@Sun.COM 	boolean_t remove;
227*11963SAfshin.Ardakani@Sun.COM 	struct timespec tswait;
228*11963SAfshin.Ardakani@Sun.COM 	tswait.tv_sec = smb_quota_fini_timeout;
229*11963SAfshin.Ardakani@Sun.COM 	tswait.tv_nsec = 0;
230*11963SAfshin.Ardakani@Sun.COM 
231*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
232*11963SAfshin.Ardakani@Sun.COM 	smb_quota_shutdown = B_TRUE;
233*11963SAfshin.Ardakani@Sun.COM 
234*11963SAfshin.Ardakani@Sun.COM 	if (!smb_quota_list_init) {
235*11963SAfshin.Ardakani@Sun.COM 		(void) mutex_unlock(&smb_quota_list_mutex);
236*11963SAfshin.Ardakani@Sun.COM 		return;
237*11963SAfshin.Ardakani@Sun.COM 	}
238*11963SAfshin.Ardakani@Sun.COM 
239*11963SAfshin.Ardakani@Sun.COM 	(void) cond_broadcast(&smb_quota_list_condvar);
240*11963SAfshin.Ardakani@Sun.COM 
241*11963SAfshin.Ardakani@Sun.COM 	while (smb_quota_tree_cnt != 0) {
242*11963SAfshin.Ardakani@Sun.COM 		qtree = list_head(&smb_quota_fs_list);
243*11963SAfshin.Ardakani@Sun.COM 		while (qtree != NULL) {
244*11963SAfshin.Ardakani@Sun.COM 			qtree_next = list_next(&smb_quota_fs_list, qtree);
245*11963SAfshin.Ardakani@Sun.COM 
246*11963SAfshin.Ardakani@Sun.COM 			(void) mutex_lock(&qtree->qt_mutex);
247*11963SAfshin.Ardakani@Sun.COM 			remove = (qtree->qt_refcnt == 1);
248*11963SAfshin.Ardakani@Sun.COM 			if (remove) {
249*11963SAfshin.Ardakani@Sun.COM 				list_remove(&smb_quota_fs_list, qtree);
250*11963SAfshin.Ardakani@Sun.COM 				--qtree->qt_refcnt;
251*11963SAfshin.Ardakani@Sun.COM 			}
252*11963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&qtree->qt_mutex);
253*11963SAfshin.Ardakani@Sun.COM 
254*11963SAfshin.Ardakani@Sun.COM 			if (remove)
255*11963SAfshin.Ardakani@Sun.COM 				smb_quota_tree_delete(qtree);
256*11963SAfshin.Ardakani@Sun.COM 
257*11963SAfshin.Ardakani@Sun.COM 			qtree = qtree_next;
258*11963SAfshin.Ardakani@Sun.COM 		}
259*11963SAfshin.Ardakani@Sun.COM 
260*11963SAfshin.Ardakani@Sun.COM 		if (smb_quota_tree_cnt != 0) {
261*11963SAfshin.Ardakani@Sun.COM 			if (cond_reltimedwait(&smb_quota_list_condvar,
262*11963SAfshin.Ardakani@Sun.COM 			    &smb_quota_list_mutex, &tswait) == ETIME) {
263*11963SAfshin.Ardakani@Sun.COM 				syslog(LOG_WARNING,
264*11963SAfshin.Ardakani@Sun.COM 				    "quota shutdown timeout expired");
265*11963SAfshin.Ardakani@Sun.COM 				break;
266*11963SAfshin.Ardakani@Sun.COM 			}
267*11963SAfshin.Ardakani@Sun.COM 		}
268*11963SAfshin.Ardakani@Sun.COM 	}
269*11963SAfshin.Ardakani@Sun.COM 
270*11963SAfshin.Ardakani@Sun.COM 	if (list_is_empty(&smb_quota_fs_list)) {
271*11963SAfshin.Ardakani@Sun.COM 		list_destroy(&smb_quota_fs_list);
272*11963SAfshin.Ardakani@Sun.COM 		smb_quota_list_init = B_FALSE;
273*11963SAfshin.Ardakani@Sun.COM 	}
274*11963SAfshin.Ardakani@Sun.COM 
275*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
276*11963SAfshin.Ardakani@Sun.COM }
277*11963SAfshin.Ardakani@Sun.COM 
278*11963SAfshin.Ardakani@Sun.COM /*
279*11963SAfshin.Ardakani@Sun.COM  * smb_quota_add_fs
280*11963SAfshin.Ardakani@Sun.COM  *
281*11963SAfshin.Ardakani@Sun.COM  * If there is not a quota tree representing the specified path,
282*11963SAfshin.Ardakani@Sun.COM  * create one and add it to the list.
283*11963SAfshin.Ardakani@Sun.COM  */
284*11963SAfshin.Ardakani@Sun.COM void
285*11963SAfshin.Ardakani@Sun.COM smb_quota_add_fs(const char *path)
286*11963SAfshin.Ardakani@Sun.COM {
287*11963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree;
288*11963SAfshin.Ardakani@Sun.COM 
289*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
290*11963SAfshin.Ardakani@Sun.COM 
291*11963SAfshin.Ardakani@Sun.COM 	if (!smb_quota_list_init || smb_quota_shutdown) {
292*11963SAfshin.Ardakani@Sun.COM 		(void) mutex_unlock(&smb_quota_list_mutex);
293*11963SAfshin.Ardakani@Sun.COM 		return;
294*11963SAfshin.Ardakani@Sun.COM 	}
295*11963SAfshin.Ardakani@Sun.COM 
296*11963SAfshin.Ardakani@Sun.COM 	qtree = list_head(&smb_quota_fs_list);
297*11963SAfshin.Ardakani@Sun.COM 	while (qtree != NULL) {
298*11963SAfshin.Ardakani@Sun.COM 		if (smb_quota_tree_match(qtree, path)) {
299*11963SAfshin.Ardakani@Sun.COM 			(void) mutex_lock(&qtree->qt_mutex);
300*11963SAfshin.Ardakani@Sun.COM 			++qtree->qt_sharecnt;
301*11963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&qtree->qt_mutex);
302*11963SAfshin.Ardakani@Sun.COM 			break;
303*11963SAfshin.Ardakani@Sun.COM 		}
304*11963SAfshin.Ardakani@Sun.COM 		qtree = list_next(&smb_quota_fs_list, qtree);
305*11963SAfshin.Ardakani@Sun.COM 	}
306*11963SAfshin.Ardakani@Sun.COM 
307*11963SAfshin.Ardakani@Sun.COM 	if (qtree == NULL) {
308*11963SAfshin.Ardakani@Sun.COM 		qtree = smb_quota_tree_create(path);
309*11963SAfshin.Ardakani@Sun.COM 		if (qtree)
310*11963SAfshin.Ardakani@Sun.COM 			list_insert_head(&smb_quota_fs_list, (void *)qtree);
311*11963SAfshin.Ardakani@Sun.COM 	}
312*11963SAfshin.Ardakani@Sun.COM 
313*11963SAfshin.Ardakani@Sun.COM 	if (qtree)
314*11963SAfshin.Ardakani@Sun.COM 		smb_quota_add_ctrldir(path);
315*11963SAfshin.Ardakani@Sun.COM 
316*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
317*11963SAfshin.Ardakani@Sun.COM }
318*11963SAfshin.Ardakani@Sun.COM 
319*11963SAfshin.Ardakani@Sun.COM /*
320*11963SAfshin.Ardakani@Sun.COM  * smb_quota_remove_fs
321*11963SAfshin.Ardakani@Sun.COM  *
322*11963SAfshin.Ardakani@Sun.COM  * If this is the last share that the quota tree represents
323*11963SAfshin.Ardakani@Sun.COM  * (qtree->qt_sharecnt == 0) remove the qtree from the list.
324*11963SAfshin.Ardakani@Sun.COM  * The qtree will be deleted if/when there is nobody using it
325*11963SAfshin.Ardakani@Sun.COM  * (qtree->qt_refcnt == 0).
326*11963SAfshin.Ardakani@Sun.COM  */
327*11963SAfshin.Ardakani@Sun.COM void
328*11963SAfshin.Ardakani@Sun.COM smb_quota_remove_fs(const char *path)
329*11963SAfshin.Ardakani@Sun.COM {
330*11963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree;
331*11963SAfshin.Ardakani@Sun.COM 	boolean_t delete = B_FALSE;
332*11963SAfshin.Ardakani@Sun.COM 
333*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
334*11963SAfshin.Ardakani@Sun.COM 
335*11963SAfshin.Ardakani@Sun.COM 	if (!smb_quota_list_init || smb_quota_shutdown) {
336*11963SAfshin.Ardakani@Sun.COM 		(void) mutex_unlock(&smb_quota_list_mutex);
337*11963SAfshin.Ardakani@Sun.COM 		return;
338*11963SAfshin.Ardakani@Sun.COM 	}
339*11963SAfshin.Ardakani@Sun.COM 
340*11963SAfshin.Ardakani@Sun.COM 	qtree = list_head(&smb_quota_fs_list);
341*11963SAfshin.Ardakani@Sun.COM 	while (qtree != NULL) {
342*11963SAfshin.Ardakani@Sun.COM 		assert(qtree->qt_refcnt > 0);
343*11963SAfshin.Ardakani@Sun.COM 		if (smb_quota_tree_match(qtree, path)) {
344*11963SAfshin.Ardakani@Sun.COM 			(void) mutex_lock(&qtree->qt_mutex);
345*11963SAfshin.Ardakani@Sun.COM 			--qtree->qt_sharecnt;
346*11963SAfshin.Ardakani@Sun.COM 			if (qtree->qt_sharecnt == 0) {
347*11963SAfshin.Ardakani@Sun.COM 				list_remove(&smb_quota_fs_list, (void *)qtree);
348*11963SAfshin.Ardakani@Sun.COM 				smb_quota_remove_ctrldir(qtree->qt_path);
349*11963SAfshin.Ardakani@Sun.COM 				--(qtree->qt_refcnt);
350*11963SAfshin.Ardakani@Sun.COM 				delete = (qtree->qt_refcnt == 0);
351*11963SAfshin.Ardakani@Sun.COM 			}
352*11963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&qtree->qt_mutex);
353*11963SAfshin.Ardakani@Sun.COM 			if (delete)
354*11963SAfshin.Ardakani@Sun.COM 				smb_quota_tree_delete(qtree);
355*11963SAfshin.Ardakani@Sun.COM 			break;
356*11963SAfshin.Ardakani@Sun.COM 		}
357*11963SAfshin.Ardakani@Sun.COM 		qtree = list_next(&smb_quota_fs_list, qtree);
358*11963SAfshin.Ardakani@Sun.COM 	}
359*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
360*11963SAfshin.Ardakani@Sun.COM }
361*11963SAfshin.Ardakani@Sun.COM 
362*11963SAfshin.Ardakani@Sun.COM /*
363*11963SAfshin.Ardakani@Sun.COM  * smb_quota_query
364*11963SAfshin.Ardakani@Sun.COM  *
365*11963SAfshin.Ardakani@Sun.COM  * Get list of user/group quotas entries.
366*11963SAfshin.Ardakani@Sun.COM  * Request->qq_query_op determines whether to get quota entries
367*11963SAfshin.Ardakani@Sun.COM  * for the specified SIDs (smb_quota_query_list) OR to get all
368*11963SAfshin.Ardakani@Sun.COM  * quota entries, optionally starting at a specified SID.
369*11963SAfshin.Ardakani@Sun.COM  *
370*11963SAfshin.Ardakani@Sun.COM  * Returns NT_STATUS codes.
371*11963SAfshin.Ardakani@Sun.COM  */
372*11963SAfshin.Ardakani@Sun.COM uint32_t
373*11963SAfshin.Ardakani@Sun.COM smb_quota_query(smb_quota_query_t *request, smb_quota_response_t *reply)
374*11963SAfshin.Ardakani@Sun.COM {
375*11963SAfshin.Ardakani@Sun.COM 	uint32_t status;
376*11963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree;
377*11963SAfshin.Ardakani@Sun.COM 	smb_quota_query_op_t query_op = request->qq_query_op;
378*11963SAfshin.Ardakani@Sun.COM 
379*11963SAfshin.Ardakani@Sun.COM 	list_create(&reply->qr_quota_list, sizeof (smb_quota_t),
380*11963SAfshin.Ardakani@Sun.COM 	    offsetof(smb_quota_t, q_list_node));
381*11963SAfshin.Ardakani@Sun.COM 
382*11963SAfshin.Ardakani@Sun.COM 	qtree = smb_quota_tree_lookup(request->qq_root_path);
383*11963SAfshin.Ardakani@Sun.COM 	if (qtree == NULL)
384*11963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_INVALID_PARAMETER);
385*11963SAfshin.Ardakani@Sun.COM 
386*11963SAfshin.Ardakani@Sun.COM 	/* If NOT resuming a previous query all, refresh qtree if required */
387*11963SAfshin.Ardakani@Sun.COM 	if ((query_op != SMB_QUOTA_QUERY_ALL) || (request->qq_restart)) {
388*11963SAfshin.Ardakani@Sun.COM 		status = smb_quota_tree_populate(qtree);
389*11963SAfshin.Ardakani@Sun.COM 		if (status != NT_STATUS_SUCCESS) {
390*11963SAfshin.Ardakani@Sun.COM 			smb_quota_tree_release(qtree);
391*11963SAfshin.Ardakani@Sun.COM 			return (status);
392*11963SAfshin.Ardakani@Sun.COM 		}
393*11963SAfshin.Ardakani@Sun.COM 	}
394*11963SAfshin.Ardakani@Sun.COM 
395*11963SAfshin.Ardakani@Sun.COM 	switch (query_op) {
396*11963SAfshin.Ardakani@Sun.COM 	case SMB_QUOTA_QUERY_SIDLIST:
397*11963SAfshin.Ardakani@Sun.COM 		status = smb_quota_query_list(qtree, request, reply);
398*11963SAfshin.Ardakani@Sun.COM 		break;
399*11963SAfshin.Ardakani@Sun.COM 	case SMB_QUOTA_QUERY_STARTSID:
400*11963SAfshin.Ardakani@Sun.COM 	case SMB_QUOTA_QUERY_ALL:
401*11963SAfshin.Ardakani@Sun.COM 		status = smb_quota_query_all(qtree, request, reply);
402*11963SAfshin.Ardakani@Sun.COM 		break;
403*11963SAfshin.Ardakani@Sun.COM 	case SMB_QUOTA_QUERY_INVALID_OP:
404*11963SAfshin.Ardakani@Sun.COM 	default:
405*11963SAfshin.Ardakani@Sun.COM 		status = NT_STATUS_INVALID_PARAMETER;
406*11963SAfshin.Ardakani@Sun.COM 		break;
407*11963SAfshin.Ardakani@Sun.COM 	}
408*11963SAfshin.Ardakani@Sun.COM 
409*11963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_release(qtree);
410*11963SAfshin.Ardakani@Sun.COM 
411*11963SAfshin.Ardakani@Sun.COM 	return (status);
412*11963SAfshin.Ardakani@Sun.COM }
413*11963SAfshin.Ardakani@Sun.COM 
414*11963SAfshin.Ardakani@Sun.COM /*
415*11963SAfshin.Ardakani@Sun.COM  * smb_quota_set
416*11963SAfshin.Ardakani@Sun.COM  *
417*11963SAfshin.Ardakani@Sun.COM  * Set the list of quota entries.
418*11963SAfshin.Ardakani@Sun.COM  */
419*11963SAfshin.Ardakani@Sun.COM uint32_t
420*11963SAfshin.Ardakani@Sun.COM smb_quota_set(smb_quota_set_t *request)
421*11963SAfshin.Ardakani@Sun.COM {
422*11963SAfshin.Ardakani@Sun.COM 	uint32_t status;
423*11963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree;
424*11963SAfshin.Ardakani@Sun.COM 
425*11963SAfshin.Ardakani@Sun.COM 	qtree = smb_quota_tree_lookup(request->qs_root_path);
426*11963SAfshin.Ardakani@Sun.COM 	if (qtree == NULL)
427*11963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_INVALID_PARAMETER);
428*11963SAfshin.Ardakani@Sun.COM 
429*11963SAfshin.Ardakani@Sun.COM 	status = smb_quota_zfs_set_quotas(qtree, request);
430*11963SAfshin.Ardakani@Sun.COM 
431*11963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_set_expired(qtree);
432*11963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_release(qtree);
433*11963SAfshin.Ardakani@Sun.COM 
434*11963SAfshin.Ardakani@Sun.COM 	return (status);
435*11963SAfshin.Ardakani@Sun.COM }
436*11963SAfshin.Ardakani@Sun.COM 
437*11963SAfshin.Ardakani@Sun.COM /*
438*11963SAfshin.Ardakani@Sun.COM  * smb_quota_free
439*11963SAfshin.Ardakani@Sun.COM  *
440*11963SAfshin.Ardakani@Sun.COM  * This method frees quota entries.
441*11963SAfshin.Ardakani@Sun.COM  */
442*11963SAfshin.Ardakani@Sun.COM void
443*11963SAfshin.Ardakani@Sun.COM smb_quota_free(smb_quota_response_t *reply)
444*11963SAfshin.Ardakani@Sun.COM {
445*11963SAfshin.Ardakani@Sun.COM 	list_t *list = &reply->qr_quota_list;
446*11963SAfshin.Ardakani@Sun.COM 	smb_quota_t *quota;
447*11963SAfshin.Ardakani@Sun.COM 
448*11963SAfshin.Ardakani@Sun.COM 	while ((quota = list_head(list)) != NULL) {
449*11963SAfshin.Ardakani@Sun.COM 		list_remove(list, quota);
450*11963SAfshin.Ardakani@Sun.COM 		free(quota);
451*11963SAfshin.Ardakani@Sun.COM 	}
452*11963SAfshin.Ardakani@Sun.COM 
453*11963SAfshin.Ardakani@Sun.COM 	list_destroy(list);
454*11963SAfshin.Ardakani@Sun.COM }
455*11963SAfshin.Ardakani@Sun.COM 
456*11963SAfshin.Ardakani@Sun.COM /*
457*11963SAfshin.Ardakani@Sun.COM  * smb_quota_query_all
458*11963SAfshin.Ardakani@Sun.COM  *
459*11963SAfshin.Ardakani@Sun.COM  * Query quotas sequentially from tree, optionally starting at a
460*11963SAfshin.Ardakani@Sun.COM  * specified sid. If request->qq_single is TRUE only one quota
461*11963SAfshin.Ardakani@Sun.COM  * should be returned, otherwise up to request->qq_max_quota
462*11963SAfshin.Ardakani@Sun.COM  * should be returned.
463*11963SAfshin.Ardakani@Sun.COM  *
464*11963SAfshin.Ardakani@Sun.COM  * SMB_QUOTA_QUERY_STARTSID
465*11963SAfshin.Ardakani@Sun.COM  * The query should start at the startsid, the first sid in
466*11963SAfshin.Ardakani@Sun.COM  * request->qq_sid_list.
467*11963SAfshin.Ardakani@Sun.COM  *
468*11963SAfshin.Ardakani@Sun.COM  * SMQ_QUOTA_QUERY_ALL
469*11963SAfshin.Ardakani@Sun.COM  * If request->qq_restart the query should restart at the start
470*11963SAfshin.Ardakani@Sun.COM  * of the avl tree. Otherwise the first sid in request->qq_sid_list
471*11963SAfshin.Ardakani@Sun.COM  * is the resume sid and the query should start at the tree entry
472*11963SAfshin.Ardakani@Sun.COM  * after the one it refers to.
473*11963SAfshin.Ardakani@Sun.COM  *
474*11963SAfshin.Ardakani@Sun.COM  * Returns NT_STATUS codes.
475*11963SAfshin.Ardakani@Sun.COM  */
476*11963SAfshin.Ardakani@Sun.COM static uint32_t
477*11963SAfshin.Ardakani@Sun.COM smb_quota_query_all(smb_quota_tree_t *qtree, smb_quota_query_t *request,
478*11963SAfshin.Ardakani@Sun.COM     smb_quota_response_t *reply)
479*11963SAfshin.Ardakani@Sun.COM {
480*11963SAfshin.Ardakani@Sun.COM 	avl_tree_t *avl_tree = &qtree->qt_avl;
481*11963SAfshin.Ardakani@Sun.COM 	avl_index_t where;
482*11963SAfshin.Ardakani@Sun.COM 	list_t *sid_list, *quota_list;
483*11963SAfshin.Ardakani@Sun.COM 	smb_quota_sid_t *sid;
484*11963SAfshin.Ardakani@Sun.COM 	smb_quota_t *quota, *quotal, key;
485*11963SAfshin.Ardakani@Sun.COM 	uint32_t count;
486*11963SAfshin.Ardakani@Sun.COM 
487*11963SAfshin.Ardakani@Sun.COM 	/* find starting sid */
488*11963SAfshin.Ardakani@Sun.COM 	if (request->qq_query_op == SMB_QUOTA_QUERY_STARTSID) {
489*11963SAfshin.Ardakani@Sun.COM 		sid_list = &request->qq_sid_list;
490*11963SAfshin.Ardakani@Sun.COM 		sid = list_head(sid_list);
491*11963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(key.q_sidstr, sid->qs_sidstr, SMB_SID_STRSZ);
492*11963SAfshin.Ardakani@Sun.COM 		quota = avl_find(avl_tree, &key, &where);
493*11963SAfshin.Ardakani@Sun.COM 		if (quota == NULL)
494*11963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_INVALID_PARAMETER);
495*11963SAfshin.Ardakani@Sun.COM 	} else if (request->qq_restart) {
496*11963SAfshin.Ardakani@Sun.COM 		quota = avl_first(avl_tree);
497*11963SAfshin.Ardakani@Sun.COM 		if (quota == NULL)
498*11963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_NO_MORE_DATA);
499*11963SAfshin.Ardakani@Sun.COM 	} else {
500*11963SAfshin.Ardakani@Sun.COM 		sid_list = &request->qq_sid_list;
501*11963SAfshin.Ardakani@Sun.COM 		sid = list_head(sid_list);
502*11963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(key.q_sidstr, sid->qs_sidstr, SMB_SID_STRSZ);
503*11963SAfshin.Ardakani@Sun.COM 		quota = avl_find(avl_tree, &key, &where);
504*11963SAfshin.Ardakani@Sun.COM 		if (quota == NULL)
505*11963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_INVALID_PARAMETER);
506*11963SAfshin.Ardakani@Sun.COM 		quota = AVL_NEXT(avl_tree, quota);
507*11963SAfshin.Ardakani@Sun.COM 		if (quota == NULL)
508*11963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_NO_MORE_DATA);
509*11963SAfshin.Ardakani@Sun.COM 	}
510*11963SAfshin.Ardakani@Sun.COM 
511*11963SAfshin.Ardakani@Sun.COM 	if ((request->qq_single) && (request->qq_max_quota > 1))
512*11963SAfshin.Ardakani@Sun.COM 		request->qq_max_quota = 1;
513*11963SAfshin.Ardakani@Sun.COM 
514*11963SAfshin.Ardakani@Sun.COM 	quota_list = &reply->qr_quota_list;
515*11963SAfshin.Ardakani@Sun.COM 	count = 0;
516*11963SAfshin.Ardakani@Sun.COM 	while (quota) {
517*11963SAfshin.Ardakani@Sun.COM 		if (count >= request->qq_max_quota)
518*11963SAfshin.Ardakani@Sun.COM 			break;
519*11963SAfshin.Ardakani@Sun.COM 
520*11963SAfshin.Ardakani@Sun.COM 		quotal = malloc(sizeof (smb_quota_t));
521*11963SAfshin.Ardakani@Sun.COM 		if (quotal == NULL)
522*11963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_NO_MEMORY);
523*11963SAfshin.Ardakani@Sun.COM 		bcopy(quota, quotal, sizeof (smb_quota_t));
524*11963SAfshin.Ardakani@Sun.COM 
525*11963SAfshin.Ardakani@Sun.COM 		list_insert_tail(quota_list, quotal);
526*11963SAfshin.Ardakani@Sun.COM 		++count;
527*11963SAfshin.Ardakani@Sun.COM 
528*11963SAfshin.Ardakani@Sun.COM 		quota = AVL_NEXT(avl_tree, quota);
529*11963SAfshin.Ardakani@Sun.COM 	}
530*11963SAfshin.Ardakani@Sun.COM 
531*11963SAfshin.Ardakani@Sun.COM 	return (NT_STATUS_SUCCESS);
532*11963SAfshin.Ardakani@Sun.COM }
533*11963SAfshin.Ardakani@Sun.COM 
534*11963SAfshin.Ardakani@Sun.COM /*
535*11963SAfshin.Ardakani@Sun.COM  * smb_quota_query_list
536*11963SAfshin.Ardakani@Sun.COM  *
537*11963SAfshin.Ardakani@Sun.COM  * Iterate through request sid list querying the avl tree for each.
538*11963SAfshin.Ardakani@Sun.COM  * Insert an entry in the reply quota list for each sid.
539*11963SAfshin.Ardakani@Sun.COM  * For any sid that cannot be found in the avl tree, the reply
540*11963SAfshin.Ardakani@Sun.COM  * quota list entry should contain zeros.
541*11963SAfshin.Ardakani@Sun.COM  */
542*11963SAfshin.Ardakani@Sun.COM static uint32_t
543*11963SAfshin.Ardakani@Sun.COM smb_quota_query_list(smb_quota_tree_t *qtree, smb_quota_query_t *request,
544*11963SAfshin.Ardakani@Sun.COM     smb_quota_response_t *reply)
545*11963SAfshin.Ardakani@Sun.COM {
546*11963SAfshin.Ardakani@Sun.COM 	avl_tree_t *avl_tree = &qtree->qt_avl;
547*11963SAfshin.Ardakani@Sun.COM 	avl_index_t where;
548*11963SAfshin.Ardakani@Sun.COM 	list_t *sid_list, *quota_list;
549*11963SAfshin.Ardakani@Sun.COM 	smb_quota_sid_t *sid;
550*11963SAfshin.Ardakani@Sun.COM 	smb_quota_t *quota, *quotal, key;
551*11963SAfshin.Ardakani@Sun.COM 
552*11963SAfshin.Ardakani@Sun.COM 	quota_list = &reply->qr_quota_list;
553*11963SAfshin.Ardakani@Sun.COM 	sid_list = &request->qq_sid_list;
554*11963SAfshin.Ardakani@Sun.COM 	sid = list_head(sid_list);
555*11963SAfshin.Ardakani@Sun.COM 	while (sid) {
556*11963SAfshin.Ardakani@Sun.COM 		quotal = malloc(sizeof (smb_quota_t));
557*11963SAfshin.Ardakani@Sun.COM 		if (quotal == NULL)
558*11963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_NO_MEMORY);
559*11963SAfshin.Ardakani@Sun.COM 
560*11963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(key.q_sidstr, sid->qs_sidstr, SMB_SID_STRSZ);
561*11963SAfshin.Ardakani@Sun.COM 		quota = avl_find(avl_tree, &key, &where);
562*11963SAfshin.Ardakani@Sun.COM 		if (quota) {
563*11963SAfshin.Ardakani@Sun.COM 			bcopy(quota, quotal, sizeof (smb_quota_t));
564*11963SAfshin.Ardakani@Sun.COM 		} else {
565*11963SAfshin.Ardakani@Sun.COM 			bzero(quotal, sizeof (smb_quota_t));
566*11963SAfshin.Ardakani@Sun.COM 			(void) strlcpy(quotal->q_sidstr, sid->qs_sidstr,
567*11963SAfshin.Ardakani@Sun.COM 			    SMB_SID_STRSZ);
568*11963SAfshin.Ardakani@Sun.COM 		}
569*11963SAfshin.Ardakani@Sun.COM 
570*11963SAfshin.Ardakani@Sun.COM 		list_insert_tail(quota_list, quotal);
571*11963SAfshin.Ardakani@Sun.COM 		sid = list_next(sid_list, sid);
572*11963SAfshin.Ardakani@Sun.COM 	}
573*11963SAfshin.Ardakani@Sun.COM 
574*11963SAfshin.Ardakani@Sun.COM 	return (NT_STATUS_SUCCESS);
575*11963SAfshin.Ardakani@Sun.COM }
576*11963SAfshin.Ardakani@Sun.COM 
577*11963SAfshin.Ardakani@Sun.COM /*
578*11963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_set_quotas
579*11963SAfshin.Ardakani@Sun.COM  *
580*11963SAfshin.Ardakani@Sun.COM  * This method sets the list of quota entries.
581*11963SAfshin.Ardakani@Sun.COM  *
582*11963SAfshin.Ardakani@Sun.COM  * A quota list or threshold value of SMB_QUOTA_UNLIMITED means that
583*11963SAfshin.Ardakani@Sun.COM  * the user / group does not have a quota limit. In ZFS this maps to
584*11963SAfshin.Ardakani@Sun.COM  * 0 (none).
585*11963SAfshin.Ardakani@Sun.COM  * A quota list or threshold value of (SMB_QUOTA_UNLIMITED - 1) means
586*11963SAfshin.Ardakani@Sun.COM  * that the user / group quota should be removed. In ZFS this maps to
587*11963SAfshin.Ardakani@Sun.COM  * 0 (none).
588*11963SAfshin.Ardakani@Sun.COM  */
589*11963SAfshin.Ardakani@Sun.COM static uint32_t
590*11963SAfshin.Ardakani@Sun.COM smb_quota_zfs_set_quotas(smb_quota_tree_t *qtree, smb_quota_set_t *request)
591*11963SAfshin.Ardakani@Sun.COM {
592*11963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_handle_t zfs_hdl;
593*11963SAfshin.Ardakani@Sun.COM 	char *typestr, qsetstr[SMB_QUOTA_CMD_STR_LENGTH];
594*11963SAfshin.Ardakani@Sun.COM 	char qlimit[SMB_QUOTA_CMD_LENGTH];
595*11963SAfshin.Ardakani@Sun.COM 	list_t *quota_list;
596*11963SAfshin.Ardakani@Sun.COM 	smb_quota_t *quota;
597*11963SAfshin.Ardakani@Sun.COM 	uint32_t id;
598*11963SAfshin.Ardakani@Sun.COM 	uint32_t status = NT_STATUS_SUCCESS;
599*11963SAfshin.Ardakani@Sun.COM 	uint32_t sidtype;
600*11963SAfshin.Ardakani@Sun.COM 
601*11963SAfshin.Ardakani@Sun.COM 	status = smb_quota_zfs_init(request->qs_root_path, &zfs_hdl);
602*11963SAfshin.Ardakani@Sun.COM 	if (status != NT_STATUS_SUCCESS)
603*11963SAfshin.Ardakani@Sun.COM 		return (status);
604*11963SAfshin.Ardakani@Sun.COM 
605*11963SAfshin.Ardakani@Sun.COM 	quota_list = &request->qs_quota_list;
606*11963SAfshin.Ardakani@Sun.COM 	quota = list_head(quota_list);
607*11963SAfshin.Ardakani@Sun.COM 
608*11963SAfshin.Ardakani@Sun.COM 	while (quota) {
609*11963SAfshin.Ardakani@Sun.COM 		if ((quota->q_limit == SMB_QUOTA_UNLIMITED) ||
610*11963SAfshin.Ardakani@Sun.COM 		    (quota->q_limit == (SMB_QUOTA_UNLIMITED - 1))) {
611*11963SAfshin.Ardakani@Sun.COM 			quota->q_limit = 0;
612*11963SAfshin.Ardakani@Sun.COM 		}
613*11963SAfshin.Ardakani@Sun.COM 		(void) snprintf(qlimit, SMB_QUOTA_CMD_LENGTH, "%llu",
614*11963SAfshin.Ardakani@Sun.COM 		    quota->q_limit);
615*11963SAfshin.Ardakani@Sun.COM 
616*11963SAfshin.Ardakani@Sun.COM 		sidtype = smb_quota_sidtype(qtree, quota->q_sidstr);
617*11963SAfshin.Ardakani@Sun.COM 		switch (sidtype) {
618*11963SAfshin.Ardakani@Sun.COM 		case SidTypeUser:
619*11963SAfshin.Ardakani@Sun.COM 			typestr = "userquota";
620*11963SAfshin.Ardakani@Sun.COM 			break;
621*11963SAfshin.Ardakani@Sun.COM 		case SidTypeWellKnownGroup:
622*11963SAfshin.Ardakani@Sun.COM 		case SidTypeGroup:
623*11963SAfshin.Ardakani@Sun.COM 		case SidTypeAlias:
624*11963SAfshin.Ardakani@Sun.COM 			typestr = "groupquota";
625*11963SAfshin.Ardakani@Sun.COM 			break;
626*11963SAfshin.Ardakani@Sun.COM 		default:
627*11963SAfshin.Ardakani@Sun.COM 			syslog(LOG_WARNING, "Failed to set quota for %s: "
628*11963SAfshin.Ardakani@Sun.COM 			    "%s (%d) not valid for quotas", quota->q_sidstr,
629*11963SAfshin.Ardakani@Sun.COM 			    smb_sid_type2str(sidtype), sidtype);
630*11963SAfshin.Ardakani@Sun.COM 			quota = list_next(quota_list, quota);
631*11963SAfshin.Ardakani@Sun.COM 			continue;
632*11963SAfshin.Ardakani@Sun.COM 		}
633*11963SAfshin.Ardakani@Sun.COM 
634*11963SAfshin.Ardakani@Sun.COM 		if ((smb_quota_getid(quota->q_sidstr, sidtype, &id) == 0) &&
635*11963SAfshin.Ardakani@Sun.COM 		    !(IDMAP_ID_IS_EPHEMERAL(id))) {
636*11963SAfshin.Ardakani@Sun.COM 			(void) snprintf(qsetstr, SMB_QUOTA_CMD_STR_LENGTH,
637*11963SAfshin.Ardakani@Sun.COM 			    "%s@%d", typestr, id);
638*11963SAfshin.Ardakani@Sun.COM 		} else {
639*11963SAfshin.Ardakani@Sun.COM 			(void) snprintf(qsetstr, SMB_QUOTA_CMD_STR_LENGTH,
640*11963SAfshin.Ardakani@Sun.COM 			    "%s@%s", typestr, quota->q_sidstr);
641*11963SAfshin.Ardakani@Sun.COM 		}
642*11963SAfshin.Ardakani@Sun.COM 
643*11963SAfshin.Ardakani@Sun.COM 		errno = 0;
644*11963SAfshin.Ardakani@Sun.COM 		if (zfs_prop_set(zfs_hdl.z_fs, qsetstr, qlimit) != 0) {
645*11963SAfshin.Ardakani@Sun.COM 			syslog(LOG_WARNING, "Failed to set quota for %s: %s",
646*11963SAfshin.Ardakani@Sun.COM 			    quota->q_sidstr, strerror(errno));
647*11963SAfshin.Ardakani@Sun.COM 			status = NT_STATUS_INVALID_PARAMETER;
648*11963SAfshin.Ardakani@Sun.COM 			break;
649*11963SAfshin.Ardakani@Sun.COM 		}
650*11963SAfshin.Ardakani@Sun.COM 
651*11963SAfshin.Ardakani@Sun.COM 		quota = list_next(quota_list, quota);
652*11963SAfshin.Ardakani@Sun.COM 	}
653*11963SAfshin.Ardakani@Sun.COM 
654*11963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_fini(&zfs_hdl);
655*11963SAfshin.Ardakani@Sun.COM 	return (status);
656*11963SAfshin.Ardakani@Sun.COM }
657*11963SAfshin.Ardakani@Sun.COM 
658*11963SAfshin.Ardakani@Sun.COM /*
659*11963SAfshin.Ardakani@Sun.COM  * smb_quota_sidtype
660*11963SAfshin.Ardakani@Sun.COM  *
661*11963SAfshin.Ardakani@Sun.COM  * Determine the type of the sid. If the sid exists in
662*11963SAfshin.Ardakani@Sun.COM  * the qtree get its type from there, otherwise do an
663*11963SAfshin.Ardakani@Sun.COM  * lsa_lookup_sid().
664*11963SAfshin.Ardakani@Sun.COM  */
665*11963SAfshin.Ardakani@Sun.COM static uint32_t
666*11963SAfshin.Ardakani@Sun.COM smb_quota_sidtype(smb_quota_tree_t *qtree, char *sidstr)
667*11963SAfshin.Ardakani@Sun.COM {
668*11963SAfshin.Ardakani@Sun.COM 	smb_quota_t key, *quota;
669*11963SAfshin.Ardakani@Sun.COM 	avl_index_t where;
670*11963SAfshin.Ardakani@Sun.COM 	smb_sid_t *sid = NULL;
671*11963SAfshin.Ardakani@Sun.COM 	smb_account_t ainfo;
672*11963SAfshin.Ardakani@Sun.COM 	uint32_t sidtype = SidTypeUnknown;
673*11963SAfshin.Ardakani@Sun.COM 
674*11963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(key.q_sidstr, sidstr, SMB_SID_STRSZ);
675*11963SAfshin.Ardakani@Sun.COM 	quota = avl_find(&qtree->qt_avl, &key, &where);
676*11963SAfshin.Ardakani@Sun.COM 	if (quota)
677*11963SAfshin.Ardakani@Sun.COM 		return (quota->q_sidtype);
678*11963SAfshin.Ardakani@Sun.COM 
679*11963SAfshin.Ardakani@Sun.COM 	sid = smb_sid_fromstr(sidstr);
680*11963SAfshin.Ardakani@Sun.COM 	if (sid != NULL) {
681*11963SAfshin.Ardakani@Sun.COM 		if (lsa_lookup_sid(sid, &ainfo) == NT_STATUS_SUCCESS) {
682*11963SAfshin.Ardakani@Sun.COM 			sidtype = ainfo.a_type;
683*11963SAfshin.Ardakani@Sun.COM 			smb_account_free(&ainfo);
684*11963SAfshin.Ardakani@Sun.COM 		}
685*11963SAfshin.Ardakani@Sun.COM 		smb_sid_free(sid);
686*11963SAfshin.Ardakani@Sun.COM 	}
687*11963SAfshin.Ardakani@Sun.COM 	return (sidtype);
688*11963SAfshin.Ardakani@Sun.COM }
689*11963SAfshin.Ardakani@Sun.COM 
690*11963SAfshin.Ardakani@Sun.COM /*
691*11963SAfshin.Ardakani@Sun.COM  * smb_quota_getid
692*11963SAfshin.Ardakani@Sun.COM  *
693*11963SAfshin.Ardakani@Sun.COM  * Get the user/group id for the sid.
694*11963SAfshin.Ardakani@Sun.COM  */
695*11963SAfshin.Ardakani@Sun.COM static int
696*11963SAfshin.Ardakani@Sun.COM smb_quota_getid(char *sidstr, uint32_t sidtype, uint32_t *id)
697*11963SAfshin.Ardakani@Sun.COM {
698*11963SAfshin.Ardakani@Sun.COM 	int rc = 0;
699*11963SAfshin.Ardakani@Sun.COM 	smb_sid_t *sid = NULL;
700*11963SAfshin.Ardakani@Sun.COM 	int idtype;
701*11963SAfshin.Ardakani@Sun.COM 
702*11963SAfshin.Ardakani@Sun.COM 	sid = smb_sid_fromstr(sidstr);
703*11963SAfshin.Ardakani@Sun.COM 	if (sid == NULL)
704*11963SAfshin.Ardakani@Sun.COM 		return (-1);
705*11963SAfshin.Ardakani@Sun.COM 
706*11963SAfshin.Ardakani@Sun.COM 	switch (sidtype) {
707*11963SAfshin.Ardakani@Sun.COM 	case SidTypeUser:
708*11963SAfshin.Ardakani@Sun.COM 		idtype = SMB_IDMAP_USER;
709*11963SAfshin.Ardakani@Sun.COM 		break;
710*11963SAfshin.Ardakani@Sun.COM 	case SidTypeWellKnownGroup:
711*11963SAfshin.Ardakani@Sun.COM 	case SidTypeGroup:
712*11963SAfshin.Ardakani@Sun.COM 	case SidTypeAlias:
713*11963SAfshin.Ardakani@Sun.COM 		idtype = SMB_IDMAP_GROUP;
714*11963SAfshin.Ardakani@Sun.COM 		break;
715*11963SAfshin.Ardakani@Sun.COM 	default:
716*11963SAfshin.Ardakani@Sun.COM 		rc = -1;
717*11963SAfshin.Ardakani@Sun.COM 		break;
718*11963SAfshin.Ardakani@Sun.COM 	}
719*11963SAfshin.Ardakani@Sun.COM 
720*11963SAfshin.Ardakani@Sun.COM 	if (rc == 0)
721*11963SAfshin.Ardakani@Sun.COM 		rc = smb_idmap_getid(sid, id, &idtype);
722*11963SAfshin.Ardakani@Sun.COM 
723*11963SAfshin.Ardakani@Sun.COM 	smb_sid_free(sid);
724*11963SAfshin.Ardakani@Sun.COM 
725*11963SAfshin.Ardakani@Sun.COM 	return (rc);
726*11963SAfshin.Ardakani@Sun.COM }
727*11963SAfshin.Ardakani@Sun.COM 
728*11963SAfshin.Ardakani@Sun.COM /*
729*11963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_lookup
730*11963SAfshin.Ardakani@Sun.COM  *
731*11963SAfshin.Ardakani@Sun.COM  * Find the quota tree in smb_quota_fs_list.
732*11963SAfshin.Ardakani@Sun.COM  *
733*11963SAfshin.Ardakani@Sun.COM  * If the tree is found but is locked, waits for it to become available.
734*11963SAfshin.Ardakani@Sun.COM  * If the tree is available, locks it and returns it.
735*11963SAfshin.Ardakani@Sun.COM  * Otherwise, returns NULL.
736*11963SAfshin.Ardakani@Sun.COM  */
737*11963SAfshin.Ardakani@Sun.COM static smb_quota_tree_t *
738*11963SAfshin.Ardakani@Sun.COM smb_quota_tree_lookup(const char *path)
739*11963SAfshin.Ardakani@Sun.COM {
740*11963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree = NULL;
741*11963SAfshin.Ardakani@Sun.COM 
742*11963SAfshin.Ardakani@Sun.COM 	assert(path);
743*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
744*11963SAfshin.Ardakani@Sun.COM 
745*11963SAfshin.Ardakani@Sun.COM 	qtree = list_head(&smb_quota_fs_list);
746*11963SAfshin.Ardakani@Sun.COM 	while (qtree != NULL) {
747*11963SAfshin.Ardakani@Sun.COM 		if (!smb_quota_list_init || smb_quota_shutdown) {
748*11963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&smb_quota_list_mutex);
749*11963SAfshin.Ardakani@Sun.COM 			return (NULL);
750*11963SAfshin.Ardakani@Sun.COM 		}
751*11963SAfshin.Ardakani@Sun.COM 
752*11963SAfshin.Ardakani@Sun.COM 		(void) mutex_lock(&qtree->qt_mutex);
753*11963SAfshin.Ardakani@Sun.COM 		assert(qtree->qt_refcnt > 0);
754*11963SAfshin.Ardakani@Sun.COM 
755*11963SAfshin.Ardakani@Sun.COM 		if (!smb_quota_tree_match(qtree, path)) {
756*11963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&qtree->qt_mutex);
757*11963SAfshin.Ardakani@Sun.COM 			qtree = list_next(&smb_quota_fs_list, qtree);
758*11963SAfshin.Ardakani@Sun.COM 			continue;
759*11963SAfshin.Ardakani@Sun.COM 		}
760*11963SAfshin.Ardakani@Sun.COM 
761*11963SAfshin.Ardakani@Sun.COM 		if (qtree->qt_locked) {
762*11963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&qtree->qt_mutex);
763*11963SAfshin.Ardakani@Sun.COM 			(void) cond_wait(&smb_quota_list_condvar,
764*11963SAfshin.Ardakani@Sun.COM 			    &smb_quota_list_mutex);
765*11963SAfshin.Ardakani@Sun.COM 			qtree = list_head(&smb_quota_fs_list);
766*11963SAfshin.Ardakani@Sun.COM 			continue;
767*11963SAfshin.Ardakani@Sun.COM 		}
768*11963SAfshin.Ardakani@Sun.COM 
769*11963SAfshin.Ardakani@Sun.COM 		++(qtree->qt_refcnt);
770*11963SAfshin.Ardakani@Sun.COM 		qtree->qt_locked = B_TRUE;
771*11963SAfshin.Ardakani@Sun.COM 		(void) mutex_unlock(&qtree->qt_mutex);
772*11963SAfshin.Ardakani@Sun.COM 		break;
773*11963SAfshin.Ardakani@Sun.COM 	};
774*11963SAfshin.Ardakani@Sun.COM 
775*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
776*11963SAfshin.Ardakani@Sun.COM 	return (qtree);
777*11963SAfshin.Ardakani@Sun.COM }
778*11963SAfshin.Ardakani@Sun.COM 
779*11963SAfshin.Ardakani@Sun.COM /*
780*11963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_release
781*11963SAfshin.Ardakani@Sun.COM  */
782*11963SAfshin.Ardakani@Sun.COM static void
783*11963SAfshin.Ardakani@Sun.COM smb_quota_tree_release(smb_quota_tree_t *qtree)
784*11963SAfshin.Ardakani@Sun.COM {
785*11963SAfshin.Ardakani@Sun.COM 	boolean_t delete;
786*11963SAfshin.Ardakani@Sun.COM 
787*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&qtree->qt_mutex);
788*11963SAfshin.Ardakani@Sun.COM 	assert(qtree->qt_locked);
789*11963SAfshin.Ardakani@Sun.COM 	assert(qtree->qt_refcnt > 0);
790*11963SAfshin.Ardakani@Sun.COM 
791*11963SAfshin.Ardakani@Sun.COM 	--(qtree->qt_refcnt);
792*11963SAfshin.Ardakani@Sun.COM 	qtree->qt_locked = B_FALSE;
793*11963SAfshin.Ardakani@Sun.COM 	delete = (qtree->qt_refcnt == 0);
794*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&qtree->qt_mutex);
795*11963SAfshin.Ardakani@Sun.COM 
796*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
797*11963SAfshin.Ardakani@Sun.COM 	if (delete)
798*11963SAfshin.Ardakani@Sun.COM 		smb_quota_tree_delete(qtree);
799*11963SAfshin.Ardakani@Sun.COM 	(void) cond_broadcast(&smb_quota_list_condvar);
800*11963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
801*11963SAfshin.Ardakani@Sun.COM }
802*11963SAfshin.Ardakani@Sun.COM 
803*11963SAfshin.Ardakani@Sun.COM /*
804*11963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_match
805*11963SAfshin.Ardakani@Sun.COM  *
806*11963SAfshin.Ardakani@Sun.COM  * Determine if qtree represents the file system identified by path
807*11963SAfshin.Ardakani@Sun.COM  */
808*11963SAfshin.Ardakani@Sun.COM static boolean_t
809*11963SAfshin.Ardakani@Sun.COM smb_quota_tree_match(smb_quota_tree_t *qtree, const char *path)
810*11963SAfshin.Ardakani@Sun.COM {
811*11963SAfshin.Ardakani@Sun.COM 	return (strncmp(qtree->qt_path, path, MAXPATHLEN) == 0);
812*11963SAfshin.Ardakani@Sun.COM }
813*11963SAfshin.Ardakani@Sun.COM 
814*11963SAfshin.Ardakani@Sun.COM /*
815*11963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_create
816*11963SAfshin.Ardakani@Sun.COM  *
817*11963SAfshin.Ardakani@Sun.COM  * Create and initialize an smb_quota_tree_t structure
818*11963SAfshin.Ardakani@Sun.COM  */
819*11963SAfshin.Ardakani@Sun.COM static smb_quota_tree_t *
820*11963SAfshin.Ardakani@Sun.COM smb_quota_tree_create(const char *path)
821*11963SAfshin.Ardakani@Sun.COM {
822*11963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree;
823*11963SAfshin.Ardakani@Sun.COM 
824*11963SAfshin.Ardakani@Sun.COM 	assert(MUTEX_HELD(&smb_quota_list_mutex));
825*11963SAfshin.Ardakani@Sun.COM 
826*11963SAfshin.Ardakani@Sun.COM 	qtree = malloc(sizeof (smb_quota_tree_t));
827*11963SAfshin.Ardakani@Sun.COM 	if (qtree == NULL)
828*11963SAfshin.Ardakani@Sun.COM 		return (NULL);
829*11963SAfshin.Ardakani@Sun.COM 
830*11963SAfshin.Ardakani@Sun.COM 	qtree->qt_path = strdup(path);
831*11963SAfshin.Ardakani@Sun.COM 	if (qtree->qt_path == NULL) {
832*11963SAfshin.Ardakani@Sun.COM 		free(qtree);
833*11963SAfshin.Ardakani@Sun.COM 		return (NULL);
834*11963SAfshin.Ardakani@Sun.COM 	}
835*11963SAfshin.Ardakani@Sun.COM 
836*11963SAfshin.Ardakani@Sun.COM 	qtree->qt_timestamp = 0;
837*11963SAfshin.Ardakani@Sun.COM 	qtree->qt_locked = B_FALSE;
838*11963SAfshin.Ardakani@Sun.COM 	qtree->qt_refcnt = 1;
839*11963SAfshin.Ardakani@Sun.COM 	qtree->qt_sharecnt = 1;
840*11963SAfshin.Ardakani@Sun.COM 
841*11963SAfshin.Ardakani@Sun.COM 	avl_create(&qtree->qt_avl, smb_quota_sid_cmp,
842*11963SAfshin.Ardakani@Sun.COM 	    sizeof (smb_quota_t), offsetof(smb_quota_t, q_avl_node));
843*11963SAfshin.Ardakani@Sun.COM 
844*11963SAfshin.Ardakani@Sun.COM 	++smb_quota_tree_cnt;
845*11963SAfshin.Ardakani@Sun.COM 	return (qtree);
846*11963SAfshin.Ardakani@Sun.COM }
847*11963SAfshin.Ardakani@Sun.COM 
848*11963SAfshin.Ardakani@Sun.COM /*
849*11963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_delete
850*11963SAfshin.Ardakani@Sun.COM  *
851*11963SAfshin.Ardakani@Sun.COM  * Free and delete the smb_quota_tree_t structure.
852*11963SAfshin.Ardakani@Sun.COM  * qtree must have no users (refcnt == 0).
853*11963SAfshin.Ardakani@Sun.COM  */
854*11963SAfshin.Ardakani@Sun.COM static void
855*11963SAfshin.Ardakani@Sun.COM smb_quota_tree_delete(smb_quota_tree_t *qtree)
856*11963SAfshin.Ardakani@Sun.COM {
857*11963SAfshin.Ardakani@Sun.COM 	void *cookie = NULL;
858*11963SAfshin.Ardakani@Sun.COM 	smb_quota_t *node;
859*11963SAfshin.Ardakani@Sun.COM 
860*11963SAfshin.Ardakani@Sun.COM 	assert(MUTEX_HELD(&smb_quota_list_mutex));
861*11963SAfshin.Ardakani@Sun.COM 	assert(qtree->qt_refcnt == 0);
862*11963SAfshin.Ardakani@Sun.COM 
863*11963SAfshin.Ardakani@Sun.COM 	while ((node = avl_destroy_nodes(&qtree->qt_avl, &cookie)) != NULL)
864*11963SAfshin.Ardakani@Sun.COM 		free(node);
865*11963SAfshin.Ardakani@Sun.COM 	avl_destroy(&qtree->qt_avl);
866*11963SAfshin.Ardakani@Sun.COM 
867*11963SAfshin.Ardakani@Sun.COM 	free(qtree->qt_path);
868*11963SAfshin.Ardakani@Sun.COM 	free(qtree);
869*11963SAfshin.Ardakani@Sun.COM 
870*11963SAfshin.Ardakani@Sun.COM 	--smb_quota_tree_cnt;
871*11963SAfshin.Ardakani@Sun.COM }
872*11963SAfshin.Ardakani@Sun.COM 
873*11963SAfshin.Ardakani@Sun.COM /*
874*11963SAfshin.Ardakani@Sun.COM  * smb_quota_sid_cmp
875*11963SAfshin.Ardakani@Sun.COM  *
876*11963SAfshin.Ardakani@Sun.COM  * Comparision function for nodes in an AVL tree which holds quota
877*11963SAfshin.Ardakani@Sun.COM  * entries indexed by SID.
878*11963SAfshin.Ardakani@Sun.COM  */
879*11963SAfshin.Ardakani@Sun.COM static int
880*11963SAfshin.Ardakani@Sun.COM smb_quota_sid_cmp(const void *l_arg, const void *r_arg)
881*11963SAfshin.Ardakani@Sun.COM {
882*11963SAfshin.Ardakani@Sun.COM 	const char *l_sid = ((smb_quota_t *)l_arg)->q_sidstr;
883*11963SAfshin.Ardakani@Sun.COM 	const char *r_sid = ((smb_quota_t *)r_arg)->q_sidstr;
884*11963SAfshin.Ardakani@Sun.COM 	int ret;
885*11963SAfshin.Ardakani@Sun.COM 
886*11963SAfshin.Ardakani@Sun.COM 	ret = strncasecmp(l_sid, r_sid, SMB_SID_STRSZ);
887*11963SAfshin.Ardakani@Sun.COM 
888*11963SAfshin.Ardakani@Sun.COM 	if (ret > 0)
889*11963SAfshin.Ardakani@Sun.COM 		return (1);
890*11963SAfshin.Ardakani@Sun.COM 	if (ret < 0)
891*11963SAfshin.Ardakani@Sun.COM 		return (-1);
892*11963SAfshin.Ardakani@Sun.COM 	return (0);
893*11963SAfshin.Ardakani@Sun.COM }
894*11963SAfshin.Ardakani@Sun.COM 
895*11963SAfshin.Ardakani@Sun.COM /*
896*11963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_populate
897*11963SAfshin.Ardakani@Sun.COM  *
898*11963SAfshin.Ardakani@Sun.COM  * If the quota tree needs to be (re)populated:
899*11963SAfshin.Ardakani@Sun.COM  * - delete the qtree's contents
900*11963SAfshin.Ardakani@Sun.COM  * - repopulate the qtree from zfs
901*11963SAfshin.Ardakani@Sun.COM  * - set the qtree's timestamp.
902*11963SAfshin.Ardakani@Sun.COM  */
903*11963SAfshin.Ardakani@Sun.COM static uint32_t
904*11963SAfshin.Ardakani@Sun.COM smb_quota_tree_populate(smb_quota_tree_t *qtree)
905*11963SAfshin.Ardakani@Sun.COM {
906*11963SAfshin.Ardakani@Sun.COM 	void *cookie = NULL;
907*11963SAfshin.Ardakani@Sun.COM 	void *node;
908*11963SAfshin.Ardakani@Sun.COM 	uint32_t status;
909*11963SAfshin.Ardakani@Sun.COM 
910*11963SAfshin.Ardakani@Sun.COM 	assert(qtree->qt_locked);
911*11963SAfshin.Ardakani@Sun.COM 
912*11963SAfshin.Ardakani@Sun.COM 	if (!smb_quota_tree_expired(qtree))
913*11963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_SUCCESS);
914*11963SAfshin.Ardakani@Sun.COM 
915*11963SAfshin.Ardakani@Sun.COM 	while ((node = avl_destroy_nodes(&qtree->qt_avl, &cookie)) != NULL)
916*11963SAfshin.Ardakani@Sun.COM 		free(node);
917*11963SAfshin.Ardakani@Sun.COM 
918*11963SAfshin.Ardakani@Sun.COM 	status = smb_quota_zfs_get_quotas(qtree);
919*11963SAfshin.Ardakani@Sun.COM 	if (status != NT_STATUS_SUCCESS)
920*11963SAfshin.Ardakani@Sun.COM 		return (status);
921*11963SAfshin.Ardakani@Sun.COM 
922*11963SAfshin.Ardakani@Sun.COM 	qtree->qt_timestamp = time(NULL);
923*11963SAfshin.Ardakani@Sun.COM 
924*11963SAfshin.Ardakani@Sun.COM 	return (NT_STATUS_SUCCESS);
925*11963SAfshin.Ardakani@Sun.COM }
926*11963SAfshin.Ardakani@Sun.COM 
927*11963SAfshin.Ardakani@Sun.COM static boolean_t
928*11963SAfshin.Ardakani@Sun.COM smb_quota_tree_expired(smb_quota_tree_t *qtree)
929*11963SAfshin.Ardakani@Sun.COM {
930*11963SAfshin.Ardakani@Sun.COM 	time_t tnow = time(NULL);
931*11963SAfshin.Ardakani@Sun.COM 	return ((tnow - qtree->qt_timestamp) > SMB_QUOTA_REFRESH);
932*11963SAfshin.Ardakani@Sun.COM }
933*11963SAfshin.Ardakani@Sun.COM 
934*11963SAfshin.Ardakani@Sun.COM static void
935*11963SAfshin.Ardakani@Sun.COM smb_quota_tree_set_expired(smb_quota_tree_t *qtree)
936*11963SAfshin.Ardakani@Sun.COM {
937*11963SAfshin.Ardakani@Sun.COM 	qtree->qt_timestamp = 0;
938*11963SAfshin.Ardakani@Sun.COM }
939*11963SAfshin.Ardakani@Sun.COM 
940*11963SAfshin.Ardakani@Sun.COM /*
941*11963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_get_quotas
942*11963SAfshin.Ardakani@Sun.COM  *
943*11963SAfshin.Ardakani@Sun.COM  * Get user and group quotas from ZFS and use them to
944*11963SAfshin.Ardakani@Sun.COM  * populate the quota tree.
945*11963SAfshin.Ardakani@Sun.COM  */
946*11963SAfshin.Ardakani@Sun.COM static uint32_t
947*11963SAfshin.Ardakani@Sun.COM smb_quota_zfs_get_quotas(smb_quota_tree_t *qtree)
948*11963SAfshin.Ardakani@Sun.COM {
949*11963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_handle_t zfs_hdl;
950*11963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_arg_t arg;
951*11963SAfshin.Ardakani@Sun.COM 	zfs_userquota_prop_t p;
952*11963SAfshin.Ardakani@Sun.COM 	uint32_t status = NT_STATUS_SUCCESS;
953*11963SAfshin.Ardakani@Sun.COM 
954*11963SAfshin.Ardakani@Sun.COM 	status = smb_quota_zfs_init(qtree->qt_path, &zfs_hdl);
955*11963SAfshin.Ardakani@Sun.COM 	if (status != NT_STATUS_SUCCESS)
956*11963SAfshin.Ardakani@Sun.COM 		return (status);
957*11963SAfshin.Ardakani@Sun.COM 
958*11963SAfshin.Ardakani@Sun.COM 	arg.qa_avl = &qtree->qt_avl;
959*11963SAfshin.Ardakani@Sun.COM 	for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
960*11963SAfshin.Ardakani@Sun.COM 		arg.qa_prop = p;
961*11963SAfshin.Ardakani@Sun.COM 		if (zfs_userspace(zfs_hdl.z_fs, p,
962*11963SAfshin.Ardakani@Sun.COM 		    smb_quota_zfs_callback, &arg) != 0) {
963*11963SAfshin.Ardakani@Sun.COM 			status = NT_STATUS_INTERNAL_ERROR;
964*11963SAfshin.Ardakani@Sun.COM 			break;
965*11963SAfshin.Ardakani@Sun.COM 		}
966*11963SAfshin.Ardakani@Sun.COM 	}
967*11963SAfshin.Ardakani@Sun.COM 
968*11963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_fini(&zfs_hdl);
969*11963SAfshin.Ardakani@Sun.COM 	return (status);
970*11963SAfshin.Ardakani@Sun.COM }
971*11963SAfshin.Ardakani@Sun.COM 
972*11963SAfshin.Ardakani@Sun.COM /*
973*11963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_callback
974*11963SAfshin.Ardakani@Sun.COM  *
975*11963SAfshin.Ardakani@Sun.COM  * Find or create a node in the avl tree (arg->qa_avl) that matches
976*11963SAfshin.Ardakani@Sun.COM  * the SID derived from domain and rid. If no domain is specified,
977*11963SAfshin.Ardakani@Sun.COM  * lookup the sid (smb_quota_sidstr()).
978*11963SAfshin.Ardakani@Sun.COM  * Populate the node.
979*11963SAfshin.Ardakani@Sun.COM  * The property type (arg->qa_prop) determines which property 'space'
980*11963SAfshin.Ardakani@Sun.COM  * refers to.
981*11963SAfshin.Ardakani@Sun.COM  */
982*11963SAfshin.Ardakani@Sun.COM static int
983*11963SAfshin.Ardakani@Sun.COM smb_quota_zfs_callback(void *arg, const char *domain, uid_t rid, uint64_t space)
984*11963SAfshin.Ardakani@Sun.COM {
985*11963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_arg_t *qarg = (smb_quota_zfs_arg_t *)arg;
986*11963SAfshin.Ardakani@Sun.COM 	zfs_userquota_prop_t qprop = qarg->qa_prop;
987*11963SAfshin.Ardakani@Sun.COM 	avl_tree_t *avl_tree = qarg->qa_avl;
988*11963SAfshin.Ardakani@Sun.COM 	avl_index_t where;
989*11963SAfshin.Ardakani@Sun.COM 	smb_quota_t *quota, key;
990*11963SAfshin.Ardakani@Sun.COM 
991*11963SAfshin.Ardakani@Sun.COM 	if (domain == NULL || domain[0] == '\0') {
992*11963SAfshin.Ardakani@Sun.COM 		if (smb_quota_sidstr(rid, qprop, key.q_sidstr) != 0)
993*11963SAfshin.Ardakani@Sun.COM 			return (0);
994*11963SAfshin.Ardakani@Sun.COM 	} else {
995*11963SAfshin.Ardakani@Sun.COM 		(void) snprintf(key.q_sidstr, SMB_SID_STRSZ, "%s-%u",
996*11963SAfshin.Ardakani@Sun.COM 		    domain, (uint32_t)rid);
997*11963SAfshin.Ardakani@Sun.COM 	}
998*11963SAfshin.Ardakani@Sun.COM 
999*11963SAfshin.Ardakani@Sun.COM 	quota = avl_find(avl_tree, &key, &where);
1000*11963SAfshin.Ardakani@Sun.COM 	if (quota == NULL) {
1001*11963SAfshin.Ardakani@Sun.COM 		quota = malloc(sizeof (smb_quota_t));
1002*11963SAfshin.Ardakani@Sun.COM 		if (quota == NULL)
1003*11963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_NO_MEMORY);
1004*11963SAfshin.Ardakani@Sun.COM 		bzero(quota, sizeof (smb_quota_t));
1005*11963SAfshin.Ardakani@Sun.COM 		quota->q_thresh = SMB_QUOTA_UNLIMITED;
1006*11963SAfshin.Ardakani@Sun.COM 		quota->q_limit = SMB_QUOTA_UNLIMITED;
1007*11963SAfshin.Ardakani@Sun.COM 		avl_insert(avl_tree, (void *)quota, where);
1008*11963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(quota->q_sidstr, key.q_sidstr, SMB_SID_STRSZ);
1009*11963SAfshin.Ardakani@Sun.COM 	}
1010*11963SAfshin.Ardakani@Sun.COM 
1011*11963SAfshin.Ardakani@Sun.COM 	switch (qprop) {
1012*11963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_USERUSED:
1013*11963SAfshin.Ardakani@Sun.COM 		quota->q_sidtype = SidTypeUser;
1014*11963SAfshin.Ardakani@Sun.COM 		quota->q_used = space;
1015*11963SAfshin.Ardakani@Sun.COM 		break;
1016*11963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_GROUPUSED:
1017*11963SAfshin.Ardakani@Sun.COM 		quota->q_sidtype = SidTypeGroup;
1018*11963SAfshin.Ardakani@Sun.COM 		quota->q_used = space;
1019*11963SAfshin.Ardakani@Sun.COM 		break;
1020*11963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_USERQUOTA:
1021*11963SAfshin.Ardakani@Sun.COM 		quota->q_sidtype = SidTypeUser;
1022*11963SAfshin.Ardakani@Sun.COM 		quota->q_limit = space;
1023*11963SAfshin.Ardakani@Sun.COM 		break;
1024*11963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_GROUPQUOTA:
1025*11963SAfshin.Ardakani@Sun.COM 		quota->q_sidtype = SidTypeGroup;
1026*11963SAfshin.Ardakani@Sun.COM 		quota->q_limit = space;
1027*11963SAfshin.Ardakani@Sun.COM 		break;
1028*11963SAfshin.Ardakani@Sun.COM 	default:
1029*11963SAfshin.Ardakani@Sun.COM 		break;
1030*11963SAfshin.Ardakani@Sun.COM 	}
1031*11963SAfshin.Ardakani@Sun.COM 
1032*11963SAfshin.Ardakani@Sun.COM 	quota->q_thresh = quota->q_limit;
1033*11963SAfshin.Ardakani@Sun.COM 
1034*11963SAfshin.Ardakani@Sun.COM 	return (0);
1035*11963SAfshin.Ardakani@Sun.COM }
1036*11963SAfshin.Ardakani@Sun.COM 
1037*11963SAfshin.Ardakani@Sun.COM /*
1038*11963SAfshin.Ardakani@Sun.COM  * smb_quota_sidstr
1039*11963SAfshin.Ardakani@Sun.COM  *
1040*11963SAfshin.Ardakani@Sun.COM  * Use idmap to get the sid for the specified id and return
1041*11963SAfshin.Ardakani@Sun.COM  * the string version of the sid in sidstr.
1042*11963SAfshin.Ardakani@Sun.COM  * sidstr must be a buffer of at least SMB_SID_STRSZ.
1043*11963SAfshin.Ardakani@Sun.COM  */
1044*11963SAfshin.Ardakani@Sun.COM static int
1045*11963SAfshin.Ardakani@Sun.COM smb_quota_sidstr(uint32_t id, zfs_userquota_prop_t qprop, char *sidstr)
1046*11963SAfshin.Ardakani@Sun.COM {
1047*11963SAfshin.Ardakani@Sun.COM 	int idtype;
1048*11963SAfshin.Ardakani@Sun.COM 	smb_sid_t *sid;
1049*11963SAfshin.Ardakani@Sun.COM 
1050*11963SAfshin.Ardakani@Sun.COM 	switch (qprop) {
1051*11963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_USERUSED:
1052*11963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_USERQUOTA:
1053*11963SAfshin.Ardakani@Sun.COM 		idtype = SMB_IDMAP_USER;
1054*11963SAfshin.Ardakani@Sun.COM 		break;
1055*11963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_GROUPUSED:
1056*11963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_GROUPQUOTA:
1057*11963SAfshin.Ardakani@Sun.COM 		idtype = SMB_IDMAP_GROUP;
1058*11963SAfshin.Ardakani@Sun.COM 		break;
1059*11963SAfshin.Ardakani@Sun.COM 	default:
1060*11963SAfshin.Ardakani@Sun.COM 		return (-1);
1061*11963SAfshin.Ardakani@Sun.COM 	}
1062*11963SAfshin.Ardakani@Sun.COM 
1063*11963SAfshin.Ardakani@Sun.COM 	if (smb_idmap_getsid(id, idtype, &sid) != IDMAP_SUCCESS)
1064*11963SAfshin.Ardakani@Sun.COM 		return (-1);
1065*11963SAfshin.Ardakani@Sun.COM 
1066*11963SAfshin.Ardakani@Sun.COM 	smb_sid_tostr(sid, sidstr);
1067*11963SAfshin.Ardakani@Sun.COM 	smb_sid_free(sid);
1068*11963SAfshin.Ardakani@Sun.COM 
1069*11963SAfshin.Ardakani@Sun.COM 	return (0);
1070*11963SAfshin.Ardakani@Sun.COM }
1071*11963SAfshin.Ardakani@Sun.COM 
1072*11963SAfshin.Ardakani@Sun.COM /*
1073*11963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_init
1074*11963SAfshin.Ardakani@Sun.COM  *
1075*11963SAfshin.Ardakani@Sun.COM  * Initialize zfs library and dataset handles
1076*11963SAfshin.Ardakani@Sun.COM  */
1077*11963SAfshin.Ardakani@Sun.COM static uint32_t
1078*11963SAfshin.Ardakani@Sun.COM smb_quota_zfs_init(const char *path, smb_quota_zfs_handle_t *zfs_hdl)
1079*11963SAfshin.Ardakani@Sun.COM {
1080*11963SAfshin.Ardakani@Sun.COM 	char dataset[MAXPATHLEN];
1081*11963SAfshin.Ardakani@Sun.COM 
1082*11963SAfshin.Ardakani@Sun.COM 	if (smb_getdataset(path, dataset, MAXPATHLEN) != 0)
1083*11963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_INVALID_PARAMETER);
1084*11963SAfshin.Ardakani@Sun.COM 
1085*11963SAfshin.Ardakani@Sun.COM 	if ((zfs_hdl->z_lib = libzfs_init()) == NULL)
1086*11963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_INTERNAL_ERROR);
1087*11963SAfshin.Ardakani@Sun.COM 
1088*11963SAfshin.Ardakani@Sun.COM 	zfs_hdl->z_fs = zfs_open(zfs_hdl->z_lib, dataset, ZFS_TYPE_DATASET);
1089*11963SAfshin.Ardakani@Sun.COM 	if (zfs_hdl->z_fs == NULL) {
1090*11963SAfshin.Ardakani@Sun.COM 		libzfs_fini(zfs_hdl->z_lib);
1091*11963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_ACCESS_DENIED);
1092*11963SAfshin.Ardakani@Sun.COM 	}
1093*11963SAfshin.Ardakani@Sun.COM 
1094*11963SAfshin.Ardakani@Sun.COM 	return (NT_STATUS_SUCCESS);
1095*11963SAfshin.Ardakani@Sun.COM }
1096*11963SAfshin.Ardakani@Sun.COM 
1097*11963SAfshin.Ardakani@Sun.COM /*
1098*11963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_fini
1099*11963SAfshin.Ardakani@Sun.COM  *
1100*11963SAfshin.Ardakani@Sun.COM  * Close zfs library and dataset handles
1101*11963SAfshin.Ardakani@Sun.COM  */
1102*11963SAfshin.Ardakani@Sun.COM static void
1103*11963SAfshin.Ardakani@Sun.COM smb_quota_zfs_fini(smb_quota_zfs_handle_t *zfs_hdl)
1104*11963SAfshin.Ardakani@Sun.COM {
1105*11963SAfshin.Ardakani@Sun.COM 	zfs_close(zfs_hdl->z_fs);
1106*11963SAfshin.Ardakani@Sun.COM 	libzfs_fini(zfs_hdl->z_lib);
1107*11963SAfshin.Ardakani@Sun.COM }
1108*11963SAfshin.Ardakani@Sun.COM 
1109*11963SAfshin.Ardakani@Sun.COM /*
1110*11963SAfshin.Ardakani@Sun.COM  * smb_quota_add_ctrldir
1111*11963SAfshin.Ardakani@Sun.COM  *
1112*11963SAfshin.Ardakani@Sun.COM  * In order to display the quota properties tab, windows clients
1113*11963SAfshin.Ardakani@Sun.COM  * check for the existence of the quota control file, created
1114*11963SAfshin.Ardakani@Sun.COM  * here as follows:
1115*11963SAfshin.Ardakani@Sun.COM  * - Create SMB_QUOTA_CNTRL_DIR directory (with A_HIDDEN & A_SYSTEM
1116*11963SAfshin.Ardakani@Sun.COM  *   attributes).
1117*11963SAfshin.Ardakani@Sun.COM  * - Create the SMB_QUOTA_CNTRL_FILE file (with extended attribute
1118*11963SAfshin.Ardakani@Sun.COM  *   SMB_QUOTA_CNTRL_INDEX_XATTR) in the SMB_QUOTA_CNTRL_DIR directory.
1119*11963SAfshin.Ardakani@Sun.COM  * - Set the acl of SMB_QUOTA_CNTRL_FILE file to SMB_QUOTA_CNTRL_PERM.
1120*11963SAfshin.Ardakani@Sun.COM  */
1121*11963SAfshin.Ardakani@Sun.COM static void
1122*11963SAfshin.Ardakani@Sun.COM smb_quota_add_ctrldir(const char *path)
1123*11963SAfshin.Ardakani@Sun.COM {
1124*11963SAfshin.Ardakani@Sun.COM 	int newfd, dirfd, afd;
1125*11963SAfshin.Ardakani@Sun.COM 	nvlist_t *request;
1126*11963SAfshin.Ardakani@Sun.COM 	char dir[MAXPATHLEN], file[MAXPATHLEN];
1127*11963SAfshin.Ardakani@Sun.COM 	acl_t *aclp;
1128*11963SAfshin.Ardakani@Sun.COM 	struct stat statbuf;
1129*11963SAfshin.Ardakani@Sun.COM 
1130*11963SAfshin.Ardakani@Sun.COM 	assert(path != NULL);
1131*11963SAfshin.Ardakani@Sun.COM 
1132*11963SAfshin.Ardakani@Sun.COM 	(void) snprintf(dir, MAXPATHLEN, ".%s/%s", path, SMB_QUOTA_CNTRL_DIR);
1133*11963SAfshin.Ardakani@Sun.COM 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dir, SMB_QUOTA_CNTRL_FILE);
1134*11963SAfshin.Ardakani@Sun.COM 	if ((mkdir(dir, 0750) < 0) && (errno != EEXIST))
1135*11963SAfshin.Ardakani@Sun.COM 		return;
1136*11963SAfshin.Ardakani@Sun.COM 
1137*11963SAfshin.Ardakani@Sun.COM 	if ((dirfd = open(dir, O_RDONLY)) < 0) {
1138*11963SAfshin.Ardakani@Sun.COM 		(void) remove(dir);
1139*11963SAfshin.Ardakani@Sun.COM 		return;
1140*11963SAfshin.Ardakani@Sun.COM 	}
1141*11963SAfshin.Ardakani@Sun.COM 
1142*11963SAfshin.Ardakani@Sun.COM 	if (nvlist_alloc(&request, NV_UNIQUE_NAME, 0) == 0) {
1143*11963SAfshin.Ardakani@Sun.COM 		if ((nvlist_add_boolean_value(request, A_HIDDEN, 1) != 0) ||
1144*11963SAfshin.Ardakani@Sun.COM 		    (nvlist_add_boolean_value(request, A_SYSTEM, 1) != 0) ||
1145*11963SAfshin.Ardakani@Sun.COM 		    (fsetattr(dirfd, XATTR_VIEW_READWRITE, request))) {
1146*11963SAfshin.Ardakani@Sun.COM 			nvlist_free(request);
1147*11963SAfshin.Ardakani@Sun.COM 			(void) close(dirfd);
1148*11963SAfshin.Ardakani@Sun.COM 			(void) remove(dir);
1149*11963SAfshin.Ardakani@Sun.COM 			return;
1150*11963SAfshin.Ardakani@Sun.COM 		}
1151*11963SAfshin.Ardakani@Sun.COM 	}
1152*11963SAfshin.Ardakani@Sun.COM 	nvlist_free(request);
1153*11963SAfshin.Ardakani@Sun.COM 	(void) close(dirfd);
1154*11963SAfshin.Ardakani@Sun.COM 
1155*11963SAfshin.Ardakani@Sun.COM 	if (stat(file, &statbuf) != 0) {
1156*11963SAfshin.Ardakani@Sun.COM 		if ((newfd = creat(file, 0640)) < 0) {
1157*11963SAfshin.Ardakani@Sun.COM 			(void) remove(dir);
1158*11963SAfshin.Ardakani@Sun.COM 			return;
1159*11963SAfshin.Ardakani@Sun.COM 		}
1160*11963SAfshin.Ardakani@Sun.COM 		(void) close(newfd);
1161*11963SAfshin.Ardakani@Sun.COM 	}
1162*11963SAfshin.Ardakani@Sun.COM 
1163*11963SAfshin.Ardakani@Sun.COM 	afd = attropen(file, SMB_QUOTA_CNTRL_INDEX_XATTR, O_RDWR | O_CREAT,
1164*11963SAfshin.Ardakani@Sun.COM 	    0640);
1165*11963SAfshin.Ardakani@Sun.COM 	if (afd == -1) {
1166*11963SAfshin.Ardakani@Sun.COM 		(void) unlink(file);
1167*11963SAfshin.Ardakani@Sun.COM 		(void) remove(dir);
1168*11963SAfshin.Ardakani@Sun.COM 		return;
1169*11963SAfshin.Ardakani@Sun.COM 	}
1170*11963SAfshin.Ardakani@Sun.COM 	(void) close(afd);
1171*11963SAfshin.Ardakani@Sun.COM 
1172*11963SAfshin.Ardakani@Sun.COM 	if (acl_fromtext(SMB_QUOTA_CNTRL_PERM, &aclp) != 0) {
1173*11963SAfshin.Ardakani@Sun.COM 		(void) unlink(file);
1174*11963SAfshin.Ardakani@Sun.COM 		(void) remove(dir);
1175*11963SAfshin.Ardakani@Sun.COM 		return;
1176*11963SAfshin.Ardakani@Sun.COM 	}
1177*11963SAfshin.Ardakani@Sun.COM 
1178*11963SAfshin.Ardakani@Sun.COM 	if (acl_set(file, aclp) == -1) {
1179*11963SAfshin.Ardakani@Sun.COM 		(void) unlink(file);
1180*11963SAfshin.Ardakani@Sun.COM 		(void) remove(dir);
1181*11963SAfshin.Ardakani@Sun.COM 		acl_free(aclp);
1182*11963SAfshin.Ardakani@Sun.COM 		return;
1183*11963SAfshin.Ardakani@Sun.COM 	}
1184*11963SAfshin.Ardakani@Sun.COM 	acl_free(aclp);
1185*11963SAfshin.Ardakani@Sun.COM }
1186*11963SAfshin.Ardakani@Sun.COM 
1187*11963SAfshin.Ardakani@Sun.COM /*
1188*11963SAfshin.Ardakani@Sun.COM  * smb_quota_remove_ctrldir
1189*11963SAfshin.Ardakani@Sun.COM  *
1190*11963SAfshin.Ardakani@Sun.COM  * Remove SMB_QUOTA_CNTRL_FILE and SMB_QUOTA_CNTRL_DIR.
1191*11963SAfshin.Ardakani@Sun.COM  */
1192*11963SAfshin.Ardakani@Sun.COM static void
1193*11963SAfshin.Ardakani@Sun.COM smb_quota_remove_ctrldir(const char *path)
1194*11963SAfshin.Ardakani@Sun.COM {
1195*11963SAfshin.Ardakani@Sun.COM 	char dir[MAXPATHLEN], file[MAXPATHLEN];
1196*11963SAfshin.Ardakani@Sun.COM 	assert(path);
1197*11963SAfshin.Ardakani@Sun.COM 
1198*11963SAfshin.Ardakani@Sun.COM 	(void) snprintf(dir, MAXPATHLEN, ".%s/%s", path, SMB_QUOTA_CNTRL_DIR);
1199*11963SAfshin.Ardakani@Sun.COM 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dir, SMB_QUOTA_CNTRL_FILE);
1200*11963SAfshin.Ardakani@Sun.COM 	(void) unlink(file);
1201*11963SAfshin.Ardakani@Sun.COM 	(void) remove(dir);
1202*11963SAfshin.Ardakani@Sun.COM }
1203