4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
39 #include <smbsrv/smb_xdr.h>
40 #include <smbsrv/libmlsvc.h>
41 #include <smbsrv/smb_idmap.h>
46 * smb_quota subsystem interface - mlsvc.h
47 * ---------------------------------------
48 * Management of the smb_quota_fs_list (see below).
54 * smb_quota public interface - libmlsvc.h
55 * ---------------------------------------
56 * Handling of requests to query and set quota data on a filesystem.
57 * smb_quota_query - query user/group quotas on a filesystem
58 * smb_quota_set - set user/group quotas ona filesystem
59 * smb_quota_free - delete the quota list created in smb_quota_query
63 * Querying user & group quotas - smb_quota_query
65 * In order to fulfill the quota query requests that can be received
66 * from clients, it is required that the quota data can be provided in
67 * a well defined and consistent order, and that a request can specify
68 * at which quota entry to begin the query.
71 * Since the file system does not support the above, an avl tree is
72 * populated with the file system's user and group quota data, and
73 * then used to provide the data to respond to query requests. The
74 * avl tree is indexed by the SID.
75 * Each node of the avl tree is an smb_quota_t structure.
78 * There is a list of avl trees, one per file system.
79 * Each node in the list is an smb_quota_tree_t structure.
80 * The list is created via a call to smb_quota_init() when the library
81 * is initialized, and destroyed via a call to smb_quota_fini() when
82 * the library is fini'd.
84 * An avl tree for a specific file system is created and added to the
85 * list via a call to smb_quota_add_fs() when the file system is shared,
86 * and removed from the list via a call to smb_quota_remove_fs() when
87 * the file system is unshared.
89 * An avl tree is (re)populated, if required, whenever a quota request
90 * (EXCLUDING a resume request) is received for its filesystem. The
91 * avl tree is considered to be expired (needs to be repopulated) if
92 * either of the following have occurred since it was last (re)populated:
93 * - SMB_QUOTA_REFRESH seconds have elapsed OR
94 * - a quota set operation has been performed on its file system
96 * In order to perform a smb_quota_query/set operation on a file system
97 * the appropriate quota tree must be identified and locked via a call
98 * to smb_quota_tree_lookup(), The quota tree is locked (qt_locked == B_TRUE)
99 * until the caller releases it via a call to smb_quota_tree_release().
104 * Represents an avl tree of user quotas for a file system.
106 * qt_refcnt - a count of the number of users of the tree.
107 * qt_refcnt is also incremented and decremented when the tree is
108 * added to and removed from the quota list.
109 * The tree cannot be deleted until this count is zero.
111 * qt_sharecnt - a count of the shares of the file system which the
112 * tree represents. smb_quota_remove_fs() cannot remove the tree from
113 * removed from the quota list until this count is zero.
115 * qt_locked - B_TRUE if someone is currently using the tree, in
116 * which case a lookup will wait for the tree to become available.
118 typedef struct smb_quota_tree
{
123 uint32_t qt_sharecnt
;
131 * list of quota trees; one per shared file system.
133 static list_t smb_quota_fs_list
;
134 static boolean_t smb_quota_list_init
= B_FALSE
;
135 static boolean_t smb_quota_shutdown
= B_FALSE
;
136 static mutex_t smb_quota_list_mutex
= DEFAULTMUTEX
;
137 static cond_t smb_quota_list_condvar
;
138 static uint32_t smb_quota_tree_cnt
= 0;
139 static int smb_quota_fini_timeout
= 1; /* seconds */
142 * smb_quota_zfs_handle_t
143 * handle to zfs library and dataset
145 typedef struct smb_quota_zfs_handle
{
146 libzfs_handle_t
*z_lib
;
148 } smb_quota_zfs_handle_t
;
151 * smb_quota_zfs_arg_t
152 * arg passed to zfs callback when querying quota properties
154 typedef struct smb_quota_zfs_arg
{
155 zfs_userquota_prop_t qa_prop
;
157 } smb_quota_zfs_arg_t
;
159 static void smb_quota_add_ctrldir(const char *);
160 static void smb_quota_remove_ctrldir(const char *);
162 static smb_quota_tree_t
*smb_quota_tree_create(const char *);
163 static void smb_quota_tree_delete(smb_quota_tree_t
*);
165 static smb_quota_tree_t
*smb_quota_tree_lookup(const char *);
166 static void smb_quota_tree_release(smb_quota_tree_t
*);
167 static boolean_t
smb_quota_tree_match(smb_quota_tree_t
*, const char *);
168 static int smb_quota_sid_cmp(const void *, const void *);
169 static uint32_t smb_quota_tree_populate(smb_quota_tree_t
*);
170 static boolean_t
smb_quota_tree_expired(smb_quota_tree_t
*);
171 static void smb_quota_tree_set_expired(smb_quota_tree_t
*);
173 static uint32_t smb_quota_zfs_init(const char *, smb_quota_zfs_handle_t
*);
174 static void smb_quota_zfs_fini(smb_quota_zfs_handle_t
*);
175 static uint32_t smb_quota_zfs_get_quotas(smb_quota_tree_t
*);
176 static int smb_quota_zfs_callback(void *, const char *, uid_t
, uint64_t);
177 static uint32_t smb_quota_zfs_set_quotas(smb_quota_tree_t
*, smb_quota_set_t
*);
178 static int smb_quota_sidstr(uint32_t, zfs_userquota_prop_t
, char *);
179 static uint32_t smb_quota_sidtype(smb_quota_tree_t
*, char *);
180 static int smb_quota_getid(char *, uint32_t, uint32_t *);
182 static uint32_t smb_quota_query_all(smb_quota_tree_t
*,
183 smb_quota_query_t
*, smb_quota_response_t
*);
184 static uint32_t smb_quota_query_list(smb_quota_tree_t
*,
185 smb_quota_query_t
*, smb_quota_response_t
*);
187 #define SMB_QUOTA_REFRESH 2
188 #define SMB_QUOTA_CMD_LENGTH 21
189 #define SMB_QUOTA_CMD_STR_LENGTH SMB_SID_STRSZ+SMB_QUOTA_CMD_LENGTH
192 * In order to display the quota properties tab, windows clients
193 * check for the existence of the quota control file.
195 #define SMB_QUOTA_CNTRL_DIR ".$EXTEND"
196 #define SMB_QUOTA_CNTRL_FILE "$QUOTA"
197 #define SMB_QUOTA_CNTRL_INDEX_XATTR "SUNWsmb:$Q:$INDEX_ALLOCATION"
199 * Note: this line needs to have the same format as what acl_totext() returns.
201 #define SMB_QUOTA_CNTRL_PERM "everyone@:rw-p--aARWc--s:-------:allow"
205 * Initialize the list to hold the quota trees.
210 (void) mutex_lock(&smb_quota_list_mutex
);
211 if (!smb_quota_list_init
) {
212 list_create(&smb_quota_fs_list
, sizeof (smb_quota_tree_t
),
213 offsetof(smb_quota_tree_t
, qt_node
));
214 smb_quota_list_init
= B_TRUE
;
215 smb_quota_shutdown
= B_FALSE
;
217 (void) mutex_unlock(&smb_quota_list_mutex
);
223 * Wait for each quota tree to not be in use (qt_refcnt == 1)
224 * then remove it from the list and delete it.
229 smb_quota_tree_t
*qtree
, *qtree_next
;
231 struct timespec tswait
;
232 tswait
.tv_sec
= smb_quota_fini_timeout
;
235 (void) mutex_lock(&smb_quota_list_mutex
);
236 smb_quota_shutdown
= B_TRUE
;
238 if (!smb_quota_list_init
) {
239 (void) mutex_unlock(&smb_quota_list_mutex
);
243 (void) cond_broadcast(&smb_quota_list_condvar
);
245 while (!list_is_empty(&smb_quota_fs_list
)) {
246 qtree
= list_head(&smb_quota_fs_list
);
247 while (qtree
!= NULL
) {
248 qtree_next
= list_next(&smb_quota_fs_list
, qtree
);
250 (void) mutex_lock(&qtree
->qt_mutex
);
251 remove
= (qtree
->qt_refcnt
== 1);
253 list_remove(&smb_quota_fs_list
, qtree
);
256 (void) mutex_unlock(&qtree
->qt_mutex
);
259 smb_quota_tree_delete(qtree
);
264 if (!list_is_empty(&smb_quota_fs_list
)) {
265 if (cond_reltimedwait(&smb_quota_list_condvar
,
266 &smb_quota_list_mutex
, &tswait
) == ETIME
) {
268 "quota shutdown timeout expired");
274 if (list_is_empty(&smb_quota_fs_list
)) {
275 list_destroy(&smb_quota_fs_list
);
276 smb_quota_list_init
= B_FALSE
;
279 (void) mutex_unlock(&smb_quota_list_mutex
);
285 * If there is not a quota tree representing the specified path,
286 * create one and add it to the list.
289 smb_quota_add_fs(const char *path
)
291 smb_quota_tree_t
*qtree
;
293 (void) mutex_lock(&smb_quota_list_mutex
);
295 if (!smb_quota_list_init
|| smb_quota_shutdown
) {
296 (void) mutex_unlock(&smb_quota_list_mutex
);
300 qtree
= list_head(&smb_quota_fs_list
);
301 while (qtree
!= NULL
) {
302 if (smb_quota_tree_match(qtree
, path
)) {
303 (void) mutex_lock(&qtree
->qt_mutex
);
304 ++qtree
->qt_sharecnt
;
305 (void) mutex_unlock(&qtree
->qt_mutex
);
308 qtree
= list_next(&smb_quota_fs_list
, qtree
);
312 qtree
= smb_quota_tree_create(path
);
314 list_insert_head(&smb_quota_fs_list
, (void *)qtree
);
318 smb_quota_add_ctrldir(path
);
320 (void) mutex_unlock(&smb_quota_list_mutex
);
324 * smb_quota_remove_fs
326 * If this is the last share that the quota tree represents
327 * (qtree->qt_sharecnt == 0) remove the qtree from the list.
328 * The qtree will be deleted if/when there is nobody using it
329 * (qtree->qt_refcnt == 0).
332 smb_quota_remove_fs(const char *path
)
334 smb_quota_tree_t
*qtree
;
335 boolean_t
delete = B_FALSE
;
337 (void) mutex_lock(&smb_quota_list_mutex
);
339 if (!smb_quota_list_init
|| smb_quota_shutdown
) {
340 (void) mutex_unlock(&smb_quota_list_mutex
);
344 qtree
= list_head(&smb_quota_fs_list
);
345 while (qtree
!= NULL
) {
346 assert(qtree
->qt_refcnt
> 0);
347 if (smb_quota_tree_match(qtree
, path
)) {
348 (void) mutex_lock(&qtree
->qt_mutex
);
349 --qtree
->qt_sharecnt
;
350 if (qtree
->qt_sharecnt
== 0) {
351 list_remove(&smb_quota_fs_list
, (void *)qtree
);
352 smb_quota_remove_ctrldir(qtree
->qt_path
);
353 --(qtree
->qt_refcnt
);
354 delete = (qtree
->qt_refcnt
== 0);
356 (void) mutex_unlock(&qtree
->qt_mutex
);
358 smb_quota_tree_delete(qtree
);
361 qtree
= list_next(&smb_quota_fs_list
, qtree
);
363 (void) mutex_unlock(&smb_quota_list_mutex
);
369 * Get list of user/group quotas entries.
370 * Request->qq_query_op determines whether to get quota entries
371 * for the specified SIDs (smb_quota_query_list) OR to get all
372 * quota entries, optionally starting at a specified SID.
374 * Returns NT_STATUS codes.
377 smb_quota_query(smb_quota_query_t
*request
, smb_quota_response_t
*reply
)
380 smb_quota_tree_t
*qtree
;
381 smb_quota_query_op_t query_op
= request
->qq_query_op
;
383 list_create(&reply
->qr_quota_list
, sizeof (smb_quota_t
),
384 offsetof(smb_quota_t
, q_list_node
));
386 qtree
= smb_quota_tree_lookup(request
->qq_root_path
);
388 return (NT_STATUS_INVALID_PARAMETER
);
390 /* If NOT resuming a previous query all, refresh qtree if required */
391 if ((query_op
!= SMB_QUOTA_QUERY_ALL
) || (request
->qq_restart
)) {
392 status
= smb_quota_tree_populate(qtree
);
393 if (status
!= NT_STATUS_SUCCESS
) {
394 smb_quota_tree_release(qtree
);
400 case SMB_QUOTA_QUERY_SIDLIST
:
401 status
= smb_quota_query_list(qtree
, request
, reply
);
403 case SMB_QUOTA_QUERY_STARTSID
:
404 case SMB_QUOTA_QUERY_ALL
:
405 status
= smb_quota_query_all(qtree
, request
, reply
);
407 case SMB_QUOTA_QUERY_INVALID_OP
:
409 status
= NT_STATUS_INVALID_PARAMETER
;
413 smb_quota_tree_release(qtree
);
421 * Set the list of quota entries.
424 smb_quota_set(smb_quota_set_t
*request
)
427 smb_quota_tree_t
*qtree
;
429 qtree
= smb_quota_tree_lookup(request
->qs_root_path
);
431 return (NT_STATUS_INVALID_PARAMETER
);
433 status
= smb_quota_zfs_set_quotas(qtree
, request
);
435 smb_quota_tree_set_expired(qtree
);
436 smb_quota_tree_release(qtree
);
444 * This method frees quota entries.
447 smb_quota_free(smb_quota_response_t
*reply
)
449 list_t
*list
= &reply
->qr_quota_list
;
452 while ((quota
= list_head(list
)) != NULL
) {
453 list_remove(list
, quota
);
461 * smb_quota_query_all
463 * Query quotas sequentially from tree, optionally starting at a
464 * specified sid. If request->qq_single is TRUE only one quota
465 * should be returned, otherwise up to request->qq_max_quota
466 * should be returned.
468 * SMB_QUOTA_QUERY_STARTSID
469 * The query should start at the startsid, the first sid in
470 * request->qq_sid_list.
472 * SMQ_QUOTA_QUERY_ALL
473 * If request->qq_restart the query should restart at the start
474 * of the avl tree. Otherwise the first sid in request->qq_sid_list
475 * is the resume sid and the query should start at the tree entry
476 * after the one it refers to.
478 * Returns NT_STATUS codes.
481 smb_quota_query_all(smb_quota_tree_t
*qtree
, smb_quota_query_t
*request
,
482 smb_quota_response_t
*reply
)
484 avl_tree_t
*avl_tree
= &qtree
->qt_avl
;
486 list_t
*sid_list
, *quota_list
;
487 smb_quota_sid_t
*sid
;
488 smb_quota_t
*quota
, *quotal
, key
;
491 /* find starting sid */
492 if (request
->qq_query_op
== SMB_QUOTA_QUERY_STARTSID
) {
493 sid_list
= &request
->qq_sid_list
;
494 sid
= list_head(sid_list
);
495 (void) strlcpy(key
.q_sidstr
, sid
->qs_sidstr
, SMB_SID_STRSZ
);
496 quota
= avl_find(avl_tree
, &key
, &where
);
498 return (NT_STATUS_INVALID_PARAMETER
);
499 } else if (request
->qq_restart
) {
500 quota
= avl_first(avl_tree
);
502 return (NT_STATUS_NO_MORE_ENTRIES
);
504 sid_list
= &request
->qq_sid_list
;
505 sid
= list_head(sid_list
);
506 (void) strlcpy(key
.q_sidstr
, sid
->qs_sidstr
, SMB_SID_STRSZ
);
507 quota
= avl_find(avl_tree
, &key
, &where
);
509 return (NT_STATUS_INVALID_PARAMETER
);
510 quota
= AVL_NEXT(avl_tree
, quota
);
512 return (NT_STATUS_NO_MORE_ENTRIES
);
515 if ((request
->qq_single
) && (request
->qq_max_quota
> 1))
516 request
->qq_max_quota
= 1;
518 quota_list
= &reply
->qr_quota_list
;
521 if (count
>= request
->qq_max_quota
)
524 quotal
= malloc(sizeof (smb_quota_t
));
526 return (NT_STATUS_NO_MEMORY
);
527 bcopy(quota
, quotal
, sizeof (smb_quota_t
));
529 list_insert_tail(quota_list
, quotal
);
532 quota
= AVL_NEXT(avl_tree
, quota
);
535 return (NT_STATUS_SUCCESS
);
539 * smb_quota_query_list
541 * Iterate through request sid list querying the avl tree for each.
542 * Insert an entry in the reply quota list for each sid.
543 * For any sid that cannot be found in the avl tree, the reply
544 * quota list entry should contain zeros.
547 smb_quota_query_list(smb_quota_tree_t
*qtree
, smb_quota_query_t
*request
,
548 smb_quota_response_t
*reply
)
550 avl_tree_t
*avl_tree
= &qtree
->qt_avl
;
552 list_t
*sid_list
, *quota_list
;
553 smb_quota_sid_t
*sid
;
554 smb_quota_t
*quota
, *quotal
, key
;
556 quota_list
= &reply
->qr_quota_list
;
557 sid_list
= &request
->qq_sid_list
;
558 sid
= list_head(sid_list
);
560 quotal
= malloc(sizeof (smb_quota_t
));
562 return (NT_STATUS_NO_MEMORY
);
564 (void) strlcpy(key
.q_sidstr
, sid
->qs_sidstr
, SMB_SID_STRSZ
);
565 quota
= avl_find(avl_tree
, &key
, &where
);
567 bcopy(quota
, quotal
, sizeof (smb_quota_t
));
569 bzero(quotal
, sizeof (smb_quota_t
));
570 (void) strlcpy(quotal
->q_sidstr
, sid
->qs_sidstr
,
574 list_insert_tail(quota_list
, quotal
);
575 sid
= list_next(sid_list
, sid
);
578 return (NT_STATUS_SUCCESS
);
582 * smb_quota_zfs_set_quotas
584 * This method sets the list of quota entries.
586 * A quota list or threshold value of SMB_QUOTA_UNLIMITED means that
587 * the user / group does not have a quota limit. In ZFS this maps to
589 * A quota list or threshold value of (SMB_QUOTA_UNLIMITED - 1) means
590 * that the user / group quota should be removed. In ZFS this maps to
594 smb_quota_zfs_set_quotas(smb_quota_tree_t
*qtree
, smb_quota_set_t
*request
)
596 smb_quota_zfs_handle_t zfs_hdl
;
597 char *typestr
, qsetstr
[SMB_QUOTA_CMD_STR_LENGTH
];
598 char qlimit
[SMB_QUOTA_CMD_LENGTH
];
602 uint32_t status
= NT_STATUS_SUCCESS
;
605 status
= smb_quota_zfs_init(request
->qs_root_path
, &zfs_hdl
);
606 if (status
!= NT_STATUS_SUCCESS
)
609 quota_list
= &request
->qs_quota_list
;
610 quota
= list_head(quota_list
);
613 if ((quota
->q_limit
== SMB_QUOTA_UNLIMITED
) ||
614 (quota
->q_limit
== (SMB_QUOTA_UNLIMITED
- 1))) {
617 (void) snprintf(qlimit
, SMB_QUOTA_CMD_LENGTH
, "%llu",
620 sidtype
= smb_quota_sidtype(qtree
, quota
->q_sidstr
);
623 typestr
= "userquota";
625 case SidTypeWellKnownGroup
:
628 typestr
= "groupquota";
631 syslog(LOG_WARNING
, "Failed to set quota for %s: "
632 "%s (%d) not valid for quotas", quota
->q_sidstr
,
633 smb_sid_type2str(sidtype
), sidtype
);
634 quota
= list_next(quota_list
, quota
);
638 if ((smb_quota_getid(quota
->q_sidstr
, sidtype
, &id
) == 0) &&
639 !(IDMAP_ID_IS_EPHEMERAL(id
))) {
640 (void) snprintf(qsetstr
, SMB_QUOTA_CMD_STR_LENGTH
,
641 "%s@%d", typestr
, id
);
643 (void) snprintf(qsetstr
, SMB_QUOTA_CMD_STR_LENGTH
,
644 "%s@%s", typestr
, quota
->q_sidstr
);
648 if (zfs_prop_set(zfs_hdl
.z_fs
, qsetstr
, qlimit
) != 0) {
649 syslog(LOG_WARNING
, "Failed to set quota for %s: %s",
650 quota
->q_sidstr
, strerror(errno
));
651 status
= NT_STATUS_INVALID_PARAMETER
;
655 quota
= list_next(quota_list
, quota
);
658 smb_quota_zfs_fini(&zfs_hdl
);
665 * Determine the type of the sid. If the sid exists in
666 * the qtree get its type from there, otherwise do an
670 smb_quota_sidtype(smb_quota_tree_t
*qtree
, char *sidstr
)
672 smb_quota_t key
, *quota
;
674 smb_sid_t
*sid
= NULL
;
676 uint32_t sidtype
= SidTypeUnknown
;
678 (void) strlcpy(key
.q_sidstr
, sidstr
, SMB_SID_STRSZ
);
679 quota
= avl_find(&qtree
->qt_avl
, &key
, &where
);
681 return (quota
->q_sidtype
);
683 sid
= smb_sid_fromstr(sidstr
);
685 if (lsa_lookup_sid(sid
, &ainfo
) == NT_STATUS_SUCCESS
) {
686 sidtype
= ainfo
.a_type
;
687 smb_account_free(&ainfo
);
697 * Get the user/group id for the sid.
700 smb_quota_getid(char *sidstr
, uint32_t sidtype
, uint32_t *id
)
703 smb_sid_t
*sid
= NULL
;
706 sid
= smb_sid_fromstr(sidstr
);
712 idtype
= SMB_IDMAP_USER
;
714 case SidTypeWellKnownGroup
:
717 idtype
= SMB_IDMAP_GROUP
;
725 rc
= smb_idmap_getid(sid
, id
, &idtype
);
733 * smb_quota_tree_lookup
735 * Find the quota tree in smb_quota_fs_list.
737 * If the tree is found but is locked, waits for it to become available.
738 * If the tree is available, locks it and returns it.
739 * Otherwise, returns NULL.
741 static smb_quota_tree_t
*
742 smb_quota_tree_lookup(const char *path
)
744 smb_quota_tree_t
*qtree
= NULL
;
747 (void) mutex_lock(&smb_quota_list_mutex
);
749 qtree
= list_head(&smb_quota_fs_list
);
750 while (qtree
!= NULL
) {
751 if (!smb_quota_list_init
|| smb_quota_shutdown
) {
752 (void) mutex_unlock(&smb_quota_list_mutex
);
756 (void) mutex_lock(&qtree
->qt_mutex
);
757 assert(qtree
->qt_refcnt
> 0);
759 if (!smb_quota_tree_match(qtree
, path
)) {
760 (void) mutex_unlock(&qtree
->qt_mutex
);
761 qtree
= list_next(&smb_quota_fs_list
, qtree
);
765 if (qtree
->qt_locked
) {
766 (void) mutex_unlock(&qtree
->qt_mutex
);
767 (void) cond_wait(&smb_quota_list_condvar
,
768 &smb_quota_list_mutex
);
769 qtree
= list_head(&smb_quota_fs_list
);
773 ++(qtree
->qt_refcnt
);
774 qtree
->qt_locked
= B_TRUE
;
775 (void) mutex_unlock(&qtree
->qt_mutex
);
779 (void) mutex_unlock(&smb_quota_list_mutex
);
784 * smb_quota_tree_release
787 smb_quota_tree_release(smb_quota_tree_t
*qtree
)
791 (void) mutex_lock(&qtree
->qt_mutex
);
792 assert(qtree
->qt_locked
);
793 assert(qtree
->qt_refcnt
> 0);
795 --(qtree
->qt_refcnt
);
796 qtree
->qt_locked
= B_FALSE
;
797 delete = (qtree
->qt_refcnt
== 0);
798 (void) mutex_unlock(&qtree
->qt_mutex
);
800 (void) mutex_lock(&smb_quota_list_mutex
);
802 smb_quota_tree_delete(qtree
);
803 (void) cond_broadcast(&smb_quota_list_condvar
);
804 (void) mutex_unlock(&smb_quota_list_mutex
);
808 * smb_quota_tree_match
810 * Determine if qtree represents the file system identified by path
813 smb_quota_tree_match(smb_quota_tree_t
*qtree
, const char *path
)
815 return (strncmp(qtree
->qt_path
, path
, MAXPATHLEN
) == 0);
819 * smb_quota_tree_create
821 * Create and initialize an smb_quota_tree_t structure
823 static smb_quota_tree_t
*
824 smb_quota_tree_create(const char *path
)
826 smb_quota_tree_t
*qtree
;
828 assert(MUTEX_HELD(&smb_quota_list_mutex
));
830 qtree
= calloc(sizeof (smb_quota_tree_t
), 1);
834 qtree
->qt_path
= strdup(path
);
835 if (qtree
->qt_path
== NULL
) {
840 qtree
->qt_timestamp
= 0;
841 qtree
->qt_locked
= B_FALSE
;
842 qtree
->qt_refcnt
= 1;
843 qtree
->qt_sharecnt
= 1;
845 avl_create(&qtree
->qt_avl
, smb_quota_sid_cmp
,
846 sizeof (smb_quota_t
), offsetof(smb_quota_t
, q_avl_node
));
848 ++smb_quota_tree_cnt
;
853 * smb_quota_tree_delete
855 * Free and delete the smb_quota_tree_t structure.
856 * qtree must have no users (refcnt == 0).
859 smb_quota_tree_delete(smb_quota_tree_t
*qtree
)
864 assert(MUTEX_HELD(&smb_quota_list_mutex
));
865 assert(qtree
->qt_refcnt
== 0);
867 while ((node
= avl_destroy_nodes(&qtree
->qt_avl
, &cookie
)) != NULL
)
869 avl_destroy(&qtree
->qt_avl
);
871 free(qtree
->qt_path
);
874 --smb_quota_tree_cnt
;
880 * Comparision function for nodes in an AVL tree which holds quota
881 * entries indexed by SID.
884 smb_quota_sid_cmp(const void *l_arg
, const void *r_arg
)
886 const char *l_sid
= ((smb_quota_t
*)l_arg
)->q_sidstr
;
887 const char *r_sid
= ((smb_quota_t
*)r_arg
)->q_sidstr
;
890 ret
= strncasecmp(l_sid
, r_sid
, SMB_SID_STRSZ
);
900 * smb_quota_tree_populate
902 * If the quota tree needs to be (re)populated:
903 * - delete the qtree's contents
904 * - repopulate the qtree from zfs
905 * - set the qtree's timestamp.
908 smb_quota_tree_populate(smb_quota_tree_t
*qtree
)
914 assert(qtree
->qt_locked
);
916 if (!smb_quota_tree_expired(qtree
))
917 return (NT_STATUS_SUCCESS
);
919 while ((node
= avl_destroy_nodes(&qtree
->qt_avl
, &cookie
)) != NULL
)
922 status
= smb_quota_zfs_get_quotas(qtree
);
923 if (status
!= NT_STATUS_SUCCESS
)
926 qtree
->qt_timestamp
= time(NULL
);
928 return (NT_STATUS_SUCCESS
);
932 smb_quota_tree_expired(smb_quota_tree_t
*qtree
)
934 time_t tnow
= time(NULL
);
935 return ((tnow
- qtree
->qt_timestamp
) > SMB_QUOTA_REFRESH
);
939 smb_quota_tree_set_expired(smb_quota_tree_t
*qtree
)
941 qtree
->qt_timestamp
= 0;
945 * smb_quota_zfs_get_quotas
947 * Get user and group quotas from ZFS and use them to
948 * populate the quota tree.
951 smb_quota_zfs_get_quotas(smb_quota_tree_t
*qtree
)
953 smb_quota_zfs_handle_t zfs_hdl
;
954 smb_quota_zfs_arg_t arg
;
955 zfs_userquota_prop_t p
;
956 uint32_t status
= NT_STATUS_SUCCESS
;
958 status
= smb_quota_zfs_init(qtree
->qt_path
, &zfs_hdl
);
959 if (status
!= NT_STATUS_SUCCESS
)
962 arg
.qa_avl
= &qtree
->qt_avl
;
963 for (p
= 0; p
< ZFS_NUM_USERQUOTA_PROPS
; p
++) {
965 if (zfs_userspace(zfs_hdl
.z_fs
, p
,
966 smb_quota_zfs_callback
, &arg
) != 0) {
967 status
= NT_STATUS_INTERNAL_ERROR
;
972 smb_quota_zfs_fini(&zfs_hdl
);
977 * smb_quota_zfs_callback
979 * Find or create a node in the avl tree (arg->qa_avl) that matches
980 * the SID derived from domain and rid. If no domain is specified,
981 * lookup the sid (smb_quota_sidstr()).
983 * The property type (arg->qa_prop) determines which property 'space'
987 smb_quota_zfs_callback(void *arg
, const char *domain
, uid_t rid
, uint64_t space
)
989 smb_quota_zfs_arg_t
*qarg
= (smb_quota_zfs_arg_t
*)arg
;
990 zfs_userquota_prop_t qprop
= qarg
->qa_prop
;
991 avl_tree_t
*avl_tree
= qarg
->qa_avl
;
993 smb_quota_t
*quota
, key
;
995 if (domain
== NULL
|| domain
[0] == '\0') {
996 if (smb_quota_sidstr(rid
, qprop
, key
.q_sidstr
) != 0)
999 (void) snprintf(key
.q_sidstr
, SMB_SID_STRSZ
, "%s-%u",
1000 domain
, (uint32_t)rid
);
1003 quota
= avl_find(avl_tree
, &key
, &where
);
1004 if (quota
== NULL
) {
1005 quota
= malloc(sizeof (smb_quota_t
));
1007 return (NT_STATUS_NO_MEMORY
);
1008 bzero(quota
, sizeof (smb_quota_t
));
1009 quota
->q_thresh
= SMB_QUOTA_UNLIMITED
;
1010 quota
->q_limit
= SMB_QUOTA_UNLIMITED
;
1011 avl_insert(avl_tree
, (void *)quota
, where
);
1012 (void) strlcpy(quota
->q_sidstr
, key
.q_sidstr
, SMB_SID_STRSZ
);
1016 case ZFS_PROP_USERUSED
:
1017 quota
->q_sidtype
= SidTypeUser
;
1018 quota
->q_used
= space
;
1020 case ZFS_PROP_GROUPUSED
:
1021 quota
->q_sidtype
= SidTypeGroup
;
1022 quota
->q_used
= space
;
1024 case ZFS_PROP_USERQUOTA
:
1025 quota
->q_sidtype
= SidTypeUser
;
1026 quota
->q_limit
= space
;
1028 case ZFS_PROP_GROUPQUOTA
:
1029 quota
->q_sidtype
= SidTypeGroup
;
1030 quota
->q_limit
= space
;
1036 quota
->q_thresh
= quota
->q_limit
;
1044 * Use idmap to get the sid for the specified id and return
1045 * the string version of the sid in sidstr.
1046 * sidstr must be a buffer of at least SMB_SID_STRSZ.
1049 smb_quota_sidstr(uint32_t id
, zfs_userquota_prop_t qprop
, char *sidstr
)
1055 case ZFS_PROP_USERUSED
:
1056 case ZFS_PROP_USERQUOTA
:
1057 idtype
= SMB_IDMAP_USER
;
1059 case ZFS_PROP_GROUPUSED
:
1060 case ZFS_PROP_GROUPQUOTA
:
1061 idtype
= SMB_IDMAP_GROUP
;
1067 if (smb_idmap_getsid(id
, idtype
, &sid
) != IDMAP_SUCCESS
)
1070 smb_sid_tostr(sid
, sidstr
);
1077 * smb_quota_zfs_init
1079 * Initialize zfs library and dataset handles
1082 smb_quota_zfs_init(const char *path
, smb_quota_zfs_handle_t
*zfs_hdl
)
1084 char dataset
[MAXPATHLEN
];
1086 if (smb_getdataset(path
, dataset
, MAXPATHLEN
) != 0)
1087 return (NT_STATUS_INVALID_PARAMETER
);
1089 if ((zfs_hdl
->z_lib
= libzfs_init()) == NULL
)
1090 return (NT_STATUS_INTERNAL_ERROR
);
1092 zfs_hdl
->z_fs
= zfs_open(zfs_hdl
->z_lib
, dataset
, ZFS_TYPE_DATASET
);
1093 if (zfs_hdl
->z_fs
== NULL
) {
1094 libzfs_fini(zfs_hdl
->z_lib
);
1095 return (NT_STATUS_ACCESS_DENIED
);
1098 return (NT_STATUS_SUCCESS
);
1102 * smb_quota_zfs_fini
1104 * Close zfs library and dataset handles
1107 smb_quota_zfs_fini(smb_quota_zfs_handle_t
*zfs_hdl
)
1109 zfs_close(zfs_hdl
->z_fs
);
1110 libzfs_fini(zfs_hdl
->z_lib
);
1114 * smb_quota_add_ctrldir
1116 * In order to display the quota properties tab, windows clients
1117 * check for the existence of the quota control file, created
1119 * - Create SMB_QUOTA_CNTRL_DIR directory (with A_HIDDEN & A_SYSTEM
1121 * - Create the SMB_QUOTA_CNTRL_FILE file (with extended attribute
1122 * SMB_QUOTA_CNTRL_INDEX_XATTR) in the SMB_QUOTA_CNTRL_DIR directory.
1123 * - Set the acl of SMB_QUOTA_CNTRL_FILE file to SMB_QUOTA_CNTRL_PERM.
1126 smb_quota_add_ctrldir(const char *path
)
1128 int newfd
, dirfd
, afd
;
1130 char dir
[MAXPATHLEN
], file
[MAXPATHLEN
], *acl_text
;
1131 acl_t
*aclp
, *existing_aclp
;
1132 boolean_t qdir_created
, prop_hidden
= B_FALSE
, prop_sys
= B_FALSE
;
1133 struct stat statbuf
;
1135 assert(path
!= NULL
);
1137 (void) snprintf(dir
, MAXPATHLEN
, ".%s/%s", path
, SMB_QUOTA_CNTRL_DIR
);
1138 (void) snprintf(file
, MAXPATHLEN
, "%s/%s", dir
, SMB_QUOTA_CNTRL_FILE
);
1139 if ((mkdir(dir
, 0750) < 0) && (errno
!= EEXIST
))
1141 qdir_created
= (errno
== EEXIST
) ? B_FALSE
: B_TRUE
;
1143 if ((dirfd
= open(dir
, O_RDONLY
)) < 0) {
1149 if (fgetattr(dirfd
, XATTR_VIEW_READWRITE
, &attr
) != 0) {
1150 (void) close(dirfd
);
1156 if ((nvlist_lookup_boolean_value(attr
, A_HIDDEN
, &prop_hidden
) != 0) ||
1157 (nvlist_lookup_boolean_value(attr
, A_SYSTEM
, &prop_sys
) != 0)) {
1159 (void) close(dirfd
);
1167 * Before setting attr or acl we check if the they have already been
1168 * set to what we want. If so we could be dealing with a received
1169 * snapshot and setting these is not needed.
1172 if (!prop_hidden
|| !prop_sys
) {
1173 if (nvlist_alloc(&attr
, NV_UNIQUE_NAME
, 0) == 0) {
1174 if ((nvlist_add_boolean_value(
1175 attr
, A_HIDDEN
, 1) != 0) ||
1176 (nvlist_add_boolean_value(attr
, A_SYSTEM
, 1) != 0)
1177 || (fsetattr(dirfd
, XATTR_VIEW_READWRITE
, attr
))) {
1179 (void) close(dirfd
);
1188 (void) close(dirfd
);
1190 if (stat(file
, &statbuf
) != 0) {
1191 if ((newfd
= creat(file
, 0640)) < 0) {
1196 (void) close(newfd
);
1199 afd
= attropen(file
, SMB_QUOTA_CNTRL_INDEX_XATTR
, O_RDWR
| O_CREAT
,
1202 (void) unlink(file
);
1209 if (acl_get(file
, 0, &existing_aclp
) == -1) {
1210 (void) unlink(file
);
1216 acl_text
= acl_totext(existing_aclp
, ACL_COMPACT_FMT
);
1217 acl_free(existing_aclp
);
1218 if (acl_text
== NULL
) {
1219 (void) unlink(file
);
1225 if (strcmp(acl_text
, SMB_QUOTA_CNTRL_PERM
) != 0) {
1226 if (acl_fromtext(SMB_QUOTA_CNTRL_PERM
, &aclp
) != 0) {
1228 (void) unlink(file
);
1233 if (acl_set(file
, aclp
) == -1) {
1235 (void) unlink(file
);
1247 * smb_quota_remove_ctrldir
1249 * Remove SMB_QUOTA_CNTRL_FILE and SMB_QUOTA_CNTRL_DIR.
1252 smb_quota_remove_ctrldir(const char *path
)
1254 char dir
[MAXPATHLEN
], file
[MAXPATHLEN
];
1257 (void) snprintf(dir
, MAXPATHLEN
, ".%s/%s", path
, SMB_QUOTA_CNTRL_DIR
);
1258 (void) snprintf(file
, MAXPATHLEN
, "%s/%s", dir
, SMB_QUOTA_CNTRL_FILE
);
1259 (void) unlink(file
);