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