1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 1989 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate /* Copyright (c) 1984 AT&T */ 28*7c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 29*7c478bd9Sstevel@tonic-gate 30*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" /* from S5R2 1.2 */ 31*7c478bd9Sstevel@tonic-gate 32*7c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/ 33*7c478bd9Sstevel@tonic-gate /*************************************************************** 34*7c478bd9Sstevel@tonic-gate * ftw - file tree walk 35*7c478bd9Sstevel@tonic-gate * 36*7c478bd9Sstevel@tonic-gate * int ftw (path, fn, depth) char *path; int (*fn)(); int depth; 37*7c478bd9Sstevel@tonic-gate * 38*7c478bd9Sstevel@tonic-gate * Given a path name, ftw starts from the file given by that path 39*7c478bd9Sstevel@tonic-gate * name and visits each file and directory in the tree beneath 40*7c478bd9Sstevel@tonic-gate * that file. If a single file has multiple links within the 41*7c478bd9Sstevel@tonic-gate * structure, it will be visited once for each such link. 42*7c478bd9Sstevel@tonic-gate * For each object visited, fn is called with three arguments. 43*7c478bd9Sstevel@tonic-gate * The first contains the path name of the object, the second 44*7c478bd9Sstevel@tonic-gate * contains a pointer to a stat buffer which will usually hold 45*7c478bd9Sstevel@tonic-gate * appropriate information for the object and the third will 46*7c478bd9Sstevel@tonic-gate * contain an integer value giving additional information about 47*7c478bd9Sstevel@tonic-gate * 48*7c478bd9Sstevel@tonic-gate * FTW_F The object is a file for which stat was 49*7c478bd9Sstevel@tonic-gate * successful. It does not guarantee that the 50*7c478bd9Sstevel@tonic-gate * file can actually be read. 51*7c478bd9Sstevel@tonic-gate * 52*7c478bd9Sstevel@tonic-gate * FTW_D The object is a directory for which stat and 53*7c478bd9Sstevel@tonic-gate * open for read were both successful. 54*7c478bd9Sstevel@tonic-gate * 55*7c478bd9Sstevel@tonic-gate * FTW_DNR The object is a directory for which stat 56*7c478bd9Sstevel@tonic-gate * succeeded, but which cannot be read. Because 57*7c478bd9Sstevel@tonic-gate * the directory cannot be read, fn will not be 58*7c478bd9Sstevel@tonic-gate * called for any descendants of this directory. 59*7c478bd9Sstevel@tonic-gate * 60*7c478bd9Sstevel@tonic-gate * FTW_NS Stat failed on the object because of lack of 61*7c478bd9Sstevel@tonic-gate * appropriate permission, or because the object is a 62*7c478bd9Sstevel@tonic-gate * symbolic link that points to a non-existent file. 63*7c478bd9Sstevel@tonic-gate * This indication will be given, for example, for each 64*7c478bd9Sstevel@tonic-gate * file in a directory with read but no execute 65*7c478bd9Sstevel@tonic-gate * permission. Because stat failed, it is not 66*7c478bd9Sstevel@tonic-gate * possible to determine whether this object is a file 67*7c478bd9Sstevel@tonic-gate * or a directory. The stat buffer passed to fn will 68*7c478bd9Sstevel@tonic-gate * contain garbage. Stat failure for any reason 69*7c478bd9Sstevel@tonic-gate * other than lack of permission will be 70*7c478bd9Sstevel@tonic-gate * considered an error and will cause ftw to stop 71*7c478bd9Sstevel@tonic-gate * and return -1 to its caller. 72*7c478bd9Sstevel@tonic-gate * 73*7c478bd9Sstevel@tonic-gate * If fn returns nonzero, ftw stops and returns the same value 74*7c478bd9Sstevel@tonic-gate * to its caller. If ftw gets into other trouble along the way, 75*7c478bd9Sstevel@tonic-gate * it returns -1 and leaves an indication of the cause in errno. 76*7c478bd9Sstevel@tonic-gate * 77*7c478bd9Sstevel@tonic-gate * The third argument to ftw does not limit the depth to which 78*7c478bd9Sstevel@tonic-gate * ftw will go. Rather, it limits the depth to which ftw will 79*7c478bd9Sstevel@tonic-gate * go before it starts recycling file descriptors. In general, 80*7c478bd9Sstevel@tonic-gate * it is necessary to use a file descriptor for each level of the 81*7c478bd9Sstevel@tonic-gate * tree, but they can be recycled for deep trees by saving the 82*7c478bd9Sstevel@tonic-gate * position, closing, re-opening, and seeking. It is possible 83*7c478bd9Sstevel@tonic-gate * to start recycling file descriptors by sensing when we have 84*7c478bd9Sstevel@tonic-gate * run out, but in general this will not be terribly useful if 85*7c478bd9Sstevel@tonic-gate * fn expects to be able to open files. We could also figure out 86*7c478bd9Sstevel@tonic-gate * how many file descriptors are available and guarantee a certain 87*7c478bd9Sstevel@tonic-gate * number to fn, but we would not know how many to guarantee, 88*7c478bd9Sstevel@tonic-gate * and we do not want to impose the extra overhead on a caller who 89*7c478bd9Sstevel@tonic-gate * knows how many are available without having to figure it out. 90*7c478bd9Sstevel@tonic-gate * 91*7c478bd9Sstevel@tonic-gate * It is possible for ftw to die with a memory fault in the event 92*7c478bd9Sstevel@tonic-gate * of a file system so deeply nested that the stack overflows. 93*7c478bd9Sstevel@tonic-gate **************************************************************/ 94*7c478bd9Sstevel@tonic-gate 95*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 96*7c478bd9Sstevel@tonic-gate #include <sys/stat.h> 97*7c478bd9Sstevel@tonic-gate #include <sys/dir.h> 98*7c478bd9Sstevel@tonic-gate #include <errno.h> 99*7c478bd9Sstevel@tonic-gate #include <ftw.h> 100*7c478bd9Sstevel@tonic-gate 101*7c478bd9Sstevel@tonic-gate #define NULL 0 102*7c478bd9Sstevel@tonic-gate 103*7c478bd9Sstevel@tonic-gate extern char *malloc(), *strcpy(); 104*7c478bd9Sstevel@tonic-gate extern void free(); 105*7c478bd9Sstevel@tonic-gate extern int errno; 106*7c478bd9Sstevel@tonic-gate 107*7c478bd9Sstevel@tonic-gate int 108*7c478bd9Sstevel@tonic-gate ftw(path, fn, depth) 109*7c478bd9Sstevel@tonic-gate char *path; 110*7c478bd9Sstevel@tonic-gate int (*fn)(); 111*7c478bd9Sstevel@tonic-gate int depth; 112*7c478bd9Sstevel@tonic-gate { 113*7c478bd9Sstevel@tonic-gate int rc, n; 114*7c478bd9Sstevel@tonic-gate DIR *dirp; 115*7c478bd9Sstevel@tonic-gate char *subpath, *component; 116*7c478bd9Sstevel@tonic-gate struct stat sb; 117*7c478bd9Sstevel@tonic-gate struct direct *dp; 118*7c478bd9Sstevel@tonic-gate 119*7c478bd9Sstevel@tonic-gate /* Try to get file status. 120*7c478bd9Sstevel@tonic-gate If unsuccessful, errno will say why. */ 121*7c478bd9Sstevel@tonic-gate if(stat(path, &sb) < 0) { 122*7c478bd9Sstevel@tonic-gate if (errno == EACCES) { 123*7c478bd9Sstevel@tonic-gate return((*fn)(path, &sb, FTW_NS)); 124*7c478bd9Sstevel@tonic-gate } else if (errno == ENOENT) { 125*7c478bd9Sstevel@tonic-gate /* Check if symbolic link points to non-existent file */ 126*7c478bd9Sstevel@tonic-gate if (lstat(path, &sb) < 0) { 127*7c478bd9Sstevel@tonic-gate return(-1); 128*7c478bd9Sstevel@tonic-gate } 129*7c478bd9Sstevel@tonic-gate else if ((sb.st_mode & S_IFMT) == S_IFLNK) { 130*7c478bd9Sstevel@tonic-gate errno = ENOENT; 131*7c478bd9Sstevel@tonic-gate return((*fn)(path, &sb, FTW_NS)); 132*7c478bd9Sstevel@tonic-gate } 133*7c478bd9Sstevel@tonic-gate else { 134*7c478bd9Sstevel@tonic-gate return(-1); 135*7c478bd9Sstevel@tonic-gate } 136*7c478bd9Sstevel@tonic-gate } else { 137*7c478bd9Sstevel@tonic-gate return(-1); 138*7c478bd9Sstevel@tonic-gate } 139*7c478bd9Sstevel@tonic-gate } 140*7c478bd9Sstevel@tonic-gate 141*7c478bd9Sstevel@tonic-gate /* 142*7c478bd9Sstevel@tonic-gate * The stat succeeded, so we know the object exists. 143*7c478bd9Sstevel@tonic-gate * If not a directory, call the user function and return. 144*7c478bd9Sstevel@tonic-gate */ 145*7c478bd9Sstevel@tonic-gate if((sb.st_mode & S_IFMT) != S_IFDIR) 146*7c478bd9Sstevel@tonic-gate return((*fn)(path, &sb, FTW_F)); 147*7c478bd9Sstevel@tonic-gate 148*7c478bd9Sstevel@tonic-gate /* 149*7c478bd9Sstevel@tonic-gate * The object was a directory. 150*7c478bd9Sstevel@tonic-gate * 151*7c478bd9Sstevel@tonic-gate * Open a file to read the directory 152*7c478bd9Sstevel@tonic-gate */ 153*7c478bd9Sstevel@tonic-gate dirp = opendir(path); 154*7c478bd9Sstevel@tonic-gate 155*7c478bd9Sstevel@tonic-gate /* 156*7c478bd9Sstevel@tonic-gate * Call the user function, telling it whether 157*7c478bd9Sstevel@tonic-gate * the directory can be read. If it can't be read 158*7c478bd9Sstevel@tonic-gate * call the user function or indicate an error, 159*7c478bd9Sstevel@tonic-gate * depending on the reason it couldn't be read. 160*7c478bd9Sstevel@tonic-gate */ 161*7c478bd9Sstevel@tonic-gate if(dirp == NULL) 162*7c478bd9Sstevel@tonic-gate return(errno == EACCES? (*fn)(path, &sb, FTW_DNR): -1); 163*7c478bd9Sstevel@tonic-gate 164*7c478bd9Sstevel@tonic-gate /* We could read the directory. Call user function. */ 165*7c478bd9Sstevel@tonic-gate rc = (*fn)(path, &sb, FTW_D); 166*7c478bd9Sstevel@tonic-gate if(rc != 0) 167*7c478bd9Sstevel@tonic-gate return(rc); 168*7c478bd9Sstevel@tonic-gate 169*7c478bd9Sstevel@tonic-gate /* Allocate a buffer to hold generated pathnames. */ 170*7c478bd9Sstevel@tonic-gate n = strlen(path); 171*7c478bd9Sstevel@tonic-gate subpath = malloc((unsigned)(n+MAXNAMLEN+2)); 172*7c478bd9Sstevel@tonic-gate if(subpath == NULL) { 173*7c478bd9Sstevel@tonic-gate closedir(dirp); 174*7c478bd9Sstevel@tonic-gate errno = ENOMEM; 175*7c478bd9Sstevel@tonic-gate return(-1); 176*7c478bd9Sstevel@tonic-gate } 177*7c478bd9Sstevel@tonic-gate 178*7c478bd9Sstevel@tonic-gate /* Create a prefix to which we will append component names */ 179*7c478bd9Sstevel@tonic-gate (void)strcpy(subpath, path); 180*7c478bd9Sstevel@tonic-gate if(subpath[0] != '\0' && subpath[n-1] != '/') 181*7c478bd9Sstevel@tonic-gate subpath[n++] = '/'; 182*7c478bd9Sstevel@tonic-gate component = &subpath[n]; 183*7c478bd9Sstevel@tonic-gate 184*7c478bd9Sstevel@tonic-gate /* 185*7c478bd9Sstevel@tonic-gate * Read the directory one component at a time. 186*7c478bd9Sstevel@tonic-gate * We must ignore "." and "..", but other than that, 187*7c478bd9Sstevel@tonic-gate * just create a path name and call self to check it out. 188*7c478bd9Sstevel@tonic-gate */ 189*7c478bd9Sstevel@tonic-gate while((dp = readdir(dirp)) != NULL) { 190*7c478bd9Sstevel@tonic-gate if(strcmp(dp->d_name, ".") != 0 && 191*7c478bd9Sstevel@tonic-gate strcmp(dp->d_name, "..") != 0) { 192*7c478bd9Sstevel@tonic-gate long here; 193*7c478bd9Sstevel@tonic-gate 194*7c478bd9Sstevel@tonic-gate /* Append component name to the working path */ 195*7c478bd9Sstevel@tonic-gate (void)strcpy(component, dp->d_name); 196*7c478bd9Sstevel@tonic-gate 197*7c478bd9Sstevel@tonic-gate /* 198*7c478bd9Sstevel@tonic-gate * If we are about to exceed our depth, 199*7c478bd9Sstevel@tonic-gate * remember where we are and close a file. 200*7c478bd9Sstevel@tonic-gate */ 201*7c478bd9Sstevel@tonic-gate if(depth <= 1) { 202*7c478bd9Sstevel@tonic-gate here = telldir(dirp); 203*7c478bd9Sstevel@tonic-gate closedir(dirp); 204*7c478bd9Sstevel@tonic-gate } 205*7c478bd9Sstevel@tonic-gate 206*7c478bd9Sstevel@tonic-gate /* 207*7c478bd9Sstevel@tonic-gate * Do a recursive call to process the file. 208*7c478bd9Sstevel@tonic-gate * (watch this, sports fans) 209*7c478bd9Sstevel@tonic-gate */ 210*7c478bd9Sstevel@tonic-gate rc = ftw(subpath, fn, depth-1); 211*7c478bd9Sstevel@tonic-gate if(rc != 0) { 212*7c478bd9Sstevel@tonic-gate free(subpath); 213*7c478bd9Sstevel@tonic-gate if(depth > 1) 214*7c478bd9Sstevel@tonic-gate closedir(dirp); 215*7c478bd9Sstevel@tonic-gate return(rc); 216*7c478bd9Sstevel@tonic-gate } 217*7c478bd9Sstevel@tonic-gate 218*7c478bd9Sstevel@tonic-gate /* 219*7c478bd9Sstevel@tonic-gate * If we closed the file, try to reopen it. 220*7c478bd9Sstevel@tonic-gate */ 221*7c478bd9Sstevel@tonic-gate if(depth <= 1) { 222*7c478bd9Sstevel@tonic-gate dirp = opendir(path); 223*7c478bd9Sstevel@tonic-gate if(dirp == NULL) { 224*7c478bd9Sstevel@tonic-gate free(subpath); 225*7c478bd9Sstevel@tonic-gate return(-1); 226*7c478bd9Sstevel@tonic-gate } 227*7c478bd9Sstevel@tonic-gate seekdir(dirp, here); 228*7c478bd9Sstevel@tonic-gate } 229*7c478bd9Sstevel@tonic-gate } 230*7c478bd9Sstevel@tonic-gate } 231*7c478bd9Sstevel@tonic-gate 232*7c478bd9Sstevel@tonic-gate /* 233*7c478bd9Sstevel@tonic-gate * We got out of the subdirectory loop. The return from 234*7c478bd9Sstevel@tonic-gate * the final readdir is in dp. Clean up. 235*7c478bd9Sstevel@tonic-gate */ 236*7c478bd9Sstevel@tonic-gate free(subpath); 237*7c478bd9Sstevel@tonic-gate closedir(dirp); 238*7c478bd9Sstevel@tonic-gate return(0); 239*7c478bd9Sstevel@tonic-gate } 240