pool.c 21.2 KB
Newer Older
1
/*  This file is part of "reprepro"
Bernhard Link's avatar
Bernhard Link committed
2
 *  Copyright (C) 2008,2009,2012 Bernhard R. Link
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *  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 <assert.h>
#include <limits.h>
Bernhard Link's avatar
Bernhard Link committed
21 22 23
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
24 25 26 27
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <search.h>
28
#include <unistd.h>
29 30 31 32
#include "error.h"
#include "ignore.h"
#include "mprintf.h"
#include "atoms.h"
Bernhard Link's avatar
Bernhard Link committed
33 34
#include "strlist.h"
#include "dirs.h"
35 36 37
#include "pool.h"
#include "reference.h"
#include "files.h"
38
#include "sources.h"
Bernhard Link's avatar
Bernhard Link committed
39
#include "outhook.h"
40 41 42 43

/* for now save them only in memory. In later times some way to store
 * them on disk would be nice */

44
static component_t reserved_components = 0;
45 46 47
static void **file_changes_per_component = NULL;
static void *legacy_file_changes = NULL;
bool pool_havedereferenced = false;
48
bool pool_havedeleted = false;
49 50 51

#define pl_ADDED 1
#define pl_UNREFERENCED 2
52
#define pl_DELETED 4
53 54 55 56 57

static int legacy_compare(const void *a, const void *b) {
	const char *v1 = a, *v2 = b;
	v1++;
	v2++;
58
	return strcmp(v1, v2);
59 60
}

61 62 63 64
struct source_node {
	void *file_changes;
	char sourcename[];
};
65

66 67 68 69 70 71
static int source_node_compare(const void *a, const void *b) {
	const struct source_node *v1 = a, *v2 = b;
	return strcmp(v1->sourcename, v2->sourcename);
}

static retvalue split_filekey(const char *filekey, /*@out@*/component_t *component_p, /*@out@*/struct source_node **node_p, /*@out@*/const char **basename_p) {
72
	const char *p, *lastp, *source;
73 74 75
	struct source_node *node;
	component_t c;

76
	if (unlikely(memcmp(filekey, "pool/", 5) != 0))
77
		return RET_NOTHING;
78 79 80 81 82 83 84 85 86 87
	lastp = filekey + 4;
	filekey = lastp + 1;
	/* components can include slashes, so look for the first valid component
	 * followed by something looking like a proper directory.
	 * This might missdetect the component, but as it only is used for
	 * the current run it will hopefully always detect the same place
	 * (and all that is important is that it is the same place) */
	while (true) {
		p = strchr(lastp + 1, '/');
		if (unlikely(p == NULL))
88
			return RET_NOTHING;
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
		lastp = p;
		c = component_find_l(filekey, (size_t)(p - filekey));
		if (unlikely(!atom_defined(c)))
			continue;
		p++;
		if (p[0] != '\0' && p[1] == '/' && p[0] != '/' && p[2] == p[0]) {
			p += 2;
			if (unlikely(p[0] == 'l' && p[1] == 'i' && p[2] == 'b'))
				continue;
			source = p;
			break;
		} else if (p[0] == 'l' && p[1] == 'i' && p[2] == 'b' && p[3] != '\0'
				&& p[4] == '/' && p[5] == 'l' && p[6] == 'i'
				&& p[7] == 'b' && p[3] != '/' && p[8] == p[3]) {
			source = p + 5;
			break;
		} else
			continue;
	}
108
	p = strchr(source, '/');
109
	if (unlikely(p == NULL))
110 111
		return RET_NOTHING;
	node = malloc(sizeof(struct source_node) + (p - source) + 1);
112
	if (FAILEDTOALLOC(node))
113
		return RET_ERROR_OOM;
114 115 116 117 118 119 120
	node->file_changes = NULL;
	memcpy(node->sourcename, source, p - source);
	node->sourcename[p - source] = '\0';
	p++;
	*basename_p = p;
	*node_p = node;
	*component_p = c;
121
	return RET_OK;
122
}
123

124 125
/* name can be either basename (in a source directory) or a full
 * filekey (in legacy fallback mode) */
126
static retvalue remember_name(void **root_p, const char *name, char mode, char mode_and) {
127
	char **p;
128

129
	p = tsearch(name - 1, root_p, legacy_compare);
130
	if (FAILEDTOALLOC(p))
131
		return RET_ERROR_OOM;
132
	if (*p == name - 1) {
133
		size_t l = strlen(name);
134
		*p = malloc(l + 2);
135
		if (FAILEDTOALLOC(*p))
136
			return RET_ERROR_OOM;
137 138
		**p = mode;
		memcpy((*p) + 1, name, l + 1);
139
	} else {
140
		**p &= mode_and;
141
		**p |= mode;
142 143
	}
	return RET_OK;
144 145
}

146
static retvalue remember_filekey(const char *filekey, char mode, char mode_and) {
147
	retvalue r;
148 149 150
	component_t c;
	struct source_node *node, **found;
	const char *basefilename;
151 152

	r = split_filekey(filekey, &c, &node, &basefilename);
153
	if (RET_WAS_ERROR(r))
154
		return r;
155 156 157
	if (r == RET_OK) {
		assert (atom_defined(c));
		if (c > reserved_components) {
158 159
			void ** h;

160
			assert (c <= components_count());
161 162

			h = realloc(file_changes_per_component,
163
					sizeof(void*) * (c + 1));
164
			if (FAILEDTOALLOC(h))
165 166
				return RET_ERROR_OOM;
			file_changes_per_component = h;
167
			while (reserved_components < c) {
168 169 170
				h[++reserved_components] = NULL;
			}
		}
171
		assert (file_changes_per_component != NULL);
172 173
		found = tsearch(node, &file_changes_per_component[c],
				source_node_compare);
174
		if (FAILEDTOALLOC(found))
175
			return RET_ERROR_OOM;
176
		if (*found != node) {
177 178 179
			free(node);
			node = *found;
		}
180 181
		return remember_name(&node->file_changes, basefilename,
				mode, mode_and);
182 183
	}
	fprintf(stderr, "Warning: strange filekey '%s'!\n", filekey);
184
	return remember_name(&legacy_file_changes, filekey, mode, mode_and);
185 186 187 188
}

retvalue pool_dereferenced(const char *filekey) {
	pool_havedereferenced = true;
189
	return remember_filekey(filekey, pl_UNREFERENCED, 0xFF);
190 191
};

192 193
retvalue pool_markadded(const char *filekey) {
	return remember_filekey(filekey, pl_ADDED, ~pl_DELETED);
194 195
};

Bernhard Link's avatar
Bernhard Link committed
196 197 198 199 200 201 202 203 204 205
/* so much code, just for the case the morguedir is on an other partition than
 * the pool dir... */

static inline retvalue copyfile(const char *source, const char *destination, int outfd, off_t length) {
	int infd, err;
	ssize_t readbytes;
	void *buffer;
	size_t bufsize = 1024*1024;

	buffer = malloc(bufsize);
206
	if (FAILEDTOALLOC(buffer)) {
Bernhard Link's avatar
Bernhard Link committed
207 208 209 210
		(void)close(outfd);
		(void)unlink(destination);
		bufsize = 16*1024;
		buffer = malloc(bufsize);
211
		if (FAILEDTOALLOC(buffer))
Bernhard Link's avatar
Bernhard Link committed
212 213 214 215
			return RET_ERROR_OOM;
	}

	infd = open(source, O_RDONLY|O_NOCTTY);
216
	if (infd < 0) {
Bernhard Link's avatar
Bernhard Link committed
217 218
		int en = errno;

219 220
		fprintf(stderr,
"error %d opening file %s to be copied into the morgue: %s\n",
Bernhard Link's avatar
Bernhard Link committed
221 222 223 224 225 226
				en, source, strerror(en));
		free(buffer);
		(void)close(outfd);
		(void)unlink(destination);
		return RET_ERRNO(en);
	}
227
	while ((readbytes = read(infd, buffer, bufsize)) > 0) {
Bernhard Link's avatar
Bernhard Link committed
228 229
		const char *start = buffer;

230 231 232
		if ((off_t)readbytes > length) {
			fprintf(stderr,
"Mismatch of sizes of '%s': files is larger than expected!\n",
Bernhard Link's avatar
Bernhard Link committed
233 234 235
					destination);
			break;
		}
236
		while (readbytes > 0) {
Bernhard Link's avatar
Bernhard Link committed
237 238 239
			ssize_t written;

			written = write(outfd, start, readbytes);
240 241
			if (written > 0) {
				assert (written <= readbytes);
Bernhard Link's avatar
Bernhard Link committed
242 243
				readbytes -= written;
				start += written;
244
			} else if (written < 0) {
Bernhard Link's avatar
Bernhard Link committed
245 246 247 248 249 250 251
				int en = errno;

				(void)close(infd);
				(void)close(outfd);
				(void)unlink(destination);
				free(buffer);

252 253
				fprintf(stderr,
"error %d writing to morgue file %s: %s\n",
Bernhard Link's avatar
Bernhard Link committed
254 255 256 257 258 259
						en, destination, strerror(en));
				return RET_ERRNO(en);
			}
		}
	}
	free(buffer);
260
	if (readbytes == 0) {
Bernhard Link's avatar
Bernhard Link committed
261
		err = close(infd);
262
		if (err != 0)
Bernhard Link's avatar
Bernhard Link committed
263 264 265
			readbytes = -1;
		infd = -1;
	}
266
	if (readbytes < 0) {
Bernhard Link's avatar
Bernhard Link committed
267
		int en = errno;
268 269
		fprintf(stderr,
"error %d reading file %s to be copied into the morgue: %s\n",
Bernhard Link's avatar
Bernhard Link committed
270
				en, source, strerror(en));
271
		if (infd >= 0)
Bernhard Link's avatar
Bernhard Link committed
272 273 274 275 276
			(void)close(infd);
		(void)close(outfd);
		(void)unlink(destination);
		return RET_ERRNO(en);
	}
277
	if (infd >= 0)
Bernhard Link's avatar
Bernhard Link committed
278 279
		(void)close(infd);
	err = close(outfd);
280
	if (err != 0) {
Bernhard Link's avatar
Bernhard Link committed
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
		int en = errno;

		fprintf(stderr, "error %d writing to morgue file %s: %s\n",
				en, destination, strerror(en));
		(void)unlink(destination);
		return RET_ERRNO(en);
	}
	return RET_OK;
}

static inline retvalue morgue_name(const char *filekey, char **name_p, int *fd_p) {
	const char *name = dirs_basename(filekey);
	char *firsttry = calc_dirconcat(global.morguedir, name);
	int fd, en, number;
	retvalue r;

297
	if (FAILEDTOALLOC(firsttry))
Bernhard Link's avatar
Bernhard Link committed
298 299 300
		return RET_ERROR_OOM;

	fd = open(firsttry, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY, 0666);
301 302
	if (fd >= 0) {
		assert (fd > 2);
Bernhard Link's avatar
Bernhard Link committed
303 304 305 306 307
		*name_p = firsttry;
		*fd_p = fd;
		return RET_OK;
	}
	en = errno;
308
	if (en == ENOENT) {
Bernhard Link's avatar
Bernhard Link committed
309
		r = dirs_make_recursive(global.morguedir);
310
		if (RET_WAS_ERROR(r)) {
Bernhard Link's avatar
Bernhard Link committed
311 312 313 314 315
			free(firsttry);
			return r;
		}
		/* try again */
		fd = open(firsttry, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY, 0666);
316 317
		if (fd >= 0) {
			assert (fd > 2);
Bernhard Link's avatar
Bernhard Link committed
318 319 320 321 322 323
			*name_p = firsttry;
			*fd_p = fd;
			return RET_OK;
		}
		en = errno;
	}
324
	if (en != EEXIST) {
Bernhard Link's avatar
Bernhard Link committed
325 326 327 328 329 330
		fprintf(stderr, "error %d creating morgue-file %s: %s\n",
				en, firsttry, strerror(en));
		free(firsttry);
		return RET_ERRNO(en);
	}
	/* file exists, try names with -number appended: */
331
	for (number = 1 ; number < 1000 ; number++) {
Bernhard Link's avatar
Bernhard Link committed
332 333
		char *try = mprintf("%s-%d", firsttry, number);

334
		if (FAILEDTOALLOC(try)) {
Bernhard Link's avatar
Bernhard Link committed
335 336 337 338
			free(firsttry);
			return RET_ERROR_OOM;
		}
		fd = open(try, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY, 0666);
339 340
		if (fd >= 0) {
			assert (fd > 2);
Bernhard Link's avatar
Bernhard Link committed
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
			free(firsttry);
			*name_p = try;
			*fd_p = fd;
			return RET_OK;
		}
		free(try);
	}
	free(firsttry);
	fprintf(stderr, "Could not create a new file '%s' in morguedir '%s'!\n",
			name, global.morguedir);
	return RET_ERROR;
}

/* if file not there, return RET_NOTHING */
static inline retvalue movefiletomorgue(const char *filekey, const char *filename, bool new) {
	char *morguefilename = NULL;
	int err;
	retvalue r;

360
	if (!new && global.morguedir != NULL) {
Bernhard Link's avatar
Bernhard Link committed
361 362 363
		int morguefd = -1;
		struct stat s;

364 365 366
		r = morgue_name(filekey, &morguefilename, &morguefd);
		assert (r != RET_NOTHING);
		if (RET_WAS_ERROR(r))
Bernhard Link's avatar
Bernhard Link committed
367 368
			return r;
		err = lstat(filename, &s);
369
		if (err != 0) {
Bernhard Link's avatar
Bernhard Link committed
370
			int en = errno;
371
			if (errno == ENOENT) {
Bernhard Link's avatar
Bernhard Link committed
372 373 374 375 376
				(void)close(morguefd);
				(void)unlink(morguefilename);
				free(morguefilename);
				return RET_NOTHING;
			}
377 378
			fprintf(stderr,
"error %d looking at file %s to be moved into the morgue: %s\n",
Bernhard Link's avatar
Bernhard Link committed
379 380 381 382 383 384
					en, filename, strerror(en));
			(void)close(morguefd);
			(void)unlink(morguefilename);
			free(morguefilename);
			return RET_ERRNO(en);
		}
385
		if (S_ISLNK(s.st_mode)) {
Bernhard Link's avatar
Bernhard Link committed
386 387 388 389 390
			/* no need to copy a symbolic link: */
			(void)close(morguefd);
			(void)unlink(morguefilename);
			free(morguefilename);
			morguefilename = NULL;
391
		} else if (S_ISREG(s.st_mode)) {
Bernhard Link's avatar
Bernhard Link committed
392
			err = rename(filename, morguefilename);
393
			if (err == 0) {
Bernhard Link's avatar
Bernhard Link committed
394 395 396 397 398 399
				(void)close(morguefd);
				free(morguefilename);
				return RET_OK;
			}
			r = copyfile(filename, morguefilename, morguefd,
				       s.st_size);
400
			if (RET_WAS_ERROR(r)) {
Bernhard Link's avatar
Bernhard Link committed
401 402 403 404
				free(morguefilename);
				return r;
			}
		} else {
405 406 407
			fprintf(stderr,
"Strange (non-regular) file '%s' in the pool.\nPlease delete manually!\n",
					filename);
Bernhard Link's avatar
Bernhard Link committed
408 409 410 411 412 413 414 415
			(void)close(morguefd);
			(void)unlink(morguefilename);
			free(morguefilename);
			morguefilename = NULL;
			return RET_ERROR;
		}
	}
	err = unlink(filename);
416
	if (err != 0) {
Bernhard Link's avatar
Bernhard Link committed
417
		int en = errno;
418
		if (errno == ENOENT)
Bernhard Link's avatar
Bernhard Link committed
419 420 421
			return RET_NOTHING;
		fprintf(stderr, "error %d while unlinking %s: %s\n",
			en, filename, strerror(en));
422
		if (morguefilename != NULL) {
Bernhard Link's avatar
Bernhard Link committed
423 424 425 426 427 428 429 430 431 432 433 434 435
			(void)unlink(morguefilename);
			free(morguefilename);
		}
		return RET_ERRNO(en);
	} else {
		free(morguefilename);
		return RET_OK;
	}
}

/* delete the file and possible parent directories,
 * if not new and morguedir set, first move/copy there */
static retvalue deletepoolfile(const char *filekey, bool new) {
436 437 438
	char *filename;
	retvalue r;

439
	if (interrupted())
440
		return RET_ERROR_INTERRUPTED;
Bernhard Link's avatar
Bernhard Link committed
441 442
	if (!new)
		outhook_send("POOLDELETE", filekey, NULL, NULL);
443
	filename = files_calcfullfilename(filekey);
444
	if (FAILEDTOALLOC(filename))
445
		return RET_ERROR_OOM;
Bernhard Link's avatar
Bernhard Link committed
446 447
	/* move to morgue or simply delete: */
	r = movefiletomorgue(filekey, filename, new);
448
	if (r == RET_NOTHING) {
Bernhard Link's avatar
Bernhard Link committed
449 450
		fprintf(stderr, "%s not found, forgetting anyway\n", filename);
	}
451
	if (!RET_IS_OK(r)) {
Bernhard Link's avatar
Bernhard Link committed
452 453 454
		free(filename);
		return r;
	}
455
	if (!global.keepdirectories) {
456 457 458 459
		/* try to delete parent directories, until one gives
		 * errors (hopefully because it still contains files) */
		size_t fixedpartlen = strlen(global.outdir);
		char *p;
Bernhard Link's avatar
Bernhard Link committed
460
		int err, en;
461

462
		while ((p = strrchr(filename, '/')) != NULL) {
463
			/* do not try to remove parts of the mirrordir */
464
			if ((size_t)(p-filename) <= fixedpartlen+1)
465 466 467 468 469 470
				break;
			*p ='\0';
			/* try to rmdir the directory, this will
			 * fail if there are still other files or directories
			 * in it: */
			err = rmdir(filename);
471 472 473 474 475
			if (err == 0) {
				if (verbose > 1) {
					printf(
"removed now empty directory %s\n",
							filename);
476 477 478
				}
			} else {
				en = errno;
479
				if (en != ENOTEMPTY) {
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
					//TODO: check here if only some
					//other error was first and it
					//is not empty so we do not have
					//to remove it anyway...
					fprintf(stderr,
"ignoring error %d trying to rmdir %s: %s\n", en, filename, strerror(en));
				}
				/* parent directories will contain this one
				 * thus not be empty, in other words:
				 * everything's done */
				break;
			}
		}

	}
	free(filename);
	return RET_OK;
}


500
retvalue pool_delete(const char *filekey) {
501 502
	retvalue r;

503 504
	if (verbose >= 1)
		printf("deleting and forgetting %s\n", filekey);
505

Bernhard Link's avatar
Bernhard Link committed
506
	r = deletepoolfile(filekey, false);
507
	if (RET_WAS_ERROR(r))
508 509
		return r;

510
	return files_remove(filekey);
511 512 513 514 515 516 517 518 519 520
}

/* called from files_remove: */
retvalue pool_markdeleted(const char *filekey) {
	pool_havedeleted = true;
	return remember_filekey(filekey, pl_DELETED, ~pl_UNREFERENCED);
};

/* libc's twalk misses a callback_data pointer, so we need some temporary
 * global variables: */
521
static retvalue result;
522
static bool first, onlycount;
523 524 525
static long woulddelete_count;
static component_t current_component;
static const char *sourcename = NULL;
526

527
static void removeifunreferenced(const void *nodep, const VISIT which, UNUSED(const int depth)) {
528
	char *node; const char *filekey;
529 530
	retvalue r;

531
	if (which != leaf && which != postorder)
532 533
		return;

534
	if (interrupted())
535 536
		return;

537
	node = *(char **)nodep;
538
	filekey = node + 1;
539
	if ((*node & pl_UNREFERENCED) == 0)
540
		return;
541
	r = references_isused(filekey);
542
	if (r != RET_NOTHING)
543 544
		return;

545
	if (onlycount) {
546
		woulddelete_count++;
547 548 549
		return;
	}

550
	if (verbose >= 0 && first) {
551 552 553
		printf("Deleting files no longer referenced...\n");
		first = false;
	}
554
	if (verbose >= 1)
555
		printf("deleting and forgetting %s\n", filekey);
Bernhard Link's avatar
Bernhard Link committed
556
	r = deletepoolfile(filekey, (*node & pl_ADDED) != 0);
557
	RET_UPDATE(result, r);
558
	if (!RET_WAS_ERROR(r)) {
559
		r = files_removesilent(filekey);
560
		RET_UPDATE(result, r);
561
		if (!RET_WAS_ERROR(r))
562
			*node &= ~pl_UNREFERENCED;
563
		if (RET_IS_OK(r))
564 565
			*node |= pl_DELETED;
	}
566 567
}

568

569
static void removeifunreferenced2(const void *nodep, const VISIT which, UNUSED(const int depth)) {
570
	char *node;
571 572 573
	char *filekey;
	retvalue r;

574
	if (which != leaf && which != postorder)
575 576
		return;

577
	if (interrupted())
578 579
		return;

580
	node = *(char **)nodep;
581
	if ((*node & pl_UNREFERENCED) == 0)
582 583
		return;
	filekey = calc_filekey(current_component, sourcename, node + 1);
584
	r = references_isused(filekey);
585
	if (r != RET_NOTHING) {
586 587 588
		free(filekey);
		return;
	}
589
	if (onlycount) {
590
		woulddelete_count++;
591 592 593
		free(filekey);
		return;
	}
594
	if (verbose >= 0 && first) {
595 596 597
		printf("Deleting files no longer referenced...\n");
		first = false;
	}
598
	if (verbose >= 1)
599
		printf("deleting and forgetting %s\n", filekey);
Bernhard Link's avatar
Bernhard Link committed
600
	r = deletepoolfile(filekey, (*node & pl_ADDED) != 0);
601
	RET_UPDATE(result, r);
602
	if (!RET_WAS_ERROR(r)) {
603
		r = files_removesilent(filekey);
604
		RET_UPDATE(result, r);
605
		if (!RET_WAS_ERROR(r))
606
			*node &= ~pl_UNREFERENCED;
607
		if (RET_IS_OK(r))
608
			*node |= pl_DELETED;
609 610 611 612 613
	}
	RET_UPDATE(result, r);
	free(filekey);
}

614
static void removeunreferenced_from_component(const void *nodep, const VISIT which, UNUSED(const int depth)) {
615 616
	struct source_node *node;

617
	if (which != leaf && which != postorder)
618 619
		return;

620
	if (interrupted())
621 622 623 624 625 626 627
		return;

	node = *(struct source_node **)nodep;
	sourcename = node->sourcename;
	twalk(node->file_changes, removeifunreferenced2);
}

628
retvalue pool_removeunreferenced(bool delete) {
629 630
	component_t c;

631
	if (!delete && verbose <= 0)
632 633
		return RET_NOTHING;

634 635
	result = RET_NOTHING;
	first = true;
636 637
	onlycount = !delete;
	woulddelete_count = 0;
638 639
	for (c = 1 ; c <= reserved_components ; c++) {
		assert (file_changes_per_component != NULL);
640 641 642 643
		current_component = c;
		twalk(file_changes_per_component[c],
				removeunreferenced_from_component);
	}
644
	twalk(legacy_file_changes, removeifunreferenced);
645
	if (interrupted())
646
		result = RET_ERROR_INTERRUPTED;
647
	if (!delete && woulddelete_count > 0) {
648 649 650 651 652
		printf(
"%lu files lost their last reference.\n"
"(dumpunreferenced lists such files, use deleteunreferenced to delete them.)\n",
			woulddelete_count);
	}
653 654
	return result;
}
655

656
static void removeunusednew(const void *nodep, const VISIT which, UNUSED(const int depth)) {
657 658 659
	char *node; const char *filekey;
	retvalue r;

660
	if (which != leaf && which != postorder)
661 662
		return;

663
	if (interrupted())
664 665 666 667
		return;

	node = *(char **)nodep;
	filekey = node + 1;
Bernhard Link's avatar
Bernhard Link committed
668
	/* only look at newly added and not already deleted */
669
	if ((*node & (pl_ADDED|pl_DELETED)) != pl_ADDED)
670
		return;
671
	r = references_isused(filekey);
672
	if (r != RET_NOTHING)
673 674
		return;

675
	if (onlycount) {
676 677 678 679
		woulddelete_count++;
		return;
	}

680 681 682 683
	if (verbose >= 0 && first) {
		printf(
"Deleting files just added to the pool but not used.\n"
"(to avoid use --keepunusednewfiles next time)\n");
684 685
		first = false;
	}
686
	if (verbose >= 1)
687
		printf("deleting and forgetting %s\n", filekey);
Bernhard Link's avatar
Bernhard Link committed
688
	r = deletepoolfile(filekey, true);
689
	RET_UPDATE(result, r);
690
	if (!RET_WAS_ERROR(r)) {
691
		r = files_removesilent(filekey);
692 693 694
		RET_UPDATE(result, r);
		/* don't remove pl_ADDED here, otherwise the hook
		 * script will be told to remove something not added */
695
		if (!RET_WAS_ERROR(r))
696
			*node &= ~pl_UNREFERENCED;
697
		if (RET_IS_OK(r))
698 699 700 701 702
			*node |= pl_DELETED;
	}
}


703
static void removeunusednew2(const void *nodep, const VISIT which, UNUSED(const int depth)) {
704 705 706 707
	char *node;
	char *filekey;
	retvalue r;

708
	if (which != leaf && which != postorder)
709 710
		return;

711
	if (interrupted())
712 713 714
		return;

	node = *(char **)nodep;
Bernhard Link's avatar
Bernhard Link committed
715
	/* only look at newly added and not already deleted */
716
	if ((*node & (pl_ADDED|pl_DELETED)) != pl_ADDED)
717 718
		return;
	filekey = calc_filekey(current_component, sourcename, node + 1);
719
	r = references_isused(filekey);
720
	if (r != RET_NOTHING) {
721 722 723
		free(filekey);
		return;
	}
724
	if (onlycount) {
725 726 727 728
		woulddelete_count++;
		free(filekey);
		return;
	}
729 730 731 732
	if (verbose >= 0 && first) {
		printf(
"Deleting files just added to the pool but not used.\n"
"(to avoid use --keepunusednewfiles next time)\n");
733 734
		first = false;
	}
735
	if (verbose >= 1)
736
		printf("deleting and forgetting %s\n", filekey);
Bernhard Link's avatar
Bernhard Link committed
737
	r = deletepoolfile(filekey, true);
738
	RET_UPDATE(result, r);
739
	if (!RET_WAS_ERROR(r)) {
740
		r = files_removesilent(filekey);
741 742 743
		RET_UPDATE(result, r);
		/* don't remove pl_ADDED here, otherwise the hook
		 * script will be told to remove something not added */
744
		if (!RET_WAS_ERROR(r))
745
			*node &= ~pl_UNREFERENCED;
746
		if (RET_IS_OK(r))
747 748 749 750 751 752
			*node |= pl_DELETED;
	}
	RET_UPDATE(result, r);
	free(filekey);
}

753
static void removeunusednew_from_component(const void *nodep, const VISIT which, UNUSED(const int depth)) {
754 755
	struct source_node *node;

756
	if (which != leaf && which != postorder)
757 758
		return;

759
	if (interrupted())
760 761 762 763 764 765 766
		return;

	node = *(struct source_node **)nodep;
	sourcename = node->sourcename;
	twalk(node->file_changes, removeunusednew2);
}

767
void pool_tidyadded(bool delete) {
768 769
	component_t c;

770
	if (!delete && verbose < 0)
771 772 773
		return;

	result = RET_NOTHING;
774 775 776
	first = true;
	onlycount = !delete;
	woulddelete_count = 0;
777 778
	for (c = 1 ; c <= reserved_components ; c++) {
		assert (file_changes_per_component != NULL);
779 780
		current_component = c;
		twalk(file_changes_per_component[c],
781
				removeunusednew_from_component);
782
	}
783 784
	// this should not really happen at all, but better safe then sorry:
	twalk(legacy_file_changes, removeunusednew);
785
	if (!delete && woulddelete_count > 0) {
786
		printf(
787 788 789
"%lu files were added but not used.\n"
"The next deleteunreferenced call will delete them.\n",
			woulddelete_count);
790 791
	}
	return;
792

793
}
794

Bernhard Link's avatar
Bernhard Link committed
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
static void reportnewlegacyfiles(const void *nodep, const VISIT which, UNUSED(const int depth)) {
	char *node;

	if (which != leaf && which != postorder)
		return;

	node = *(char **)nodep;
	/* only look at newly added and not already deleted */
	if ((*node & (pl_ADDED|pl_DELETED)) != pl_ADDED)
		return;
	outhook_sendpool(atom_unknown, NULL, node + 1);
}


static void reportnewproperfiles(const void *nodep, const VISIT which, UNUSED(const int depth)) {
	char *node;

	if (which != leaf && which != postorder)
		return;

	node = *(char **)nodep;
	/* only look at newly added and not already deleted */
	if ((*node & (pl_ADDED|pl_DELETED)) != pl_ADDED)
		return;
	outhook_sendpool(current_component, sourcename, node + 1);
}

static void reportnewfiles(const void *nodep, const VISIT which, UNUSED(const int depth)) {
	struct source_node *node;

	if (which != leaf && which != postorder)
		return;

	node = *(struct source_node **)nodep;
	sourcename = node->sourcename;
	twalk(node->file_changes, reportnewproperfiles);
}

void pool_sendnewfiles(void) {
	component_t c;

	for (c = 1 ; c <= reserved_components ; c++) {
		assert (file_changes_per_component != NULL);
		current_component = c;
		twalk(file_changes_per_component[c],
				reportnewfiles);
	}
	twalk(legacy_file_changes, reportnewlegacyfiles);
	return;

}

847 848 849 850 851 852 853 854 855 856 857 858 859
#ifdef HAVE_TDESTROY
static void sourcename_free(void *n) {
	struct source_node *node = n;

	tdestroy(node->file_changes, free);
	free(node);
}
#endif

void pool_free(void) {
#ifdef HAVE_TDESTROY
	component_t c;

860
	for (c = 1 ; c <= reserved_components ; c++) {
861 862 863 864 865 866 867 868 869
		tdestroy(file_changes_per_component[c], sourcename_free);
	}
	reserved_components = 0;
	free(file_changes_per_component);
	file_changes_per_component = NULL;
	tdestroy(legacy_file_changes, free);
	legacy_file_changes = NULL;
#endif
}