#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <pkgdev.h>
#include <pkgstrct.h>
#include <locale.h>
#include <libintl.h>
#include <pkglib.h>
#include <libadm.h>
#include <libinst.h>
extern struct pkgdev pkgdev;
#define MALSIZ 500
#define EFACTOR 128ULL
#define WRN_LIMIT "WARNING: -l limit (%llu blocks) exceeds device " \
"capacity (%llu blocks)"
#define ERR_MEMORY "memory allocation failure, errno=%d"
#define ERR_TOOBIG "%s (%llu blocks) does not fit on a volume"
#define ERR_INFOFIRST "information file <%s> must appear on first part"
#define ERR_INFOSPACE "all install files must appear on first part"
#define ERR_VOLBLKS "Objects selected for part %d require %llu blocks, " \
"limit=%llu."
#define ERR_VOLFILES "Objects selected for part %d require %llu files, " \
"limit=%llu."
#define ERR_FREE "package does not fit space currently available in <%s>"
struct data {
fsblkcnt_t blks;
struct cfent *ept;
};
struct class_type {
char *name;
int first;
int last;
};
static fsblkcnt_t btotal;
static fsblkcnt_t bmax;
static fsfilcnt_t ftotal;
static fsfilcnt_t fmax;
static fsblkcnt_t bpkginfo;
static char **dirlist;
static short volno;
static int nparts = -1;
static int nclass;
static fsblkcnt_t DIRSIZE;
static struct class_type *cl;
static int nodecount(char *path);
static int store(struct data **, unsigned int, char *, fsblkcnt_t,
fsblkcnt_t);
static void addclass(char *aclass, int vol);
static void allocnode(char *path);
static void newvolume(struct data **, unsigned int, fsblkcnt_t limit,
fsblkcnt_t);
static void sortsize(struct data *f, struct data **sf, unsigned int eptnum);
int
splpkgmap(struct cfent **eptlist, unsigned int eptnum, char *order[],
ulong_t bsize, ulong_t frsize, fsblkcnt_t *plimit, fsfilcnt_t *pilimit,
fsblkcnt_t *pllimit)
{
struct data *f, **sf;
struct cfent *ept;
register int i, j;
int new_vol_set;
short new_vol;
int flag, errflg;
fsblkcnt_t total;
fsblkcnt_t btemp;
fsfilcnt_t ftemp;
f = (struct data *)calloc(eptnum, sizeof (struct data));
if (f == NULL) {
progerr(gettext(ERR_MEMORY), errno);
quit(99);
}
sf = (struct data **)calloc(eptnum, sizeof (struct data *));
if (sf == NULL) {
progerr(gettext(ERR_MEMORY), errno);
quit(99);
}
nclass = 0;
cl = (struct class_type *)calloc(MALSIZ, sizeof (struct class_type));
if (cl == NULL) {
progerr(gettext(ERR_MEMORY), errno);
quit(99);
}
errflg = 0;
total = 0;
DIRSIZE = ((fsblkcnt_t)frsize > 0) ?
howmany(frsize, DEV_BSIZE) :
howmany(bsize, DEV_BSIZE);
if (!pkgdev.mount) {
allocnode(NULL);
if ((*pilimit > 0) && (eptnum+1 > *pilimit)) {
progerr(gettext(ERR_FREE), pkgdev.dirname);
quit(1);
}
for (i = 0; i < eptnum; i++) {
if (strchr("dxslcbp", eptlist[i]->ftype))
continue;
else {
total +=
(nodecount(eptlist[i]->path) * DIRSIZE);
total +=
nblk(eptlist[i]->cinfo.size, bsize, frsize);
if (total > *plimit) {
progerr(gettext(ERR_FREE),
pkgdev.dirname);
quit(1);
}
allocnode(eptlist[i]->path);
}
}
}
if (*pllimit != 0) {
if (pkgdev.mount && *pllimit > *plimit)
logerr(gettext(WRN_LIMIT), *pllimit, *plimit);
*plimit = *pllimit;
}
for (i = 0; i < eptnum; i++) {
f[i].ept = ept = eptlist[i];
if (ept->volno > nparts)
nparts = ept->volno;
addclass(ept->pkg_class, 0);
if (strchr("dxslcbp", ept->ftype))
f[i].blks = 0;
else
f[i].blks = nblk(ept->cinfo.size, bsize, frsize);
if (!bpkginfo && (strcmp(f[i].ept->path, "pkginfo") == 0))
bpkginfo = f[i].blks;
}
for (i = 1; i <= nparts; i++) {
btemp = (bpkginfo + 2LL);
ftemp = 2LL;
if (i == 1) {
ftemp += 2;
btemp += nblk(eptnum * EFACTOR, bsize, frsize);
btemp += 2;
}
allocnode(NULL);
for (j = 0; j < eptnum; j++) {
if (i == 1 && f[j].ept->ftype == 'i' &&
(strcmp(f[j].ept->path, "pkginfo") == 0 ||
strcmp(f[j].ept->path, "pkgmap") == 0))
continue;
if (f[j].ept->volno == i ||
(f[j].ept->ftype == 'i' && i == 1)) {
ftemp += nodecount(f[j].ept->path);
btemp += f[j].blks;
allocnode(f[j].ept->path);
}
}
btemp += (ftemp * DIRSIZE);
if (btemp > *plimit) {
progerr(gettext(ERR_VOLBLKS), i, btemp, *plimit);
errflg++;
} else if ((*pilimit > 0) && (ftemp+1 > *pilimit)) {
progerr(gettext(ERR_VOLFILES), i, ftemp + 1, *pilimit);
errflg++;
}
}
if (errflg)
quit(1);
sortsize(f, sf, eptnum);
newvolume(sf, eptnum, *plimit, *pilimit);
btotal += nblk((fsblkcnt_t)(eptnum * EFACTOR), bsize, frsize);
ftotal++;
allocnode(NULL);
flag = 0;
for (j = 0; j < eptnum; ++j) {
if (f[j].ept->ftype != 'i')
continue;
else if (!flag++) {
ftotal++;
btotal += 2ULL;
}
if (!f[j].ept->volno) {
f[j].ept->volno = 1;
ftotal++;
btotal += f[j].blks;
} else if (f[j].ept->volno != 1) {
progerr(gettext(ERR_INFOFIRST), f[j].ept->path);
errflg++;
}
}
if (errflg)
quit(1);
if (btotal > *plimit) {
progerr(gettext(ERR_INFOSPACE));
quit(1);
}
for (j = 0; j < eptnum; ++j) {
btemp = nodecount(f[j].ept->path) * DIRSIZE;
btemp += (bpkginfo + 2L);
if ((f[j].blks + btemp) > *plimit) {
errflg++;
progerr(gettext(ERR_TOOBIG), f[j].ept->path, f[j].blks);
}
}
if (errflg)
quit(1);
if (order) {
for (i = 0; order[i]; ++i) {
while (store(sf, eptnum, order[i], *plimit, *pilimit))
;
}
}
while (store(sf, eptnum, (char *)0, *plimit, *pilimit))
;
for (i = 0; i < nclass; ++i) {
if (cl[i].first == 0)
cl[i].last = cl[i].first = (i ? cl[i-1].last : 1);
for (j = 0; j < eptnum; j++) {
if ((f[j].ept->volno == 0) &&
strcmp(f[j].ept->pkg_class, cl[i].name) == 0) {
if (strchr("sl", f[j].ept->ftype))
f[j].ept->volno = cl[i].last;
else
f[j].ept->volno = cl[i].first;
}
}
}
if (btotal)
newvolume(sf, eptnum, *plimit, *pilimit);
if (nparts > (volno - 1)) {
new_vol = volno;
for (i = volno; i <= nparts; i++) {
new_vol_set = 0;
for (j = 0; j < eptnum; j++) {
if (f[j].ept->volno == i) {
f[j].ept->volno = new_vol;
new_vol_set = 1;
}
}
new_vol += new_vol_set;
}
nparts = new_vol - 1;
} else
nparts = volno - 1;
*plimit = bmax;
*pilimit = fmax;
free(f);
free(sf);
for (i = 0; i < nclass; ++i)
free(cl[i].name);
free(cl);
for (i = 0; dirlist[i]; i++)
free(dirlist[i]);
free(dirlist);
return (errflg ? -1 : nparts);
}
static int
store(struct data **sf, unsigned int eptnum, char *aclass, fsblkcnt_t limit,
fsfilcnt_t ilimit)
{
int i, svnodes, choice, select;
long ftemp;
fsblkcnt_t btemp;
svnodes = 0;
select = 0;
choice = (-1);
for (i = 0; i < eptnum; ++i) {
if (sf[i]->ept->volno || strchr("sldxcbp", sf[i]->ept->ftype))
continue;
if (aclass && strcmp(aclass, sf[i]->ept->pkg_class))
continue;
select++;
ftemp = nodecount(sf[i]->ept->path);
btemp = sf[i]->blks + (ftemp * DIRSIZE);
if (((limit == 0) || ((btotal + btemp) <= limit)) &&
((ilimit == 0) || ((ftotal + ftemp) < ilimit))) {
choice = i;
svnodes = ftemp;
break;
}
}
if (!select)
return (0);
if (choice < 0) {
newvolume(sf, eptnum, limit, ilimit);
return (store(sf, eptnum, aclass, limit, ilimit));
}
sf[choice]->ept->volno = (char)volno;
ftotal += svnodes + 1;
btotal += sf[choice]->blks + (svnodes * DIRSIZE);
allocnode(sf[i]->ept->path);
addclass(sf[choice]->ept->pkg_class, volno);
return (++choice);
}
static void
allocnode(char *path)
{
register int i;
int found;
char *pt;
if (path == NULL) {
if (dirlist) {
for (i = 0; dirlist[i]; i++)
free(dirlist[i]);
free(dirlist);
}
dirlist = (char **)calloc(MALSIZ, sizeof (char *));
if (dirlist == NULL) {
progerr(gettext(ERR_MEMORY), errno);
quit(99);
}
return;
}
pt = path;
if (*pt == '/')
pt++;
while (pt = strchr(pt, '/')) {
*pt = '\0';
found = 0;
for (i = 0; dirlist[i] != NULL; i++) {
if (strcmp(path, dirlist[i]) == 0) {
found++;
break;
}
}
if (!found) {
dirlist[i] = qstrdup(path);
if ((++i % MALSIZ) == 0) {
dirlist = (char **)realloc(dirlist,
(i+MALSIZ) * sizeof (char *));
if (dirlist == NULL) {
progerr(gettext(ERR_MEMORY), errno);
quit(99);
}
}
dirlist[i] = (char *)NULL;
}
*pt++ = '/';
}
}
static int
nodecount(char *path)
{
char *pt;
int i, found, count;
pt = path;
if (*pt == '/')
pt++;
count = 0;
while (pt = strchr(pt, '/')) {
*pt = '\0';
found = 0;
for (i = 0; dirlist[i]; i++) {
if (strcmp(path, dirlist[i]) != 0) {
found++;
break;
}
}
if (!found)
count++;
*pt++ = '/';
}
return (count);
}
static void
newvolume(struct data **sf, unsigned int eptnum, fsblkcnt_t limit,
fsblkcnt_t ilimit)
{
register int i;
int newnodes;
if (volno) {
(void) fprintf(stderr,
gettext("part %2d -- %llu blocks, %llu entries\n"),
volno, btotal, ftotal);
if (btotal > bmax)
bmax = btotal;
if (ftotal > fmax)
fmax = ftotal;
btotal = bpkginfo + 2ULL;
ftotal = 2;
} else {
btotal = 2ULL;
ftotal = 1;
}
volno++;
allocnode((char *)0);
for (i = 0; i < eptnum; i++) {
if (sf[i]->ept->volno == volno) {
newnodes = nodecount(sf[i]->ept->path);
ftotal += newnodes + 1;
btotal += sf[i]->blks + (newnodes * DIRSIZE);
if (btotal > limit) {
progerr(gettext(ERR_VOLBLKS), volno, btotal,
limit);
quit(1);
} else if ((ilimit == 0) && (ftotal+1 > ilimit)) {
progerr(gettext(ERR_VOLFILES), volno, ftotal+1,
ilimit);
quit(1);
}
}
}
}
static void
addclass(char *aclass, int vol)
{
int i;
for (i = 0; i < nclass; ++i) {
if (strcmp(cl[i].name, aclass) == 0) {
if (vol <= 0)
return;
if (!cl[i].first || (vol < cl[i].first))
cl[i].first = vol;
if (vol > cl[i].last)
cl[i].last = vol;
return;
}
}
cl[nclass].name = qstrdup(aclass);
cl[nclass].first = vol;
cl[nclass].last = vol;
if ((++nclass % MALSIZ) == 0) {
cl = (struct class_type *)realloc((char *)cl,
sizeof (struct class_type) * (nclass+MALSIZ));
if (!cl) {
progerr(gettext(ERR_MEMORY), errno);
quit(99);
}
}
}
static void
sortsize(struct data *f, struct data **sf, unsigned int eptnum)
{
int nsf;
int j, k;
unsigned int i;
nsf = 0;
for (i = 0; i < eptnum; i++) {
for (j = 0; j < nsf; ++j) {
if (f[i].blks > sf[j]->blks) {
for (k = nsf; k > j; k--) {
sf[k] = sf[k-1];
}
break;
}
}
sf[j] = &f[i];
nsf++;
}
}