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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * lofiadm - administer lofi(7d). Very simple, add and remove file<->device 28 * associations, and display status. All the ioctls are private between 29 * lofi and lofiadm, and so are very simple - device information is 30 * communicated via a minor number. 31 */ 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/lofi.h> 38 #include <sys/stat.h> 39 #include <netinet/in.h> 40 #include <stdio.h> 41 #include <fcntl.h> 42 #include <locale.h> 43 #include <string.h> 44 #include <strings.h> 45 #include <errno.h> 46 #include <stdlib.h> 47 #include <unistd.h> 48 #include <stropts.h> 49 #include <libdevinfo.h> 50 #include <libgen.h> 51 #include <ctype.h> 52 #include <dlfcn.h> 53 #include "utils.h" 54 55 static const char USAGE[] = 56 "Usage: %s -a file [ device ]\n" 57 " %s -d file | device \n" 58 " %s -C [algorithm] [-s segment_size] file \n" 59 " %s -U file \n" 60 " %s [ device | file ]\n"; 61 62 static const char *pname; 63 static int addflag = 0; 64 static int deleteflag = 0; 65 static int errflag = 0; 66 static int compressflag = 0; 67 static int uncompressflag = 0; 68 69 static int gzip_compress(void *src, size_t srclen, void *dst, 70 size_t *destlen, int level); 71 72 lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = { 73 {NULL, gzip_compress, 6, "gzip"}, /* default */ 74 {NULL, gzip_compress, 6, "gzip-6"}, 75 {NULL, gzip_compress, 9, "gzip-9"} 76 }; 77 78 #define FORMAT "%-20s %-30s %s\n" 79 #define NONE "-" 80 #define COMPRESS "Compressed" 81 #define COMPRESS_ALGORITHM "gzip" 82 #define COMPRESS_THRESHOLD 2048 83 #define SEGSIZE 131072 84 #define BLOCK_SIZE 512 85 #define KILOBYTE 1024 86 #define MEGABYTE (KILOBYTE * KILOBYTE) 87 #define GIGABYTE (KILOBYTE * MEGABYTE) 88 #define LIBZ "libz.so" 89 90 static int (*compress2p)(void *, ulong_t *, void *, size_t, int) = NULL; 91 92 static int gzip_compress(void *src, size_t srclen, void *dst, 93 size_t *dstlen, int level) 94 { 95 void *libz_hdl = NULL; 96 97 /* 98 * The first time we are called, attempt to dlopen() 99 * libz.so and get a pointer to the compress2() function 100 */ 101 if (compress2p == NULL) { 102 if ((libz_hdl = openlib(LIBZ)) == NULL) 103 die(gettext("could not find %s. " 104 "gzip compression unavailable\n"), LIBZ); 105 106 if ((compress2p = 107 (int (*)(void *, ulong_t *, void *, size_t, int)) 108 dlsym(libz_hdl, "compress2")) == NULL) { 109 closelib(); 110 die(gettext("could not find the correct %s. " 111 "gzip compression unavailable\n"), LIBZ); 112 } 113 } 114 115 if ((*compress2p)(dst, (ulong_t *)dstlen, src, srclen, level) != 0) 116 return (-1); 117 return (0); 118 } 119 120 /* 121 * Print the list of all the mappings. Including a header. 122 */ 123 static void 124 print_mappings(int fd) 125 { 126 struct lofi_ioctl li; 127 int minor; 128 int maxminor; 129 char path[MAXPATHLEN]; 130 char options[MAXPATHLEN]; 131 132 li.li_minor = 0; 133 if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) { 134 perror("ioctl"); 135 exit(E_ERROR); 136 } 137 138 maxminor = li.li_minor; 139 140 (void) printf(FORMAT, "Block Device", "File", "Options"); 141 for (minor = 1; minor <= maxminor; minor++) { 142 li.li_minor = minor; 143 if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) { 144 if (errno == ENXIO) 145 continue; 146 perror("ioctl"); 147 break; 148 } 149 (void) snprintf(path, sizeof (path), "/dev/%s/%d", 150 LOFI_BLOCK_NAME, minor); 151 if (li.li_algorithm[0] == '\0') 152 (void) snprintf(options, sizeof (options), "%s", NONE); 153 else 154 (void) snprintf(options, sizeof (options), 155 COMPRESS "(%s)", li.li_algorithm); 156 157 (void) printf(FORMAT, path, li.li_filename, options); 158 } 159 } 160 161 static void 162 usage(void) 163 { 164 (void) fprintf(stderr, gettext(USAGE), pname, pname, 165 pname, pname, pname); 166 exit(E_USAGE); 167 } 168 169 /* 170 * Translate a lofi device name to a minor number. We might be asked 171 * to do this when there is no association (such as when the user specifies 172 * a particular device), so we can only look at the string. 173 */ 174 static int 175 name_to_minor(const char *devicename) 176 { 177 int minor; 178 179 if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) { 180 return (minor); 181 } 182 if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) { 183 return (minor); 184 } 185 return (0); 186 } 187 188 /* 189 * This might be the first time we've used this minor number. If so, 190 * it might also be that the /dev links are in the process of being created 191 * by devfsadmd (or that they'll be created "soon"). We cannot return 192 * until they're there or the invoker of lofiadm might try to use them 193 * and not find them. This can happen if a shell script is running on 194 * an MP. 195 */ 196 static int sleeptime = 2; /* number of seconds to sleep between stat's */ 197 static int maxsleep = 120; /* maximum number of seconds to sleep */ 198 199 static void 200 wait_until_dev_complete(int minor) 201 { 202 struct stat64 buf; 203 int cursleep; 204 char blkpath[MAXPATHLEN]; 205 char charpath[MAXPATHLEN]; 206 di_devlink_handle_t hdl; 207 208 209 (void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d", 210 LOFI_BLOCK_NAME, minor); 211 (void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d", 212 LOFI_CHAR_NAME, minor); 213 214 /* Check if links already present */ 215 if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0) 216 return; 217 218 /* First use di_devlink_init() */ 219 if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) { 220 (void) di_devlink_fini(&hdl); 221 goto out; 222 } 223 224 /* 225 * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will 226 * only fail if the caller is non-root. In that case, wait for 227 * link creation via sysevents. 228 */ 229 cursleep = 0; 230 while (cursleep < maxsleep) { 231 if ((stat64(blkpath, &buf) == -1) || 232 (stat64(charpath, &buf) == -1)) { 233 (void) sleep(sleeptime); 234 cursleep += sleeptime; 235 continue; 236 } 237 return; 238 } 239 240 /* one last try */ 241 242 out: 243 if (stat64(blkpath, &buf) == -1) { 244 die(gettext("%s was not created"), blkpath); 245 } 246 if (stat64(charpath, &buf) == -1) { 247 die(gettext("%s was not created"), charpath); 248 } 249 } 250 251 /* 252 * Add a device association. If devicename is NULL, let the driver 253 * pick a device. 254 */ 255 static void 256 add_mapping(int lfd, const char *devicename, const char *filename, 257 int *minor_created, int suppress) 258 { 259 struct lofi_ioctl li; 260 int minor; 261 262 if (devicename == NULL) { 263 /* pick one */ 264 li.li_minor = 0; 265 (void) strlcpy(li.li_filename, filename, 266 sizeof (li.li_filename)); 267 minor = ioctl(lfd, LOFI_MAP_FILE, &li); 268 if (minor == -1) { 269 die(gettext("could not map file %s"), filename); 270 } 271 wait_until_dev_complete(minor); 272 /* print one picked */ 273 if (!suppress) 274 (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor); 275 276 /* fill in the minor if needed */ 277 if (minor_created != NULL) { 278 *minor_created = minor; 279 } 280 return; 281 } 282 /* use device we were given */ 283 minor = name_to_minor(devicename); 284 if (minor == 0) { 285 die(gettext("malformed device name %s\n"), devicename); 286 } 287 (void) strlcpy(li.li_filename, filename, sizeof (li.li_filename)); 288 li.li_minor = minor; 289 if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) { 290 die(gettext("could not map file %s to %s"), filename, 291 devicename); 292 } 293 wait_until_dev_complete(minor); 294 } 295 296 /* 297 * Remove an association. Delete by device name if non-NULL, or by 298 * filename otherwise. 299 */ 300 static void 301 delete_mapping(int lfd, const char *devicename, const char *filename, 302 boolean_t force) 303 { 304 struct lofi_ioctl li; 305 306 li.li_force = force; 307 if (devicename == NULL) { 308 /* delete by filename */ 309 (void) strlcpy(li.li_filename, filename, 310 sizeof (li.li_filename)); 311 li.li_minor = 0; 312 if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) { 313 die(gettext("could not unmap file %s"), filename); 314 } 315 return; 316 } 317 /* delete by device */ 318 319 li.li_minor = name_to_minor(devicename); 320 if (li.li_minor == 0) { 321 die(gettext("malformed device name %s\n"), devicename); 322 } 323 if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) { 324 die(gettext("could not unmap device %s"), devicename); 325 } 326 } 327 328 static void 329 print_one_mapping(int lfd, const char *devicename, const char *filename) 330 { 331 struct lofi_ioctl li; 332 333 if (devicename == NULL) { 334 /* given filename, print devicename */ 335 li.li_minor = 0; 336 (void) strlcpy(li.li_filename, filename, 337 sizeof (li.li_filename)); 338 if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) { 339 die(gettext("could not find device for %s"), filename); 340 } 341 (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor); 342 return; 343 } 344 345 /* given devicename, print filename */ 346 li.li_minor = name_to_minor(devicename); 347 if (li.li_minor == 0) { 348 die(gettext("malformed device name %s\n"), devicename); 349 } 350 if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) { 351 die(gettext("could not find filename for %s"), devicename); 352 } 353 (void) printf("%s\n", li.li_filename); 354 } 355 356 /* 357 * Uncompress a file. 358 * 359 * First map the file in to establish a device 360 * association, then read from it. On-the-fly 361 * decompression will automatically uncompress 362 * the file if it's compressed 363 * 364 * If the file is mapped and a device association 365 * has been established, disallow uncompressing 366 * the file until it is unmapped. 367 */ 368 static void 369 lofi_uncompress(int lfd, const char *filename) 370 { 371 struct lofi_ioctl li; 372 char buf[MAXBSIZE]; 373 char devicename[32]; 374 char tmpfilename[MAXPATHLEN]; 375 char *dir = NULL; 376 char *file = NULL; 377 int minor = 0; 378 struct stat64 statbuf; 379 int compfd = -1; 380 int uncompfd = -1; 381 ssize_t rbytes; 382 383 /* 384 * Disallow uncompressing the file if it is 385 * already mapped. 386 */ 387 li.li_minor = 0; 388 (void) strlcpy(li.li_filename, filename, sizeof (li.li_filename)); 389 if (ioctl(lfd, LOFI_GET_MINOR, &li) != -1) 390 die(gettext("%s must be unmapped before uncompressing"), 391 filename); 392 393 /* Zero length files don't need to be uncompressed */ 394 if (stat64(filename, &statbuf) == -1) 395 die(gettext("stat: %s"), filename); 396 if (statbuf.st_size == 0) 397 return; 398 399 add_mapping(lfd, NULL, filename, &minor, 1); 400 (void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d", 401 LOFI_BLOCK_NAME, minor); 402 403 /* If the file isn't compressed, we just return */ 404 if ((ioctl(lfd, LOFI_CHECK_COMPRESSED, &li) == -1) || 405 (li.li_algorithm == '\0')) { 406 delete_mapping(lfd, devicename, filename, B_TRUE); 407 return; 408 } 409 410 if ((compfd = open64(devicename, O_RDONLY | O_NONBLOCK)) == -1) { 411 delete_mapping(lfd, devicename, filename, B_TRUE); 412 die(gettext("open: %s"), filename); 413 } 414 /* Create a temp file in the same directory */ 415 dir = strdup(filename); 416 dir = dirname(dir); 417 file = strdup(filename); 418 file = basename(file); 419 (void) snprintf(tmpfilename, sizeof (tmpfilename), 420 "%s/.%sXXXXXX", dir, file); 421 422 if ((uncompfd = mkstemp64(tmpfilename)) == -1) { 423 (void) close(compfd); 424 delete_mapping(lfd, devicename, filename, B_TRUE); 425 free(dir); 426 free(file); 427 return; 428 } 429 430 /* 431 * Set the mode bits and the owner of this temporary 432 * file to be that of the original uncompressed file 433 */ 434 (void) fchmod(uncompfd, statbuf.st_mode); 435 436 if (fchown(uncompfd, statbuf.st_uid, statbuf.st_gid) == -1) { 437 (void) close(compfd); 438 (void) close(uncompfd); 439 delete_mapping(lfd, devicename, filename, B_TRUE); 440 free(dir); 441 free(file); 442 return; 443 } 444 445 /* Now read from the device in MAXBSIZE-sized chunks */ 446 for (;;) { 447 rbytes = read(compfd, buf, sizeof (buf)); 448 449 if (rbytes <= 0) 450 break; 451 452 if (write(uncompfd, buf, rbytes) != rbytes) { 453 rbytes = -1; 454 break; 455 } 456 } 457 458 (void) close(compfd); 459 (void) close(uncompfd); 460 free(dir); 461 free(file); 462 463 /* Delete the mapping */ 464 delete_mapping(lfd, devicename, filename, B_TRUE); 465 466 /* 467 * If an error occured while reading or writing, rbytes will 468 * be negative 469 */ 470 if (rbytes < 0) { 471 (void) unlink(tmpfilename); 472 die(gettext("could not read from %s"), filename); 473 } 474 475 /* Rename the temp file to the actual file */ 476 if (rename(tmpfilename, filename) == -1) 477 (void) unlink(tmpfilename); 478 } 479 480 /* 481 * Compress a file 482 */ 483 static void 484 lofi_compress(int lfd, const char *filename, int compress_index, 485 uint32_t segsize) 486 { 487 struct lofi_ioctl lic; 488 lofi_compress_info_t *li; 489 char tmpfilename[MAXPATHLEN]; 490 char comp_filename[MAXPATHLEN]; 491 char algorithm[MAXALGLEN]; 492 char *dir = NULL, *file = NULL; 493 uchar_t *uncompressed_seg = NULL; 494 uchar_t *compressed_seg = NULL; 495 uint32_t compressed_segsize; 496 uint32_t len_compressed, count; 497 uint32_t index_entries, index_sz; 498 uint64_t *index = NULL; 499 uint64_t offset; 500 size_t real_segsize; 501 struct stat64 statbuf; 502 int compfd = -1, uncompfd = -1; 503 int tfd = -1; 504 ssize_t rbytes, wbytes, lastread; 505 int i, type; 506 507 /* 508 * Disallow compressing the file if it is 509 * already mapped 510 */ 511 lic.li_minor = 0; 512 (void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename)); 513 if (ioctl(lfd, LOFI_GET_MINOR, &lic) != -1) 514 die(gettext("%s must be unmapped before compressing"), 515 filename); 516 517 li = &lofi_compress_table[compress_index]; 518 519 /* 520 * The size of the buffer to hold compressed data must 521 * be slightly larger than the compressed segment size. 522 * 523 * The compress functions use part of the buffer as 524 * scratch space to do calculations. 525 * Ref: http://www.zlib.net/manual.html#compress2 526 */ 527 compressed_segsize = segsize + (segsize >> 6); 528 compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR); 529 uncompressed_seg = (uchar_t *)malloc(segsize); 530 531 if (compressed_seg == NULL || uncompressed_seg == NULL) 532 die(gettext("No memory")); 533 534 if ((uncompfd = open64(filename, O_RDONLY|O_LARGEFILE, 0)) == -1) 535 die(gettext("open: %s"), filename); 536 537 if (fstat64(uncompfd, &statbuf) == -1) { 538 (void) close(uncompfd); 539 die(gettext("fstat: %s"), filename); 540 } 541 542 /* Zero length files don't need to be compressed */ 543 if (statbuf.st_size == 0) { 544 (void) close(uncompfd); 545 return; 546 } 547 548 /* 549 * Create temporary files in the same directory that 550 * will hold the intermediate data 551 */ 552 dir = strdup(filename); 553 dir = dirname(dir); 554 file = strdup(filename); 555 file = basename(file); 556 (void) snprintf(tmpfilename, sizeof (tmpfilename), 557 "%s/.%sXXXXXX", dir, file); 558 (void) snprintf(comp_filename, sizeof (comp_filename), 559 "%s/.%sXXXXXX", dir, file); 560 561 if ((tfd = mkstemp64(tmpfilename)) == -1) 562 goto cleanup; 563 564 if ((compfd = mkstemp64(comp_filename)) == -1) 565 goto cleanup; 566 567 /* 568 * Set the mode bits and owner of the compressed 569 * file to be that of the original uncompressed file 570 */ 571 (void) fchmod(compfd, statbuf.st_mode); 572 573 if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1) 574 goto cleanup; 575 576 /* 577 * Calculate the number of index entries required. 578 * index entries are stored as an array. adding 579 * a '2' here accounts for the fact that the last 580 * segment may not be a multiple of the segment size 581 */ 582 index_sz = (statbuf.st_size / segsize) + 2; 583 index = malloc(sizeof (*index) * index_sz); 584 585 if (index == NULL) 586 goto cleanup; 587 588 offset = 0; 589 lastread = segsize; 590 count = 0; 591 592 /* 593 * Now read from the uncompressed file in 'segsize' 594 * sized chunks, compress what was read in and 595 * write it out to a temporary file 596 */ 597 for (;;) { 598 rbytes = read(uncompfd, uncompressed_seg, segsize); 599 600 if (rbytes <= 0) 601 break; 602 603 if (lastread < segsize) 604 goto cleanup; 605 606 /* 607 * Account for the first byte that 608 * indicates whether a segment is 609 * compressed or not 610 */ 611 real_segsize = segsize - 1; 612 (void) li->l_compress(uncompressed_seg, rbytes, 613 compressed_seg + SEGHDR, &real_segsize, li->l_level); 614 615 /* 616 * If the length of the compressed data is more 617 * than a threshold then there isn't any benefit 618 * to be had from compressing this segment - leave 619 * it uncompressed. 620 * 621 * NB. In case an error occurs during compression (above) 622 * the 'real_segsize' isn't changed. The logic below 623 * ensures that that segment is left uncompressed. 624 */ 625 len_compressed = real_segsize; 626 if (real_segsize > segsize - COMPRESS_THRESHOLD) { 627 (void) memcpy(compressed_seg + SEGHDR, uncompressed_seg, 628 rbytes); 629 type = UNCOMPRESSED; 630 len_compressed = rbytes; 631 } else { 632 type = COMPRESSED; 633 } 634 635 /* 636 * Set the first byte or the SEGHDR to 637 * indicate if it's compressed or not 638 */ 639 *compressed_seg = type; 640 wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR); 641 if (wbytes != (len_compressed + SEGHDR)) { 642 rbytes = -1; 643 break; 644 } 645 646 index[count] = BE_64(offset); 647 offset += wbytes; 648 lastread = rbytes; 649 count++; 650 } 651 652 (void) close(uncompfd); 653 654 if (rbytes < 0) 655 goto cleanup; 656 /* 657 * The last index entry is a sentinel entry. It does not point to 658 * an actual compressed segment but helps in computing the size of 659 * the compressed segment. The size of each compressed segment is 660 * computed by subtracting the current index value from the next 661 * one (the compressed blocks are stored sequentially) 662 */ 663 index[count++] = BE_64(offset); 664 665 /* 666 * Now write the compressed data along with the 667 * header information to this file which will 668 * later be renamed to the original uncompressed 669 * file name 670 * 671 * The header is as follows - 672 * 673 * Signature (name of the compression algorithm) 674 * Compression segment size (a multiple of 512) 675 * Number of index entries 676 * Size of the last block 677 * The array containing the index entries 678 * 679 * the header is always stored in network byte 680 * order 681 */ 682 (void) bzero(algorithm, sizeof (algorithm)); 683 (void) strlcpy(algorithm, li->l_name, sizeof (algorithm)); 684 if (write(compfd, algorithm, sizeof (algorithm)) 685 != sizeof (algorithm)) 686 goto cleanup; 687 688 segsize = htonl(segsize); 689 if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize)) 690 goto cleanup; 691 692 index_entries = htonl(count); 693 if (write(compfd, &index_entries, sizeof (index_entries)) != 694 sizeof (index_entries)) 695 goto cleanup; 696 697 lastread = htonl(lastread); 698 if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread)) 699 goto cleanup; 700 701 for (i = 0; i < count; i++) { 702 if (write(compfd, index + i, sizeof (*index)) != 703 sizeof (*index)) 704 goto cleanup; 705 } 706 707 /* Header is written, now write the compressed data */ 708 if (lseek(tfd, 0, SEEK_SET) != 0) 709 goto cleanup; 710 711 rbytes = wbytes = 0; 712 713 for (;;) { 714 rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR); 715 716 if (rbytes <= 0) 717 break; 718 719 if (write(compfd, compressed_seg, rbytes) != rbytes) 720 goto cleanup; 721 } 722 723 if (fstat64(compfd, &statbuf) == -1) 724 goto cleanup; 725 726 /* 727 * Round up the compressed file size to be a multiple of 728 * DEV_BSIZE. lofi(7D) likes it that way. 729 */ 730 if ((offset = statbuf.st_size % DEV_BSIZE) > 0) { 731 732 offset = DEV_BSIZE - offset; 733 734 for (i = 0; i < offset; i++) 735 uncompressed_seg[i] = '\0'; 736 if (write(compfd, uncompressed_seg, offset) != offset) 737 goto cleanup; 738 } 739 (void) close(compfd); 740 (void) close(tfd); 741 (void) unlink(tmpfilename); 742 cleanup: 743 if (rbytes < 0) { 744 if (tfd != -1) 745 (void) unlink(tmpfilename); 746 if (compfd != -1) 747 (void) unlink(comp_filename); 748 die(gettext("error compressing file %s"), filename); 749 } else { 750 /* Rename the compressed file to the actual file */ 751 if (rename(comp_filename, filename) == -1) { 752 (void) unlink(comp_filename); 753 die(gettext("error compressing file %s"), filename); 754 } 755 } 756 if (compressed_seg != NULL) 757 free(compressed_seg); 758 if (uncompressed_seg != NULL) 759 free(uncompressed_seg); 760 if (dir != NULL) 761 free(dir); 762 if (file != NULL) 763 free(file); 764 if (index != NULL) 765 free(index); 766 if (compfd != -1) 767 (void) close(compfd); 768 if (uncompfd != -1) 769 (void) close(uncompfd); 770 if (tfd != -1) 771 (void) close(tfd); 772 } 773 774 static int 775 lofi_compress_select(const char *algname) 776 { 777 int i; 778 779 for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) { 780 if (strcmp(lofi_compress_table[i].l_name, algname) == 0) 781 return (i); 782 } 783 return (-1); 784 } 785 786 static void 787 check_algorithm_validity(const char *algname, int *compress_index) 788 { 789 *compress_index = lofi_compress_select(algname); 790 if (*compress_index < 0) 791 die(gettext("invalid algorithm name: %s\n"), algname); 792 } 793 794 static void 795 check_file_validity(const char *filename) 796 { 797 struct stat64 buf; 798 int error; 799 int fd = -1; 800 801 fd = open64(filename, O_RDONLY); 802 if (fd == -1) { 803 die(gettext("open: %s"), filename); 804 } 805 error = fstat64(fd, &buf); 806 if (error == -1) { 807 die(gettext("fstat: %s"), filename); 808 } else if (!S_ISLOFIABLE(buf.st_mode)) { 809 die(gettext("%s is not a regular file, " 810 "block, or character device\n"), 811 filename); 812 } else if ((buf.st_size % DEV_BSIZE) != 0) { 813 die(gettext("size of %s is not a multiple " 814 "of %d\n"), 815 filename, DEV_BSIZE); 816 } 817 (void) close(fd); 818 819 if (name_to_minor(filename) != 0) { 820 die(gettext("cannot use " LOFI_DRIVER_NAME 821 " on itself\n"), NULL); 822 } 823 } 824 825 static uint32_t 826 convert_to_num(const char *str) 827 { 828 int len; 829 uint32_t segsize, mult = 1; 830 831 len = strlen(str); 832 if (len && isalpha(str[len - 1])) { 833 switch (str[len - 1]) { 834 case 'k': 835 case 'K': 836 mult = KILOBYTE; 837 break; 838 case 'b': 839 case 'B': 840 mult = BLOCK_SIZE; 841 break; 842 case 'm': 843 case 'M': 844 mult = MEGABYTE; 845 break; 846 case 'g': 847 case 'G': 848 mult = GIGABYTE; 849 break; 850 default: 851 die(gettext("invalid segment size %s\n"), str); 852 } 853 } 854 855 segsize = atol(str); 856 segsize *= mult; 857 858 return (segsize); 859 } 860 861 int 862 main(int argc, char *argv[]) 863 { 864 int lfd; 865 int c; 866 const char *devicename = NULL; 867 const char *filename = NULL; 868 const char *algname = COMPRESS_ALGORITHM; 869 int openflag; 870 int minor; 871 int compress_index; 872 uint32_t segsize = SEGSIZE; 873 static char *lofictl = "/dev/" LOFI_CTL_NAME; 874 boolean_t force = B_FALSE; 875 876 pname = getpname(argv[0]); 877 878 (void) setlocale(LC_ALL, ""); 879 (void) textdomain(TEXT_DOMAIN); 880 881 while ((c = getopt(argc, argv, "a:C:d:s:U:f")) != EOF) { 882 switch (c) { 883 case 'a': 884 addflag = 1; 885 filename = optarg; 886 check_file_validity(filename); 887 888 if (((argc - optind) > 0) && (*argv[optind] != '-')) { 889 /* optional device */ 890 devicename = argv[optind]; 891 optind++; 892 } 893 break; 894 case 'C': 895 compressflag = 1; 896 897 if (((argc - optind) > 0) && 898 (*optarg == '-')) { 899 check_algorithm_validity(algname, 900 &compress_index); 901 optind--; 902 break; 903 } else if (((argc - optind) == 1) && 904 (*argv[optind] != '-')) { 905 algname = optarg; 906 filename = argv[optind]; 907 optind++; 908 } else if (((argc - optind) > 1) && 909 (*argv[optind] == '-')) { 910 algname = optarg; 911 check_algorithm_validity(algname, 912 &compress_index); 913 break; 914 } else { 915 filename = optarg; 916 } 917 918 check_file_validity(filename); 919 check_algorithm_validity(algname, &compress_index); 920 break; 921 case 'd': 922 deleteflag = 1; 923 924 minor = name_to_minor(optarg); 925 if (minor != 0) 926 devicename = optarg; 927 else 928 filename = optarg; 929 break; 930 case 'f': 931 force = B_TRUE; 932 break; 933 case 's': 934 segsize = convert_to_num(optarg); 935 936 if (segsize == 0 || segsize % DEV_BSIZE) 937 die(gettext("segment size %s is invalid " 938 "or not a multiple of minimum block " 939 "size %ld\n"), optarg, DEV_BSIZE); 940 941 filename = argv[optind]; 942 check_file_validity(filename); 943 optind++; 944 break; 945 case 'U': 946 uncompressflag = 1; 947 filename = optarg; 948 check_file_validity(filename); 949 break; 950 case '?': 951 default: 952 errflag = 1; 953 break; 954 } 955 } 956 if (errflag || 957 (addflag && deleteflag) || 958 ((compressflag || uncompressflag) && (addflag || deleteflag))) 959 usage(); 960 961 switch (argc - optind) { 962 case 0: /* no more args */ 963 break; 964 case 1: /* one arg without options means print the association */ 965 if (addflag || deleteflag) 966 usage(); 967 if (compressflag || uncompressflag) 968 usage(); 969 minor = name_to_minor(argv[optind]); 970 if (minor != 0) 971 devicename = argv[optind]; 972 else 973 filename = argv[optind]; 974 break; 975 default: 976 usage(); 977 break; 978 } 979 980 if (filename && !valid_abspath(filename)) 981 exit(E_ERROR); 982 983 /* 984 * Here, we know the arguments are correct, the filename is an 985 * absolute path, it exists and is a regular file. We don't yet 986 * know that the device name is ok or not. 987 */ 988 /* 989 * Now to the real work. 990 */ 991 openflag = O_EXCL; 992 if (addflag || deleteflag || compressflag || uncompressflag) 993 openflag |= O_RDWR; 994 else 995 openflag |= O_RDONLY; 996 lfd = open(lofictl, openflag); 997 if (lfd == -1) { 998 if ((errno == EPERM) || (errno == EACCES)) { 999 die("you do not have permission to perform " 1000 "that operation.\n"); 1001 } else { 1002 die("%s", lofictl); 1003 } 1004 /*NOTREACHED*/ 1005 } 1006 if (addflag) 1007 add_mapping(lfd, devicename, filename, NULL, 0); 1008 else if (compressflag) 1009 lofi_compress(lfd, filename, compress_index, segsize); 1010 else if (uncompressflag) 1011 lofi_uncompress(lfd, filename); 1012 else if (deleteflag) 1013 delete_mapping(lfd, devicename, filename, force); 1014 else if (filename || devicename) 1015 print_one_mapping(lfd, devicename, filename); 1016 else 1017 print_mappings(lfd); 1018 1019 (void) close(lfd); 1020 closelib(); 1021 return (E_SUCCESS); 1022 } 1023