extractcontrol.c 11 KB
Newer Older
Bernhard Link's avatar
Bernhard Link committed
1
/*  This file is part of "reprepro"
2
 *  Copyright (C) 2003,2007 Bernhard R. Link
3
 *  This program is free software; you can redistribute it and/or modify
4
 *  it under the terms of the GNU General Public License version 2 as
Bernhard Link's avatar
Bernhard Link committed
5
 *  published by the Free Software Foundation.
6 7 8 9 10 11 12 13
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
Bernhard Link's avatar
Bernhard Link committed
14
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1301  USA
15 16 17 18 19 20 21 22 23 24 25 26 27
 */
#include <config.h>

#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include "error.h"
28 29
#include "filecntl.h"
#include "readtextfile.h"
30
#include "debfile.h"
31
#include "chunks.h"
32

33 34 35
#ifdef HAVE_LIBARCHIVE
#error Why did this file got compiled instead of debfile.c?
#endif
Bernhard Link's avatar
Bernhard Link committed
36
// **********************************************************************
37
// * This is a very simple implementation calling ar and tar, which
38
// * is only used with --without-libarchive or when no libarchive was
39
// * found.
Bernhard Link's avatar
Bernhard Link committed
40
// **********************************************************************
41

42
static retvalue try_extractcontrol(char **control, const char *debfile, bool brokentar) {
Bernhard Link's avatar
Bernhard Link committed
43 44
	int pipe_1[2];
	int pipe_2[2];
45
	int ret;
46
	pid_t ar, tar, pid;
47
	int status;
48
	char *controlchunk;
49

50
	retvalue result, r;
51 52 53

	result = RET_OK;

Bernhard Link's avatar
Bernhard Link committed
54
	ret = pipe(pipe_1);
55
	if (ret < 0) {
Bernhard Link's avatar
Bernhard Link committed
56
		int e = errno;
Bernhard Link's avatar
Bernhard Link committed
57
		fprintf(stderr, "Error %d creating pipe: %s\n", e, strerror(e));
Bernhard Link's avatar
Bernhard Link committed
58
		return RET_ERRNO(e);
59 60
	}

Bernhard Link's avatar
Bernhard Link committed
61
	ret = pipe(pipe_2);
62
	if (ret < 0) {
Bernhard Link's avatar
Bernhard Link committed
63
		int e = errno;
Bernhard Link's avatar
Bernhard Link committed
64
		close(pipe_1[0]); close(pipe_1[1]);
Bernhard Link's avatar
Bernhard Link committed
65
		fprintf(stderr, "Error %d creating pipe: %s\n", e, strerror(e));
Bernhard Link's avatar
Bernhard Link committed
66
		return RET_ERRNO(e);
67
	}
68

69
	ar = fork();
70
	if (ar < 0) {
Bernhard Link's avatar
Bernhard Link committed
71
		int e = errno;
Bernhard Link's avatar
Bernhard Link committed
72
		fprintf(stderr, "Error %d forking: %s\n", e, strerror(e));
Bernhard Link's avatar
Bernhard Link committed
73
		result = RET_ERRNO(e);
Bernhard Link's avatar
Bernhard Link committed
74 75
		close(pipe_1[0]); close(pipe_1[1]);
		close(pipe_2[0]); close(pipe_2[1]);
76 77 78
		return result;
	}

79
	if (ar == 0) {
Bernhard Link's avatar
Bernhard Link committed
80
		int e;
81
		/* calling ar */
Bernhard Link's avatar
Bernhard Link committed
82
		if (dup2(pipe_1[1], 1) < 0)
83
			exit(255);
Bernhard Link's avatar
Bernhard Link committed
84 85
		close(pipe_1[0]); close(pipe_1[1]);
		close(pipe_2[0]); close(pipe_2[1]);
86
		//TODO without explicit path
87 88 89
		ret = execl("/usr/bin/ar",
				"ar", "p", debfile, "control.tar.gz",
				ENDOFARGUMENTS);
Bernhard Link's avatar
Bernhard Link committed
90 91 92
		e = errno;
		fprintf(stderr, "ar call failed with error %d: %s\n",
				e, strerror(e));
93 94 95 96
		exit(254);
	}

	tar = fork();
97
	if (tar < 0) {
Bernhard Link's avatar
Bernhard Link committed
98 99
		int e = errno;
		result = RET_ERRNO(e);
Bernhard Link's avatar
Bernhard Link committed
100
		fprintf(stderr, "Error %d forking: %s\n", e, strerror(e));
Bernhard Link's avatar
Bernhard Link committed
101 102
		close(pipe_1[0]); close(pipe_1[1]);
		close(pipe_2[0]); close(pipe_2[1]);
103
		tar = -1;
104
	} else if (tar == 0) {
Bernhard Link's avatar
Bernhard Link committed
105
		int e;
106
		/* calling tar */
Bernhard Link's avatar
Bernhard Link committed
107
		if (dup2(pipe_1[0], 0) < 0)
108
			exit(255);
Bernhard Link's avatar
Bernhard Link committed
109
		if (dup2(pipe_2[1], 1) < 0)
110
			exit(255);
Bernhard Link's avatar
Bernhard Link committed
111 112
		close(pipe_1[0]); close(pipe_1[1]);
		close(pipe_2[0]); close(pipe_2[1]);
113
		//TODO without explicit path
114
		execl("/bin/tar", "tar", "-xOzf", "-",
115 116
				brokentar?"control":"./control",
				ENDOFARGUMENTS);
Bernhard Link's avatar
Bernhard Link committed
117 118 119
		e = errno;
		fprintf(stderr, "tar call failed with error %d: %s\n",
				e, strerror(e));
120
		exit(254);
121

122 123
	}

Bernhard Link's avatar
Bernhard Link committed
124 125
	close(pipe_1[0]); close(pipe_1[1]);
	markcloseonexec(pipe_2[0]); close(pipe_2[1]);
126

127 128
	controlchunk = NULL;

129
	/* read data: */
130
	if (RET_IS_OK(result)) {
Bernhard Link's avatar
Bernhard Link committed
131 132
		size_t len, controllen;
		const char *afterchanges;
133

Bernhard Link's avatar
Bernhard Link committed
134
		r = readtextfilefd(pipe_2[0],
135
				brokentar?
136 137
"output from ar p <debfile> control.tar.gz | tar -xOzf - control":
"output from ar p <debfile> control.tar.gz | tar -xOzf - ./control",
Bernhard Link's avatar
Bernhard Link committed
138
				&controlchunk, &controllen);
139
		if (RET_IS_OK(r)) {
Bernhard Link's avatar
Bernhard Link committed
140 141
			len = chunk_extract(controlchunk,
					controlchunk, controllen,
142
					false, &afterchanges);
143
			if (len == 0)
144
				r = RET_NOTHING;
145
			if (*afterchanges != '\0') {
146
				fprintf(stderr,
147
"Unexpected empty line in control information within '%s'\n"
148
"(obtained via 'ar p %s control.tar.gz | tar -XOzf - %scontrol')\n",
149
						debfile, debfile,
150 151 152 153 154 155
						brokentar?"":"./");
				free(controlchunk);
				controlchunk = NULL;
				r = RET_ERROR;
			}
		}
156
		if (r == RET_NOTHING) {
157 158
			free(controlchunk);
			controlchunk = NULL;
159 160 161 162 163
			fprintf(stderr,
"No control information found in .deb!\n");
			/* only report error now,
			 * if we haven't try everything yet */
			if (brokentar)
164
				r = RET_ERROR_MISSING;
165
		}
166
		RET_UPDATE(result, r);
167

168
	}
169

170
	while (ar != -1 || tar != -1) {
171
		pid=wait(&status);
172 173 174
		if (pid < 0) {
			if (errno != EINTR)
				RET_UPDATE(result, RET_ERRNO(errno));
175
		} else {
176
			if (pid == ar) {
177
				ar = -1;
178 179 180
				if (!WIFEXITED(status)) {
					fprintf(stderr,
"Ar exited unnaturally!\n");
181
					result = RET_ERROR;
182 183 184
				} else if (WEXITSTATUS(status) != 0) {
					fprintf(stderr,
"Error from ar for '%s': %d\n", debfile, WEXITSTATUS(status));
185 186
					result = RET_ERROR;
				}
187
			} else if (pid == tar) {
188
				tar = -1;
189 190 191
				if (!WIFEXITED(status)) {
					fprintf(stderr,
"Tar exited unnaturally!\n");
192
					result = RET_ERROR;
193 194
				} else if (!brokentar && WEXITSTATUS(status) == 2) {
					if (RET_IS_OK(result))
195
						result = RET_NOTHING;
196 197 198
				} else if (WEXITSTATUS(status) != 0) {
					fprintf(stderr,
"Error from tar for control.tar.gz within '%s': %d\n",
199 200
							debfile,
							WEXITSTATUS(status));
201 202 203 204
					result = RET_ERROR;
				}
			} else {
				// WTH?
205 206
				fprintf(stderr,
"Who is %d, and why does this bother me?\n", (int)pid);
207 208
			}
		}
209

210
	}
211 212
	if (RET_IS_OK(result)) {
		if (controlchunk == NULL)
213 214 215 216 217
			/* we got not data but tar gave not error.. */
			return RET_ERROR_MISSING;
		else
			*control = controlchunk;
	} else
218
		free(controlchunk);
219 220
	return result;
}
221

222
retvalue extractcontrol(char **control, const char *debfile) {
223 224
	retvalue r;

225
	r = try_extractcontrol(control, debfile, false);
226
	if (r != RET_NOTHING)
227 228 229
		return r;
	/* perhaps the control.tar.gz is packaged by hand wrongly,
	 * try again: */
230
	r = try_extractcontrol(control, debfile, true);
231 232 233 234 235
	if (RET_IS_OK(r)) {
		fprintf(stderr,
"WARNING: '%s' contains a broken/unusual control.tar.gz.\n"
"reprepro was able to work around this but other tools or versions might not.\n",
				debfile);
236
	}
237
	assert (r != RET_NOTHING);
238 239 240
	return r;
}

241
retvalue getfilelist(/*@out@*/char **filelist, /*@out@*/size_t *size, const char *debfile) {
242 243
	fprintf(stderr,
"Extraction of file list without libarchive currently not implemented.\n");
244 245
	return RET_ERROR;
#if 0
Bernhard Link's avatar
Bernhard Link committed
246 247
	int pipe_1[2];
	int pipe_2[2];
248
	int ret;
249
	pid_t ar, tar, pid;
250
	int status;
251 252
	struct filelistcompressor c;
	size_t last = 0;
253 254
	retvalue result;

255 256
#error this still needs to be reimplemented...
	result = filelistcompressor_setup(&c);
257
	if (RET_WAS_ERROR(result))
258 259
		return result;

260 261
	result = RET_OK;

Bernhard Link's avatar
Bernhard Link committed
262
	ret = pipe(pipe_1);
263
	if (ret < 0) {
Bernhard Link's avatar
Bernhard Link committed
264
		int e = errno;
Bernhard Link's avatar
Bernhard Link committed
265
		fprintf(stderr, "Error %d creating pipe: %s\n", e, strerror(e));
266
		filelistcompressor_cancel(&c);
Bernhard Link's avatar
Bernhard Link committed
267
		return RET_ERRNO(e);
268 269
	}

Bernhard Link's avatar
Bernhard Link committed
270
	ret = pipe(pipe_2);
271
	if (ret < 0) {
Bernhard Link's avatar
Bernhard Link committed
272
		int e = errno;
Bernhard Link's avatar
Bernhard Link committed
273
		fprintf(stderr, "Error %d creating pipe: %s\n", e, strerror(e));
Bernhard Link's avatar
Bernhard Link committed
274
		close(pipe_1[0]); close(pipe_1[1]);
275
		filelistcompressor_cancel(&c);
Bernhard Link's avatar
Bernhard Link committed
276
		return RET_ERRNO(e);
277
	}
278

279
	ar = fork();
280
	if (ar < 0) {
Bernhard Link's avatar
Bernhard Link committed
281
		int e = errno;
Bernhard Link's avatar
Bernhard Link committed
282
		fprintf(stderr, "Error %d forking: %s\n", e, strerror(e));
Bernhard Link's avatar
Bernhard Link committed
283
		result = RET_ERRNO(e);
Bernhard Link's avatar
Bernhard Link committed
284 285
		close(pipe_1[0]); close(pipe_1[1]);
		close(pipe_2[0]); close(pipe_2[1]);
286
		filelistcompressor_cancel(&c);
287 288 289
		return result;
	}

290
	if (ar == 0) {
Bernhard Link's avatar
Bernhard Link committed
291
		int e;
292
		/* calling ar */
Bernhard Link's avatar
Bernhard Link committed
293
		if (dup2(pipe_1[1], 1) < 0)
294
			exit(255);
Bernhard Link's avatar
Bernhard Link committed
295 296
		close(pipe_1[0]); close(pipe_1[1]);
		close(pipe_2[0]); close(pipe_2[1]);
297
		//TODO without explicit path
298 299 300
		ret = execl("/usr/bin/ar",
				"ar", "p", debfile, "data.tar.gz",
				ENDOFARGUMENTS);
Bernhard Link's avatar
Bernhard Link committed
301 302 303
		e = errno;
		fprintf(stderr, "ar call failed with error %d: %s\n",
				e, strerror(e));
304 305 306 307
		exit(254);
	}

	tar = fork();
308
	if (tar < 0) {
Bernhard Link's avatar
Bernhard Link committed
309 310
		int e = errno;
		result = RET_ERRNO(e);
Bernhard Link's avatar
Bernhard Link committed
311
		fprintf(stderr, "Error %d forking: %s\n", e, strerror(e));
Bernhard Link's avatar
Bernhard Link committed
312 313
		close(pipe_1[0]); close(pipe_1[1]);
		close(pipe_2[0]); close(pipe_2[1]);
314
		tar = -1;
315
	} else if (tar == 0) {
Bernhard Link's avatar
Bernhard Link committed
316
		int e;
317
		/* calling tar */
Bernhard Link's avatar
Bernhard Link committed
318
		if (dup2(pipe_1[0], 0) < 0)
319
			exit(255);
Bernhard Link's avatar
Bernhard Link committed
320
		if (dup2(pipe_2[1], 1) < 0)
321
			exit(255);
Bernhard Link's avatar
Bernhard Link committed
322 323
		close(pipe_1[0]); close(pipe_1[1]);
		close(pipe_2[0]); close(pipe_2[1]);
324
		//TODO without explicit path
325
		execl("/bin/tar", "tar", "-tzf", "-", ENDOFARGUMENTS);
Bernhard Link's avatar
Bernhard Link committed
326 327 328
		e = errno;
		fprintf(stderr, "tar call failed with error %d: %s\n",
				e, strerror(e));
329
		exit(254);
330

331 332
	}

Bernhard Link's avatar
Bernhard Link committed
333 334
	close(pipe_1[0]); close(pipe_1[1]);
	close(pipe_2[1]);
335 336

	/* read data: */
337
	if (RET_IS_OK(result)) do {
338 339 340
		ssize_t bytes_read;
		size_t ignore;

341
		if (listsize <= len + 512) {
342 343 344 345
			char *n;

			listsize = len + 1024;
			n = realloc(list, listsize);
346
			if (FAILEDTOALLOC(n)) {
347 348 349 350 351 352 353
				result = RET_ERROR_OOM;
				break;
			}
			list = n;
		}

		ignore = 0;
Bernhard Link's avatar
Bernhard Link committed
354
		bytes_read = read(pipe_2[0], list+len, listsize-len-1);
355
		if (bytes_read < 0) {
356
			int e = errno;
Bernhard Link's avatar
Bernhard Link committed
357
			fprintf(stderr, "Error %d reading from pipe: %s\n",
358 359 360
					e, strerror(e));
			result = RET_ERRNO(e);
			break;
361
		} else if (bytes_read == 0)
362
			break;
363 364
		else while (bytes_read > 0) {
			if (list[len] == '\0') {
Bernhard Link's avatar
Bernhard Link committed
365 366
				fprintf(stderr,
"Unexpected NUL character from tar while getting file list from %s!\n", debfile);
367 368
				result = RET_ERROR;
				break;
369 370
			} else if (list[len] == '\n') {
				if (len > last+ignore && list[len-1] != '/') {
371 372 373 374 375 376 377 378 379 380 381
					list[len] = '\0';
					len++;
					bytes_read--;
					memmove(list+last, list+last+ignore,
						1+len-last-ignore);
					last = len-ignore;
				} else {
					len++;
					bytes_read--;
					ignore = len-last;
				}
382
			} else if (list[len] == '.' && len == last+ignore) {
383 384
				len++; ignore++;
				bytes_read--;
385
			} else if (list[len] == '/' && len == last+ignore) {
386 387 388 389 390 391 392
				len++; ignore++;
				bytes_read--;
			} else {
				len++;
				bytes_read--;
			}
		}
393 394
		if (ignore > 0) {
			if (len <= last+ignore)
395 396
				len = last;
			else {
397
				memmove(list+last, list+last+ignore,
398 399 400 401
						1+len-last-ignore);
				len -= ignore;
			}
		}
402 403
	} while (true);
	if (len != last) {
Bernhard Link's avatar
Bernhard Link committed
404 405
		fprintf(stderr,
"WARNING: unterminated output from tar pipe while extracting file list of %s\n", debfile);
406 407 408 409 410
		list[len] = '\0';
		fprintf(stderr, "The item '%s' might got lost.\n",
				list+last);
		result = RET_ERROR;
	} else {
411 412
		char *n = realloc(list, len+1);
		if (FAILEDTOALLOC(n))
413 414 415 416 417 418
			result = RET_ERROR_OOM;
		else {
			list = n;
			list[len] = '\0';
		}
	}
Bernhard Link's avatar
Bernhard Link committed
419
	close(pipe_2[0]);
420

421
	while (ar != -1 || tar != -1) {
422
		pid=wait(&status);
423 424 425
		if (pid < 0) {
			if (errno != EINTR)
				RET_UPDATE(result, RET_ERRNO(errno));
426
		} else {
427
			if (pid == ar) {
428
				ar = -1;
429
				if (!WIFEXITED(status) ||
430
						WEXITSTATUS(status) != 0) {
431 432
					fprintf(stderr,
"Error from ar for '%s': %d\n", debfile, WEXITSTATUS(status));
433 434
					result = RET_ERROR;
				}
435
			} else if (pid == tar) {
436
				tar = -1;
437 438 439 440
				if (!WIFEXITED(status) ||
						WEXITSTATUS(status) != 0) {
					fprintf(stderr,
"Error from tar for data.tar.gz within '%s': %d\n",
441 442
							debfile,
							WEXITSTATUS(status));
443 444 445 446
					result = RET_ERROR;
				}
			} else {
				// WTH?
447 448
				fprintf(stderr,
"Who is %d, and why does this bother me?\n", pid);
449 450 451
			}
		}
	}
452
	if (RET_IS_OK(result))
453
		return filelistcompressor_finish(&c, filelist);
454
	else
455
		filelistcompressor_cancel(&c);
456
	return result;
457
#endif
458
}