xref: /onnv-gate/usr/src/lib/smbsrv/libmlsvc/common/smb_quota.c (revision 12508:edb7861a1533)
111963SAfshin.Ardakani@Sun.COM /*
211963SAfshin.Ardakani@Sun.COM  * CDDL HEADER START
311963SAfshin.Ardakani@Sun.COM  *
411963SAfshin.Ardakani@Sun.COM  * The contents of this file are subject to the terms of the
511963SAfshin.Ardakani@Sun.COM  * Common Development and Distribution License (the "License").
611963SAfshin.Ardakani@Sun.COM  * You may not use this file except in compliance with the License.
711963SAfshin.Ardakani@Sun.COM  *
811963SAfshin.Ardakani@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911963SAfshin.Ardakani@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011963SAfshin.Ardakani@Sun.COM  * See the License for the specific language governing permissions
1111963SAfshin.Ardakani@Sun.COM  * and limitations under the License.
1211963SAfshin.Ardakani@Sun.COM  *
1311963SAfshin.Ardakani@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411963SAfshin.Ardakani@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511963SAfshin.Ardakani@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611963SAfshin.Ardakani@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711963SAfshin.Ardakani@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811963SAfshin.Ardakani@Sun.COM  *
1911963SAfshin.Ardakani@Sun.COM  * CDDL HEADER END
2011963SAfshin.Ardakani@Sun.COM  */
21*12508Samw@Sun.COM 
2211963SAfshin.Ardakani@Sun.COM /*
2312065SKeyur.Desai@Sun.COM  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2411963SAfshin.Ardakani@Sun.COM  */
25*12508Samw@Sun.COM 
2611963SAfshin.Ardakani@Sun.COM #include <stdio.h>
2711963SAfshin.Ardakani@Sun.COM #include <stdlib.h>
2811963SAfshin.Ardakani@Sun.COM #include <fcntl.h>
2911963SAfshin.Ardakani@Sun.COM #include <attr.h>
3011963SAfshin.Ardakani@Sun.COM #include <unistd.h>
3111963SAfshin.Ardakani@Sun.COM #include <libuutil.h>
3211963SAfshin.Ardakani@Sun.COM #include <libzfs.h>
3311963SAfshin.Ardakani@Sun.COM #include <assert.h>
3411963SAfshin.Ardakani@Sun.COM #include <stddef.h>
3511963SAfshin.Ardakani@Sun.COM #include <strings.h>
3611963SAfshin.Ardakani@Sun.COM #include <errno.h>
3711963SAfshin.Ardakani@Sun.COM #include <synch.h>
3811963SAfshin.Ardakani@Sun.COM #include <smbsrv/smb_xdr.h>
3911963SAfshin.Ardakani@Sun.COM #include <smbsrv/libmlsvc.h>
4011963SAfshin.Ardakani@Sun.COM #include <smbsrv/smb_idmap.h>
4111963SAfshin.Ardakani@Sun.COM #include <mlsvc.h>
4211963SAfshin.Ardakani@Sun.COM #include <sys/avl.h>
4311963SAfshin.Ardakani@Sun.COM 
4411963SAfshin.Ardakani@Sun.COM /*
4511963SAfshin.Ardakani@Sun.COM  * smb_quota subsystem interface - mlsvc.h
4611963SAfshin.Ardakani@Sun.COM  * ---------------------------------------
4711963SAfshin.Ardakani@Sun.COM  * Management of the smb_quota_fs_list (see below).
4811963SAfshin.Ardakani@Sun.COM  * smb_quota_init
4911963SAfshin.Ardakani@Sun.COM  * smb_quota_fini
5011963SAfshin.Ardakani@Sun.COM  * smb_quota_add_fs
5111963SAfshin.Ardakani@Sun.COM  * smb_quota_remove_fs
5211963SAfshin.Ardakani@Sun.COM  *
5311963SAfshin.Ardakani@Sun.COM  * smb_quota public interface - libmlsvc.h
5411963SAfshin.Ardakani@Sun.COM  * ---------------------------------------
5511963SAfshin.Ardakani@Sun.COM  * Handling of requests to query and set quota data on a filesystem.
5611963SAfshin.Ardakani@Sun.COM  * smb_quota_query - query user/group quotas on a filesystem
5711963SAfshin.Ardakani@Sun.COM  * smb_quota_set - set user/group quotas ona filesystem
5811963SAfshin.Ardakani@Sun.COM  * smb_quota_free - delete the quota list created in smb_quota_query
5911963SAfshin.Ardakani@Sun.COM  */
6011963SAfshin.Ardakani@Sun.COM 
6111963SAfshin.Ardakani@Sun.COM /*
6211963SAfshin.Ardakani@Sun.COM  * Querying user & group quotas - smb_quota_query
6311963SAfshin.Ardakani@Sun.COM  *
6411963SAfshin.Ardakani@Sun.COM  * In order to fulfill the quota query requests that can be received
6511963SAfshin.Ardakani@Sun.COM  * from clients, it is required that the quota data can be provided in
6611963SAfshin.Ardakani@Sun.COM  * a well defined and consistent order, and that a request can specify
6711963SAfshin.Ardakani@Sun.COM  * at which quota entry to begin the query.
6811963SAfshin.Ardakani@Sun.COM  *
6911963SAfshin.Ardakani@Sun.COM  * Quota Tree
7011963SAfshin.Ardakani@Sun.COM  * Since the file system does not support the above, an avl tree is
7111963SAfshin.Ardakani@Sun.COM  * populated with the file system's user and group quota data, and
7211963SAfshin.Ardakani@Sun.COM  * then used to provide the data to respond to query requests. The
7311963SAfshin.Ardakani@Sun.COM  * avl tree is indexed by the SID.
7411963SAfshin.Ardakani@Sun.COM  * Each node of the avl tree is an smb_quota_t structure.
7511963SAfshin.Ardakani@Sun.COM  *
7611963SAfshin.Ardakani@Sun.COM  * Quota List
7711963SAfshin.Ardakani@Sun.COM  * There is a list of avl trees, one per file system.
7811963SAfshin.Ardakani@Sun.COM  * Each node in the list is an smb_quota_tree_t structure.
7911963SAfshin.Ardakani@Sun.COM  * The list is created via a call to smb_quota_init() when the library
8011963SAfshin.Ardakani@Sun.COM  * is initialized, and destroyed via a call to smb_quota_fini() when
8111963SAfshin.Ardakani@Sun.COM  * the library is fini'd.
8211963SAfshin.Ardakani@Sun.COM  *
8311963SAfshin.Ardakani@Sun.COM  * An avl tree for a specific file system is created and added to the
8411963SAfshin.Ardakani@Sun.COM  * list via a call to smb_quota_add_fs() when the file system is shared,
8511963SAfshin.Ardakani@Sun.COM  * and removed from the list via a call to smb_quota_remove_fs() when
8611963SAfshin.Ardakani@Sun.COM  * the file system is unshared.
8711963SAfshin.Ardakani@Sun.COM  *
8811963SAfshin.Ardakani@Sun.COM  * An avl tree is (re)populated, if required, whenever a quota request
8911963SAfshin.Ardakani@Sun.COM  * (EXCLUDING a resume request) is received for its filesystem. The
9011963SAfshin.Ardakani@Sun.COM  * avl tree is considered to be expired (needs to be repopulated) if
9111963SAfshin.Ardakani@Sun.COM  * either of the following have occurred since it was last (re)populated:
9211963SAfshin.Ardakani@Sun.COM  * - SMB_QUOTA_REFRESH seconds have elapsed OR
9311963SAfshin.Ardakani@Sun.COM  * - a quota set operation has been performed on its file system
9411963SAfshin.Ardakani@Sun.COM  *
9511963SAfshin.Ardakani@Sun.COM  * In order to perform a smb_quota_query/set operation on a file system
9611963SAfshin.Ardakani@Sun.COM  * the appropriate quota tree must be identified and locked via a call
9711963SAfshin.Ardakani@Sun.COM  * to smb_quota_tree_lookup(), The quota tree is locked (qt_locked == B_TRUE)
9811963SAfshin.Ardakani@Sun.COM  * until the caller releases it via a call to smb_quota_tree_release().
9911963SAfshin.Ardakani@Sun.COM  */
10011963SAfshin.Ardakani@Sun.COM 
10111963SAfshin.Ardakani@Sun.COM /*
10211963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_t
10311963SAfshin.Ardakani@Sun.COM  * Represents an avl tree of user quotas for a file system.
10411963SAfshin.Ardakani@Sun.COM  *
10511963SAfshin.Ardakani@Sun.COM  * qt_refcnt - a count of the number of users of the tree.
10611963SAfshin.Ardakani@Sun.COM  * qt_refcnt is also incremented and decremented when the tree is
10711963SAfshin.Ardakani@Sun.COM  * added to and removed from the quota list.
10811963SAfshin.Ardakani@Sun.COM  * The tree cannot be deleted until this count is zero.
10911963SAfshin.Ardakani@Sun.COM  *
11011963SAfshin.Ardakani@Sun.COM  * qt_sharecnt - a count of the shares of the file system which the
11111963SAfshin.Ardakani@Sun.COM  * tree represents.  smb_quota_remove_fs() cannot remove the tree from
11211963SAfshin.Ardakani@Sun.COM  * removed from the quota list until this count is zero.
11311963SAfshin.Ardakani@Sun.COM  *
11411963SAfshin.Ardakani@Sun.COM  * qt_locked - B_TRUE if someone is currently using the tree, in
11511963SAfshin.Ardakani@Sun.COM  * which case a lookup will wait for the tree to become available.
11611963SAfshin.Ardakani@Sun.COM  */
11711963SAfshin.Ardakani@Sun.COM typedef struct smb_quota_tree {
11811963SAfshin.Ardakani@Sun.COM 	list_node_t	qt_node;
11911963SAfshin.Ardakani@Sun.COM 	char		*qt_path;
12011963SAfshin.Ardakani@Sun.COM 	time_t		qt_timestamp;
12111963SAfshin.Ardakani@Sun.COM 	uint32_t	qt_refcnt;
12211963SAfshin.Ardakani@Sun.COM 	uint32_t	qt_sharecnt;
12311963SAfshin.Ardakani@Sun.COM 	boolean_t	qt_locked;
12411963SAfshin.Ardakani@Sun.COM 	avl_tree_t	qt_avl;
12511963SAfshin.Ardakani@Sun.COM 	mutex_t		qt_mutex;
12611963SAfshin.Ardakani@Sun.COM }smb_quota_tree_t;
12711963SAfshin.Ardakani@Sun.COM 
12811963SAfshin.Ardakani@Sun.COM /*
12911963SAfshin.Ardakani@Sun.COM  * smb_quota_fs_list
13011963SAfshin.Ardakani@Sun.COM  * list of quota trees; one per shared file system.
13111963SAfshin.Ardakani@Sun.COM  */
13211963SAfshin.Ardakani@Sun.COM static list_t smb_quota_fs_list;
13311963SAfshin.Ardakani@Sun.COM static boolean_t smb_quota_list_init = B_FALSE;
13411963SAfshin.Ardakani@Sun.COM static boolean_t smb_quota_shutdown = B_FALSE;
13512065SKeyur.Desai@Sun.COM static mutex_t smb_quota_list_mutex = DEFAULTMUTEX;
13611963SAfshin.Ardakani@Sun.COM static cond_t smb_quota_list_condvar;
13711963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_tree_cnt = 0;
13811963SAfshin.Ardakani@Sun.COM static int smb_quota_fini_timeout = 1; /* seconds */
13911963SAfshin.Ardakani@Sun.COM 
14011963SAfshin.Ardakani@Sun.COM /*
14111963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_handle_t
14211963SAfshin.Ardakani@Sun.COM  * handle to zfs library and dataset
14311963SAfshin.Ardakani@Sun.COM  */
14411963SAfshin.Ardakani@Sun.COM typedef struct smb_quota_zfs_handle {
14511963SAfshin.Ardakani@Sun.COM 	libzfs_handle_t *z_lib;
14611963SAfshin.Ardakani@Sun.COM 	zfs_handle_t *z_fs;
14711963SAfshin.Ardakani@Sun.COM } smb_quota_zfs_handle_t;
14811963SAfshin.Ardakani@Sun.COM 
14911963SAfshin.Ardakani@Sun.COM /*
15011963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_arg_t
15111963SAfshin.Ardakani@Sun.COM  * arg passed to zfs callback when querying quota properties
15211963SAfshin.Ardakani@Sun.COM  */
15311963SAfshin.Ardakani@Sun.COM typedef struct smb_quota_zfs_arg {
15411963SAfshin.Ardakani@Sun.COM 	zfs_userquota_prop_t qa_prop;
15511963SAfshin.Ardakani@Sun.COM 	avl_tree_t *qa_avl;
15611963SAfshin.Ardakani@Sun.COM } smb_quota_zfs_arg_t;
15711963SAfshin.Ardakani@Sun.COM 
15811963SAfshin.Ardakani@Sun.COM static void smb_quota_add_ctrldir(const char *);
15911963SAfshin.Ardakani@Sun.COM static void smb_quota_remove_ctrldir(const char *);
16011963SAfshin.Ardakani@Sun.COM 
16111963SAfshin.Ardakani@Sun.COM static smb_quota_tree_t *smb_quota_tree_create(const char *);
16211963SAfshin.Ardakani@Sun.COM static void smb_quota_tree_delete(smb_quota_tree_t *);
16311963SAfshin.Ardakani@Sun.COM 
16411963SAfshin.Ardakani@Sun.COM static smb_quota_tree_t *smb_quota_tree_lookup(const char *);
16511963SAfshin.Ardakani@Sun.COM static void smb_quota_tree_release(smb_quota_tree_t *);
16611963SAfshin.Ardakani@Sun.COM static boolean_t smb_quota_tree_match(smb_quota_tree_t *, const char *);
16711963SAfshin.Ardakani@Sun.COM static int smb_quota_sid_cmp(const void *, const void *);
16811963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_tree_populate(smb_quota_tree_t *);
16911963SAfshin.Ardakani@Sun.COM static boolean_t smb_quota_tree_expired(smb_quota_tree_t *);
17011963SAfshin.Ardakani@Sun.COM static void smb_quota_tree_set_expired(smb_quota_tree_t *);
17111963SAfshin.Ardakani@Sun.COM 
17211963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_zfs_init(const char *, smb_quota_zfs_handle_t *);
17311963SAfshin.Ardakani@Sun.COM static void smb_quota_zfs_fini(smb_quota_zfs_handle_t *);
17411963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_zfs_get_quotas(smb_quota_tree_t *);
17511963SAfshin.Ardakani@Sun.COM static int smb_quota_zfs_callback(void *, const char *, uid_t, uint64_t);
17611963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_zfs_set_quotas(smb_quota_tree_t *, smb_quota_set_t *);
17711963SAfshin.Ardakani@Sun.COM static int smb_quota_sidstr(uint32_t, zfs_userquota_prop_t, char *);
17811963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_sidtype(smb_quota_tree_t *, char *);
17911963SAfshin.Ardakani@Sun.COM static int smb_quota_getid(char *, uint32_t, uint32_t *);
18011963SAfshin.Ardakani@Sun.COM 
18111963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_query_all(smb_quota_tree_t *,
18211963SAfshin.Ardakani@Sun.COM     smb_quota_query_t *, smb_quota_response_t *);
18311963SAfshin.Ardakani@Sun.COM static uint32_t smb_quota_query_list(smb_quota_tree_t *,
18411963SAfshin.Ardakani@Sun.COM     smb_quota_query_t *, smb_quota_response_t *);
18511963SAfshin.Ardakani@Sun.COM 
18611963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_REFRESH		2
18711963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CMD_LENGTH		21
18811963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CMD_STR_LENGTH	SMB_SID_STRSZ+SMB_QUOTA_CMD_LENGTH
18911963SAfshin.Ardakani@Sun.COM 
19011963SAfshin.Ardakani@Sun.COM /*
19111963SAfshin.Ardakani@Sun.COM  * In order to display the quota properties tab, windows clients
19211963SAfshin.Ardakani@Sun.COM  * check for the existence of the quota control file.
19311963SAfshin.Ardakani@Sun.COM  */
19411963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CNTRL_DIR		".$EXTEND"
19511963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CNTRL_FILE		"$QUOTA"
19611963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CNTRL_INDEX_XATTR	"SUNWsmb:$Q:$INDEX_ALLOCATION"
19711963SAfshin.Ardakani@Sun.COM #define	SMB_QUOTA_CNTRL_PERM		"everyone@:rwpaARWc::allow"
19811963SAfshin.Ardakani@Sun.COM 
19911963SAfshin.Ardakani@Sun.COM /*
20011963SAfshin.Ardakani@Sun.COM  * smb_quota_init
20111963SAfshin.Ardakani@Sun.COM  * Initialize the list to hold the quota trees.
20211963SAfshin.Ardakani@Sun.COM  */
20311963SAfshin.Ardakani@Sun.COM void
smb_quota_init(void)20411963SAfshin.Ardakani@Sun.COM smb_quota_init(void)
20511963SAfshin.Ardakani@Sun.COM {
20611963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
20711963SAfshin.Ardakani@Sun.COM 	if (!smb_quota_list_init) {
20811963SAfshin.Ardakani@Sun.COM 		list_create(&smb_quota_fs_list, sizeof (smb_quota_tree_t),
20911963SAfshin.Ardakani@Sun.COM 		    offsetof(smb_quota_tree_t, qt_node));
21011963SAfshin.Ardakani@Sun.COM 		smb_quota_list_init = B_TRUE;
21111963SAfshin.Ardakani@Sun.COM 		smb_quota_shutdown = B_FALSE;
21211963SAfshin.Ardakani@Sun.COM 	}
21311963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
21411963SAfshin.Ardakani@Sun.COM }
21511963SAfshin.Ardakani@Sun.COM 
21611963SAfshin.Ardakani@Sun.COM /*
21711963SAfshin.Ardakani@Sun.COM  * smb_quota_fini
21811963SAfshin.Ardakani@Sun.COM  *
21911963SAfshin.Ardakani@Sun.COM  * Wait for each quota tree to not be in use (qt_refcnt == 1)
22011963SAfshin.Ardakani@Sun.COM  * then remove it from the list and delete it.
22111963SAfshin.Ardakani@Sun.COM  */
22211963SAfshin.Ardakani@Sun.COM void
smb_quota_fini(void)22311963SAfshin.Ardakani@Sun.COM smb_quota_fini(void)
22411963SAfshin.Ardakani@Sun.COM {
22511963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree, *qtree_next;
22611963SAfshin.Ardakani@Sun.COM 	boolean_t remove;
22711963SAfshin.Ardakani@Sun.COM 	struct timespec tswait;
22811963SAfshin.Ardakani@Sun.COM 	tswait.tv_sec = smb_quota_fini_timeout;
22911963SAfshin.Ardakani@Sun.COM 	tswait.tv_nsec = 0;
23011963SAfshin.Ardakani@Sun.COM 
23111963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
23211963SAfshin.Ardakani@Sun.COM 	smb_quota_shutdown = B_TRUE;
23311963SAfshin.Ardakani@Sun.COM 
23411963SAfshin.Ardakani@Sun.COM 	if (!smb_quota_list_init) {
23511963SAfshin.Ardakani@Sun.COM 		(void) mutex_unlock(&smb_quota_list_mutex);
23611963SAfshin.Ardakani@Sun.COM 		return;
23711963SAfshin.Ardakani@Sun.COM 	}
23811963SAfshin.Ardakani@Sun.COM 
23911963SAfshin.Ardakani@Sun.COM 	(void) cond_broadcast(&smb_quota_list_condvar);
24011963SAfshin.Ardakani@Sun.COM 
24112065SKeyur.Desai@Sun.COM 	while (!list_is_empty(&smb_quota_fs_list)) {
24211963SAfshin.Ardakani@Sun.COM 		qtree = list_head(&smb_quota_fs_list);
24311963SAfshin.Ardakani@Sun.COM 		while (qtree != NULL) {
24411963SAfshin.Ardakani@Sun.COM 			qtree_next = list_next(&smb_quota_fs_list, qtree);
24511963SAfshin.Ardakani@Sun.COM 
24611963SAfshin.Ardakani@Sun.COM 			(void) mutex_lock(&qtree->qt_mutex);
24711963SAfshin.Ardakani@Sun.COM 			remove = (qtree->qt_refcnt == 1);
24811963SAfshin.Ardakani@Sun.COM 			if (remove) {
24911963SAfshin.Ardakani@Sun.COM 				list_remove(&smb_quota_fs_list, qtree);
25011963SAfshin.Ardakani@Sun.COM 				--qtree->qt_refcnt;
25111963SAfshin.Ardakani@Sun.COM 			}
25211963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&qtree->qt_mutex);
25311963SAfshin.Ardakani@Sun.COM 
25411963SAfshin.Ardakani@Sun.COM 			if (remove)
25511963SAfshin.Ardakani@Sun.COM 				smb_quota_tree_delete(qtree);
25611963SAfshin.Ardakani@Sun.COM 
25711963SAfshin.Ardakani@Sun.COM 			qtree = qtree_next;
25811963SAfshin.Ardakani@Sun.COM 		}
25911963SAfshin.Ardakani@Sun.COM 
26012065SKeyur.Desai@Sun.COM 		if (!list_is_empty(&smb_quota_fs_list)) {
26111963SAfshin.Ardakani@Sun.COM 			if (cond_reltimedwait(&smb_quota_list_condvar,
26211963SAfshin.Ardakani@Sun.COM 			    &smb_quota_list_mutex, &tswait) == ETIME) {
26311963SAfshin.Ardakani@Sun.COM 				syslog(LOG_WARNING,
26411963SAfshin.Ardakani@Sun.COM 				    "quota shutdown timeout expired");
26511963SAfshin.Ardakani@Sun.COM 				break;
26611963SAfshin.Ardakani@Sun.COM 			}
26711963SAfshin.Ardakani@Sun.COM 		}
26811963SAfshin.Ardakani@Sun.COM 	}
26911963SAfshin.Ardakani@Sun.COM 
27011963SAfshin.Ardakani@Sun.COM 	if (list_is_empty(&smb_quota_fs_list)) {
27111963SAfshin.Ardakani@Sun.COM 		list_destroy(&smb_quota_fs_list);
27211963SAfshin.Ardakani@Sun.COM 		smb_quota_list_init = B_FALSE;
27311963SAfshin.Ardakani@Sun.COM 	}
27411963SAfshin.Ardakani@Sun.COM 
27511963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
27611963SAfshin.Ardakani@Sun.COM }
27711963SAfshin.Ardakani@Sun.COM 
27811963SAfshin.Ardakani@Sun.COM /*
27911963SAfshin.Ardakani@Sun.COM  * smb_quota_add_fs
28011963SAfshin.Ardakani@Sun.COM  *
28111963SAfshin.Ardakani@Sun.COM  * If there is not a quota tree representing the specified path,
28211963SAfshin.Ardakani@Sun.COM  * create one and add it to the list.
28311963SAfshin.Ardakani@Sun.COM  */
28411963SAfshin.Ardakani@Sun.COM void
smb_quota_add_fs(const char * path)28511963SAfshin.Ardakani@Sun.COM smb_quota_add_fs(const char *path)
28611963SAfshin.Ardakani@Sun.COM {
28711963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree;
28811963SAfshin.Ardakani@Sun.COM 
28911963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
29011963SAfshin.Ardakani@Sun.COM 
29111963SAfshin.Ardakani@Sun.COM 	if (!smb_quota_list_init || smb_quota_shutdown) {
29211963SAfshin.Ardakani@Sun.COM 		(void) mutex_unlock(&smb_quota_list_mutex);
29311963SAfshin.Ardakani@Sun.COM 		return;
29411963SAfshin.Ardakani@Sun.COM 	}
29511963SAfshin.Ardakani@Sun.COM 
29611963SAfshin.Ardakani@Sun.COM 	qtree = list_head(&smb_quota_fs_list);
29711963SAfshin.Ardakani@Sun.COM 	while (qtree != NULL) {
29811963SAfshin.Ardakani@Sun.COM 		if (smb_quota_tree_match(qtree, path)) {
29911963SAfshin.Ardakani@Sun.COM 			(void) mutex_lock(&qtree->qt_mutex);
30011963SAfshin.Ardakani@Sun.COM 			++qtree->qt_sharecnt;
30111963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&qtree->qt_mutex);
30211963SAfshin.Ardakani@Sun.COM 			break;
30311963SAfshin.Ardakani@Sun.COM 		}
30411963SAfshin.Ardakani@Sun.COM 		qtree = list_next(&smb_quota_fs_list, qtree);
30511963SAfshin.Ardakani@Sun.COM 	}
30611963SAfshin.Ardakani@Sun.COM 
30711963SAfshin.Ardakani@Sun.COM 	if (qtree == NULL) {
30811963SAfshin.Ardakani@Sun.COM 		qtree = smb_quota_tree_create(path);
30911963SAfshin.Ardakani@Sun.COM 		if (qtree)
31011963SAfshin.Ardakani@Sun.COM 			list_insert_head(&smb_quota_fs_list, (void *)qtree);
31111963SAfshin.Ardakani@Sun.COM 	}
31211963SAfshin.Ardakani@Sun.COM 
31311963SAfshin.Ardakani@Sun.COM 	if (qtree)
31411963SAfshin.Ardakani@Sun.COM 		smb_quota_add_ctrldir(path);
31511963SAfshin.Ardakani@Sun.COM 
31611963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
31711963SAfshin.Ardakani@Sun.COM }
31811963SAfshin.Ardakani@Sun.COM 
31911963SAfshin.Ardakani@Sun.COM /*
32011963SAfshin.Ardakani@Sun.COM  * smb_quota_remove_fs
32111963SAfshin.Ardakani@Sun.COM  *
32211963SAfshin.Ardakani@Sun.COM  * If this is the last share that the quota tree represents
32311963SAfshin.Ardakani@Sun.COM  * (qtree->qt_sharecnt == 0) remove the qtree from the list.
32411963SAfshin.Ardakani@Sun.COM  * The qtree will be deleted if/when there is nobody using it
32511963SAfshin.Ardakani@Sun.COM  * (qtree->qt_refcnt == 0).
32611963SAfshin.Ardakani@Sun.COM  */
32711963SAfshin.Ardakani@Sun.COM void
smb_quota_remove_fs(const char * path)32811963SAfshin.Ardakani@Sun.COM smb_quota_remove_fs(const char *path)
32911963SAfshin.Ardakani@Sun.COM {
33011963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree;
33111963SAfshin.Ardakani@Sun.COM 	boolean_t delete = B_FALSE;
33211963SAfshin.Ardakani@Sun.COM 
33311963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
33411963SAfshin.Ardakani@Sun.COM 
33511963SAfshin.Ardakani@Sun.COM 	if (!smb_quota_list_init || smb_quota_shutdown) {
33611963SAfshin.Ardakani@Sun.COM 		(void) mutex_unlock(&smb_quota_list_mutex);
33711963SAfshin.Ardakani@Sun.COM 		return;
33811963SAfshin.Ardakani@Sun.COM 	}
33911963SAfshin.Ardakani@Sun.COM 
34011963SAfshin.Ardakani@Sun.COM 	qtree = list_head(&smb_quota_fs_list);
34111963SAfshin.Ardakani@Sun.COM 	while (qtree != NULL) {
34211963SAfshin.Ardakani@Sun.COM 		assert(qtree->qt_refcnt > 0);
34311963SAfshin.Ardakani@Sun.COM 		if (smb_quota_tree_match(qtree, path)) {
34411963SAfshin.Ardakani@Sun.COM 			(void) mutex_lock(&qtree->qt_mutex);
34511963SAfshin.Ardakani@Sun.COM 			--qtree->qt_sharecnt;
34611963SAfshin.Ardakani@Sun.COM 			if (qtree->qt_sharecnt == 0) {
34711963SAfshin.Ardakani@Sun.COM 				list_remove(&smb_quota_fs_list, (void *)qtree);
34811963SAfshin.Ardakani@Sun.COM 				smb_quota_remove_ctrldir(qtree->qt_path);
34911963SAfshin.Ardakani@Sun.COM 				--(qtree->qt_refcnt);
35011963SAfshin.Ardakani@Sun.COM 				delete = (qtree->qt_refcnt == 0);
35111963SAfshin.Ardakani@Sun.COM 			}
35211963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&qtree->qt_mutex);
35311963SAfshin.Ardakani@Sun.COM 			if (delete)
35411963SAfshin.Ardakani@Sun.COM 				smb_quota_tree_delete(qtree);
35511963SAfshin.Ardakani@Sun.COM 			break;
35611963SAfshin.Ardakani@Sun.COM 		}
35711963SAfshin.Ardakani@Sun.COM 		qtree = list_next(&smb_quota_fs_list, qtree);
35811963SAfshin.Ardakani@Sun.COM 	}
35911963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
36011963SAfshin.Ardakani@Sun.COM }
36111963SAfshin.Ardakani@Sun.COM 
36211963SAfshin.Ardakani@Sun.COM /*
36311963SAfshin.Ardakani@Sun.COM  * smb_quota_query
36411963SAfshin.Ardakani@Sun.COM  *
36511963SAfshin.Ardakani@Sun.COM  * Get list of user/group quotas entries.
36611963SAfshin.Ardakani@Sun.COM  * Request->qq_query_op determines whether to get quota entries
36711963SAfshin.Ardakani@Sun.COM  * for the specified SIDs (smb_quota_query_list) OR to get all
36811963SAfshin.Ardakani@Sun.COM  * quota entries, optionally starting at a specified SID.
36911963SAfshin.Ardakani@Sun.COM  *
37011963SAfshin.Ardakani@Sun.COM  * Returns NT_STATUS codes.
37111963SAfshin.Ardakani@Sun.COM  */
37211963SAfshin.Ardakani@Sun.COM uint32_t
smb_quota_query(smb_quota_query_t * request,smb_quota_response_t * reply)37311963SAfshin.Ardakani@Sun.COM smb_quota_query(smb_quota_query_t *request, smb_quota_response_t *reply)
37411963SAfshin.Ardakani@Sun.COM {
37511963SAfshin.Ardakani@Sun.COM 	uint32_t status;
37611963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree;
37711963SAfshin.Ardakani@Sun.COM 	smb_quota_query_op_t query_op = request->qq_query_op;
37811963SAfshin.Ardakani@Sun.COM 
37911963SAfshin.Ardakani@Sun.COM 	list_create(&reply->qr_quota_list, sizeof (smb_quota_t),
38011963SAfshin.Ardakani@Sun.COM 	    offsetof(smb_quota_t, q_list_node));
38111963SAfshin.Ardakani@Sun.COM 
38211963SAfshin.Ardakani@Sun.COM 	qtree = smb_quota_tree_lookup(request->qq_root_path);
38311963SAfshin.Ardakani@Sun.COM 	if (qtree == NULL)
38411963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_INVALID_PARAMETER);
38511963SAfshin.Ardakani@Sun.COM 
38611963SAfshin.Ardakani@Sun.COM 	/* If NOT resuming a previous query all, refresh qtree if required */
38711963SAfshin.Ardakani@Sun.COM 	if ((query_op != SMB_QUOTA_QUERY_ALL) || (request->qq_restart)) {
38811963SAfshin.Ardakani@Sun.COM 		status = smb_quota_tree_populate(qtree);
38911963SAfshin.Ardakani@Sun.COM 		if (status != NT_STATUS_SUCCESS) {
39011963SAfshin.Ardakani@Sun.COM 			smb_quota_tree_release(qtree);
39111963SAfshin.Ardakani@Sun.COM 			return (status);
39211963SAfshin.Ardakani@Sun.COM 		}
39311963SAfshin.Ardakani@Sun.COM 	}
39411963SAfshin.Ardakani@Sun.COM 
39511963SAfshin.Ardakani@Sun.COM 	switch (query_op) {
39611963SAfshin.Ardakani@Sun.COM 	case SMB_QUOTA_QUERY_SIDLIST:
39711963SAfshin.Ardakani@Sun.COM 		status = smb_quota_query_list(qtree, request, reply);
39811963SAfshin.Ardakani@Sun.COM 		break;
39911963SAfshin.Ardakani@Sun.COM 	case SMB_QUOTA_QUERY_STARTSID:
40011963SAfshin.Ardakani@Sun.COM 	case SMB_QUOTA_QUERY_ALL:
40111963SAfshin.Ardakani@Sun.COM 		status = smb_quota_query_all(qtree, request, reply);
40211963SAfshin.Ardakani@Sun.COM 		break;
40311963SAfshin.Ardakani@Sun.COM 	case SMB_QUOTA_QUERY_INVALID_OP:
40411963SAfshin.Ardakani@Sun.COM 	default:
40511963SAfshin.Ardakani@Sun.COM 		status = NT_STATUS_INVALID_PARAMETER;
40611963SAfshin.Ardakani@Sun.COM 		break;
40711963SAfshin.Ardakani@Sun.COM 	}
40811963SAfshin.Ardakani@Sun.COM 
40911963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_release(qtree);
41011963SAfshin.Ardakani@Sun.COM 
41111963SAfshin.Ardakani@Sun.COM 	return (status);
41211963SAfshin.Ardakani@Sun.COM }
41311963SAfshin.Ardakani@Sun.COM 
41411963SAfshin.Ardakani@Sun.COM /*
41511963SAfshin.Ardakani@Sun.COM  * smb_quota_set
41611963SAfshin.Ardakani@Sun.COM  *
41711963SAfshin.Ardakani@Sun.COM  * Set the list of quota entries.
41811963SAfshin.Ardakani@Sun.COM  */
41911963SAfshin.Ardakani@Sun.COM uint32_t
smb_quota_set(smb_quota_set_t * request)42011963SAfshin.Ardakani@Sun.COM smb_quota_set(smb_quota_set_t *request)
42111963SAfshin.Ardakani@Sun.COM {
42211963SAfshin.Ardakani@Sun.COM 	uint32_t status;
42311963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree;
42411963SAfshin.Ardakani@Sun.COM 
42511963SAfshin.Ardakani@Sun.COM 	qtree = smb_quota_tree_lookup(request->qs_root_path);
42611963SAfshin.Ardakani@Sun.COM 	if (qtree == NULL)
42711963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_INVALID_PARAMETER);
42811963SAfshin.Ardakani@Sun.COM 
42911963SAfshin.Ardakani@Sun.COM 	status = smb_quota_zfs_set_quotas(qtree, request);
43011963SAfshin.Ardakani@Sun.COM 
43111963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_set_expired(qtree);
43211963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_release(qtree);
43311963SAfshin.Ardakani@Sun.COM 
43411963SAfshin.Ardakani@Sun.COM 	return (status);
43511963SAfshin.Ardakani@Sun.COM }
43611963SAfshin.Ardakani@Sun.COM 
43711963SAfshin.Ardakani@Sun.COM /*
43811963SAfshin.Ardakani@Sun.COM  * smb_quota_free
43911963SAfshin.Ardakani@Sun.COM  *
44011963SAfshin.Ardakani@Sun.COM  * This method frees quota entries.
44111963SAfshin.Ardakani@Sun.COM  */
44211963SAfshin.Ardakani@Sun.COM void
smb_quota_free(smb_quota_response_t * reply)44311963SAfshin.Ardakani@Sun.COM smb_quota_free(smb_quota_response_t *reply)
44411963SAfshin.Ardakani@Sun.COM {
44511963SAfshin.Ardakani@Sun.COM 	list_t *list = &reply->qr_quota_list;
44611963SAfshin.Ardakani@Sun.COM 	smb_quota_t *quota;
44711963SAfshin.Ardakani@Sun.COM 
44811963SAfshin.Ardakani@Sun.COM 	while ((quota = list_head(list)) != NULL) {
44911963SAfshin.Ardakani@Sun.COM 		list_remove(list, quota);
45011963SAfshin.Ardakani@Sun.COM 		free(quota);
45111963SAfshin.Ardakani@Sun.COM 	}
45211963SAfshin.Ardakani@Sun.COM 
45311963SAfshin.Ardakani@Sun.COM 	list_destroy(list);
45411963SAfshin.Ardakani@Sun.COM }
45511963SAfshin.Ardakani@Sun.COM 
45611963SAfshin.Ardakani@Sun.COM /*
45711963SAfshin.Ardakani@Sun.COM  * smb_quota_query_all
45811963SAfshin.Ardakani@Sun.COM  *
45911963SAfshin.Ardakani@Sun.COM  * Query quotas sequentially from tree, optionally starting at a
46011963SAfshin.Ardakani@Sun.COM  * specified sid. If request->qq_single is TRUE only one quota
46111963SAfshin.Ardakani@Sun.COM  * should be returned, otherwise up to request->qq_max_quota
46211963SAfshin.Ardakani@Sun.COM  * should be returned.
46311963SAfshin.Ardakani@Sun.COM  *
46411963SAfshin.Ardakani@Sun.COM  * SMB_QUOTA_QUERY_STARTSID
46511963SAfshin.Ardakani@Sun.COM  * The query should start at the startsid, the first sid in
46611963SAfshin.Ardakani@Sun.COM  * request->qq_sid_list.
46711963SAfshin.Ardakani@Sun.COM  *
46811963SAfshin.Ardakani@Sun.COM  * SMQ_QUOTA_QUERY_ALL
46911963SAfshin.Ardakani@Sun.COM  * If request->qq_restart the query should restart at the start
47011963SAfshin.Ardakani@Sun.COM  * of the avl tree. Otherwise the first sid in request->qq_sid_list
47111963SAfshin.Ardakani@Sun.COM  * is the resume sid and the query should start at the tree entry
47211963SAfshin.Ardakani@Sun.COM  * after the one it refers to.
47311963SAfshin.Ardakani@Sun.COM  *
47411963SAfshin.Ardakani@Sun.COM  * Returns NT_STATUS codes.
47511963SAfshin.Ardakani@Sun.COM  */
47611963SAfshin.Ardakani@Sun.COM static uint32_t
smb_quota_query_all(smb_quota_tree_t * qtree,smb_quota_query_t * request,smb_quota_response_t * reply)47711963SAfshin.Ardakani@Sun.COM smb_quota_query_all(smb_quota_tree_t *qtree, smb_quota_query_t *request,
47811963SAfshin.Ardakani@Sun.COM     smb_quota_response_t *reply)
47911963SAfshin.Ardakani@Sun.COM {
48011963SAfshin.Ardakani@Sun.COM 	avl_tree_t *avl_tree = &qtree->qt_avl;
48111963SAfshin.Ardakani@Sun.COM 	avl_index_t where;
48211963SAfshin.Ardakani@Sun.COM 	list_t *sid_list, *quota_list;
48311963SAfshin.Ardakani@Sun.COM 	smb_quota_sid_t *sid;
48411963SAfshin.Ardakani@Sun.COM 	smb_quota_t *quota, *quotal, key;
48511963SAfshin.Ardakani@Sun.COM 	uint32_t count;
48611963SAfshin.Ardakani@Sun.COM 
48711963SAfshin.Ardakani@Sun.COM 	/* find starting sid */
48811963SAfshin.Ardakani@Sun.COM 	if (request->qq_query_op == SMB_QUOTA_QUERY_STARTSID) {
48911963SAfshin.Ardakani@Sun.COM 		sid_list = &request->qq_sid_list;
49011963SAfshin.Ardakani@Sun.COM 		sid = list_head(sid_list);
49111963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(key.q_sidstr, sid->qs_sidstr, SMB_SID_STRSZ);
49211963SAfshin.Ardakani@Sun.COM 		quota = avl_find(avl_tree, &key, &where);
49311963SAfshin.Ardakani@Sun.COM 		if (quota == NULL)
49411963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_INVALID_PARAMETER);
49511963SAfshin.Ardakani@Sun.COM 	} else if (request->qq_restart) {
49611963SAfshin.Ardakani@Sun.COM 		quota = avl_first(avl_tree);
49711963SAfshin.Ardakani@Sun.COM 		if (quota == NULL)
498*12508Samw@Sun.COM 			return (NT_STATUS_NO_MORE_ENTRIES);
49911963SAfshin.Ardakani@Sun.COM 	} else {
50011963SAfshin.Ardakani@Sun.COM 		sid_list = &request->qq_sid_list;
50111963SAfshin.Ardakani@Sun.COM 		sid = list_head(sid_list);
50211963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(key.q_sidstr, sid->qs_sidstr, SMB_SID_STRSZ);
50311963SAfshin.Ardakani@Sun.COM 		quota = avl_find(avl_tree, &key, &where);
50411963SAfshin.Ardakani@Sun.COM 		if (quota == NULL)
50511963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_INVALID_PARAMETER);
50611963SAfshin.Ardakani@Sun.COM 		quota = AVL_NEXT(avl_tree, quota);
50711963SAfshin.Ardakani@Sun.COM 		if (quota == NULL)
508*12508Samw@Sun.COM 			return (NT_STATUS_NO_MORE_ENTRIES);
50911963SAfshin.Ardakani@Sun.COM 	}
51011963SAfshin.Ardakani@Sun.COM 
51111963SAfshin.Ardakani@Sun.COM 	if ((request->qq_single) && (request->qq_max_quota > 1))
51211963SAfshin.Ardakani@Sun.COM 		request->qq_max_quota = 1;
51311963SAfshin.Ardakani@Sun.COM 
51411963SAfshin.Ardakani@Sun.COM 	quota_list = &reply->qr_quota_list;
51511963SAfshin.Ardakani@Sun.COM 	count = 0;
51611963SAfshin.Ardakani@Sun.COM 	while (quota) {
51711963SAfshin.Ardakani@Sun.COM 		if (count >= request->qq_max_quota)
51811963SAfshin.Ardakani@Sun.COM 			break;
51911963SAfshin.Ardakani@Sun.COM 
52011963SAfshin.Ardakani@Sun.COM 		quotal = malloc(sizeof (smb_quota_t));
52111963SAfshin.Ardakani@Sun.COM 		if (quotal == NULL)
52211963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_NO_MEMORY);
52311963SAfshin.Ardakani@Sun.COM 		bcopy(quota, quotal, sizeof (smb_quota_t));
52411963SAfshin.Ardakani@Sun.COM 
52511963SAfshin.Ardakani@Sun.COM 		list_insert_tail(quota_list, quotal);
52611963SAfshin.Ardakani@Sun.COM 		++count;
52711963SAfshin.Ardakani@Sun.COM 
52811963SAfshin.Ardakani@Sun.COM 		quota = AVL_NEXT(avl_tree, quota);
52911963SAfshin.Ardakani@Sun.COM 	}
53011963SAfshin.Ardakani@Sun.COM 
53111963SAfshin.Ardakani@Sun.COM 	return (NT_STATUS_SUCCESS);
53211963SAfshin.Ardakani@Sun.COM }
53311963SAfshin.Ardakani@Sun.COM 
53411963SAfshin.Ardakani@Sun.COM /*
53511963SAfshin.Ardakani@Sun.COM  * smb_quota_query_list
53611963SAfshin.Ardakani@Sun.COM  *
53711963SAfshin.Ardakani@Sun.COM  * Iterate through request sid list querying the avl tree for each.
53811963SAfshin.Ardakani@Sun.COM  * Insert an entry in the reply quota list for each sid.
53911963SAfshin.Ardakani@Sun.COM  * For any sid that cannot be found in the avl tree, the reply
54011963SAfshin.Ardakani@Sun.COM  * quota list entry should contain zeros.
54111963SAfshin.Ardakani@Sun.COM  */
54211963SAfshin.Ardakani@Sun.COM static uint32_t
smb_quota_query_list(smb_quota_tree_t * qtree,smb_quota_query_t * request,smb_quota_response_t * reply)54311963SAfshin.Ardakani@Sun.COM smb_quota_query_list(smb_quota_tree_t *qtree, smb_quota_query_t *request,
54411963SAfshin.Ardakani@Sun.COM     smb_quota_response_t *reply)
54511963SAfshin.Ardakani@Sun.COM {
54611963SAfshin.Ardakani@Sun.COM 	avl_tree_t *avl_tree = &qtree->qt_avl;
54711963SAfshin.Ardakani@Sun.COM 	avl_index_t where;
54811963SAfshin.Ardakani@Sun.COM 	list_t *sid_list, *quota_list;
54911963SAfshin.Ardakani@Sun.COM 	smb_quota_sid_t *sid;
55011963SAfshin.Ardakani@Sun.COM 	smb_quota_t *quota, *quotal, key;
55111963SAfshin.Ardakani@Sun.COM 
55211963SAfshin.Ardakani@Sun.COM 	quota_list = &reply->qr_quota_list;
55311963SAfshin.Ardakani@Sun.COM 	sid_list = &request->qq_sid_list;
55411963SAfshin.Ardakani@Sun.COM 	sid = list_head(sid_list);
55511963SAfshin.Ardakani@Sun.COM 	while (sid) {
55611963SAfshin.Ardakani@Sun.COM 		quotal = malloc(sizeof (smb_quota_t));
55711963SAfshin.Ardakani@Sun.COM 		if (quotal == NULL)
55811963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_NO_MEMORY);
55911963SAfshin.Ardakani@Sun.COM 
56011963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(key.q_sidstr, sid->qs_sidstr, SMB_SID_STRSZ);
56111963SAfshin.Ardakani@Sun.COM 		quota = avl_find(avl_tree, &key, &where);
56211963SAfshin.Ardakani@Sun.COM 		if (quota) {
56311963SAfshin.Ardakani@Sun.COM 			bcopy(quota, quotal, sizeof (smb_quota_t));
56411963SAfshin.Ardakani@Sun.COM 		} else {
56511963SAfshin.Ardakani@Sun.COM 			bzero(quotal, sizeof (smb_quota_t));
56611963SAfshin.Ardakani@Sun.COM 			(void) strlcpy(quotal->q_sidstr, sid->qs_sidstr,
56711963SAfshin.Ardakani@Sun.COM 			    SMB_SID_STRSZ);
56811963SAfshin.Ardakani@Sun.COM 		}
56911963SAfshin.Ardakani@Sun.COM 
57011963SAfshin.Ardakani@Sun.COM 		list_insert_tail(quota_list, quotal);
57111963SAfshin.Ardakani@Sun.COM 		sid = list_next(sid_list, sid);
57211963SAfshin.Ardakani@Sun.COM 	}
57311963SAfshin.Ardakani@Sun.COM 
57411963SAfshin.Ardakani@Sun.COM 	return (NT_STATUS_SUCCESS);
57511963SAfshin.Ardakani@Sun.COM }
57611963SAfshin.Ardakani@Sun.COM 
57711963SAfshin.Ardakani@Sun.COM /*
57811963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_set_quotas
57911963SAfshin.Ardakani@Sun.COM  *
58011963SAfshin.Ardakani@Sun.COM  * This method sets the list of quota entries.
58111963SAfshin.Ardakani@Sun.COM  *
58211963SAfshin.Ardakani@Sun.COM  * A quota list or threshold value of SMB_QUOTA_UNLIMITED means that
58311963SAfshin.Ardakani@Sun.COM  * the user / group does not have a quota limit. In ZFS this maps to
58411963SAfshin.Ardakani@Sun.COM  * 0 (none).
58511963SAfshin.Ardakani@Sun.COM  * A quota list or threshold value of (SMB_QUOTA_UNLIMITED - 1) means
58611963SAfshin.Ardakani@Sun.COM  * that the user / group quota should be removed. In ZFS this maps to
58711963SAfshin.Ardakani@Sun.COM  * 0 (none).
58811963SAfshin.Ardakani@Sun.COM  */
58911963SAfshin.Ardakani@Sun.COM static uint32_t
smb_quota_zfs_set_quotas(smb_quota_tree_t * qtree,smb_quota_set_t * request)59011963SAfshin.Ardakani@Sun.COM smb_quota_zfs_set_quotas(smb_quota_tree_t *qtree, smb_quota_set_t *request)
59111963SAfshin.Ardakani@Sun.COM {
59211963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_handle_t zfs_hdl;
59311963SAfshin.Ardakani@Sun.COM 	char *typestr, qsetstr[SMB_QUOTA_CMD_STR_LENGTH];
59411963SAfshin.Ardakani@Sun.COM 	char qlimit[SMB_QUOTA_CMD_LENGTH];
59511963SAfshin.Ardakani@Sun.COM 	list_t *quota_list;
59611963SAfshin.Ardakani@Sun.COM 	smb_quota_t *quota;
59711963SAfshin.Ardakani@Sun.COM 	uint32_t id;
59811963SAfshin.Ardakani@Sun.COM 	uint32_t status = NT_STATUS_SUCCESS;
59911963SAfshin.Ardakani@Sun.COM 	uint32_t sidtype;
60011963SAfshin.Ardakani@Sun.COM 
60111963SAfshin.Ardakani@Sun.COM 	status = smb_quota_zfs_init(request->qs_root_path, &zfs_hdl);
60211963SAfshin.Ardakani@Sun.COM 	if (status != NT_STATUS_SUCCESS)
60311963SAfshin.Ardakani@Sun.COM 		return (status);
60411963SAfshin.Ardakani@Sun.COM 
60511963SAfshin.Ardakani@Sun.COM 	quota_list = &request->qs_quota_list;
60611963SAfshin.Ardakani@Sun.COM 	quota = list_head(quota_list);
60711963SAfshin.Ardakani@Sun.COM 
60811963SAfshin.Ardakani@Sun.COM 	while (quota) {
60911963SAfshin.Ardakani@Sun.COM 		if ((quota->q_limit == SMB_QUOTA_UNLIMITED) ||
61011963SAfshin.Ardakani@Sun.COM 		    (quota->q_limit == (SMB_QUOTA_UNLIMITED - 1))) {
61111963SAfshin.Ardakani@Sun.COM 			quota->q_limit = 0;
61211963SAfshin.Ardakani@Sun.COM 		}
61311963SAfshin.Ardakani@Sun.COM 		(void) snprintf(qlimit, SMB_QUOTA_CMD_LENGTH, "%llu",
61411963SAfshin.Ardakani@Sun.COM 		    quota->q_limit);
61511963SAfshin.Ardakani@Sun.COM 
61611963SAfshin.Ardakani@Sun.COM 		sidtype = smb_quota_sidtype(qtree, quota->q_sidstr);
61711963SAfshin.Ardakani@Sun.COM 		switch (sidtype) {
61811963SAfshin.Ardakani@Sun.COM 		case SidTypeUser:
61911963SAfshin.Ardakani@Sun.COM 			typestr = "userquota";
62011963SAfshin.Ardakani@Sun.COM 			break;
62111963SAfshin.Ardakani@Sun.COM 		case SidTypeWellKnownGroup:
62211963SAfshin.Ardakani@Sun.COM 		case SidTypeGroup:
62311963SAfshin.Ardakani@Sun.COM 		case SidTypeAlias:
62411963SAfshin.Ardakani@Sun.COM 			typestr = "groupquota";
62511963SAfshin.Ardakani@Sun.COM 			break;
62611963SAfshin.Ardakani@Sun.COM 		default:
62711963SAfshin.Ardakani@Sun.COM 			syslog(LOG_WARNING, "Failed to set quota for %s: "
62811963SAfshin.Ardakani@Sun.COM 			    "%s (%d) not valid for quotas", quota->q_sidstr,
62911963SAfshin.Ardakani@Sun.COM 			    smb_sid_type2str(sidtype), sidtype);
63011963SAfshin.Ardakani@Sun.COM 			quota = list_next(quota_list, quota);
63111963SAfshin.Ardakani@Sun.COM 			continue;
63211963SAfshin.Ardakani@Sun.COM 		}
63311963SAfshin.Ardakani@Sun.COM 
63411963SAfshin.Ardakani@Sun.COM 		if ((smb_quota_getid(quota->q_sidstr, sidtype, &id) == 0) &&
63511963SAfshin.Ardakani@Sun.COM 		    !(IDMAP_ID_IS_EPHEMERAL(id))) {
63611963SAfshin.Ardakani@Sun.COM 			(void) snprintf(qsetstr, SMB_QUOTA_CMD_STR_LENGTH,
63711963SAfshin.Ardakani@Sun.COM 			    "%s@%d", typestr, id);
63811963SAfshin.Ardakani@Sun.COM 		} else {
63911963SAfshin.Ardakani@Sun.COM 			(void) snprintf(qsetstr, SMB_QUOTA_CMD_STR_LENGTH,
64011963SAfshin.Ardakani@Sun.COM 			    "%s@%s", typestr, quota->q_sidstr);
64111963SAfshin.Ardakani@Sun.COM 		}
64211963SAfshin.Ardakani@Sun.COM 
64311963SAfshin.Ardakani@Sun.COM 		errno = 0;
64411963SAfshin.Ardakani@Sun.COM 		if (zfs_prop_set(zfs_hdl.z_fs, qsetstr, qlimit) != 0) {
64511963SAfshin.Ardakani@Sun.COM 			syslog(LOG_WARNING, "Failed to set quota for %s: %s",
64611963SAfshin.Ardakani@Sun.COM 			    quota->q_sidstr, strerror(errno));
64711963SAfshin.Ardakani@Sun.COM 			status = NT_STATUS_INVALID_PARAMETER;
64811963SAfshin.Ardakani@Sun.COM 			break;
64911963SAfshin.Ardakani@Sun.COM 		}
65011963SAfshin.Ardakani@Sun.COM 
65111963SAfshin.Ardakani@Sun.COM 		quota = list_next(quota_list, quota);
65211963SAfshin.Ardakani@Sun.COM 	}
65311963SAfshin.Ardakani@Sun.COM 
65411963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_fini(&zfs_hdl);
65511963SAfshin.Ardakani@Sun.COM 	return (status);
65611963SAfshin.Ardakani@Sun.COM }
65711963SAfshin.Ardakani@Sun.COM 
65811963SAfshin.Ardakani@Sun.COM /*
65911963SAfshin.Ardakani@Sun.COM  * smb_quota_sidtype
66011963SAfshin.Ardakani@Sun.COM  *
66111963SAfshin.Ardakani@Sun.COM  * Determine the type of the sid. If the sid exists in
66211963SAfshin.Ardakani@Sun.COM  * the qtree get its type from there, otherwise do an
66311963SAfshin.Ardakani@Sun.COM  * lsa_lookup_sid().
66411963SAfshin.Ardakani@Sun.COM  */
66511963SAfshin.Ardakani@Sun.COM static uint32_t
smb_quota_sidtype(smb_quota_tree_t * qtree,char * sidstr)66611963SAfshin.Ardakani@Sun.COM smb_quota_sidtype(smb_quota_tree_t *qtree, char *sidstr)
66711963SAfshin.Ardakani@Sun.COM {
66811963SAfshin.Ardakani@Sun.COM 	smb_quota_t key, *quota;
66911963SAfshin.Ardakani@Sun.COM 	avl_index_t where;
67011963SAfshin.Ardakani@Sun.COM 	smb_sid_t *sid = NULL;
67111963SAfshin.Ardakani@Sun.COM 	smb_account_t ainfo;
67211963SAfshin.Ardakani@Sun.COM 	uint32_t sidtype = SidTypeUnknown;
67311963SAfshin.Ardakani@Sun.COM 
67411963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(key.q_sidstr, sidstr, SMB_SID_STRSZ);
67511963SAfshin.Ardakani@Sun.COM 	quota = avl_find(&qtree->qt_avl, &key, &where);
67611963SAfshin.Ardakani@Sun.COM 	if (quota)
67711963SAfshin.Ardakani@Sun.COM 		return (quota->q_sidtype);
67811963SAfshin.Ardakani@Sun.COM 
67911963SAfshin.Ardakani@Sun.COM 	sid = smb_sid_fromstr(sidstr);
68011963SAfshin.Ardakani@Sun.COM 	if (sid != NULL) {
68111963SAfshin.Ardakani@Sun.COM 		if (lsa_lookup_sid(sid, &ainfo) == NT_STATUS_SUCCESS) {
68211963SAfshin.Ardakani@Sun.COM 			sidtype = ainfo.a_type;
68311963SAfshin.Ardakani@Sun.COM 			smb_account_free(&ainfo);
68411963SAfshin.Ardakani@Sun.COM 		}
68511963SAfshin.Ardakani@Sun.COM 		smb_sid_free(sid);
68611963SAfshin.Ardakani@Sun.COM 	}
68711963SAfshin.Ardakani@Sun.COM 	return (sidtype);
68811963SAfshin.Ardakani@Sun.COM }
68911963SAfshin.Ardakani@Sun.COM 
69011963SAfshin.Ardakani@Sun.COM /*
69111963SAfshin.Ardakani@Sun.COM  * smb_quota_getid
69211963SAfshin.Ardakani@Sun.COM  *
69311963SAfshin.Ardakani@Sun.COM  * Get the user/group id for the sid.
69411963SAfshin.Ardakani@Sun.COM  */
69511963SAfshin.Ardakani@Sun.COM static int
smb_quota_getid(char * sidstr,uint32_t sidtype,uint32_t * id)69611963SAfshin.Ardakani@Sun.COM smb_quota_getid(char *sidstr, uint32_t sidtype, uint32_t *id)
69711963SAfshin.Ardakani@Sun.COM {
69811963SAfshin.Ardakani@Sun.COM 	int rc = 0;
69911963SAfshin.Ardakani@Sun.COM 	smb_sid_t *sid = NULL;
70011963SAfshin.Ardakani@Sun.COM 	int idtype;
70111963SAfshin.Ardakani@Sun.COM 
70211963SAfshin.Ardakani@Sun.COM 	sid = smb_sid_fromstr(sidstr);
70311963SAfshin.Ardakani@Sun.COM 	if (sid == NULL)
70411963SAfshin.Ardakani@Sun.COM 		return (-1);
70511963SAfshin.Ardakani@Sun.COM 
70611963SAfshin.Ardakani@Sun.COM 	switch (sidtype) {
70711963SAfshin.Ardakani@Sun.COM 	case SidTypeUser:
70811963SAfshin.Ardakani@Sun.COM 		idtype = SMB_IDMAP_USER;
70911963SAfshin.Ardakani@Sun.COM 		break;
71011963SAfshin.Ardakani@Sun.COM 	case SidTypeWellKnownGroup:
71111963SAfshin.Ardakani@Sun.COM 	case SidTypeGroup:
71211963SAfshin.Ardakani@Sun.COM 	case SidTypeAlias:
71311963SAfshin.Ardakani@Sun.COM 		idtype = SMB_IDMAP_GROUP;
71411963SAfshin.Ardakani@Sun.COM 		break;
71511963SAfshin.Ardakani@Sun.COM 	default:
71611963SAfshin.Ardakani@Sun.COM 		rc = -1;
71711963SAfshin.Ardakani@Sun.COM 		break;
71811963SAfshin.Ardakani@Sun.COM 	}
71911963SAfshin.Ardakani@Sun.COM 
72011963SAfshin.Ardakani@Sun.COM 	if (rc == 0)
72111963SAfshin.Ardakani@Sun.COM 		rc = smb_idmap_getid(sid, id, &idtype);
72211963SAfshin.Ardakani@Sun.COM 
72311963SAfshin.Ardakani@Sun.COM 	smb_sid_free(sid);
72411963SAfshin.Ardakani@Sun.COM 
72511963SAfshin.Ardakani@Sun.COM 	return (rc);
72611963SAfshin.Ardakani@Sun.COM }
72711963SAfshin.Ardakani@Sun.COM 
72811963SAfshin.Ardakani@Sun.COM /*
72911963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_lookup
73011963SAfshin.Ardakani@Sun.COM  *
73111963SAfshin.Ardakani@Sun.COM  * Find the quota tree in smb_quota_fs_list.
73211963SAfshin.Ardakani@Sun.COM  *
73311963SAfshin.Ardakani@Sun.COM  * If the tree is found but is locked, waits for it to become available.
73411963SAfshin.Ardakani@Sun.COM  * If the tree is available, locks it and returns it.
73511963SAfshin.Ardakani@Sun.COM  * Otherwise, returns NULL.
73611963SAfshin.Ardakani@Sun.COM  */
73711963SAfshin.Ardakani@Sun.COM static smb_quota_tree_t *
smb_quota_tree_lookup(const char * path)73811963SAfshin.Ardakani@Sun.COM smb_quota_tree_lookup(const char *path)
73911963SAfshin.Ardakani@Sun.COM {
74011963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree = NULL;
74111963SAfshin.Ardakani@Sun.COM 
74211963SAfshin.Ardakani@Sun.COM 	assert(path);
74311963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
74411963SAfshin.Ardakani@Sun.COM 
74511963SAfshin.Ardakani@Sun.COM 	qtree = list_head(&smb_quota_fs_list);
74611963SAfshin.Ardakani@Sun.COM 	while (qtree != NULL) {
74711963SAfshin.Ardakani@Sun.COM 		if (!smb_quota_list_init || smb_quota_shutdown) {
74811963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&smb_quota_list_mutex);
74911963SAfshin.Ardakani@Sun.COM 			return (NULL);
75011963SAfshin.Ardakani@Sun.COM 		}
75111963SAfshin.Ardakani@Sun.COM 
75211963SAfshin.Ardakani@Sun.COM 		(void) mutex_lock(&qtree->qt_mutex);
75311963SAfshin.Ardakani@Sun.COM 		assert(qtree->qt_refcnt > 0);
75411963SAfshin.Ardakani@Sun.COM 
75511963SAfshin.Ardakani@Sun.COM 		if (!smb_quota_tree_match(qtree, path)) {
75611963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&qtree->qt_mutex);
75711963SAfshin.Ardakani@Sun.COM 			qtree = list_next(&smb_quota_fs_list, qtree);
75811963SAfshin.Ardakani@Sun.COM 			continue;
75911963SAfshin.Ardakani@Sun.COM 		}
76011963SAfshin.Ardakani@Sun.COM 
76111963SAfshin.Ardakani@Sun.COM 		if (qtree->qt_locked) {
76211963SAfshin.Ardakani@Sun.COM 			(void) mutex_unlock(&qtree->qt_mutex);
76311963SAfshin.Ardakani@Sun.COM 			(void) cond_wait(&smb_quota_list_condvar,
76411963SAfshin.Ardakani@Sun.COM 			    &smb_quota_list_mutex);
76511963SAfshin.Ardakani@Sun.COM 			qtree = list_head(&smb_quota_fs_list);
76611963SAfshin.Ardakani@Sun.COM 			continue;
76711963SAfshin.Ardakani@Sun.COM 		}
76811963SAfshin.Ardakani@Sun.COM 
76911963SAfshin.Ardakani@Sun.COM 		++(qtree->qt_refcnt);
77011963SAfshin.Ardakani@Sun.COM 		qtree->qt_locked = B_TRUE;
77111963SAfshin.Ardakani@Sun.COM 		(void) mutex_unlock(&qtree->qt_mutex);
77211963SAfshin.Ardakani@Sun.COM 		break;
77311963SAfshin.Ardakani@Sun.COM 	};
77411963SAfshin.Ardakani@Sun.COM 
77511963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
77611963SAfshin.Ardakani@Sun.COM 	return (qtree);
77711963SAfshin.Ardakani@Sun.COM }
77811963SAfshin.Ardakani@Sun.COM 
77911963SAfshin.Ardakani@Sun.COM /*
78011963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_release
78111963SAfshin.Ardakani@Sun.COM  */
78211963SAfshin.Ardakani@Sun.COM static void
smb_quota_tree_release(smb_quota_tree_t * qtree)78311963SAfshin.Ardakani@Sun.COM smb_quota_tree_release(smb_quota_tree_t *qtree)
78411963SAfshin.Ardakani@Sun.COM {
78511963SAfshin.Ardakani@Sun.COM 	boolean_t delete;
78611963SAfshin.Ardakani@Sun.COM 
78711963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&qtree->qt_mutex);
78811963SAfshin.Ardakani@Sun.COM 	assert(qtree->qt_locked);
78911963SAfshin.Ardakani@Sun.COM 	assert(qtree->qt_refcnt > 0);
79011963SAfshin.Ardakani@Sun.COM 
79111963SAfshin.Ardakani@Sun.COM 	--(qtree->qt_refcnt);
79211963SAfshin.Ardakani@Sun.COM 	qtree->qt_locked = B_FALSE;
79311963SAfshin.Ardakani@Sun.COM 	delete = (qtree->qt_refcnt == 0);
79411963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&qtree->qt_mutex);
79511963SAfshin.Ardakani@Sun.COM 
79611963SAfshin.Ardakani@Sun.COM 	(void) mutex_lock(&smb_quota_list_mutex);
79711963SAfshin.Ardakani@Sun.COM 	if (delete)
79811963SAfshin.Ardakani@Sun.COM 		smb_quota_tree_delete(qtree);
79911963SAfshin.Ardakani@Sun.COM 	(void) cond_broadcast(&smb_quota_list_condvar);
80011963SAfshin.Ardakani@Sun.COM 	(void) mutex_unlock(&smb_quota_list_mutex);
80111963SAfshin.Ardakani@Sun.COM }
80211963SAfshin.Ardakani@Sun.COM 
80311963SAfshin.Ardakani@Sun.COM /*
80411963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_match
80511963SAfshin.Ardakani@Sun.COM  *
80611963SAfshin.Ardakani@Sun.COM  * Determine if qtree represents the file system identified by path
80711963SAfshin.Ardakani@Sun.COM  */
80811963SAfshin.Ardakani@Sun.COM static boolean_t
smb_quota_tree_match(smb_quota_tree_t * qtree,const char * path)80911963SAfshin.Ardakani@Sun.COM smb_quota_tree_match(smb_quota_tree_t *qtree, const char *path)
81011963SAfshin.Ardakani@Sun.COM {
81111963SAfshin.Ardakani@Sun.COM 	return (strncmp(qtree->qt_path, path, MAXPATHLEN) == 0);
81211963SAfshin.Ardakani@Sun.COM }
81311963SAfshin.Ardakani@Sun.COM 
81411963SAfshin.Ardakani@Sun.COM /*
81511963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_create
81611963SAfshin.Ardakani@Sun.COM  *
81711963SAfshin.Ardakani@Sun.COM  * Create and initialize an smb_quota_tree_t structure
81811963SAfshin.Ardakani@Sun.COM  */
81911963SAfshin.Ardakani@Sun.COM static smb_quota_tree_t *
smb_quota_tree_create(const char * path)82011963SAfshin.Ardakani@Sun.COM smb_quota_tree_create(const char *path)
82111963SAfshin.Ardakani@Sun.COM {
82211963SAfshin.Ardakani@Sun.COM 	smb_quota_tree_t *qtree;
82311963SAfshin.Ardakani@Sun.COM 
82411963SAfshin.Ardakani@Sun.COM 	assert(MUTEX_HELD(&smb_quota_list_mutex));
82511963SAfshin.Ardakani@Sun.COM 
82612065SKeyur.Desai@Sun.COM 	qtree = calloc(sizeof (smb_quota_tree_t), 1);
82711963SAfshin.Ardakani@Sun.COM 	if (qtree == NULL)
82811963SAfshin.Ardakani@Sun.COM 		return (NULL);
82911963SAfshin.Ardakani@Sun.COM 
83011963SAfshin.Ardakani@Sun.COM 	qtree->qt_path = strdup(path);
83111963SAfshin.Ardakani@Sun.COM 	if (qtree->qt_path == NULL) {
83211963SAfshin.Ardakani@Sun.COM 		free(qtree);
83311963SAfshin.Ardakani@Sun.COM 		return (NULL);
83411963SAfshin.Ardakani@Sun.COM 	}
83511963SAfshin.Ardakani@Sun.COM 
83611963SAfshin.Ardakani@Sun.COM 	qtree->qt_timestamp = 0;
83711963SAfshin.Ardakani@Sun.COM 	qtree->qt_locked = B_FALSE;
83811963SAfshin.Ardakani@Sun.COM 	qtree->qt_refcnt = 1;
83911963SAfshin.Ardakani@Sun.COM 	qtree->qt_sharecnt = 1;
84011963SAfshin.Ardakani@Sun.COM 
84111963SAfshin.Ardakani@Sun.COM 	avl_create(&qtree->qt_avl, smb_quota_sid_cmp,
84211963SAfshin.Ardakani@Sun.COM 	    sizeof (smb_quota_t), offsetof(smb_quota_t, q_avl_node));
84311963SAfshin.Ardakani@Sun.COM 
84411963SAfshin.Ardakani@Sun.COM 	++smb_quota_tree_cnt;
84511963SAfshin.Ardakani@Sun.COM 	return (qtree);
84611963SAfshin.Ardakani@Sun.COM }
84711963SAfshin.Ardakani@Sun.COM 
84811963SAfshin.Ardakani@Sun.COM /*
84911963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_delete
85011963SAfshin.Ardakani@Sun.COM  *
85111963SAfshin.Ardakani@Sun.COM  * Free and delete the smb_quota_tree_t structure.
85211963SAfshin.Ardakani@Sun.COM  * qtree must have no users (refcnt == 0).
85311963SAfshin.Ardakani@Sun.COM  */
85411963SAfshin.Ardakani@Sun.COM static void
smb_quota_tree_delete(smb_quota_tree_t * qtree)85511963SAfshin.Ardakani@Sun.COM smb_quota_tree_delete(smb_quota_tree_t *qtree)
85611963SAfshin.Ardakani@Sun.COM {
85711963SAfshin.Ardakani@Sun.COM 	void *cookie = NULL;
85811963SAfshin.Ardakani@Sun.COM 	smb_quota_t *node;
85911963SAfshin.Ardakani@Sun.COM 
86011963SAfshin.Ardakani@Sun.COM 	assert(MUTEX_HELD(&smb_quota_list_mutex));
86111963SAfshin.Ardakani@Sun.COM 	assert(qtree->qt_refcnt == 0);
86211963SAfshin.Ardakani@Sun.COM 
86311963SAfshin.Ardakani@Sun.COM 	while ((node = avl_destroy_nodes(&qtree->qt_avl, &cookie)) != NULL)
86411963SAfshin.Ardakani@Sun.COM 		free(node);
86511963SAfshin.Ardakani@Sun.COM 	avl_destroy(&qtree->qt_avl);
86611963SAfshin.Ardakani@Sun.COM 
86711963SAfshin.Ardakani@Sun.COM 	free(qtree->qt_path);
86811963SAfshin.Ardakani@Sun.COM 	free(qtree);
86911963SAfshin.Ardakani@Sun.COM 
87011963SAfshin.Ardakani@Sun.COM 	--smb_quota_tree_cnt;
87111963SAfshin.Ardakani@Sun.COM }
87211963SAfshin.Ardakani@Sun.COM 
87311963SAfshin.Ardakani@Sun.COM /*
87411963SAfshin.Ardakani@Sun.COM  * smb_quota_sid_cmp
87511963SAfshin.Ardakani@Sun.COM  *
87611963SAfshin.Ardakani@Sun.COM  * Comparision function for nodes in an AVL tree which holds quota
87711963SAfshin.Ardakani@Sun.COM  * entries indexed by SID.
87811963SAfshin.Ardakani@Sun.COM  */
87911963SAfshin.Ardakani@Sun.COM static int
smb_quota_sid_cmp(const void * l_arg,const void * r_arg)88011963SAfshin.Ardakani@Sun.COM smb_quota_sid_cmp(const void *l_arg, const void *r_arg)
88111963SAfshin.Ardakani@Sun.COM {
88211963SAfshin.Ardakani@Sun.COM 	const char *l_sid = ((smb_quota_t *)l_arg)->q_sidstr;
88311963SAfshin.Ardakani@Sun.COM 	const char *r_sid = ((smb_quota_t *)r_arg)->q_sidstr;
88411963SAfshin.Ardakani@Sun.COM 	int ret;
88511963SAfshin.Ardakani@Sun.COM 
88611963SAfshin.Ardakani@Sun.COM 	ret = strncasecmp(l_sid, r_sid, SMB_SID_STRSZ);
88711963SAfshin.Ardakani@Sun.COM 
88811963SAfshin.Ardakani@Sun.COM 	if (ret > 0)
88911963SAfshin.Ardakani@Sun.COM 		return (1);
89011963SAfshin.Ardakani@Sun.COM 	if (ret < 0)
89111963SAfshin.Ardakani@Sun.COM 		return (-1);
89211963SAfshin.Ardakani@Sun.COM 	return (0);
89311963SAfshin.Ardakani@Sun.COM }
89411963SAfshin.Ardakani@Sun.COM 
89511963SAfshin.Ardakani@Sun.COM /*
89611963SAfshin.Ardakani@Sun.COM  * smb_quota_tree_populate
89711963SAfshin.Ardakani@Sun.COM  *
89811963SAfshin.Ardakani@Sun.COM  * If the quota tree needs to be (re)populated:
89911963SAfshin.Ardakani@Sun.COM  * - delete the qtree's contents
90011963SAfshin.Ardakani@Sun.COM  * - repopulate the qtree from zfs
90111963SAfshin.Ardakani@Sun.COM  * - set the qtree's timestamp.
90211963SAfshin.Ardakani@Sun.COM  */
90311963SAfshin.Ardakani@Sun.COM static uint32_t
smb_quota_tree_populate(smb_quota_tree_t * qtree)90411963SAfshin.Ardakani@Sun.COM smb_quota_tree_populate(smb_quota_tree_t *qtree)
90511963SAfshin.Ardakani@Sun.COM {
90611963SAfshin.Ardakani@Sun.COM 	void *cookie = NULL;
90711963SAfshin.Ardakani@Sun.COM 	void *node;
90811963SAfshin.Ardakani@Sun.COM 	uint32_t status;
90911963SAfshin.Ardakani@Sun.COM 
91011963SAfshin.Ardakani@Sun.COM 	assert(qtree->qt_locked);
91111963SAfshin.Ardakani@Sun.COM 
91211963SAfshin.Ardakani@Sun.COM 	if (!smb_quota_tree_expired(qtree))
91311963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_SUCCESS);
91411963SAfshin.Ardakani@Sun.COM 
91511963SAfshin.Ardakani@Sun.COM 	while ((node = avl_destroy_nodes(&qtree->qt_avl, &cookie)) != NULL)
91611963SAfshin.Ardakani@Sun.COM 		free(node);
91711963SAfshin.Ardakani@Sun.COM 
91811963SAfshin.Ardakani@Sun.COM 	status = smb_quota_zfs_get_quotas(qtree);
91911963SAfshin.Ardakani@Sun.COM 	if (status != NT_STATUS_SUCCESS)
92011963SAfshin.Ardakani@Sun.COM 		return (status);
92111963SAfshin.Ardakani@Sun.COM 
92211963SAfshin.Ardakani@Sun.COM 	qtree->qt_timestamp = time(NULL);
92311963SAfshin.Ardakani@Sun.COM 
92411963SAfshin.Ardakani@Sun.COM 	return (NT_STATUS_SUCCESS);
92511963SAfshin.Ardakani@Sun.COM }
92611963SAfshin.Ardakani@Sun.COM 
92711963SAfshin.Ardakani@Sun.COM static boolean_t
smb_quota_tree_expired(smb_quota_tree_t * qtree)92811963SAfshin.Ardakani@Sun.COM smb_quota_tree_expired(smb_quota_tree_t *qtree)
92911963SAfshin.Ardakani@Sun.COM {
93011963SAfshin.Ardakani@Sun.COM 	time_t tnow = time(NULL);
93111963SAfshin.Ardakani@Sun.COM 	return ((tnow - qtree->qt_timestamp) > SMB_QUOTA_REFRESH);
93211963SAfshin.Ardakani@Sun.COM }
93311963SAfshin.Ardakani@Sun.COM 
93411963SAfshin.Ardakani@Sun.COM static void
smb_quota_tree_set_expired(smb_quota_tree_t * qtree)93511963SAfshin.Ardakani@Sun.COM smb_quota_tree_set_expired(smb_quota_tree_t *qtree)
93611963SAfshin.Ardakani@Sun.COM {
93711963SAfshin.Ardakani@Sun.COM 	qtree->qt_timestamp = 0;
93811963SAfshin.Ardakani@Sun.COM }
93911963SAfshin.Ardakani@Sun.COM 
94011963SAfshin.Ardakani@Sun.COM /*
94111963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_get_quotas
94211963SAfshin.Ardakani@Sun.COM  *
94311963SAfshin.Ardakani@Sun.COM  * Get user and group quotas from ZFS and use them to
94411963SAfshin.Ardakani@Sun.COM  * populate the quota tree.
94511963SAfshin.Ardakani@Sun.COM  */
94611963SAfshin.Ardakani@Sun.COM static uint32_t
smb_quota_zfs_get_quotas(smb_quota_tree_t * qtree)94711963SAfshin.Ardakani@Sun.COM smb_quota_zfs_get_quotas(smb_quota_tree_t *qtree)
94811963SAfshin.Ardakani@Sun.COM {
94911963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_handle_t zfs_hdl;
95011963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_arg_t arg;
95111963SAfshin.Ardakani@Sun.COM 	zfs_userquota_prop_t p;
95211963SAfshin.Ardakani@Sun.COM 	uint32_t status = NT_STATUS_SUCCESS;
95311963SAfshin.Ardakani@Sun.COM 
95411963SAfshin.Ardakani@Sun.COM 	status = smb_quota_zfs_init(qtree->qt_path, &zfs_hdl);
95511963SAfshin.Ardakani@Sun.COM 	if (status != NT_STATUS_SUCCESS)
95611963SAfshin.Ardakani@Sun.COM 		return (status);
95711963SAfshin.Ardakani@Sun.COM 
95811963SAfshin.Ardakani@Sun.COM 	arg.qa_avl = &qtree->qt_avl;
95911963SAfshin.Ardakani@Sun.COM 	for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
96011963SAfshin.Ardakani@Sun.COM 		arg.qa_prop = p;
96111963SAfshin.Ardakani@Sun.COM 		if (zfs_userspace(zfs_hdl.z_fs, p,
96211963SAfshin.Ardakani@Sun.COM 		    smb_quota_zfs_callback, &arg) != 0) {
96311963SAfshin.Ardakani@Sun.COM 			status = NT_STATUS_INTERNAL_ERROR;
96411963SAfshin.Ardakani@Sun.COM 			break;
96511963SAfshin.Ardakani@Sun.COM 		}
96611963SAfshin.Ardakani@Sun.COM 	}
96711963SAfshin.Ardakani@Sun.COM 
96811963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_fini(&zfs_hdl);
96911963SAfshin.Ardakani@Sun.COM 	return (status);
97011963SAfshin.Ardakani@Sun.COM }
97111963SAfshin.Ardakani@Sun.COM 
97211963SAfshin.Ardakani@Sun.COM /*
97311963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_callback
97411963SAfshin.Ardakani@Sun.COM  *
97511963SAfshin.Ardakani@Sun.COM  * Find or create a node in the avl tree (arg->qa_avl) that matches
97611963SAfshin.Ardakani@Sun.COM  * the SID derived from domain and rid. If no domain is specified,
97711963SAfshin.Ardakani@Sun.COM  * lookup the sid (smb_quota_sidstr()).
97811963SAfshin.Ardakani@Sun.COM  * Populate the node.
97911963SAfshin.Ardakani@Sun.COM  * The property type (arg->qa_prop) determines which property 'space'
98011963SAfshin.Ardakani@Sun.COM  * refers to.
98111963SAfshin.Ardakani@Sun.COM  */
98211963SAfshin.Ardakani@Sun.COM static int
smb_quota_zfs_callback(void * arg,const char * domain,uid_t rid,uint64_t space)98311963SAfshin.Ardakani@Sun.COM smb_quota_zfs_callback(void *arg, const char *domain, uid_t rid, uint64_t space)
98411963SAfshin.Ardakani@Sun.COM {
98511963SAfshin.Ardakani@Sun.COM 	smb_quota_zfs_arg_t *qarg = (smb_quota_zfs_arg_t *)arg;
98611963SAfshin.Ardakani@Sun.COM 	zfs_userquota_prop_t qprop = qarg->qa_prop;
98711963SAfshin.Ardakani@Sun.COM 	avl_tree_t *avl_tree = qarg->qa_avl;
98811963SAfshin.Ardakani@Sun.COM 	avl_index_t where;
98911963SAfshin.Ardakani@Sun.COM 	smb_quota_t *quota, key;
99011963SAfshin.Ardakani@Sun.COM 
99111963SAfshin.Ardakani@Sun.COM 	if (domain == NULL || domain[0] == '\0') {
99211963SAfshin.Ardakani@Sun.COM 		if (smb_quota_sidstr(rid, qprop, key.q_sidstr) != 0)
99311963SAfshin.Ardakani@Sun.COM 			return (0);
99411963SAfshin.Ardakani@Sun.COM 	} else {
99511963SAfshin.Ardakani@Sun.COM 		(void) snprintf(key.q_sidstr, SMB_SID_STRSZ, "%s-%u",
99611963SAfshin.Ardakani@Sun.COM 		    domain, (uint32_t)rid);
99711963SAfshin.Ardakani@Sun.COM 	}
99811963SAfshin.Ardakani@Sun.COM 
99911963SAfshin.Ardakani@Sun.COM 	quota = avl_find(avl_tree, &key, &where);
100011963SAfshin.Ardakani@Sun.COM 	if (quota == NULL) {
100111963SAfshin.Ardakani@Sun.COM 		quota = malloc(sizeof (smb_quota_t));
100211963SAfshin.Ardakani@Sun.COM 		if (quota == NULL)
100311963SAfshin.Ardakani@Sun.COM 			return (NT_STATUS_NO_MEMORY);
100411963SAfshin.Ardakani@Sun.COM 		bzero(quota, sizeof (smb_quota_t));
100511963SAfshin.Ardakani@Sun.COM 		quota->q_thresh = SMB_QUOTA_UNLIMITED;
100611963SAfshin.Ardakani@Sun.COM 		quota->q_limit = SMB_QUOTA_UNLIMITED;
100711963SAfshin.Ardakani@Sun.COM 		avl_insert(avl_tree, (void *)quota, where);
100811963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(quota->q_sidstr, key.q_sidstr, SMB_SID_STRSZ);
100911963SAfshin.Ardakani@Sun.COM 	}
101011963SAfshin.Ardakani@Sun.COM 
101111963SAfshin.Ardakani@Sun.COM 	switch (qprop) {
101211963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_USERUSED:
101311963SAfshin.Ardakani@Sun.COM 		quota->q_sidtype = SidTypeUser;
101411963SAfshin.Ardakani@Sun.COM 		quota->q_used = space;
101511963SAfshin.Ardakani@Sun.COM 		break;
101611963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_GROUPUSED:
101711963SAfshin.Ardakani@Sun.COM 		quota->q_sidtype = SidTypeGroup;
101811963SAfshin.Ardakani@Sun.COM 		quota->q_used = space;
101911963SAfshin.Ardakani@Sun.COM 		break;
102011963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_USERQUOTA:
102111963SAfshin.Ardakani@Sun.COM 		quota->q_sidtype = SidTypeUser;
102211963SAfshin.Ardakani@Sun.COM 		quota->q_limit = space;
102311963SAfshin.Ardakani@Sun.COM 		break;
102411963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_GROUPQUOTA:
102511963SAfshin.Ardakani@Sun.COM 		quota->q_sidtype = SidTypeGroup;
102611963SAfshin.Ardakani@Sun.COM 		quota->q_limit = space;
102711963SAfshin.Ardakani@Sun.COM 		break;
102811963SAfshin.Ardakani@Sun.COM 	default:
102911963SAfshin.Ardakani@Sun.COM 		break;
103011963SAfshin.Ardakani@Sun.COM 	}
103111963SAfshin.Ardakani@Sun.COM 
103211963SAfshin.Ardakani@Sun.COM 	quota->q_thresh = quota->q_limit;
103311963SAfshin.Ardakani@Sun.COM 
103411963SAfshin.Ardakani@Sun.COM 	return (0);
103511963SAfshin.Ardakani@Sun.COM }
103611963SAfshin.Ardakani@Sun.COM 
103711963SAfshin.Ardakani@Sun.COM /*
103811963SAfshin.Ardakani@Sun.COM  * smb_quota_sidstr
103911963SAfshin.Ardakani@Sun.COM  *
104011963SAfshin.Ardakani@Sun.COM  * Use idmap to get the sid for the specified id and return
104111963SAfshin.Ardakani@Sun.COM  * the string version of the sid in sidstr.
104211963SAfshin.Ardakani@Sun.COM  * sidstr must be a buffer of at least SMB_SID_STRSZ.
104311963SAfshin.Ardakani@Sun.COM  */
104411963SAfshin.Ardakani@Sun.COM static int
smb_quota_sidstr(uint32_t id,zfs_userquota_prop_t qprop,char * sidstr)104511963SAfshin.Ardakani@Sun.COM smb_quota_sidstr(uint32_t id, zfs_userquota_prop_t qprop, char *sidstr)
104611963SAfshin.Ardakani@Sun.COM {
104711963SAfshin.Ardakani@Sun.COM 	int idtype;
104811963SAfshin.Ardakani@Sun.COM 	smb_sid_t *sid;
104911963SAfshin.Ardakani@Sun.COM 
105011963SAfshin.Ardakani@Sun.COM 	switch (qprop) {
105111963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_USERUSED:
105211963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_USERQUOTA:
105311963SAfshin.Ardakani@Sun.COM 		idtype = SMB_IDMAP_USER;
105411963SAfshin.Ardakani@Sun.COM 		break;
105511963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_GROUPUSED:
105611963SAfshin.Ardakani@Sun.COM 	case ZFS_PROP_GROUPQUOTA:
105711963SAfshin.Ardakani@Sun.COM 		idtype = SMB_IDMAP_GROUP;
105811963SAfshin.Ardakani@Sun.COM 		break;
105911963SAfshin.Ardakani@Sun.COM 	default:
106011963SAfshin.Ardakani@Sun.COM 		return (-1);
106111963SAfshin.Ardakani@Sun.COM 	}
106211963SAfshin.Ardakani@Sun.COM 
106311963SAfshin.Ardakani@Sun.COM 	if (smb_idmap_getsid(id, idtype, &sid) != IDMAP_SUCCESS)
106411963SAfshin.Ardakani@Sun.COM 		return (-1);
106511963SAfshin.Ardakani@Sun.COM 
106611963SAfshin.Ardakani@Sun.COM 	smb_sid_tostr(sid, sidstr);
106711963SAfshin.Ardakani@Sun.COM 	smb_sid_free(sid);
106811963SAfshin.Ardakani@Sun.COM 
106911963SAfshin.Ardakani@Sun.COM 	return (0);
107011963SAfshin.Ardakani@Sun.COM }
107111963SAfshin.Ardakani@Sun.COM 
107211963SAfshin.Ardakani@Sun.COM /*
107311963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_init
107411963SAfshin.Ardakani@Sun.COM  *
107511963SAfshin.Ardakani@Sun.COM  * Initialize zfs library and dataset handles
107611963SAfshin.Ardakani@Sun.COM  */
107711963SAfshin.Ardakani@Sun.COM static uint32_t
smb_quota_zfs_init(const char * path,smb_quota_zfs_handle_t * zfs_hdl)107811963SAfshin.Ardakani@Sun.COM smb_quota_zfs_init(const char *path, smb_quota_zfs_handle_t *zfs_hdl)
107911963SAfshin.Ardakani@Sun.COM {
108011963SAfshin.Ardakani@Sun.COM 	char dataset[MAXPATHLEN];
108111963SAfshin.Ardakani@Sun.COM 
108211963SAfshin.Ardakani@Sun.COM 	if (smb_getdataset(path, dataset, MAXPATHLEN) != 0)
108311963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_INVALID_PARAMETER);
108411963SAfshin.Ardakani@Sun.COM 
108511963SAfshin.Ardakani@Sun.COM 	if ((zfs_hdl->z_lib = libzfs_init()) == NULL)
108611963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_INTERNAL_ERROR);
108711963SAfshin.Ardakani@Sun.COM 
108811963SAfshin.Ardakani@Sun.COM 	zfs_hdl->z_fs = zfs_open(zfs_hdl->z_lib, dataset, ZFS_TYPE_DATASET);
108911963SAfshin.Ardakani@Sun.COM 	if (zfs_hdl->z_fs == NULL) {
109011963SAfshin.Ardakani@Sun.COM 		libzfs_fini(zfs_hdl->z_lib);
109111963SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_ACCESS_DENIED);
109211963SAfshin.Ardakani@Sun.COM 	}
109311963SAfshin.Ardakani@Sun.COM 
109411963SAfshin.Ardakani@Sun.COM 	return (NT_STATUS_SUCCESS);
109511963SAfshin.Ardakani@Sun.COM }
109611963SAfshin.Ardakani@Sun.COM 
109711963SAfshin.Ardakani@Sun.COM /*
109811963SAfshin.Ardakani@Sun.COM  * smb_quota_zfs_fini
109911963SAfshin.Ardakani@Sun.COM  *
110011963SAfshin.Ardakani@Sun.COM  * Close zfs library and dataset handles
110111963SAfshin.Ardakani@Sun.COM  */
110211963SAfshin.Ardakani@Sun.COM static void
smb_quota_zfs_fini(smb_quota_zfs_handle_t * zfs_hdl)110311963SAfshin.Ardakani@Sun.COM smb_quota_zfs_fini(smb_quota_zfs_handle_t *zfs_hdl)
110411963SAfshin.Ardakani@Sun.COM {
110511963SAfshin.Ardakani@Sun.COM 	zfs_close(zfs_hdl->z_fs);
110611963SAfshin.Ardakani@Sun.COM 	libzfs_fini(zfs_hdl->z_lib);
110711963SAfshin.Ardakani@Sun.COM }
110811963SAfshin.Ardakani@Sun.COM 
110911963SAfshin.Ardakani@Sun.COM /*
111011963SAfshin.Ardakani@Sun.COM  * smb_quota_add_ctrldir
111111963SAfshin.Ardakani@Sun.COM  *
111211963SAfshin.Ardakani@Sun.COM  * In order to display the quota properties tab, windows clients
111311963SAfshin.Ardakani@Sun.COM  * check for the existence of the quota control file, created
111411963SAfshin.Ardakani@Sun.COM  * here as follows:
111511963SAfshin.Ardakani@Sun.COM  * - Create SMB_QUOTA_CNTRL_DIR directory (with A_HIDDEN & A_SYSTEM
111611963SAfshin.Ardakani@Sun.COM  *   attributes).
111711963SAfshin.Ardakani@Sun.COM  * - Create the SMB_QUOTA_CNTRL_FILE file (with extended attribute
111811963SAfshin.Ardakani@Sun.COM  *   SMB_QUOTA_CNTRL_INDEX_XATTR) in the SMB_QUOTA_CNTRL_DIR directory.
111911963SAfshin.Ardakani@Sun.COM  * - Set the acl of SMB_QUOTA_CNTRL_FILE file to SMB_QUOTA_CNTRL_PERM.
112011963SAfshin.Ardakani@Sun.COM  */
112111963SAfshin.Ardakani@Sun.COM static void
smb_quota_add_ctrldir(const char * path)112211963SAfshin.Ardakani@Sun.COM smb_quota_add_ctrldir(const char *path)
112311963SAfshin.Ardakani@Sun.COM {
112411963SAfshin.Ardakani@Sun.COM 	int newfd, dirfd, afd;
112511963SAfshin.Ardakani@Sun.COM 	nvlist_t *request;
112611963SAfshin.Ardakani@Sun.COM 	char dir[MAXPATHLEN], file[MAXPATHLEN];
112711963SAfshin.Ardakani@Sun.COM 	acl_t *aclp;
112811963SAfshin.Ardakani@Sun.COM 	struct stat statbuf;
112911963SAfshin.Ardakani@Sun.COM 
113011963SAfshin.Ardakani@Sun.COM 	assert(path != NULL);
113111963SAfshin.Ardakani@Sun.COM 
113211963SAfshin.Ardakani@Sun.COM 	(void) snprintf(dir, MAXPATHLEN, ".%s/%s", path, SMB_QUOTA_CNTRL_DIR);
113311963SAfshin.Ardakani@Sun.COM 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dir, SMB_QUOTA_CNTRL_FILE);
113411963SAfshin.Ardakani@Sun.COM 	if ((mkdir(dir, 0750) < 0) && (errno != EEXIST))
113511963SAfshin.Ardakani@Sun.COM 		return;
113611963SAfshin.Ardakani@Sun.COM 
113711963SAfshin.Ardakani@Sun.COM 	if ((dirfd = open(dir, O_RDONLY)) < 0) {
113811963SAfshin.Ardakani@Sun.COM 		(void) remove(dir);
113911963SAfshin.Ardakani@Sun.COM 		return;
114011963SAfshin.Ardakani@Sun.COM 	}
114111963SAfshin.Ardakani@Sun.COM 
114211963SAfshin.Ardakani@Sun.COM 	if (nvlist_alloc(&request, NV_UNIQUE_NAME, 0) == 0) {
114311963SAfshin.Ardakani@Sun.COM 		if ((nvlist_add_boolean_value(request, A_HIDDEN, 1) != 0) ||
114411963SAfshin.Ardakani@Sun.COM 		    (nvlist_add_boolean_value(request, A_SYSTEM, 1) != 0) ||
114511963SAfshin.Ardakani@Sun.COM 		    (fsetattr(dirfd, XATTR_VIEW_READWRITE, request))) {
114611963SAfshin.Ardakani@Sun.COM 			nvlist_free(request);
114711963SAfshin.Ardakani@Sun.COM 			(void) close(dirfd);
114811963SAfshin.Ardakani@Sun.COM 			(void) remove(dir);
114911963SAfshin.Ardakani@Sun.COM 			return;
115011963SAfshin.Ardakani@Sun.COM 		}
115111963SAfshin.Ardakani@Sun.COM 	}
115211963SAfshin.Ardakani@Sun.COM 	nvlist_free(request);
115311963SAfshin.Ardakani@Sun.COM 	(void) close(dirfd);
115411963SAfshin.Ardakani@Sun.COM 
115511963SAfshin.Ardakani@Sun.COM 	if (stat(file, &statbuf) != 0) {
115611963SAfshin.Ardakani@Sun.COM 		if ((newfd = creat(file, 0640)) < 0) {
115711963SAfshin.Ardakani@Sun.COM 			(void) remove(dir);
115811963SAfshin.Ardakani@Sun.COM 			return;
115911963SAfshin.Ardakani@Sun.COM 		}
116011963SAfshin.Ardakani@Sun.COM 		(void) close(newfd);
116111963SAfshin.Ardakani@Sun.COM 	}
116211963SAfshin.Ardakani@Sun.COM 
116311963SAfshin.Ardakani@Sun.COM 	afd = attropen(file, SMB_QUOTA_CNTRL_INDEX_XATTR, O_RDWR | O_CREAT,
116411963SAfshin.Ardakani@Sun.COM 	    0640);
116511963SAfshin.Ardakani@Sun.COM 	if (afd == -1) {
116611963SAfshin.Ardakani@Sun.COM 		(void) unlink(file);
116711963SAfshin.Ardakani@Sun.COM 		(void) remove(dir);
116811963SAfshin.Ardakani@Sun.COM 		return;
116911963SAfshin.Ardakani@Sun.COM 	}
117011963SAfshin.Ardakani@Sun.COM 	(void) close(afd);
117111963SAfshin.Ardakani@Sun.COM 
117211963SAfshin.Ardakani@Sun.COM 	if (acl_fromtext(SMB_QUOTA_CNTRL_PERM, &aclp) != 0) {
117311963SAfshin.Ardakani@Sun.COM 		(void) unlink(file);
117411963SAfshin.Ardakani@Sun.COM 		(void) remove(dir);
117511963SAfshin.Ardakani@Sun.COM 		return;
117611963SAfshin.Ardakani@Sun.COM 	}
117711963SAfshin.Ardakani@Sun.COM 
117811963SAfshin.Ardakani@Sun.COM 	if (acl_set(file, aclp) == -1) {
117911963SAfshin.Ardakani@Sun.COM 		(void) unlink(file);
118011963SAfshin.Ardakani@Sun.COM 		(void) remove(dir);
118111963SAfshin.Ardakani@Sun.COM 		acl_free(aclp);
118211963SAfshin.Ardakani@Sun.COM 		return;
118311963SAfshin.Ardakani@Sun.COM 	}
118411963SAfshin.Ardakani@Sun.COM 	acl_free(aclp);
118511963SAfshin.Ardakani@Sun.COM }
118611963SAfshin.Ardakani@Sun.COM 
118711963SAfshin.Ardakani@Sun.COM /*
118811963SAfshin.Ardakani@Sun.COM  * smb_quota_remove_ctrldir
118911963SAfshin.Ardakani@Sun.COM  *
119011963SAfshin.Ardakani@Sun.COM  * Remove SMB_QUOTA_CNTRL_FILE and SMB_QUOTA_CNTRL_DIR.
119111963SAfshin.Ardakani@Sun.COM  */
119211963SAfshin.Ardakani@Sun.COM static void
smb_quota_remove_ctrldir(const char * path)119311963SAfshin.Ardakani@Sun.COM smb_quota_remove_ctrldir(const char *path)
119411963SAfshin.Ardakani@Sun.COM {
119511963SAfshin.Ardakani@Sun.COM 	char dir[MAXPATHLEN], file[MAXPATHLEN];
119611963SAfshin.Ardakani@Sun.COM 	assert(path);
119711963SAfshin.Ardakani@Sun.COM 
119811963SAfshin.Ardakani@Sun.COM 	(void) snprintf(dir, MAXPATHLEN, ".%s/%s", path, SMB_QUOTA_CNTRL_DIR);
119911963SAfshin.Ardakani@Sun.COM 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dir, SMB_QUOTA_CNTRL_FILE);
120011963SAfshin.Ardakani@Sun.COM 	(void) unlink(file);
120111963SAfshin.Ardakani@Sun.COM 	(void) remove(dir);
120211963SAfshin.Ardakani@Sun.COM }
1203