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