1 /* traverse.c -- traverse directory structures recursively
2 *
3 * Written by Jay Laefer
4 *
5 * (C) Copyright 1989, by Jay Laefer and Mike Darweesh
6 * All Rights Reserved.
7 * Permission is granted to copy, modify, and use this as long
8 * as this message remains intact. This is a nifty program.
9 * The authors are not responsible for any damage caused by
10 * this program.
11 * Copyright (c) 1991- by Charles Swiger
12 */
13 #include "niftyclean.h"
14 #include <dirent.h>
15 #include <sys/stat.h>
16
17 #ifdef VICE
18 #include <afs/param.h>
19 #include <sys/ioctl.h>
20 #include <afs/venus.h>
21 #include <afs/afsint.h>
22
23 static struct voltype *vollist = NULL; /* List of volumes crossed so far */
24 void free_volume_list(), add_volume();
25 int init_volume_list();
26 #endif
27
28 void traverse(), add_dir(), scan_dirs();
29
30 /* traverse() takes a directory, opens it for reading and lstat's
31 * all the files in the directory. It calls add_dir() on each
32 * subdirectory, and dofile() on each file. It calls scan_dirs()
33 * after closing the directory in preparation for recursing.
34 */
35 void
traverse(char * d,int leaf,int onvice)36 traverse (char *d, int leaf, int onvice)
37 {
38 DIR *dirp;
39 int d_len;
40 int dostat;
41
42 skip = 0;
43 d_len = strlen(d); /* The length of the directory name */
44 if ((d_len > 1) && (d[d_len - 1] == '/'))
45 d[d_len - 1] = '\0';
46
47 if (!(flag & (FORCE | QUIET))) {
48 fputs("Entering: ", stdout);
49 puts(d);
50 }
51
52 dostat = ((flag & TIME) ||
53 (!onvice && !leaf) ||
54 (onvice && (flag & READWRITE)));
55
56 /* open the directory for reading */
57 if ((dirp = opendir(d))) {
58 struct dirtype *head = NULL; /* The head of the subdir list */
59 struct dirent *d_struct;
60
61 while ((d_struct = readdir(dirp)) && (!skip)) {
62 /* Scan through the directory */
63 int f_len;
64 char *file;
65
66 /* quick references */
67 file = d_struct->d_name;
68 f_len = strlen(file);
69
70 /* check for exceeding MAXPATHLEN */
71 if (d_len + f_len >= MAXPATHLEN) {
72 errorh(WARNING, "filename too long");
73 errorh(WARNING, d);
74
75 /* check for "." and ".." */
76 } else if ((f_len > 2) || (file[0] != '.') || ((file[1] != '.') && (file[1] != '\0'))) {
77 /* Prep for lstat() */
78 char f_name[MAXPATHLEN];
79 struct stat stbuf;
80
81 strcpy(f_name, d);
82 strcat(f_name, "/");
83 strcat(f_name, file);
84
85 /* stat the file if we have to */
86 if (dostat && (lstat(f_name, &stbuf) == -1)) continue;
87
88 if ((onvice && (d_struct->d_ino & 1)) ||
89 (dostat && ((stbuf.st_mode & S_IFMT) == S_IFDIR))) {
90 if (!(flag & FLAT))
91 /* it's a dir, add it to the list */
92 add_dir(&head, file, f_len, dostat &&(stbuf.st_nlink<=2));
93
94 } else if (!(flag & TIME) || (minimum_age > stbuf.st_mtime)) {
95 /* Pass it to dofile().
96 We check to see if the TIME flag is set,
97 and, if so, make sure the file is older than
98 then minimum_age */
99 (void)dofile(d, file);
100 }
101 }
102 }
103 closedir(dirp);
104
105 /* finished reading dir, now check out subdirs */
106 if (!(flag & FLAT) && head)
107 scan_dirs(head, d, d_len);
108
109 } else
110 errorh(WARNING, "Couldn't open directory");
111 }
112
113 /* add_dir() takes a double pointer to the head of the list (so it can
114 be altered), the name of the subdirectory, and the length of the
115 subdir. It malloc's a structure and space for the name, then it
116 adds the structure to the front of the list that begins with *headp.
117 */
118 void
add_dir(struct dirtype ** headp,char * file,int len,int leaf)119 add_dir (struct dirtype **headp, char *file, int len, int leaf)
120 {
121 struct dirtype *dirptr;
122
123 if ((dirptr = (struct dirtype *)malloc((sizeof(struct dirtype)) + len)) == NULL)
124 errorh(FATAL, "Malloc failed in add_dir()");
125
126 /* copy subdir info into the structure */
127 strcpy(dirptr->subdir, file);
128 dirptr->len = len;
129 dirptr->leaf = leaf;
130
131 dirptr->next = *headp;
132
133 *headp = dirptr;
134 }
135
136 /* scan_dirs() takes the head of the list of subdirs, the
137 full path leading to them, and the length of that path.
138 In the Vice version, we try to stat each subdir as a
139 mount point. This way, we make sure we don't go through
140 any unwanted volumes, nor do we enter the same volume
141 twice. In the non-Vice version, we just call traverse
142 on each subdir. Either way, we free() the memory as we go.
143 */
144 void
scan_dirs(struct dirtype * head,char * d,int d_len)145 scan_dirs (struct dirtype *head, char *d, int d_len)
146 {
147 int onvice = 0;
148 #ifdef VICE
149 struct ViceIoctl blob1, blob2;
150 char space1[MAXSIZE], space2[MAXSIZE];
151
152 blob1.out_size = MAXSIZE;
153 blob1.out = space1;
154
155 blob2.in_size = 0;
156 blob2.out_size = MAXSIZE;
157 blob2.out = space2;
158 #endif
159
160 while(head) {
161 struct dirtype *temp;
162 char dir[MAXPATHLEN];
163
164 if ((d_len == 1) && (d[0] == '/'))
165 strcpy(dir, "/");
166 else {
167 strcpy(dir, d);
168 strcat(dir, "/");
169 }
170 strcat(dir, head->subdir);
171
172 temp = head;
173
174 #ifdef VICE
175 bzero(space1, MAXSIZE);
176 bzero(space2, MAXSIZE);
177 blob1.in_size = head->len + 1;
178 blob1.in = head->subdir;
179
180 /* The pioctl VIOC_AFS_STAT_MT_PT takes a path (d) and a file
181 (placed in blob.in) and determines if it is a mount point
182 (return value == 0). It returns the name of the mounted
183 volume. Because it is possible to mount % vs. # (named vs.
184 read-only preference), we must use the pioctl VIOCGETVOLSTAT
185 which stats the full directory to get the true name of the
186 mounted volume. This pioctl tries to stat the directory
187 that's been passed in. If it succeeds (return value == 0),
188 we get information about the volume the directory is located
189 in. */
190 if (pioctl(d, VIOC_AFS_STAT_MT_PT, &blob1, 0) == 0) {
191 /* Now we have a mount point. If we're not in the business
192 of traversing any mount points, don't bother to get the
193 name of the volume. */
194 /* if ((flag & (READWRITE | READONLY | BACKUP)) && */
195 if ((flag & READWRITE) &&
196 (pioctl(dir, VIOCGETVOLSTAT, &blob2, 0) == 0)) {
197 struct VolumeStatus *status;
198 /* char *name; */
199 /* int bkp, ro; */
200
201 /* bkp = ro = 0; */
202
203 status = (struct VolumeStatus *)space2;
204
205 /* Get the volume name */
206 /* name = space2 + (sizeof(struct VolumeStatus)); */
207
208 /* Type is 1 for a read/write volume, 0 for backup and read-only */
209 /* if (!status->Type) { */
210 /* char *ext; */
211 /* Get the last part of the volume name */
212 /* ext = strrchr(name, '.'); */
213 /* if (strcmp(++ext, "backup")) */
214 /* ro = 1; */
215 /* else */
216 /* bkp = 1; */
217 /* } */
218
219 /* Check to make sure we want to go through this volume */
220 /* if ((((flag & READWRITE) && status->Type) || */
221 /* ((flag & READONLY) && ro) || */
222 /* ((flag & BACKUP) && bkp)) */
223 if (((flag & READWRITE) && status->Type)
224 && (check_volume_list(status->Vid))) {
225 add_volume(status->Vid);
226 traverse(dir,head->leaf, 1);
227 }
228 }
229 } else
230 #endif
231 if (check_excl_list(head->subdir)) {
232 #ifdef VICE
233 if ((pioctl(dir, VIOCGETVOLSTAT, &blob2, 0) == 0) ||
234 errno != EINVAL)
235 onvice = 1;
236 #endif
237 traverse(dir, head->leaf, onvice);
238 }
239
240 head = head->next;
241 free((char *)temp);
242 }
243 }
244
245 #ifdef VICE /* Vice-specific functions */
246 /* init_volume_list() takes the initial directory the program
247 * starts with and sets up the list of volumes traversed.
248 * If the pioctl() fails with EINVAL, then we're not on vice
249 * and we return a non-zero status.
250 */
251 int
init_volume_list(char * dir)252 init_volume_list (char *dir)
253 {
254 struct ViceIoctl blob;
255 struct VolumeStatus *status;
256 char space[MAXSIZE];
257
258 /* prep for pioctl() */
259 blob.in_size = 0;
260 blob.out_size = MAXSIZE;
261 blob.out = space;
262
263 vollist = NULL; /* Init the global variable */
264
265 /* This pioctl tries to stat the directory thats been passed in.
266 If it succeeds (return value == 0), we get information about
267 the volume the directory is located on. */
268 if (pioctl(dir, VIOCGETVOLSTAT, &blob, 0) == 0) {
269 status = (struct VolumeStatus *)space;
270 add_volume(status->Vid); /* Add the volume to the list */
271 }
272 else if (errno == EINVAL) {
273 return 1;
274 }
275 return 0;
276 }
277
278 /* add_volume() takes a volume id, allocates space for the structure,
279 and adds the structure to the list of visited volumes.
280 */
281 static void
add_volume(long volume)282 add_volume (long volume)
283 {
284 struct voltype *newvol;
285
286 if ((newvol = (struct voltype *)malloc(sizeof(struct voltype))) == NULL)
287 errorh(FATAL, "Malloc() failed in add_volume()");
288
289 newvol->Vid = volume; /* Get the volume ID number */
290 newvol->next = vollist;
291
292 vollist = newvol;
293 }
294
295 /* check_volume_list() takes a volume id and checks to see if it's
296 already been visited. If so, a 0 is returned. Otherwise, return 1.
297 */
298 static
check_volume_list(long volume)299 check_volume_list (long volume)
300 {
301 struct voltype *temp;
302
303 /* Move down the linked list that starts with vollist */
304 for (temp = vollist; temp; temp = temp->next)
305 if (temp->Vid == volume)
306 return(0); /* Found */
307
308 return(1); /* Not found */
309 }
310
311 /* free_volume_list() frees the list of volumes headed by vollist
312 */
313 void
free_volume_list(void)314 free_volume_list (void)
315 {
316 struct voltype *temp;
317
318 while (vollist) {
319 temp = vollist;
320 vollist = vollist->next;
321 free(temp);
322 }
323 }
324 #endif /* End of Vice-specific functions */
325