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