1 #ident "$Id$"
2 /* ----------------------------------------------------------------------- *
3  *
4  *   Copyright 2001-2006 H. Peter Anvin - All Rights Reserved
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
9  *   USA; either version 2 of the License, or (at your option) any later
10  *   version; incorporated herein by reference.
11  *
12  * ----------------------------------------------------------------------- */
13 
14 /*
15  * walk.c
16  *
17  * Functions to walk the file tree
18  */
19 
20 #include "mkzftree.h"		/* Must be included first! */
21 
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <limits.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <dirent.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 
32 #include "iso9660.h"
33 
munge_file(const char * inpath,const char * outpath,const char * cribpath,struct stat * st)34 static int munge_file(const char *inpath, const char *outpath,
35 		      const char *cribpath, struct stat *st)
36 {
37   FILE *in, *out;
38   int err = 0;
39 
40   if ( cribpath ) {
41     struct stat cst;
42     struct compressed_file_header cfh;
43 
44     /* Compare as much as we realistically can */
45     if ( !stat(cribpath, &cst) &&
46 	 st->st_mode == cst.st_mode &&
47 	 st->st_uid == cst.st_uid &&
48 	 st->st_gid == cst.st_gid &&
49 	 st->st_mtime == cst.st_mtime ) {
50       if ( (in = fopen(cribpath, "rb")) ) {
51 	int e = fread(&cfh, 1, sizeof cfh, in);
52 	fclose(in);
53 	/* Attempt to restore the atime */
54 	copytime(cribpath, &cst);
55 
56 	if ( (e == sizeof cfh &&
57 	      !memcmp(cfh.magic, zisofs_magic, sizeof zisofs_magic) &&
58 	      (off_t)get_731(cfh.uncompressed_len) == st->st_size) ||
59 	     (st->st_size == cst.st_size &&
60 	      (e < (int)(sizeof zisofs_magic) ||
61 	       memcmp(cfh.magic, zisofs_magic, sizeof zisofs_magic))) ) {
62 	  /* File is cribbable.  Steal it. */
63 	  if ( !link(cribpath, outpath) ) {
64 	    message(vl_crib, "crib: %s -> %s\n", cribpath, outpath);
65 	    copytime(outpath, st);	/* Set the the atime */
66 	    return 0;
67 	  }
68 	}
69       }
70     }
71   }
72 
73   in = fopen(inpath, "rb");
74   if ( !in )
75     return EX_NOINPUT;
76   out = fopen(outpath, "wb");
77   if ( !out ) {
78     fclose(in);
79     return EX_CANTCREAT;
80   }
81 
82   if ( spawn_worker() ) {
83     err = opt.munger(in, out, st->st_size);
84     fclose(in);
85     fclose(out);
86 
87     chown(outpath, st->st_uid, st->st_gid);
88     chmod(outpath, st->st_mode);
89     copytime(outpath, st);
90 
91     end_worker(err);
92   } else {
93     fclose(in);
94     fclose(out);
95   }
96 
97   return err;
98 }
99 
munge_tree(const char * intree,const char * outtree,const char * cribtree)100 int munge_tree(const char *intree, const char *outtree, const char *cribtree)
101 {
102   char *in_path, *out_path, *crib_path;
103   char *in_file, *out_file, *crib_file;
104   DIR *thisdir;
105   struct dirent *dirent;
106   struct stat dirst;
107   int err = 0;
108 
109   /* Construct buffers with the common filename prefix, and point to the end */
110 
111   in_path = xmalloc(strlen(intree) + NAME_MAX + 2);
112   strcpy(in_path, intree);
113   in_file = strchr(in_path, '\0');
114   *in_file++ = '/';
115 
116   out_path = xmalloc(strlen(outtree) + NAME_MAX + 2);
117   strcpy(out_path, outtree);
118   out_file = strchr(out_path, '\0');
119   *out_file++ = '/';
120 
121   if ( cribtree ) {
122     crib_path = xmalloc(strlen(cribtree) + NAME_MAX + 2);
123     strcpy(crib_path, cribtree);
124     crib_file = strchr(crib_path, '\0');
125     *crib_file++ = '/';
126   } else {
127     crib_path = crib_file = NULL;
128   }
129 
130   /* Get directory information */
131   if ( stat(intree, &dirst) ) {
132     message(vl_error, "%s: Failed to stat directory %s: %s\n",
133 	    program, intree, strerror(errno));
134     return EX_NOINPUT;
135   }
136 
137   /* Open the directory */
138   thisdir = opendir(intree);
139   if ( !thisdir ) {
140     message(vl_error, "%s: Failed to open directory %s: %s\n",
141 	    program, intree, strerror(errno));
142     return EX_NOINPUT;
143   }
144 
145   /* Create output directory */
146   if ( mkdir(outtree, 0700) ) {
147     message(vl_error, "%s: Cannot create output directory %s: %s\n",
148 	    program, outtree, strerror(errno));
149     return EX_CANTCREAT;
150   }
151 
152   while ( (dirent = readdir(thisdir)) != NULL ) {
153     if ( !strcmp(dirent->d_name, ".") ||
154 	 !strcmp(dirent->d_name, "..") )
155       continue;			/* Ignore . and .. */
156 
157     strcpy(in_file, dirent->d_name);
158     strcpy(out_file, dirent->d_name);
159     if ( crib_file )
160       strcpy(crib_file, dirent->d_name);
161 
162     err = munge_entry(in_path, out_path, crib_path, &dirst);
163     if ( err )
164       break;
165   }
166   closedir(thisdir);
167 
168   free(in_path);
169   free(out_path);
170 
171   return err;
172 }
173 
174 
munge_entry(const char * in_path,const char * out_path,const char * crib_path,const struct stat * dirst)175 int munge_entry(const char *in_path, const char *out_path,
176 		const char *crib_path, const struct stat *dirst)
177 {
178   struct stat st;
179   int err = 0;
180 
181   message(vl_filename, "%s -> %s\n", in_path, out_path);
182 
183   if ( lstat(in_path, &st) ) {
184     message(vl_error, "%s: Failed to stat file %s: %s\n",
185 	    program, in_path, strerror(errno));
186     return EX_NOINPUT;
187   }
188 
189   if ( S_ISREG(st.st_mode) ) {
190     if ( st.st_nlink > 1 ) {
191       /* Hard link. */
192       const char *linkname;
193 
194       if ( (linkname = hash_find_file(&st)) != NULL ) {
195 	/* We've seen it before, hard link it */
196 
197 	if ( link(linkname, out_path) ) {
198 	  message(vl_error, "%s: hard link %s -> %s failed: %s\n",
199 		  program, out_path, linkname, strerror(errno));
200 	  return EX_CANTCREAT;
201 	}
202       } else {
203 	/* First encounter, compress and enter into hash */
204 	if ( (err = munge_file(in_path, out_path, crib_path, &st)) != 0 ) {
205 	  message(vl_error, "%s: %s: %s", program, in_path, strerror(errno));
206 	  return err;
207 	}
208 	hash_insert_file(&st, out_path);
209       }
210     } else {
211       /* Singleton file; no funnies */
212       if ( (err = munge_file(in_path, out_path, crib_path, &st)) != 0 ) {
213 	message(vl_error, "%s: %s: %s", program, in_path, strerror(errno));
214 	return err;
215       }
216     }
217   } else if ( S_ISDIR(st.st_mode) ) {
218     /* Recursion: see recursion */
219     if ( !opt.onedir &&
220 	 (!opt.onefs || (dirst && dirst->st_dev == st.st_dev)) ) {
221       if ( (err = munge_tree(in_path, out_path, crib_path)) != 0 )
222 	return err;
223     } else if ( opt.do_mkdir ) {
224       /* Create stub directories */
225       if ( mkdir(out_path, st.st_mode) ) {
226 	message(vl_error, "%s: %s: %s", program, out_path, strerror(errno));
227 	return EX_CANTCREAT;
228       }
229     }
230   } else if ( S_ISLNK(st.st_mode) ) {
231     int chars;
232 #ifdef PATH_MAX
233 #define BUFFER_SLACK PATH_MAX
234 #else
235 #define BUFFER_SLACK BUFSIZ
236 #endif
237     int buffer_len = st.st_size + BUFFER_SLACK + 1;
238     char *buffer = xmalloc(buffer_len);
239     if ( (chars = readlink(in_path, buffer, buffer_len)) < 0 ) {
240       message(vl_error, "%s: readlink failed for %s: %s\n",
241 	      program, in_path, strerror(errno));
242       return EX_NOINPUT;
243     }
244     buffer[chars] = '\0';
245     if ( symlink(buffer, out_path) ) {
246       message(vl_error, "%s: symlink %s -> %s failed: %s\n",
247 	      program, out_path, buffer, strerror(errno));
248       return EX_CANTCREAT;
249     }
250     free(buffer);
251   } else {
252     if ( st.st_nlink > 1 ) {
253       /* Hard link. */
254       const char *linkname;
255 
256       if ( (linkname = hash_find_file(&st)) != NULL ) {
257 	/* We've seen it before, hard link it */
258 
259 	if ( link(linkname, out_path) ) {
260 	  message(vl_error, "%s: hard link %s -> %s failed: %s\n",
261 		  program, out_path, linkname, strerror(errno));
262 	  return EX_CANTCREAT;
263 	}
264       } else {
265 	/* First encounter, create and enter into hash */
266 	if ( mknod(out_path, st.st_mode, st.st_rdev) ) {
267 	  message(vl_error, "%s: mknod failed for %s: %s\n",
268 		  program, out_path, strerror(errno));
269 	  return EX_CANTCREAT;
270 	}
271 	hash_insert_file(&st, out_path);
272       }
273     } else {
274       /* Singleton node; no funnies */
275       if ( mknod(out_path, st.st_mode, st.st_rdev) ) {
276 	message(vl_error, "%s: mknod failed for %s: %s\n",
277 		program, out_path, strerror(errno));
278 	return EX_CANTCREAT;
279       }
280     }
281   }
282 
283   /* This is done by munge_file() for files */
284   if ( !S_ISREG(st.st_mode) ) {
285 #ifdef HAVE_LCHOWN
286     if ( lchown(out_path, st.st_uid, st.st_gid) && opt.sloppy && !err ) {
287       message(vl_error, "%s: %s: %s", program, out_path, strerror(errno));
288       err = EX_CANTCREAT;
289     }
290 #endif
291     if ( !S_ISLNK(st.st_mode) ) {
292 #ifndef HAVE_LCHOWN
293       if ( chown(out_path, st.st_uid, st.st_gid) && !opt.sloppy && !err ) {
294 	message(vl_error, "%s: %s: %s", program, out_path, strerror(errno));
295 	err = EX_CANTCREAT;
296       }
297 #endif
298       if ( chmod(out_path, st.st_mode) && !opt.sloppy && !err ) {
299 	message(vl_error, "%s: %s: %s", program, out_path, strerror(errno));
300 	err = EX_CANTCREAT;
301       }
302       if ( copytime(out_path, &st) && !opt.sloppy && !err ) {
303 	message(vl_error, "%s: %s: %s", program, out_path, strerror(errno));
304 	err = EX_CANTCREAT;
305       }
306     }
307   }
308 
309   return err;
310 }
311 
312