xref: /titanic_50/usr/src/cmd/lofiadm/main.c (revision 579df0ad79cbf73b8473820d721b202c79d12d34)
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 /*
223d7072f8Seschrock  * Copyright 2007 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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <sys/param.h>
377c478bd9Sstevel@tonic-gate #include <sys/lofi.h>
387c478bd9Sstevel@tonic-gate #include <sys/stat.h>
3987117650Saalok #include <netinet/in.h>
407c478bd9Sstevel@tonic-gate #include <stdio.h>
417c478bd9Sstevel@tonic-gate #include <fcntl.h>
427c478bd9Sstevel@tonic-gate #include <locale.h>
437c478bd9Sstevel@tonic-gate #include <string.h>
4487117650Saalok #include <strings.h>
457c478bd9Sstevel@tonic-gate #include <errno.h>
467c478bd9Sstevel@tonic-gate #include <stdlib.h>
477c478bd9Sstevel@tonic-gate #include <unistd.h>
487c478bd9Sstevel@tonic-gate #include <stropts.h>
491d8ed0f0Svikram #include <libdevinfo.h>
5087117650Saalok #include <libgen.h>
5187117650Saalok #include <ctype.h>
52*579df0adSaalok #include <dlfcn.h>
537c478bd9Sstevel@tonic-gate #include "utils.h"
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate static const char USAGE[] =
567c478bd9Sstevel@tonic-gate 	"Usage: %s -a file [ device ]\n"
577c478bd9Sstevel@tonic-gate 	"       %s -d file | device \n"
5887117650Saalok 	"       %s -C [algorithm] [-s segment_size] file \n"
5987117650Saalok 	"       %s -U file \n"
607c478bd9Sstevel@tonic-gate 	"       %s [ device | file ]\n";
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate static const char *pname;
637c478bd9Sstevel@tonic-gate static int	addflag = 0;
647c478bd9Sstevel@tonic-gate static int	deleteflag = 0;
657c478bd9Sstevel@tonic-gate static int	errflag = 0;
6687117650Saalok static int	compressflag = 0;
6787117650Saalok static int 	uncompressflag = 0;
687c478bd9Sstevel@tonic-gate 
6987117650Saalok static int gzip_compress(void *src, size_t srclen, void *dst,
7087117650Saalok 	size_t *destlen, int level);
7187117650Saalok 
7287117650Saalok lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = {
7387117650Saalok 	{NULL,  gzip_compress,  6,	"gzip"}, /* default */
7487117650Saalok 	{NULL,	gzip_compress,	6,	"gzip-6"},
7587117650Saalok 	{NULL,	gzip_compress,	9, 	"gzip-9"}
7687117650Saalok };
7787117650Saalok 
7887117650Saalok #define	FORMAT 			"%-20s     %-30s	%s\n"
7987117650Saalok #define	NONE			"-"
8087117650Saalok #define	COMPRESS		"Compressed"
8187117650Saalok #define	COMPRESS_ALGORITHM	"gzip"
8287117650Saalok #define	COMPRESS_THRESHOLD	2048
8387117650Saalok #define	SEGSIZE			131072
8487117650Saalok #define	BLOCK_SIZE		512
8587117650Saalok #define	KILOBYTE		1024
8687117650Saalok #define	MEGABYTE		(KILOBYTE * KILOBYTE)
8787117650Saalok #define	GIGABYTE		(KILOBYTE * MEGABYTE)
88*579df0adSaalok #define	LIBZ			"libz.so"
89*579df0adSaalok 
90*579df0adSaalok static int (*compress2p)(void *, ulong_t *, void *, size_t, int) = NULL;
9187117650Saalok 
9287117650Saalok static int gzip_compress(void *src, size_t srclen, void *dst,
9387117650Saalok 	size_t *dstlen, int level)
9487117650Saalok {
95*579df0adSaalok 	void *libz_hdl = NULL;
9687117650Saalok 
97*579df0adSaalok 	/*
98*579df0adSaalok 	 * The first time we are called, attempt to dlopen()
99*579df0adSaalok 	 * libz.so and get a pointer to the compress2() function
100*579df0adSaalok 	 */
101*579df0adSaalok 	if (compress2p == NULL) {
102*579df0adSaalok 		if ((libz_hdl = openlib(LIBZ)) == NULL)
103*579df0adSaalok 			die(gettext("could not find %s. "
104*579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
105*579df0adSaalok 
106*579df0adSaalok 		if ((compress2p =
107*579df0adSaalok 		    (int (*)(void *, ulong_t *, void *, size_t, int))
108*579df0adSaalok 		    dlsym(libz_hdl, "compress2")) == NULL) {
109*579df0adSaalok 			closelib();
110*579df0adSaalok 			die(gettext("could not find the correct %s. "
111*579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
112*579df0adSaalok 		}
113*579df0adSaalok 	}
114*579df0adSaalok 
115*579df0adSaalok 	if ((*compress2p)(dst, (ulong_t *)dstlen, src, srclen, level) != 0)
116*579df0adSaalok 		return (-1);
11787117650Saalok 	return (0);
11887117650Saalok }
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate /*
1217c478bd9Sstevel@tonic-gate  * Print the list of all the mappings. Including a header.
1227c478bd9Sstevel@tonic-gate  */
1237c478bd9Sstevel@tonic-gate static void
1247c478bd9Sstevel@tonic-gate print_mappings(int fd)
1257c478bd9Sstevel@tonic-gate {
1267c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
1277c478bd9Sstevel@tonic-gate 	int	minor;
1287c478bd9Sstevel@tonic-gate 	int	maxminor;
12987117650Saalok 	char	path[MAXPATHLEN];
13087117650Saalok 	char	options[MAXPATHLEN];
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	li.li_minor = 0;
1337c478bd9Sstevel@tonic-gate 	if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) {
1347c478bd9Sstevel@tonic-gate 		perror("ioctl");
1357c478bd9Sstevel@tonic-gate 		exit(E_ERROR);
1367c478bd9Sstevel@tonic-gate 	}
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate 	maxminor = li.li_minor;
1397c478bd9Sstevel@tonic-gate 
14087117650Saalok 	(void) printf(FORMAT, "Block Device", "File", "Options");
1417c478bd9Sstevel@tonic-gate 	for (minor = 1; minor <= maxminor; minor++) {
1427c478bd9Sstevel@tonic-gate 		li.li_minor = minor;
1437c478bd9Sstevel@tonic-gate 		if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) {
1447c478bd9Sstevel@tonic-gate 			if (errno == ENXIO)
1457c478bd9Sstevel@tonic-gate 				continue;
1467c478bd9Sstevel@tonic-gate 			perror("ioctl");
1477c478bd9Sstevel@tonic-gate 			break;
1487c478bd9Sstevel@tonic-gate 		}
1497c478bd9Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "/dev/%s/%d",
1507c478bd9Sstevel@tonic-gate 		    LOFI_BLOCK_NAME, minor);
15187117650Saalok 		if (li.li_algorithm[0] == '\0')
15287117650Saalok 			(void) snprintf(options, sizeof (options), "%s", NONE);
15387117650Saalok 		else
15487117650Saalok 			(void) snprintf(options, sizeof (options),
15587117650Saalok 			    COMPRESS "(%s)", li.li_algorithm);
15687117650Saalok 
15787117650Saalok 		(void) printf(FORMAT, path, li.li_filename, options);
1587c478bd9Sstevel@tonic-gate 	}
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate static void
1627c478bd9Sstevel@tonic-gate usage(void)
1637c478bd9Sstevel@tonic-gate {
16487117650Saalok 	(void) fprintf(stderr, gettext(USAGE), pname, pname,
16587117650Saalok 	    pname, pname, pname);
1667c478bd9Sstevel@tonic-gate 	exit(E_USAGE);
1677c478bd9Sstevel@tonic-gate }
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate /*
1707c478bd9Sstevel@tonic-gate  * Translate a lofi device name to a minor number. We might be asked
1717c478bd9Sstevel@tonic-gate  * to do this when there is no association (such as when the user specifies
1727c478bd9Sstevel@tonic-gate  * a particular device), so we can only look at the string.
1737c478bd9Sstevel@tonic-gate  */
1747c478bd9Sstevel@tonic-gate static int
1757c478bd9Sstevel@tonic-gate name_to_minor(const char *devicename)
1767c478bd9Sstevel@tonic-gate {
1777c478bd9Sstevel@tonic-gate 	int	minor;
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate 	if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) {
1807c478bd9Sstevel@tonic-gate 		return (minor);
1817c478bd9Sstevel@tonic-gate 	}
1827c478bd9Sstevel@tonic-gate 	if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) {
1837c478bd9Sstevel@tonic-gate 		return (minor);
1847c478bd9Sstevel@tonic-gate 	}
1857c478bd9Sstevel@tonic-gate 	return (0);
1867c478bd9Sstevel@tonic-gate }
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate /*
1897c478bd9Sstevel@tonic-gate  * This might be the first time we've used this minor number. If so,
1907c478bd9Sstevel@tonic-gate  * it might also be that the /dev links are in the process of being created
1917c478bd9Sstevel@tonic-gate  * by devfsadmd (or that they'll be created "soon"). We cannot return
1927c478bd9Sstevel@tonic-gate  * until they're there or the invoker of lofiadm might try to use them
1937c478bd9Sstevel@tonic-gate  * and not find them. This can happen if a shell script is running on
1947c478bd9Sstevel@tonic-gate  * an MP.
1957c478bd9Sstevel@tonic-gate  */
1967c478bd9Sstevel@tonic-gate static int sleeptime = 2;	/* number of seconds to sleep between stat's */
1977c478bd9Sstevel@tonic-gate static int maxsleep = 120;	/* maximum number of seconds to sleep */
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate static void
2007c478bd9Sstevel@tonic-gate wait_until_dev_complete(int minor)
2017c478bd9Sstevel@tonic-gate {
2027c478bd9Sstevel@tonic-gate 	struct stat64 buf;
2037c478bd9Sstevel@tonic-gate 	int	cursleep;
20487117650Saalok 	char	blkpath[MAXPATHLEN];
20587117650Saalok 	char	charpath[MAXPATHLEN];
2061d8ed0f0Svikram 	di_devlink_handle_t hdl;
2071d8ed0f0Svikram 
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	(void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d",
2107c478bd9Sstevel@tonic-gate 	    LOFI_BLOCK_NAME, minor);
2117c478bd9Sstevel@tonic-gate 	(void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d",
2127c478bd9Sstevel@tonic-gate 	    LOFI_CHAR_NAME, minor);
2131d8ed0f0Svikram 
2141d8ed0f0Svikram 	/* Check if links already present */
2151d8ed0f0Svikram 	if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
2161d8ed0f0Svikram 		return;
2171d8ed0f0Svikram 
2181d8ed0f0Svikram 	/* First use di_devlink_init() */
2191d8ed0f0Svikram 	if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) {
2201d8ed0f0Svikram 		(void) di_devlink_fini(&hdl);
2211d8ed0f0Svikram 		goto out;
2221d8ed0f0Svikram 	}
2231d8ed0f0Svikram 
2241d8ed0f0Svikram 	/*
2251d8ed0f0Svikram 	 * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will
2261d8ed0f0Svikram 	 * only fail if the caller is non-root. In that case, wait for
2271d8ed0f0Svikram 	 * link creation via sysevents.
2281d8ed0f0Svikram 	 */
2297c478bd9Sstevel@tonic-gate 	cursleep = 0;
2307c478bd9Sstevel@tonic-gate 	while (cursleep < maxsleep) {
2317c478bd9Sstevel@tonic-gate 		if ((stat64(blkpath, &buf) == -1) ||
2327c478bd9Sstevel@tonic-gate 		    (stat64(charpath, &buf) == -1)) {
2337c478bd9Sstevel@tonic-gate 			(void) sleep(sleeptime);
2347c478bd9Sstevel@tonic-gate 			cursleep += sleeptime;
2357c478bd9Sstevel@tonic-gate 			continue;
2367c478bd9Sstevel@tonic-gate 		}
2377c478bd9Sstevel@tonic-gate 		return;
2387c478bd9Sstevel@tonic-gate 	}
2391d8ed0f0Svikram 
2407c478bd9Sstevel@tonic-gate 	/* one last try */
2411d8ed0f0Svikram 
2421d8ed0f0Svikram out:
2437c478bd9Sstevel@tonic-gate 	if (stat64(blkpath, &buf) == -1) {
2447c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), blkpath);
2457c478bd9Sstevel@tonic-gate 	}
2467c478bd9Sstevel@tonic-gate 	if (stat64(charpath, &buf) == -1) {
2477c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), charpath);
2487c478bd9Sstevel@tonic-gate 	}
2497c478bd9Sstevel@tonic-gate }
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate /*
2527c478bd9Sstevel@tonic-gate  * Add a device association. If devicename is NULL, let the driver
2537c478bd9Sstevel@tonic-gate  * pick a device.
2547c478bd9Sstevel@tonic-gate  */
2557c478bd9Sstevel@tonic-gate static void
25687117650Saalok add_mapping(int lfd, const char *devicename, const char *filename,
25787117650Saalok     int *minor_created, int suppress)
2587c478bd9Sstevel@tonic-gate {
2597c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
2607c478bd9Sstevel@tonic-gate 	int	minor;
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
2637c478bd9Sstevel@tonic-gate 		/* pick one */
2647c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
26587117650Saalok 		(void) strlcpy(li.li_filename, filename,
26687117650Saalok 		    sizeof (li.li_filename));
2677c478bd9Sstevel@tonic-gate 		minor = ioctl(lfd, LOFI_MAP_FILE, &li);
2687c478bd9Sstevel@tonic-gate 		if (minor == -1) {
2697c478bd9Sstevel@tonic-gate 			die(gettext("could not map file %s"), filename);
2707c478bd9Sstevel@tonic-gate 		}
2717c478bd9Sstevel@tonic-gate 		wait_until_dev_complete(minor);
2727c478bd9Sstevel@tonic-gate 		/* print one picked */
27387117650Saalok 		if (!suppress)
2747c478bd9Sstevel@tonic-gate 			(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor);
27587117650Saalok 
27687117650Saalok 		/* fill in the minor if needed */
27787117650Saalok 		if (minor_created != NULL) {
27887117650Saalok 			*minor_created = minor;
27987117650Saalok 		}
2807c478bd9Sstevel@tonic-gate 		return;
2817c478bd9Sstevel@tonic-gate 	}
2827c478bd9Sstevel@tonic-gate 	/* use device we were given */
2837c478bd9Sstevel@tonic-gate 	minor = name_to_minor(devicename);
2847c478bd9Sstevel@tonic-gate 	if (minor == 0) {
2857c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
2867c478bd9Sstevel@tonic-gate 	}
28787117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
2887c478bd9Sstevel@tonic-gate 	li.li_minor = minor;
2897c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) {
2907c478bd9Sstevel@tonic-gate 		die(gettext("could not map file %s to %s"), filename,
2917c478bd9Sstevel@tonic-gate 		    devicename);
2927c478bd9Sstevel@tonic-gate 	}
2937c478bd9Sstevel@tonic-gate 	wait_until_dev_complete(minor);
2947c478bd9Sstevel@tonic-gate }
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate /*
2977c478bd9Sstevel@tonic-gate  * Remove an association. Delete by device name if non-NULL, or by
2987c478bd9Sstevel@tonic-gate  * filename otherwise.
2997c478bd9Sstevel@tonic-gate  */
3007c478bd9Sstevel@tonic-gate static void
3013d7072f8Seschrock delete_mapping(int lfd, const char *devicename, const char *filename,
3023d7072f8Seschrock     boolean_t force)
3037c478bd9Sstevel@tonic-gate {
3047c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
3057c478bd9Sstevel@tonic-gate 
3063d7072f8Seschrock 	li.li_force = force;
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 	/* delete by device */
3187c478bd9Sstevel@tonic-gate 
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) ||
40587117650Saalok 	    (li.li_algorithm == '\0')) {
40687117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
40787117650Saalok 		return;
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);
42787117650Saalok 		return;
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);
44287117650Saalok 		return;
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
48487117650Saalok 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;
48987117650Saalok 	char tmpfilename[MAXPATHLEN];
49087117650Saalok 	char comp_filename[MAXPATHLEN];
49187117650Saalok 	char algorithm[MAXALGLEN];
49287117650Saalok 	char *dir = NULL, *file = NULL;
49387117650Saalok 	uchar_t *uncompressed_seg = NULL;
49487117650Saalok 	uchar_t *compressed_seg = NULL;
49587117650Saalok 	uint32_t compressed_segsize;
49687117650Saalok 	uint32_t len_compressed, count;
49787117650Saalok 	uint32_t index_entries, index_sz;
49887117650Saalok 	uint64_t *index = NULL;
49987117650Saalok 	uint64_t offset;
50087117650Saalok 	size_t real_segsize;
50187117650Saalok 	struct stat64 statbuf;
50287117650Saalok 	int compfd = -1, uncompfd = -1;
50387117650Saalok 	int tfd = -1;
50487117650Saalok 	ssize_t rbytes, wbytes, lastread;
50587117650Saalok 	int i, type;
50687117650Saalok 
50787117650Saalok 	/*
50887117650Saalok 	 * Disallow compressing the file if it is
50987117650Saalok 	 * already mapped
51087117650Saalok 	 */
51187117650Saalok 	lic.li_minor = 0;
51287117650Saalok 	(void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename));
51387117650Saalok 	if (ioctl(lfd, LOFI_GET_MINOR, &lic) != -1)
51487117650Saalok 		die(gettext("%s must be unmapped before compressing"),
51587117650Saalok 		    filename);
51687117650Saalok 
51787117650Saalok 	li = &lofi_compress_table[compress_index];
51887117650Saalok 
51987117650Saalok 	/*
52087117650Saalok 	 * The size of the buffer to hold compressed data must
52187117650Saalok 	 * be slightly larger than the compressed segment size.
52287117650Saalok 	 *
52387117650Saalok 	 * The compress functions use part of the buffer as
52487117650Saalok 	 * scratch space to do calculations.
52587117650Saalok 	 * Ref: http://www.zlib.net/manual.html#compress2
52687117650Saalok 	 */
52787117650Saalok 	compressed_segsize = segsize + (segsize >> 6);
52887117650Saalok 	compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR);
52987117650Saalok 	uncompressed_seg = (uchar_t *)malloc(segsize);
53087117650Saalok 
53187117650Saalok 	if (compressed_seg == NULL || uncompressed_seg == NULL)
53287117650Saalok 		die(gettext("No memory"));
53387117650Saalok 
53487117650Saalok 	if ((uncompfd = open64(filename, O_RDONLY|O_LARGEFILE, 0)) == -1)
53587117650Saalok 		die(gettext("open: %s"), filename);
53687117650Saalok 
53787117650Saalok 	if (fstat64(uncompfd, &statbuf) == -1) {
53887117650Saalok 		(void) close(uncompfd);
53987117650Saalok 		die(gettext("fstat: %s"), filename);
54087117650Saalok 	}
54187117650Saalok 
54287117650Saalok 	/* Zero length files don't need to be compressed */
54387117650Saalok 	if (statbuf.st_size == 0) {
54487117650Saalok 		(void) close(uncompfd);
54587117650Saalok 		return;
54687117650Saalok 	}
54787117650Saalok 
54887117650Saalok 	/*
54987117650Saalok 	 * Create temporary files in the same directory that
55087117650Saalok 	 * will hold the intermediate data
55187117650Saalok 	 */
55287117650Saalok 	dir = strdup(filename);
55387117650Saalok 	dir = dirname(dir);
55487117650Saalok 	file = strdup(filename);
55587117650Saalok 	file = basename(file);
55687117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
55787117650Saalok 	    "%s/.%sXXXXXX", dir, file);
55887117650Saalok 	(void) snprintf(comp_filename, sizeof (comp_filename),
55987117650Saalok 	    "%s/.%sXXXXXX", dir, file);
56087117650Saalok 
56187117650Saalok 	if ((tfd = mkstemp64(tmpfilename)) == -1)
56287117650Saalok 		goto cleanup;
56387117650Saalok 
56487117650Saalok 	if ((compfd = mkstemp64(comp_filename)) == -1)
56587117650Saalok 		goto cleanup;
56687117650Saalok 
56787117650Saalok 	/*
56887117650Saalok 	 * Set the mode bits and owner of the compressed
56987117650Saalok 	 * file to be that of the original uncompressed file
57087117650Saalok 	 */
57187117650Saalok 	(void) fchmod(compfd, statbuf.st_mode);
57287117650Saalok 
57387117650Saalok 	if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1)
57487117650Saalok 		goto cleanup;
57587117650Saalok 
57687117650Saalok 	/*
57787117650Saalok 	 * Calculate the number of index entries required.
57887117650Saalok 	 * index entries are stored as an array. adding
57987117650Saalok 	 * a '2' here accounts for the fact that the last
58087117650Saalok 	 * segment may not be a multiple of the segment size
58187117650Saalok 	 */
58287117650Saalok 	index_sz = (statbuf.st_size / segsize) + 2;
58387117650Saalok 	index = malloc(sizeof (*index) * index_sz);
58487117650Saalok 
58587117650Saalok 	if (index == NULL)
58687117650Saalok 		goto cleanup;
58787117650Saalok 
58887117650Saalok 	offset = 0;
58987117650Saalok 	lastread = segsize;
59087117650Saalok 	count = 0;
59187117650Saalok 
59287117650Saalok 	/*
59387117650Saalok 	 * Now read from the uncompressed file in 'segsize'
59487117650Saalok 	 * sized chunks, compress what was read in and
59587117650Saalok 	 * write it out to a temporary file
59687117650Saalok 	 */
59787117650Saalok 	for (;;) {
59887117650Saalok 		rbytes = read(uncompfd, uncompressed_seg, segsize);
59987117650Saalok 
60087117650Saalok 		if (rbytes <= 0)
60187117650Saalok 			break;
60287117650Saalok 
60387117650Saalok 		if (lastread < segsize)
60487117650Saalok 			goto cleanup;
60587117650Saalok 
60687117650Saalok 		/*
60787117650Saalok 		 * Account for the first byte that
60887117650Saalok 		 * indicates whether a segment is
60987117650Saalok 		 * compressed or not
61087117650Saalok 		 */
61187117650Saalok 		real_segsize = segsize - 1;
61287117650Saalok 		(void) li->l_compress(uncompressed_seg, rbytes,
61387117650Saalok 		    compressed_seg + SEGHDR, &real_segsize, li->l_level);
61487117650Saalok 
61587117650Saalok 		/*
61687117650Saalok 		 * If the length of the compressed data is more
61787117650Saalok 		 * than a threshold then there isn't any benefit
61887117650Saalok 		 * to be had from compressing this segment - leave
61987117650Saalok 		 * it uncompressed.
62087117650Saalok 		 *
62187117650Saalok 		 * NB. In case an error occurs during compression (above)
62287117650Saalok 		 * the 'real_segsize' isn't changed. The logic below
62387117650Saalok 		 * ensures that that segment is left uncompressed.
62487117650Saalok 		 */
62587117650Saalok 		len_compressed = real_segsize;
62687117650Saalok 		if (real_segsize > segsize - COMPRESS_THRESHOLD) {
62787117650Saalok 			(void) memcpy(compressed_seg + SEGHDR, uncompressed_seg,
62887117650Saalok 			    rbytes);
62987117650Saalok 			type = UNCOMPRESSED;
63087117650Saalok 			len_compressed = rbytes;
63187117650Saalok 		} else {
63287117650Saalok 			type = COMPRESSED;
63387117650Saalok 		}
63487117650Saalok 
63587117650Saalok 		/*
63687117650Saalok 		 * Set the first byte or the SEGHDR to
63787117650Saalok 		 * indicate if it's compressed or not
63887117650Saalok 		 */
63987117650Saalok 		*compressed_seg = type;
64087117650Saalok 		wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR);
64187117650Saalok 		if (wbytes != (len_compressed + SEGHDR)) {
64287117650Saalok 			rbytes = -1;
64387117650Saalok 			break;
64487117650Saalok 		}
64587117650Saalok 
64687117650Saalok 		index[count] = BE_64(offset);
64787117650Saalok 		offset += wbytes;
64887117650Saalok 		lastread = rbytes;
64987117650Saalok 		count++;
65087117650Saalok 	}
65187117650Saalok 
65287117650Saalok 	(void) close(uncompfd);
65387117650Saalok 
65487117650Saalok 	if (rbytes < 0)
65587117650Saalok 		goto cleanup;
65687117650Saalok 	/*
65787117650Saalok 	 * The last index entry is a sentinel entry. It does not point to
65887117650Saalok 	 * an actual compressed segment but helps in computing the size of
65987117650Saalok 	 * the compressed segment. The size of each compressed segment is
66087117650Saalok 	 * computed by subtracting the current index value from the next
66187117650Saalok 	 * one (the compressed blocks are stored sequentially)
66287117650Saalok 	 */
66387117650Saalok 	index[count++] = BE_64(offset);
66487117650Saalok 
66587117650Saalok 	/*
66687117650Saalok 	 * Now write the compressed data along with the
66787117650Saalok 	 * header information to this file which will
66887117650Saalok 	 * later be renamed to the original uncompressed
66987117650Saalok 	 * file name
67087117650Saalok 	 *
67187117650Saalok 	 * The header is as follows -
67287117650Saalok 	 *
67387117650Saalok 	 * Signature (name of the compression algorithm)
67487117650Saalok 	 * Compression segment size (a multiple of 512)
67587117650Saalok 	 * Number of index entries
67687117650Saalok 	 * Size of the last block
67787117650Saalok 	 * The array containing the index entries
67887117650Saalok 	 *
67987117650Saalok 	 * the header is always stored in network byte
68087117650Saalok 	 * order
68187117650Saalok 	 */
68287117650Saalok 	(void) bzero(algorithm, sizeof (algorithm));
68387117650Saalok 	(void) strlcpy(algorithm, li->l_name, sizeof (algorithm));
68487117650Saalok 	if (write(compfd, algorithm, sizeof (algorithm))
68587117650Saalok 	    != sizeof (algorithm))
68687117650Saalok 		goto cleanup;
68787117650Saalok 
68887117650Saalok 	segsize = htonl(segsize);
68987117650Saalok 	if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize))
69087117650Saalok 		goto cleanup;
69187117650Saalok 
69287117650Saalok 	index_entries = htonl(count);
69387117650Saalok 	if (write(compfd, &index_entries, sizeof (index_entries)) !=
69487117650Saalok 	    sizeof (index_entries))
69587117650Saalok 		goto cleanup;
69687117650Saalok 
69787117650Saalok 	lastread = htonl(lastread);
69887117650Saalok 	if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread))
69987117650Saalok 		goto cleanup;
70087117650Saalok 
70187117650Saalok 	for (i = 0; i < count; i++) {
70287117650Saalok 		if (write(compfd, index + i, sizeof (*index)) !=
70387117650Saalok 		    sizeof (*index))
70487117650Saalok 			goto cleanup;
70587117650Saalok 	}
70687117650Saalok 
70787117650Saalok 	/* Header is written, now write the compressed data */
70887117650Saalok 	if (lseek(tfd, 0, SEEK_SET) != 0)
70987117650Saalok 		goto cleanup;
71087117650Saalok 
71187117650Saalok 	rbytes = wbytes = 0;
71287117650Saalok 
71387117650Saalok 	for (;;) {
71487117650Saalok 		rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR);
71587117650Saalok 
71687117650Saalok 		if (rbytes <= 0)
71787117650Saalok 			break;
71887117650Saalok 
71987117650Saalok 		if (write(compfd, compressed_seg, rbytes) != rbytes)
72087117650Saalok 			goto cleanup;
72187117650Saalok 	}
72287117650Saalok 
72387117650Saalok 	if (fstat64(compfd, &statbuf) == -1)
72487117650Saalok 		goto cleanup;
72587117650Saalok 
72687117650Saalok 	/*
72787117650Saalok 	 * Round up the compressed file size to be a multiple of
72887117650Saalok 	 * DEV_BSIZE. lofi(7D) likes it that way.
72987117650Saalok 	 */
73087117650Saalok 	if ((offset = statbuf.st_size % DEV_BSIZE) > 0) {
73187117650Saalok 
73287117650Saalok 		offset = DEV_BSIZE - offset;
73387117650Saalok 
73487117650Saalok 		for (i = 0; i < offset; i++)
73587117650Saalok 			uncompressed_seg[i] = '\0';
73687117650Saalok 		if (write(compfd, uncompressed_seg, offset) != offset)
73787117650Saalok 			goto cleanup;
73887117650Saalok 	}
73987117650Saalok 	(void) close(compfd);
74087117650Saalok 	(void) close(tfd);
74187117650Saalok 	(void) unlink(tmpfilename);
74287117650Saalok cleanup:
74387117650Saalok 	if (rbytes < 0) {
74487117650Saalok 		if (tfd != -1)
74587117650Saalok 			(void) unlink(tmpfilename);
74687117650Saalok 		if (compfd != -1)
74787117650Saalok 			(void) unlink(comp_filename);
74887117650Saalok 		die(gettext("error compressing file %s"), filename);
74987117650Saalok 	} else {
75087117650Saalok 		/* Rename the compressed file to the actual file */
75187117650Saalok 		if (rename(comp_filename, filename) == -1) {
75287117650Saalok 			(void) unlink(comp_filename);
75387117650Saalok 			die(gettext("error compressing file %s"), filename);
75487117650Saalok 		}
75587117650Saalok 	}
75687117650Saalok 	if (compressed_seg != NULL)
75787117650Saalok 		free(compressed_seg);
75887117650Saalok 	if (uncompressed_seg != NULL)
75987117650Saalok 		free(uncompressed_seg);
76087117650Saalok 	if (dir != NULL)
76187117650Saalok 		free(dir);
76287117650Saalok 	if (file != NULL)
76387117650Saalok 		free(file);
76487117650Saalok 	if (index != NULL)
76587117650Saalok 		free(index);
76687117650Saalok 	if (compfd != -1)
76787117650Saalok 		(void) close(compfd);
76887117650Saalok 	if (uncompfd != -1)
76987117650Saalok 		(void) close(uncompfd);
77087117650Saalok 	if (tfd != -1)
77187117650Saalok 		(void) close(tfd);
77287117650Saalok }
77387117650Saalok 
77487117650Saalok static int
77587117650Saalok lofi_compress_select(const char *algname)
77687117650Saalok {
77787117650Saalok 	int i;
77887117650Saalok 
77987117650Saalok 	for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) {
78087117650Saalok 		if (strcmp(lofi_compress_table[i].l_name, algname) == 0)
78187117650Saalok 			return (i);
78287117650Saalok 	}
78387117650Saalok 	return (-1);
78487117650Saalok }
78587117650Saalok 
78687117650Saalok static void
78787117650Saalok check_algorithm_validity(const char *algname, int *compress_index)
78887117650Saalok {
78987117650Saalok 	*compress_index = lofi_compress_select(algname);
79087117650Saalok 	if (*compress_index < 0)
79187117650Saalok 		die(gettext("invalid algorithm name: %s\n"), algname);
79287117650Saalok }
79387117650Saalok 
79487117650Saalok static void
79587117650Saalok check_file_validity(const char *filename)
79687117650Saalok {
7977c478bd9Sstevel@tonic-gate 	struct stat64 buf;
79887117650Saalok 	int 	error;
7997c478bd9Sstevel@tonic-gate 	int	fd = -1;
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 	fd = open64(filename, O_RDONLY);
8027c478bd9Sstevel@tonic-gate 	if (fd == -1) {
8037c478bd9Sstevel@tonic-gate 		die(gettext("open: %s"), filename);
8047c478bd9Sstevel@tonic-gate 	}
8057c478bd9Sstevel@tonic-gate 	error = fstat64(fd, &buf);
8067c478bd9Sstevel@tonic-gate 	if (error == -1) {
8077c478bd9Sstevel@tonic-gate 		die(gettext("fstat: %s"), filename);
8087c478bd9Sstevel@tonic-gate 	} else if (!S_ISLOFIABLE(buf.st_mode)) {
8097c478bd9Sstevel@tonic-gate 		die(gettext("%s is not a regular file, "
8107c478bd9Sstevel@tonic-gate 		    "block, or character device\n"),
8117c478bd9Sstevel@tonic-gate 		    filename);
8127c478bd9Sstevel@tonic-gate 	} else if ((buf.st_size % DEV_BSIZE) != 0) {
8137c478bd9Sstevel@tonic-gate 		die(gettext("size of %s is not a multiple "
8147c478bd9Sstevel@tonic-gate 		    "of %d\n"),
8157c478bd9Sstevel@tonic-gate 		    filename, DEV_BSIZE);
8167c478bd9Sstevel@tonic-gate 	}
8177c478bd9Sstevel@tonic-gate 	(void) close(fd);
81887117650Saalok 
81987117650Saalok 	if (name_to_minor(filename) != 0) {
8207c478bd9Sstevel@tonic-gate 		die(gettext("cannot use " LOFI_DRIVER_NAME
82187117650Saalok 		    " on itself\n"), NULL);
8227c478bd9Sstevel@tonic-gate 	}
82387117650Saalok }
82487117650Saalok 
82587117650Saalok static uint32_t
82687117650Saalok convert_to_num(const char *str)
82787117650Saalok {
82887117650Saalok 	int len;
82987117650Saalok 	uint32_t segsize, mult = 1;
83087117650Saalok 
83187117650Saalok 	len = strlen(str);
83287117650Saalok 	if (len && isalpha(str[len - 1])) {
83387117650Saalok 		switch (str[len - 1]) {
83487117650Saalok 		case 'k':
83587117650Saalok 		case 'K':
83687117650Saalok 			mult = KILOBYTE;
83787117650Saalok 			break;
83887117650Saalok 		case 'b':
83987117650Saalok 		case 'B':
84087117650Saalok 			mult = BLOCK_SIZE;
84187117650Saalok 			break;
84287117650Saalok 		case 'm':
84387117650Saalok 		case 'M':
84487117650Saalok 			mult = MEGABYTE;
84587117650Saalok 			break;
84687117650Saalok 		case 'g':
84787117650Saalok 		case 'G':
84887117650Saalok 			mult = GIGABYTE;
84987117650Saalok 			break;
85087117650Saalok 		default:
85187117650Saalok 			die(gettext("invalid segment size %s\n"), str);
85287117650Saalok 		}
85387117650Saalok 	}
85487117650Saalok 
85587117650Saalok 	segsize = atol(str);
85687117650Saalok 	segsize *= mult;
85787117650Saalok 
85887117650Saalok 	return (segsize);
85987117650Saalok }
86087117650Saalok 
86187117650Saalok int
86287117650Saalok main(int argc, char *argv[])
86387117650Saalok {
86487117650Saalok 	int	lfd;
86587117650Saalok 	int	c;
86687117650Saalok 	const char *devicename = NULL;
86787117650Saalok 	const char *filename = NULL;
86887117650Saalok 	const char *algname = COMPRESS_ALGORITHM;
86987117650Saalok 	int	openflag;
87087117650Saalok 	int	minor;
87187117650Saalok 	int 	compress_index;
87287117650Saalok 	uint32_t segsize = SEGSIZE;
87387117650Saalok 	static char *lofictl = "/dev/" LOFI_CTL_NAME;
87487117650Saalok 	boolean_t force = B_FALSE;
87587117650Saalok 
87687117650Saalok 	pname = getpname(argv[0]);
87787117650Saalok 
87887117650Saalok 	(void) setlocale(LC_ALL, "");
87987117650Saalok 	(void) textdomain(TEXT_DOMAIN);
88087117650Saalok 
88187117650Saalok 	while ((c = getopt(argc, argv, "a:C:d:s:U:f")) != EOF) {
88287117650Saalok 		switch (c) {
88387117650Saalok 		case 'a':
88487117650Saalok 			addflag = 1;
88587117650Saalok 			filename = optarg;
88687117650Saalok 			check_file_validity(filename);
88787117650Saalok 
8887c478bd9Sstevel@tonic-gate 			if (((argc - optind) > 0) && (*argv[optind] != '-')) {
8897c478bd9Sstevel@tonic-gate 				/* optional device */
8907c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
8917c478bd9Sstevel@tonic-gate 				optind++;
8927c478bd9Sstevel@tonic-gate 			}
8937c478bd9Sstevel@tonic-gate 			break;
89487117650Saalok 		case 'C':
89587117650Saalok 			compressflag = 1;
89687117650Saalok 
89787117650Saalok 			if (((argc - optind) > 0) &&
89887117650Saalok 			    (*optarg == '-')) {
89987117650Saalok 				check_algorithm_validity(algname,
90087117650Saalok 				    &compress_index);
90187117650Saalok 				optind--;
90287117650Saalok 				break;
90387117650Saalok 			} else if (((argc - optind) == 1) &&
90487117650Saalok 			    (*argv[optind] != '-')) {
90587117650Saalok 				algname = optarg;
90687117650Saalok 				filename = argv[optind];
90787117650Saalok 				optind++;
90887117650Saalok 			} else if (((argc - optind) > 1) &&
90987117650Saalok 			    (*argv[optind] == '-')) {
91087117650Saalok 				algname = optarg;
91187117650Saalok 				check_algorithm_validity(algname,
91287117650Saalok 				    &compress_index);
91387117650Saalok 				break;
91487117650Saalok 			} else {
91587117650Saalok 				filename = optarg;
91687117650Saalok 			}
91787117650Saalok 
91887117650Saalok 			check_file_validity(filename);
91987117650Saalok 			check_algorithm_validity(algname, &compress_index);
92087117650Saalok 			break;
9217c478bd9Sstevel@tonic-gate 		case 'd':
9227c478bd9Sstevel@tonic-gate 			deleteflag = 1;
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 			minor = name_to_minor(optarg);
9257c478bd9Sstevel@tonic-gate 			if (minor != 0)
9267c478bd9Sstevel@tonic-gate 				devicename = optarg;
9277c478bd9Sstevel@tonic-gate 			else
9287c478bd9Sstevel@tonic-gate 				filename = optarg;
9297c478bd9Sstevel@tonic-gate 			break;
9303d7072f8Seschrock 		case 'f':
9313d7072f8Seschrock 			force = B_TRUE;
9323d7072f8Seschrock 			break;
93387117650Saalok 		case 's':
93487117650Saalok 			segsize = convert_to_num(optarg);
93587117650Saalok 
93687117650Saalok 			if (segsize == 0 || segsize % DEV_BSIZE)
93787117650Saalok 				die(gettext("segment size %s is invalid "
93887117650Saalok 				    "or not a multiple of minimum block "
93987117650Saalok 				    "size %ld\n"), optarg, DEV_BSIZE);
94087117650Saalok 
94187117650Saalok 			filename = argv[optind];
94287117650Saalok 			check_file_validity(filename);
94387117650Saalok 			optind++;
94487117650Saalok 			break;
94587117650Saalok 		case 'U':
94687117650Saalok 			uncompressflag = 1;
94787117650Saalok 			filename = optarg;
94887117650Saalok 			check_file_validity(filename);
94987117650Saalok 			break;
9507c478bd9Sstevel@tonic-gate 		case '?':
9517c478bd9Sstevel@tonic-gate 		default:
9527c478bd9Sstevel@tonic-gate 			errflag = 1;
9537c478bd9Sstevel@tonic-gate 			break;
9547c478bd9Sstevel@tonic-gate 		}
9557c478bd9Sstevel@tonic-gate 	}
95687117650Saalok 	if (errflag ||
95787117650Saalok 	    (addflag && deleteflag) ||
95887117650Saalok 	    ((compressflag || uncompressflag) && (addflag || deleteflag)))
9597c478bd9Sstevel@tonic-gate 		usage();
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 	switch (argc - optind) {
9627c478bd9Sstevel@tonic-gate 	case 0: /* no more args */
9637c478bd9Sstevel@tonic-gate 		break;
9647c478bd9Sstevel@tonic-gate 	case 1: /* one arg without options means print the association */
9657c478bd9Sstevel@tonic-gate 		if (addflag || deleteflag)
9667c478bd9Sstevel@tonic-gate 			usage();
96787117650Saalok 		if (compressflag || uncompressflag)
96887117650Saalok 			usage();
9697c478bd9Sstevel@tonic-gate 		minor = name_to_minor(argv[optind]);
9707c478bd9Sstevel@tonic-gate 		if (minor != 0)
9717c478bd9Sstevel@tonic-gate 			devicename = argv[optind];
9727c478bd9Sstevel@tonic-gate 		else
9737c478bd9Sstevel@tonic-gate 			filename = argv[optind];
9747c478bd9Sstevel@tonic-gate 		break;
9757c478bd9Sstevel@tonic-gate 	default:
9767c478bd9Sstevel@tonic-gate 		usage();
9777c478bd9Sstevel@tonic-gate 		break;
9787c478bd9Sstevel@tonic-gate 	}
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate 	if (filename && !valid_abspath(filename))
9817c478bd9Sstevel@tonic-gate 		exit(E_ERROR);
9827c478bd9Sstevel@tonic-gate 
9837c478bd9Sstevel@tonic-gate 	/*
9847c478bd9Sstevel@tonic-gate 	 * Here, we know the arguments are correct, the filename is an
9857c478bd9Sstevel@tonic-gate 	 * absolute path, it exists and is a regular file. We don't yet
9867c478bd9Sstevel@tonic-gate 	 * know that the device name is ok or not.
9877c478bd9Sstevel@tonic-gate 	 */
9887c478bd9Sstevel@tonic-gate 	/*
9897c478bd9Sstevel@tonic-gate 	 * Now to the real work.
9907c478bd9Sstevel@tonic-gate 	 */
9917c478bd9Sstevel@tonic-gate 	openflag = O_EXCL;
99287117650Saalok 	if (addflag || deleteflag || compressflag || uncompressflag)
9937c478bd9Sstevel@tonic-gate 		openflag |= O_RDWR;
9947c478bd9Sstevel@tonic-gate 	else
9957c478bd9Sstevel@tonic-gate 		openflag |= O_RDONLY;
9967c478bd9Sstevel@tonic-gate 	lfd = open(lofictl, openflag);
9977c478bd9Sstevel@tonic-gate 	if (lfd == -1) {
9987c478bd9Sstevel@tonic-gate 		if ((errno == EPERM) || (errno == EACCES)) {
9997c478bd9Sstevel@tonic-gate 			die("you do not have permission to perform "
10007c478bd9Sstevel@tonic-gate 			    "that operation.\n");
10017c478bd9Sstevel@tonic-gate 		} else {
10027c478bd9Sstevel@tonic-gate 			die("%s", lofictl);
10037c478bd9Sstevel@tonic-gate 		}
10047c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
10057c478bd9Sstevel@tonic-gate 	}
10067c478bd9Sstevel@tonic-gate 	if (addflag)
100787117650Saalok 		add_mapping(lfd, devicename, filename, NULL, 0);
100887117650Saalok 	else if (compressflag)
100987117650Saalok 		lofi_compress(lfd, filename, compress_index, segsize);
101087117650Saalok 	else if (uncompressflag)
101187117650Saalok 		lofi_uncompress(lfd, filename);
10127c478bd9Sstevel@tonic-gate 	else if (deleteflag)
10133d7072f8Seschrock 		delete_mapping(lfd, devicename, filename, force);
10147c478bd9Sstevel@tonic-gate 	else if (filename || devicename)
10157c478bd9Sstevel@tonic-gate 		print_one_mapping(lfd, devicename, filename);
10167c478bd9Sstevel@tonic-gate 	else
10177c478bd9Sstevel@tonic-gate 		print_mappings(lfd);
1018*579df0adSaalok 
10197c478bd9Sstevel@tonic-gate 	(void) close(lfd);
1020*579df0adSaalok 	closelib();
10217c478bd9Sstevel@tonic-gate 	return (E_SUCCESS);
10227c478bd9Sstevel@tonic-gate }
1023