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