1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 
31 #include <stdio.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <limits.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <pkgdev.h>
40 #include <pkgstrct.h>
41 #include <locale.h>
42 #include <libintl.h>
43 #include <pkglib.h>
44 #include <libadm.h>
45 #include <libinst.h>
46 
47 extern struct pkgdev pkgdev;
48 
49 #define	MALSIZ	500
50 #define	EFACTOR	128ULL	/* typical size of a single entry in a pkgmap file */
51 
52 #define	WRN_LIMIT	"WARNING: -l limit (%llu blocks) exceeds device " \
53 			"capacity (%llu blocks)"
54 #define	ERR_MEMORY	"memory allocation failure, errno=%d"
55 #define	ERR_TOOBIG	"%s (%llu blocks) does not fit on a volume"
56 #define	ERR_INFOFIRST	"information file <%s> must appear on first part"
57 #define	ERR_INFOSPACE	"all install files must appear on first part"
58 #define	ERR_VOLBLKS	"Objects selected for part %d require %llu blocks, " \
59 			"limit=%llu."
60 #define	ERR_VOLFILES	"Objects selected for part %d require %llu files, " \
61 			"limit=%llu."
62 #define	ERR_FREE	"package does not fit space currently available in <%s>"
63 
64 struct data {
65 	fsblkcnt_t	blks;
66 	struct cfent *ept;
67 };
68 
69 struct class_type {
70 	char *name;
71 	int first;
72 	int last;
73 };
74 
75 static fsblkcnt_t	btotal;	/* blocks stored on current part */
76 static fsblkcnt_t	bmax;	/* maximum number of blocks on any part */
77 
78 static fsfilcnt_t	ftotal;	/* files stored on current part */
79 static fsfilcnt_t	fmax;	/* maximum number of files on any part */
80 static fsblkcnt_t	bpkginfo;	/* blocks used by pkginfo file */
81 static char	**dirlist;
82 static short	volno;		/* current part */
83 static int	nparts = -1;	/* total number of parts */
84 static int	nclass;
85 static fsblkcnt_t	DIRSIZE;
86 static struct	class_type *cl;
87 
88 static int	nodecount(char *path);
89 static int	store(struct data **, unsigned int, char *, fsblkcnt_t,
90     fsblkcnt_t);
91 static void	addclass(char *aclass, int vol);
92 static void	allocnode(char *path);
93 static void	newvolume(struct data **, unsigned int, fsblkcnt_t limit,
94     fsblkcnt_t);
95 static void	sortsize(struct data *f, struct data **sf, unsigned int eptnum);
96 
97 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)98 splpkgmap(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 
402 static int
store(struct data ** sf,unsigned int eptnum,char * aclass,fsblkcnt_t limit,fsfilcnt_t ilimit)403 store(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 	svnodes = 0;
411 	select = 0;
412 	choice = (-1);
413 	for (i = 0; i < eptnum; ++i) {
414 		if (sf[i]->ept->volno || strchr("sldxcbp", sf[i]->ept->ftype))
415 			continue; /* defer storage until class is selected */
416 		if (aclass && strcmp(aclass, sf[i]->ept->pkg_class))
417 			continue;
418 		select++; /* we need to place at least one object */
419 		ftemp = nodecount(sf[i]->ept->path);
420 		btemp = sf[i]->blks + (ftemp * DIRSIZE);
421 		if (((limit == 0) || ((btotal + btemp) <= limit)) &&
422 		    ((ilimit == 0) || ((ftotal + ftemp) < ilimit))) {
423 			/* largest object which fits on this volume */
424 			choice = i;
425 			svnodes = ftemp;
426 			break;
427 		}
428 	}
429 	if (!select)
430 		return (0); /* no more to objects to place */
431 
432 	if (choice < 0) {
433 		newvolume(sf, eptnum, limit, ilimit);
434 		return (store(sf, eptnum, aclass, limit, ilimit));
435 	}
436 	sf[choice]->ept->volno = (char)volno;
437 	ftotal += svnodes + 1;
438 	btotal += sf[choice]->blks + (svnodes * DIRSIZE);
439 	allocnode(sf[i]->ept->path);
440 	addclass(sf[choice]->ept->pkg_class, volno);
441 	return (++choice); /* return non-zero if more work to do */
442 }
443 
444 static void
allocnode(char * path)445 allocnode(char *path)
446 {
447 	register int i;
448 	int	found;
449 	char	*pt;
450 
451 	if (path == NULL) {
452 		if (dirlist) {
453 			/*
454 			 * free everything
455 			 */
456 			for (i = 0; dirlist[i]; i++)
457 				free(dirlist[i]);
458 			free(dirlist);
459 		}
460 		dirlist = (char **)calloc(MALSIZ, sizeof (char *));
461 		if (dirlist == NULL) {
462 			progerr(gettext(ERR_MEMORY), errno);
463 			quit(99);
464 		}
465 		return;
466 	}
467 
468 	pt = path;
469 	if (*pt == '/')
470 		pt++;
471 	/*
472 	 * since the pathname supplied is never just a directory,
473 	 * we store only the dirname of of the path.
474 	 */
475 	while (pt = strchr(pt, '/')) {
476 		*pt = '\0';
477 		found = 0;
478 		for (i = 0; dirlist[i] != NULL; i++) {
479 			if (strcmp(path, dirlist[i]) == 0) {
480 				found++;
481 				break;
482 			}
483 		}
484 		if (!found) {
485 			/* insert this path in node list */
486 			dirlist[i] = qstrdup(path);
487 			if ((++i % MALSIZ) == 0) {
488 				dirlist = (char **)realloc(dirlist,
489 				    (i+MALSIZ) * sizeof (char *));
490 				if (dirlist == NULL) {
491 					progerr(gettext(ERR_MEMORY), errno);
492 					quit(99);
493 				}
494 			}
495 			dirlist[i] = (char *)NULL;
496 		}
497 		*pt++ = '/';
498 	}
499 }
500 
501 static int
nodecount(char * path)502 nodecount(char *path)
503 {
504 	char	*pt;
505 	int	i, found, count;
506 
507 	pt = path;
508 	if (*pt == '/')
509 		pt++;
510 
511 	/*
512 	 * we want to count the number of path
513 	 * segments that need to be created, not
514 	 * including the basename of the path;
515 	 * this works only since we are never
516 	 * passed a pathname which itself is a
517 	 * directory
518 	 */
519 	count = 0;
520 	while (pt = strchr(pt, '/')) {
521 		*pt = '\0';
522 		found = 0;
523 		for (i = 0; dirlist[i]; i++) {
524 			if (strcmp(path, dirlist[i]) != 0) {
525 				found++;
526 				break;
527 			}
528 		}
529 		if (!found)
530 			count++;
531 		*pt++ = '/';
532 	}
533 	return (count);
534 }
535 
536 static void
newvolume(struct data ** sf,unsigned int eptnum,fsblkcnt_t limit,fsblkcnt_t ilimit)537 newvolume(struct data **sf, unsigned int eptnum, fsblkcnt_t limit,
538     fsblkcnt_t ilimit)
539 {
540 	register int i;
541 	int	newnodes;
542 
543 	if (volno) {
544 		(void) fprintf(stderr,
545 		    gettext("part %2d -- %llu blocks, %llu entries\n"),
546 		    volno, btotal, ftotal);
547 		if (btotal > bmax)
548 			bmax = btotal;
549 		if (ftotal > fmax)
550 			fmax = ftotal;
551 		btotal = bpkginfo + 2ULL;
552 		ftotal = 2;
553 	} else {
554 		btotal = 2ULL;
555 		ftotal = 1;
556 	}
557 	volno++;
558 
559 	/*
560 	 * zero out directory storage
561 	 */
562 	allocnode((char *)0);
563 
564 	/*
565 	 * force storage of files whose volume number has already been assigned
566 	 */
567 	for (i = 0; i < eptnum; i++) {
568 		if (sf[i]->ept->volno == volno) {
569 			newnodes = nodecount(sf[i]->ept->path);
570 			ftotal += newnodes + 1;
571 			btotal += sf[i]->blks + (newnodes * DIRSIZE);
572 			if (btotal > limit) {
573 				progerr(gettext(ERR_VOLBLKS), volno, btotal,
574 				    limit);
575 				quit(1);
576 			} else if ((ilimit == 0) && (ftotal+1 > ilimit)) {
577 				progerr(gettext(ERR_VOLFILES), volno, ftotal+1,
578 				    ilimit);
579 				quit(1);
580 			}
581 		}
582 	}
583 }
584 
585 static void
addclass(char * aclass,int vol)586 addclass(char *aclass, int vol)
587 {
588 	int i;
589 
590 	for (i = 0; i < nclass; ++i) {
591 		if (strcmp(cl[i].name, aclass) == 0) {
592 			if (vol <= 0)
593 				return;
594 			if (!cl[i].first || (vol < cl[i].first))
595 				cl[i].first = vol;
596 			if (vol > cl[i].last)
597 				cl[i].last = vol;
598 			return;
599 		}
600 	}
601 	cl[nclass].name = qstrdup(aclass);
602 	cl[nclass].first = vol;
603 	cl[nclass].last = vol;
604 	if ((++nclass % MALSIZ) == 0) {
605 		cl = (struct class_type *)realloc((char *)cl,
606 		    sizeof (struct class_type) * (nclass+MALSIZ));
607 		if (!cl) {
608 			progerr(gettext(ERR_MEMORY), errno);
609 			quit(99);
610 		}
611 	}
612 }
613 
614 static void
sortsize(struct data * f,struct data ** sf,unsigned int eptnum)615 sortsize(struct data *f, struct data **sf, unsigned int eptnum)
616 {
617 	int	nsf;
618 	int	j, k;
619 	unsigned int	i;
620 
621 	nsf = 0;
622 	for (i = 0; i < eptnum; i++) {
623 		for (j = 0; j < nsf; ++j) {
624 			if (f[i].blks > sf[j]->blks) {
625 				for (k = nsf; k > j; k--) {
626 					sf[k] = sf[k-1];
627 				}
628 				break;
629 			}
630 		}
631 		sf[j] = &f[i];
632 		nsf++;
633 	}
634 }
635