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