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