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 <strings.h> 28 #include <errno.h> 29 #include <unistd.h> 30 #include <fcntl.h> 31 #include <dirent.h> 32 #include <dlfcn.h> 33 #include <pthread.h> 34 #include <syslog.h> 35 #include <sys/fs_reparse.h> 36 #include <uuid/uuid.h> 37 38 #include <smbsrv/libsmb.h> 39 #include <smbsrv/libmlsvc.h> 40 #include <smbsrv/smb_dfs.h> 41 #include <smbsrv/smb_share.h> 42 #include <dfs.h> 43 44 /* 45 * default timeout (TTL) values (in second) for root and link 46 */ 47 #define DFS_ROOT_TIMEOUT 300 48 #define DFS_LINK_TIMEOUT 1800 49 50 /* 51 * DFS link data format in reparse point 52 * 53 * ver:state:prop:timeout:guid:ntarget:cmntlen:comment 54 * [[:tserver:tshare:tstate:pclass:prank]...] 55 */ 56 #define DFS_LINK_V1 1 57 #define DFS_LINK_HDR_NFIELDS 7 /* # fields in header section */ 58 #define DFS_LINK_TRGT_NFIELDS 5 /* # fields for each target */ 59 60 #define DFS_ROOT_XATTR "SUNWdfs.rootinfo" 61 62 #define DFS_INFO_ALL 0 63 64 static void *dfs_intr_hdl = NULL; 65 66 static struct { 67 int (*dfsops_remote_count)(uint32_t *); 68 } dfs_intr_ops; 69 70 /* 71 * Namespace cache 72 * 73 * Caches links' UNC and filesystem path where the key is the UNC path. 74 */ 75 static smb_cache_t dfs_nscache; 76 static char dfs_nbname[NETBIOS_NAME_SZ]; 77 78 /* 79 * The name of cached namespace. This will be the only 80 * exported namespace until hosting multiple namespaces 81 * is supported 82 */ 83 static char dfs_cached_ns[MAXNAMELEN]; 84 static mutex_t dfs_nsmtx; 85 86 /* 87 * Lock for accessing root information (extended attribute) 88 */ 89 static rwlock_t dfs_root_rwl; 90 91 extern uint32_t srvsvc_shr_setdfsroot(smb_share_t *, boolean_t); 92 93 /* 94 * Namespace functions 95 */ 96 static boolean_t dfs_namespace_findlink(const char *, char *, char *, size_t); 97 static void *dfs_namespace_cache(void *); 98 static boolean_t dfs_namespace_iscached(const char *); 99 100 /* 101 * Root functions 102 */ 103 static int dfs_root_add(const char *, dfs_info_t *); 104 static uint32_t dfs_root_remove(const char *); 105 static uint32_t dfs_root_encode(dfs_info_t *, char **, size_t *); 106 static uint32_t dfs_root_decode(dfs_info_t *, char *, size_t, uint32_t); 107 static uint32_t dfs_root_isvalidstate(uint32_t); 108 109 static int dfs_root_xopen(const char *, int); 110 static void dfs_root_xclose(int); 111 static uint32_t dfs_root_xwrite(int, dfs_info_t *); 112 static uint32_t dfs_root_xread(int, dfs_info_t *, uint32_t); 113 114 /* 115 * Link functions 116 */ 117 static uint32_t dfs_link_encode(dfs_info_t *, char *, size_t); 118 static uint32_t dfs_link_decode(dfs_info_t *, char *, uint32_t); 119 static uint32_t dfs_link_commit(const char *, dfs_info_t *); 120 static boolean_t dfs_link_isvalidstate(uint32_t); 121 122 /* 123 * Target functions 124 */ 125 static void dfs_target_init(dfs_target_t *, const char *, const char *, 126 uint32_t); 127 static int dfs_target_find(dfs_target_t *, uint32_t, const char *, 128 const char *); 129 static boolean_t dfs_target_isvalidstate(uint32_t); 130 131 /* 132 * Cache functions 133 */ 134 static uint32_t dfs_cache_add_byunc(const char *, const char *, uint32_t); 135 static void dfs_cache_populate(const char *, const char *); 136 static int dfs_cache_cmp(const void *, const void *); 137 static void dfs_cache_flush(const char *); 138 static uint32_t dfs_cache_nscount(void); 139 140 /* 141 * Utility functions 142 */ 143 static boolean_t dfs_path_isdir(const char *); 144 static uint32_t dfs_modinfo(uint32_t, dfs_info_t *, dfs_info_t *, uint32_t); 145 146 /* 147 * DFS module initializationr: 148 * 149 * - creates the namespace cache 150 * - gets system's NetBIOS name 151 */ 152 void 153 dfs_init(void) 154 { 155 smb_cache_create(&dfs_nscache, 0, dfs_cache_cmp, free, bcopy, 156 sizeof (dfs_nscnode_t)); 157 158 if (smb_getnetbiosname(dfs_nbname, sizeof (dfs_nbname)) != 0) { 159 syslog(LOG_ERR, "dfs: can't get machine name"); 160 return; 161 } 162 163 bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops)); 164 165 if ((dfs_intr_hdl = smb_dlopen()) == NULL) 166 return; 167 168 if ((dfs_intr_ops.dfsops_remote_count = 169 (int (*)())dlsym(dfs_intr_hdl, "smb_dfs_remote_count")) == NULL) { 170 smb_dlclose(dfs_intr_hdl); 171 dfs_intr_hdl = NULL; 172 bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops)); 173 } 174 } 175 176 /* 177 * DFS module cleanup: 178 * 179 * - destroys the namespace cache 180 */ 181 void 182 dfs_fini(void) 183 { 184 smb_dlclose(dfs_intr_hdl); 185 smb_cache_destroy(&dfs_nscache); 186 } 187 188 /* 189 * To successfully handle some of link/root requests, some 190 * file system operations need to be performed. These operations 191 * should take place on behalf of the connected user (typically 192 * Administrator) and to do so we need to have an infrastructure 193 * in place so that smbd can act as a client and sends request to 194 * the kernel. Right now, we lack this infrastructure, so we make 195 * a compromise by temporarily enabling some privileges for smbd 196 * to be able to fulfill various link/root requests. 197 */ 198 void 199 dfs_setpriv(priv_op_t op) 200 { 201 (void) priv_set(op, PRIV_EFFECTIVE, 202 PRIV_FILE_DAC_READ, 203 PRIV_FILE_DAC_WRITE, 204 PRIV_FILE_DAC_EXECUTE, 205 PRIV_FILE_DAC_SEARCH, NULL); 206 } 207 208 /* 209 * ======================== 210 * Namespace API (public) 211 * ======================== 212 */ 213 214 /* 215 * Launches a thread to cache the specified namespace 216 */ 217 void 218 dfs_namespace_load(const char *name) 219 { 220 pthread_t thr; 221 pthread_attr_t tattr; 222 char *rootshr; 223 int rc; 224 225 if ((rootshr = strdup(name)) == NULL) { 226 syslog(LOG_ERR, "dfs: failed to load %s namespace (no memory)", 227 name); 228 return; 229 } 230 231 (void) pthread_attr_init(&tattr); 232 (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 233 rc = pthread_create(&thr, &tattr, dfs_namespace_cache, rootshr); 234 (void) pthread_attr_destroy(&tattr); 235 236 if (rc != 0) 237 syslog(LOG_ERR, "dfs: fail to loading %s namespace (%d)", 238 name, rc); 239 } 240 241 /* 242 * Flushes the cache when a DFS root share is removed 243 */ 244 void /*ARGSUSED*/ 245 dfs_namespace_unload(const char *name) 246 { 247 dfs_cache_flush(name); 248 } 249 250 /* 251 * Returns the file system path for the given share if it 252 * is a DFS root share. 253 * If 'path' is NULL, this function only indicates whether 254 * or not the given share represents a DFS namespace 255 */ 256 uint32_t 257 dfs_namespace_path(const char *name, char *path, size_t pathsz) 258 { 259 smb_share_t si; 260 261 if (smb_shr_get((char *)name, &si) != NERR_Success) 262 return (ERROR_NOT_FOUND); 263 264 if ((si.shr_flags & SMB_SHRF_DFSROOT) == 0) 265 return (ERROR_NOT_FOUND); 266 267 if (!dfs_namespace_iscached(name)) 268 return (ERROR_NOT_FOUND); 269 270 if (path != NULL) 271 (void) strlcpy(path, si.shr_path, pathsz); 272 273 return (ERROR_SUCCESS); 274 } 275 276 /* 277 * Returns the number of DFS root shares i.e. the number 278 * of standalone namespaces. 279 */ 280 uint32_t 281 dfs_namespace_count(void) 282 { 283 uint32_t nroot = 0; 284 int rc; 285 286 if (dfs_intr_ops.dfsops_remote_count != NULL && 287 (rc = dfs_intr_ops.dfsops_remote_count(&nroot)) != 0) { 288 /* 289 * If this call fails, let's assume there's at least one root 290 * namespace already configured. The interposer library cannot 291 * confirm or deny the presence of a namespace, so let's take 292 * the safe approach and assume one exists. 293 */ 294 nroot = 1; 295 syslog(LOG_WARNING, "dfs: dfsops_remote_count() failed: %d, " 296 "assuming one namespace exists", rc); 297 } 298 299 nroot += dfs_cache_nscount(); 300 301 return (nroot); 302 } 303 304 /* 305 * Creates a DFS root with the given name and comment. 306 * 307 * This function does not create the root share, it 308 * should already exist. 309 */ 310 uint32_t 311 dfs_namespace_add(const char *rootshr, const char *cmnt) 312 { 313 dfs_info_t info; 314 dfs_target_t t; 315 smb_share_t si; 316 uuid_t uuid; 317 uint32_t status; 318 319 if (*rootshr == '\\') { 320 /* Windows has a special case here! */ 321 return (ERROR_BAD_PATHNAME); 322 } 323 324 if (smb_shr_get((char *)rootshr, &si) != NERR_Success) 325 return (NERR_NetNameNotFound); 326 327 (void) mutex_lock(&dfs_nsmtx); 328 if (smb_strcasecmp(dfs_cached_ns, rootshr, 0) == 0) { 329 /* This DFS root is already exported */ 330 (void) mutex_unlock(&dfs_nsmtx); 331 return (ERROR_FILE_EXISTS); 332 } 333 334 if (*dfs_cached_ns != '\0') { 335 syslog(LOG_WARNING, "dfs: trying to add %s namespace." 336 " Only one standalone namespace is supported." 337 " A namespace is already exported for %s", 338 rootshr, dfs_cached_ns); 339 (void) mutex_unlock(&dfs_nsmtx); 340 return (ERROR_NOT_SUPPORTED); 341 } 342 343 bzero(&info, sizeof (info)); 344 if (cmnt) 345 (void) strlcpy(info.i_comment, cmnt, sizeof (info.i_comment)); 346 info.i_state = DFS_VOLUME_STATE_OK | DFS_VOLUME_FLAVOR_STANDALONE; 347 info.i_timeout = DFS_ROOT_TIMEOUT; 348 info.i_propflags = 0; 349 350 uuid_generate_random(uuid); 351 uuid_unparse(uuid, info.i_guid); 352 353 dfs_target_init(&t, dfs_nbname, rootshr, DFS_STORAGE_STATE_ONLINE); 354 355 info.i_ntargets = 1; 356 info.i_targets = &t; 357 358 if ((status = dfs_root_add(si.shr_path, &info)) != ERROR_SUCCESS) { 359 (void) mutex_unlock(&dfs_nsmtx); 360 return (status); 361 } 362 363 status = srvsvc_shr_setdfsroot(&si, B_TRUE); 364 if (status == ERROR_SUCCESS) { 365 (void) dfs_cache_add_byname(rootshr, NULL, DFS_OBJECT_ROOT); 366 (void) strlcpy(dfs_cached_ns, rootshr, sizeof (dfs_cached_ns)); 367 (void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 1); 368 } 369 (void) mutex_unlock(&dfs_nsmtx); 370 371 return (status); 372 } 373 374 /* 375 * Removes the namespace and all the links in it. 376 */ 377 uint32_t 378 dfs_namespace_remove(const char *name) 379 { 380 smb_cache_cursor_t cursor; 381 dfs_nscnode_t nscnode; 382 smb_share_t si; 383 uint32_t status; 384 385 if (smb_shr_get((char *)name, &si) != NERR_Success) 386 return (ERROR_NOT_FOUND); 387 388 if ((si.shr_flags & SMB_SHRF_DFSROOT) == 0) 389 return (ERROR_NOT_FOUND); 390 391 if ((status = dfs_root_remove(si.shr_path)) != ERROR_SUCCESS) 392 return (status); 393 394 status = srvsvc_shr_setdfsroot(&si, B_FALSE); 395 if (status != ERROR_SUCCESS) 396 syslog(LOG_WARNING, "dfs: failed to disable root share %s (%d)", 397 name, status); 398 399 if (!dfs_namespace_iscached(name)) 400 return (ERROR_SUCCESS); 401 402 smb_cache_iterinit(&dfs_nscache, &cursor); 403 404 while (smb_cache_iterate(&dfs_nscache, &cursor, &nscnode)) { 405 if (nscnode.nsc_type == DFS_OBJECT_ROOT) 406 continue; 407 status = dfs_link_remove(nscnode.nsc_fspath, NULL, NULL); 408 if (status != ERROR_SUCCESS) 409 syslog(LOG_WARNING, "dfs: failed to remove %s (%d)", 410 nscnode.nsc_fspath, status); 411 } 412 413 dfs_cache_flush(name); 414 415 /* TODO: remove empty dirs */ 416 return (ERROR_SUCCESS); 417 } 418 419 /* 420 * Determines the DFS namespace flavor. 421 */ 422 uint32_t 423 dfs_namespace_getflavor(const char *name) 424 { 425 char rootdir[DFS_PATH_MAX]; 426 dfs_info_t info; 427 428 if (dfs_namespace_path(name, rootdir, DFS_PATH_MAX) != ERROR_SUCCESS) 429 return (0); 430 431 /* get flavor info from state info (info level 2) */ 432 if (dfs_root_getinfo(rootdir, &info, 2) != ERROR_SUCCESS) 433 return (0); 434 435 return (info.i_state & DFS_VOLUME_FLAVORS); 436 } 437 438 /* 439 * ================== 440 * Root API (public) 441 * ================== 442 */ 443 444 /* 445 * Retrieves the information of the root specified by its path. 446 * 447 * Info level (1) only needs the UNC path which is not stored, 448 * it is constructed so the function will return without 449 * accessing the backend storage. 450 */ 451 uint32_t 452 dfs_root_getinfo(const char *rootdir, dfs_info_t *info, uint32_t infolvl) 453 { 454 uint32_t status = ERROR_INTERNAL_ERROR; 455 int xfd; 456 457 bzero(info, sizeof (dfs_info_t)); 458 info->i_type = DFS_OBJECT_ROOT; 459 460 if (infolvl == 1) 461 return (ERROR_SUCCESS); 462 463 (void) rw_rdlock(&dfs_root_rwl); 464 if ((xfd = dfs_root_xopen(rootdir, O_RDONLY)) > 0) { 465 status = dfs_root_xread(xfd, info, infolvl); 466 dfs_root_xclose(xfd); 467 } 468 (void) rw_unlock(&dfs_root_rwl); 469 470 return (status); 471 } 472 473 /* 474 * Sets the provided information for the specified root or root target. 475 * Root is specified by 'rootdir' and the target is specified by 476 * (t_server, t_share) pair. Only information items needed for given 477 * information level (infolvl) is valid in the passed DFS info structure 478 * 'info'. 479 */ 480 uint32_t 481 dfs_root_setinfo(const char *rootdir, dfs_info_t *info, uint32_t infolvl) 482 { 483 dfs_info_t curinfo; 484 uint32_t status = ERROR_SUCCESS; 485 int xfd; 486 487 (void) rw_wrlock(&dfs_root_rwl); 488 if ((xfd = dfs_root_xopen(rootdir, O_RDWR)) < 0) { 489 (void) rw_unlock(&dfs_root_rwl); 490 return (ERROR_INTERNAL_ERROR); 491 } 492 493 status = dfs_root_xread(xfd, &curinfo, DFS_INFO_ALL); 494 if (status != ERROR_SUCCESS) { 495 dfs_root_xclose(xfd); 496 (void) rw_unlock(&dfs_root_rwl); 497 return (status); 498 } 499 500 status = dfs_modinfo(DFS_OBJECT_ROOT, &curinfo, info, infolvl); 501 if (status == ERROR_SUCCESS) 502 status = dfs_root_xwrite(xfd, &curinfo); 503 504 dfs_root_xclose(xfd); 505 (void) rw_unlock(&dfs_root_rwl); 506 507 dfs_info_free(&curinfo); 508 return (status); 509 } 510 511 /* 512 * ================== 513 * Link API (public) 514 * ================== 515 */ 516 517 /* 518 * Gets the status of the given path as a link 519 */ 520 uint32_t 521 dfs_link_stat(const char *path, uint32_t *stat) 522 { 523 if (smb_reparse_stat(path, stat) != 0) 524 return (ERROR_INTERNAL_ERROR); 525 526 switch (*stat) { 527 case SMB_REPARSE_NOTFOUND: 528 *stat = DFS_STAT_NOTFOUND; 529 break; 530 case SMB_REPARSE_NOTREPARSE: 531 *stat = DFS_STAT_NOTLINK; 532 break; 533 case SMB_REPARSE_ISREPARSE: 534 *stat = DFS_STAT_ISREPARSE; 535 if (smb_reparse_svcget(path, DFS_REPARSE_SVCTYPE, NULL) == 0) 536 *stat = DFS_STAT_ISDFS; 537 break; 538 default: 539 *stat = DFS_STAT_UNKNOWN; 540 break; 541 } 542 543 return (ERROR_SUCCESS); 544 } 545 546 /* 547 * Creates a new DFS link or adds a new target to an existing link 548 */ 549 uint32_t 550 dfs_link_add(const char *path, const char *server, const char *share, 551 const char *cmnt, uint32_t flags, boolean_t *newlink) 552 { 553 dfs_info_t info; 554 dfs_target_t *t; 555 int ntargets; 556 uint32_t status; 557 uint32_t stat; 558 559 *newlink = B_FALSE; 560 561 if ((status = dfs_link_stat(path, &stat)) != ERROR_SUCCESS) 562 return (status); 563 564 switch (stat) { 565 case DFS_STAT_NOTFOUND: 566 case DFS_STAT_ISREPARSE: 567 /* Create a new DFS link */ 568 569 status = dfs_link_getinfo(NULL, &info, DFS_INFO_ALL); 570 if (status != ERROR_SUCCESS) 571 return (status); 572 573 (void) strlcpy(info.i_comment, (cmnt) ? cmnt : "", 574 sizeof (info.i_comment)); 575 *newlink = B_TRUE; 576 break; 577 578 case DFS_STAT_ISDFS: 579 /* Add a target to an existing link */ 580 581 if (flags & DFS_ADD_VOLUME) 582 return (ERROR_FILE_EXISTS); 583 584 status = dfs_link_getinfo(path, &info, DFS_INFO_ALL); 585 if (status != ERROR_SUCCESS) 586 return (status); 587 588 break; 589 590 case DFS_STAT_NOTLINK: 591 /* specified path points to a non-reparse object */ 592 return (ERROR_FILE_EXISTS); 593 594 default: 595 return (ERROR_INTERNAL_ERROR); 596 } 597 598 /* checks to see if the target already exists */ 599 ntargets = info.i_ntargets; 600 if (dfs_target_find(info.i_targets, ntargets, server, share) != -1) { 601 dfs_info_free(&info); 602 return (ERROR_FILE_EXISTS); 603 } 604 605 /* add the new target */ 606 t = realloc(info.i_targets, (ntargets + 1) * sizeof (dfs_target_t)); 607 if (t == NULL) { 608 dfs_info_free(&info); 609 return (ERROR_NOT_ENOUGH_MEMORY); 610 } 611 612 info.i_targets = t; 613 dfs_target_init(&info.i_targets[ntargets], server, share, 614 DFS_STORAGE_STATE_ONLINE); 615 info.i_ntargets++; 616 617 status = dfs_link_commit(path, &info); 618 619 dfs_info_free(&info); 620 return (status); 621 } 622 623 /* 624 * Removes a link or a link target from a DFS namespace. A link can be 625 * removed regardless of the number of targets associated with it. 626 * 627 * 'server' and 'share' parameters specify a target, so if they are NULL 628 * it means the link should be removed, otherwise the specified target 629 * is removed if found. 630 */ 631 uint32_t 632 dfs_link_remove(const char *path, const char *server, const char *share) 633 { 634 dfs_info_t info; 635 uint32_t status, stat; 636 int rc, idx; 637 638 if ((status = dfs_link_stat(path, &stat)) != ERROR_SUCCESS) 639 return (status); 640 641 if (stat != DFS_STAT_ISDFS) 642 return (ERROR_NOT_FOUND); 643 644 if (server == NULL && share == NULL) { 645 /* remove the link */ 646 if (smb_reparse_svcdel(path, DFS_REPARSE_SVCTYPE) != 0) 647 return (ERROR_INTERNAL_ERROR); 648 649 return (ERROR_SUCCESS); 650 } 651 652 /* remove the specified target in the link */ 653 654 status = dfs_link_getinfo(path, &info, DFS_INFO_ALL); 655 if (status != ERROR_SUCCESS) 656 return (status); 657 658 /* checks to see if the target exists */ 659 idx = dfs_target_find(info.i_targets, info.i_ntargets, server, share); 660 if (idx != -1) { 661 bcopy(&info.i_targets[idx + 1], &info.i_targets[idx], 662 (info.i_ntargets - idx - 1) * sizeof (dfs_target_t)); 663 info.i_ntargets--; 664 } else { 665 dfs_info_free(&info); 666 return (ERROR_FILE_NOT_FOUND); 667 } 668 669 if (info.i_ntargets == 0) { 670 /* if last target, then remove the link */ 671 rc = smb_reparse_svcdel(path, DFS_REPARSE_SVCTYPE); 672 status = (rc == 0) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR; 673 } else { 674 status = dfs_link_commit(path, &info); 675 } 676 677 dfs_info_free(&info); 678 return (status); 679 } 680 681 /* 682 * Sets the provided information for the specified link or link target. 683 * Link is specified by 'path' and the target is specified by 684 * (t_server, t_share) pair. Only information items needed for given 685 * information level (infolvl) is valid in the passed DFS info structure 686 * 'info'. 687 */ 688 uint32_t 689 dfs_link_setinfo(const char *path, dfs_info_t *info, uint32_t infolvl) 690 { 691 dfs_info_t curinfo; 692 uint32_t status; 693 694 status = dfs_link_getinfo(path, &curinfo, DFS_INFO_ALL); 695 if (status != ERROR_SUCCESS) 696 return (status); 697 698 status = dfs_modinfo(DFS_OBJECT_LINK, &curinfo, info, infolvl); 699 if (status == ERROR_SUCCESS) 700 status = dfs_link_commit(path, &curinfo); 701 702 dfs_info_free(&curinfo); 703 return (status); 704 } 705 706 /* 707 * Gets the DFS link info. 708 * 709 * If path is NULL, it just does some initialization. 710 * 711 * Info level (1) only needs the UNC path which is not 712 * stored, it is constructed so the function will return 713 * without accessing the backend storage. 714 */ 715 uint32_t 716 dfs_link_getinfo(const char *path, dfs_info_t *info, uint32_t infolvl) 717 { 718 char *link_data; 719 uint32_t status; 720 uuid_t uuid; 721 int rc; 722 723 bzero(info, sizeof (dfs_info_t)); 724 info->i_type = DFS_OBJECT_LINK; 725 726 if (path == NULL) { 727 info->i_state = DFS_VOLUME_STATE_OK; 728 info->i_timeout = DFS_LINK_TIMEOUT; 729 info->i_propflags = 0; 730 uuid_generate_random(uuid); 731 uuid_unparse(uuid, info->i_guid); 732 return (ERROR_SUCCESS); 733 } 734 735 if (infolvl == 1) 736 return (ERROR_SUCCESS); 737 738 rc = smb_reparse_svcget(path, DFS_REPARSE_SVCTYPE, &link_data); 739 if (rc != 0) 740 return (ERROR_INTERNAL_ERROR); 741 742 status = dfs_link_decode(info, link_data, infolvl); 743 free(link_data); 744 745 return (status); 746 } 747 748 /* 749 * =================== 750 * Cache API (public) 751 * =================== 752 */ 753 754 /* 755 * Adds an entry with given DFS name (root sharename) and relative path 756 * to the share (relpath) and the specified entry type (i.e. root/link) 757 * to the namespace cache. 758 */ 759 uint32_t 760 dfs_cache_add_byname(const char *name, const char *relpath, uint32_t type) 761 { 762 char uncpath[DFS_PATH_MAX]; 763 char fspath[DFS_PATH_MAX]; 764 smb_share_t si; 765 766 if (smb_shr_get((char *)name, &si) != NERR_Success) 767 return (ERROR_NOT_FOUND); 768 769 if (type == DFS_OBJECT_ROOT) { 770 (void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s", 771 dfs_nbname, name); 772 return (dfs_cache_add_byunc(uncpath, si.shr_path, type)); 773 } 774 775 /* add link entry */ 776 (void) snprintf(fspath, DFS_PATH_MAX, "%s/%s", si.shr_path, relpath); 777 (void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s\\%s", dfs_nbname, 778 name, relpath); 779 780 /* relpath may contain '/' */ 781 (void) strsubst(uncpath, '/', '\\'); 782 783 return (dfs_cache_add_byunc(uncpath, fspath, type)); 784 } 785 786 /* 787 * Removes the namespace cache entry for the given link 788 * in the namespace ('name') with 'relpath' 789 */ 790 void 791 dfs_cache_remove(const char *name, const char *relpath) 792 { 793 dfs_nscnode_t dn; 794 795 (void) snprintf(dn.nsc_uncpath, sizeof (dn.nsc_uncpath), 796 "\\\\%s\\%s\\%s", dfs_nbname, name, relpath); 797 798 /* relpath may contain '/' */ 799 (void) strsubst(dn.nsc_uncpath, '/', '\\'); 800 801 smb_cache_remove(&dfs_nscache, &dn); 802 } 803 804 /* 805 * Get the DFS data for the specified cache entry 806 */ 807 uint32_t 808 dfs_cache_getinfo(dfs_nscnode_t *dn, dfs_info_t *info, uint32_t infolvl) 809 { 810 uint32_t status; 811 812 if (dn->nsc_type == DFS_OBJECT_LINK) 813 status = dfs_link_getinfo(dn->nsc_fspath, info, infolvl); 814 else 815 status = dfs_root_getinfo(dn->nsc_fspath, info, infolvl); 816 817 (void) strlcpy(info->i_uncpath, dn->nsc_uncpath, 818 sizeof (info->i_uncpath)); 819 820 if (status == ERROR_SUCCESS) 821 dfs_info_trace("dfs_cache_getinfo", info); 822 823 return (status); 824 } 825 826 /* 827 * Returns the number of cache entries i.e. the number of 828 * root(s) and link(s) 829 */ 830 uint32_t 831 dfs_cache_num(void) 832 { 833 return (smb_cache_num(&dfs_nscache)); 834 } 835 836 void 837 dfs_cache_iterinit(smb_cache_cursor_t *cursor) 838 { 839 smb_cache_iterinit(&dfs_nscache, cursor); 840 } 841 842 boolean_t 843 dfs_cache_iterate(smb_cache_cursor_t *cursor, dfs_nscnode_t *dn) 844 { 845 return (smb_cache_iterate(&dfs_nscache, cursor, dn)); 846 } 847 848 /* 849 * ================== 850 * Misc API (public) 851 * ================== 852 */ 853 854 /* 855 * This is the function that is called by smbd door server to 856 * fullfil a GetReferrals request from smbsrv kernel module 857 * 858 * 'reftype' specifies the requested referral type. If it is 859 * DFS_REFERRAL_ROOT then dfs_path should point to a namespace 860 * root. If it is DFS_REFERRAL_LINK then dfs_path should CONTAIN 861 * a link, in which case this function will find the link and 862 * returns its target information. 863 */ 864 uint32_t 865 dfs_get_referrals(const char *dfs_path, dfs_reftype_t reftype, 866 dfs_info_t *referrals) 867 { 868 dfs_path_t path; 869 smb_unc_t *unc; 870 char linkpath[DFS_PATH_MAX]; 871 uint32_t status; 872 873 status = dfs_path_parse(&path, dfs_path, DFS_OBJECT_ANY); 874 if (status != ERROR_SUCCESS) 875 return (status); 876 877 dfs_setpriv(PRIV_ON); 878 879 referrals->i_type = path.p_type; 880 881 switch (reftype) { 882 case DFS_REFERRAL_ROOT: 883 if (path.p_type != DFS_OBJECT_ROOT) { 884 status = ERROR_INVALID_PARAMETER; 885 break; 886 } 887 888 status = dfs_root_getinfo((const char *)path.p_fspath, 889 referrals, DFS_INFO_ALL); 890 (void) strlcpy(referrals->i_uncpath, dfs_path, DFS_PATH_MAX); 891 break; 892 893 case DFS_REFERRAL_LINK: 894 if (path.p_type != DFS_OBJECT_LINK) { 895 status = ERROR_INVALID_PARAMETER; 896 break; 897 } 898 899 unc = &path.p_unc; 900 if (!dfs_namespace_findlink(unc->unc_share, unc->unc_path, 901 linkpath, DFS_PATH_MAX)) { 902 status = ERROR_NOT_FOUND; 903 break; 904 } 905 906 status = dfs_link_getinfo(linkpath, referrals, DFS_INFO_ALL); 907 (void) snprintf(referrals->i_uncpath, DFS_PATH_MAX, "/%s/%s/%s", 908 unc->unc_server, unc->unc_share, unc->unc_path); 909 break; 910 911 default: 912 status = ERROR_INVALID_PARAMETER; 913 break; 914 } 915 916 dfs_setpriv(PRIV_OFF); 917 dfs_path_free(&path); 918 return (status); 919 } 920 921 /* 922 * Takes a DFS path in UNC format (dfs_path) and parse it into a dfs_path_t 923 * structure. 924 * 925 * dfs_path_free() MUST be called to free the allocated memory in this 926 * function. 927 * 928 * Returns: 929 * 930 * ERROR_INVALID_PARAMETER path is not a valid UNC or not valid for the 931 * specified object type 932 * ERROR_NOT_ENOUGH_MEMORY not enough memory to peform the parse 933 * ERROR_NOT_FOUND namespace specified does not exist 934 */ 935 uint32_t 936 dfs_path_parse(dfs_path_t *path, const char *dfs_path, uint32_t path_type) 937 { 938 char rootdir[DFS_PATH_MAX]; 939 smb_unc_t *unc; 940 uint32_t status = ERROR_SUCCESS; 941 int rc; 942 943 bzero(path, sizeof (dfs_path_t)); 944 unc = &path->p_unc; 945 946 rc = smb_unc_init(dfs_path, unc); 947 switch (rc) { 948 case EINVAL: 949 return (ERROR_INVALID_PARAMETER); 950 case ENOMEM: 951 return (ERROR_NOT_ENOUGH_MEMORY); 952 default: 953 break; 954 } 955 956 if (dfs_namespace_path(unc->unc_share, rootdir, DFS_PATH_MAX) 957 != ERROR_SUCCESS) { 958 smb_unc_free(unc); 959 return (ERROR_NOT_FOUND); 960 } 961 962 if (path_type == DFS_OBJECT_ANY) 963 path->p_type = (unc->unc_path != NULL) 964 ? DFS_OBJECT_LINK : DFS_OBJECT_ROOT; 965 else 966 path->p_type = path_type; 967 968 switch (path->p_type) { 969 case DFS_OBJECT_LINK: 970 if ((unc->unc_path == NULL) || (*unc->unc_path == '\0')) 971 status = ERROR_NOT_FOUND; 972 else 973 (void) snprintf(path->p_fspath, sizeof (path->p_fspath), 974 "%s/%s", rootdir, unc->unc_path); 975 break; 976 977 case DFS_OBJECT_ROOT: 978 if (unc->unc_path == NULL) 979 (void) strlcpy(path->p_fspath, rootdir, 980 sizeof (path->p_fspath)); 981 else 982 status = ERROR_INVALID_PARAMETER; 983 break; 984 985 default: 986 status = ERROR_INVALID_PARAMETER; 987 } 988 989 if (status != ERROR_SUCCESS) 990 smb_unc_free(unc); 991 992 return (status); 993 } 994 995 /* 996 * Frees the allocated memory for p_unc field of the passed path 997 */ 998 void 999 dfs_path_free(dfs_path_t *path) 1000 { 1001 if (path != NULL) 1002 smb_unc_free(&path->p_unc); 1003 } 1004 1005 /* 1006 * Free the allocated memory for targets in the given info 1007 * structure 1008 */ 1009 void 1010 dfs_info_free(dfs_info_t *info) 1011 { 1012 if (info) 1013 free(info->i_targets); 1014 } 1015 1016 /* 1017 * Trace the given DFS info structure 1018 */ 1019 void 1020 dfs_info_trace(const char *msg, dfs_info_t *info) 1021 { 1022 dfs_target_t *t; 1023 int i; 1024 1025 smb_tracef("%s", msg); 1026 if (info == NULL) 1027 return; 1028 1029 smb_tracef("UNC\t%s", info->i_uncpath); 1030 smb_tracef("comment\t%s", info->i_comment); 1031 smb_tracef("GUID\t%s", info->i_guid); 1032 smb_tracef("state\t%X", info->i_state); 1033 smb_tracef("timeout\t%d", info->i_timeout); 1034 smb_tracef("props\t%X", info->i_propflags); 1035 smb_tracef("# targets\t%X", info->i_ntargets); 1036 1037 if (info->i_targets == NULL) 1038 return; 1039 1040 for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) { 1041 smb_tracef("[%d] \\\\%s\\%s", i, t->t_server, t->t_share); 1042 smb_tracef("[%d] state\t%X", i, t->t_state); 1043 smb_tracef("[%d] priority\t%d:%d", i, t->t_priority.p_class, 1044 t->t_priority.p_rank); 1045 } 1046 } 1047 1048 /* 1049 * Search the path specified by 'relpath' to see if it contains 1050 * a DFS link starting from the last component. If a link is found 1051 * the full path is returned in 'linkpath' 1052 */ 1053 static boolean_t 1054 dfs_namespace_findlink(const char *name, char *relpath, 1055 char *linkpath, size_t bufsz) 1056 { 1057 char rootdir[DFS_PATH_MAX]; 1058 uint32_t stat; 1059 char *p; 1060 1061 if (dfs_namespace_path(name, rootdir, DFS_PATH_MAX) != ERROR_SUCCESS) 1062 return (B_FALSE); 1063 1064 (void) snprintf(linkpath, bufsz, "%s/%s", rootdir, relpath); 1065 1066 for (;;) { 1067 if (dfs_link_stat(linkpath, &stat) != ERROR_SUCCESS) 1068 return (B_FALSE); 1069 1070 if (stat == DFS_STAT_ISDFS) 1071 return (B_TRUE); 1072 1073 if ((p = strrchr(relpath, '/')) == NULL) 1074 return (B_FALSE); 1075 *p = '\0'; 1076 1077 (void) snprintf(linkpath, bufsz, "%s/%s", rootdir, relpath); 1078 } 1079 1080 /*NOTREACHED*/ 1081 return (B_FALSE); 1082 } 1083 1084 /* 1085 * Caches the specified namespace 1086 */ 1087 static void * 1088 dfs_namespace_cache(void *arg) 1089 { 1090 char *share = arg; 1091 char uncpath[DFS_PATH_MAX]; 1092 smb_share_t si; 1093 1094 if (smb_shr_get(share, &si) != NERR_Success) { 1095 free(share); 1096 return (NULL); 1097 } 1098 1099 /* 1100 * This check should be removed when multiple standalone 1101 * namespaces are supported. 1102 */ 1103 (void) mutex_lock(&dfs_nsmtx); 1104 if (*dfs_cached_ns != '\0') { 1105 syslog(LOG_WARNING, "dfs: trying to load %s namespace." 1106 " Only one standalone namespace is supported." 1107 " A namespace is already exported for %s", 1108 share, dfs_cached_ns); 1109 (void) mutex_unlock(&dfs_nsmtx); 1110 free(share); 1111 return (NULL); 1112 } 1113 (void) strlcpy(dfs_cached_ns, share, sizeof (dfs_cached_ns)); 1114 (void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 1); 1115 (void) mutex_unlock(&dfs_nsmtx); 1116 1117 (void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s", dfs_nbname, share); 1118 (void) dfs_cache_add_byunc(uncpath, si.shr_path, DFS_OBJECT_ROOT); 1119 1120 dfs_cache_populate(uncpath, si.shr_path); 1121 1122 free(share); 1123 return (NULL); 1124 } 1125 1126 /* 1127 * Checks whether the given name matches the name of 1128 * the cached namespace. 1129 */ 1130 static boolean_t 1131 dfs_namespace_iscached(const char *name) 1132 { 1133 boolean_t iscached; 1134 1135 (void) mutex_lock(&dfs_nsmtx); 1136 iscached = (smb_strcasecmp(name, dfs_cached_ns, 0) == 0); 1137 (void) mutex_unlock(&dfs_nsmtx); 1138 1139 return (iscached); 1140 } 1141 1142 static int 1143 dfs_root_add(const char *rootdir, dfs_info_t *info) 1144 { 1145 uint32_t status = ERROR_INTERNAL_ERROR; 1146 int xfd; 1147 1148 (void) rw_wrlock(&dfs_root_rwl); 1149 if ((xfd = dfs_root_xopen(rootdir, O_CREAT | O_TRUNC | O_RDWR)) > 0) { 1150 status = dfs_root_xwrite(xfd, info); 1151 dfs_root_xclose(xfd); 1152 } 1153 (void) rw_unlock(&dfs_root_rwl); 1154 1155 return (status); 1156 } 1157 1158 /* 1159 * Deletes the specified root information 1160 */ 1161 static uint32_t 1162 dfs_root_remove(const char *rootdir) 1163 { 1164 int attrdirfd; 1165 int err = 0; 1166 1167 (void) rw_wrlock(&dfs_root_rwl); 1168 1169 if ((attrdirfd = attropen(rootdir, ".", O_RDONLY)) > 0) { 1170 if (unlinkat(attrdirfd, DFS_ROOT_XATTR, 0) == -1) { 1171 if (errno != ENOENT) 1172 err = errno; 1173 } 1174 (void) close(attrdirfd); 1175 } else { 1176 err = errno; 1177 } 1178 1179 (void) rw_unlock(&dfs_root_rwl); 1180 1181 if (err != 0) { 1182 syslog(LOG_DEBUG, "dfs: failed to remove root info %s (%d)", 1183 rootdir, err); 1184 return (ERROR_INTERNAL_ERROR); 1185 } 1186 1187 return (ERROR_SUCCESS); 1188 } 1189 1190 /* 1191 * Opens DFS root directory's extended attribute with the given mode. 1192 */ 1193 static int 1194 dfs_root_xopen(const char *rootdir, int oflag) 1195 { 1196 int dfd; 1197 int xfd = -1; 1198 int err = 0; 1199 1200 if ((dfd = open(rootdir, O_RDONLY)) > 0) { 1201 xfd = openat(dfd, DFS_ROOT_XATTR, oflag | O_XATTR, 0600); 1202 if (xfd == -1) 1203 err = errno; 1204 (void) close(dfd); 1205 } else { 1206 err = errno; 1207 } 1208 1209 if (err != 0) { 1210 syslog(LOG_DEBUG, "dfs: failed to open root directory %s (%d)", 1211 rootdir, err); 1212 } 1213 1214 return (xfd); 1215 } 1216 1217 /* 1218 * Closes given extended attribute file descriptor 1219 */ 1220 static void 1221 dfs_root_xclose(int xfd) 1222 { 1223 (void) close(xfd); 1224 } 1225 1226 /* 1227 * Writes the given DFS data in the DFS root directory's 1228 * extended attribute specified with xfd file descriptor. 1229 */ 1230 static uint32_t 1231 dfs_root_xwrite(int xfd, dfs_info_t *info) 1232 { 1233 size_t nbytes; 1234 char *buf = NULL; 1235 size_t buflen; 1236 uint32_t status; 1237 1238 if ((status = dfs_root_encode(info, &buf, &buflen)) != ERROR_SUCCESS) 1239 return (status); 1240 1241 (void) lseek(xfd, 0, SEEK_SET); 1242 nbytes = write(xfd, buf, buflen); 1243 free(buf); 1244 1245 return ((nbytes == buflen) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR); 1246 } 1247 1248 /* 1249 * Reads DFS root information from its directory extended attribute 1250 * and parse it into given dfs_info_t structure 1251 */ 1252 static uint32_t 1253 dfs_root_xread(int xfd, dfs_info_t *info, uint32_t infolvl) 1254 { 1255 struct stat statbuf; 1256 uint32_t status; 1257 char *buf; 1258 1259 if (fstat(xfd, &statbuf) != 0) 1260 return (ERROR_INTERNAL_ERROR); 1261 1262 if ((buf = malloc(statbuf.st_size)) == NULL) 1263 return (ERROR_NOT_ENOUGH_MEMORY); 1264 1265 if (read(xfd, buf, statbuf.st_size) == statbuf.st_size) 1266 status = dfs_root_decode(info, buf, statbuf.st_size, infolvl); 1267 else 1268 status = ERROR_INTERNAL_ERROR; 1269 1270 free(buf); 1271 return (status); 1272 } 1273 1274 /* 1275 * Encodes (packs) DFS information in 'info' into a flat 1276 * buffer in a name-value format. This function allocates a 1277 * buffer with appropriate size to contain all the information 1278 * so the caller MUST free the allocated memory by calling free(). 1279 */ 1280 static uint32_t 1281 dfs_root_encode(dfs_info_t *info, char **buf, size_t *bufsz) 1282 { 1283 dfs_target_t *t; 1284 nvlist_t *nvl; 1285 int rc; 1286 1287 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 1288 return (ERROR_NOT_ENOUGH_MEMORY); 1289 1290 rc = nvlist_add_string(nvl, "comment", info->i_comment); 1291 rc |= nvlist_add_string(nvl, "guid", info->i_guid); 1292 rc |= nvlist_add_uint32(nvl, "state", info->i_state); 1293 rc |= nvlist_add_uint32(nvl, "timeout", info->i_timeout); 1294 rc |= nvlist_add_uint32(nvl, "propflags", info->i_propflags); 1295 t = info->i_targets; 1296 rc |= nvlist_add_string(nvl, "t_server", t->t_server); 1297 rc |= nvlist_add_string(nvl, "t_share", t->t_share); 1298 rc |= nvlist_add_uint32(nvl, "t_state", t->t_state); 1299 rc |= nvlist_add_uint32(nvl, "t_priority_class", 1300 t->t_priority.p_class); 1301 rc |= nvlist_add_uint16(nvl, "t_priority_rank", 1302 t->t_priority.p_rank); 1303 1304 if (rc == 0) 1305 rc = nvlist_pack(nvl, buf, bufsz, NV_ENCODE_NATIVE, 0); 1306 1307 nvlist_free(nvl); 1308 1309 return ((rc == 0) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR); 1310 } 1311 1312 /* 1313 * Decodes (unpack) provided buffer which contains a list of name-value 1314 * pairs into given dfs_info_t structure 1315 */ 1316 static uint32_t 1317 dfs_root_decode(dfs_info_t *info, char *buf, size_t bufsz, uint32_t infolvl) 1318 { 1319 nvlist_t *nvl; 1320 char *cmnt, *guid; 1321 char *t_server, *t_share; 1322 uint32_t t_state; 1323 uint32_t t_priority_class; 1324 uint16_t t_priority_rank; 1325 boolean_t decode_priority = B_FALSE; 1326 int rc; 1327 1328 if (nvlist_unpack(buf, bufsz, &nvl, 0) != 0) 1329 return (ERROR_INTERNAL_ERROR); 1330 1331 rc = nvlist_lookup_string(nvl, "comment", &cmnt); 1332 rc |= nvlist_lookup_string(nvl, "guid", &guid); 1333 rc |= nvlist_lookup_uint32(nvl, "state", &info->i_state); 1334 rc |= nvlist_lookup_uint32(nvl, "timeout", &info->i_timeout); 1335 rc |= nvlist_lookup_uint32(nvl, "propflags", &info->i_propflags); 1336 1337 if (rc != 0) { 1338 nvlist_free(nvl); 1339 return (ERROR_INTERNAL_ERROR); 1340 } 1341 1342 (void) strlcpy(info->i_comment, (cmnt) ? cmnt : "", 1343 sizeof (info->i_comment)); 1344 (void) strlcpy(info->i_guid, (guid) ? guid : "", sizeof (info->i_guid)); 1345 1346 info->i_targets = NULL; 1347 info->i_ntargets = 1; 1348 1349 switch (infolvl) { 1350 case DFS_INFO_ALL: 1351 case 3: 1352 case 4: 1353 /* need target information */ 1354 break; 1355 case 6: 1356 case 9: 1357 /* need target and priority information */ 1358 decode_priority = B_TRUE; 1359 break; 1360 default: 1361 nvlist_free(nvl); 1362 return (ERROR_SUCCESS); 1363 } 1364 1365 info->i_targets = malloc(sizeof (dfs_target_t)); 1366 if (info->i_targets == NULL) { 1367 nvlist_free(nvl); 1368 return (ERROR_NOT_ENOUGH_MEMORY); 1369 } 1370 1371 rc = nvlist_lookup_string(nvl, "t_server", &t_server); 1372 rc |= nvlist_lookup_string(nvl, "t_share", &t_share); 1373 rc |= nvlist_lookup_uint32(nvl, "t_state", &t_state); 1374 if (rc != 0) { 1375 nvlist_free(nvl); 1376 free(info->i_targets); 1377 return (ERROR_INTERNAL_ERROR); 1378 } 1379 dfs_target_init(info->i_targets, t_server, t_share, t_state); 1380 1381 if (decode_priority) { 1382 rc = nvlist_lookup_uint32(nvl, "t_priority_class", 1383 &t_priority_class); 1384 if (rc == 0) 1385 rc = nvlist_lookup_uint16(nvl, "t_priority_rank", 1386 &t_priority_rank); 1387 1388 if (rc != 0 && rc != ENOENT) { 1389 nvlist_free(nvl); 1390 free(info->i_targets); 1391 return (ERROR_INTERNAL_ERROR); 1392 } else if (rc == 0) { 1393 info->i_targets->t_priority.p_class = t_priority_class; 1394 info->i_targets->t_priority.p_rank = t_priority_rank; 1395 } 1396 } 1397 1398 nvlist_free(nvl); 1399 return (ERROR_SUCCESS); 1400 } 1401 1402 /* 1403 * Determines if the passed state is valid for a DFS root 1404 * 1405 * This is based on test results against Win2003 and in some cases 1406 * does not match [MS-DFSNM] spec. 1407 */ 1408 static uint32_t 1409 dfs_root_isvalidstate(uint32_t state) 1410 { 1411 switch (state) { 1412 case DFS_VOLUME_STATE_OK: 1413 case DFS_VOLUME_STATE_RESYNCHRONIZE: 1414 return (ERROR_SUCCESS); 1415 1416 case DFS_VOLUME_STATE_INCONSISTENT: 1417 case DFS_VOLUME_STATE_FORCE_SYNC: 1418 return (ERROR_INVALID_PARAMETER); 1419 1420 case DFS_VOLUME_STATE_OFFLINE: 1421 case DFS_VOLUME_STATE_ONLINE: 1422 case DFS_VOLUME_STATE_STANDBY: 1423 return (ERROR_NOT_SUPPORTED); 1424 default: 1425 break; 1426 } 1427 1428 return (ERROR_INVALID_PARAMETER); 1429 } 1430 1431 /* 1432 * Decodes the link info from given string buffer (buf) into 1433 * dfs_info_t structure. 1434 */ 1435 static uint32_t 1436 dfs_link_decode(dfs_info_t *info, char *buf, uint32_t infolvl) 1437 { 1438 char *lfield[DFS_LINK_HDR_NFIELDS]; 1439 dfs_target_t *t; 1440 uint32_t linkver; 1441 uint32_t cmntlen; 1442 uint32_t cpylen; 1443 int i, j; 1444 1445 /* 1446 * Header format 1447 * ver:state:prop:timeout:guid:ntarget:cmntlen:comment: 1448 */ 1449 for (i = 0; i < DFS_LINK_HDR_NFIELDS; i++) { 1450 if ((lfield[i] = strsep((char **)&buf, ":")) == NULL) 1451 return (ERROR_INVALID_DATA); 1452 } 1453 1454 i = 0; 1455 linkver = strtoul(lfield[i++], NULL, 10); 1456 if (linkver != DFS_LINK_V1) 1457 return (ERROR_INVALID_DATA); 1458 1459 info->i_state = strtoul(lfield[i++], NULL, 10); 1460 info->i_propflags = strtoul(lfield[i++], NULL, 10); 1461 info->i_timeout = strtoul(lfield[i++], NULL, 10); 1462 (void) strlcpy(info->i_guid, lfield[i++], sizeof (info->i_guid)); 1463 info->i_ntargets = strtoul(lfield[i++], NULL, 10); 1464 info->i_targets = NULL; 1465 1466 cpylen = cmntlen = strtoul(lfield[i++], NULL, 10); 1467 1468 if (cmntlen > sizeof (info->i_comment)) 1469 cpylen = sizeof (info->i_comment); 1470 else if (cmntlen != 0) 1471 cpylen = cmntlen + 1; 1472 1473 (void) strlcpy(info->i_comment, buf, cpylen); 1474 buf += (cmntlen + 1); 1475 1476 switch (infolvl) { 1477 case DFS_INFO_ALL: 1478 case 3: 1479 case 4: 1480 case 6: 1481 case 9: 1482 /* need target information */ 1483 break; 1484 default: 1485 return (ERROR_SUCCESS); 1486 } 1487 1488 info->i_targets = calloc(info->i_ntargets, sizeof (dfs_target_t)); 1489 if (info->i_targets == NULL) 1490 return (ERROR_NOT_ENOUGH_MEMORY); 1491 1492 /* 1493 * Format for each target 1494 * server:share:state:class:rank 1495 */ 1496 for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) { 1497 for (j = 0; j < DFS_LINK_TRGT_NFIELDS; j++) { 1498 if ((lfield[j] = strsep((char **)&buf, ":")) == NULL) { 1499 dfs_info_free(info); 1500 return (ERROR_INVALID_DATA); 1501 } 1502 } 1503 1504 (void) strlcpy(t->t_server, lfield[0], sizeof (t->t_server)); 1505 (void) strlcpy(t->t_share, lfield[1], sizeof (t->t_share)); 1506 t->t_state = strtoul(lfield[2], NULL, 10); 1507 t->t_priority.p_class = strtoul(lfield[3], NULL, 10); 1508 t->t_priority.p_rank = strtoul(lfield[4], NULL, 10); 1509 } 1510 1511 return (ERROR_SUCCESS); 1512 } 1513 1514 /* 1515 * Encodes given link information (info) 1516 */ 1517 static uint32_t 1518 dfs_link_encode(dfs_info_t *info, char *buf, size_t bufsz) 1519 { 1520 char linkdata[MAXREPARSELEN]; 1521 dfs_target_t *t; 1522 int i, sz; 1523 1524 /* 1525 * Header format 1526 * ver:state:prop:timeout:guid:ntarget:cmntlen:comment 1527 */ 1528 sz = snprintf(buf, bufsz, "%u:%u:%u:%u:%s:%u:%u:%s", 1529 DFS_LINK_V1, info->i_state, info->i_propflags, info->i_timeout, 1530 info->i_guid, info->i_ntargets, 1531 strlen(info->i_comment), info->i_comment); 1532 1533 if (sz > bufsz) { 1534 syslog(LOG_WARNING, "dfs: link data is too large"); 1535 dfs_info_trace("DFS link encode", info); 1536 return (ERROR_INTERNAL_ERROR); 1537 } 1538 1539 /* 1540 * Format for each target 1541 * :server:share:state:class:rank 1542 */ 1543 bufsz -= sz; 1544 for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) { 1545 if (strchr(t->t_server, ':') || strchr(t->t_share, ':')) 1546 return (ERROR_INVALID_NAME); 1547 1548 sz = snprintf(linkdata, MAXREPARSELEN, ":%s:%s:%u:%u:%u", 1549 t->t_server, t->t_share, t->t_state, 1550 t->t_priority.p_class, t->t_priority.p_rank); 1551 if (sz > bufsz) { 1552 syslog(LOG_WARNING, "dfs: link data is too large"); 1553 dfs_info_trace("DFS link encode", info); 1554 return (ERROR_INTERNAL_ERROR); 1555 } 1556 (void) strcat(buf, linkdata); 1557 bufsz -= sz; 1558 } 1559 1560 return (ERROR_SUCCESS); 1561 } 1562 1563 /* 1564 * Stores given information for the specified link 1565 */ 1566 static uint32_t 1567 dfs_link_commit(const char *path, dfs_info_t *info) 1568 { 1569 char linkdata[MAXREPARSELEN]; 1570 uint32_t status; 1571 int rc; 1572 1573 status = dfs_link_encode(info, linkdata, MAXREPARSELEN); 1574 if (status == ERROR_SUCCESS) { 1575 rc = smb_reparse_svcadd(path, DFS_REPARSE_SVCTYPE, linkdata); 1576 if (rc != 0) 1577 status = ERROR_INTERNAL_ERROR; 1578 } 1579 1580 return (status); 1581 } 1582 1583 /* 1584 * Determines if the passed state is valid for a link 1585 */ 1586 static boolean_t 1587 dfs_link_isvalidstate(uint32_t state) 1588 { 1589 return (state == DFS_VOLUME_STATE_OK || 1590 state == DFS_VOLUME_STATE_OFFLINE || 1591 state == DFS_VOLUME_STATE_ONLINE); 1592 } 1593 1594 /* 1595 * Initializes the given target structure (t) with provided information. 1596 */ 1597 static void 1598 dfs_target_init(dfs_target_t *t, const char *srv, const char *share, 1599 uint32_t state) 1600 { 1601 (void) strlcpy(t->t_server, (srv) ? srv : "", sizeof (t->t_server)); 1602 (void) strlcpy(t->t_share, (share) ? share : "", sizeof (t->t_share)); 1603 t->t_state = state; 1604 t->t_priority.p_class = DfsSiteCostNormalPriorityClass; 1605 t->t_priority.p_rank = 0; 1606 } 1607 1608 /* 1609 * Lookup the specified target (server, share) in the given 1610 * target list (targets). If there is a match its index is 1611 * returned, otherwise -1 will be returned. 1612 */ 1613 static int 1614 dfs_target_find(dfs_target_t *targets, uint32_t ntargets, 1615 const char *server, const char *share) 1616 { 1617 dfs_target_t *t; 1618 int i; 1619 1620 for (i = 0, t = targets; i < ntargets; i++, t++) { 1621 if ((smb_strcasecmp(t->t_server, server, 0) == 0) && 1622 (smb_strcasecmp(t->t_share, share, 0) == 0)) 1623 return (i); 1624 } 1625 1626 return (-1); 1627 } 1628 1629 /* 1630 * Determines if the passed state is valid for a link/root target 1631 */ 1632 static boolean_t 1633 dfs_target_isvalidstate(uint32_t state) 1634 { 1635 return (state == DFS_STORAGE_STATE_ONLINE || 1636 state == DFS_STORAGE_STATE_OFFLINE); 1637 } 1638 1639 /* 1640 * Cache compare function, the key is UNC path 1641 */ 1642 static int 1643 dfs_cache_cmp(const void *p1, const void *p2) 1644 { 1645 smb_cache_node_t *cn1 = (smb_cache_node_t *)p1; 1646 smb_cache_node_t *cn2 = (smb_cache_node_t *)p2; 1647 dfs_nscnode_t *dn1 = cn1->cn_data; 1648 dfs_nscnode_t *dn2 = cn2->cn_data; 1649 int rc; 1650 1651 rc = smb_strcasecmp(dn1->nsc_uncpath, dn2->nsc_uncpath, 0); 1652 1653 if (rc < 0) 1654 return (-1); 1655 1656 if (rc > 0) 1657 return (1); 1658 1659 return (0); 1660 } 1661 1662 /* 1663 * Adds an entry with given UNC and filesystem path and the specified 1664 * entry type (i.e. root/link) to the namespace cache. 1665 */ 1666 static uint32_t 1667 dfs_cache_add_byunc(const char *uncpath, const char *fspath, uint32_t type) 1668 { 1669 dfs_nscnode_t *dn; 1670 uint32_t status = ERROR_SUCCESS; 1671 1672 if ((dn = malloc(sizeof (dfs_nscnode_t))) == NULL) 1673 return (ERROR_NOT_ENOUGH_MEMORY); 1674 1675 (void) strlcpy(dn->nsc_uncpath, uncpath, sizeof (dn->nsc_uncpath)); 1676 (void) strlcpy(dn->nsc_fspath, fspath, sizeof (dn->nsc_fspath)); 1677 dn->nsc_type = type; 1678 if (smb_cache_add(&dfs_nscache, dn, SMB_CACHE_ADD) != 0) { 1679 free(dn); 1680 status = ERROR_INTERNAL_ERROR; 1681 } 1682 1683 return (status); 1684 } 1685 1686 /* 1687 * starting from DFS root directory, scans the tree for DFS links 1688 * and adds them to the cache. 1689 */ 1690 static void 1691 dfs_cache_populate(const char *unc_prefix, const char *dir) 1692 { 1693 char fspath[DFS_PATH_MAX]; 1694 char uncpath[DFS_PATH_MAX]; 1695 char *fname; 1696 int nentries, i; 1697 struct dirent **entry_list; 1698 uint32_t stat; 1699 1700 nentries = scandir(dir, &entry_list, NULL, NULL); 1701 if (nentries == -1) 1702 return; 1703 1704 for (i = 0; i < nentries; i++) { 1705 fname = entry_list[i]->d_name; 1706 1707 if (strcmp(fname, ".") == 0 || 1708 strcmp(fname, "..") == 0) { 1709 free(entry_list[i]); 1710 continue; 1711 } 1712 1713 (void) snprintf(fspath, DFS_PATH_MAX, "%s/%s", dir, fname); 1714 (void) snprintf(uncpath, DFS_PATH_MAX, "%s\\%s", unc_prefix, 1715 fname); 1716 1717 if (dfs_path_isdir(fspath)) { 1718 dfs_cache_populate(uncpath, fspath); 1719 } else if (dfs_link_stat(fspath, &stat) == ERROR_SUCCESS) { 1720 if (stat == DFS_STAT_ISDFS) 1721 (void) dfs_cache_add_byunc(uncpath, fspath, 1722 DFS_OBJECT_LINK); 1723 } 1724 1725 free(entry_list[i]); 1726 } 1727 1728 for (; i < nentries; i++) 1729 free(entry_list[i]); 1730 1731 free(entry_list); 1732 } 1733 1734 /* 1735 * If this namespace hasn't been cached then return 1736 * without flushing the cache; otherwise clear the 1737 * name and flush the cache. 1738 */ 1739 static void 1740 dfs_cache_flush(const char *name) 1741 { 1742 (void) mutex_lock(&dfs_nsmtx); 1743 if (smb_strcasecmp(name, dfs_cached_ns, 0) != 0) { 1744 (void) mutex_unlock(&dfs_nsmtx); 1745 return; 1746 } 1747 *dfs_cached_ns = '\0'; 1748 (void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 0); 1749 (void) mutex_unlock(&dfs_nsmtx); 1750 1751 smb_cache_flush(&dfs_nscache); 1752 } 1753 1754 /* 1755 * Returns the number of cached namespaces 1756 */ 1757 static uint32_t 1758 dfs_cache_nscount(void) 1759 { 1760 uint32_t nscount; 1761 1762 (void) mutex_lock(&dfs_nsmtx); 1763 nscount = (*dfs_cached_ns == '\0') ? 0 : 1; 1764 (void) mutex_unlock(&dfs_nsmtx); 1765 1766 return (nscount); 1767 } 1768 1769 /* 1770 * Determines whether the given path is a directory. 1771 */ 1772 static boolean_t 1773 dfs_path_isdir(const char *path) 1774 { 1775 struct stat statbuf; 1776 1777 if (lstat(path, &statbuf) != 0) 1778 return (B_FALSE); 1779 1780 return ((statbuf.st_mode & S_IFMT) == S_IFDIR); 1781 } 1782 1783 /* 1784 * Validates the given state based on the object type (root/link), info 1785 * level, and whether it is the object's state or its target's state 1786 */ 1787 static uint32_t 1788 dfs_isvalidstate(uint32_t state, uint32_t type, boolean_t target, 1789 uint32_t infolvl) 1790 { 1791 uint32_t status = ERROR_SUCCESS; 1792 1793 switch (infolvl) { 1794 case 101: 1795 if (type == DFS_OBJECT_ROOT) { 1796 if (!target) 1797 return (dfs_root_isvalidstate(state)); 1798 1799 if (!dfs_target_isvalidstate(state)) 1800 status = ERROR_INVALID_PARAMETER; 1801 else if (state == DFS_STORAGE_STATE_OFFLINE) 1802 status = ERROR_NOT_SUPPORTED; 1803 } else { 1804 if (!target) { 1805 if (!dfs_link_isvalidstate(state)) 1806 status = ERROR_INVALID_PARAMETER; 1807 } else { 1808 if (!dfs_target_isvalidstate(state)) 1809 status = ERROR_INVALID_PARAMETER; 1810 } 1811 } 1812 break; 1813 1814 case 105: 1815 if (state == 0) 1816 return (ERROR_SUCCESS); 1817 1818 if (type == DFS_OBJECT_ROOT) { 1819 switch (state) { 1820 case DFS_VOLUME_STATE_OK: 1821 case DFS_VOLUME_STATE_OFFLINE: 1822 case DFS_VOLUME_STATE_ONLINE: 1823 case DFS_VOLUME_STATE_RESYNCHRONIZE: 1824 case DFS_VOLUME_STATE_STANDBY: 1825 status = ERROR_NOT_SUPPORTED; 1826 break; 1827 1828 default: 1829 status = ERROR_INVALID_PARAMETER; 1830 } 1831 } else { 1832 switch (state) { 1833 case DFS_VOLUME_STATE_OK: 1834 case DFS_VOLUME_STATE_OFFLINE: 1835 case DFS_VOLUME_STATE_ONLINE: 1836 break; 1837 1838 case DFS_VOLUME_STATE_RESYNCHRONIZE: 1839 case DFS_VOLUME_STATE_STANDBY: 1840 status = ERROR_NOT_SUPPORTED; 1841 break; 1842 1843 default: 1844 status = ERROR_INVALID_PARAMETER; 1845 } 1846 } 1847 break; 1848 1849 default: 1850 status = ERROR_INVALID_LEVEL; 1851 } 1852 1853 return (status); 1854 } 1855 1856 /* 1857 * Validates the given property flag mask based on the object 1858 * type (root/link) and namespace flavor. 1859 */ 1860 static uint32_t 1861 dfs_isvalidpropflagmask(uint32_t propflag_mask, uint32_t type, 1862 uint32_t flavor) 1863 { 1864 uint32_t flgs_not_supported; 1865 1866 flgs_not_supported = DFS_PROPERTY_FLAG_ROOT_SCALABILITY 1867 | DFS_PROPERTY_FLAG_CLUSTER_ENABLED 1868 | DFS_PROPERTY_FLAG_ABDE; 1869 1870 if (flavor == DFS_VOLUME_FLAVOR_STANDALONE) { 1871 if (type == DFS_OBJECT_LINK) 1872 flgs_not_supported |= DFS_PROPERTY_FLAG_SITE_COSTING; 1873 if (propflag_mask & flgs_not_supported) 1874 return (ERROR_NOT_SUPPORTED); 1875 } 1876 1877 return (ERROR_SUCCESS); 1878 } 1879 1880 /* 1881 * Based on the specified information level (infolvl) copy parts of the 1882 * information provided through newinfo into the existing information 1883 * (info) for the given object. 1884 */ 1885 static uint32_t 1886 dfs_modinfo(uint32_t type, dfs_info_t *info, dfs_info_t *newinfo, 1887 uint32_t infolvl) 1888 { 1889 boolean_t target_op = B_FALSE; 1890 uint32_t status = ERROR_SUCCESS; 1891 uint32_t state; 1892 int target_idx; 1893 1894 if (newinfo->i_targets != NULL) { 1895 target_idx = dfs_target_find(info->i_targets, info->i_ntargets, 1896 newinfo->i_targets->t_server, newinfo->i_targets->t_share); 1897 if (target_idx == -1) 1898 return (ERROR_FILE_NOT_FOUND); 1899 target_op = B_TRUE; 1900 } 1901 1902 switch (infolvl) { 1903 case 100: 1904 (void) strlcpy(info->i_comment, newinfo->i_comment, 1905 sizeof (newinfo->i_comment)); 1906 break; 1907 1908 case 101: 1909 state = (target_op) 1910 ? newinfo->i_targets->t_state : newinfo->i_state; 1911 status = dfs_isvalidstate(state, type, target_op, 101); 1912 if (status != ERROR_SUCCESS) 1913 return (status); 1914 1915 if (!target_op) { 1916 /* 1917 * states specified by this mask should not be stored 1918 */ 1919 if (state & DFS_VOLUME_STATES_SRV_OPS) 1920 return (ERROR_SUCCESS); 1921 1922 info->i_state = state; 1923 } else { 1924 info->i_targets[target_idx].t_state = state; 1925 } 1926 break; 1927 1928 case 102: 1929 info->i_timeout = newinfo->i_timeout; 1930 break; 1931 1932 case 103: 1933 info->i_propflags = newinfo->i_propflags; 1934 break; 1935 1936 case 104: 1937 info->i_targets[target_idx].t_priority = 1938 newinfo->i_targets->t_priority; 1939 break; 1940 1941 case 105: 1942 status = dfs_isvalidstate(newinfo->i_state, type, B_FALSE, 105); 1943 if (status != ERROR_SUCCESS) 1944 return (status); 1945 1946 status = dfs_isvalidpropflagmask(newinfo->i_propflag_mask, type, 1947 newinfo->i_flavor); 1948 if (status != ERROR_SUCCESS) 1949 return (status); 1950 1951 (void) strlcpy(info->i_comment, newinfo->i_comment, 1952 sizeof (newinfo->i_comment)); 1953 if (newinfo->i_state != 0) 1954 info->i_state = newinfo->i_state; 1955 info->i_timeout = newinfo->i_timeout; 1956 info->i_propflags = newinfo->i_propflags; 1957 break; 1958 1959 default: 1960 status = ERROR_INVALID_LEVEL; 1961 } 1962 1963 return (status); 1964 } 1965