4 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
5 * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
6 * Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org>
7 * Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org>
8 * Copyright (c) 2006 by Christian Hamar <krics@linuxforum.hu>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
33 #if defined(__APPLE__) || defined(__OpenBSD__)
34 #include <sys/syslimits.h>
41 #include "alpm_list.h"
51 /** See if potential conflict 'name' matches package 'pkg'.
52 * @param target the name of the parent package we're checking
53 * @param depname the name of the dependency we're checking
54 * @param pkg the package to check
55 * @param conflict the name of the possible conflict
56 * @return A depmissing struct indicating the conflict
57 * @note The first two paramters are here to simplify the addition
58 * of new 'depmiss' objects.
60 * TODO WTF is a 'depmissing' doing indicating a conflict??
62 static pmdepmissing_t
*does_conflict(const char *target
, const char *depname
,
63 pmpkg_t
*pkg
, const char *conflict
)
67 /* check the actual package name, easy */
68 if(strcmp(alpm_pkg_get_name(pkg
), conflict
) == 0) {
69 _alpm_log(PM_LOG_DEBUG
, _(" found conflict '%s' : package '%s'"), conflict
, target
);
70 return(_alpm_depmiss_new(target
, PM_DEP_TYPE_CONFLICT
,
71 PM_DEP_MOD_ANY
, depname
, NULL
));
73 /* check what this package provides, harder */
74 for(i
= alpm_pkg_get_provides(pkg
); i
; i
= i
->next
) {
75 const char *provision
= i
->data
;
77 if(strcmp(provision
, conflict
) == 0) {
78 _alpm_log(PM_LOG_DEBUG
, _(" found conflict '%s' : package '%s' provides '%s'"),
79 conflict
, target
, provision
);
80 return(_alpm_depmiss_new(target
, PM_DEP_TYPE_CONFLICT
,
81 PM_DEP_MOD_ANY
, depname
, NULL
));
85 return(NULL
); /* not a conflict */
88 static alpm_list_t
*chk_pkg_vs_db(alpm_list_t
*baddeps
, pmpkg_t
*pkg
, pmdb_t
*db
)
90 pmdepmissing_t
*miss
= NULL
;
94 pkgname
= alpm_pkg_get_name(pkg
);
96 for(i
= alpm_pkg_get_conflicts(pkg
); i
; i
= i
->next
) {
97 const char *conflict
= i
->data
;
99 if(strcmp(pkgname
, conflict
) == 0) {
100 /* a package cannot conflict with itself -- that's just not nice */
101 _alpm_log(PM_LOG_DEBUG
, _("package '%s' conflicts with itself - packaging error"),
106 /* CHECK 1: check targets against database */
107 _alpm_log(PM_LOG_DEBUG
, _("checkconflicts: target '%s' vs db"), pkgname
);
109 for(j
= _alpm_db_get_pkgcache(db
); j
; j
= j
->next
) {
110 pmpkg_t
*dbpkg
= j
->data
;
112 if(strcmp(alpm_pkg_get_name(dbpkg
), pkgname
) == 0) {
113 /* skip the package we're currently processing */
117 miss
= does_conflict(pkgname
, alpm_pkg_get_name(dbpkg
), dbpkg
, conflict
);
118 if(miss
&& !_alpm_depmiss_isin(miss
, baddeps
)) {
119 baddeps
= alpm_list_add(baddeps
, miss
);
128 static alpm_list_t
*chk_pkg_vs_targets(alpm_list_t
*baddeps
,
129 pmpkg_t
*pkg
, pmdb_t
*db
,
130 alpm_list_t
*targets
)
132 pmdepmissing_t
*miss
= NULL
;
136 pkgname
= alpm_pkg_get_name(pkg
);
138 for(i
= alpm_pkg_get_conflicts(pkg
); i
; i
= i
->next
) {
139 const char *conflict
= i
->data
;
141 if(strcmp(pkgname
, conflict
) == 0) {
142 /* a package cannot conflict with itself -- that's just not nice */
143 _alpm_log(PM_LOG_DEBUG
, _("package '%s' conflicts with itself - packaging error"),
148 /* CHECK 2: check targets against targets */
149 _alpm_log(PM_LOG_DEBUG
, _("checkconflicts: target '%s' vs all targets"), pkgname
);
151 for(j
= targets
; j
; j
= j
->next
) {
152 const char *targetname
;
153 pmpkg_t
*target
= j
->data
;
154 targetname
= alpm_pkg_get_name(target
);
156 if(strcmp(targetname
, pkgname
) == 0) {
157 /* skip the package we're currently processing */
161 miss
= does_conflict(pkgname
, targetname
, target
, conflict
);
162 if(miss
&& !_alpm_depmiss_isin(miss
, baddeps
)) {
163 baddeps
= alpm_list_add(baddeps
, miss
);
172 static alpm_list_t
*chk_db_vs_targets(alpm_list_t
*baddeps
, pmpkg_t
*pkg
,
173 pmdb_t
*db
, alpm_list_t
*targets
)
175 pmdepmissing_t
*miss
= NULL
;
179 pkgname
= alpm_pkg_get_name(pkg
);
181 _alpm_log(PM_LOG_DEBUG
, _("checkconflicts: db vs target '%s'"), pkgname
);
183 for(i
= _alpm_db_get_pkgcache(db
); i
; i
= i
->next
) {
184 alpm_list_t
*conflicts
= NULL
;
185 const char *dbpkgname
;
187 pmpkg_t
*dbpkg
= i
->data
;
188 dbpkgname
= alpm_pkg_get_name(dbpkg
);
190 if(strcmp(dbpkgname
, pkgname
) == 0) {
191 /* skip the package we're currently processing */
195 /* is this db package in the targets? if so use the
196 * new package's conflict list to pick up new changes */
197 int use_newconflicts
= 0;
198 for(j
= targets
; j
; j
= j
->next
) {
199 pmpkg_t
*targ
= j
->data
;
200 if(strcmp(alpm_pkg_get_name(targ
), dbpkgname
) == 0) {
201 _alpm_log(PM_LOG_DEBUG
, _("target '%s' is also in target list, using NEW conflicts"),
203 conflicts
= alpm_pkg_get_conflicts(targ
);
204 use_newconflicts
= 1;
208 /* if we didn't find newer conflicts, use the original list */
209 if(!use_newconflicts
) {
210 conflicts
= alpm_pkg_get_conflicts(dbpkg
);
213 for(j
= conflicts
; j
; j
= j
->next
) {
214 const char *conflict
= j
->data
;
217 miss
= does_conflict(pkgname
, dbpkgname
, pkg
, conflict
);
218 if(miss
&& !_alpm_depmiss_isin(miss
, baddeps
)) {
219 baddeps
= alpm_list_add(baddeps
, miss
);
228 /* Returns a alpm_list_t* of pmdepmissing_t pointers.
230 * conflicts are always name only
232 alpm_list_t
*_alpm_checkconflicts(pmdb_t
*db
, alpm_list_t
*packages
)
234 alpm_list_t
*i
, *baddeps
= NULL
;
242 for(i
= packages
; i
; i
= i
->next
) {
243 pmpkg_t
*pkg
= i
->data
;
248 /* run three different conflict checks on each package */
249 baddeps
= chk_pkg_vs_db(baddeps
, pkg
, db
);
250 baddeps
= chk_pkg_vs_targets(baddeps
, pkg
, db
, packages
);
251 baddeps
= chk_db_vs_targets(baddeps
, pkg
, db
, packages
);
255 for(i
= baddeps
; i
; i
= i
->next
) {
256 pmdepmissing_t
*miss
= i
->data
;
257 _alpm_log(PM_LOG_DEBUG
, _("\tCONFLICTS:: %s conflicts with %s"), miss
->target
, miss
->depend
.name
);
264 /* Returns a alpm_list_t* of file conflicts.
265 * Hooray for set-intersects!
266 * Pre-condition: both lists are sorted!
268 static alpm_list_t
*chk_fileconflicts(alpm_list_t
*filesA
, alpm_list_t
*filesB
)
270 alpm_list_t
*ret
= NULL
;
271 alpm_list_t
*pA
= filesA
, *pB
= filesB
;
274 const char *strA
= pA
->data
;
275 const char *strB
= pB
->data
;
276 /* skip directories, we don't care about dir conflicts */
277 if(strA
[strlen(strA
)-1] == '/') {
279 } else if(strB
[strlen(strB
)-1] == '/') {
282 int cmp
= strcmp(strA
, strB
);
284 /* item only in filesA, ignore it */
287 /* item only in filesB, ignore it */
290 /* item in both, record it */
291 ret
= alpm_list_add(ret
, strdup(strA
));
301 /* Returns a alpm_list_t* of files that are in filesA but *NOT* in filesB
302 * This is an 'A minus B' set operation
303 * Pre-condition: both lists are sorted!
305 static alpm_list_t
*chk_filedifference(alpm_list_t
*filesA
, alpm_list_t
*filesB
)
307 alpm_list_t
*ret
= NULL
;
308 alpm_list_t
*pA
= filesA
, *pB
= filesB
;
311 const char *strA
= pA
->data
;
312 const char *strB
= pB
->data
;
313 /* skip directories, we don't care about dir conflicts */
314 if(strA
[strlen(strA
)-1] == '/') {
316 } else if(strB
[strlen(strB
)-1] == '/') {
319 int cmp
= strcmp(strA
, strB
);
321 /* item only in filesA, record it */
322 ret
= alpm_list_add(ret
, strdup(strA
));
325 /* item only in fileB, but this means nothing */
328 /* item in both, ignore it */
338 /* Adds pmconflict_t to a conflicts list. Pass the conflicts list, type (either
339 * PM_CONFLICT_TYPE_TARGET or PM_CONFLICT_TYPE_FILE), a file string, and either
340 * two package names or one package name and NULL. This is a wrapper for former
341 * functionality that was done inline.
343 static alpm_list_t
*add_fileconflict(alpm_list_t
*conflicts
,
344 pmconflicttype_t type
, const char *filestr
,
345 const char* name1
, const char* name2
)
347 pmconflict_t
*conflict
= malloc(sizeof(pmconflict_t
));
348 if(conflict
== NULL
) {
349 _alpm_log(PM_LOG_ERROR
, _("malloc failure: could not allocate %d bytes"),
350 sizeof(pmconflict_t
));
353 conflict
->type
= type
;
354 STRNCPY(conflict
->target
, name1
, PKG_NAME_LEN
);
355 STRNCPY(conflict
->file
, filestr
, CONFLICT_FILE_LEN
);
357 STRNCPY(conflict
->ctarget
, name2
, PKG_NAME_LEN
);
359 conflict
->ctarget
[0] = '\0';
362 conflicts
= alpm_list_add(conflicts
, conflict
);
363 _alpm_log(PM_LOG_DEBUG
, "found file conflict %s, packages %s and %s",
364 filestr
, name1
, name2
? name2
: "(filesystem)");
369 /* Find file conflicts that may occur during the transaction with two checks:
370 * 1: check every target against every target
371 * 2: check every target against the filesystem */
372 alpm_list_t
*_alpm_db_find_conflicts(pmdb_t
*db
, pmtrans_t
*trans
, char *root
)
374 alpm_list_t
*i
, *conflicts
= NULL
;
375 alpm_list_t
*targets
= trans
->packages
;
376 int numtargs
= alpm_list_count(targets
);
380 if(db
== NULL
|| targets
== NULL
|| root
== NULL
) {
384 for(i
= targets
; i
; i
= i
->next
) {
385 alpm_list_t
*j
, *k
, *tmpfiles
= NULL
;
386 pmpkg_t
*p1
, *p2
, *dbpkg
;
387 char path
[PATH_MAX
+1];
394 double percent
= (double)(alpm_list_count(targets
) - alpm_list_count(i
) + 1)
395 / alpm_list_count(targets
);
396 PROGRESS(trans
, PM_TRANS_PROGRESS_CONFLICTS_START
, "", (percent
* 100),
397 numtargs
, (numtargs
- alpm_list_count(i
) +1));
398 /* CHECK 1: check every target against every target */
399 for(j
= i
->next
; j
; j
= j
->next
) {
404 _alpm_log(PM_LOG_DEBUG
, "searching for file conflicts: %s and %s",
405 alpm_pkg_get_name(p1
), alpm_pkg_get_name(p2
));
406 tmpfiles
= chk_fileconflicts(alpm_pkg_get_files(p1
), alpm_pkg_get_files(p2
));
409 for(k
= tmpfiles
; k
; k
= k
->next
) {
410 snprintf(path
, PATH_MAX
, "%s%s", root
, (char *)k
->data
);
411 conflicts
= add_fileconflict(conflicts
, PM_CONFLICT_TYPE_TARGET
, path
,
412 alpm_pkg_get_name(p1
), alpm_pkg_get_name(p2
));
414 alpm_list_free_inner(tmpfiles
, &free
);
415 alpm_list_free(tmpfiles
);
419 /* declarations for second check */
421 char *filestr
= NULL
;
423 /* CHECK 2: check every target against the filesystem */
424 _alpm_log(PM_LOG_DEBUG
, "searching for filesystem conflicts: %s", p1
->name
);
425 dbpkg
= _alpm_db_get_pkgfromcache(db
, p1
->name
);
427 /* Do two different checks here. f the package is currently installed,
428 * then only check files that are new in the new package. If the package
429 * is not currently installed, then simply stat the whole filelist */
431 /* older ver of package currently installed */
432 tmpfiles
= chk_filedifference(alpm_pkg_get_files(p1
), alpm_pkg_get_files(dbpkg
));
434 /* no version of package currently installed */
435 tmpfiles
= alpm_list_strdup(alpm_pkg_get_files(p1
));
438 /* loop over each file to be installed */
439 for(j
= tmpfiles
; j
; j
= j
->next
) {
442 snprintf(path
, PATH_MAX
, "%s%s", root
, filestr
);
444 /* stat the file - if it exists and is not a dir, do some checks */
445 if(lstat(path
, &buf
) == 0 && !S_ISDIR(buf
.st_mode
)) {
446 _alpm_log(PM_LOG_DEBUG
, "checking possible conflict: %s", path
);
448 /* Make sure the possible conflict is not a symlink that points to a
449 * path in the old package. This is kind of dirty with inode usage */
452 char str
[PATH_MAX
+1];
454 for(k
= dbpkg
->files
; k
; k
= k
->next
) {
455 snprintf(str
, PATH_MAX
, "%s%s", root
, (char*)k
->data
);
457 if(buf
.st_ino
== buf2
.st_ino
) {
459 _alpm_log(PM_LOG_DEBUG
, "conflict was a symlink: %s", path
);
468 /* Look at all the targets to see if file has changed hands */
469 int resolved_conflict
= 0; /* have we acted on this conflict? */
470 for(k
= targets
; k
; k
= k
->next
) {
472 if(!p2
|| strcmp(p1
->name
, p2
->name
) == 0) {
476 pmpkg_t
*localp2
= _alpm_db_get_pkgfromcache(db
, p2
->name
);
478 /* Check if it used to exist in a package, but doesn't anymore */
479 alpm_list_t
*pkgfiles
, *localfiles
; /* added for readability */
480 pkgfiles
= alpm_pkg_get_files(p2
);
481 localfiles
= alpm_pkg_get_files(localp2
);
483 if(localp2
&& !alpm_list_find_str(pkgfiles
, filestr
)
484 && alpm_list_find_str(localfiles
, filestr
)) {
485 /* check if the file is now in the backup array */
486 if(alpm_list_find_str(alpm_pkg_get_backup(p1
), filestr
)) {
487 /* keep file intact if it is in backup array */
488 trans
->skip_add
= alpm_list_add(trans
->skip_add
, strdup(path
));
489 trans
->skip_remove
= alpm_list_add(trans
->skip_remove
, strdup(path
));
490 _alpm_log(PM_LOG_DEBUG
, "file in backup array, adding to add and remove skiplist: %s", filestr
);
491 resolved_conflict
= 1;
494 /* skip removal of file, but not add. this will prevent a second
495 * package from removing the file when it was already installed
496 * by its new owner */
497 trans
->skip_remove
= alpm_list_add(trans
->skip_remove
, strdup(path
));
498 _alpm_log(PM_LOG_DEBUG
, "file changed packages, adding to remove skiplist: %s", filestr
);
499 resolved_conflict
= 1;
504 if(!resolved_conflict
) {
505 _alpm_log(PM_LOG_DEBUG
, "file found in conflict: %s", path
);
506 conflicts
= add_fileconflict(conflicts
, PM_CONFLICT_TYPE_FILE
,
507 path
, p1
->name
, NULL
);
510 _alpm_log(PM_LOG_DEBUG
, "%s is a directory, not a conflict", path
);
513 alpm_list_free_inner(tmpfiles
, &free
);
514 alpm_list_free(tmpfiles
);
520 const char SYMEXPORT
*alpm_conflict_get_target(pmconflict_t
*conflict
)
525 ASSERT(handle
!= NULL
, return(NULL
));
526 ASSERT(conflict
!= NULL
, return(NULL
));
528 return conflict
->target
;
531 pmconflicttype_t SYMEXPORT
alpm_conflict_get_type(pmconflict_t
*conflict
)
536 ASSERT(handle
!= NULL
, return(-1));
537 ASSERT(conflict
!= NULL
, return(-1));
539 return conflict
->type
;
542 const char SYMEXPORT
*alpm_conflict_get_file(pmconflict_t
*conflict
)
547 ASSERT(handle
!= NULL
, return(NULL
));
548 ASSERT(conflict
!= NULL
, return(NULL
));
550 return conflict
->file
;
553 const char SYMEXPORT
*alpm_conflict_get_ctarget(pmconflict_t
*conflict
)
558 ASSERT(handle
!= NULL
, return(NULL
));
559 ASSERT(conflict
!= NULL
, return(NULL
));
561 return conflict
->ctarget
;
563 /* vim: set ts=2 sw=2 noet: */