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