|
|
1.1 root 1: /*
2: * ftw - file tree walk
3: *
4: * int ftw (path, fn, depth) char *path; int (*fn)(); int depth;
5: *
6: * Given a path name, ftw starts from the file given by that path
7: * name and visits each file and directory in the tree beneath
8: * that file. If a single file has multiple links within the
9: * structure, it will be visited once for each such link.
10: * For each object visited, fn is called with three arguments.
11: * The first contains the path name of the object, the second
12: * contains a pointer to a stat buffer which will usually hold
13: * appropriate information for the object and the third will contain
14: * an integer value giving additional information about the
15: * object, as follows:
16: *
17: * FTW_F The object is a file for which stat was
18: * successful. It does not guarantee that the
19: * file can actually be read.
20: *
21: * FTW_D The object is a directory for which stat and
22: * open for read were both successful. This is
23: * a preorder visit -- objects in the directory
24: * are yet to be visited.
25: *
26: * FTW_DNR The object is a directory for which stat
27: * succeeded, but which cannot be read. Because
28: * the directory cannot be read, fn will not be
29: * called for any descendants of this directory.
30: *
31: * FTW_DP The object is a directory for which stat and
32: * open for read were both successful. This is
33: * a postorder visit -- everything in the directory
34: * has already been visited.
35: *
36: * FTW_NS Stat failed on the object because of lack of
37: * appropriate permission. This indication will
38: * be given, for example, for each file in a directory
39: * with read but no execute permission. Because
40: * stat failed, it is not possible to determine
41: * whether this object is a file or a directory.
42: * the stat buffer passed to fn will contain garbage.
43: * Stat failure for any reason other than lack of
44: * permission will be considered an error and will
45: * cause ftw to stop and return -1 to its caller.
46: *
47: * If fn returns nonzero, ftw stops and returns the same value
48: * to its caller. If ftw gets into other trouble along the way,
49: * it returns -1 and leaves an indication of the cause in errno.
50: *
51: * The third argument to ftw does not limit the depth to which
52: * ftw will go. Rather, it limits the depth to which ftw will
53: * go before it starts recycling file descriptors. In general,
54: * it is necessary to use a file descriptor for each level of the
55: * tree, but they can be recycled for deep trees by saving the position,
56: * closing, re-opening, and seeking. It is possible to start
57: * recycling file descriptors by sensing when we have run out, but
58: * in general this will not be terribly useful if fn expects to be
59: * able to open files. We could also figure out how many file descriptors
60: * are available and guarantee a certain number to fn, but we would not
61: * know how many to guarantee, and we do not want to impose the extra
62: * overhead on a caller who knows how many are available without
63: * having to figure it out.
64: *
65: * It is possible for ftw to die with a memory fault in the event
66: * of a file system so deeply nested that the stack overflows.
67: */
68:
69: #include <sys/types.h>
70: #include <sys/stat.h>
71: #include <sys/dir.h>
72: #include <errno.h>
73: #include "ftw.h"
74:
75: #define NULL 0
76:
77: char *malloc(), *strcpy();
78: long lseek();
79: extern int errno;
80:
81: int
82: ftw (path, fn, depth)
83: char *path;
84: int (*fn)();
85: int depth;
86: {
87: int rc, rl, n, fd;
88: char *subpath, *component;
89: struct stat sb;
90: struct direct dir;
91:
92: /* Try to get file status. If unsuccessful, errno will say why. */
93: if (stat (path, &sb) < 0)
94: return errno == EACCES? (*fn) (path, &sb, FTW_NS): -1;
95:
96: /*
97: * The stat succeeded, so we know the object exists.
98: * If not a directory, call the user function and return.
99: */
100: if ((sb.st_mode & S_IFMT) != S_IFDIR)
101: return (*fn) (path, &sb, FTW_F);
102:
103: /*
104: * The object was a directory.
105: *
106: * Open a file to read the directory
107: */
108: fd = open (path, 0);
109:
110: /*
111: * Call the user function, telling it whether
112: * the directory can be read. If it can't be read
113: * call the user function or indicate an error,
114: * depending on the reason it couldn't be read.
115: */
116: if (fd < 0)
117: return errno == EACCES? (*fn) (path, &sb, FTW_DNR): -1;
118:
119: /* We could read the directory. Call user function. */
120: rc = (*fn) (path, &sb, FTW_D);
121: if (rc != 0)
122: return rc;
123:
124: /* Allocate a buffer to hold generated pathnames. */
125: n = strlen (path);
126: subpath = malloc ((unsigned) (n + DIRSIZ + 2));
127: if (subpath == NULL) {
128: (void) close (fd);
129: errno = ENOMEM;
130: return -1;
131: }
132:
133: /* Create a prefix to which we will append component names */
134: (void) strcpy (subpath, path);
135: if (subpath[0] != '\0' && subpath[n - 1] != '/')
136: subpath[n++] = '/';
137: component = &subpath[n];
138:
139: /*
140: * Read the directory one component at a time.
141: * We must ignore "." and "..", but other than that,
142: * just create a path name and call self to check it out.
143: */
144: while ((rl = iread (fd, (char *) &dir, sizeof(struct direct)))
145: == sizeof(struct direct)) {
146: if (dir.d_ino != 0
147: && strcmp (dir.d_name, ".") != 0
148: && strcmp (dir.d_name, "..") != 0) {
149: int i;
150: char *p, *q;
151: long here;
152:
153: /* Append the component name to the working path */
154: p = component;
155: q = dir.d_name;
156: for (i = 0; i < DIRSIZ && *q != '\0'; i++)
157: *p++ = *q++;
158: *p = '\0';
159:
160: /*
161: * If we are about to exceed our depth,
162: * remember where we are and close the file.
163: */
164: if (depth <= 1) {
165: here = lseek (fd, 0L, 1);
166: if (close (fd) < 0) {
167: free (subpath);
168: return -1;
169: }
170: }
171:
172: /*
173: * Do a recursive call to process the file.
174: * (watch this, sports fans)
175: */
176: rc = ftw (subpath, fn, depth - 1);
177: if (rc != 0) {
178: free (subpath);
179: if (depth > 1)
180: (void) close (fd);
181: return rc;
182: }
183:
184: /*
185: * If we closed the file, try to reopen it.
186: */
187: if (depth <= 1) {
188: fd = open (path, 0);
189: if (fd < 0) {
190: free (subpath);
191: return -1;
192: }
193: if (lseek (fd, here, 0) < 0) {
194: (void) close (fd);
195: free (subpath);
196: return -1;
197: }
198: }
199: }
200: }
201:
202: /*
203: * We got out of the subdirectory loop. The return from the
204: * final iread is in rl. Call the user function again at the
205: * end, clean up, and then check that the final
206: * iread was successful. If not, give an error return.
207: */
208: free (subpath);
209:
210: rc = (*fn) (path, &sb, FTW_DP);
211: if (rc != 0)
212: return rc;
213:
214: if (close (fd) < 0 || rl != 0)
215: return -1;
216: return 0;
217: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.