1 /*
2 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4
5 /*
6 * BSD 3 Clause License
7 *
8 * Copyright (c) 2007, The Storage Networking Industry Association.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * - Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * - Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * - Neither the name of The Storage Networking Industry Association (SNIA)
22 * nor the names of its contributors may be used to endorse or promote
23 * products derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38 /* Copyright 2017 Nexenta Systems, Inc. All rights reserved. */
39
40 /*
41 * This file implemets the post-order, pre-order and level-order
42 * traversing of the file system. The related macros and constants
43 * are defined in traverse.h.
44 */
45
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <sys/param.h>
49 #include <assert.h>
50 #include <cstack.h>
51 #include <dirent.h>
52 #include <errno.h>
53 #include <traverse.h>
54 #include <limits.h>
55 #include <stdarg.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <syslog.h>
60 #include <fcntl.h>
61 #include <unistd.h>
62 #include <tlm.h>
63 #include "tlm_proto.h"
64
65 /*
66 * Check if it's "." or ".."
67 */
68 boolean_t
69 rootfs_dot_or_dotdot(char *name)
70 {
71 if (*name != '.')
72 return (FALSE);
73
74 if ((name[1] == 0) || (name[1] == '.' && name[2] == 0))
75 return (TRUE);
76
77 return (FALSE);
78 }
79
80 /*
81 * Macros on fs_traverse flags.
82 */
83 #define STOP_ONERR(f) ((f)->ft_flags & FST_STOP_ONERR)
84 #define STOP_ONLONG(f) ((f)->ft_flags & FST_STOP_ONLONG)
85
86 #define CALLBACK(pp, ep) \
87 (*(ftp)->ft_callbk)((ftp)->ft_arg, pp, ep)
88
89 #define NEGATE(rv) ((rv) = -(rv))
90
91 /*
92 * The traversing state that is pushed onto the stack.
93 * This include:
94 * - The end of the path of the current directory.
95 * - The position of the last component on it.
96 * - The read position in the directory.
97 * - The file handle of the directory.
98 * - The stat of the directory.
99 */
100 typedef struct traverse_state {
101 char *ts_end;
102 char *ts_ent;
103 long ts_dpos; /* position in the directory when reading its entries */
104 fs_fhandle_t ts_fh;
105 struct stat64 ts_st;
106 } traverse_state_t;
107
108 typedef struct {
109 struct stat64 fd_attr;
110 fs_fhandle_t fd_fh;
111 short fd_len;
112 char fd_name[1];
113 } fs_dent_info_t;
114
115 typedef struct dent_arg {
116 char *da_buf;
117 int da_end;
118 int da_size;
119 } dent_arg_t;
120
121 static int traverse_level_nondir(struct fs_traverse *ftp,
122 traverse_state_t *tsp, struct fst_node *pnp);
123
124 /*
125 * Creates a new traversing state based on the path passed to it.
126 */
127 static traverse_state_t *
128 new_tsp(char *path)
129 {
130 traverse_state_t *tsp;
131 tsp = ndmp_malloc(sizeof (traverse_state_t));
132 if (!tsp)
133 return (NULL);
134
135 tsp->ts_end = strchr(path, '\0');
136 if (*(tsp->ts_end-1) == '/')
137 *--tsp->ts_end = '\0';
138 tsp->ts_ent = NULL;
139 tsp->ts_dpos = 0;
140
141 return (tsp);
142 }
143
144 /*
145 * Create a file handle and get stats for the given path
146 */
147 int
148 fs_getstat(char *path, fs_fhandle_t *fh, struct stat64 *st)
149 {
150 if (lstat64(path, st) == -1) {
151 syslog(LOG_INFO,
152 "lstat64() says [%s] not found errno=(%d)", path, errno);
153 return (errno);
154 }
155
156 fh->fh_fid = st->st_ino;
157
158 if (!S_ISDIR(st->st_mode))
159 fh->fh_fpath = NULL;
160 else
161 fh->fh_fpath = strdup(path);
162 return (0);
163 }
164
165 /*
166 * Read the directory entries and return the information about
167 * each entry
168 */
169 int
170 fs_readdir(fs_fhandle_t *ts_fh, char *path, long *dpos,
171 char *nm, int *el, fs_fhandle_t *efh, struct stat64 *est)
172 {
173 struct dirent *dp;
174 char file_path[PATH_MAX + 1];
175 DIR *dirp;
176 int rv;
177
178 if ((dirp = opendir(ts_fh->fh_fpath)) == NULL)
179 return (errno);
180
181 seekdir(dirp, *dpos);
182 if ((dp = readdir(dirp)) == NULL) {
183 rv = 0; /* skip this dir */
184 *el = 0;
185 } else {
186 (void) snprintf(file_path, PATH_MAX, "%s/", path);
187 (void) strlcat(file_path, dp->d_name, PATH_MAX + 1);
188
189 rv = fs_getstat(file_path, efh, est);
190 if (rv == 0) {
191 *dpos = telldir(dirp);
192 (void) strlcpy(nm, dp->d_name, NAME_MAX + 1);
193 *el = strlen(dp->d_name);
194 } else {
195 *el = 0;
196 }
197 }
198 (void) closedir(dirp);
199 return (rv);
200 }
201
202 /*
203 * Traverse the file system in the post-order way. The description
204 * and example is in the header file.
205 *
206 * The callback function should return 0, on success and non-zero on
207 * failure. If the callback function returns non-zero return value,
208 * the traversing stops.
209 */
210 int
211 traverse_post(struct fs_traverse *ftp)
212 {
213 char path[PATH_MAX + 1]; /* full path name of the current dir */
214 char nm[NAME_MAX + 1]; /* directory entry name */
215 char *lp; /* last position on the path */
216 int next_dir, rv;
217 int pl, el; /* path and directory entry length */
218 cstack_t *sp;
219 fs_fhandle_t pfh, efh;
220 struct stat64 pst, est;
221 traverse_state_t *tsp;
222 struct fst_node pn, en; /* parent and entry nodes */
223
224 if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {
225 errno = EINVAL;
226 return (-1);
227 }
228
229 /* set the default log function if it's not already set */
230 if (!ftp->ft_logfp) {
231 ftp->ft_logfp = (ft_log_t)syslog;
232 syslog(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
233 }
234
235 /* set the logical path to physical path if it's not already set */
236 if (!ftp->ft_lpath) {
237 syslog(LOG_DEBUG,
238 "report the same paths: \"%s\"", ftp->ft_path);
239 ftp->ft_lpath = ftp->ft_path;
240 }
241
242 pl = strlen(ftp->ft_lpath);
243 if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
244 syslog(LOG_ERR, "lpath too long \"%s\"", ftp->ft_path);
245 errno = ENAMETOOLONG;
246 return (-1);
247 }
248 (void) strcpy(path, ftp->ft_lpath);
249 (void) memset(&pfh, 0, sizeof (pfh));
250 rv = fs_getstat(ftp->ft_lpath, &pfh, &pst);
251
252 if (rv != 0) {
253 syslog(LOG_ERR,
254 "Error %d on fs_getstat(%s)", rv, ftp->ft_path);
255 return (rv);
256 }
257
258 if (!S_ISDIR(pst.st_mode)) {
259 pn.tn_path = ftp->ft_lpath;
260 pn.tn_fh = &pfh;
261 pn.tn_st = &pst;
262 en.tn_path = NULL;
263 en.tn_fh = NULL;
264 en.tn_st = NULL;
265 rv = CALLBACK(&pn, &en);
266 free(pfh.fh_fpath);
267 return (rv);
268 }
269
270 sp = cstack_new();
271 if (!sp) {
272 errno = ENOMEM;
273 free(pfh.fh_fpath);
274 return (-1);
275 }
276 tsp = new_tsp(path);
277 if (!tsp) {
278 cstack_delete(sp);
279 errno = ENOMEM;
280 free(pfh.fh_fpath);
281 return (-1);
282 }
283 tsp->ts_ent = tsp->ts_end;
284 tsp->ts_fh = pfh;
285 tsp->ts_st = pst;
286 pn.tn_path = path;
287 pn.tn_fh = &tsp->ts_fh;
288 pn.tn_st = &tsp->ts_st;
289
290 rv = 0;
291 next_dir = 1;
292 do {
293 if (next_dir) {
294 *tsp->ts_end = '\0';
295 }
296
297 next_dir = 0;
298 do {
299 el = NAME_MAX;
300 rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
301 &tsp->ts_dpos, nm, &el,
302 &efh, &est);
303
304 if (rv != 0) {
305 syslog(LOG_ERR,
306 "Error %d on readdir(%s) pos %d",
307 rv, path, tsp->ts_dpos);
308 if (STOP_ONERR(ftp))
309 break;
310 rv = SKIP_ENTRY;
311
312 continue;
313 }
314
315 /* done with this directory */
316 if (el == 0) {
317 break;
318 }
319 nm[el] = '\0';
320
321 if (rootfs_dot_or_dotdot(nm)) {
322 free(efh.fh_fpath);
323 continue;
324 }
325
326 if (pl + 1 + el > PATH_MAX) {
327 syslog(LOG_ERR, "Path %s/%s is too long.",
328 path, nm);
329 if (STOP_ONLONG(ftp))
330 rv = ENAMETOOLONG;
331 free(efh.fh_fpath);
332 continue;
333 }
334
335 /*
336 * Push the current directory on to the stack and
337 * dive into the entry found.
338 */
339 if (S_ISDIR(est.st_mode)) {
340
341 assert(tsp != NULL);
342 if (cstack_push(sp, tsp, 0)) {
343 rv = ENOMEM;
344 free(efh.fh_fpath);
345 break;
346 }
347
348 /*
349 * Concatenate the current entry with the
350 * current path. This will be the path of
351 * the new directory to be scanned.
352 *
353 * Note:
354 * sprintf(tsp->ts_end, "/%s", de->d_name);
355 * could be used here, but concatenating
356 * strings like this might be faster.
357 * The length of the new path has been
358 * checked above. So strcpy() can be
359 * safe and should not lead to a buffer
360 * over-run.
361 */
362 lp = tsp->ts_end;
363 *tsp->ts_end = '/';
364 (void) strcpy(tsp->ts_end + 1, nm);
365
366 tsp = new_tsp(path);
367 if (!tsp) {
368 free(efh.fh_fpath);
369 rv = ENOMEM;
370 } else {
371 next_dir = 1;
372 pl += el;
373 tsp->ts_fh = efh;
374 tsp->ts_st = est;
375 tsp->ts_ent = lp;
376 pn.tn_fh = &tsp->ts_fh;
377 pn.tn_st = &tsp->ts_st;
378 }
379 break;
380 } else {
381 /*
382 * The entry is not a directory so the
383 * callback function must be called.
384 */
385 en.tn_path = nm;
386 en.tn_fh = &efh;
387 en.tn_st = &est;
388 rv = CALLBACK(&pn, &en);
389 free(efh.fh_fpath);
390 if (rv != 0)
391 break;
392 }
393 } while (rv == 0);
394
395 /*
396 * A new directory must be processed, go to the start of
397 * the loop, open it and process it.
398 */
399 if (next_dir)
400 continue;
401
402 if (rv == SKIP_ENTRY)
403 rv = 0; /* We should skip the current directory */
404
405 if (rv == 0) {
406 /*
407 * Remove the ent from the end of path and send it
408 * as an entry of the path.
409 */
410 lp = tsp->ts_ent;
411 *lp = '\0';
412 efh = tsp->ts_fh;
413 est = tsp->ts_st;
414 free(tsp);
415 if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
416 break;
417
418 assert(tsp != NULL);
419 pl = tsp->ts_end - path;
420
421 pn.tn_fh = &tsp->ts_fh;
422 pn.tn_st = &tsp->ts_st;
423 en.tn_path = lp + 1;
424 en.tn_fh = &efh;
425 en.tn_st = &est;
426
427 rv = CALLBACK(&pn, &en);
428 free(efh.fh_fpath);
429 /*
430 * Does not need to free tsp here. It will be released
431 * later.
432 */
433 }
434
435 if (rv != 0 && tsp) {
436 free(tsp->ts_fh.fh_fpath);
437 free(tsp);
438 }
439
440 } while (rv == 0);
441
442 /*
443 * For the 'ftp->ft_path' directory itself.
444 */
445 if (rv == 0) {
446 pn.tn_fh = &efh;
447 pn.tn_st = &est;
448 en.tn_path = NULL;
449 en.tn_fh = NULL;
450 en.tn_st = NULL;
451 rv = CALLBACK(&pn, &en);
452 }
453
454 /*
455 * Pop and free all the remaining entries on the stack.
456 */
457 while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {
458 free(tsp->ts_fh.fh_fpath);
459 free(tsp);
460 }
461
462 cstack_delete(sp);
463 return (rv);
464 }
465
466 /*
467 * In one pass, read all the directory entries of the specified
468 * directory and call the callback function for non-directory
469 * entries.
470 *
471 * On return:
472 * 0: Lets the directory to be scanned for directory entries.
473 * < 0: Completely stops traversing.
474 * FST_SKIP: stops further scanning of the directory. Traversing
475 * will continue with the next directory in the hierarchy.
476 * SKIP_ENTRY: Failed to get the directory entries, so the caller
477 * should skip this entry.
478 */
479 static int
480 traverse_level_nondir(struct fs_traverse *ftp,
481 traverse_state_t *tsp, struct fst_node *pnp)
482 {
483 struct stat64 st;
484 fs_fhandle_t fh;
485 DIR *dp;
486 struct dirent *dirp;
487 struct fst_node en; /* entry node */
488 char path[MAXPATHLEN+MAXNAMELEN+2];
489 int rv = 0;
490
491 if ((dp = opendir(tsp->ts_fh.fh_fpath)) == NULL) {
492 syslog(LOG_ERR,
493 "traverse_level_nondir: open directory "
494 "%s failed: %m", tsp->ts_fh.fh_fpath);
495 return (errno);
496 }
497
498 while ((dirp = readdir(dp)) != NULL) {
499 if ((strcmp(dirp->d_name, ".") == 0) ||
500 (strcmp(dirp->d_name, "..") == 0)) {
501 continue;
502 }
503
504 if (!tlm_cat_path(path, tsp->ts_fh.fh_fpath,
505 dirp->d_name)) {
506 continue;
507 }
508
509 if (lstat64(path, &st) != 0) {
510 syslog(LOG_ERR,
511 "traverse_level_nondir: failed to get file"
512 " status for %s skipping: %m", tsp->ts_fh.fh_fpath);
513 continue;
514 }
515 fh.fh_fid = st.st_ino;
516
517 /*
518 * The entry is not a directory so the callback
519 * function must be called.
520 */
521 if (!S_ISDIR(st.st_mode)) {
522 en.tn_path = dirp->d_name;
523 en.tn_fh = &fh;
524 en.tn_st = &st;
525 rv = CALLBACK(pnp, &en);
526 if (rv < 0) {
527 syslog(LOG_DEBUG,
528 "traverse_level_nondir: result is %d "
529 "with %s", rv, path);
530 break;
531 }
532 if (rv == FST_SKIP) {
533 syslog(LOG_DEBUG,
534 "traverse_level_nondir: skipping "
535 "%s", path);
536 break;
537 }
538 }
539 }
540
541 (void) closedir(dp);
542 return (rv);
543 }
544
545 /*
546 * Traverse the file system in the level-order way. The description
547 * and example is in the header file.
548 */
549 int
550 traverse_level(struct fs_traverse *ftp)
551 {
552 char path[PATH_MAX + 1]; /* full path name of the current dir */
553 char nm[NAME_MAX + 1]; /* directory entry name */
554 char *lp; /* last position on the path */
555 int next_dir, rv;
556 int pl, el; /* path and directory entry length */
557
558 cstack_t *sp;
559 fs_fhandle_t pfh, efh;
560 struct stat64 pst, est;
561 traverse_state_t *tsp;
562 struct fst_node pn, en; /* parent and entry nodes */
563
564 if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) {
565 errno = EINVAL;
566 return (-1);
567 }
568 /* set the default log function if it's not already set */
569 if (!ftp->ft_logfp) {
570 ftp->ft_logfp = (ft_log_t)syslog;
571 syslog(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path);
572 }
573 if (!ftp->ft_lpath) {
574 syslog(LOG_DEBUG,
575 "report the same paths \"%s\"", ftp->ft_path);
576 ftp->ft_lpath = ftp->ft_path;
577 }
578
579 pl = strlen(ftp->ft_lpath);
580 if (pl + 1 > PATH_MAX) { /* +1 for the '/' */
581 syslog(LOG_ERR, "lpath too long \"%s\"", ftp->ft_path);
582 errno = ENAMETOOLONG;
583 return (-1);
584 }
585 (void) strcpy(path, ftp->ft_lpath);
586 (void) memset(&pfh, 0, sizeof (pfh));
587 rv = fs_getstat(ftp->ft_lpath, &pfh, &pst);
588 if (rv != 0) {
589 syslog(LOG_DEBUG,
590 "Error %d on fs_getstat(%s)", rv, ftp->ft_lpath);
591 return (-1);
592 }
593
594 en.tn_path = NULL;
595 en.tn_fh = NULL;
596 en.tn_st = NULL;
597 if (!S_ISDIR(pst.st_mode)) {
598 pn.tn_path = ftp->ft_lpath;
599 pn.tn_fh = &pfh;
600 pn.tn_st = &pst;
601 rv = CALLBACK(&pn, &en);
602 free(pfh.fh_fpath);
603 return (rv);
604 }
605
606 sp = cstack_new();
607 if (!sp) {
608 free(pfh.fh_fpath);
609 errno = ENOMEM;
610 return (-1);
611 }
612 tsp = new_tsp(path);
613 if (!tsp) {
614 cstack_delete(sp);
615 free(pfh.fh_fpath);
616 errno = ENOMEM;
617 return (-1);
618 }
619
620 tsp->ts_ent = tsp->ts_end;
621 tsp->ts_fh = pfh;
622 tsp->ts_st = pst;
623 pn.tn_path = path;
624 pn.tn_fh = &tsp->ts_fh;
625 pn.tn_st = &tsp->ts_st;
626
627 /* call the callback function on the path itself */
628 rv = CALLBACK(&pn, &en);
629 if (rv < 0) {
630 free(tsp);
631 goto end;
632 }
633 if (rv == FST_SKIP) {
634 free(tsp);
635 rv = 0;
636 goto end;
637 }
638
639 rv = 0;
640 next_dir = 1;
641 do {
642 if (next_dir) {
643 *tsp->ts_end = '\0';
644 rv = traverse_level_nondir(ftp, tsp, &pn);
645 if (rv < 0) {
646 NEGATE(rv);
647 free(tsp->ts_fh.fh_fpath);
648 free(tsp);
649 break;
650 }
651 /*
652 * If skipped by the callback function or
653 * error happened reading the information
654 */
655 if (rv == FST_SKIP || rv == SKIP_ENTRY) {
656 /*
657 * N.B. next_dir should be set to 0 as
658 * well. This prevents the infinite loop.
659 * If it's not set the same directory will
660 * be poped from the stack and will be
661 * scanned again.
662 */
663 next_dir = 0;
664 rv = 0;
665 goto skip_dir;
666 }
667
668 /* re-start reading entries of the directory */
669 tsp->ts_dpos = 0;
670 }
671
672 next_dir = 0;
673 do {
674 el = NAME_MAX;
675 rv = fs_readdir(&tsp->ts_fh, pn.tn_path,
676 &tsp->ts_dpos, nm, &el, &efh,
677 &est);
678 if (rv != 0) {
679 syslog(LOG_DEBUG,
680 "Error %d on readdir(%s) pos %d",
681 rv, path, tsp->ts_dpos);
682 if (STOP_ONERR(ftp))
683 break;
684 rv = SKIP_ENTRY;
685 continue;
686 }
687
688 /* done with this directory */
689 if (el == 0)
690 break;
691
692 nm[el] = '\0';
693
694 if (rootfs_dot_or_dotdot(nm)) {
695 free(efh.fh_fpath);
696 continue;
697 }
698
699 if (pl + 1 + el > PATH_MAX) {
700 /*
701 * The long paths were already encountered
702 * when processing non-dir entries in.
703 * traverse_level_nondir.
704 * We don't increase fss_longpath_err
705 * counter for them again here.
706 */
707 syslog(LOG_ERR, "Path %s/%s is too long.",
708 path, nm);
709 if (STOP_ONLONG(ftp))
710 rv = ENAMETOOLONG;
711 free(efh.fh_fpath);
712 continue;
713 }
714
715 if (!S_ISDIR(est.st_mode))
716 continue;
717
718 /*
719 * Call the callback function for the new
720 * directory found, then push the current
721 * directory on to the stack. Then dive
722 * into the entry found.
723 */
724 en.tn_path = nm;
725 en.tn_fh = &efh;
726 en.tn_st = &est;
727 rv = CALLBACK(&pn, &en);
728
729 if (rv < 0) {
730 NEGATE(rv);
731 free(efh.fh_fpath);
732 break;
733 }
734 if (rv == FST_SKIP) {
735 free(efh.fh_fpath);
736 rv = 0;
737 continue;
738 }
739
740 /*
741 * Push the current directory on to the stack and
742 * dive into the entry found.
743 */
744 if (cstack_push(sp, tsp, 0)) {
745 rv = ENOMEM;
746 } else {
747 lp = tsp->ts_end;
748 *tsp->ts_end = '/';
749 (void) strcpy(tsp->ts_end + 1, nm);
750
751 tsp = new_tsp(path);
752 if (!tsp)
753 rv = ENOMEM;
754 else {
755 next_dir = 1;
756 pl += el + 1;
757 tsp->ts_fh = efh;
758 tsp->ts_st = est;
759 tsp->ts_ent = lp;
760 pn.tn_fh = &tsp->ts_fh;
761 pn.tn_st = &tsp->ts_st;
762 }
763 }
764 break;
765
766 } while (rv == 0);
767
768 /*
769 * A new directory must be processed, go to the start of
770 * the loop, open it and process it.
771 */
772 if (next_dir)
773 continue;
774 skip_dir:
775 if (tsp) {
776 free(tsp->ts_fh.fh_fpath);
777 free(tsp);
778 }
779
780 if (rv == SKIP_ENTRY)
781 rv = 0;
782
783 if (rv == 0) {
784 if (cstack_pop(sp, (void **)&tsp, (int *)NULL))
785 break;
786
787 *tsp->ts_end = '\0';
788 pl = tsp->ts_end - path;
789 pn.tn_fh = &tsp->ts_fh;
790 pn.tn_st = &tsp->ts_st;
791 }
792 } while (rv == 0);
793
794 /*
795 * Pop and free all the remaining entries on the stack.
796 */
797 while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) {
798 free(tsp->ts_fh.fh_fpath);
799 free(tsp);
800 }
801 end:
802 cstack_delete(sp);
803 return (rv);
804 }
805
806 /*
807 * filecopy - Copy a file
808 *
809 * Parameters:
810 * char *dest - Destination path
811 * char *src - Source path
812 *
813 * Returns:
814 * 0 - No errors
815 * #0 - Error occured
816 * -4 - read/write error
817 * -5 - source modified during copy
818 *
819 * Simplified version for Solaris
820 */
821 #define BUFSIZE 32768
822 int
823 filecopy(char *dest, char *src)
824 {
825 FILE *src_fh = 0;
826 FILE *dst_fh = 0;
827 struct stat64 src_attr;
828 struct stat64 dst_attr;
829 char *buf = 0;
830 u_longlong_t bytes_to_copy;
831 size_t nbytes;
832 int file_copied = 0;
833
834 buf = ndmp_malloc(BUFSIZE);
835 if (!buf)
836 return (-1);
837
838 src_fh = fopen(src, "r");
839 if (src_fh == 0) {
840 free(buf);
841 return (-2);
842 }
843
844 dst_fh = fopen(dest, "w");
845 if (dst_fh == NULL) {
846 free(buf);
847 (void) fclose(src_fh);
848 return (-3);
849 }
850
851 if (stat64(src, &src_attr) < 0) {
852 free(buf);
853 (void) fclose(src_fh);
854 (void) fclose(dst_fh);
855 return (-2);
856 }
857
858 bytes_to_copy = src_attr.st_size;
859 while (bytes_to_copy) {
860 if (bytes_to_copy > BUFSIZE)
861 nbytes = BUFSIZE;
862 else
863 nbytes = bytes_to_copy;
864
865 if ((fread(buf, nbytes, 1, src_fh) != 1) ||
866 (fwrite(buf, nbytes, 1, dst_fh) != 1))
867 break;
868 bytes_to_copy -= nbytes;
869 }
870
871 (void) fclose(src_fh);
872 (void) fclose(dst_fh);
873
874 if (bytes_to_copy > 0) {
875 free(buf);
876 /* short read/write, remove the partial file */
877 return (-4);
878 }
879
880 if (stat64(src, &dst_attr) < 0) {
881 free(buf);
882 return (-2);
883 }
884
885 free(buf);
886
887 if (!file_copied)
888 return (-5); /* source modified during copy */
889 else
890 return (0);
891 }