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 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 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 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 443 static void 444 allocnode(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 500 static int 501 nodecount(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 535 static void 536 newvolume(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 584 static void 585 addclass(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 613 static void 614 sortsize(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