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