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