/*
* Copyright (c) 2007-2016, SUSE LLC
*
* This program is licensed under the BSD license, read LICENSE.BSD
* for further information
*/
/*
* diskusage.c
*
* calculate needed space on partitions
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include "pool.h"
#include "poolarch.h"
#include "repo.h"
#include "util.h"
#include "bitmap.h"
struct mptree {
Id sibling;
Id child;
const char *comp;
int compl;
Id mountpoint;
};
struct ducbdata {
DUChanges *mps;
struct mptree *mptree;
int addsub;
int hasdu;
Id *dirmap;
int nmap;
Repodata *olddata;
};
static int
solver_fill_DU_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value)
{
struct ducbdata *cbd = cbdata;
Id mp;
if (data != cbd->olddata)
{
Id dn, mp, comp, *dirmap, *dirs;
int i, compl;
const char *compstr;
struct mptree *mptree;
/* create map from dir to mptree */
cbd->dirmap = solv_free(cbd->dirmap);
cbd->nmap = 0;
dirmap = solv_calloc(data->dirpool.ndirs, sizeof(Id));
mptree = cbd->mptree;
mp = 0;
for (dn = 2, dirs = data->dirpool.dirs + dn; dn < data->dirpool.ndirs; dn++)
{
comp = *dirs++;
if (comp <= 0)
{
mp = dirmap[-comp];
continue;
}
if (mp < 0)
{
/* unconnected */
dirmap[dn] = mp;
continue;
}
if (!mptree[mp].child)
{
dirmap[dn] = -mp;
continue;
}
if (data->localpool)
compstr = stringpool_id2str(&data->spool, comp);
else
compstr = pool_id2str(data->repo->pool, comp);
compl = strlen(compstr);
for (i = mptree[mp].child; i; i = mptree[i].sibling)
if (mptree[i].compl == compl && !strncmp(mptree[i].comp, compstr, compl))
break;
dirmap[dn] = i ? i : -mp;
}
/* change dirmap to point to mountpoint instead of mptree */
for (dn = 0; dn < data->dirpool.ndirs; dn++)
{
mp = dirmap[dn];
dirmap[dn] = mptree[mp > 0 ? mp : -mp].mountpoint;
}
cbd->dirmap = dirmap;
cbd->nmap = data->dirpool.ndirs;
cbd->olddata = data;
}
cbd->hasdu = 1;
if (value->id < 0 || value->id >= cbd->nmap)
return 0;
mp = cbd->dirmap[value->id];
if (mp < 0)
return 0;
if (cbd->addsub > 0)
{
cbd->mps[mp].kbytes += value->num;
cbd->mps[mp].files += value->num2;
}
else if (!(cbd->mps[mp].flags & DUCHANGES_ONLYADD))
{
cbd->mps[mp].kbytes -= value->num;
cbd->mps[mp].files -= value->num2;
}
return 0;
}
static void
propagate_mountpoints(struct mptree *mptree, int pos, Id mountpoint)
{
int i;
if (mptree[pos].mountpoint == -1)
mptree[pos].mountpoint = mountpoint;
else
mountpoint = mptree[pos].mountpoint;
for (i = mptree[pos].child; i; i = mptree[i].sibling)
propagate_mountpoints(mptree, i, mountpoint);
}
#define MPTREE_BLOCK 15
static struct mptree *
create_mptree(DUChanges *mps, int nmps)
{
int i, nmptree;
struct mptree *mptree;
int pos, compl;
int mp;
const char *p, *path, *compstr;
mptree = solv_extend_resize(0, 1, sizeof(struct mptree), MPTREE_BLOCK);
/* our root node */
mptree[0].sibling = 0;
mptree[0].child = 0;
mptree[0].comp = 0;
mptree[0].compl = 0;
mptree[0].mountpoint = -1;
nmptree = 1;
/* create component tree */
for (mp = 0; mp < nmps; mp++)
{
mps[mp].kbytes = 0;
mps[mp].files = 0;
pos = 0;
path = mps[mp].path;
while(*path == '/')
path++;
while (*path)
{
if ((p = strchr(path, '/')) == 0)
{
compstr = path;
compl = strlen(compstr);
path += compl;
}
else
{
compstr = path;
compl = p - path;
path = p + 1;
while(*path == '/')
path++;
}
for (i = mptree[pos].child; i; i = mptree[i].sibling)
if (mptree[i].compl == compl && !strncmp(mptree[i].comp, compstr, compl))
break;
if (!i)
{
/* create new node */
mptree = solv_extend(mptree, nmptree, 1, sizeof(struct mptree), MPTREE_BLOCK);
i = nmptree++;
mptree[i].sibling = mptree[pos].child;
mptree[i].child = 0;
mptree[i].comp = compstr;
mptree[i].compl = compl;
mptree[i].mountpoint = -1;
mptree[pos].child = i;
}
pos = i;
}
mptree[pos].mountpoint = mp;
}
propagate_mountpoints(mptree, 0, mptree[0].mountpoint);
#if 0
for (i = 0; i < nmptree; i++)
{
printf("#%d sibling: %d\n", i, mptree[i].sibling);
printf("#%d child: %d\n", i, mptree[i].child);
printf("#%d comp: %s\n", i, mptree[i].comp);
printf("#%d compl: %d\n", i, mptree[i].compl);
printf("#%d mountpont: %d\n", i, mptree[i].mountpoint);
}
#endif
return mptree;
}
void
pool_calc_duchanges(Pool *pool, Map *installedmap, DUChanges *mps, int nmps)
{
struct mptree *mptree;
struct ducbdata cbd;
Solvable *s;
int i, sp;
Map ignoredu;
Repo *oldinstalled = pool->installed;
int haveonlyadd = 0;
map_init(&ignoredu, 0);
mptree = create_mptree(mps, nmps);
for (i = 0; i < nmps; i++)
if ((mps[i].flags & DUCHANGES_ONLYADD) != 0)
haveonlyadd = 1;
cbd.mps = mps;
cbd.dirmap = 0;
cbd.nmap = 0;
cbd.olddata = 0;
cbd.mptree = mptree;
cbd.addsub = 1;
for (sp = 1, s = pool->solvables + sp; sp < pool->nsolvables; sp++, s++)
{
if (!s->repo || (oldinstalled && s->repo == oldinstalled))
continue;
if (!MAPTST(installedmap, sp))
continue;
cbd.hasdu = 0;
repo_search(s->repo, sp, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
if (!cbd.hasdu && oldinstalled)
{
Id op, opp;
int didonlyadd = 0;
/* no du data available, ignore data of all installed solvables we obsolete */
if (!ignoredu.size)
map_grow(&ignoredu, oldinstalled->end - oldinstalled->start);
FOR_PROVIDES(op, opp, s->name)
{
Solvable *s2 = pool->solvables + op;
if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
continue;
if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, s2))
continue;
if (op >= oldinstalled->start && op < oldinstalled->end)
{
MAPSET(&ignoredu, op - oldinstalled->start);
if (haveonlyadd && pool->solvables[op].repo == oldinstalled && !didonlyadd)
{
repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
cbd.addsub = -1;
repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
cbd.addsub = 1;
didonlyadd = 1;
}
}
}
if (s->obsoletes)
{
Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
while ((obs = *obsp++) != 0)
FOR_PROVIDES(op, opp, obs)
{
Solvable *s2 = pool->solvables + op;
if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, s2, obs))
continue;
if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
continue;
if (op >= oldinstalled->start && op < oldinstalled->end)
{
MAPSET(&ignoredu, op - oldinstalled->start);
if (haveonlyadd && pool->solvables[op].repo == oldinstalled && !didonlyadd)
{
repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
cbd.addsub = -1;
repo_search(oldinstalled, op, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
cbd.addsub = 1;
didonlyadd = 1;
}
}
}
}
}
}
cbd.addsub = -1;
if (oldinstalled)
{
/* assumes we allways have du data for installed solvables */
FOR_REPO_SOLVABLES(oldinstalled, sp, s)
{
if (MAPTST(installedmap, sp))
continue;
if (ignoredu.size && MAPTST(&ignoredu, sp - oldinstalled->start))
continue;
repo_search(oldinstalled, sp, SOLVABLE_DISKUSAGE, 0, 0, solver_fill_DU_cb, &cbd);
}
}
map_free(&ignoredu);
solv_free(cbd.dirmap);
solv_free(mptree);
}
long long
pool_calc_installsizechange(Pool *pool, Map *installedmap)
{
Id sp;
Solvable *s;
long long change = 0;
Repo *oldinstalled = pool->installed;
for (sp = 1, s = pool->solvables + sp; sp < pool->nsolvables; sp++, s++)
{
if (!s->repo || (oldinstalled && s->repo == oldinstalled))
continue;
if (!MAPTST(installedmap, sp))
continue;
change += solvable_lookup_sizek(s, SOLVABLE_INSTALLSIZE, 0);
}
if (oldinstalled)
{
FOR_REPO_SOLVABLES(oldinstalled, sp, s)
{
if (MAPTST(installedmap, sp))
continue;
change -= solvable_lookup_sizek(s, SOLVABLE_INSTALLSIZE, 0);
}
}
return change;
}