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