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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <fcntl.h> 28 #include <attr.h> 29 #include <unistd.h> 30 #include <libuutil.h> 31 #include <libzfs.h> 32 #include <assert.h> 33 #include <stddef.h> 34 #include <strings.h> 35 #include <errno.h> 36 #include <synch.h> 37 #include <smbsrv/smb_xdr.h> 38 #include <smbsrv/ntstatus.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 */ 117 typedef 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 */ 132 static list_t smb_quota_fs_list; 133 static boolean_t smb_quota_list_init = B_FALSE; 134 static boolean_t smb_quota_shutdown = B_FALSE; 135 static mutex_t smb_quota_list_mutex; 136 static cond_t smb_quota_list_condvar; 137 static uint32_t smb_quota_tree_cnt = 0; 138 static int smb_quota_fini_timeout = 1; /* seconds */ 139 140 /* 141 * smb_quota_zfs_handle_t 142 * handle to zfs library and dataset 143 */ 144 typedef 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 */ 153 typedef 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 158 static void smb_quota_add_ctrldir(const char *); 159 static void smb_quota_remove_ctrldir(const char *); 160 161 static smb_quota_tree_t *smb_quota_tree_create(const char *); 162 static void smb_quota_tree_delete(smb_quota_tree_t *); 163 164 static smb_quota_tree_t *smb_quota_tree_lookup(const char *); 165 static void smb_quota_tree_release(smb_quota_tree_t *); 166 static boolean_t smb_quota_tree_match(smb_quota_tree_t *, const char *); 167 static int smb_quota_sid_cmp(const void *, const void *); 168 static uint32_t smb_quota_tree_populate(smb_quota_tree_t *); 169 static boolean_t smb_quota_tree_expired(smb_quota_tree_t *); 170 static void smb_quota_tree_set_expired(smb_quota_tree_t *); 171 172 static uint32_t smb_quota_zfs_init(const char *, smb_quota_zfs_handle_t *); 173 static void smb_quota_zfs_fini(smb_quota_zfs_handle_t *); 174 static uint32_t smb_quota_zfs_get_quotas(smb_quota_tree_t *); 175 static int smb_quota_zfs_callback(void *, const char *, uid_t, uint64_t); 176 static uint32_t smb_quota_zfs_set_quotas(smb_quota_tree_t *, smb_quota_set_t *); 177 static int smb_quota_sidstr(uint32_t, zfs_userquota_prop_t, char *); 178 static uint32_t smb_quota_sidtype(smb_quota_tree_t *, char *); 179 static int smb_quota_getid(char *, uint32_t, uint32_t *); 180 181 static uint32_t smb_quota_query_all(smb_quota_tree_t *, 182 smb_quota_query_t *, smb_quota_response_t *); 183 static 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 */ 203 void 204 smb_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 */ 222 void 223 smb_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 (smb_quota_tree_cnt != 0) { 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 (smb_quota_tree_cnt != 0) { 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 */ 284 void 285 smb_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 */ 327 void 328 smb_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 */ 372 uint32_t 373 smb_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 */ 419 uint32_t 420 smb_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 */ 442 void 443 smb_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 */ 476 static uint32_t 477 smb_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_DATA); 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_DATA); 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 */ 542 static uint32_t 543 smb_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 */ 589 static uint32_t 590 smb_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 */ 665 static uint32_t 666 smb_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 */ 695 static int 696 smb_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 */ 737 static smb_quota_tree_t * 738 smb_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 */ 782 static void 783 smb_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 */ 808 static boolean_t 809 smb_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 */ 819 static smb_quota_tree_t * 820 smb_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 = malloc(sizeof (smb_quota_tree_t)); 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 */ 854 static void 855 smb_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 */ 879 static int 880 smb_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 */ 903 static uint32_t 904 smb_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 927 static boolean_t 928 smb_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 934 static void 935 smb_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 */ 946 static uint32_t 947 smb_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 */ 982 static int 983 smb_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 */ 1044 static int 1045 smb_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 */ 1077 static uint32_t 1078 smb_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 */ 1102 static void 1103 smb_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 */ 1121 static void 1122 smb_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 */ 1192 static void 1193 smb_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