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] == '\0')) { 408 delete_mapping(lfd, devicename, filename, B_TRUE); 409 die("%s is not compressed\n", filename); 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 die("%s could not be uncompressed\n", filename); 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 die("%s could not be uncompressed\n", filename); 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 struct flock lock; 492 char tmpfilename[MAXPATHLEN]; 493 char comp_filename[MAXPATHLEN]; 494 char algorithm[MAXALGLEN]; 495 char *dir = NULL, *file = NULL; 496 uchar_t *uncompressed_seg = NULL; 497 uchar_t *compressed_seg = NULL; 498 uint32_t compressed_segsize; 499 uint32_t len_compressed, count; 500 uint32_t index_entries, index_sz; 501 uint64_t *index = NULL; 502 uint64_t offset; 503 size_t real_segsize; 504 struct stat64 statbuf; 505 int compfd = -1, uncompfd = -1; 506 int tfd = -1; 507 ssize_t rbytes, wbytes, lastread; 508 int i, type; 509 510 /* 511 * Disallow compressing the file if it is 512 * already mapped 513 */ 514 lic.li_minor = 0; 515 (void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename)); 516 if (ioctl(*lfd, LOFI_GET_MINOR, &lic) != -1) 517 die(gettext("%s must be unmapped before compressing"), 518 filename); 519 520 /* 521 * Close the control device so other operations 522 * can use it 523 */ 524 (void) close(*lfd); 525 *lfd = -1; 526 527 li = &lofi_compress_table[compress_index]; 528 529 /* 530 * The size of the buffer to hold compressed data must 531 * be slightly larger than the compressed segment size. 532 * 533 * The compress functions use part of the buffer as 534 * scratch space to do calculations. 535 * Ref: http://www.zlib.net/manual.html#compress2 536 */ 537 compressed_segsize = segsize + (segsize >> 6); 538 compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR); 539 uncompressed_seg = (uchar_t *)malloc(segsize); 540 541 if (compressed_seg == NULL || uncompressed_seg == NULL) 542 die(gettext("No memory")); 543 544 if ((uncompfd = open64(filename, O_RDWR|O_LARGEFILE, 0)) == -1) 545 die(gettext("open: %s"), filename); 546 547 lock.l_type = F_WRLCK; 548 lock.l_whence = SEEK_SET; 549 lock.l_start = 0; 550 lock.l_len = 0; 551 552 /* 553 * Use an advisory lock to ensure that only a 554 * single lofiadm process compresses a given 555 * file at any given time 556 * 557 * A close on the file descriptor automatically 558 * closes all lock state on the file 559 */ 560 if (fcntl(uncompfd, F_SETLKW, &lock) == -1) 561 die(gettext("fcntl: %s"), filename); 562 563 if (fstat64(uncompfd, &statbuf) == -1) { 564 (void) close(uncompfd); 565 die(gettext("fstat: %s"), filename); 566 } 567 568 /* Zero length files don't need to be compressed */ 569 if (statbuf.st_size == 0) { 570 (void) close(uncompfd); 571 return; 572 } 573 574 /* 575 * Create temporary files in the same directory that 576 * will hold the intermediate data 577 */ 578 dir = strdup(filename); 579 dir = dirname(dir); 580 file = strdup(filename); 581 file = basename(file); 582 (void) snprintf(tmpfilename, sizeof (tmpfilename), 583 "%s/.%sXXXXXX", dir, file); 584 (void) snprintf(comp_filename, sizeof (comp_filename), 585 "%s/.%sXXXXXX", dir, file); 586 587 if ((tfd = mkstemp64(tmpfilename)) == -1) 588 goto cleanup; 589 590 if ((compfd = mkstemp64(comp_filename)) == -1) 591 goto cleanup; 592 593 /* 594 * Set the mode bits and owner of the compressed 595 * file to be that of the original uncompressed file 596 */ 597 (void) fchmod(compfd, statbuf.st_mode); 598 599 if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1) 600 goto cleanup; 601 602 /* 603 * Calculate the number of index entries required. 604 * index entries are stored as an array. adding 605 * a '2' here accounts for the fact that the last 606 * segment may not be a multiple of the segment size 607 */ 608 index_sz = (statbuf.st_size / segsize) + 2; 609 index = malloc(sizeof (*index) * index_sz); 610 611 if (index == NULL) 612 goto cleanup; 613 614 offset = 0; 615 lastread = segsize; 616 count = 0; 617 618 /* 619 * Now read from the uncompressed file in 'segsize' 620 * sized chunks, compress what was read in and 621 * write it out to a temporary file 622 */ 623 for (;;) { 624 rbytes = read(uncompfd, uncompressed_seg, segsize); 625 626 if (rbytes <= 0) 627 break; 628 629 if (lastread < segsize) 630 goto cleanup; 631 632 /* 633 * Account for the first byte that 634 * indicates whether a segment is 635 * compressed or not 636 */ 637 real_segsize = segsize - 1; 638 (void) li->l_compress(uncompressed_seg, rbytes, 639 compressed_seg + SEGHDR, &real_segsize, li->l_level); 640 641 /* 642 * If the length of the compressed data is more 643 * than a threshold then there isn't any benefit 644 * to be had from compressing this segment - leave 645 * it uncompressed. 646 * 647 * NB. In case an error occurs during compression (above) 648 * the 'real_segsize' isn't changed. The logic below 649 * ensures that that segment is left uncompressed. 650 */ 651 len_compressed = real_segsize; 652 if (real_segsize > segsize - COMPRESS_THRESHOLD) { 653 (void) memcpy(compressed_seg + SEGHDR, uncompressed_seg, 654 rbytes); 655 type = UNCOMPRESSED; 656 len_compressed = rbytes; 657 } else { 658 type = COMPRESSED; 659 } 660 661 /* 662 * Set the first byte or the SEGHDR to 663 * indicate if it's compressed or not 664 */ 665 *compressed_seg = type; 666 wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR); 667 if (wbytes != (len_compressed + SEGHDR)) { 668 rbytes = -1; 669 break; 670 } 671 672 index[count] = BE_64(offset); 673 offset += wbytes; 674 lastread = rbytes; 675 count++; 676 } 677 678 (void) close(uncompfd); 679 680 if (rbytes < 0) 681 goto cleanup; 682 /* 683 * The last index entry is a sentinel entry. It does not point to 684 * an actual compressed segment but helps in computing the size of 685 * the compressed segment. The size of each compressed segment is 686 * computed by subtracting the current index value from the next 687 * one (the compressed blocks are stored sequentially) 688 */ 689 index[count++] = BE_64(offset); 690 691 /* 692 * Now write the compressed data along with the 693 * header information to this file which will 694 * later be renamed to the original uncompressed 695 * file name 696 * 697 * The header is as follows - 698 * 699 * Signature (name of the compression algorithm) 700 * Compression segment size (a multiple of 512) 701 * Number of index entries 702 * Size of the last block 703 * The array containing the index entries 704 * 705 * the header is always stored in network byte 706 * order 707 */ 708 (void) bzero(algorithm, sizeof (algorithm)); 709 (void) strlcpy(algorithm, li->l_name, sizeof (algorithm)); 710 if (write(compfd, algorithm, sizeof (algorithm)) 711 != sizeof (algorithm)) 712 goto cleanup; 713 714 segsize = htonl(segsize); 715 if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize)) 716 goto cleanup; 717 718 index_entries = htonl(count); 719 if (write(compfd, &index_entries, sizeof (index_entries)) != 720 sizeof (index_entries)) 721 goto cleanup; 722 723 lastread = htonl(lastread); 724 if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread)) 725 goto cleanup; 726 727 for (i = 0; i < count; i++) { 728 if (write(compfd, index + i, sizeof (*index)) != 729 sizeof (*index)) 730 goto cleanup; 731 } 732 733 /* Header is written, now write the compressed data */ 734 if (lseek(tfd, 0, SEEK_SET) != 0) 735 goto cleanup; 736 737 rbytes = wbytes = 0; 738 739 for (;;) { 740 rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR); 741 742 if (rbytes <= 0) 743 break; 744 745 if (write(compfd, compressed_seg, rbytes) != rbytes) 746 goto cleanup; 747 } 748 749 if (fstat64(compfd, &statbuf) == -1) 750 goto cleanup; 751 752 /* 753 * Round up the compressed file size to be a multiple of 754 * DEV_BSIZE. lofi(7D) likes it that way. 755 */ 756 if ((offset = statbuf.st_size % DEV_BSIZE) > 0) { 757 758 offset = DEV_BSIZE - offset; 759 760 for (i = 0; i < offset; i++) 761 uncompressed_seg[i] = '\0'; 762 if (write(compfd, uncompressed_seg, offset) != offset) 763 goto cleanup; 764 } 765 (void) close(compfd); 766 (void) close(tfd); 767 (void) unlink(tmpfilename); 768 cleanup: 769 if (rbytes < 0) { 770 if (tfd != -1) 771 (void) unlink(tmpfilename); 772 if (compfd != -1) 773 (void) unlink(comp_filename); 774 die(gettext("error compressing file %s"), filename); 775 } else { 776 /* Rename the compressed file to the actual file */ 777 if (rename(comp_filename, filename) == -1) { 778 (void) unlink(comp_filename); 779 die(gettext("error compressing file %s"), filename); 780 } 781 } 782 if (compressed_seg != NULL) 783 free(compressed_seg); 784 if (uncompressed_seg != NULL) 785 free(uncompressed_seg); 786 if (dir != NULL) 787 free(dir); 788 if (file != NULL) 789 free(file); 790 if (index != NULL) 791 free(index); 792 if (compfd != -1) 793 (void) close(compfd); 794 if (uncompfd != -1) 795 (void) close(uncompfd); 796 if (tfd != -1) 797 (void) close(tfd); 798 } 799 800 static int 801 lofi_compress_select(const char *algname) 802 { 803 int i; 804 805 for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) { 806 if (strcmp(lofi_compress_table[i].l_name, algname) == 0) 807 return (i); 808 } 809 return (-1); 810 } 811 812 static void 813 check_algorithm_validity(const char *algname, int *compress_index) 814 { 815 *compress_index = lofi_compress_select(algname); 816 if (*compress_index < 0) 817 die(gettext("invalid algorithm name: %s\n"), algname); 818 } 819 820 static void 821 check_file_validity(const char *filename) 822 { 823 struct stat64 buf; 824 int error; 825 int fd = -1; 826 827 fd = open64(filename, O_RDONLY); 828 if (fd == -1) { 829 die(gettext("open: %s"), filename); 830 } 831 error = fstat64(fd, &buf); 832 if (error == -1) { 833 die(gettext("fstat: %s"), filename); 834 } else if (!S_ISLOFIABLE(buf.st_mode)) { 835 die(gettext("%s is not a regular file, " 836 "block, or character device\n"), 837 filename); 838 } else if ((buf.st_size % DEV_BSIZE) != 0) { 839 die(gettext("size of %s is not a multiple " 840 "of %d\n"), 841 filename, DEV_BSIZE); 842 } 843 (void) close(fd); 844 845 if (name_to_minor(filename) != 0) { 846 die(gettext("cannot use " LOFI_DRIVER_NAME 847 " on itself\n"), NULL); 848 } 849 } 850 851 static uint32_t 852 convert_to_num(const char *str) 853 { 854 int len; 855 uint32_t segsize, mult = 1; 856 857 len = strlen(str); 858 if (len && isalpha(str[len - 1])) { 859 switch (str[len - 1]) { 860 case 'k': 861 case 'K': 862 mult = KILOBYTE; 863 break; 864 case 'b': 865 case 'B': 866 mult = BLOCK_SIZE; 867 break; 868 case 'm': 869 case 'M': 870 mult = MEGABYTE; 871 break; 872 case 'g': 873 case 'G': 874 mult = GIGABYTE; 875 break; 876 default: 877 die(gettext("invalid segment size %s\n"), str); 878 } 879 } 880 881 segsize = atol(str); 882 segsize *= mult; 883 884 return (segsize); 885 } 886 887 int 888 main(int argc, char *argv[]) 889 { 890 int lfd; 891 int c; 892 const char *devicename = NULL; 893 const char *filename = NULL; 894 const char *algname = COMPRESS_ALGORITHM; 895 int openflag; 896 int minor; 897 int compress_index; 898 uint32_t segsize = SEGSIZE; 899 static char *lofictl = "/dev/" LOFI_CTL_NAME; 900 boolean_t force = B_FALSE; 901 902 pname = getpname(argv[0]); 903 904 (void) setlocale(LC_ALL, ""); 905 (void) textdomain(TEXT_DOMAIN); 906 907 while ((c = getopt(argc, argv, "a:C:d:s:U:f")) != EOF) { 908 switch (c) { 909 case 'a': 910 addflag = 1; 911 filename = optarg; 912 check_file_validity(filename); 913 914 if (((argc - optind) > 0) && (*argv[optind] != '-')) { 915 /* optional device */ 916 devicename = argv[optind]; 917 optind++; 918 } 919 break; 920 case 'C': 921 compressflag = 1; 922 923 if (((argc - optind) > 0) && 924 (*optarg == '-')) { 925 check_algorithm_validity(algname, 926 &compress_index); 927 optind--; 928 break; 929 } else if (((argc - optind) == 1) && 930 (*argv[optind] != '-')) { 931 algname = optarg; 932 filename = 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 filename = optarg; 942 } 943 944 check_file_validity(filename); 945 check_algorithm_validity(algname, &compress_index); 946 break; 947 case 'd': 948 deleteflag = 1; 949 950 minor = name_to_minor(optarg); 951 if (minor != 0) 952 devicename = optarg; 953 else 954 filename = optarg; 955 break; 956 case 'f': 957 force = B_TRUE; 958 break; 959 case 's': 960 segsize = convert_to_num(optarg); 961 962 if (segsize == 0 || segsize % DEV_BSIZE) 963 die(gettext("segment size %s is invalid " 964 "or not a multiple of minimum block " 965 "size %ld\n"), optarg, DEV_BSIZE); 966 967 filename = argv[optind]; 968 check_file_validity(filename); 969 optind++; 970 break; 971 case 'U': 972 uncompressflag = 1; 973 filename = optarg; 974 check_file_validity(filename); 975 break; 976 case '?': 977 default: 978 errflag = 1; 979 break; 980 } 981 } 982 if (errflag || 983 (addflag && deleteflag) || 984 ((compressflag || uncompressflag) && (addflag || deleteflag))) 985 usage(); 986 987 switch (argc - optind) { 988 case 0: /* no more args */ 989 break; 990 case 1: /* one arg without options means print the association */ 991 if (addflag || deleteflag) 992 usage(); 993 if (compressflag || uncompressflag) 994 usage(); 995 minor = name_to_minor(argv[optind]); 996 if (minor != 0) 997 devicename = argv[optind]; 998 else 999 filename = argv[optind]; 1000 break; 1001 default: 1002 usage(); 1003 break; 1004 } 1005 1006 if (filename && !valid_abspath(filename)) 1007 exit(E_ERROR); 1008 1009 /* 1010 * Here, we know the arguments are correct, the filename is an 1011 * absolute path, it exists and is a regular file. We don't yet 1012 * know that the device name is ok or not. 1013 */ 1014 /* 1015 * Now to the real work. 1016 */ 1017 openflag = O_EXCL; 1018 if (addflag || deleteflag || compressflag || uncompressflag) 1019 openflag |= O_RDWR; 1020 else 1021 openflag |= O_RDONLY; 1022 lfd = open(lofictl, openflag); 1023 if (lfd == -1) { 1024 if ((errno == EPERM) || (errno == EACCES)) { 1025 die("you do not have permission to perform " 1026 "that operation.\n"); 1027 } else { 1028 die("%s", lofictl); 1029 } 1030 /*NOTREACHED*/ 1031 } 1032 if (addflag) 1033 add_mapping(lfd, devicename, filename, NULL, 0); 1034 else if (compressflag) 1035 lofi_compress(&lfd, filename, compress_index, segsize); 1036 else if (uncompressflag) 1037 lofi_uncompress(lfd, filename); 1038 else if (deleteflag) 1039 delete_mapping(lfd, devicename, filename, force); 1040 else if (filename || devicename) 1041 print_one_mapping(lfd, devicename, filename); 1042 else 1043 print_mappings(lfd); 1044 1045 if (lfd != -1) 1046 (void) close(lfd); 1047 closelib(); 1048 return (E_SUCCESS); 1049 } 1050