Print this page
PSARC 2007/569 lofi(7D) compression support
6618343 lofi compression support

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