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 (c) 1998,2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 /* Copyright (c) 1988 AT&T */ 28 /* All Rights Reserved */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 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: 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. This indication will 62 * be given, for example, for each file in a 63 * directory with read but no execute permission. 64 * Because stat failed, it is not possible to 65 * determine whether this object is a file or a 66 * directory. The stat buffer passed to fn will 67 * contain garbage. Stat failure for any reason 68 * other than lack of permission will be 69 * considered an error and will cause ftw to stop 70 * and return -1 to its caller. 71 * 72 * If fn returns nonzero, ftw stops and returns the same value 73 * to its caller. If ftw gets into other trouble along the way, 74 * it returns -1 and leaves an indication of the cause in errno. 75 * 76 * The third argument to ftw does not limit the depth to which 77 * ftw will go. Rather, it limits the depth to which ftw will 78 * go before it starts recycling file descriptors. In general, 79 * it is necessary to use a file descriptor for each level of the 80 * tree, but they can be recycled for deep trees by saving the 81 * position, closing, re-opening, and seeking. It is possible 82 * to start recycling file descriptors by sensing when we have 83 * run out, but in general this will not be terribly useful if 84 * fn expects to be able to open files. We could also figure out 85 * how many file descriptors are available and guarantee a certain 86 * number to fn, but we would not know how many to guarantee, 87 * and we do not want to impose the extra overhead on a caller who 88 * knows how many are available without having to figure it out. 89 * 90 * It is possible for ftw to die with a memory fault in the event 91 * of a file system so deeply nested that the stack overflows. 92 */ 93 94 #include <sys/fs/ufs_inode.h> 95 #include <sys/types.h> 96 #include <sys/stat.h> 97 #include <dirent.h> 98 #include <errno.h> 99 #include <malloc.h> 100 #include <string.h> 101 #include <fcntl.h> 102 #include <unistd.h> 103 #include <ftw.h> 104 105 #define NULL 0 106 107 static int pwdfd; 108 109 #ifdef __STDC__ 110 static int lf_xftw( 111 const char *, 112 int (*)(const char *, const struct stat64 *, int), 113 int, 114 int (*)(const char *, struct stat64 *)); 115 #else 116 static int lf_xftw(); 117 #endif 118 119 #ifdef __STDC__ 120 lf_lftw( 121 const char *path, 122 int (*fn)(const char *, const struct stat64 *, int), 123 int depth) 124 #else 125 lf_lftw(path, fn, depth) 126 char *path; 127 int (*fn)(); 128 int depth; 129 #endif 130 { 131 int rc; 132 133 if ((pwdfd = open(".", O_RDONLY)) < 0) { 134 return (-1); 135 } else { 136 rc = (lf_xftw(path, fn, depth, lstat64)); 137 (void) close(pwdfd); 138 return (rc); 139 } 140 } 141 142 static int 143 #ifdef __STDC__ 144 lf_xftw( 145 const char *path, 146 int (*fn)(const char *, const struct stat64 *, int), 147 int depth, 148 int (*statfn)(const char *, struct stat64 *)) 149 #else 150 lf_xftw(path, fn, depth, statfn) 151 char *path; 152 int (*fn)(); 153 int depth; 154 int (*statfn)(); 155 #endif 156 { 157 int n; 158 int rc, sublen, saverr, attrfd; 159 DIR *dirp; 160 char *subpath, *component; 161 struct stat64 sb; 162 struct dirent *dp; 163 extern dev_t partial_dev; 164 165 /* 166 * Try to get file status. 167 * If unsuccessful, errno will say why. 168 */ 169 if ((*statfn)(path, &sb) < 0) 170 return (errno == EACCES? (*fn)(path, &sb, FTW_NS): -1); 171 /* 172 * The stat succeeded, so we know the object exists. 173 * Make sure it is not a mount point for another filesystem. 174 * The following check must be made here because: 175 * 176 * + namefs can be mounted on anything, but a directory 177 * + all other filesystems must be mounted on a directory 178 */ 179 if (sb.st_dev != partial_dev) { 180 return (0); 181 } 182 /* 183 * Check for presence of attributes on file 184 */ 185 if (pathconf(path, _PC_XATTR_EXISTS) == 1) { 186 attrfd = attropen64(path, ".", O_RDONLY|O_NONBLOCK); 187 } else { 188 attrfd = -1; 189 } 190 /* 191 * If not a directory, call the user function and return. 192 */ 193 if ((sb.st_mode & S_IFMT) != S_IFDIR && 194 (sb.st_mode & IFMT) != IFATTRDIR) { 195 rc = (*fn)(path, &sb, FTW_F); 196 if (rc == 0 && attrfd != -1) { 197 (void) fchdir(attrfd); 198 rc = lf_xftw(".", fn, depth-1, statfn); 199 (void) fchdir(pwdfd); 200 (void) close(attrfd); 201 } 202 return (rc); 203 } 204 /* 205 * The object was a directory and not a mount point. 206 * 207 * Open a file to read the directory 208 */ 209 dirp = opendir(path); 210 211 /* 212 * Call the user function, telling it whether 213 * the directory can be read. If it can't be read 214 * call the user function or indicate an error, 215 * depending on the reason it couldn't be read. 216 */ 217 if (dirp == NULL) 218 rc = (errno == EACCES? (*fn)(path, &sb, FTW_DNR): -1); 219 else 220 rc = (*fn)(path, &sb, FTW_D); 221 /* 222 * If the directory has attributes, process the 223 * attributes before processing the directory contents. 224 */ 225 if (rc == 0 && attrfd != -1) { 226 (void) fchdir(attrfd); 227 rc = lf_xftw(".", fn, depth-1, statfn); 228 (void) fchdir(pwdfd); 229 (void) close(attrfd); 230 } 231 if (rc != 0 || dirp == NULL) 232 return (rc); 233 234 /* Allocate a buffer to hold generated pathnames. */ 235 /* LINTED: the length will fit into a signed integer */ 236 n = (int)strlen(path); 237 sublen = n + MAXNAMLEN + 1; /* +1 for appended / */ 238 subpath = malloc((unsigned)(sublen+1)); /* +1 for NUL */ 239 if (subpath == NULL) { 240 saverr = errno; 241 (void) closedir(dirp); 242 errno = saverr; 243 return (-1); 244 } 245 246 /* Create a prefix to which we will append component names */ 247 (void) strcpy(subpath, path); 248 if (subpath[0] != '\0' && subpath[n-1] != '/') 249 subpath[n++] = '/'; 250 component = &subpath[n]; 251 /* LINTED: result will fit into a 32-bit int */ 252 sublen -= component - subpath; 253 254 /* 255 * Read the directory one component at a time. 256 * We must ignore "." and "..", but other than that, 257 * just create a path name and call self to check it out. 258 */ 259 while ((dp = readdir(dirp)) != NULL) { 260 if (strcmp(dp->d_name, ".") != 0 && 261 strcmp(dp->d_name, "..") != 0) { 262 long here; 263 264 /* Append component name to the working path */ 265 (void) strncpy(component, dp->d_name, sublen); 266 component[sublen - 1] = '\0'; 267 268 /* 269 * If we are about to exceed our depth, 270 * remember where we are and close a file. 271 */ 272 if (depth <= 1) { 273 here = telldir(dirp); 274 (void) closedir(dirp); 275 } 276 277 /* 278 * Do a recursive call to process the file. 279 * (watch this, sports fans) 280 */ 281 rc = lf_xftw(subpath, fn, depth-1, statfn); 282 if (rc != 0) { 283 free(subpath); 284 if (depth > 1) 285 (void) closedir(dirp); 286 return (rc); 287 } 288 289 /* 290 * If we closed the file, try to reopen it. 291 */ 292 if (depth <= 1) { 293 dirp = opendir(path); 294 if (dirp == NULL) { 295 free(subpath); 296 return (-1); 297 } 298 seekdir(dirp, here); 299 } 300 } 301 } 302 303 /* 304 * We got out of the subdirectory loop. The return from 305 * the final readdir is in dp. Clean up. 306 */ 307 free(subpath); 308 (void) closedir(dirp); 309 return (0); 310 } 311