1 /*
2  *  conflict.c
3  *
4  *  Copyright (c) 2006-2018 Pacman Development Team <pacman-dev@archlinux.org>
5  *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
6  *  Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
7  *  Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org>
8  *  Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org>
9  *  Copyright (c) 2006 by Christian Hamar <krics@linuxforum.hu>
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <limits.h>
29 #include <sys/stat.h>
30 #include <dirent.h>
31 
32 /* libalpm */
33 #include "conflict.h"
34 #include "alpm_list.h"
35 #include "alpm.h"
36 #include "handle.h"
37 #include "trans.h"
38 #include "util.h"
39 #include "log.h"
40 #include "deps.h"
41 #include "filelist.h"
42 
43 /**
44  * @brief Creates a new conflict.
45  */
conflict_new(alpm_pkg_t * pkg1,alpm_pkg_t * pkg2,alpm_depend_t * reason)46 static alpm_conflict_t *conflict_new(alpm_pkg_t *pkg1, alpm_pkg_t *pkg2,
47 		alpm_depend_t *reason)
48 {
49 	alpm_conflict_t *conflict;
50 
51 	CALLOC(conflict, 1, sizeof(alpm_conflict_t), return NULL);
52 
53 	conflict->package1_hash = pkg1->name_hash;
54 	conflict->package2_hash = pkg2->name_hash;
55 	STRDUP(conflict->package1, pkg1->name, goto error);
56 	STRDUP(conflict->package2, pkg2->name, goto error);
57 	conflict->reason = reason;
58 
59 	return conflict;
60 
61 error:
62 	alpm_conflict_free(conflict);
63 	return NULL;
64 }
65 
66 /**
67  * @brief Free a conflict and its members.
68  */
alpm_conflict_free(alpm_conflict_t * conflict)69 void SYMEXPORT alpm_conflict_free(alpm_conflict_t *conflict)
70 {
71 	ASSERT(conflict != NULL, return);
72 	FREE(conflict->package2);
73 	FREE(conflict->package1);
74 	FREE(conflict);
75 }
76 
77 /**
78  * @brief Creates a copy of a conflict.
79  */
_alpm_conflict_dup(const alpm_conflict_t * conflict)80 alpm_conflict_t *_alpm_conflict_dup(const alpm_conflict_t *conflict)
81 {
82 	alpm_conflict_t *newconflict;
83 	CALLOC(newconflict, 1, sizeof(alpm_conflict_t), return NULL);
84 
85 	newconflict->package1_hash = conflict->package1_hash;
86 	newconflict->package2_hash = conflict->package2_hash;
87 	STRDUP(newconflict->package1, conflict->package1, goto error);
88 	STRDUP(newconflict->package2, conflict->package2, goto error);
89 	newconflict->reason = conflict->reason;
90 
91 	return newconflict;
92 
93 error:
94 	alpm_conflict_free(newconflict);
95 	return NULL;
96 }
97 
98 /**
99  * @brief Searches for a conflict in a list.
100  *
101  * @param needle conflict to search for
102  * @param haystack list of conflicts to search
103  *
104  * @return 1 if needle is in haystack, 0 otherwise
105  */
conflict_isin(alpm_conflict_t * needle,alpm_list_t * haystack)106 static int conflict_isin(alpm_conflict_t *needle, alpm_list_t *haystack)
107 {
108 	alpm_list_t *i;
109 	for(i = haystack; i; i = i->next) {
110 		alpm_conflict_t *conflict = i->data;
111 		if(needle->package1_hash == conflict->package1_hash
112 				&& needle->package2_hash == conflict->package2_hash
113 				&& strcmp(needle->package1, conflict->package1) == 0
114 				&& strcmp(needle->package2, conflict->package2) == 0) {
115 			return 1;
116 		}
117 	}
118 
119 	return 0;
120 }
121 
122 /**
123  * @brief Adds the pkg1/pkg2 conflict to the baddeps list.
124  *
125  * @param handle the context handle
126  * @param baddeps list to add conflict to
127  * @param pkg1 first package
128  * @param pkg2 package causing conflict
129  * @param reason reason for this conflict
130  *
131  * @return 0 on success, -1 on error
132  */
add_conflict(alpm_handle_t * handle,alpm_list_t ** baddeps,alpm_pkg_t * pkg1,alpm_pkg_t * pkg2,alpm_depend_t * reason)133 static int add_conflict(alpm_handle_t *handle, alpm_list_t **baddeps,
134 		alpm_pkg_t *pkg1, alpm_pkg_t *pkg2, alpm_depend_t *reason)
135 {
136 	alpm_conflict_t *conflict = conflict_new(pkg1, pkg2, reason);
137 	if(!conflict) {
138 		return -1;
139 	}
140 	if(!conflict_isin(conflict, *baddeps)) {
141 		char *conflict_str = alpm_dep_compute_string(reason);
142 		*baddeps = alpm_list_add(*baddeps, conflict);
143 		_alpm_log(handle, ALPM_LOG_DEBUG, "package %s conflicts with %s (by %s)\n",
144 				pkg1->name, pkg2->name, conflict_str);
145 		free(conflict_str);
146 	} else {
147 		alpm_conflict_free(conflict);
148 	}
149 	return 0;
150 }
151 
152 /**
153  * @brief Check if packages from list1 conflict with packages from list2.
154  *
155  * @details This looks at the conflicts fields of all packages from list1, and
156  * sees if they match packages from list2. If a conflict (pkg1, pkg2) is found,
157  * it is added to the baddeps list in this order if order >= 0, or reverse
158  * order (pkg2,pkg1) otherwise.
159  *
160  * @param handle the context handle
161  * @param list1 first list of packages
162  * @param list2 second list of packages
163  * @param baddeps list to store conflicts
164  * @param order if >= 0 the conflict order is preserved, if < 0 it's reversed
165  */
check_conflict(alpm_handle_t * handle,alpm_list_t * list1,alpm_list_t * list2,alpm_list_t ** baddeps,int order)166 static void check_conflict(alpm_handle_t *handle,
167 		alpm_list_t *list1, alpm_list_t *list2,
168 		alpm_list_t **baddeps, int order)
169 {
170 	alpm_list_t *i;
171 
172 	if(!baddeps) {
173 		return;
174 	}
175 	for(i = list1; i; i = i->next) {
176 		alpm_pkg_t *pkg1 = i->data;
177 		alpm_list_t *j;
178 
179 		for(j = alpm_pkg_get_conflicts(pkg1); j; j = j->next) {
180 			alpm_depend_t *conflict = j->data;
181 			alpm_list_t *k;
182 
183 			for(k = list2; k; k = k->next) {
184 				alpm_pkg_t *pkg2 = k->data;
185 
186 				if(pkg1->name_hash == pkg2->name_hash
187 						&& strcmp(pkg1->name, pkg2->name) == 0) {
188 					/* skip the package we're currently processing */
189 					continue;
190 				}
191 
192 				if(_alpm_depcmp(pkg2, conflict)) {
193 					if(order >= 0) {
194 						add_conflict(handle, baddeps, pkg1, pkg2, conflict);
195 					} else {
196 						add_conflict(handle, baddeps, pkg2, pkg1, conflict);
197 					}
198 				}
199 			}
200 		}
201 	}
202 }
203 
204 /**
205  * @brief Check for inter-conflicts in a list of packages.
206  *
207  * @param handle the context handle
208  * @param packages list of packages to check
209  *
210  * @return list of conflicts
211  */
_alpm_innerconflicts(alpm_handle_t * handle,alpm_list_t * packages)212 alpm_list_t *_alpm_innerconflicts(alpm_handle_t *handle, alpm_list_t *packages)
213 {
214 	alpm_list_t *baddeps = NULL;
215 
216 	_alpm_log(handle, ALPM_LOG_DEBUG, "check targets vs targets\n");
217 	check_conflict(handle, packages, packages, &baddeps, 0);
218 
219 	return baddeps;
220 }
221 
222 /**
223  * @brief Returns a list of conflicts between a db and a list of packages.
224  */
_alpm_outerconflicts(alpm_db_t * db,alpm_list_t * packages)225 alpm_list_t *_alpm_outerconflicts(alpm_db_t *db, alpm_list_t *packages)
226 {
227 	alpm_list_t *baddeps = NULL;
228 
229 	if(db == NULL) {
230 		return NULL;
231 	}
232 
233 	alpm_list_t *dblist = alpm_list_diff(_alpm_db_get_pkgcache(db),
234 			packages, _alpm_pkg_cmp);
235 
236 	/* two checks to be done here for conflicts */
237 	_alpm_log(db->handle, ALPM_LOG_DEBUG, "check targets vs db\n");
238 	check_conflict(db->handle, packages, dblist, &baddeps, 1);
239 	_alpm_log(db->handle, ALPM_LOG_DEBUG, "check db vs targets\n");
240 	check_conflict(db->handle, dblist, packages, &baddeps, -1);
241 
242 	alpm_list_free(dblist);
243 	return baddeps;
244 }
245 
246 /**
247  * @brief Check the package conflicts in a database
248  *
249  * @param handle the context handle
250  * @param pkglist the list of packages to check
251  *
252  * @return an alpm_list_t of alpm_conflict_t
253  */
alpm_checkconflicts(alpm_handle_t * handle,alpm_list_t * pkglist)254 alpm_list_t SYMEXPORT *alpm_checkconflicts(alpm_handle_t *handle,
255 		alpm_list_t *pkglist)
256 {
257 	CHECK_HANDLE(handle, return NULL);
258 	return _alpm_innerconflicts(handle, pkglist);
259 }
260 
261 /**
262  * @brief Creates and adds a file conflict to a conflict list.
263  *
264  * @param handle the context handle
265  * @param conflicts the list of conflicts to append to
266  * @param filestr the conflicting file path
267  * @param pkg1 package that wishes to install the file
268  * @param pkg2 package that currently owns the file, or NULL if unowned
269  *
270  * @return the updated conflict list
271  */
add_fileconflict(alpm_handle_t * handle,alpm_list_t * conflicts,const char * filestr,alpm_pkg_t * pkg1,alpm_pkg_t * pkg2)272 static alpm_list_t *add_fileconflict(alpm_handle_t *handle,
273 		alpm_list_t *conflicts, const char *filestr,
274 		alpm_pkg_t *pkg1, alpm_pkg_t *pkg2)
275 {
276 	alpm_fileconflict_t *conflict;
277 	CALLOC(conflict, 1, sizeof(alpm_fileconflict_t), goto error);
278 
279 	STRDUP(conflict->target, pkg1->name, goto error);
280 	STRDUP(conflict->file, filestr, goto error);
281 	if(!pkg2) {
282 		conflict->type = ALPM_FILECONFLICT_FILESYSTEM;
283 		STRDUP(conflict->ctarget, "", goto error);
284 	} else if(pkg2->origin == ALPM_PKG_FROM_LOCALDB) {
285 		conflict->type = ALPM_FILECONFLICT_FILESYSTEM;
286 		STRDUP(conflict->ctarget, pkg2->name, goto error);
287 	} else {
288 		conflict->type = ALPM_FILECONFLICT_TARGET;
289 		STRDUP(conflict->ctarget, pkg2->name, goto error);
290 	}
291 
292 	conflicts = alpm_list_add(conflicts, conflict);
293 	_alpm_log(handle, ALPM_LOG_DEBUG, "found file conflict %s, packages %s and %s\n",
294 	          filestr, pkg1->name, pkg2 ? pkg2->name : "(filesystem)");
295 
296 	return conflicts;
297 
298 error:
299 	alpm_fileconflict_free(conflict);
300 	RET_ERR(handle, ALPM_ERR_MEMORY, conflicts);
301 }
302 
303 /**
304  * @brief Frees a conflict and its members.
305  */
alpm_fileconflict_free(alpm_fileconflict_t * conflict)306 void SYMEXPORT alpm_fileconflict_free(alpm_fileconflict_t *conflict)
307 {
308 	ASSERT(conflict != NULL, return);
309 	FREE(conflict->ctarget);
310 	FREE(conflict->file);
311 	FREE(conflict->target);
312 	FREE(conflict);
313 }
314 
315 /**
316  * @brief Recursively checks if a set of packages own all subdirectories and
317  * files in a directory.
318  *
319  * @param handle the context handle
320  * @param dirpath path of the directory to check
321  * @param pkgs packages being checked against
322  *
323  * @return 1 if a package owns all subdirectories and files, 0 otherwise
324  */
dir_belongsto_pkgs(alpm_handle_t * handle,const char * dirpath,alpm_list_t * pkgs)325 static int dir_belongsto_pkgs(alpm_handle_t *handle, const char *dirpath,
326 		alpm_list_t *pkgs)
327 {
328 	char path[PATH_MAX], full_path[PATH_MAX];
329 	DIR *dir;
330 	struct dirent *ent = NULL;
331 
332 	snprintf(full_path, PATH_MAX, "%s%s", handle->root, dirpath);
333 	dir = opendir(full_path);
334 	if(dir == NULL) {
335 		return 0;
336 	}
337 
338 	while((ent = readdir(dir)) != NULL) {
339 		const char *name = ent->d_name;
340 		int owned = 0, is_dir = 0;
341 		alpm_list_t *i;
342 		struct stat sbuf;
343 
344 		if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
345 			continue;
346 		}
347 
348 		snprintf(full_path, PATH_MAX, "%s%s%s", handle->root, dirpath, name);
349 
350 		if(lstat(full_path, &sbuf) != 0) {
351 			_alpm_log(handle, ALPM_LOG_DEBUG, "could not stat %s\n", full_path);
352 			closedir(dir);
353 			return 0;
354 		}
355 		is_dir = S_ISDIR(sbuf.st_mode);
356 
357 		snprintf(path, PATH_MAX, "%s%s%s", dirpath, name, is_dir ? "/" : "");
358 
359 		for(i = pkgs; i && !owned; i = i->next) {
360 			if(alpm_filelist_contains(alpm_pkg_get_files(i->data), path)) {
361 				owned = 1;
362 			}
363 		}
364 
365 		if(owned && is_dir) {
366 			owned = dir_belongsto_pkgs(handle, path, pkgs);
367 		}
368 
369 		if(!owned) {
370 			closedir(dir);
371 			_alpm_log(handle, ALPM_LOG_DEBUG,
372 					"unowned file %s found in directory\n", path);
373 			return 0;
374 		}
375 	}
376 	closedir(dir);
377 	return 1;
378 }
379 
alpm_db_find_file_owners(alpm_db_t * db,const char * path)380 static alpm_list_t *alpm_db_find_file_owners(alpm_db_t* db, const char *path)
381 {
382 	alpm_list_t *i, *owners = NULL;
383 	for(i = alpm_db_get_pkgcache(db); i; i = i->next) {
384 		if(alpm_filelist_contains(alpm_pkg_get_files(i->data), path)) {
385 			owners = alpm_list_add(owners, i->data);
386 		}
387 	}
388 	return owners;
389 }
390 
_alpm_find_file_owner(alpm_handle_t * handle,const char * path)391 static alpm_pkg_t *_alpm_find_file_owner(alpm_handle_t *handle, const char *path)
392 {
393 	alpm_list_t *i;
394 	for(i = alpm_db_get_pkgcache(handle->db_local); i; i = i->next) {
395 		if(alpm_filelist_contains(alpm_pkg_get_files(i->data), path)) {
396 			return i->data;
397 		}
398 	}
399 	return NULL;
400 }
401 
_alpm_can_overwrite_file(alpm_handle_t * handle,const char * path,const char * rootedpath)402 static int _alpm_can_overwrite_file(alpm_handle_t *handle, const char *path, const char *rootedpath)
403 {
404 	return handle->trans->flags & ALPM_TRANS_FLAG_FORCE
405 		|| _alpm_fnmatch_patterns(handle->overwrite_files, path) == 0
406 		|| _alpm_fnmatch_patterns(handle->overwrite_files, rootedpath) == 0;
407 }
408 
409 /**
410  * @brief Find file conflicts that may occur during the transaction.
411  *
412  * @details Performs two checks:
413  *   1. check every target against every target
414  *   2. check every target against the filesystem
415  *
416  * @param handle the context handle
417  * @param upgrade list of packages being installed
418  * @param rem list of packages being removed
419  *
420  * @return list of file conflicts
421  */
_alpm_db_find_fileconflicts(alpm_handle_t * handle,alpm_list_t * upgrade,alpm_list_t * rem)422 alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle,
423 		alpm_list_t *upgrade, alpm_list_t *rem)
424 {
425 	alpm_list_t *i, *conflicts = NULL;
426 	size_t numtargs = alpm_list_count(upgrade);
427 	size_t current;
428 	size_t rootlen;
429 
430 	if(!upgrade) {
431 		return NULL;
432 	}
433 
434 	rootlen = strlen(handle->root);
435 
436 	/* TODO this whole function needs a huge change, which hopefully will
437 	 * be possible with real transactions. Right now we only do half as much
438 	 * here as we do when we actually extract files in add.c with our 12
439 	 * different cases. */
440 	for(current = 0, i = upgrade; i; i = i->next, current++) {
441 		alpm_pkg_t *p1 = i->data;
442 		alpm_list_t *j;
443 		alpm_list_t *newfiles = NULL;
444 		alpm_pkg_t *dbpkg;
445 
446 		int percent = (current * 100) / numtargs;
447 		PROGRESS(handle, ALPM_PROGRESS_CONFLICTS_START, "", percent,
448 		         numtargs, current);
449 
450 		/* CHECK 1: check every target against every target */
451 		_alpm_log(handle, ALPM_LOG_DEBUG, "searching for file conflicts: %s\n",
452 				p1->name);
453 		for(j = i->next; j; j = j->next) {
454 			alpm_list_t *common_files;
455 			alpm_pkg_t *p2 = j->data;
456 
457 			alpm_filelist_t *p1_files = alpm_pkg_get_files(p1);
458 			alpm_filelist_t *p2_files = alpm_pkg_get_files(p2);
459 
460 			common_files = _alpm_filelist_intersection(p1_files, p2_files);
461 
462 			if(common_files) {
463 				alpm_list_t *k;
464 				char path[PATH_MAX];
465 				for(k = common_files; k; k = k->next) {
466 					char *filename = k->data;
467 					snprintf(path, PATH_MAX, "%s%s", handle->root, filename);
468 
469 					/* can skip file-file conflicts when forced *
470 					 * checking presence in p2_files detects dir-file or file-dir
471 					 * conflicts as the path from p1 is returned */
472 					if(_alpm_can_overwrite_file(handle, filename, path)
473 							&& alpm_filelist_contains(p2_files, filename)) {
474 						_alpm_log(handle, ALPM_LOG_DEBUG,
475 							"%s exists in both '%s' and '%s'\n", filename,
476 							p1->name, p2->name);
477 						_alpm_log(handle, ALPM_LOG_DEBUG,
478 							"file-file conflict being forced\n");
479 						continue;
480 					}
481 
482 					conflicts = add_fileconflict(handle, conflicts, path, p1, p2);
483 					if(handle->pm_errno == ALPM_ERR_MEMORY) {
484 						alpm_list_free_inner(conflicts,
485 								(alpm_list_fn_free) alpm_conflict_free);
486 						alpm_list_free(conflicts);
487 						alpm_list_free(common_files);
488 						return NULL;
489 					}
490 				}
491 				alpm_list_free(common_files);
492 			}
493 		}
494 
495 		/* CHECK 2: check every target against the filesystem */
496 		_alpm_log(handle, ALPM_LOG_DEBUG, "searching for filesystem conflicts: %s\n",
497 				p1->name);
498 		dbpkg = _alpm_db_get_pkgfromcache(handle->db_local, p1->name);
499 
500 		/* Do two different checks here. If the package is currently installed,
501 		 * then only check files that are new in the new package. If the package
502 		 * is not currently installed, then simply stat the whole filelist. Note
503 		 * that the former list needs to be freed while the latter list should NOT
504 		 * be freed. */
505 		if(dbpkg) {
506 			/* older ver of package currently installed */
507 			newfiles = _alpm_filelist_difference(alpm_pkg_get_files(p1),
508 					alpm_pkg_get_files(dbpkg));
509 		} else {
510 			/* no version of package currently installed */
511 			alpm_filelist_t *fl = alpm_pkg_get_files(p1);
512 			size_t filenum;
513 			for(filenum = 0; filenum < fl->count; filenum++) {
514 				newfiles = alpm_list_add(newfiles, fl->files[filenum].name);
515 			}
516 		}
517 
518 		for(j = newfiles; j; j = j->next) {
519 			const char *filestr = j->data;
520 			const char *relative_path;
521 			alpm_list_t *k;
522 			/* have we acted on this conflict? */
523 			int resolved_conflict = 0;
524 			struct stat lsbuf;
525 			char path[PATH_MAX];
526 			size_t pathlen;
527 			int pfile_isdir;
528 
529 			pathlen = snprintf(path, PATH_MAX, "%s%s", handle->root, filestr);
530 			relative_path = path + rootlen;
531 
532 			/* stat the file - if it exists, do some checks */
533 			if(llstat(path, &lsbuf) != 0) {
534 				continue;
535 			}
536 
537 			_alpm_log(handle, ALPM_LOG_DEBUG, "checking possible conflict: %s\n", path);
538 
539 			pfile_isdir = path[pathlen - 1] == '/';
540 			if(pfile_isdir) {
541 				if(S_ISDIR(lsbuf.st_mode)) {
542 					_alpm_log(handle, ALPM_LOG_DEBUG, "file is a directory, not a conflict\n");
543 					continue;
544 				}
545 				/* if we made it to here, we want all subsequent path comparisons to
546 				 * not include the trailing slash. This allows things like file ->
547 				 * directory replacements. */
548 				path[pathlen - 1] = '\0';
549 
550 				/* Check if the directory was a file in dbpkg */
551 				if(alpm_filelist_contains(alpm_pkg_get_files(dbpkg), relative_path)) {
552 					size_t fslen = strlen(filestr);
553 					_alpm_log(handle, ALPM_LOG_DEBUG,
554 							"replacing package file with a directory, not a conflict\n");
555 					resolved_conflict = 1;
556 
557 					/* go ahead and skip any files inside filestr as they will
558 					 * necessarily be resolved by replacing the file with a dir
559 					 * NOTE: afterward, j will point to the last file inside filestr */
560 					for( ; j->next; j = j->next) {
561 						const char *filestr2 = j->next->data;
562 						if(strncmp(filestr, filestr2, fslen) != 0) {
563 							break;
564 						}
565 					}
566 				}
567 			}
568 
569 			/* Check remove list (will we remove the conflicting local file?) */
570 			for(k = rem; k && !resolved_conflict; k = k->next) {
571 				alpm_pkg_t *rempkg = k->data;
572 				if(rempkg && alpm_filelist_contains(alpm_pkg_get_files(rempkg),
573 							relative_path)) {
574 					_alpm_log(handle, ALPM_LOG_DEBUG,
575 							"local file will be removed, not a conflict\n");
576 					resolved_conflict = 1;
577 					if(pfile_isdir) {
578 						/* go ahead and skip any files inside filestr as they will
579 						 * necessarily be resolved by replacing the file with a dir
580 						 * NOTE: afterward, j will point to the last file inside filestr */
581 						size_t fslen = strlen(filestr);
582 						for( ; j->next; j = j->next) {
583 							const char *filestr2 = j->next->data;
584 							if(strncmp(filestr, filestr2, fslen) != 0) {
585 								break;
586 							}
587 						}
588 					}
589 				}
590 			}
591 
592 			/* Look at all the targets to see if file has changed hands */
593 			for(k = upgrade; k && !resolved_conflict; k = k->next) {
594 				alpm_pkg_t *localp2, *p2 = k->data;
595 				if(!p2 || p1 == p2) {
596 					/* skip p1; both p1 and p2 come directly from the upgrade list
597 					 * so they can be compared directly */
598 					continue;
599 				}
600 				localp2 = _alpm_db_get_pkgfromcache(handle->db_local, p2->name);
601 
602 				/* localp2->files will be removed (target conflicts are handled by CHECK 1) */
603 				if(localp2 && alpm_filelist_contains(alpm_pkg_get_files(localp2), relative_path)) {
604 					size_t fslen = strlen(filestr);
605 
606 					/* skip removal of file, but not add. this will prevent a second
607 					 * package from removing the file when it was already installed
608 					 * by its new owner (whether the file is in backup array or not */
609 					handle->trans->skip_remove =
610 						alpm_list_add(handle->trans->skip_remove, strdup(relative_path));
611 					_alpm_log(handle, ALPM_LOG_DEBUG,
612 							"file changed packages, adding to remove skiplist\n");
613 					resolved_conflict = 1;
614 
615 					if(filestr[fslen - 1] == '/') {
616 						/* replacing a file with a directory:
617 						 * go ahead and skip any files inside filestr as they will
618 						 * necessarily be resolved by replacing the file with a dir
619 						 * NOTE: afterward, j will point to the last file inside filestr */
620 						for( ; j->next; j = j->next) {
621 							const char *filestr2 = j->next->data;
622 							if(strncmp(filestr, filestr2, fslen) != 0) {
623 								break;
624 							}
625 						}
626 					}
627 				}
628 			}
629 
630 			/* check if all files of the dir belong to the installed pkg */
631 			if(!resolved_conflict && S_ISDIR(lsbuf.st_mode)) {
632 				alpm_list_t *owners;
633 				char *dir = malloc(strlen(relative_path) + 2);
634 				sprintf(dir, "%s/", relative_path);
635 
636 				owners = alpm_db_find_file_owners(handle->db_local, dir);
637 				if(owners) {
638 					alpm_list_t *pkgs = NULL, *diff;
639 
640 					if(dbpkg) {
641 						pkgs = alpm_list_add(pkgs, dbpkg);
642 					}
643 					pkgs = alpm_list_join(pkgs, alpm_list_copy(rem));
644 					if((diff = alpm_list_diff(owners, pkgs, _alpm_pkg_cmp))) {
645 						/* dir is owned by files we aren't removing */
646 						/* TODO: with better commit ordering, we may be able to check
647 						 * against upgrades as well */
648 						alpm_list_free(diff);
649 					} else {
650 						_alpm_log(handle, ALPM_LOG_DEBUG,
651 								"checking if all files in %s belong to removed packages\n",
652 								dir);
653 						resolved_conflict = dir_belongsto_pkgs(handle, dir, owners);
654 					}
655 					alpm_list_free(pkgs);
656 					alpm_list_free(owners);
657 				}
658 				free(dir);
659 			}
660 
661 			/* is the file unowned and in the backup list of the new package? */
662 			if(!resolved_conflict && _alpm_needbackup(relative_path, p1)) {
663 				alpm_list_t *local_pkgs = _alpm_db_get_pkgcache(handle->db_local);
664 				int found = 0;
665 				for(k = local_pkgs; k && !found; k = k->next) {
666 					if(alpm_filelist_contains(alpm_pkg_get_files(k->data), relative_path)) {
667 							found = 1;
668 					}
669 				}
670 				if(!found) {
671 					_alpm_log(handle, ALPM_LOG_DEBUG,
672 							"file was unowned but in new backup list\n");
673 					resolved_conflict = 1;
674 				}
675 			}
676 
677 			/* skip file-file conflicts when being forced */
678 			if(!S_ISDIR(lsbuf.st_mode)
679 					&& _alpm_can_overwrite_file(handle, filestr, path)) {
680 				_alpm_log(handle, ALPM_LOG_DEBUG,
681 							"conflict with file on filesystem being forced\n");
682 				resolved_conflict = 1;
683 			}
684 
685 			if(!resolved_conflict) {
686 				conflicts = add_fileconflict(handle, conflicts, path, p1,
687 						_alpm_find_file_owner(handle, relative_path));
688 				if(handle->pm_errno == ALPM_ERR_MEMORY) {
689 					alpm_list_free_inner(conflicts,
690 							(alpm_list_fn_free) alpm_conflict_free);
691 					alpm_list_free(conflicts);
692 					alpm_list_free(newfiles);
693 					return NULL;
694 				}
695 			}
696 		}
697 		alpm_list_free(newfiles);
698 	}
699 	PROGRESS(handle, ALPM_PROGRESS_CONFLICTS_START, "", 100,
700 			numtargs, current);
701 
702 	return conflicts;
703 }
704