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 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 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 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 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 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 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