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