10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 70Sstevel@tonic-gate * with the License. 80Sstevel@tonic-gate * 90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 110Sstevel@tonic-gate * See the License for the specific language governing permissions 120Sstevel@tonic-gate * and limitations under the License. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 190Sstevel@tonic-gate * 200Sstevel@tonic-gate * CDDL HEADER END 210Sstevel@tonic-gate */ 22*1219Sraf 230Sstevel@tonic-gate /* 24*1219Sraf * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 250Sstevel@tonic-gate * Use is subject to license terms. 260Sstevel@tonic-gate */ 270Sstevel@tonic-gate 280Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 290Sstevel@tonic-gate 300Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */ 310Sstevel@tonic-gate /* All Rights Reserved */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate 340Sstevel@tonic-gate /* 350Sstevel@tonic-gate * nftw - new file tree walk 360Sstevel@tonic-gate * 370Sstevel@tonic-gate * int nftw(char *path, int (*fn)(), int depth, int flags); 380Sstevel@tonic-gate * 390Sstevel@tonic-gate * Derived from System V ftw() by David Korn 400Sstevel@tonic-gate * 410Sstevel@tonic-gate * nftw visits each file and directory in the tree starting at 420Sstevel@tonic-gate * path. It uses the generic directory reading library so it works 430Sstevel@tonic-gate * for any file system type. The flags field is used to specify: 440Sstevel@tonic-gate * FTW_PHYS Physical walk, does not follow symblolic links 450Sstevel@tonic-gate * Otherwise, nftw will follow links but will not 460Sstevel@tonic-gate * walk down any path the crosses itself. 470Sstevel@tonic-gate * FTW_MOUNT The walk will not cross a mount point. 480Sstevel@tonic-gate * FTW_DEPTH All subdirectories will be visited before the 490Sstevel@tonic-gate * directory itself. 500Sstevel@tonic-gate * FTW_CHDIR The walk will change to each directory before 510Sstevel@tonic-gate * reading it. This is faster but core dumps 520Sstevel@tonic-gate * may not get generated. 530Sstevel@tonic-gate * 540Sstevel@tonic-gate * The following flags are private, and are used by the find 550Sstevel@tonic-gate * utility: 560Sstevel@tonic-gate * FTW_ANYERR Call the callback function and return 570Sstevel@tonic-gate * FTW_NS on any stat failure, not just 580Sstevel@tonic-gate * lack of permission. 590Sstevel@tonic-gate * FTW_HOPTION Use stat the first time the walk 600Sstevel@tonic-gate * function is called, regardless of 610Sstevel@tonic-gate * whether or not FTW_PHYS is specified. 620Sstevel@tonic-gate * 630Sstevel@tonic-gate * fn is called with four arguments at each file and directory. 640Sstevel@tonic-gate * The first argument is the pathname of the object, the second 650Sstevel@tonic-gate * is a pointer to the stat buffer and the third is an integer 660Sstevel@tonic-gate * giving additional information as follows: 670Sstevel@tonic-gate * 680Sstevel@tonic-gate * FTW_F The object is a file. 690Sstevel@tonic-gate * FTW_D The object is a directory. 700Sstevel@tonic-gate * FTW_DP The object is a directory and subdirectories 710Sstevel@tonic-gate * have been visited. 720Sstevel@tonic-gate * FTW_SL The object is a symbolic link. 730Sstevel@tonic-gate * FTW_SLN The object is a symbolic link pointing at a 740Sstevel@tonic-gate * non-existing file. 750Sstevel@tonic-gate * FTW_DNR The object is a directory that cannot be read. 760Sstevel@tonic-gate * fn will not be called for any of its descendants. 770Sstevel@tonic-gate * FTW_NS Stat failed on the object because of lack of 780Sstevel@tonic-gate * appropriate permission. The stat buffer passed to fn 790Sstevel@tonic-gate * is undefined. Stat failure for any reason is 800Sstevel@tonic-gate * considered an error and nftw will return -1. 810Sstevel@tonic-gate * The fourth argument is a struct FTW* which contains the depth 820Sstevel@tonic-gate * and the offset into pathname to the base name. 830Sstevel@tonic-gate * If fn returns nonzero, nftw returns this value to its caller. 840Sstevel@tonic-gate * 850Sstevel@tonic-gate * depth limits the number of open directories that ftw uses 860Sstevel@tonic-gate * before it starts recycling file descriptors. In general, 870Sstevel@tonic-gate * a file descriptor is used for each level. 880Sstevel@tonic-gate * 890Sstevel@tonic-gate */ 900Sstevel@tonic-gate 910Sstevel@tonic-gate #include <sys/feature_tests.h> 920Sstevel@tonic-gate 930Sstevel@tonic-gate #if !defined(_LP64) && _FILE_OFFSET_BITS == 64 940Sstevel@tonic-gate #pragma weak nftw64 = _nftw64 950Sstevel@tonic-gate #define _nftw _nftw64 960Sstevel@tonic-gate #define fstat64 _fstat64 970Sstevel@tonic-gate #define lstat64 _lstat64 980Sstevel@tonic-gate #define readdir64 _readdir64 990Sstevel@tonic-gate #define stat64 _stat64 1000Sstevel@tonic-gate #else 1010Sstevel@tonic-gate #pragma weak nftw = _nftw 1020Sstevel@tonic-gate #define fstat _fstat 1030Sstevel@tonic-gate #define lstat _lstat 1040Sstevel@tonic-gate #define readdir _readdir 1050Sstevel@tonic-gate #define stat _stat 1060Sstevel@tonic-gate #endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */ 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate #define chdir _chdir 1090Sstevel@tonic-gate #define closedir _closedir 1100Sstevel@tonic-gate #define fchdir _fchdir 1110Sstevel@tonic-gate #define fprintf _fprintf 1120Sstevel@tonic-gate #define getcwd _getcwd 1130Sstevel@tonic-gate #define opendir _opendir 1140Sstevel@tonic-gate #define seekdir _seekdir 115*1219Sraf #define strerror _strerror 1160Sstevel@tonic-gate #define telldir _telldir 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate #include "lint.h" 1190Sstevel@tonic-gate #include <mtlib.h> 1200Sstevel@tonic-gate #include <sys/types.h> 1210Sstevel@tonic-gate #include <sys/stat.h> 1220Sstevel@tonic-gate #include <dirent.h> 1230Sstevel@tonic-gate #include <errno.h> 1240Sstevel@tonic-gate #include <limits.h> 1250Sstevel@tonic-gate #include <ftw.h> 1260Sstevel@tonic-gate #include <stdlib.h> 1270Sstevel@tonic-gate #include <string.h> 1280Sstevel@tonic-gate #include <unistd.h> 1290Sstevel@tonic-gate #include <thread.h> 1300Sstevel@tonic-gate #include <synch.h> 1310Sstevel@tonic-gate #include <stdio.h> 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate #ifndef PATH_MAX 1340Sstevel@tonic-gate #define PATH_MAX 1023 1350Sstevel@tonic-gate #endif 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate /* 1380Sstevel@tonic-gate * Local variables (used to be static local). 1390Sstevel@tonic-gate * Putting them into a structure that is passed 1400Sstevel@tonic-gate * around makes nftw() MT-safe with no locking required. 1410Sstevel@tonic-gate */ 1420Sstevel@tonic-gate struct Var { 1430Sstevel@tonic-gate char *fullpath; 1440Sstevel@tonic-gate char *tmppath; 1450Sstevel@tonic-gate int curflags; 1460Sstevel@tonic-gate dev_t cur_mount; 1470Sstevel@tonic-gate struct FTW state; 1480Sstevel@tonic-gate int walklevel; 1490Sstevel@tonic-gate int (*statf)(const char *, struct stat *); 1500Sstevel@tonic-gate int (*savedstatf)(const char *, struct stat *); 1510Sstevel@tonic-gate }; 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate struct Save { 1540Sstevel@tonic-gate struct Save *last; 1550Sstevel@tonic-gate DIR *fd; 1560Sstevel@tonic-gate char *comp; 1570Sstevel@tonic-gate long here; 1580Sstevel@tonic-gate dev_t dev; 1590Sstevel@tonic-gate ino_t inode; 1600Sstevel@tonic-gate }; 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate static int oldclose(struct Save *); 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate /* 1650Sstevel@tonic-gate * This is the recursive walker. 1660Sstevel@tonic-gate */ 1670Sstevel@tonic-gate static int 1680Sstevel@tonic-gate walk(char *component, 1690Sstevel@tonic-gate int (*fn)(const char *, const struct stat *, int, struct FTW *), 1700Sstevel@tonic-gate int depth, struct Save *last, struct Var *vp) 1710Sstevel@tonic-gate { 1720Sstevel@tonic-gate struct stat statb; 1730Sstevel@tonic-gate char *p; 1740Sstevel@tonic-gate int type; 1750Sstevel@tonic-gate char *comp; 1760Sstevel@tonic-gate struct dirent *dir; 1770Sstevel@tonic-gate char *q; 1780Sstevel@tonic-gate int rc = 0; 1790Sstevel@tonic-gate int val = -1; 1800Sstevel@tonic-gate int cdval = -1; 1810Sstevel@tonic-gate int oldbase; 1820Sstevel@tonic-gate int skip; 1830Sstevel@tonic-gate struct Save this; 1840Sstevel@tonic-gate 1850Sstevel@tonic-gate this.last = last; 1860Sstevel@tonic-gate this.fd = 0; 1870Sstevel@tonic-gate if ((vp->curflags & FTW_CHDIR) && last) 1880Sstevel@tonic-gate comp = last->comp; 1890Sstevel@tonic-gate else 1900Sstevel@tonic-gate comp = vp->tmppath; 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate if (vp->savedstatf == NULL) 1930Sstevel@tonic-gate vp->savedstatf = vp->statf; 1940Sstevel@tonic-gate 1950Sstevel@tonic-gate if ((vp->walklevel++ == 0) && (vp->curflags & FTW_HOPTION)) 1960Sstevel@tonic-gate vp->statf = stat; 1970Sstevel@tonic-gate else 1980Sstevel@tonic-gate vp->statf = vp->savedstatf; 1990Sstevel@tonic-gate 2000Sstevel@tonic-gate /* 2010Sstevel@tonic-gate * Determine the type of the component. 2020Sstevel@tonic-gate */ 2030Sstevel@tonic-gate if ((*vp->statf)(comp, &statb) >= 0) { 2040Sstevel@tonic-gate if ((statb.st_mode & S_IFMT) == S_IFDIR) { 2050Sstevel@tonic-gate type = FTW_D; 2060Sstevel@tonic-gate if (depth <= 1) 2070Sstevel@tonic-gate (void) oldclose(last); 2080Sstevel@tonic-gate if ((this.fd = opendir(comp)) == 0) { 2090Sstevel@tonic-gate if (errno == EMFILE && oldclose(last) && 2100Sstevel@tonic-gate (this.fd = opendir(comp)) != 0) { 2110Sstevel@tonic-gate depth = 1; 2120Sstevel@tonic-gate } else { 2130Sstevel@tonic-gate type = FTW_DNR; 2140Sstevel@tonic-gate goto fail; 2150Sstevel@tonic-gate } 2160Sstevel@tonic-gate } 2170Sstevel@tonic-gate if (statb.st_fstype[0] == 'a' && 2180Sstevel@tonic-gate strcmp(statb.st_fstype, "autofs") == 0) { 2190Sstevel@tonic-gate /* 2200Sstevel@tonic-gate * this dir is on autofs 2210Sstevel@tonic-gate */ 2220Sstevel@tonic-gate if (fstat(this.fd->dd_fd, &statb) < 0) { 2230Sstevel@tonic-gate (void) closedir(this.fd); 2240Sstevel@tonic-gate type = FTW_NS; 2250Sstevel@tonic-gate goto fail; 2260Sstevel@tonic-gate } 2270Sstevel@tonic-gate } 2280Sstevel@tonic-gate } else if ((statb.st_mode & S_IFMT) == S_IFLNK) { 2290Sstevel@tonic-gate type = FTW_SL; 2300Sstevel@tonic-gate } else { 2310Sstevel@tonic-gate type = FTW_F; 2320Sstevel@tonic-gate } 2330Sstevel@tonic-gate } else if ((vp->curflags & FTW_ANYERR) && errno != ENOENT) { 2340Sstevel@tonic-gate /* 2350Sstevel@tonic-gate * If FTW_ANYERR is specified, then a stat error 2360Sstevel@tonic-gate * other than ENOENT automatically results in 2370Sstevel@tonic-gate * failure. This allows the callback function 2380Sstevel@tonic-gate * to properly handle ENAMETOOLONG and ELOOP and 2390Sstevel@tonic-gate * things of that nature, that would be masked 2400Sstevel@tonic-gate * by calling lstat before failing. 2410Sstevel@tonic-gate */ 2420Sstevel@tonic-gate type = FTW_NS; 2430Sstevel@tonic-gate goto fail; 2440Sstevel@tonic-gate } else { 2450Sstevel@tonic-gate /* 2460Sstevel@tonic-gate * Statf has failed. If stat was used instead of lstat, 2470Sstevel@tonic-gate * try using lstat. If lstat doesn't fail, "comp" 2480Sstevel@tonic-gate * must be a symbolic link pointing to a non-existent 2490Sstevel@tonic-gate * file. Such a symbolic link should be ignored. 2500Sstevel@tonic-gate * Also check the file type, if possible, for symbolic 2510Sstevel@tonic-gate * link. 2520Sstevel@tonic-gate */ 2530Sstevel@tonic-gate if ((vp->statf == stat) && (lstat(comp, &statb) >= 0) && 2540Sstevel@tonic-gate ((statb.st_mode & S_IFMT) == S_IFLNK)) { 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate /* 2570Sstevel@tonic-gate * Ignore bad symbolic link, let "fn" 2580Sstevel@tonic-gate * report it. 2590Sstevel@tonic-gate */ 2600Sstevel@tonic-gate 2610Sstevel@tonic-gate errno = ENOENT; 2620Sstevel@tonic-gate type = FTW_SLN; 2630Sstevel@tonic-gate } else { 2640Sstevel@tonic-gate type = FTW_NS; 2650Sstevel@tonic-gate fail: 2660Sstevel@tonic-gate /* 2670Sstevel@tonic-gate * if FTW_ANYERR is set in flags, we call 2680Sstevel@tonic-gate * the user function with FTW_NS set, regardless 2690Sstevel@tonic-gate * of the reason stat failed. 2700Sstevel@tonic-gate */ 2710Sstevel@tonic-gate if (!(vp->curflags & FTW_ANYERR)) 2720Sstevel@tonic-gate if (errno != EACCES) 2730Sstevel@tonic-gate return (-1); 2740Sstevel@tonic-gate } 2750Sstevel@tonic-gate } 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate /* 2780Sstevel@tonic-gate * If the walk is not supposed to cross a mount point, 2790Sstevel@tonic-gate * and it did, get ready to return. 2800Sstevel@tonic-gate */ 2810Sstevel@tonic-gate if ((vp->curflags & FTW_MOUNT) && type != FTW_NS && 2820Sstevel@tonic-gate statb.st_dev != vp->cur_mount) 2830Sstevel@tonic-gate goto quit; 2840Sstevel@tonic-gate vp->state.quit = 0; 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate /* 2870Sstevel@tonic-gate * If current component is not a directory, call user 2880Sstevel@tonic-gate * specified function and get ready to return. 2890Sstevel@tonic-gate */ 2900Sstevel@tonic-gate if (type != FTW_D || (vp->curflags & FTW_DEPTH) == 0) 2910Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 2920Sstevel@tonic-gate if (rc > 0) 2930Sstevel@tonic-gate val = rc; 2940Sstevel@tonic-gate skip = (vp->state.quit & FTW_SKD); 2950Sstevel@tonic-gate if (rc != 0 || type != FTW_D || (vp->state.quit & FTW_PRUNE)) 2960Sstevel@tonic-gate goto quit; 2970Sstevel@tonic-gate 2980Sstevel@tonic-gate if (vp->tmppath[0] != '\0' && component[-1] != '/') 2990Sstevel@tonic-gate *component++ = '/'; 3000Sstevel@tonic-gate if (vp->curflags & FTW_CHDIR) { 3010Sstevel@tonic-gate struct stat statb2; 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate *component = 0; 3040Sstevel@tonic-gate /* 3050Sstevel@tonic-gate * Security check (there is a window between 3060Sstevel@tonic-gate * (*vp->statf)() and opendir() above). 3070Sstevel@tonic-gate */ 3080Sstevel@tonic-gate if ((vp->curflags & FTW_PHYS) && 3090Sstevel@tonic-gate (fstat(this.fd->dd_fd, &statb2) < 0 || 3100Sstevel@tonic-gate statb2.st_ino != statb.st_ino || 3110Sstevel@tonic-gate statb2.st_dev != statb.st_dev)) { 3120Sstevel@tonic-gate errno = EAGAIN; 3130Sstevel@tonic-gate rc = -1; 3140Sstevel@tonic-gate goto quit; 3150Sstevel@tonic-gate } 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate if ((cdval = fchdir(this.fd->dd_fd)) >= 0) { 3180Sstevel@tonic-gate this.comp = component; 3190Sstevel@tonic-gate } else { 3200Sstevel@tonic-gate type = FTW_DNR; 3210Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 3220Sstevel@tonic-gate goto quit; 3230Sstevel@tonic-gate } 3240Sstevel@tonic-gate } 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate /* 3270Sstevel@tonic-gate * If the walk has followed a symbolic link, traverse 3280Sstevel@tonic-gate * the walk back to make sure there is not a loop. 3290Sstevel@tonic-gate * 3300Sstevel@tonic-gate * XXX - may need to look at this 3310Sstevel@tonic-gate * There's code to check for cycles, but only for FTW_PHYS 3320Sstevel@tonic-gate * (find -L flag). However, all directories should be 3330Sstevel@tonic-gate * checked, even if not following links because of hardlinks 3340Sstevel@tonic-gate * to directories (not recommended, but can exist). 3350Sstevel@tonic-gate * 3360Sstevel@tonic-gate * We might have added AVL tree routines here to store and search 3370Sstevel@tonic-gate * the inodes and devices, as is done for du/ls/chgrp/chown, 3380Sstevel@tonic-gate * but libcmdutils is for for internal use only, so we can't 3390Sstevel@tonic-gate * add it to a public libc function (nftw()). 3400Sstevel@tonic-gate */ 3410Sstevel@tonic-gate if ((vp->curflags & FTW_PHYS) == 0) { 3420Sstevel@tonic-gate struct Save *sp = last; 3430Sstevel@tonic-gate while (sp) { 3440Sstevel@tonic-gate /* 3450Sstevel@tonic-gate * If the same node has already been visited, there 3460Sstevel@tonic-gate * is a loop. Get ready to return. 3470Sstevel@tonic-gate */ 3480Sstevel@tonic-gate if (sp->dev == statb.st_dev && 3490Sstevel@tonic-gate sp->inode == statb.st_ino) 3500Sstevel@tonic-gate goto quit; 3510Sstevel@tonic-gate sp = sp->last; 3520Sstevel@tonic-gate } 3530Sstevel@tonic-gate } 3540Sstevel@tonic-gate this.dev = statb.st_dev; 3550Sstevel@tonic-gate this.inode = statb.st_ino; 3560Sstevel@tonic-gate oldbase = vp->state.base; 3570Sstevel@tonic-gate vp->state.base = (int)(component - vp->tmppath); 3580Sstevel@tonic-gate while (dir = readdir(this.fd)) { 3590Sstevel@tonic-gate if (dir->d_ino == 0) 3600Sstevel@tonic-gate continue; 3610Sstevel@tonic-gate q = dir->d_name; 3620Sstevel@tonic-gate if (*q == '.') { 3630Sstevel@tonic-gate if (q[1] == 0) 3640Sstevel@tonic-gate continue; 3650Sstevel@tonic-gate else if (q[1] == '.' && q[2] == 0) 3660Sstevel@tonic-gate continue; 3670Sstevel@tonic-gate } 3680Sstevel@tonic-gate p = component; 3690Sstevel@tonic-gate while (p < &vp->tmppath[PATH_MAX] && *q != '\0') 3700Sstevel@tonic-gate *p++ = *q++; 3710Sstevel@tonic-gate *p = '\0'; 3720Sstevel@tonic-gate vp->state.level++; 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate /* Call walk() recursively. */ 3750Sstevel@tonic-gate rc = walk(p, fn, depth-1, &this, vp); 3760Sstevel@tonic-gate vp->state.level--; 3770Sstevel@tonic-gate if (this.fd == 0) { 3780Sstevel@tonic-gate *component = 0; 3790Sstevel@tonic-gate if (vp->curflags & FTW_CHDIR) { 3800Sstevel@tonic-gate this.fd = opendir("."); 3810Sstevel@tonic-gate } else { 3820Sstevel@tonic-gate this.fd = opendir(comp); 3830Sstevel@tonic-gate } 3840Sstevel@tonic-gate if (this.fd == 0) { 3850Sstevel@tonic-gate rc = -1; 3860Sstevel@tonic-gate goto quit; 3870Sstevel@tonic-gate } 3880Sstevel@tonic-gate seekdir(this.fd, this.here); 3890Sstevel@tonic-gate } 3900Sstevel@tonic-gate if (rc != 0) { 3910Sstevel@tonic-gate if (errno == ENOENT) { 3920Sstevel@tonic-gate (void) fprintf(stderr, "cannot open %s: %s\n", 3930Sstevel@tonic-gate vp->tmppath, strerror(errno)); 3940Sstevel@tonic-gate val = rc; 3950Sstevel@tonic-gate continue; 3960Sstevel@tonic-gate } 3970Sstevel@tonic-gate goto quit; /* this seems extreme */ 3980Sstevel@tonic-gate } 3990Sstevel@tonic-gate } 4000Sstevel@tonic-gate vp->state.base = oldbase; 4010Sstevel@tonic-gate *--component = 0; 4020Sstevel@tonic-gate type = FTW_DP; 4030Sstevel@tonic-gate if ((vp->tmppath[0] != '\0') && (vp->curflags & FTW_DEPTH) && !skip) 4040Sstevel@tonic-gate rc = (*fn)(vp->tmppath, &statb, type, &vp->state); 4050Sstevel@tonic-gate quit: 4060Sstevel@tonic-gate if (cdval >= 0 && last) { 4070Sstevel@tonic-gate /* try to change back to previous directory */ 4080Sstevel@tonic-gate if (last->fd != NULL) { 4090Sstevel@tonic-gate if (fchdir(last->fd->dd_fd) < 0) { 4100Sstevel@tonic-gate rc = -1; 4110Sstevel@tonic-gate } 4120Sstevel@tonic-gate } else { 4130Sstevel@tonic-gate if ((cdval = chdir("..")) >= 0) { 4140Sstevel@tonic-gate if ((*vp->statf)(".", &statb) < 0 || 4150Sstevel@tonic-gate statb.st_ino != last->inode || 4160Sstevel@tonic-gate statb.st_dev != last->dev) 4170Sstevel@tonic-gate cdval = -1; 4180Sstevel@tonic-gate } 4190Sstevel@tonic-gate *comp = 0; 4200Sstevel@tonic-gate if (cdval < 0) { 4210Sstevel@tonic-gate if (chdir(vp->fullpath) < 0) { 4220Sstevel@tonic-gate rc = -1; 4230Sstevel@tonic-gate } else { 4240Sstevel@tonic-gate /* Security check */ 4250Sstevel@tonic-gate if ((vp->curflags & FTW_PHYS) && 4260Sstevel@tonic-gate ((*vp->statf)(".", &statb) < 0 || 4270Sstevel@tonic-gate statb.st_ino != last->inode || 4280Sstevel@tonic-gate statb.st_dev != last->dev)) { 4290Sstevel@tonic-gate errno = EAGAIN; 4300Sstevel@tonic-gate rc = -1; 4310Sstevel@tonic-gate } 4320Sstevel@tonic-gate } 4330Sstevel@tonic-gate } 4340Sstevel@tonic-gate } 4350Sstevel@tonic-gate } 4360Sstevel@tonic-gate if (this.fd) 4370Sstevel@tonic-gate (void) closedir(this.fd); 4380Sstevel@tonic-gate if (val > rc) 4390Sstevel@tonic-gate return (val); 4400Sstevel@tonic-gate else 4410Sstevel@tonic-gate return (rc); 4420Sstevel@tonic-gate } 4430Sstevel@tonic-gate 4440Sstevel@tonic-gate int 4450Sstevel@tonic-gate _nftw(const char *path, 4460Sstevel@tonic-gate int (*fn)(const char *, const struct stat *, int, struct FTW *), 4470Sstevel@tonic-gate int depth, int flags) 4480Sstevel@tonic-gate { 4490Sstevel@tonic-gate struct Var var; 4500Sstevel@tonic-gate struct stat statb; 4510Sstevel@tonic-gate char home[2*(PATH_MAX+1)]; 4520Sstevel@tonic-gate int rc = -1; 4530Sstevel@tonic-gate char *dp; 4540Sstevel@tonic-gate char *base; 4550Sstevel@tonic-gate char *endhome; 4560Sstevel@tonic-gate const char *savepath = path; 4570Sstevel@tonic-gate int save_errno; 4580Sstevel@tonic-gate 4590Sstevel@tonic-gate home[0] = 0; 4600Sstevel@tonic-gate 4610Sstevel@tonic-gate /* 4620Sstevel@tonic-gate * If the walk is going to change directory before 4630Sstevel@tonic-gate * reading it, save current woring directory. 4640Sstevel@tonic-gate */ 4650Sstevel@tonic-gate if (flags & FTW_CHDIR) { 4660Sstevel@tonic-gate if (getcwd(home, PATH_MAX+1) == 0) 4670Sstevel@tonic-gate return (-1); 4680Sstevel@tonic-gate } 4690Sstevel@tonic-gate endhome = dp = home + strlen(home); 4700Sstevel@tonic-gate if (*path == '/') 4710Sstevel@tonic-gate var.fullpath = dp; 4720Sstevel@tonic-gate else { 4730Sstevel@tonic-gate *dp++ = '/'; 4740Sstevel@tonic-gate var.fullpath = home; 4750Sstevel@tonic-gate } 4760Sstevel@tonic-gate var.tmppath = dp; 4770Sstevel@tonic-gate base = dp-1; 4780Sstevel@tonic-gate while (*path && dp < &var.tmppath[PATH_MAX]) { 4790Sstevel@tonic-gate *dp = *path; 4800Sstevel@tonic-gate if (*dp == '/') 4810Sstevel@tonic-gate base = dp; 4820Sstevel@tonic-gate dp++, path++; 4830Sstevel@tonic-gate } 4840Sstevel@tonic-gate *dp = 0; 4850Sstevel@tonic-gate var.state.base = (int)(base + 1 - var.tmppath); 4860Sstevel@tonic-gate if (*path) { 4870Sstevel@tonic-gate errno = ENAMETOOLONG; 4880Sstevel@tonic-gate return (-1); 4890Sstevel@tonic-gate } 4900Sstevel@tonic-gate var.curflags = flags; 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate /* 4930Sstevel@tonic-gate * If doing a physical walk (not following symbolic link), set 4940Sstevel@tonic-gate * var.statf to lstat(). Otherwise, set var.statf to stat(). 4950Sstevel@tonic-gate */ 4960Sstevel@tonic-gate if ((flags & FTW_PHYS) == 0) 4970Sstevel@tonic-gate var.statf = stat; 4980Sstevel@tonic-gate else 4990Sstevel@tonic-gate var.statf = lstat; 5000Sstevel@tonic-gate 5010Sstevel@tonic-gate /* 5020Sstevel@tonic-gate * If walk is not going to cross a mount point, 5030Sstevel@tonic-gate * save the current mount point. 5040Sstevel@tonic-gate */ 5050Sstevel@tonic-gate if (flags & FTW_MOUNT) { 5060Sstevel@tonic-gate if ((*var.statf)(savepath, &statb) >= 0) 5070Sstevel@tonic-gate var.cur_mount = statb.st_dev; 5080Sstevel@tonic-gate else 5090Sstevel@tonic-gate goto done; 5100Sstevel@tonic-gate } 5110Sstevel@tonic-gate var.state.level = 0; 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate /* 5140Sstevel@tonic-gate * Call walk() which does most of the work. 5150Sstevel@tonic-gate * walk() uses errno in a rather obtuse way 5160Sstevel@tonic-gate * so we shield any incoming errno. 5170Sstevel@tonic-gate */ 5180Sstevel@tonic-gate save_errno = errno; 5190Sstevel@tonic-gate errno = 0; 5200Sstevel@tonic-gate var.savedstatf = NULL; 5210Sstevel@tonic-gate var.walklevel = 0; 5220Sstevel@tonic-gate rc = walk(dp, fn, depth, (struct Save *)0, &var); 5230Sstevel@tonic-gate if (errno == 0) 5240Sstevel@tonic-gate errno = save_errno; 5250Sstevel@tonic-gate done: 5260Sstevel@tonic-gate *endhome = 0; 5270Sstevel@tonic-gate if (flags & FTW_CHDIR) 5280Sstevel@tonic-gate (void) chdir(home); 5290Sstevel@tonic-gate return (rc); 5300Sstevel@tonic-gate } 5310Sstevel@tonic-gate 5320Sstevel@tonic-gate /* 5330Sstevel@tonic-gate * close the oldest directory. It saves the seek offset. 5340Sstevel@tonic-gate * return value is 0 unless it was unable to close any descriptor 5350Sstevel@tonic-gate */ 5360Sstevel@tonic-gate 5370Sstevel@tonic-gate static int 5380Sstevel@tonic-gate oldclose(struct Save *sp) 5390Sstevel@tonic-gate { 5400Sstevel@tonic-gate struct Save *spnext; 5410Sstevel@tonic-gate while (sp) { 5420Sstevel@tonic-gate spnext = sp->last; 5430Sstevel@tonic-gate if (spnext == 0 || spnext->fd == 0) 5440Sstevel@tonic-gate break; 5450Sstevel@tonic-gate sp = spnext; 5460Sstevel@tonic-gate } 5470Sstevel@tonic-gate if (sp == 0 || sp->fd == 0) 5480Sstevel@tonic-gate return (0); 5490Sstevel@tonic-gate sp->here = telldir(sp->fd); 5500Sstevel@tonic-gate (void) closedir(sp->fd); 5510Sstevel@tonic-gate sp->fd = 0; 5520Sstevel@tonic-gate return (1); 5530Sstevel@tonic-gate } 554