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