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