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 <errno.h>
  45 #include <stdlib.h>
  46 #include <unistd.h>
  47 #include <stropts.h>
  48 #include <libdevinfo.h>
  49 #include <libgen.h>
  50 #include <zlib.h>
  51 #include "utils.h"
  52 
  53 static const char USAGE[] =
  54         "Usage: %s -a file [ device ]\n"
  55         "       %s -d file | device \n"
  56         "       %s -C [algorithm] [-s segment_size] file \n"
  57         "       %s -U file \n"
  58         "       %s [ device | file ]\n";
  59 
  60 static const char *pname;
  61 static int      addflag = 0;
  62 static int      deleteflag = 0;
  63 static int      errflag = 0;
  64 static int      compressflag = 0;
  65 static int      uncompressflag = 0;
  66 
  67 static int gzip_compress(void *src, size_t srclen, void *dst,
  68         size_t *destlen, int level);
  69 
  70 lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = {
  71         {NULL,  gzip_compress,  6,      "gzip"}, /* default */
  72         {NULL,  gzip_compress,  6,      "gzip-6"},
  73         {NULL,  gzip_compress,  9,      "gzip-9"}
  74 };
  75 
  76 #define FORMAT                  "%-20s     %-30s        %s\n"
  77 #define 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 
  92 /*
  93  * Print the list of all the mappings. Including a header.
  94  */
  95 static void
  96 print_mappings(int fd)
  97 {
  98         struct lofi_ioctl li;
  99         int     minor;
 100         int     maxminor;
 101         char    path[MAXPATHLEN];
 102         char    type[MAXPATHLEN];
 103 
 104         li.li_minor = 0;
 105         if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) {
 106                 perror("ioctl");
 107                 exit(E_ERROR);
 108         }
 109 
 110         maxminor = li.li_minor;
 111 
 112         (void) printf(FORMAT, "Block Device", "File", "Options");
 113         for (minor = 1; minor <= maxminor; minor++) {
 114                 li.li_minor = minor;
 115                 if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) {
 116                         if (errno == ENXIO)
 117                                 continue;
 118                         perror("ioctl");
 119                         break;
 120                 }
 121                 (void) snprintf(path, sizeof (path), "/dev/%s/%d",
 122                     LOFI_BLOCK_NAME, minor);
 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);
 130         }
 131 }
 132 
 133 static void
 134 usage(void)
 135 {
 136         (void) fprintf(stderr, gettext(USAGE), pname, pname,
 137             pname, pname, pname);
 138         exit(E_USAGE);
 139 }
 140 
 141 /*
 142  * Translate a lofi device name to a minor number. We might be asked
 143  * to do this when there is no association (such as when the user specifies
 144  * a particular device), so we can only look at the string.
 145  */
 146 static int
 147 name_to_minor(const char *devicename)
 148 {
 149         int     minor;
 150 
 151         if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) {
 152                 return (minor);
 153         }
 154         if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) {
 155                 return (minor);
 156         }
 157         return (0);
 158 }
 159 
 160 /*
 161  * This might be the first time we've used this minor number. If so,
 162  * it might also be that the /dev links are in the process of being created
 163  * by devfsadmd (or that they'll be created "soon"). We cannot return
 164  * until they're there or the invoker of lofiadm might try to use them
 165  * and not find them. This can happen if a shell script is running on
 166  * an MP.
 167  */
 168 static int sleeptime = 2;       /* number of seconds to sleep between stat's */
 169 static int maxsleep = 120;      /* maximum number of seconds to sleep */
 170 
 171 static void
 172 wait_until_dev_complete(int minor)
 173 {
 174         struct stat64 buf;
 175         int     cursleep;
 176         char    blkpath[MAXPATHLEN];
 177         char    charpath[MAXPATHLEN];
 178         di_devlink_handle_t hdl;
 179 
 180 
 181         (void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d",
 182             LOFI_BLOCK_NAME, minor);
 183         (void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d",
 184             LOFI_CHAR_NAME, minor);
 185 
 186         /* Check if links already present */
 187         if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
 188                 return;
 189 
 190         /* First use di_devlink_init() */
 191         if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) {
 192                 (void) di_devlink_fini(&hdl);
 193                 goto out;
 194         }
 195 
 196         /*
 197          * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will
 198          * only fail if the caller is non-root. In that case, wait for
 199          * link creation via sysevents.
 200          */
 201         cursleep = 0;
 202         while (cursleep < maxsleep) {
 203                 if ((stat64(blkpath, &buf) == -1) ||
 204                     (stat64(charpath, &buf) == -1)) {
 205                         (void) sleep(sleeptime);
 206                         cursleep += sleeptime;
 207                         continue;
 208                 }
 209                 return;
 210         }
 211 
 212         /* one last try */
 213 
 214 out:
 215         if (stat64(blkpath, &buf) == -1) {
 216                 die(gettext("%s was not created"), blkpath);
 217         }
 218         if (stat64(charpath, &buf) == -1) {
 219                 die(gettext("%s was not created"), charpath);
 220         }
 221 }
 222 
 223 /*
 224  * Add a device association. If devicename is NULL, let the driver
 225  * pick a device.
 226  */
 227 static void
 228 add_mapping(int lfd, const char *devicename, const char *filename,
 229     int *minor_created, int suppress)
 230 {
 231         struct lofi_ioctl li;
 232         int     minor;
 233 
 234         if (devicename == NULL) {
 235                 /* pick one */
 236                 li.li_minor = 0;
 237                 (void) strlcpy(li.li_filename, filename,
 238                     sizeof (li.li_filename));
 239                 minor = ioctl(lfd, LOFI_MAP_FILE, &li);
 240                 if (minor == -1) {
 241                         die(gettext("could not map file %s"), filename);
 242                 }
 243                 wait_until_dev_complete(minor);
 244                 /* print one picked */
 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                 }
 252                 return;
 253         }
 254         /* use device we were given */
 255         minor = name_to_minor(devicename);
 256         if (minor == 0) {
 257                 die(gettext("malformed device name %s\n"), devicename);
 258         }
 259         (void) strcpy(li.li_filename, filename);
 260         li.li_minor = minor;
 261         if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) {
 262                 die(gettext("could not map file %s to %s"), filename,
 263                     devicename);
 264         }
 265         wait_until_dev_complete(minor);
 266 }
 267 
 268 /*
 269  * Remove an association. Delete by device name if non-NULL, or by
 270  * filename otherwise.
 271  */
 272 static void
 273 delete_mapping(int lfd, const char *devicename, const char *filename,
 274     boolean_t force)
 275 {
 276         struct lofi_ioctl li;
 277 
 278         li.li_force = force;
 279         if (devicename == NULL) {
 280                 /* delete by filename */
 281                 (void) strcpy(li.li_filename, filename);
 282                 li.li_minor = 0;
 283                 if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) {
 284                         die(gettext("could not unmap file %s"), filename);
 285                 }
 286                 return;
 287         }
 288         /* delete by device */
 289 
 290         li.li_minor = name_to_minor(devicename);
 291         if (li.li_minor == 0) {
 292                 die(gettext("malformed device name %s\n"), devicename);
 293         }
 294         if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) {
 295                 die(gettext("could not unmap device %s"), devicename);
 296         }
 297 }
 298 
 299 static void
 300 print_one_mapping(int lfd, const char *devicename, const char *filename)
 301 {
 302         struct lofi_ioctl li;
 303 
 304         if (devicename == NULL) {
 305                 /* given filename, print devicename */
 306                 li.li_minor = 0;
 307                 (void) strcpy(li.li_filename, filename);
 308                 if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) {
 309                         die(gettext("could not find device for %s"), filename);
 310                 }
 311                 (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor);
 312                 return;
 313         }
 314 
 315         /* given devicename, print filename */
 316         li.li_minor = name_to_minor(devicename);
 317         if (li.li_minor == 0) {
 318                 die(gettext("malformed device name %s\n"), devicename);
 319         }
 320         if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) {
 321                 die(gettext("could not find filename for %s"), devicename);
 322         }
 323         (void) printf("%s\n", li.li_filename);
 324 }
 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 
 754 int
 755 main(int argc, char *argv[])
 756 {
 757         int     lfd;
 758         int     c;
 759         int     error;
 760         struct stat64 buf;
 761         const char *devicename = NULL;
 762         const char *filename = NULL;
 763         const char *algname = COMPRESS_ALGORITHM;
 764         int     openflag;
 765         int     minor;
 766         int     fd = -1;
 767         int     compress_index;
 768         uint32_t segsize = SEGSIZE;
 769         static char *lofictl = "/dev/" LOFI_CTL_NAME;
 770         boolean_t force = B_FALSE;
 771 
 772         pname = getpname(argv[0]);
 773 
 774         (void) setlocale(LC_ALL, "");
 775         (void) textdomain(TEXT_DOMAIN);
 776 
 777         while ((c = getopt(argc, argv, "a:C:d:s:U:f")) != EOF) {
 778                 switch (c) {
 779                 case 'a':
 780                         addflag = 1;
 781                         filename = optarg;
 782                         fd = open64(filename, O_RDONLY);
 783                         if (fd == -1) {
 784                                 die(gettext("open: %s"), filename);
 785                         }
 786                         error = fstat64(fd, &buf);
 787                         if (error == -1) {
 788                                 die(gettext("fstat: %s"), filename);
 789                         } else if (!S_ISLOFIABLE(buf.st_mode)) {
 790                                 die(gettext("%s is not a regular file, "
 791                                     "block, or character device\n"),
 792                                     filename);
 793                         } else if ((buf.st_size % DEV_BSIZE) != 0) {
 794                                 die(gettext("size of %s is not a multiple "
 795                                     "of %d\n"),
 796                                     filename, DEV_BSIZE);
 797                         }
 798                         (void) close(fd);
 799                         minor = name_to_minor(filename);
 800                         if (minor != 0) {
 801                                 die(gettext("cannot use " LOFI_DRIVER_NAME
 802                                     " on itself\n"), devicename);
 803                         }
 804                         if (((argc - optind) > 0) && (*argv[optind] != '-')) {
 805                                 /* optional device */
 806                                 devicename = argv[optind];
 807                                 optind++;
 808                         }
 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;
 847                 case 'd':
 848                         deleteflag = 1;
 849 
 850                         minor = name_to_minor(optarg);
 851                         if (minor != 0)
 852                                 devicename = optarg;
 853                         else
 854                                 filename = optarg;
 855                         break;
 856                 case 'f':
 857                         force = B_TRUE;
 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;
 897                 case '?':
 898                 default:
 899                         errflag = 1;
 900                         break;
 901                 }
 902         }
 903         if (errflag ||
 904             (addflag && deleteflag) ||
 905             ((compressflag || uncompressflag) && (addflag || deleteflag)))
 906                 usage();
 907 
 908         switch (argc - optind) {
 909         case 0: /* no more args */
 910                 break;
 911         case 1: /* one arg without options means print the association */
 912                 if (addflag || deleteflag)
 913                         usage();
 914                 if (compressflag || uncompressflag)
 915                         usage();
 916                 minor = name_to_minor(argv[optind]);
 917                 if (minor != 0)
 918                         devicename = argv[optind];
 919                 else
 920                         filename = argv[optind];
 921                 break;
 922         default:
 923                 usage();
 924                 break;
 925         }
 926 
 927         if (filename && !valid_abspath(filename))
 928                 exit(E_ERROR);
 929 
 930         /*
 931          * Here, we know the arguments are correct, the filename is an
 932          * absolute path, it exists and is a regular file. We don't yet
 933          * know that the device name is ok or not.
 934          */
 935         /*
 936          * Now to the real work.
 937          */
 938         openflag = O_EXCL;
 939         if (addflag || deleteflag || compressflag || uncompressflag)
 940                 openflag |= O_RDWR;
 941         else
 942                 openflag |= O_RDONLY;
 943         lfd = open(lofictl, openflag);
 944         if (lfd == -1) {
 945                 if ((errno == EPERM) || (errno == EACCES)) {
 946                         die("you do not have permission to perform "
 947                             "that operation.\n");
 948                 } else {
 949                         die("%s", lofictl);
 950                 }
 951                 /*NOTREACHED*/
 952         }
 953         if (addflag)
 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);
 959         else if (deleteflag)
 960                 delete_mapping(lfd, devicename, filename, force);
 961         else if (filename || devicename)
 962                 print_one_mapping(lfd, devicename, filename);
 963         else
 964                 print_mappings(lfd);
 965         (void) close(lfd);
 966         return (E_SUCCESS);
 967 }