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