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