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