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