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 2008 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 li.li_cleanup = B_FALSE; 308 309 if (devicename == NULL) { 310 /* delete by filename */ 311 (void) strlcpy(li.li_filename, filename, 312 sizeof (li.li_filename)); 313 li.li_minor = 0; 314 if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) { 315 die(gettext("could not unmap file %s"), filename); 316 } 317 return; 318 } 319 /* delete by device */ 320 321 li.li_minor = name_to_minor(devicename); 322 if (li.li_minor == 0) { 323 die(gettext("malformed device name %s\n"), devicename); 324 } 325 if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) { 326 die(gettext("could not unmap device %s"), devicename); 327 } 328 } 329 330 static void 331 print_one_mapping(int lfd, const char *devicename, const char *filename) 332 { 333 struct lofi_ioctl li; 334 335 if (devicename == NULL) { 336 /* given filename, print devicename */ 337 li.li_minor = 0; 338 (void) strlcpy(li.li_filename, filename, 339 sizeof (li.li_filename)); 340 if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) { 341 die(gettext("could not find device for %s"), filename); 342 } 343 (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor); 344 return; 345 } 346 347 /* given devicename, print filename */ 348 li.li_minor = name_to_minor(devicename); 349 if (li.li_minor == 0) { 350 die(gettext("malformed device name %s\n"), devicename); 351 } 352 if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) { 353 die(gettext("could not find filename for %s"), devicename); 354 } 355 (void) printf("%s\n", li.li_filename); 356 } 357 358 /* 359 * Uncompress a file. 360 * 361 * First map the file in to establish a device 362 * association, then read from it. On-the-fly 363 * decompression will automatically uncompress 364 * the file if it's compressed 365 * 366 * If the file is mapped and a device association 367 * has been established, disallow uncompressing 368 * the file until it is unmapped. 369 */ 370 static void 371 lofi_uncompress(int lfd, const char *filename) 372 { 373 struct lofi_ioctl li; 374 char buf[MAXBSIZE]; 375 char devicename[32]; 376 char tmpfilename[MAXPATHLEN]; 377 char *dir = NULL; 378 char *file = NULL; 379 int minor = 0; 380 struct stat64 statbuf; 381 int compfd = -1; 382 int uncompfd = -1; 383 ssize_t rbytes; 384 385 /* 386 * Disallow uncompressing the file if it is 387 * already mapped. 388 */ 389 li.li_minor = 0; 390 (void) strlcpy(li.li_filename, filename, sizeof (li.li_filename)); 391 if (ioctl(lfd, LOFI_GET_MINOR, &li) != -1) 392 die(gettext("%s must be unmapped before uncompressing"), 393 filename); 394 395 /* Zero length files don't need to be uncompressed */ 396 if (stat64(filename, &statbuf) == -1) 397 die(gettext("stat: %s"), filename); 398 if (statbuf.st_size == 0) 399 return; 400 401 add_mapping(lfd, NULL, filename, &minor, 1); 402 (void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d", 403 LOFI_BLOCK_NAME, minor); 404 405 /* If the file isn't compressed, we just return */ 406 if ((ioctl(lfd, LOFI_CHECK_COMPRESSED, &li) == -1) || 407 (li.li_algorithm == '\0')) { 408 delete_mapping(lfd, devicename, filename, B_TRUE); 409 return; 410 } 411 412 if ((compfd = open64(devicename, O_RDONLY | O_NONBLOCK)) == -1) { 413 delete_mapping(lfd, devicename, filename, B_TRUE); 414 die(gettext("open: %s"), filename); 415 } 416 /* Create a temp file in the same directory */ 417 dir = strdup(filename); 418 dir = dirname(dir); 419 file = strdup(filename); 420 file = basename(file); 421 (void) snprintf(tmpfilename, sizeof (tmpfilename), 422 "%s/.%sXXXXXX", dir, file); 423 424 if ((uncompfd = mkstemp64(tmpfilename)) == -1) { 425 (void) close(compfd); 426 delete_mapping(lfd, devicename, filename, B_TRUE); 427 free(dir); 428 free(file); 429 return; 430 } 431 432 /* 433 * Set the mode bits and the owner of this temporary 434 * file to be that of the original uncompressed file 435 */ 436 (void) fchmod(uncompfd, statbuf.st_mode); 437 438 if (fchown(uncompfd, statbuf.st_uid, statbuf.st_gid) == -1) { 439 (void) close(compfd); 440 (void) close(uncompfd); 441 delete_mapping(lfd, devicename, filename, B_TRUE); 442 free(dir); 443 free(file); 444 return; 445 } 446 447 /* Now read from the device in MAXBSIZE-sized chunks */ 448 for (;;) { 449 rbytes = read(compfd, buf, sizeof (buf)); 450 451 if (rbytes <= 0) 452 break; 453 454 if (write(uncompfd, buf, rbytes) != rbytes) { 455 rbytes = -1; 456 break; 457 } 458 } 459 460 (void) close(compfd); 461 (void) close(uncompfd); 462 free(dir); 463 free(file); 464 465 /* Delete the mapping */ 466 delete_mapping(lfd, devicename, filename, B_TRUE); 467 468 /* 469 * If an error occured while reading or writing, rbytes will 470 * be negative 471 */ 472 if (rbytes < 0) { 473 (void) unlink(tmpfilename); 474 die(gettext("could not read from %s"), filename); 475 } 476 477 /* Rename the temp file to the actual file */ 478 if (rename(tmpfilename, filename) == -1) 479 (void) unlink(tmpfilename); 480 } 481 482 /* 483 * Compress a file 484 */ 485 static void 486 lofi_compress(int lfd, const char *filename, int compress_index, 487 uint32_t segsize) 488 { 489 struct lofi_ioctl lic; 490 lofi_compress_info_t *li; 491 char tmpfilename[MAXPATHLEN]; 492 char comp_filename[MAXPATHLEN]; 493 char algorithm[MAXALGLEN]; 494 char *dir = NULL, *file = NULL; 495 uchar_t *uncompressed_seg = NULL; 496 uchar_t *compressed_seg = NULL; 497 uint32_t compressed_segsize; 498 uint32_t len_compressed, count; 499 uint32_t index_entries, index_sz; 500 uint64_t *index = NULL; 501 uint64_t offset; 502 size_t real_segsize; 503 struct stat64 statbuf; 504 int compfd = -1, uncompfd = -1; 505 int tfd = -1; 506 ssize_t rbytes, wbytes, lastread; 507 int i, type; 508 509 /* 510 * Disallow compressing the file if it is 511 * already mapped 512 */ 513 lic.li_minor = 0; 514 (void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename)); 515 if (ioctl(lfd, LOFI_GET_MINOR, &lic) != -1) 516 die(gettext("%s must be unmapped before compressing"), 517 filename); 518 519 li = &lofi_compress_table[compress_index]; 520 521 /* 522 * The size of the buffer to hold compressed data must 523 * be slightly larger than the compressed segment size. 524 * 525 * The compress functions use part of the buffer as 526 * scratch space to do calculations. 527 * Ref: http://www.zlib.net/manual.html#compress2 528 */ 529 compressed_segsize = segsize + (segsize >> 6); 530 compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR); 531 uncompressed_seg = (uchar_t *)malloc(segsize); 532 533 if (compressed_seg == NULL || uncompressed_seg == NULL) 534 die(gettext("No memory")); 535 536 if ((uncompfd = open64(filename, O_RDONLY|O_LARGEFILE, 0)) == -1) 537 die(gettext("open: %s"), filename); 538 539 if (fstat64(uncompfd, &statbuf) == -1) { 540 (void) close(uncompfd); 541 die(gettext("fstat: %s"), filename); 542 } 543 544 /* Zero length files don't need to be compressed */ 545 if (statbuf.st_size == 0) { 546 (void) close(uncompfd); 547 return; 548 } 549 550 /* 551 * Create temporary files in the same directory that 552 * will hold the intermediate data 553 */ 554 dir = strdup(filename); 555 dir = dirname(dir); 556 file = strdup(filename); 557 file = basename(file); 558 (void) snprintf(tmpfilename, sizeof (tmpfilename), 559 "%s/.%sXXXXXX", dir, file); 560 (void) snprintf(comp_filename, sizeof (comp_filename), 561 "%s/.%sXXXXXX", dir, file); 562 563 if ((tfd = mkstemp64(tmpfilename)) == -1) 564 goto cleanup; 565 566 if ((compfd = mkstemp64(comp_filename)) == -1) 567 goto cleanup; 568 569 /* 570 * Set the mode bits and owner of the compressed 571 * file to be that of the original uncompressed file 572 */ 573 (void) fchmod(compfd, statbuf.st_mode); 574 575 if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1) 576 goto cleanup; 577 578 /* 579 * Calculate the number of index entries required. 580 * index entries are stored as an array. adding 581 * a '2' here accounts for the fact that the last 582 * segment may not be a multiple of the segment size 583 */ 584 index_sz = (statbuf.st_size / segsize) + 2; 585 index = malloc(sizeof (*index) * index_sz); 586 587 if (index == NULL) 588 goto cleanup; 589 590 offset = 0; 591 lastread = segsize; 592 count = 0; 593 594 /* 595 * Now read from the uncompressed file in 'segsize' 596 * sized chunks, compress what was read in and 597 * write it out to a temporary file 598 */ 599 for (;;) { 600 rbytes = read(uncompfd, uncompressed_seg, segsize); 601 602 if (rbytes <= 0) 603 break; 604 605 if (lastread < segsize) 606 goto cleanup; 607 608 /* 609 * Account for the first byte that 610 * indicates whether a segment is 611 * compressed or not 612 */ 613 real_segsize = segsize - 1; 614 (void) li->l_compress(uncompressed_seg, rbytes, 615 compressed_seg + SEGHDR, &real_segsize, li->l_level); 616 617 /* 618 * If the length of the compressed data is more 619 * than a threshold then there isn't any benefit 620 * to be had from compressing this segment - leave 621 * it uncompressed. 622 * 623 * NB. In case an error occurs during compression (above) 624 * the 'real_segsize' isn't changed. The logic below 625 * ensures that that segment is left uncompressed. 626 */ 627 len_compressed = real_segsize; 628 if (real_segsize > segsize - COMPRESS_THRESHOLD) { 629 (void) memcpy(compressed_seg + SEGHDR, uncompressed_seg, 630 rbytes); 631 type = UNCOMPRESSED; 632 len_compressed = rbytes; 633 } else { 634 type = COMPRESSED; 635 } 636 637 /* 638 * Set the first byte or the SEGHDR to 639 * indicate if it's compressed or not 640 */ 641 *compressed_seg = type; 642 wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR); 643 if (wbytes != (len_compressed + SEGHDR)) { 644 rbytes = -1; 645 break; 646 } 647 648 index[count] = BE_64(offset); 649 offset += wbytes; 650 lastread = rbytes; 651 count++; 652 } 653 654 (void) close(uncompfd); 655 656 if (rbytes < 0) 657 goto cleanup; 658 /* 659 * The last index entry is a sentinel entry. It does not point to 660 * an actual compressed segment but helps in computing the size of 661 * the compressed segment. The size of each compressed segment is 662 * computed by subtracting the current index value from the next 663 * one (the compressed blocks are stored sequentially) 664 */ 665 index[count++] = BE_64(offset); 666 667 /* 668 * Now write the compressed data along with the 669 * header information to this file which will 670 * later be renamed to the original uncompressed 671 * file name 672 * 673 * The header is as follows - 674 * 675 * Signature (name of the compression algorithm) 676 * Compression segment size (a multiple of 512) 677 * Number of index entries 678 * Size of the last block 679 * The array containing the index entries 680 * 681 * the header is always stored in network byte 682 * order 683 */ 684 (void) bzero(algorithm, sizeof (algorithm)); 685 (void) strlcpy(algorithm, li->l_name, sizeof (algorithm)); 686 if (write(compfd, algorithm, sizeof (algorithm)) 687 != sizeof (algorithm)) 688 goto cleanup; 689 690 segsize = htonl(segsize); 691 if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize)) 692 goto cleanup; 693 694 index_entries = htonl(count); 695 if (write(compfd, &index_entries, sizeof (index_entries)) != 696 sizeof (index_entries)) 697 goto cleanup; 698 699 lastread = htonl(lastread); 700 if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread)) 701 goto cleanup; 702 703 for (i = 0; i < count; i++) { 704 if (write(compfd, index + i, sizeof (*index)) != 705 sizeof (*index)) 706 goto cleanup; 707 } 708 709 /* Header is written, now write the compressed data */ 710 if (lseek(tfd, 0, SEEK_SET) != 0) 711 goto cleanup; 712 713 rbytes = wbytes = 0; 714 715 for (;;) { 716 rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR); 717 718 if (rbytes <= 0) 719 break; 720 721 if (write(compfd, compressed_seg, rbytes) != rbytes) 722 goto cleanup; 723 } 724 725 if (fstat64(compfd, &statbuf) == -1) 726 goto cleanup; 727 728 /* 729 * Round up the compressed file size to be a multiple of 730 * DEV_BSIZE. lofi(7D) likes it that way. 731 */ 732 if ((offset = statbuf.st_size % DEV_BSIZE) > 0) { 733 734 offset = DEV_BSIZE - offset; 735 736 for (i = 0; i < offset; i++) 737 uncompressed_seg[i] = '\0'; 738 if (write(compfd, uncompressed_seg, offset) != offset) 739 goto cleanup; 740 } 741 (void) close(compfd); 742 (void) close(tfd); 743 (void) unlink(tmpfilename); 744 cleanup: 745 if (rbytes < 0) { 746 if (tfd != -1) 747 (void) unlink(tmpfilename); 748 if (compfd != -1) 749 (void) unlink(comp_filename); 750 die(gettext("error compressing file %s"), filename); 751 } else { 752 /* Rename the compressed file to the actual file */ 753 if (rename(comp_filename, filename) == -1) { 754 (void) unlink(comp_filename); 755 die(gettext("error compressing file %s"), filename); 756 } 757 } 758 if (compressed_seg != NULL) 759 free(compressed_seg); 760 if (uncompressed_seg != NULL) 761 free(uncompressed_seg); 762 if (dir != NULL) 763 free(dir); 764 if (file != NULL) 765 free(file); 766 if (index != NULL) 767 free(index); 768 if (compfd != -1) 769 (void) close(compfd); 770 if (uncompfd != -1) 771 (void) close(uncompfd); 772 if (tfd != -1) 773 (void) close(tfd); 774 } 775 776 static int 777 lofi_compress_select(const char *algname) 778 { 779 int i; 780 781 for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) { 782 if (strcmp(lofi_compress_table[i].l_name, algname) == 0) 783 return (i); 784 } 785 return (-1); 786 } 787 788 static void 789 check_algorithm_validity(const char *algname, int *compress_index) 790 { 791 *compress_index = lofi_compress_select(algname); 792 if (*compress_index < 0) 793 die(gettext("invalid algorithm name: %s\n"), algname); 794 } 795 796 static void 797 check_file_validity(const char *filename) 798 { 799 struct stat64 buf; 800 int error; 801 int fd = -1; 802 803 fd = open64(filename, O_RDONLY); 804 if (fd == -1) { 805 die(gettext("open: %s"), filename); 806 } 807 error = fstat64(fd, &buf); 808 if (error == -1) { 809 die(gettext("fstat: %s"), filename); 810 } else if (!S_ISLOFIABLE(buf.st_mode)) { 811 die(gettext("%s is not a regular file, " 812 "block, or character device\n"), 813 filename); 814 } else if ((buf.st_size % DEV_BSIZE) != 0) { 815 die(gettext("size of %s is not a multiple " 816 "of %d\n"), 817 filename, DEV_BSIZE); 818 } 819 (void) close(fd); 820 821 if (name_to_minor(filename) != 0) { 822 die(gettext("cannot use " LOFI_DRIVER_NAME 823 " on itself\n"), NULL); 824 } 825 } 826 827 static uint32_t 828 convert_to_num(const char *str) 829 { 830 int len; 831 uint32_t segsize, mult = 1; 832 833 len = strlen(str); 834 if (len && isalpha(str[len - 1])) { 835 switch (str[len - 1]) { 836 case 'k': 837 case 'K': 838 mult = KILOBYTE; 839 break; 840 case 'b': 841 case 'B': 842 mult = BLOCK_SIZE; 843 break; 844 case 'm': 845 case 'M': 846 mult = MEGABYTE; 847 break; 848 case 'g': 849 case 'G': 850 mult = GIGABYTE; 851 break; 852 default: 853 die(gettext("invalid segment size %s\n"), str); 854 } 855 } 856 857 segsize = atol(str); 858 segsize *= mult; 859 860 return (segsize); 861 } 862 863 int 864 main(int argc, char *argv[]) 865 { 866 int lfd; 867 int c; 868 const char *devicename = NULL; 869 const char *filename = NULL; 870 const char *algname = COMPRESS_ALGORITHM; 871 int openflag; 872 int minor; 873 int compress_index; 874 uint32_t segsize = SEGSIZE; 875 static char *lofictl = "/dev/" LOFI_CTL_NAME; 876 boolean_t force = B_FALSE; 877 878 pname = getpname(argv[0]); 879 880 (void) setlocale(LC_ALL, ""); 881 (void) textdomain(TEXT_DOMAIN); 882 883 while ((c = getopt(argc, argv, "a:C:d:s:U:f")) != EOF) { 884 switch (c) { 885 case 'a': 886 addflag = 1; 887 filename = optarg; 888 check_file_validity(filename); 889 890 if (((argc - optind) > 0) && (*argv[optind] != '-')) { 891 /* optional device */ 892 devicename = argv[optind]; 893 optind++; 894 } 895 break; 896 case 'C': 897 compressflag = 1; 898 899 if (((argc - optind) > 0) && 900 (*optarg == '-')) { 901 check_algorithm_validity(algname, 902 &compress_index); 903 optind--; 904 break; 905 } else if (((argc - optind) == 1) && 906 (*argv[optind] != '-')) { 907 algname = optarg; 908 filename = argv[optind]; 909 optind++; 910 } else if (((argc - optind) > 1) && 911 (*argv[optind] == '-')) { 912 algname = optarg; 913 check_algorithm_validity(algname, 914 &compress_index); 915 break; 916 } else { 917 filename = optarg; 918 } 919 920 check_file_validity(filename); 921 check_algorithm_validity(algname, &compress_index); 922 break; 923 case 'd': 924 deleteflag = 1; 925 926 minor = name_to_minor(optarg); 927 if (minor != 0) 928 devicename = optarg; 929 else 930 filename = optarg; 931 break; 932 case 'f': 933 force = B_TRUE; 934 break; 935 case 's': 936 segsize = convert_to_num(optarg); 937 938 if (segsize == 0 || segsize % DEV_BSIZE) 939 die(gettext("segment size %s is invalid " 940 "or not a multiple of minimum block " 941 "size %ld\n"), optarg, DEV_BSIZE); 942 943 filename = argv[optind]; 944 check_file_validity(filename); 945 optind++; 946 break; 947 case 'U': 948 uncompressflag = 1; 949 filename = optarg; 950 check_file_validity(filename); 951 break; 952 case '?': 953 default: 954 errflag = 1; 955 break; 956 } 957 } 958 if (errflag || 959 (addflag && deleteflag) || 960 ((compressflag || uncompressflag) && (addflag || deleteflag))) 961 usage(); 962 963 switch (argc - optind) { 964 case 0: /* no more args */ 965 break; 966 case 1: /* one arg without options means print the association */ 967 if (addflag || deleteflag) 968 usage(); 969 if (compressflag || uncompressflag) 970 usage(); 971 minor = name_to_minor(argv[optind]); 972 if (minor != 0) 973 devicename = argv[optind]; 974 else 975 filename = argv[optind]; 976 break; 977 default: 978 usage(); 979 break; 980 } 981 982 if (filename && !valid_abspath(filename)) 983 exit(E_ERROR); 984 985 /* 986 * Here, we know the arguments are correct, the filename is an 987 * absolute path, it exists and is a regular file. We don't yet 988 * know that the device name is ok or not. 989 */ 990 /* 991 * Now to the real work. 992 */ 993 openflag = O_EXCL; 994 if (addflag || deleteflag || compressflag || uncompressflag) 995 openflag |= O_RDWR; 996 else 997 openflag |= O_RDONLY; 998 lfd = open(lofictl, openflag); 999 if (lfd == -1) { 1000 if ((errno == EPERM) || (errno == EACCES)) { 1001 die("you do not have permission to perform " 1002 "that operation.\n"); 1003 } else { 1004 die("%s", lofictl); 1005 } 1006 /*NOTREACHED*/ 1007 } 1008 if (addflag) 1009 add_mapping(lfd, devicename, filename, NULL, 0); 1010 else if (compressflag) 1011 lofi_compress(lfd, filename, compress_index, segsize); 1012 else if (uncompressflag) 1013 lofi_uncompress(lfd, filename); 1014 else if (deleteflag) 1015 delete_mapping(lfd, devicename, filename, force); 1016 else if (filename || devicename) 1017 print_one_mapping(lfd, devicename, filename); 1018 else 1019 print_mappings(lfd); 1020 1021 (void) close(lfd); 1022 closelib(); 1023 return (E_SUCCESS); 1024 } 1025