splpkgmap.c revision 9781:ccf49524d5dc
1259698Sdim/*
2259698Sdim * CDDL HEADER START
3353358Sdim *
4353358Sdim * The contents of this file are subject to the terms of the
5353358Sdim * Common Development and Distribution License (the "License").
6259698Sdim * You may not use this file except in compliance with the License.
7259698Sdim *
8259698Sdim * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9259698Sdim * or http://www.opensolaris.org/os/licensing.
10259698Sdim * See the License for the specific language governing permissions
11259698Sdim * and limitations under the License.
12321369Sdim *
13259698Sdim * When distributing Covered Code, include this CDDL HEADER in each
14259698Sdim * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15321369Sdim * If applicable, add the following below this CDDL HEADER, with the
16321369Sdim * fields enclosed by brackets "[]" replaced with your own identifying
17321369Sdim * information: Portions Copyright [yyyy] [name of copyright owner]
18259698Sdim *
19259698Sdim * CDDL HEADER END
20259698Sdim */
21259698Sdim
22259698Sdim/*
23259698Sdim * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24259698Sdim * Use is subject to license terms.
25259698Sdim */
26259698Sdim
27259698Sdim/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28259698Sdim/* All Rights Reserved */
29259698Sdim
30259698Sdim
31321369Sdim#include <stdio.h>
32259698Sdim#include <errno.h>
33259698Sdim#include <string.h>
34259698Sdim#include <limits.h>
35321369Sdim#include <stdlib.h>
36259698Sdim#include <unistd.h>
37259698Sdim#include <sys/types.h>
38259698Sdim#include <sys/param.h>
39259698Sdim#include <pkgdev.h>
40259698Sdim#include <pkgstrct.h>
41259698Sdim#include <locale.h>
42259698Sdim#include <libintl.h>
43259698Sdim#include <pkglib.h>
44259698Sdim#include <libadm.h>
45259698Sdim#include <libinst.h>
46259698Sdim
47259698Sdimextern struct pkgdev pkgdev;
48259698Sdim
49259698Sdim#define	MALSIZ	500
50259698Sdim#define	EFACTOR	128ULL	/* typical size of a single entry in a pkgmap file */
51259698Sdim
52259698Sdim#define	WRN_LIMIT	"WARNING: -l limit (%llu blocks) exceeds device " \
53259698Sdim			"capacity (%llu blocks)"
54259698Sdim#define	ERR_MEMORY	"memory allocation failure, errno=%d"
55321369Sdim#define	ERR_TOOBIG	"%s (%llu blocks) does not fit on a volume"
56259698Sdim#define	ERR_INFOFIRST	"information file <%s> must appear on first part"
57259698Sdim#define	ERR_INFOSPACE	"all install files must appear on first part"
58259698Sdim#define	ERR_VOLBLKS	"Objects selected for part %d require %llu blocks, " \
59321369Sdim			"limit=%llu."
60259698Sdim#define	ERR_VOLFILES	"Objects selected for part %d require %llu files, " \
61259698Sdim			"limit=%llu."
62259698Sdim#define	ERR_FREE	"package does not fit space currently available in <%s>"
63259698Sdim
64321369Sdimstruct data {
65321369Sdim	fsblkcnt_t	blks;
66259698Sdim	struct cfent *ept;
67259698Sdim};
68259698Sdim
69321369Sdimstruct class_type {
70259698Sdim	char *name;
71259698Sdim	int first;
72321369Sdim	int last;
73259698Sdim};
74321369Sdim
75321369Sdimstatic fsblkcnt_t	btotal;	/* blocks stored on current part */
76259698Sdimstatic fsblkcnt_t	bmax; 	/* maximum number of blocks on any part */
77259698Sdim
78259698Sdimstatic fsfilcnt_t	ftotal;	/* files stored on current part */
79259698Sdimstatic fsfilcnt_t	fmax;	/* maximum number of files on any part */
80259698Sdimstatic fsblkcnt_t	bpkginfo; 	/* blocks used by pkginfo file */
81259698Sdimstatic char	**dirlist;
82321369Sdimstatic short	volno; 		/* current part */
83321369Sdimstatic int	nparts = -1; 	/* total number of parts */
84321369Sdimstatic int	nclass;
85static fsblkcnt_t 	DIRSIZE;
86static struct	class_type *cl;
87
88static int	nodecount(char *path);
89static int	store(struct data **, unsigned int, char *, fsblkcnt_t,
90    fsblkcnt_t);
91static void	addclass(char *aclass, int vol);
92static void	allocnode(char *path);
93static void	newvolume(struct data **, unsigned int, fsblkcnt_t limit,
94    fsblkcnt_t);
95static void	sortsize(struct data *f, struct data **sf, unsigned int eptnum);
96
97int
98splpkgmap(struct cfent **eptlist, unsigned int eptnum, char *order[],
99    ulong_t bsize, ulong_t frsize, fsblkcnt_t *plimit, fsfilcnt_t *pilimit,
100    fsblkcnt_t *pllimit)
101{
102	struct data	*f, **sf;
103	struct cfent	*ept;
104	register int	i, j;
105	int		new_vol_set;
106	short		new_vol;
107	int		flag, errflg;
108	fsblkcnt_t	total;
109	fsblkcnt_t	btemp;
110	fsfilcnt_t	ftemp;
111
112	f = (struct data *)calloc(eptnum, sizeof (struct data));
113	if (f == NULL) {
114		progerr(gettext(ERR_MEMORY), errno);
115		quit(99);
116	}
117
118	sf = (struct data **)calloc(eptnum, sizeof (struct data *));
119	if (sf == NULL) {
120		progerr(gettext(ERR_MEMORY), errno);
121		quit(99);
122	}
123
124	nclass = 0;
125	cl = (struct class_type *)calloc(MALSIZ, sizeof (struct class_type));
126	if (cl == NULL) {
127		progerr(gettext(ERR_MEMORY), errno);
128		quit(99);
129	}
130
131	errflg = 0;
132
133	/*
134	 * The next bit of code checks to see if, when creating a package
135	 * on a directory, there are enough free blocks and inodes before
136	 * continuing.
137	 */
138	total = 0;
139	/*
140	 * DIRSIZE takes up 1 logical block, iff we have no frags, else
141	 * it just takes a frag
142	 */
143	DIRSIZE = ((fsblkcnt_t)frsize > 0) ?
144	    howmany(frsize, DEV_BSIZE) :
145	    howmany(bsize, DEV_BSIZE);
146
147	if (!pkgdev.mount) {
148		allocnode(NULL);
149		/*
150		 * If we appear to have a valid value for free inodes
151		 * and there's not enough for the package contents,
152		 * then exit
153		 */
154		if ((*pilimit > 0) && (eptnum+1 > *pilimit)) {
155			progerr(gettext(ERR_FREE), pkgdev.dirname);
156			quit(1);
157		}
158		for (i = 0; i < eptnum; i++) {
159			if (strchr("dxslcbp", eptlist[i]->ftype))
160				continue;
161			else {
162				total +=
163				    (nodecount(eptlist[i]->path) * DIRSIZE);
164				total +=
165				    nblk(eptlist[i]->cinfo.size, bsize, frsize);
166				if (total > *plimit) {
167					progerr(gettext(ERR_FREE),
168						pkgdev.dirname);
169					quit(1);
170				}
171				allocnode(eptlist[i]->path);
172			}
173		}
174	}
175	/*
176	 * if there is a value in pllimit (-l specified limit), use that for
177	 * the limit from now on.
178	 */
179
180	if (*pllimit != 0) {
181		if (pkgdev.mount && *pllimit > *plimit)
182			logerr(gettext(WRN_LIMIT), *pllimit, *plimit);
183		*plimit = *pllimit;
184	}
185	/*
186	 * calculate number of physical blocks used by each object
187	 */
188	for (i = 0; i < eptnum; i++) {
189		f[i].ept = ept = eptlist[i];
190		if (ept->volno > nparts)
191			nparts = ept->volno;
192		addclass(ept->pkg_class, 0);
193		if (strchr("dxslcbp", ept->ftype))
194			/*
195			 * virtual object (no contents)
196			 */
197			f[i].blks = 0;
198		else
199			/*
200			 * space consumers
201			 *
202			 * (directories are space consumers as well, but they
203			 * get accounted for later).
204			 *
205			 */
206
207			f[i].blks = nblk(ept->cinfo.size, bsize, frsize);
208
209		if (!bpkginfo && (strcmp(f[i].ept->path, "pkginfo") == 0))
210			bpkginfo = f[i].blks;
211	}
212
213	/*
214	 * Make sure that items slated for a given 'part' do not exceed a single
215	 * volume.
216	 */
217	for (i = 1; i <= nparts; i++) {
218		btemp = (bpkginfo + 2LL);
219		ftemp = 2LL;
220		if (i == 1) {
221			/*
222			 * save room for install directory
223			 */
224			ftemp += 2;
225			btemp += nblk(eptnum * EFACTOR, bsize, frsize);
226			btemp += 2;
227		}
228		allocnode(NULL);
229		for (j = 0; j < eptnum; j++) {
230			if (i == 1 && f[j].ept->ftype == 'i' &&
231			    (strcmp(f[j].ept->path, "pkginfo") == 0 ||
232			    strcmp(f[j].ept->path, "pkgmap") == 0))
233				continue;
234			if (f[j].ept->volno == i ||
235			    (f[j].ept->ftype == 'i' && i == 1)) {
236				ftemp += nodecount(f[j].ept->path);
237				btemp += f[j].blks;
238				allocnode(f[j].ept->path);
239			}
240		}
241		btemp += (ftemp * DIRSIZE);
242		if (btemp > *plimit) {
243			progerr(gettext(ERR_VOLBLKS), i, btemp, *plimit);
244			errflg++;
245		/* If we have a valid inode limit, ensure this part will fit */
246		} else if ((*pilimit > 0) && (ftemp+1 > *pilimit)) {
247			progerr(gettext(ERR_VOLFILES), i, ftemp + 1, *pilimit);
248			errflg++;
249		}
250	}
251	if (errflg)
252		quit(1);
253
254	/*
255	 * "sf" - array sorted in decreasing file size order, based on "f".
256	 */
257	sortsize(f, sf, eptnum);
258
259	/*
260	 * initialize first volume
261	 */
262	newvolume(sf, eptnum, *plimit, *pilimit);
263
264	/*
265	 * reserve room on first volume for pkgmap
266	 */
267	btotal += nblk((fsblkcnt_t)(eptnum * EFACTOR), bsize, frsize);
268	ftotal++;
269
270
271	/*
272	 * initialize directory info
273	 */
274	allocnode(NULL);
275
276	/*
277	 * place installation files on first volume!
278	 */
279	flag = 0;
280	for (j = 0; j < eptnum; ++j) {
281		if (f[j].ept->ftype != 'i')
282			continue;
283		else if (!flag++) {
284			/*
285			 * save room for install directory
286			 */
287			ftotal++;
288			btotal += 2ULL;
289		}
290		if (!f[j].ept->volno) {
291			f[j].ept->volno = 1;
292			ftotal++;
293			btotal += f[j].blks;
294		} else if (f[j].ept->volno != 1) {
295			progerr(gettext(ERR_INFOFIRST), f[j].ept->path);
296			errflg++;
297		}
298	}
299
300	if (errflg)
301		quit(1);
302	if (btotal > *plimit) {
303		progerr(gettext(ERR_INFOSPACE));
304		quit(1);
305	}
306
307	/*
308	 * Make sure that any given file will fit on a single volume, this
309	 * calculation has to take into account packaging overhead, otherwise
310	 * the function store() will go into a severe recursive plunge.
311	 */
312	for (j = 0; j < eptnum; ++j) {
313		/*
314		 * directory overhead.
315		 */
316		btemp = nodecount(f[j].ept->path) * DIRSIZE;
317		/*
318		 * packaging overhead.
319		 */
320		btemp += (bpkginfo + 2L); 	/* from newvolume() */
321		if ((f[j].blks + btemp) > *plimit) {
322			errflg++;
323			progerr(gettext(ERR_TOOBIG), f[j].ept->path, f[j].blks);
324		}
325	}
326	if (errflg)
327		quit(1);
328
329	/*
330	 * place classes listed on command line
331	 */
332	if (order) {
333		for (i = 0; order[i]; ++i)  {
334			while (store(sf, eptnum, order[i], *plimit, *pilimit))
335				/* stay in loop until store is complete */
336				/* void */;
337		}
338	}
339
340	while (store(sf, eptnum, (char *)0, *plimit, *pilimit))
341		/* stay in loop until store is complete */
342		/* void */;
343
344	/*
345	 * place all virtual objects, e.g. links and spec devices
346	 */
347	for (i = 0; i < nclass; ++i) {
348		/*
349		 * if no objects were associated, attempt to
350		 * distribute in order of class list
351		 */
352		if (cl[i].first == 0)
353			cl[i].last = cl[i].first = (i ? cl[i-1].last : 1);
354		for (j = 0; j < eptnum; j++) {
355			if ((f[j].ept->volno == 0) &&
356			    strcmp(f[j].ept->pkg_class, cl[i].name) == 0) {
357				if (strchr("sl", f[j].ept->ftype))
358					f[j].ept->volno = cl[i].last;
359				else
360					f[j].ept->volno = cl[i].first;
361			}
362		}
363	}
364
365	if (btotal)
366		newvolume(sf, eptnum, *plimit, *pilimit);
367
368	if (nparts > (volno - 1)) {
369		new_vol = volno;
370		for (i = volno; i <= nparts; i++) {
371			new_vol_set = 0;
372			for (j = 0; j < eptnum; j++) {
373				if (f[j].ept->volno == i) {
374					f[j].ept->volno = new_vol;
375					new_vol_set = 1;
376				}
377			}
378			new_vol += new_vol_set;
379		}
380		nparts = new_vol - 1;
381	} else
382		nparts = volno - 1;
383
384	*plimit = bmax;
385	*pilimit = fmax;
386
387	/*
388	 * free up dynamic space used by this module
389	 */
390	free(f);
391	free(sf);
392	for (i = 0; i < nclass; ++i)
393		free(cl[i].name);
394	free(cl);
395	for (i = 0; dirlist[i]; i++)
396		free(dirlist[i]);
397	free(dirlist);
398
399	return (errflg ? -1 : nparts);
400}
401
402static int
403store(struct data **sf, unsigned int eptnum, char *aclass, fsblkcnt_t limit,
404    fsfilcnt_t ilimit)
405{
406	int	i, svnodes, choice, select;
407	long	ftemp;
408	fsblkcnt_t	btemp;
409
410	select = 0;
411	choice = (-1);
412	for (i = 0; i < eptnum; ++i) {
413		if (sf[i]->ept->volno || strchr("sldxcbp", sf[i]->ept->ftype))
414			continue; /* defer storage until class is selected */
415		if (aclass && strcmp(aclass, sf[i]->ept->pkg_class))
416			continue;
417		select++; /* we need to place at least one object */
418		ftemp = nodecount(sf[i]->ept->path);
419		btemp = sf[i]->blks + (ftemp * DIRSIZE);
420		if (((limit == 0) || ((btotal + btemp) <= limit)) &&
421		    ((ilimit == 0) || ((ftotal + ftemp) < ilimit))) {
422			/* largest object which fits on this volume */
423			choice = i;
424			svnodes = ftemp;
425			break;
426		}
427	}
428	if (!select)
429		return (0); /* no more to objects to place */
430
431	if (choice < 0) {
432		newvolume(sf, eptnum, limit, ilimit);
433		return (store(sf, eptnum, aclass, limit, ilimit));
434	}
435	sf[choice]->ept->volno = (char)volno;
436	ftotal += svnodes + 1;
437	btotal += sf[choice]->blks + (svnodes * DIRSIZE);
438	allocnode(sf[i]->ept->path);
439	addclass(sf[choice]->ept->pkg_class, volno);
440	return (++choice); /* return non-zero if more work to do */
441}
442
443static void
444allocnode(char *path)
445{
446	register int i;
447	int	found;
448	char	*pt;
449
450	if (path == NULL) {
451		if (dirlist) {
452			/*
453			 * free everything
454			 */
455			for (i = 0; dirlist[i]; i++)
456				free(dirlist[i]);
457			free(dirlist);
458		}
459		dirlist = (char **)calloc(MALSIZ, sizeof (char *));
460		if (dirlist == NULL) {
461			progerr(gettext(ERR_MEMORY), errno);
462			quit(99);
463		}
464		return;
465	}
466
467	pt = path;
468	if (*pt == '/')
469		pt++;
470	/*
471	 * since the pathname supplied is never just a directory,
472	 * we store only the dirname of of the path.
473	 */
474	while (pt = strchr(pt, '/')) {
475		*pt = '\0';
476		found = 0;
477		for (i = 0; dirlist[i] != NULL; i++) {
478			if (strcmp(path, dirlist[i]) == 0) {
479				found++;
480				break;
481			}
482		}
483		if (!found) {
484			/* insert this path in node list */
485			dirlist[i] = qstrdup(path);
486			if ((++i % MALSIZ) == 0) {
487				dirlist = (char **)realloc(dirlist,
488					(i+MALSIZ) * sizeof (char *));
489				if (dirlist == NULL) {
490					progerr(gettext(ERR_MEMORY), errno);
491					quit(99);
492				}
493			}
494			dirlist[i] = (char *)NULL;
495		}
496		*pt++ = '/';
497	}
498}
499
500static int
501nodecount(char *path)
502{
503	char	*pt;
504	int	i, found, count;
505
506	pt = path;
507	if (*pt == '/')
508		pt++;
509
510	/*
511	 * we want to count the number of path
512	 * segments that need to be created, not
513	 * including the basename of the path;
514	 * this works only since we are never
515	 * passed a pathname which itself is a
516	 * directory
517	 */
518	count = 0;
519	while (pt = strchr(pt, '/')) {
520		*pt = '\0';
521		found = 0;
522		for (i = 0; dirlist[i]; i++) {
523			if (strcmp(path, dirlist[i]) != 0) {
524				found++;
525				break;
526			}
527		}
528		if (!found)
529			count++;
530		*pt++ = '/';
531	}
532	return (count);
533}
534
535static void
536newvolume(struct data **sf, unsigned int eptnum, fsblkcnt_t limit,
537    fsblkcnt_t ilimit)
538{
539	register int i;
540	int	newnodes;
541
542	if (volno) {
543		(void) fprintf(stderr,
544		    gettext("part %2d -- %llu blocks, %llu entries\n"),
545		    volno, btotal, ftotal);
546		if (btotal > bmax)
547			bmax = btotal;
548		if (ftotal > fmax)
549			fmax = ftotal;
550		btotal = bpkginfo + 2ULL;
551		ftotal = 2;
552	} else {
553		btotal = 2ULL;
554		ftotal = 1;
555	}
556	volno++;
557
558	/*
559	 * zero out directory storage
560	 */
561	allocnode((char *)0);
562
563	/*
564	 * force storage of files whose volume number has already been assigned
565	 */
566	for (i = 0; i < eptnum; i++) {
567		if (sf[i]->ept->volno == volno) {
568			newnodes = nodecount(sf[i]->ept->path);
569			ftotal += newnodes + 1;
570			btotal += sf[i]->blks + (newnodes * DIRSIZE);
571			if (btotal > limit) {
572				progerr(gettext(ERR_VOLBLKS), volno, btotal,
573					limit);
574				quit(1);
575			} else if ((ilimit == 0) && (ftotal+1 > ilimit)) {
576				progerr(gettext(ERR_VOLFILES), volno, ftotal+1,
577				    ilimit);
578				quit(1);
579			}
580		}
581	}
582}
583
584static void
585addclass(char *aclass, int vol)
586{
587	int i;
588
589	for (i = 0; i < nclass; ++i) {
590		if (strcmp(cl[i].name, aclass) == 0) {
591			if (vol <= 0)
592				return;
593			if (!cl[i].first || (vol < cl[i].first))
594				cl[i].first = vol;
595			if (vol > cl[i].last)
596				cl[i].last = vol;
597			return;
598		}
599	}
600	cl[nclass].name = qstrdup(aclass);
601	cl[nclass].first = vol;
602	cl[nclass].last = vol;
603	if ((++nclass % MALSIZ) == 0) {
604		cl = (struct class_type *)realloc((char *)cl,
605			sizeof (struct class_type) * (nclass+MALSIZ));
606		if (!cl) {
607			progerr(gettext(ERR_MEMORY), errno);
608			quit(99);
609		}
610	}
611}
612
613static void
614sortsize(struct data *f, struct data **sf, unsigned int eptnum)
615{
616	int	nsf;
617	int	j, k;
618	unsigned int	i;
619
620	nsf = 0;
621	for (i = 0; i < eptnum; i++) {
622		for (j = 0; j < nsf; ++j) {
623			if (f[i].blks > sf[j]->blks) {
624				for (k = nsf; k > j; k--) {
625					sf[k] = sf[k-1];
626				}
627				break;
628			}
629		}
630		sf[j] = &f[i];
631		nsf++;
632	}
633}
634