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