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