17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51d8ed0f0Svikram * Common Development and Distribution License (the "License"). 61d8ed0f0Svikram * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 2293239addSjohnlev * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 231d8ed0f0Svikram * Use is subject to license terms. 243d7072f8Seschrock */ 253d7072f8Seschrock 263d7072f8Seschrock /* 277c478bd9Sstevel@tonic-gate * lofiadm - administer lofi(7d). Very simple, add and remove file<->device 287c478bd9Sstevel@tonic-gate * associations, and display status. All the ioctls are private between 297c478bd9Sstevel@tonic-gate * lofi and lofiadm, and so are very simple - device information is 307c478bd9Sstevel@tonic-gate * communicated via a minor number. 317c478bd9Sstevel@tonic-gate */ 327c478bd9Sstevel@tonic-gate 337c478bd9Sstevel@tonic-gate #include <sys/types.h> 347c478bd9Sstevel@tonic-gate #include <sys/param.h> 357c478bd9Sstevel@tonic-gate #include <sys/lofi.h> 367c478bd9Sstevel@tonic-gate #include <sys/stat.h> 3787117650Saalok #include <netinet/in.h> 387c478bd9Sstevel@tonic-gate #include <stdio.h> 397c478bd9Sstevel@tonic-gate #include <fcntl.h> 407c478bd9Sstevel@tonic-gate #include <locale.h> 417c478bd9Sstevel@tonic-gate #include <string.h> 4287117650Saalok #include <strings.h> 437c478bd9Sstevel@tonic-gate #include <errno.h> 447c478bd9Sstevel@tonic-gate #include <stdlib.h> 457c478bd9Sstevel@tonic-gate #include <unistd.h> 467c478bd9Sstevel@tonic-gate #include <stropts.h> 471d8ed0f0Svikram #include <libdevinfo.h> 4887117650Saalok #include <libgen.h> 4987117650Saalok #include <ctype.h> 50579df0adSaalok #include <dlfcn.h> 517c478bd9Sstevel@tonic-gate #include "utils.h" 527c478bd9Sstevel@tonic-gate 537c478bd9Sstevel@tonic-gate static const char USAGE[] = 547c478bd9Sstevel@tonic-gate "Usage: %s -a file [ device ]\n" 557c478bd9Sstevel@tonic-gate " %s -d file | device\n" 5687117650Saalok " %s -C [algorithm] [-s segment_size] file\n" 5787117650Saalok " %s -U file\n" 58*f9153c6bSDina K Nimeh " %s [ file | device ]\n"; 597c478bd9Sstevel@tonic-gate 607c478bd9Sstevel@tonic-gate static const char *pname; 617c478bd9Sstevel@tonic-gate static int addflag = 0; 627c478bd9Sstevel@tonic-gate static int deleteflag = 0; 637c478bd9Sstevel@tonic-gate static int errflag = 0; 6487117650Saalok static int compressflag = 0; 6587117650Saalok static int uncompressflag = 0; 667c478bd9Sstevel@tonic-gate 6787117650Saalok static int gzip_compress(void *src, size_t srclen, void *dst, 6887117650Saalok size_t *destlen, int level); 6987117650Saalok 7087117650Saalok lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = { 7187117650Saalok {NULL, gzip_compress, 6, "gzip"}, /* default */ 7287117650Saalok {NULL, gzip_compress, 6, "gzip-6"}, 7387117650Saalok {NULL, gzip_compress, 9, "gzip-9"} 7487117650Saalok }; 7587117650Saalok 7687117650Saalok #define FORMAT "%-20s %-30s %s\n" 7787117650Saalok #define NONE "-" 7887117650Saalok #define COMPRESS "Compressed" 7987117650Saalok #define COMPRESS_ALGORITHM "gzip" 8087117650Saalok #define COMPRESS_THRESHOLD 2048 8187117650Saalok #define SEGSIZE 131072 8287117650Saalok #define BLOCK_SIZE 512 8387117650Saalok #define KILOBYTE 1024 8487117650Saalok #define MEGABYTE (KILOBYTE * KILOBYTE) 8587117650Saalok #define GIGABYTE (KILOBYTE * MEGABYTE) 86579df0adSaalok #define LIBZ "libz.so" 87579df0adSaalok 88579df0adSaalok static int (*compress2p)(void *, ulong_t *, void *, size_t, int) = NULL; 8987117650Saalok 9087117650Saalok static int gzip_compress(void *src, size_t srclen, void *dst, 9187117650Saalok size_t *dstlen, int level) 9287117650Saalok { 93579df0adSaalok void *libz_hdl = NULL; 9487117650Saalok 95579df0adSaalok /* 96579df0adSaalok * The first time we are called, attempt to dlopen() 97579df0adSaalok * libz.so and get a pointer to the compress2() function 98579df0adSaalok */ 99579df0adSaalok if (compress2p == NULL) { 100579df0adSaalok if ((libz_hdl = openlib(LIBZ)) == NULL) 101579df0adSaalok die(gettext("could not find %s. " 102579df0adSaalok "gzip compression unavailable\n"), LIBZ); 103579df0adSaalok 104579df0adSaalok if ((compress2p = 105579df0adSaalok (int (*)(void *, ulong_t *, void *, size_t, int)) 106579df0adSaalok dlsym(libz_hdl, "compress2")) == NULL) { 107579df0adSaalok closelib(); 108579df0adSaalok die(gettext("could not find the correct %s. " 109579df0adSaalok "gzip compression unavailable\n"), LIBZ); 110579df0adSaalok } 111579df0adSaalok } 112579df0adSaalok 113579df0adSaalok if ((*compress2p)(dst, (ulong_t *)dstlen, src, srclen, level) != 0) 114579df0adSaalok return (-1); 11587117650Saalok return (0); 11687117650Saalok } 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate /* 1197c478bd9Sstevel@tonic-gate * Print the list of all the mappings. Including a header. 1207c478bd9Sstevel@tonic-gate */ 1217c478bd9Sstevel@tonic-gate static void 1227c478bd9Sstevel@tonic-gate print_mappings(int fd) 1237c478bd9Sstevel@tonic-gate { 1247c478bd9Sstevel@tonic-gate struct lofi_ioctl li; 1257c478bd9Sstevel@tonic-gate int minor; 1267c478bd9Sstevel@tonic-gate int maxminor; 12787117650Saalok char path[MAXPATHLEN]; 12887117650Saalok char options[MAXPATHLEN]; 1297c478bd9Sstevel@tonic-gate 1307c478bd9Sstevel@tonic-gate li.li_minor = 0; 1317c478bd9Sstevel@tonic-gate if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) { 1327c478bd9Sstevel@tonic-gate perror("ioctl"); 1337c478bd9Sstevel@tonic-gate exit(E_ERROR); 1347c478bd9Sstevel@tonic-gate } 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate maxminor = li.li_minor; 1377c478bd9Sstevel@tonic-gate 13887117650Saalok (void) printf(FORMAT, "Block Device", "File", "Options"); 1397c478bd9Sstevel@tonic-gate for (minor = 1; minor <= maxminor; minor++) { 1407c478bd9Sstevel@tonic-gate li.li_minor = minor; 1417c478bd9Sstevel@tonic-gate if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) { 1427c478bd9Sstevel@tonic-gate if (errno == ENXIO) 1437c478bd9Sstevel@tonic-gate continue; 1447c478bd9Sstevel@tonic-gate perror("ioctl"); 1457c478bd9Sstevel@tonic-gate break; 1467c478bd9Sstevel@tonic-gate } 1477c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "/dev/%s/%d", 1487c478bd9Sstevel@tonic-gate LOFI_BLOCK_NAME, minor); 14987117650Saalok if (li.li_algorithm[0] == '\0') 15087117650Saalok (void) snprintf(options, sizeof (options), "%s", NONE); 15187117650Saalok else 15287117650Saalok (void) snprintf(options, sizeof (options), 15387117650Saalok COMPRESS "(%s)", li.li_algorithm); 15487117650Saalok 15587117650Saalok (void) printf(FORMAT, path, li.li_filename, options); 1567c478bd9Sstevel@tonic-gate } 1577c478bd9Sstevel@tonic-gate } 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate static void 1607c478bd9Sstevel@tonic-gate usage(void) 1617c478bd9Sstevel@tonic-gate { 16287117650Saalok (void) fprintf(stderr, gettext(USAGE), pname, pname, 16387117650Saalok pname, pname, pname); 1647c478bd9Sstevel@tonic-gate exit(E_USAGE); 1657c478bd9Sstevel@tonic-gate } 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate /* 1687c478bd9Sstevel@tonic-gate * Translate a lofi device name to a minor number. We might be asked 1697c478bd9Sstevel@tonic-gate * to do this when there is no association (such as when the user specifies 1707c478bd9Sstevel@tonic-gate * a particular device), so we can only look at the string. 1717c478bd9Sstevel@tonic-gate */ 1727c478bd9Sstevel@tonic-gate static int 1737c478bd9Sstevel@tonic-gate name_to_minor(const char *devicename) 1747c478bd9Sstevel@tonic-gate { 1757c478bd9Sstevel@tonic-gate int minor; 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) { 1787c478bd9Sstevel@tonic-gate return (minor); 1797c478bd9Sstevel@tonic-gate } 1807c478bd9Sstevel@tonic-gate if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) { 1817c478bd9Sstevel@tonic-gate return (minor); 1827c478bd9Sstevel@tonic-gate } 1837c478bd9Sstevel@tonic-gate return (0); 1847c478bd9Sstevel@tonic-gate } 1857c478bd9Sstevel@tonic-gate 1867c478bd9Sstevel@tonic-gate /* 1877c478bd9Sstevel@tonic-gate * This might be the first time we've used this minor number. If so, 1887c478bd9Sstevel@tonic-gate * it might also be that the /dev links are in the process of being created 1897c478bd9Sstevel@tonic-gate * by devfsadmd (or that they'll be created "soon"). We cannot return 1907c478bd9Sstevel@tonic-gate * until they're there or the invoker of lofiadm might try to use them 1917c478bd9Sstevel@tonic-gate * and not find them. This can happen if a shell script is running on 1927c478bd9Sstevel@tonic-gate * an MP. 1937c478bd9Sstevel@tonic-gate */ 1947c478bd9Sstevel@tonic-gate static int sleeptime = 2; /* number of seconds to sleep between stat's */ 1957c478bd9Sstevel@tonic-gate static int maxsleep = 120; /* maximum number of seconds to sleep */ 1967c478bd9Sstevel@tonic-gate 1977c478bd9Sstevel@tonic-gate static void 1987c478bd9Sstevel@tonic-gate wait_until_dev_complete(int minor) 1997c478bd9Sstevel@tonic-gate { 2007c478bd9Sstevel@tonic-gate struct stat64 buf; 2017c478bd9Sstevel@tonic-gate int cursleep; 20287117650Saalok char blkpath[MAXPATHLEN]; 20387117650Saalok char charpath[MAXPATHLEN]; 2041d8ed0f0Svikram di_devlink_handle_t hdl; 2051d8ed0f0Svikram 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate (void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d", 2087c478bd9Sstevel@tonic-gate LOFI_BLOCK_NAME, minor); 2097c478bd9Sstevel@tonic-gate (void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d", 2107c478bd9Sstevel@tonic-gate LOFI_CHAR_NAME, minor); 2111d8ed0f0Svikram 2121d8ed0f0Svikram /* Check if links already present */ 2131d8ed0f0Svikram if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0) 2141d8ed0f0Svikram return; 2151d8ed0f0Svikram 2161d8ed0f0Svikram /* First use di_devlink_init() */ 2171d8ed0f0Svikram if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) { 2181d8ed0f0Svikram (void) di_devlink_fini(&hdl); 2191d8ed0f0Svikram goto out; 2201d8ed0f0Svikram } 2211d8ed0f0Svikram 2221d8ed0f0Svikram /* 2231d8ed0f0Svikram * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will 2241d8ed0f0Svikram * only fail if the caller is non-root. In that case, wait for 2251d8ed0f0Svikram * link creation via sysevents. 2261d8ed0f0Svikram */ 2277c478bd9Sstevel@tonic-gate cursleep = 0; 2287c478bd9Sstevel@tonic-gate while (cursleep < maxsleep) { 2297c478bd9Sstevel@tonic-gate if ((stat64(blkpath, &buf) == -1) || 2307c478bd9Sstevel@tonic-gate (stat64(charpath, &buf) == -1)) { 2317c478bd9Sstevel@tonic-gate (void) sleep(sleeptime); 2327c478bd9Sstevel@tonic-gate cursleep += sleeptime; 2337c478bd9Sstevel@tonic-gate continue; 2347c478bd9Sstevel@tonic-gate } 2357c478bd9Sstevel@tonic-gate return; 2367c478bd9Sstevel@tonic-gate } 2371d8ed0f0Svikram 2387c478bd9Sstevel@tonic-gate /* one last try */ 2391d8ed0f0Svikram 2401d8ed0f0Svikram out: 2417c478bd9Sstevel@tonic-gate if (stat64(blkpath, &buf) == -1) { 2427c478bd9Sstevel@tonic-gate die(gettext("%s was not created"), blkpath); 2437c478bd9Sstevel@tonic-gate } 2447c478bd9Sstevel@tonic-gate if (stat64(charpath, &buf) == -1) { 2457c478bd9Sstevel@tonic-gate die(gettext("%s was not created"), charpath); 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate /* 2507c478bd9Sstevel@tonic-gate * Add a device association. If devicename is NULL, let the driver 2517c478bd9Sstevel@tonic-gate * pick a device. 2527c478bd9Sstevel@tonic-gate */ 2537c478bd9Sstevel@tonic-gate static void 25487117650Saalok add_mapping(int lfd, const char *devicename, const char *filename, 25587117650Saalok int *minor_created, int suppress) 2567c478bd9Sstevel@tonic-gate { 2577c478bd9Sstevel@tonic-gate struct lofi_ioctl li; 2587c478bd9Sstevel@tonic-gate int minor; 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate if (devicename == NULL) { 2617c478bd9Sstevel@tonic-gate /* pick one */ 2627c478bd9Sstevel@tonic-gate li.li_minor = 0; 26387117650Saalok (void) strlcpy(li.li_filename, filename, 26487117650Saalok sizeof (li.li_filename)); 2657c478bd9Sstevel@tonic-gate minor = ioctl(lfd, LOFI_MAP_FILE, &li); 2667c478bd9Sstevel@tonic-gate if (minor == -1) { 2677c478bd9Sstevel@tonic-gate die(gettext("could not map file %s"), filename); 2687c478bd9Sstevel@tonic-gate } 2697c478bd9Sstevel@tonic-gate wait_until_dev_complete(minor); 2707c478bd9Sstevel@tonic-gate /* print one picked */ 27187117650Saalok if (!suppress) 2727c478bd9Sstevel@tonic-gate (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor); 27387117650Saalok 27487117650Saalok /* fill in the minor if needed */ 27587117650Saalok if (minor_created != NULL) { 27687117650Saalok *minor_created = minor; 27787117650Saalok } 2787c478bd9Sstevel@tonic-gate return; 2797c478bd9Sstevel@tonic-gate } 2807c478bd9Sstevel@tonic-gate /* use device we were given */ 2817c478bd9Sstevel@tonic-gate minor = name_to_minor(devicename); 2827c478bd9Sstevel@tonic-gate if (minor == 0) { 2837c478bd9Sstevel@tonic-gate die(gettext("malformed device name %s\n"), devicename); 2847c478bd9Sstevel@tonic-gate } 28587117650Saalok (void) strlcpy(li.li_filename, filename, sizeof (li.li_filename)); 2867c478bd9Sstevel@tonic-gate li.li_minor = minor; 2877c478bd9Sstevel@tonic-gate if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) { 2887c478bd9Sstevel@tonic-gate die(gettext("could not map file %s to %s"), filename, 2897c478bd9Sstevel@tonic-gate devicename); 2907c478bd9Sstevel@tonic-gate } 2917c478bd9Sstevel@tonic-gate wait_until_dev_complete(minor); 2927c478bd9Sstevel@tonic-gate } 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate /* 2957c478bd9Sstevel@tonic-gate * Remove an association. Delete by device name if non-NULL, or by 2967c478bd9Sstevel@tonic-gate * filename otherwise. 2977c478bd9Sstevel@tonic-gate */ 2987c478bd9Sstevel@tonic-gate static void 2993d7072f8Seschrock delete_mapping(int lfd, const char *devicename, const char *filename, 3003d7072f8Seschrock boolean_t force) 3017c478bd9Sstevel@tonic-gate { 3027c478bd9Sstevel@tonic-gate struct lofi_ioctl li; 3037c478bd9Sstevel@tonic-gate 3043d7072f8Seschrock li.li_force = force; 30593239addSjohnlev li.li_cleanup = B_FALSE; 30693239addSjohnlev 3077c478bd9Sstevel@tonic-gate if (devicename == NULL) { 3087c478bd9Sstevel@tonic-gate /* delete by filename */ 30987117650Saalok (void) strlcpy(li.li_filename, filename, 31087117650Saalok sizeof (li.li_filename)); 3117c478bd9Sstevel@tonic-gate li.li_minor = 0; 3127c478bd9Sstevel@tonic-gate if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) { 3137c478bd9Sstevel@tonic-gate die(gettext("could not unmap file %s"), filename); 3147c478bd9Sstevel@tonic-gate } 3157c478bd9Sstevel@tonic-gate return; 3167c478bd9Sstevel@tonic-gate } 3177c478bd9Sstevel@tonic-gate 318*f9153c6bSDina K Nimeh /* delete by device */ 3197c478bd9Sstevel@tonic-gate li.li_minor = name_to_minor(devicename); 3207c478bd9Sstevel@tonic-gate if (li.li_minor == 0) { 3217c478bd9Sstevel@tonic-gate die(gettext("malformed device name %s\n"), devicename); 3227c478bd9Sstevel@tonic-gate } 3237c478bd9Sstevel@tonic-gate if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) { 3247c478bd9Sstevel@tonic-gate die(gettext("could not unmap device %s"), devicename); 3257c478bd9Sstevel@tonic-gate } 3267c478bd9Sstevel@tonic-gate } 3277c478bd9Sstevel@tonic-gate 3287c478bd9Sstevel@tonic-gate static void 3297c478bd9Sstevel@tonic-gate print_one_mapping(int lfd, const char *devicename, const char *filename) 3307c478bd9Sstevel@tonic-gate { 3317c478bd9Sstevel@tonic-gate struct lofi_ioctl li; 3327c478bd9Sstevel@tonic-gate 3337c478bd9Sstevel@tonic-gate if (devicename == NULL) { 3347c478bd9Sstevel@tonic-gate /* given filename, print devicename */ 3357c478bd9Sstevel@tonic-gate li.li_minor = 0; 33687117650Saalok (void) strlcpy(li.li_filename, filename, 33787117650Saalok sizeof (li.li_filename)); 3387c478bd9Sstevel@tonic-gate if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) { 3397c478bd9Sstevel@tonic-gate die(gettext("could not find device for %s"), filename); 3407c478bd9Sstevel@tonic-gate } 3417c478bd9Sstevel@tonic-gate (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor); 3427c478bd9Sstevel@tonic-gate return; 3437c478bd9Sstevel@tonic-gate } 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate /* given devicename, print filename */ 3467c478bd9Sstevel@tonic-gate li.li_minor = name_to_minor(devicename); 3477c478bd9Sstevel@tonic-gate if (li.li_minor == 0) { 3487c478bd9Sstevel@tonic-gate die(gettext("malformed device name %s\n"), devicename); 3497c478bd9Sstevel@tonic-gate } 3507c478bd9Sstevel@tonic-gate if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) { 3517c478bd9Sstevel@tonic-gate die(gettext("could not find filename for %s"), devicename); 3527c478bd9Sstevel@tonic-gate } 3537c478bd9Sstevel@tonic-gate (void) printf("%s\n", li.li_filename); 3547c478bd9Sstevel@tonic-gate } 3557c478bd9Sstevel@tonic-gate 35687117650Saalok /* 35787117650Saalok * Uncompress a file. 35887117650Saalok * 35987117650Saalok * First map the file in to establish a device 36087117650Saalok * association, then read from it. On-the-fly 36187117650Saalok * decompression will automatically uncompress 36287117650Saalok * the file if it's compressed 36387117650Saalok * 36487117650Saalok * If the file is mapped and a device association 36587117650Saalok * has been established, disallow uncompressing 36687117650Saalok * the file until it is unmapped. 36787117650Saalok */ 36887117650Saalok static void 36987117650Saalok lofi_uncompress(int lfd, const char *filename) 3707c478bd9Sstevel@tonic-gate { 37187117650Saalok struct lofi_ioctl li; 37287117650Saalok char buf[MAXBSIZE]; 37387117650Saalok char devicename[32]; 37487117650Saalok char tmpfilename[MAXPATHLEN]; 37587117650Saalok char *dir = NULL; 37687117650Saalok char *file = NULL; 37787117650Saalok int minor = 0; 37887117650Saalok struct stat64 statbuf; 37987117650Saalok int compfd = -1; 38087117650Saalok int uncompfd = -1; 38187117650Saalok ssize_t rbytes; 38287117650Saalok 38387117650Saalok /* 38487117650Saalok * Disallow uncompressing the file if it is 38587117650Saalok * already mapped. 38687117650Saalok */ 38787117650Saalok li.li_minor = 0; 38887117650Saalok (void) strlcpy(li.li_filename, filename, sizeof (li.li_filename)); 38987117650Saalok if (ioctl(lfd, LOFI_GET_MINOR, &li) != -1) 39087117650Saalok die(gettext("%s must be unmapped before uncompressing"), 39187117650Saalok filename); 39287117650Saalok 39387117650Saalok /* Zero length files don't need to be uncompressed */ 39487117650Saalok if (stat64(filename, &statbuf) == -1) 39587117650Saalok die(gettext("stat: %s"), filename); 39687117650Saalok if (statbuf.st_size == 0) 39787117650Saalok return; 39887117650Saalok 39987117650Saalok add_mapping(lfd, NULL, filename, &minor, 1); 40087117650Saalok (void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d", 40187117650Saalok LOFI_BLOCK_NAME, minor); 40287117650Saalok 40387117650Saalok /* If the file isn't compressed, we just return */ 40487117650Saalok if ((ioctl(lfd, LOFI_CHECK_COMPRESSED, &li) == -1) || 405a423e759Saalok (li.li_algorithm[0] == '\0')) { 40687117650Saalok delete_mapping(lfd, devicename, filename, B_TRUE); 407a423e759Saalok die("%s is not compressed\n", filename); 40887117650Saalok } 40987117650Saalok 41087117650Saalok if ((compfd = open64(devicename, O_RDONLY | O_NONBLOCK)) == -1) { 41187117650Saalok delete_mapping(lfd, devicename, filename, B_TRUE); 41287117650Saalok die(gettext("open: %s"), filename); 41387117650Saalok } 41487117650Saalok /* Create a temp file in the same directory */ 41587117650Saalok dir = strdup(filename); 41687117650Saalok dir = dirname(dir); 41787117650Saalok file = strdup(filename); 41887117650Saalok file = basename(file); 41987117650Saalok (void) snprintf(tmpfilename, sizeof (tmpfilename), 42087117650Saalok "%s/.%sXXXXXX", dir, file); 42187117650Saalok 42287117650Saalok if ((uncompfd = mkstemp64(tmpfilename)) == -1) { 42387117650Saalok (void) close(compfd); 42487117650Saalok delete_mapping(lfd, devicename, filename, B_TRUE); 42587117650Saalok free(dir); 42687117650Saalok free(file); 427a423e759Saalok die("%s could not be uncompressed\n", filename); 42887117650Saalok } 42987117650Saalok 43087117650Saalok /* 43187117650Saalok * Set the mode bits and the owner of this temporary 43287117650Saalok * file to be that of the original uncompressed file 43387117650Saalok */ 43487117650Saalok (void) fchmod(uncompfd, statbuf.st_mode); 43587117650Saalok 43687117650Saalok if (fchown(uncompfd, statbuf.st_uid, statbuf.st_gid) == -1) { 43787117650Saalok (void) close(compfd); 43887117650Saalok (void) close(uncompfd); 43987117650Saalok delete_mapping(lfd, devicename, filename, B_TRUE); 44087117650Saalok free(dir); 44187117650Saalok free(file); 442a423e759Saalok die("%s could not be uncompressed\n", filename); 44387117650Saalok } 44487117650Saalok 44587117650Saalok /* Now read from the device in MAXBSIZE-sized chunks */ 44687117650Saalok for (;;) { 44787117650Saalok rbytes = read(compfd, buf, sizeof (buf)); 44887117650Saalok 44987117650Saalok if (rbytes <= 0) 45087117650Saalok break; 45187117650Saalok 45287117650Saalok if (write(uncompfd, buf, rbytes) != rbytes) { 45387117650Saalok rbytes = -1; 45487117650Saalok break; 45587117650Saalok } 45687117650Saalok } 45787117650Saalok 45887117650Saalok (void) close(compfd); 45987117650Saalok (void) close(uncompfd); 46087117650Saalok free(dir); 46187117650Saalok free(file); 46287117650Saalok 46387117650Saalok /* Delete the mapping */ 46487117650Saalok delete_mapping(lfd, devicename, filename, B_TRUE); 46587117650Saalok 46687117650Saalok /* 46787117650Saalok * If an error occured while reading or writing, rbytes will 46887117650Saalok * be negative 46987117650Saalok */ 47087117650Saalok if (rbytes < 0) { 47187117650Saalok (void) unlink(tmpfilename); 47287117650Saalok die(gettext("could not read from %s"), filename); 47387117650Saalok } 47487117650Saalok 47587117650Saalok /* Rename the temp file to the actual file */ 47687117650Saalok if (rename(tmpfilename, filename) == -1) 47787117650Saalok (void) unlink(tmpfilename); 47887117650Saalok } 47987117650Saalok 48087117650Saalok /* 48187117650Saalok * Compress a file 48287117650Saalok */ 48387117650Saalok static void 4849525893dSaalok lofi_compress(int *lfd, const char *filename, int compress_index, 48587117650Saalok uint32_t segsize) 48687117650Saalok { 48787117650Saalok struct lofi_ioctl lic; 48887117650Saalok lofi_compress_info_t *li; 4899525893dSaalok struct flock lock; 49087117650Saalok char tmpfilename[MAXPATHLEN]; 49187117650Saalok char comp_filename[MAXPATHLEN]; 49287117650Saalok char algorithm[MAXALGLEN]; 49387117650Saalok char *dir = NULL, *file = NULL; 49487117650Saalok uchar_t *uncompressed_seg = NULL; 49587117650Saalok uchar_t *compressed_seg = NULL; 49687117650Saalok uint32_t compressed_segsize; 49787117650Saalok uint32_t len_compressed, count; 49887117650Saalok uint32_t index_entries, index_sz; 49987117650Saalok uint64_t *index = NULL; 50087117650Saalok uint64_t offset; 50187117650Saalok size_t real_segsize; 50287117650Saalok struct stat64 statbuf; 50387117650Saalok int compfd = -1, uncompfd = -1; 50487117650Saalok int tfd = -1; 50587117650Saalok ssize_t rbytes, wbytes, lastread; 50687117650Saalok int i, type; 50787117650Saalok 50887117650Saalok /* 50987117650Saalok * Disallow compressing the file if it is 51087117650Saalok * already mapped 51187117650Saalok */ 51287117650Saalok lic.li_minor = 0; 51387117650Saalok (void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename)); 5149525893dSaalok if (ioctl(*lfd, LOFI_GET_MINOR, &lic) != -1) 51587117650Saalok die(gettext("%s must be unmapped before compressing"), 51687117650Saalok filename); 51787117650Saalok 5189525893dSaalok /* 5199525893dSaalok * Close the control device so other operations 5209525893dSaalok * can use it 5219525893dSaalok */ 5229525893dSaalok (void) close(*lfd); 5239525893dSaalok *lfd = -1; 5249525893dSaalok 52587117650Saalok li = &lofi_compress_table[compress_index]; 52687117650Saalok 52787117650Saalok /* 52887117650Saalok * The size of the buffer to hold compressed data must 52987117650Saalok * be slightly larger than the compressed segment size. 53087117650Saalok * 53187117650Saalok * The compress functions use part of the buffer as 53287117650Saalok * scratch space to do calculations. 53387117650Saalok * Ref: http://www.zlib.net/manual.html#compress2 53487117650Saalok */ 53587117650Saalok compressed_segsize = segsize + (segsize >> 6); 53687117650Saalok compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR); 53787117650Saalok uncompressed_seg = (uchar_t *)malloc(segsize); 53887117650Saalok 53987117650Saalok if (compressed_seg == NULL || uncompressed_seg == NULL) 54087117650Saalok die(gettext("No memory")); 54187117650Saalok 5429525893dSaalok if ((uncompfd = open64(filename, O_RDWR|O_LARGEFILE, 0)) == -1) 54387117650Saalok die(gettext("open: %s"), filename); 54487117650Saalok 5459525893dSaalok lock.l_type = F_WRLCK; 5469525893dSaalok lock.l_whence = SEEK_SET; 5479525893dSaalok lock.l_start = 0; 5489525893dSaalok lock.l_len = 0; 5499525893dSaalok 5509525893dSaalok /* 5519525893dSaalok * Use an advisory lock to ensure that only a 5529525893dSaalok * single lofiadm process compresses a given 5539525893dSaalok * file at any given time 5549525893dSaalok * 5559525893dSaalok * A close on the file descriptor automatically 5569525893dSaalok * closes all lock state on the file 5579525893dSaalok */ 5589525893dSaalok if (fcntl(uncompfd, F_SETLKW, &lock) == -1) 5599525893dSaalok die(gettext("fcntl: %s"), filename); 5609525893dSaalok 56187117650Saalok if (fstat64(uncompfd, &statbuf) == -1) { 56287117650Saalok (void) close(uncompfd); 56387117650Saalok die(gettext("fstat: %s"), filename); 56487117650Saalok } 56587117650Saalok 56687117650Saalok /* Zero length files don't need to be compressed */ 56787117650Saalok if (statbuf.st_size == 0) { 56887117650Saalok (void) close(uncompfd); 56987117650Saalok return; 57087117650Saalok } 57187117650Saalok 57287117650Saalok /* 57387117650Saalok * Create temporary files in the same directory that 57487117650Saalok * will hold the intermediate data 57587117650Saalok */ 57687117650Saalok dir = strdup(filename); 57787117650Saalok dir = dirname(dir); 57887117650Saalok file = strdup(filename); 57987117650Saalok file = basename(file); 58087117650Saalok (void) snprintf(tmpfilename, sizeof (tmpfilename), 58187117650Saalok "%s/.%sXXXXXX", dir, file); 58287117650Saalok (void) snprintf(comp_filename, sizeof (comp_filename), 58387117650Saalok "%s/.%sXXXXXX", dir, file); 58487117650Saalok 58587117650Saalok if ((tfd = mkstemp64(tmpfilename)) == -1) 58687117650Saalok goto cleanup; 58787117650Saalok 58887117650Saalok if ((compfd = mkstemp64(comp_filename)) == -1) 58987117650Saalok goto cleanup; 59087117650Saalok 59187117650Saalok /* 59287117650Saalok * Set the mode bits and owner of the compressed 59387117650Saalok * file to be that of the original uncompressed file 59487117650Saalok */ 59587117650Saalok (void) fchmod(compfd, statbuf.st_mode); 59687117650Saalok 59787117650Saalok if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1) 59887117650Saalok goto cleanup; 59987117650Saalok 60087117650Saalok /* 60187117650Saalok * Calculate the number of index entries required. 60287117650Saalok * index entries are stored as an array. adding 60387117650Saalok * a '2' here accounts for the fact that the last 60487117650Saalok * segment may not be a multiple of the segment size 60587117650Saalok */ 60687117650Saalok index_sz = (statbuf.st_size / segsize) + 2; 60787117650Saalok index = malloc(sizeof (*index) * index_sz); 60887117650Saalok 60987117650Saalok if (index == NULL) 61087117650Saalok goto cleanup; 61187117650Saalok 61287117650Saalok offset = 0; 61387117650Saalok lastread = segsize; 61487117650Saalok count = 0; 61587117650Saalok 61687117650Saalok /* 61787117650Saalok * Now read from the uncompressed file in 'segsize' 61887117650Saalok * sized chunks, compress what was read in and 61987117650Saalok * write it out to a temporary file 62087117650Saalok */ 62187117650Saalok for (;;) { 62287117650Saalok rbytes = read(uncompfd, uncompressed_seg, segsize); 62387117650Saalok 62487117650Saalok if (rbytes <= 0) 62587117650Saalok break; 62687117650Saalok 62787117650Saalok if (lastread < segsize) 62887117650Saalok goto cleanup; 62987117650Saalok 63087117650Saalok /* 63187117650Saalok * Account for the first byte that 63287117650Saalok * indicates whether a segment is 63387117650Saalok * compressed or not 63487117650Saalok */ 63587117650Saalok real_segsize = segsize - 1; 63687117650Saalok (void) li->l_compress(uncompressed_seg, rbytes, 63787117650Saalok compressed_seg + SEGHDR, &real_segsize, li->l_level); 63887117650Saalok 63987117650Saalok /* 64087117650Saalok * If the length of the compressed data is more 64187117650Saalok * than a threshold then there isn't any benefit 64287117650Saalok * to be had from compressing this segment - leave 64387117650Saalok * it uncompressed. 64487117650Saalok * 64587117650Saalok * NB. In case an error occurs during compression (above) 64687117650Saalok * the 'real_segsize' isn't changed. The logic below 64787117650Saalok * ensures that that segment is left uncompressed. 64887117650Saalok */ 64987117650Saalok len_compressed = real_segsize; 65087117650Saalok if (real_segsize > segsize - COMPRESS_THRESHOLD) { 65187117650Saalok (void) memcpy(compressed_seg + SEGHDR, uncompressed_seg, 65287117650Saalok rbytes); 65387117650Saalok type = UNCOMPRESSED; 65487117650Saalok len_compressed = rbytes; 65587117650Saalok } else { 65687117650Saalok type = COMPRESSED; 65787117650Saalok } 65887117650Saalok 65987117650Saalok /* 66087117650Saalok * Set the first byte or the SEGHDR to 66187117650Saalok * indicate if it's compressed or not 66287117650Saalok */ 66387117650Saalok *compressed_seg = type; 66487117650Saalok wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR); 66587117650Saalok if (wbytes != (len_compressed + SEGHDR)) { 66687117650Saalok rbytes = -1; 66787117650Saalok break; 66887117650Saalok } 66987117650Saalok 67087117650Saalok index[count] = BE_64(offset); 67187117650Saalok offset += wbytes; 67287117650Saalok lastread = rbytes; 67387117650Saalok count++; 67487117650Saalok } 67587117650Saalok 67687117650Saalok (void) close(uncompfd); 67787117650Saalok 67887117650Saalok if (rbytes < 0) 67987117650Saalok goto cleanup; 68087117650Saalok /* 68187117650Saalok * The last index entry is a sentinel entry. It does not point to 68287117650Saalok * an actual compressed segment but helps in computing the size of 68387117650Saalok * the compressed segment. The size of each compressed segment is 68487117650Saalok * computed by subtracting the current index value from the next 68587117650Saalok * one (the compressed blocks are stored sequentially) 68687117650Saalok */ 68787117650Saalok index[count++] = BE_64(offset); 68887117650Saalok 68987117650Saalok /* 69087117650Saalok * Now write the compressed data along with the 69187117650Saalok * header information to this file which will 69287117650Saalok * later be renamed to the original uncompressed 69387117650Saalok * file name 69487117650Saalok * 69587117650Saalok * The header is as follows - 69687117650Saalok * 69787117650Saalok * Signature (name of the compression algorithm) 69887117650Saalok * Compression segment size (a multiple of 512) 69987117650Saalok * Number of index entries 70087117650Saalok * Size of the last block 70187117650Saalok * The array containing the index entries 70287117650Saalok * 70387117650Saalok * the header is always stored in network byte 70487117650Saalok * order 70587117650Saalok */ 70687117650Saalok (void) bzero(algorithm, sizeof (algorithm)); 70787117650Saalok (void) strlcpy(algorithm, li->l_name, sizeof (algorithm)); 70887117650Saalok if (write(compfd, algorithm, sizeof (algorithm)) 70987117650Saalok != sizeof (algorithm)) 71087117650Saalok goto cleanup; 71187117650Saalok 71287117650Saalok segsize = htonl(segsize); 71387117650Saalok if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize)) 71487117650Saalok goto cleanup; 71587117650Saalok 71687117650Saalok index_entries = htonl(count); 71787117650Saalok if (write(compfd, &index_entries, sizeof (index_entries)) != 71887117650Saalok sizeof (index_entries)) 71987117650Saalok goto cleanup; 72087117650Saalok 72187117650Saalok lastread = htonl(lastread); 72287117650Saalok if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread)) 72387117650Saalok goto cleanup; 72487117650Saalok 72587117650Saalok for (i = 0; i < count; i++) { 72687117650Saalok if (write(compfd, index + i, sizeof (*index)) != 72787117650Saalok sizeof (*index)) 72887117650Saalok goto cleanup; 72987117650Saalok } 73087117650Saalok 73187117650Saalok /* Header is written, now write the compressed data */ 73287117650Saalok if (lseek(tfd, 0, SEEK_SET) != 0) 73387117650Saalok goto cleanup; 73487117650Saalok 73587117650Saalok rbytes = wbytes = 0; 73687117650Saalok 73787117650Saalok for (;;) { 73887117650Saalok rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR); 73987117650Saalok 74087117650Saalok if (rbytes <= 0) 74187117650Saalok break; 74287117650Saalok 74387117650Saalok if (write(compfd, compressed_seg, rbytes) != rbytes) 74487117650Saalok goto cleanup; 74587117650Saalok } 74687117650Saalok 74787117650Saalok if (fstat64(compfd, &statbuf) == -1) 74887117650Saalok goto cleanup; 74987117650Saalok 75087117650Saalok /* 75187117650Saalok * Round up the compressed file size to be a multiple of 75287117650Saalok * DEV_BSIZE. lofi(7D) likes it that way. 75387117650Saalok */ 75487117650Saalok if ((offset = statbuf.st_size % DEV_BSIZE) > 0) { 75587117650Saalok 75687117650Saalok offset = DEV_BSIZE - offset; 75787117650Saalok 75887117650Saalok for (i = 0; i < offset; i++) 75987117650Saalok uncompressed_seg[i] = '\0'; 76087117650Saalok if (write(compfd, uncompressed_seg, offset) != offset) 76187117650Saalok goto cleanup; 76287117650Saalok } 76387117650Saalok (void) close(compfd); 76487117650Saalok (void) close(tfd); 76587117650Saalok (void) unlink(tmpfilename); 76687117650Saalok cleanup: 76787117650Saalok if (rbytes < 0) { 76887117650Saalok if (tfd != -1) 76987117650Saalok (void) unlink(tmpfilename); 77087117650Saalok if (compfd != -1) 77187117650Saalok (void) unlink(comp_filename); 77287117650Saalok die(gettext("error compressing file %s"), filename); 77387117650Saalok } else { 77487117650Saalok /* Rename the compressed file to the actual file */ 77587117650Saalok if (rename(comp_filename, filename) == -1) { 77687117650Saalok (void) unlink(comp_filename); 77787117650Saalok die(gettext("error compressing file %s"), filename); 77887117650Saalok } 77987117650Saalok } 78087117650Saalok if (compressed_seg != NULL) 78187117650Saalok free(compressed_seg); 78287117650Saalok if (uncompressed_seg != NULL) 78387117650Saalok free(uncompressed_seg); 78487117650Saalok if (dir != NULL) 78587117650Saalok free(dir); 78687117650Saalok if (file != NULL) 78787117650Saalok free(file); 78887117650Saalok if (index != NULL) 78987117650Saalok free(index); 79087117650Saalok if (compfd != -1) 79187117650Saalok (void) close(compfd); 79287117650Saalok if (uncompfd != -1) 79387117650Saalok (void) close(uncompfd); 79487117650Saalok if (tfd != -1) 79587117650Saalok (void) close(tfd); 79687117650Saalok } 79787117650Saalok 79887117650Saalok static int 79987117650Saalok lofi_compress_select(const char *algname) 80087117650Saalok { 80187117650Saalok int i; 80287117650Saalok 80387117650Saalok for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) { 80487117650Saalok if (strcmp(lofi_compress_table[i].l_name, algname) == 0) 80587117650Saalok return (i); 80687117650Saalok } 80787117650Saalok return (-1); 80887117650Saalok } 80987117650Saalok 81087117650Saalok static void 81187117650Saalok check_algorithm_validity(const char *algname, int *compress_index) 81287117650Saalok { 81387117650Saalok *compress_index = lofi_compress_select(algname); 81487117650Saalok if (*compress_index < 0) 81587117650Saalok die(gettext("invalid algorithm name: %s\n"), algname); 81687117650Saalok } 81787117650Saalok 81887117650Saalok static void 81987117650Saalok check_file_validity(const char *filename) 82087117650Saalok { 8217c478bd9Sstevel@tonic-gate struct stat64 buf; 82287117650Saalok int error; 8237c478bd9Sstevel@tonic-gate int fd = -1; 8247c478bd9Sstevel@tonic-gate 8257c478bd9Sstevel@tonic-gate fd = open64(filename, O_RDONLY); 8267c478bd9Sstevel@tonic-gate if (fd == -1) { 8277c478bd9Sstevel@tonic-gate die(gettext("open: %s"), filename); 8287c478bd9Sstevel@tonic-gate } 8297c478bd9Sstevel@tonic-gate error = fstat64(fd, &buf); 8307c478bd9Sstevel@tonic-gate if (error == -1) { 8317c478bd9Sstevel@tonic-gate die(gettext("fstat: %s"), filename); 8327c478bd9Sstevel@tonic-gate } else if (!S_ISLOFIABLE(buf.st_mode)) { 8337c478bd9Sstevel@tonic-gate die(gettext("%s is not a regular file, " 8347c478bd9Sstevel@tonic-gate "block, or character device\n"), 8357c478bd9Sstevel@tonic-gate filename); 8367c478bd9Sstevel@tonic-gate } else if ((buf.st_size % DEV_BSIZE) != 0) { 837*f9153c6bSDina K Nimeh die(gettext("size of %s is not a multiple of %d\n"), 8387c478bd9Sstevel@tonic-gate filename, DEV_BSIZE); 8397c478bd9Sstevel@tonic-gate } 8407c478bd9Sstevel@tonic-gate (void) close(fd); 84187117650Saalok 84287117650Saalok if (name_to_minor(filename) != 0) { 843*f9153c6bSDina K Nimeh die(gettext("cannot use %s on itself\n"), LOFI_DRIVER_NAME); 8447c478bd9Sstevel@tonic-gate } 84587117650Saalok } 84687117650Saalok 84787117650Saalok static uint32_t 84887117650Saalok convert_to_num(const char *str) 84987117650Saalok { 85087117650Saalok int len; 85187117650Saalok uint32_t segsize, mult = 1; 85287117650Saalok 85387117650Saalok len = strlen(str); 85487117650Saalok if (len && isalpha(str[len - 1])) { 85587117650Saalok switch (str[len - 1]) { 85687117650Saalok case 'k': 85787117650Saalok case 'K': 85887117650Saalok mult = KILOBYTE; 85987117650Saalok break; 86087117650Saalok case 'b': 86187117650Saalok case 'B': 86287117650Saalok mult = BLOCK_SIZE; 86387117650Saalok break; 86487117650Saalok case 'm': 86587117650Saalok case 'M': 86687117650Saalok mult = MEGABYTE; 86787117650Saalok break; 86887117650Saalok case 'g': 86987117650Saalok case 'G': 87087117650Saalok mult = GIGABYTE; 87187117650Saalok break; 87287117650Saalok default: 87387117650Saalok die(gettext("invalid segment size %s\n"), str); 87487117650Saalok } 87587117650Saalok } 87687117650Saalok 87787117650Saalok segsize = atol(str); 87887117650Saalok segsize *= mult; 87987117650Saalok 88087117650Saalok return (segsize); 88187117650Saalok } 88287117650Saalok 88387117650Saalok int 88487117650Saalok main(int argc, char *argv[]) 88587117650Saalok { 88687117650Saalok int lfd; 88787117650Saalok int c; 88887117650Saalok const char *devicename = NULL; 88987117650Saalok const char *filename = NULL; 89087117650Saalok const char *algname = COMPRESS_ALGORITHM; 89187117650Saalok int openflag; 89287117650Saalok int minor; 89387117650Saalok int compress_index; 89487117650Saalok uint32_t segsize = SEGSIZE; 89587117650Saalok static char *lofictl = "/dev/" LOFI_CTL_NAME; 89687117650Saalok boolean_t force = B_FALSE; 897*f9153c6bSDina K Nimeh char realfilename[MAXPATHLEN]; 89887117650Saalok 89987117650Saalok pname = getpname(argv[0]); 90087117650Saalok 90187117650Saalok (void) setlocale(LC_ALL, ""); 90287117650Saalok (void) textdomain(TEXT_DOMAIN); 90387117650Saalok 90487117650Saalok while ((c = getopt(argc, argv, "a:C:d:s:U:f")) != EOF) { 90587117650Saalok switch (c) { 90687117650Saalok case 'a': 90787117650Saalok addflag = 1; 908*f9153c6bSDina K Nimeh if ((filename = realpath(optarg, realfilename)) == NULL) 909*f9153c6bSDina K Nimeh die("%s", optarg); 91087117650Saalok check_file_validity(filename); 91187117650Saalok 9127c478bd9Sstevel@tonic-gate if (((argc - optind) > 0) && (*argv[optind] != '-')) { 9137c478bd9Sstevel@tonic-gate /* optional device */ 9147c478bd9Sstevel@tonic-gate devicename = argv[optind]; 9157c478bd9Sstevel@tonic-gate optind++; 9167c478bd9Sstevel@tonic-gate } 9177c478bd9Sstevel@tonic-gate break; 91887117650Saalok case 'C': 91987117650Saalok compressflag = 1; 92087117650Saalok 92187117650Saalok if (((argc - optind) > 0) && 92287117650Saalok (*optarg == '-')) { 92387117650Saalok check_algorithm_validity(algname, 92487117650Saalok &compress_index); 92587117650Saalok optind--; 92687117650Saalok break; 92787117650Saalok } else if (((argc - optind) == 1) && 92887117650Saalok (*argv[optind] != '-')) { 92987117650Saalok algname = optarg; 930*f9153c6bSDina K Nimeh if ((filename = realpath(argv[optind], 931*f9153c6bSDina K Nimeh realfilename)) == NULL) 932*f9153c6bSDina K Nimeh die("%s", argv[optind]); 93387117650Saalok optind++; 93487117650Saalok } else if (((argc - optind) > 1) && 93587117650Saalok (*argv[optind] == '-')) { 93687117650Saalok algname = optarg; 93787117650Saalok check_algorithm_validity(algname, 93887117650Saalok &compress_index); 93987117650Saalok break; 94087117650Saalok } else { 941*f9153c6bSDina K Nimeh if ((filename = realpath(optarg, 942*f9153c6bSDina K Nimeh realfilename)) == NULL) 943*f9153c6bSDina K Nimeh die("%s", optarg); 94487117650Saalok } 94587117650Saalok 94687117650Saalok check_file_validity(filename); 94787117650Saalok check_algorithm_validity(algname, &compress_index); 94887117650Saalok break; 9497c478bd9Sstevel@tonic-gate case 'd': 9507c478bd9Sstevel@tonic-gate deleteflag = 1; 9517c478bd9Sstevel@tonic-gate 9527c478bd9Sstevel@tonic-gate minor = name_to_minor(optarg); 9537c478bd9Sstevel@tonic-gate if (minor != 0) 9547c478bd9Sstevel@tonic-gate devicename = optarg; 955*f9153c6bSDina K Nimeh else { 956*f9153c6bSDina K Nimeh if ((filename = realpath(optarg, 957*f9153c6bSDina K Nimeh realfilename)) == NULL) 958*f9153c6bSDina K Nimeh die("%s", optarg); 959*f9153c6bSDina K Nimeh } 9607c478bd9Sstevel@tonic-gate break; 9613d7072f8Seschrock case 'f': 9623d7072f8Seschrock force = B_TRUE; 9633d7072f8Seschrock break; 96487117650Saalok case 's': 96587117650Saalok segsize = convert_to_num(optarg); 96687117650Saalok 96787117650Saalok if (segsize == 0 || segsize % DEV_BSIZE) 96887117650Saalok die(gettext("segment size %s is invalid " 96987117650Saalok "or not a multiple of minimum block " 97087117650Saalok "size %ld\n"), optarg, DEV_BSIZE); 97187117650Saalok 972*f9153c6bSDina K Nimeh if ((filename = realpath(argv[optind], 973*f9153c6bSDina K Nimeh realfilename)) == NULL) 974*f9153c6bSDina K Nimeh die("%s", argv[optind]); 97587117650Saalok check_file_validity(filename); 97687117650Saalok optind++; 97787117650Saalok break; 97887117650Saalok case 'U': 97987117650Saalok uncompressflag = 1; 980*f9153c6bSDina K Nimeh if ((filename = realpath(optarg, realfilename)) == NULL) 981*f9153c6bSDina K Nimeh die("%s", optarg); 98287117650Saalok check_file_validity(filename); 98387117650Saalok break; 9847c478bd9Sstevel@tonic-gate case '?': 9857c478bd9Sstevel@tonic-gate default: 9867c478bd9Sstevel@tonic-gate errflag = 1; 9877c478bd9Sstevel@tonic-gate break; 9887c478bd9Sstevel@tonic-gate } 9897c478bd9Sstevel@tonic-gate } 99087117650Saalok if (errflag || 99187117650Saalok (addflag && deleteflag) || 99287117650Saalok ((compressflag || uncompressflag) && (addflag || deleteflag))) 9937c478bd9Sstevel@tonic-gate usage(); 9947c478bd9Sstevel@tonic-gate 9957c478bd9Sstevel@tonic-gate switch (argc - optind) { 9967c478bd9Sstevel@tonic-gate case 0: /* no more args */ 9977c478bd9Sstevel@tonic-gate break; 9987c478bd9Sstevel@tonic-gate case 1: /* one arg without options means print the association */ 9997c478bd9Sstevel@tonic-gate if (addflag || deleteflag) 10007c478bd9Sstevel@tonic-gate usage(); 100187117650Saalok if (compressflag || uncompressflag) 100287117650Saalok usage(); 10037c478bd9Sstevel@tonic-gate minor = name_to_minor(argv[optind]); 10047c478bd9Sstevel@tonic-gate if (minor != 0) 10057c478bd9Sstevel@tonic-gate devicename = argv[optind]; 1006*f9153c6bSDina K Nimeh else { 1007*f9153c6bSDina K Nimeh if ((filename = realpath(argv[optind], 1008*f9153c6bSDina K Nimeh realfilename)) == NULL) 1009*f9153c6bSDina K Nimeh die("%s", argv[optind]); 1010*f9153c6bSDina K Nimeh } 10117c478bd9Sstevel@tonic-gate break; 10127c478bd9Sstevel@tonic-gate default: 10137c478bd9Sstevel@tonic-gate usage(); 10147c478bd9Sstevel@tonic-gate break; 10157c478bd9Sstevel@tonic-gate } 10167c478bd9Sstevel@tonic-gate 10177c478bd9Sstevel@tonic-gate if (filename && !valid_abspath(filename)) 10187c478bd9Sstevel@tonic-gate exit(E_ERROR); 10197c478bd9Sstevel@tonic-gate 10207c478bd9Sstevel@tonic-gate /* 10217c478bd9Sstevel@tonic-gate * Here, we know the arguments are correct, the filename is an 10227c478bd9Sstevel@tonic-gate * absolute path, it exists and is a regular file. We don't yet 10237c478bd9Sstevel@tonic-gate * know that the device name is ok or not. 10247c478bd9Sstevel@tonic-gate */ 10257c478bd9Sstevel@tonic-gate /* 10267c478bd9Sstevel@tonic-gate * Now to the real work. 10277c478bd9Sstevel@tonic-gate */ 10287c478bd9Sstevel@tonic-gate openflag = O_EXCL; 102987117650Saalok if (addflag || deleteflag || compressflag || uncompressflag) 10307c478bd9Sstevel@tonic-gate openflag |= O_RDWR; 10317c478bd9Sstevel@tonic-gate else 10327c478bd9Sstevel@tonic-gate openflag |= O_RDONLY; 10337c478bd9Sstevel@tonic-gate lfd = open(lofictl, openflag); 10347c478bd9Sstevel@tonic-gate if (lfd == -1) { 10357c478bd9Sstevel@tonic-gate if ((errno == EPERM) || (errno == EACCES)) { 1036*f9153c6bSDina K Nimeh die(gettext("you do not have permission to perform " 1037*f9153c6bSDina K Nimeh "that operation.\n")); 10387c478bd9Sstevel@tonic-gate } else { 1039*f9153c6bSDina K Nimeh die(gettext("open: %s"), lofictl); 10407c478bd9Sstevel@tonic-gate } 10417c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 10427c478bd9Sstevel@tonic-gate } 10437c478bd9Sstevel@tonic-gate if (addflag) 104487117650Saalok add_mapping(lfd, devicename, filename, NULL, 0); 104587117650Saalok else if (compressflag) 10469525893dSaalok lofi_compress(&lfd, filename, compress_index, segsize); 104787117650Saalok else if (uncompressflag) 104887117650Saalok lofi_uncompress(lfd, filename); 10497c478bd9Sstevel@tonic-gate else if (deleteflag) 10503d7072f8Seschrock delete_mapping(lfd, devicename, filename, force); 10517c478bd9Sstevel@tonic-gate else if (filename || devicename) 10527c478bd9Sstevel@tonic-gate print_one_mapping(lfd, devicename, filename); 10537c478bd9Sstevel@tonic-gate else 10547c478bd9Sstevel@tonic-gate print_mappings(lfd); 1055579df0adSaalok 10569525893dSaalok if (lfd != -1) 10577c478bd9Sstevel@tonic-gate (void) close(lfd); 1058579df0adSaalok closelib(); 10597c478bd9Sstevel@tonic-gate return (E_SUCCESS); 10607c478bd9Sstevel@tonic-gate } 1061