/* * Copyright (c) 2007-2016, SUSE LLC * * This program is licensed under the BSD license, read LICENSE.BSD * for further information */ /* * fileprovides.c * * Add missing file dependencies to the package provides */ #include #include #include #include #include #include "pool.h" #include "repo.h" #include "util.h" #include "bitmap.h" struct searchfiles { Id *ids; int nfiles; Map seen; }; #define SEARCHFILES_BLOCK 127 static void pool_addfileprovides_dep(Pool *pool, Id *ida, struct searchfiles *sf, struct searchfiles *isf) { Id dep, sid; const char *s; struct searchfiles *csf; while ((dep = *ida++) != 0) { csf = sf; while (ISRELDEP(dep)) { Reldep *rd; sid = pool->ss.nstrings + GETRELID(dep); if (MAPTST(&csf->seen, sid)) { dep = 0; break; } MAPSET(&csf->seen, sid); rd = GETRELDEP(pool, dep); if (rd->flags < 8) dep = rd->name; else if (rd->flags == REL_NAMESPACE) { if (rd->name == NAMESPACE_SPLITPROVIDES) { csf = isf; if (!csf || MAPTST(&csf->seen, sid)) { dep = 0; break; } MAPSET(&csf->seen, sid); } dep = rd->evr; } else if (rd->flags == REL_FILECONFLICT) { dep = 0; break; } else { Id ids[2]; ids[0] = rd->name; ids[1] = 0; pool_addfileprovides_dep(pool, ids, csf, isf); dep = rd->evr; } } if (!dep) continue; if (MAPTST(&csf->seen, dep)) continue; MAPSET(&csf->seen, dep); s = pool_id2str(pool, dep); if (*s != '/') continue; if (csf != isf && pool->addedfileprovides == 1 && !repodata_filelistfilter_matches(0, s)) continue; /* skip non-standard locations csf == isf: installed case */ csf->ids = solv_extend(csf->ids, csf->nfiles, 1, sizeof(Id), SEARCHFILES_BLOCK); csf->ids[csf->nfiles++] = dep; } } struct addfileprovides_cbdata { int nfiles; Id *ids; char **dirs; char **names; Id *dids; Map *providedids; int provstart; int provend; Map *todo; int todo_start; int todo_end; }; /* split filelist dep into basename and dirname */ static void create_dirs_names_array(struct addfileprovides_cbdata *cbd, Pool *pool) { int i; cbd->dirs = solv_malloc2(cbd->nfiles, sizeof(char *)); cbd->names = solv_malloc2(cbd->nfiles, sizeof(char *)); for (i = 0; i < cbd->nfiles; i++) { char *s = solv_strdup(pool_id2str(pool, cbd->ids[i])); cbd->dirs[i] = s; s = strrchr(s, '/'); *s = 0; cbd->names[i] = s + 1; } } static void free_dirs_names_array(struct addfileprovides_cbdata *cbd) { int i; if (cbd->dirs) { for (i = 0; i < cbd->nfiles; i++) solv_free(cbd->dirs[i]); cbd->dirs = solv_free(cbd->dirs); cbd->names = solv_free(cbd->names); } } static void prune_todo_range(Repo *repo, struct addfileprovides_cbdata *cbd) { int start = cbd->todo_start, end = cbd->todo_end; while (start < end && !MAPTST(cbd->todo, start - repo->start)) start++; while (end > start && !MAPTST(cbd->todo, end - 1 - repo->start)) end--; cbd->todo_start = start; cbd->todo_end = end; } static int repodata_intersects_todo(Repodata *data, struct addfileprovides_cbdata *cbd) { Repo *repo; int p, start = data->start, end = data->end; if (start >= cbd->todo_end || end <= cbd->todo_start) return 0; repo = data->repo; if (start < cbd->todo_start) start = cbd->todo_start; if (end > cbd->todo_end) end = cbd->todo_end; for (p = start; p < end; p++) if (MAPTST(cbd->todo, p - repo->start)) return 1; return 0; } /* forward declaration */ static void repodata_addfileprovides_search(Repodata *data, struct addfileprovides_cbdata *cbd); /* search a subset of the todo range */ static void repodata_addfileprovides_search_limited(Repodata *data, struct addfileprovides_cbdata *cbd, int start, int end) { int old_todo_start = cbd->todo_start; int old_todo_end = cbd->todo_end; if (start < cbd->todo_start) start = cbd->todo_start; if (end > cbd->todo_end) end = cbd->todo_end; if (start >= end) return; cbd->todo_start = start; cbd->todo_end = end; repodata_addfileprovides_search(data, cbd); cbd->todo_start = old_todo_start; cbd->todo_end = old_todo_end; prune_todo_range(data->repo, cbd); } static void repodata_addfileprovides_search(Repodata *data, struct addfileprovides_cbdata *cbd) { Repo *repo = data->repo; int i, p, start, end; Map useddirs; Map *providedids = 0; /* make it available */ if (data->state == REPODATA_STUB) repodata_load(data); if (data->state != REPODATA_AVAILABLE) return; if (!data->incoredata || !data->dirpool.ndirs) return; start = cbd->todo_start > data->start ? cbd->todo_start : data->start; end = cbd->todo_end > data->end ? data->end : cbd->todo_end; if (start >= end) return; /* deal with provideids overlap */ if (cbd->providedids) { if (start >= cbd->provstart && end <= cbd->provend) providedids = cbd->providedids; /* complete overlap */ else if (start < cbd->provend && end > cbd->provstart) { /* partial overlap, need to split search */ if (start < cbd->provstart) { repodata_addfileprovides_search_limited(data, cbd, start, cbd->provstart); start = cbd->provstart; } if (end > cbd->provend) { repodata_addfileprovides_search_limited(data, cbd, cbd->provend, end); end = cbd->provend; } if (start < end) repodata_addfileprovides_search_limited(data, cbd, start, end); return; } } /* set up dirs and names array if not already done */ if (!cbd->dirs) create_dirs_names_array(cbd, repo->pool); /* set up useddirs map and the cbd->dids array */ map_init(&useddirs, data->dirpool.ndirs); for (i = 0; i < cbd->nfiles; i++) { Id did; if (providedids && MAPTST(providedids, cbd->ids[i])) { cbd->dids[i] = 0; /* already included, do not add again */ continue; } cbd->dids[i] = did = repodata_str2dir(data, cbd->dirs[i], 0); if (did) MAPSET(&useddirs, did); } repodata_free_dircache(data); /* repodata_str2dir created it */ for (p = start; p < end; p++) { const unsigned char *dp; Solvable *s; if (!MAPTST(cbd->todo, p - repo->start)) continue; dp = repodata_lookup_packed_dirstrarray(data, p, SOLVABLE_FILELIST); if (!dp) continue; /* now iterate through the packed array */ s = repo->pool->solvables + p; MAPCLR(cbd->todo, p - repo->start); /* this entry is done */ for (;;) { Id did = 0; int c; while ((c = *dp++) & 0x80) did = (did << 7) ^ c ^ 0x80; did = (did << 6) | (c & 0x3f); if ((unsigned int)did < (unsigned int)data->dirpool.ndirs && MAPTST(&useddirs, did)) { /* there is at least one entry with that did */ for (i = 0; i < cbd->nfiles; i++) if (cbd->dids[i] == did && !strcmp(cbd->names[i], (const char *)dp)) s->provides = repo_addid_dep(s->repo, s->provides, cbd->ids[i], SOLVABLE_FILEMARKER); } if (!(c & 0x40)) break; dp += strlen((const char *)dp) + 1; } } map_free(&useddirs); prune_todo_range(repo, cbd); } static void repo_addfileprovides_search_filtered(Repo *repo, struct addfileprovides_cbdata *cbd, int filteredid, Map *postpone) { Repodata *data = repo->repodata + filteredid; Map *providedids = cbd->providedids; int rdid; int start, end, p, i; Map old_todo; int old_todo_start, old_todo_end; start = cbd->todo_start > data->start ? cbd->todo_start : data->start; end = cbd->todo_end > data->end ? data->end : cbd->todo_end; if (providedids) { /* check if all solvables are in the provide range */ if (start < cbd->provstart || end > cbd->provend) { /* unclear, check each solvable */ for (p = start; p < end; p++) { if (p >= cbd->provstart && p < cbd->provend) continue; if (data->incoreoffset[p - data->start] && MAPTST(cbd->todo, p - repo->start)) { providedids = 0; /* nope, cannot prune with providedids */ break; } } } } /* check if the filtered files are enough */ for (i = 0; i < cbd->nfiles; i++) { if (providedids && MAPTST(providedids, cbd->ids[i])) /* this one is already provided */ continue; if (!repodata_filelistfilter_matches(data, pool_id2str(repo->pool, cbd->ids[i]))) break; } if (i < cbd->nfiles) { /* nope, need to search the extensions as well. postpone. */ for (p = start; p < end; p++) { if (data->incoreoffset[p - data->start] && MAPTST(cbd->todo, p - repo->start)) { if (!postpone->size) map_grow(postpone, repo->nsolvables); MAPSET(postpone, p - repo->start); MAPCLR(cbd->todo, p - repo->start); } } prune_todo_range(repo, cbd); return; } /* now check if there is no data marked withour EXTENSION */ /* limit todo to the solvables in this repodata */ old_todo_start = cbd->todo_start; old_todo_end = cbd->todo_end; old_todo = *cbd->todo; map_init(cbd->todo, repo->nsolvables); for (p = start; p < end; p++) if (data->incoreoffset[p - data->start] && MAPTST(&old_todo, p - repo->start)) { MAPCLR(&old_todo, p - repo->start); MAPSET(cbd->todo, p - repo->start); } prune_todo_range(repo, cbd); /* do the check */ for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > filteredid ; rdid--, data--) { if (data->filelisttype == REPODATA_FILELIST_EXTENSION) continue; if (data->start >= cbd->todo_end || data->end <= cbd->todo_start) continue; if (!repodata_has_keyname(data, SOLVABLE_FILELIST)) continue; if (!repodata_intersects_todo(data, cbd)) continue; /* oh no, this filelist data is not tagged with REPODATA_FILELIST_EXTENSION! */ /* postpone entries that have filelist data */ start = cbd->todo_start > data->start ? cbd->todo_start : data->start; end = cbd->todo_end > data->end ? data->end : cbd->todo_end; for (p = start; p < end; p++) if (MAPTST(cbd->todo, p - repo->start)) if (repodata_lookup_type(data, p, SOLVABLE_FILELIST)) { if (!postpone->size) map_grow(postpone, repo->nsolvables); MAPSET(postpone, p - repo->start); MAPCLR(cbd->todo, p - repo->start); } prune_todo_range(repo, cbd); if (cbd->todo_start >= cbd->todo_end) break; } /* do the search over the filtered file list with the remaining entries*/ if (cbd->todo_start < cbd->todo_end) repodata_addfileprovides_search(repo->repodata + filteredid, cbd); /* restore todo map */ map_free(cbd->todo); *cbd->todo = old_todo; cbd->todo_start = old_todo_start; cbd->todo_end = old_todo_end; prune_todo_range(repo, cbd); } static void repo_addfileprovides_search(Repo *repo, struct addfileprovides_cbdata *cbd, struct searchfiles *sf) { Repodata *data; int rdid, p, i; int provstart, provend; Map todo; Map providedids; if (repo->end <= repo->start || !repo->nsolvables || !sf->nfiles) return; /* update search data if changed */ if (cbd->nfiles != sf->nfiles || cbd->ids != sf->ids) { free_dirs_names_array(cbd); cbd->nfiles = sf->nfiles; cbd->ids = sf->ids; cbd->dids = solv_realloc2(cbd->dids, sf->nfiles, sizeof(Id)); } /* create todo map and range */ map_init(&todo, repo->end - repo->start); for (p = repo->start; p < repo->end; p++) if (repo->pool->solvables[p].repo == repo) MAPSET(&todo, p - repo->start); cbd->todo = &todo; cbd->todo_start = repo->start; cbd->todo_end = repo->end; prune_todo_range(repo, cbd); provstart = provend = 0; map_init(&providedids, 0); data = repo_lookup_repodata(repo, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES); if (data) { Queue fileprovidesq; queue_init(&fileprovidesq); if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq)) { map_grow(&providedids, repo->pool->ss.nstrings); cbd->providedids = &providedids; provstart = data->start; provend = data->end; for (i = 0; i < fileprovidesq.count; i++) MAPSET(&providedids, fileprovidesq.elements[i]); for (i = 0; i < cbd->nfiles; i++) if (!MAPTST(&providedids, cbd->ids[i])) break; if (i == cbd->nfiles) { /* all included, clear entries from todo list */ if (provstart <= cbd->todo_start && provend >= cbd->todo_end) cbd->todo_end = cbd->todo_start; /* clear complete range */ else { for (p = provstart; p < provend; p++) MAPCLR(&todo, p - repo->start); prune_todo_range(repo, cbd); } } } queue_free(&fileprovidesq); } if (cbd->todo_start >= cbd->todo_end) { map_free(&todo); cbd->todo = 0; map_free(&providedids); cbd->providedids = 0; return; } /* this is similar to repo_lookup_filelist_repodata in repo.c */ for (rdid = 1, data = repo->repodata + rdid; rdid < repo->nrepodata; rdid++, data++) if (data->filelisttype == REPODATA_FILELIST_FILTERED) break; for (; rdid < repo->nrepodata; rdid++, data++) if (data->filelisttype == REPODATA_FILELIST_EXTENSION) break; if (rdid < repo->nrepodata) { /* have at least one repodata with REPODATA_FILELIST_FILTERED followed by REPODATA_FILELIST_EXTENSION */ Map postpone; map_init(&postpone, 0); for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > 0; rdid--, data--) { if (data->filelisttype != REPODATA_FILELIST_FILTERED) continue; if (!repodata_intersects_todo(data, cbd)) continue; if (data->state != REPODATA_AVAILABLE) { if (data->state != REPODATA_STUB) continue; repodata_load(data); if (data->state != REPODATA_AVAILABLE || data->filelisttype != REPODATA_FILELIST_FILTERED) continue; } repo_addfileprovides_search_filtered(repo, cbd, rdid, &postpone); } if (postpone.size) { /* add postponed entries back to todo */ map_or(&todo, &postpone); cbd->todo_start = repo->start; cbd->todo_end = repo->end; prune_todo_range(repo, cbd); } map_free(&postpone); } /* search remaining entries in the standard way */ if (cbd->todo_start < cbd->todo_end) { for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > 0; rdid--, data--) { if (data->start >= cbd->todo_end || data->end <= cbd->todo_start) continue; if (!repodata_has_keyname(data, SOLVABLE_FILELIST)) continue; if (!repodata_intersects_todo(data, cbd)) continue; repodata_addfileprovides_search(data, cbd); if (cbd->todo_start >= cbd->todo_end) break; } } map_free(&todo); cbd->todo = 0; map_free(&providedids); cbd->providedids = 0; } void pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst) { Solvable *s; Repo *installed, *repo; struct searchfiles sf, isf, *isfp; struct addfileprovides_cbdata cbd; int i; unsigned int now; installed = pool->installed; now = solv_timems(0); memset(&cbd, 0, sizeof(cbd)); memset(&sf, 0, sizeof(sf)); map_init(&sf.seen, pool->ss.nstrings + pool->nrels); memset(&isf, 0, sizeof(isf)); map_init(&isf.seen, pool->ss.nstrings + pool->nrels); pool->addedfileprovides = pool->addfileprovidesfiltered ? 1 : 2; if (idq) queue_empty(idq); if (idqinst) queue_empty(idqinst); isfp = installed ? &isf : 0; for (i = 1, s = pool->solvables + i; i < pool->nsolvables; i++, s++) { repo = s->repo; if (!repo) continue; if (s->obsoletes) pool_addfileprovides_dep(pool, repo->idarraydata + s->obsoletes, &sf, isfp); if (s->conflicts) pool_addfileprovides_dep(pool, repo->idarraydata + s->conflicts, &sf, isfp); if (s->requires) pool_addfileprovides_dep(pool, repo->idarraydata + s->requires, &sf, isfp); if (s->recommends) pool_addfileprovides_dep(pool, repo->idarraydata + s->recommends, &sf, isfp); if (s->suggests) pool_addfileprovides_dep(pool, repo->idarraydata + s->suggests, &sf, isfp); if (s->supplements) pool_addfileprovides_dep(pool, repo->idarraydata + s->supplements, &sf, isfp); if (s->enhances) pool_addfileprovides_dep(pool, repo->idarraydata + s->enhances, &sf, isfp); } map_free(&sf.seen); map_free(&isf.seen); POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file dependencies, %d installed file dependencies\n", sf.nfiles, isf.nfiles); if (sf.nfiles) { #if 0 for (i = 0; i < sf.nfiles; i++) POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in filelist\n", pool_id2str(pool, sf.ids[i])); #endif FOR_REPOS(i, repo) repo_addfileprovides_search(repo, &cbd, &sf); if (idq) queue_insertn(idq, idq->count, sf.nfiles, sf.ids); if (idqinst) queue_insertn(idqinst, idqinst->count, sf.nfiles, sf.ids); solv_free(sf.ids); } if (isf.nfiles) { #if 0 for (i = 0; i < isf.nfiles; i++) POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in installed filelist\n", pool_id2str(pool, isf.ids[i])); #endif if (installed) repo_addfileprovides_search(installed, &cbd, &isf); if (installed && idqinst) for (i = 0; i < isf.nfiles; i++) queue_pushunique(idqinst, isf.ids[i]); solv_free(isf.ids); } free_dirs_names_array(&cbd); solv_free(cbd.dids); pool_freewhatprovides(pool); /* as we have added provides */ POOL_DEBUG(SOLV_DEBUG_STATS, "addfileprovides took %d ms\n", solv_timems(now)); } void pool_addfileprovides(Pool *pool) { pool_addfileprovides_queue(pool, 0, 0); }