indexfile.c 7.59 KB
Newer Older
1
/*  This file is part of "reprepro"
2
 *  Copyright (C) 2003,2004,2005,2007,2008,2010,2016 Bernhard R. Link
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1301  USA
 */
#include <config.h>

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include "error.h"
25
#include "ignore.h"
26 27
#include "chunks.h"
#include "names.h"
28
#include "uncompression.h"
29
#include "package.h"
30 31 32 33 34 35
#include "indexfile.h"

/* the purpose of this code is to read index files, either from a snapshot
 * previously generated or downloaded while updating. */

struct indexfile {
36
	struct compressedfile *f;
37 38 39 40
	char *filename;
	int linenumber, startlinenumber;
	retvalue status;
	char *buffer;
41
	int size, ofs, content;
42
	bool failed;
43 44
};

45
retvalue indexfile_open(struct indexfile **file_p, const char *filename, enum compression compression) {
46
	struct indexfile *f = zNEW(struct indexfile);
47
	retvalue r;
48

49
	if (FAILEDTOALLOC(f))
50 51
		return RET_ERROR_OOM;
	f->filename = strdup(filename);
52
	if (FAILEDTOALLOC(f->filename)) {
53 54 55
		free(f);
		return RET_ERROR_OOM;
	}
56
	r = uncompress_open(&f->f, filename, compression);
57 58
	assert (r != RET_NOTHING);
	if (RET_WAS_ERROR(r)) {
59 60 61 62 63 64 65
		free(f->filename);
		free(f);
		return RET_ERRNO(errno);
	}
	f->linenumber = 0;
	f->startlinenumber = 0;
	f->status = RET_OK;
66 67 68 69 70
	f->size = 256*1024;
	f->ofs = 0;
	f->content = 0;
	/* +1 for *d = '\0' in eof case */
	f->buffer = malloc(f->size + 1);
71
	if (FAILEDTOALLOC(f->buffer)) {
72
		uncompress_abort(f->f);
73
		free(f->filename);
74 75 76 77 78 79 80 81 82 83
		free(f);
		return RET_ERROR_OOM;
	}
	*file_p = f;
	return RET_OK;
}

retvalue indexfile_close(struct indexfile *f) {
	retvalue r;

84
	r = uncompress_close(f->f);
85 86 87

	free(f->filename);
	free(f->buffer);
88
	RET_UPDATE(r, f->status);
89 90 91 92 93 94
	free(f);

	return r;
}

static retvalue indexfile_get(struct indexfile *f) {
95 96 97 98
	char *p, *d, *e, *start;
	bool afternewline, nothingyet;
	int bytes_read;

99
	if (f->failed)
100 101
		return RET_ERROR;

102 103 104 105 106 107 108 109 110 111 112 113
	d = f->buffer;
	afternewline = true;
	nothingyet = true;
	do {
		start = f->buffer + f->ofs;
		p = start ;
		e = p + f->content;

		// TODO: if the chunk_get* are more tested with strange
		// input, this could be kept in-situ and only chunk_edit
		// beautifying this chunk...

114
		while (p < e) {
115
			/* just ignore '\r', even if not line-end... */
116
			if (*p == '\r') {
117 118 119
				p++;
				continue;
			}
120
			if (*p == '\n') {
121
				f->linenumber++;
122
				if (afternewline) {
123 124 125
					p++;
					f->content -= (p - start);
					f->ofs += (p - start);
126 127
					assert (f->ofs == (p - f->buffer));
					if (nothingyet)
128 129
						/* restart */
						return indexfile_get(f);
130
					if (d > f->buffer && *(d-1) == '\n')
131 132 133 134 135 136 137 138
						d--;
					*d = '\0';
					return RET_OK;
				}
				afternewline = true;
				nothingyet = false;
			} else
				afternewline = false;
139
			if (unlikely(*p == '\0')) {
140 141 142 143
				*(d++) = ' ';
				p++;
			} else
				*(d++) = *(p++);
144
		}
145 146 147 148 149 150
		/* ** out of data, read new ** */

		/* start at beginning of free space */
		f->ofs = (d - f->buffer);
		f->content = 0;

151
		if (f->size - f->ofs <= 2048) {
152 153 154 155 156 157 158 159 160 161 162 163
			/* Adding code to enlarge the buffer in this case
			 * is risky as hard to test properly.
			 *
			 * Also it is almost certainly caused by some
			 * mis-representation of the file or perhaps
			 * some attack. Requesting all existing memory in
			 * those cases does not sound very useful. */

			fprintf(stderr,
"Error parsing %s line %d: Ridiculous long (>= 256K) control chunk!\n",
					f->filename,
					f->startlinenumber);
Bernhard Link's avatar
Bernhard Link committed
164
			f->failed = true;
165
			return RET_ERROR;
166
		}
167

168
		bytes_read = uncompress_read(f->f, d, f->size - f->ofs);
169
		if (bytes_read < 0)
170
			return RET_ERROR;
171
		else if (bytes_read == 0)
172
			break;
173
		f->content = bytes_read;
174
	} while (true);
175

176
	if (d == f->buffer)
177
		return RET_NOTHING;
178 179

	/* end of file reached, return what we got so far */
180 181 182
	assert (f->content == 0);
	assert (d-f->buffer <= f->size);
	if (d > f->buffer && *(d-1) == '\n')
183 184
		d--;
	*d = '\0';
185 186 187
	return RET_OK;
}

188
bool indexfile_getnext(struct indexfile *f, struct package *pkgout, struct target *target, bool allowwrongarchitecture) {
189 190
	retvalue r;
	bool ignorecruft = false; // TODO
191
	char *packagename, *version;
192
	const char *control;
193
	architecture_t atom;
194

195
	packagename = NULL; version = NULL;
196
	do {
197 198
		free(packagename); packagename = NULL;
		free(version); version = NULL;
199
		f->startlinenumber = f->linenumber + 1;
200
		r = indexfile_get(f);
201
		if (!RET_IS_OK(r))
202
			break;
203 204
		control = f->buffer;
		r = chunk_getvalue(control, "Package", &packagename);
205
		if (r == RET_NOTHING) {
206
			fprintf(stderr,
207 208 209
"Error parsing %s line %d to %d: Chunk without 'Package:' field!\n",
					f->filename,
					f->startlinenumber, f->linenumber);
210
			if (!ignorecruft)
211 212 213 214
				r = RET_ERROR_MISSING;
			else
				continue;
		}
215
		if (RET_WAS_ERROR(r))
216
			break;
217 218

		r = chunk_getvalue(control, "Version", &version);
219
		if (r == RET_NOTHING) {
220
			fprintf(stderr,
221 222 223
"Error parsing %s line %d to %d: Chunk without 'Version:' field!\n",
					f->filename,
					f->startlinenumber, f->linenumber);
224
			if (!ignorecruft)
225
				r = RET_ERROR_MISSING;
226
			else
227 228
				continue;
		}
229
		if (RET_WAS_ERROR(r))
230
			break;
231
		if (target->packagetype == pt_dsc) {
232
			atom = architecture_source;
233
		} else {
234 235 236 237 238 239 240 241
			char *architecture;

			r = chunk_getvalue(control, "Architecture", &architecture);
			if (RET_WAS_ERROR(r))
				break;
			if (r == RET_NOTHING)
				architecture = NULL;

242 243
			/* check if architecture fits for target and error
			    out if not ignorewrongarchitecture */
244
			if (architecture == NULL) {
245 246 247 248
				fprintf(stderr,
"Error parsing %s line %d to %d: Chunk without 'Architecture:' field!\n",
						f->filename,
						f->startlinenumber, f->linenumber);
249
				if (!ignorecruft) {
250 251 252 253
					r = RET_ERROR_MISSING;
					break;
				} else
					continue;
254
			} else if (strcmp(architecture, "all") == 0) {
255
				atom = architecture_all;
256
			} else if (strcmp(architecture,
257
					   atoms_architectures[
258
						target->architecture
259
						]) == 0) {
260 261 262
				atom = target->architecture;
			} else if (!allowwrongarchitecture
					&& !ignore[IGN_wrongarchitecture]) {
263 264 265
				fprintf(stderr,
"Warning: ignoring package because of wrong 'Architecture:' field '%s'"
" (expected 'all' or '%s') in %s lines %d to %d!\n",
266
						architecture,
267
						atoms_architectures[
268
						target->architecture],
269 270 271
						f->filename,
						f->startlinenumber,
						f->linenumber);
272
				if (ignored[IGN_wrongarchitecture] == 0) {
273
					fprintf(stderr,
274
"This either mean the repository you get packages from is of an extremely\n"
275 276
"low quality, or something went wrong. Trying to ignore it now, though.\n"
"To no longer get this message use '--ignore=wrongarchitecture'.\n");
277
				}
278 279 280 281 282 283 284 285
				ignored[IGN_wrongarchitecture]++;
				free(architecture);
				continue;
			} else {
				/* just ignore this because of wrong
				 * architecture */
				free(architecture);
				continue;
286 287
			}
			free(architecture);
288
		}
289
		if (RET_WAS_ERROR(r))
290
			break;
291 292 293 294 295 296 297
		pkgout->target = target;
		pkgout->control = control;
		pkgout->pkgname = packagename;
		pkgout->name = pkgout->pkgname;
		pkgout->pkgversion = version;
		pkgout->version = pkgout->pkgversion;
		pkgout->architecture = atom;
298
		return true;
299
	} while (true);
300 301 302 303
	free(packagename);
	free(version);
	RET_UPDATE(f->status, r);
	return false;
304
}