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