xref: /titanic_50/usr/src/cmd/lofiadm/main.c (revision a423e759b1b3e58affccafde9c320c61d7566a21)
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 #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>
52579df0adSaalok #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)
88579df0adSaalok #define	LIBZ			"libz.so"
89579df0adSaalok 
90579df0adSaalok 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 {
95579df0adSaalok 	void *libz_hdl = NULL;
9687117650Saalok 
97579df0adSaalok 	/*
98579df0adSaalok 	 * The first time we are called, attempt to dlopen()
99579df0adSaalok 	 * libz.so and get a pointer to the compress2() function
100579df0adSaalok 	 */
101579df0adSaalok 	if (compress2p == NULL) {
102579df0adSaalok 		if ((libz_hdl = openlib(LIBZ)) == NULL)
103579df0adSaalok 			die(gettext("could not find %s. "
104579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
105579df0adSaalok 
106579df0adSaalok 		if ((compress2p =
107579df0adSaalok 		    (int (*)(void *, ulong_t *, void *, size_t, int))
108579df0adSaalok 		    dlsym(libz_hdl, "compress2")) == NULL) {
109579df0adSaalok 			closelib();
110579df0adSaalok 			die(gettext("could not find the correct %s. "
111579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
112579df0adSaalok 		}
113579df0adSaalok 	}
114579df0adSaalok 
115579df0adSaalok 	if ((*compress2p)(dst, (ulong_t *)dstlen, src, srclen, level) != 0)
116579df0adSaalok 		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;
30793239addSjohnlev 	li.li_cleanup = B_FALSE;
30893239addSjohnlev 
3097c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
3107c478bd9Sstevel@tonic-gate 		/* delete by filename */
31187117650Saalok 		(void) strlcpy(li.li_filename, filename,
31287117650Saalok 		    sizeof (li.li_filename));
3137c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
3147c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) {
3157c478bd9Sstevel@tonic-gate 			die(gettext("could not unmap file %s"), filename);
3167c478bd9Sstevel@tonic-gate 		}
3177c478bd9Sstevel@tonic-gate 		return;
3187c478bd9Sstevel@tonic-gate 	}
3197c478bd9Sstevel@tonic-gate 	/* delete by device */
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 	li.li_minor = name_to_minor(devicename);
3227c478bd9Sstevel@tonic-gate 	if (li.li_minor == 0) {
3237c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
3247c478bd9Sstevel@tonic-gate 	}
3257c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) {
3267c478bd9Sstevel@tonic-gate 		die(gettext("could not unmap device %s"), devicename);
3277c478bd9Sstevel@tonic-gate 	}
3287c478bd9Sstevel@tonic-gate }
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate static void
3317c478bd9Sstevel@tonic-gate print_one_mapping(int lfd, const char *devicename, const char *filename)
3327c478bd9Sstevel@tonic-gate {
3337c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
3367c478bd9Sstevel@tonic-gate 		/* given filename, print devicename */
3377c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
33887117650Saalok 		(void) strlcpy(li.li_filename, filename,
33987117650Saalok 		    sizeof (li.li_filename));
3407c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) {
3417c478bd9Sstevel@tonic-gate 			die(gettext("could not find device for %s"), filename);
3427c478bd9Sstevel@tonic-gate 		}
3437c478bd9Sstevel@tonic-gate 		(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor);
3447c478bd9Sstevel@tonic-gate 		return;
3457c478bd9Sstevel@tonic-gate 	}
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	/* given devicename, print filename */
3487c478bd9Sstevel@tonic-gate 	li.li_minor = name_to_minor(devicename);
3497c478bd9Sstevel@tonic-gate 	if (li.li_minor == 0) {
3507c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
3517c478bd9Sstevel@tonic-gate 	}
3527c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) {
3537c478bd9Sstevel@tonic-gate 		die(gettext("could not find filename for %s"), devicename);
3547c478bd9Sstevel@tonic-gate 	}
3557c478bd9Sstevel@tonic-gate 	(void) printf("%s\n", li.li_filename);
3567c478bd9Sstevel@tonic-gate }
3577c478bd9Sstevel@tonic-gate 
35887117650Saalok /*
35987117650Saalok  * Uncompress a file.
36087117650Saalok  *
36187117650Saalok  * First map the file in to establish a device
36287117650Saalok  * association, then read from it. On-the-fly
36387117650Saalok  * decompression will automatically uncompress
36487117650Saalok  * the file if it's compressed
36587117650Saalok  *
36687117650Saalok  * If the file is mapped and a device association
36787117650Saalok  * has been established, disallow uncompressing
36887117650Saalok  * the file until it is unmapped.
36987117650Saalok  */
37087117650Saalok static void
37187117650Saalok lofi_uncompress(int lfd, const char *filename)
3727c478bd9Sstevel@tonic-gate {
37387117650Saalok 	struct lofi_ioctl li;
37487117650Saalok 	char buf[MAXBSIZE];
37587117650Saalok 	char devicename[32];
37687117650Saalok 	char tmpfilename[MAXPATHLEN];
37787117650Saalok 	char *dir = NULL;
37887117650Saalok 	char *file = NULL;
37987117650Saalok 	int minor = 0;
38087117650Saalok 	struct stat64 statbuf;
38187117650Saalok 	int compfd = -1;
38287117650Saalok 	int uncompfd = -1;
38387117650Saalok 	ssize_t rbytes;
38487117650Saalok 
38587117650Saalok 	/*
38687117650Saalok 	 * Disallow uncompressing the file if it is
38787117650Saalok 	 * already mapped.
38887117650Saalok 	 */
38987117650Saalok 	li.li_minor = 0;
39087117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
39187117650Saalok 	if (ioctl(lfd, LOFI_GET_MINOR, &li) != -1)
39287117650Saalok 		die(gettext("%s must be unmapped before uncompressing"),
39387117650Saalok 		    filename);
39487117650Saalok 
39587117650Saalok 	/* Zero length files don't need to be uncompressed */
39687117650Saalok 	if (stat64(filename, &statbuf) == -1)
39787117650Saalok 		die(gettext("stat: %s"), filename);
39887117650Saalok 	if (statbuf.st_size == 0)
39987117650Saalok 		return;
40087117650Saalok 
40187117650Saalok 	add_mapping(lfd, NULL, filename, &minor, 1);
40287117650Saalok 	(void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d",
40387117650Saalok 	    LOFI_BLOCK_NAME, minor);
40487117650Saalok 
40587117650Saalok 	/* If the file isn't compressed, we just return */
40687117650Saalok 	if ((ioctl(lfd, LOFI_CHECK_COMPRESSED, &li) == -1) ||
407*a423e759Saalok 	    (li.li_algorithm[0] == '\0')) {
40887117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
409*a423e759Saalok 		die("%s is not compressed\n", filename);
41087117650Saalok 	}
41187117650Saalok 
41287117650Saalok 	if ((compfd = open64(devicename, O_RDONLY | O_NONBLOCK)) == -1) {
41387117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
41487117650Saalok 		die(gettext("open: %s"), filename);
41587117650Saalok 	}
41687117650Saalok 	/* Create a temp file in the same directory */
41787117650Saalok 	dir = strdup(filename);
41887117650Saalok 	dir = dirname(dir);
41987117650Saalok 	file = strdup(filename);
42087117650Saalok 	file = basename(file);
42187117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
42287117650Saalok 	    "%s/.%sXXXXXX", dir, file);
42387117650Saalok 
42487117650Saalok 	if ((uncompfd = mkstemp64(tmpfilename)) == -1) {
42587117650Saalok 		(void) close(compfd);
42687117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
42787117650Saalok 		free(dir);
42887117650Saalok 		free(file);
429*a423e759Saalok 		die("%s could not be uncompressed\n", filename);
43087117650Saalok 	}
43187117650Saalok 
43287117650Saalok 	/*
43387117650Saalok 	 * Set the mode bits and the owner of this temporary
43487117650Saalok 	 * file to be that of the original uncompressed file
43587117650Saalok 	 */
43687117650Saalok 	(void) fchmod(uncompfd, statbuf.st_mode);
43787117650Saalok 
43887117650Saalok 	if (fchown(uncompfd, statbuf.st_uid, statbuf.st_gid) == -1) {
43987117650Saalok 		(void) close(compfd);
44087117650Saalok 		(void) close(uncompfd);
44187117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
44287117650Saalok 		free(dir);
44387117650Saalok 		free(file);
444*a423e759Saalok 		die("%s could not be uncompressed\n", filename);
44587117650Saalok 	}
44687117650Saalok 
44787117650Saalok 	/* Now read from the device in MAXBSIZE-sized chunks */
44887117650Saalok 	for (;;) {
44987117650Saalok 		rbytes = read(compfd, buf, sizeof (buf));
45087117650Saalok 
45187117650Saalok 		if (rbytes <= 0)
45287117650Saalok 			break;
45387117650Saalok 
45487117650Saalok 		if (write(uncompfd, buf, rbytes) != rbytes) {
45587117650Saalok 			rbytes = -1;
45687117650Saalok 			break;
45787117650Saalok 		}
45887117650Saalok 	}
45987117650Saalok 
46087117650Saalok 	(void) close(compfd);
46187117650Saalok 	(void) close(uncompfd);
46287117650Saalok 	free(dir);
46387117650Saalok 	free(file);
46487117650Saalok 
46587117650Saalok 	/* Delete the mapping */
46687117650Saalok 	delete_mapping(lfd, devicename, filename, B_TRUE);
46787117650Saalok 
46887117650Saalok 	/*
46987117650Saalok 	 * If an error occured while reading or writing, rbytes will
47087117650Saalok 	 * be negative
47187117650Saalok 	 */
47287117650Saalok 	if (rbytes < 0) {
47387117650Saalok 		(void) unlink(tmpfilename);
47487117650Saalok 		die(gettext("could not read from %s"), filename);
47587117650Saalok 	}
47687117650Saalok 
47787117650Saalok 	/* Rename the temp file to the actual file */
47887117650Saalok 	if (rename(tmpfilename, filename) == -1)
47987117650Saalok 		(void) unlink(tmpfilename);
48087117650Saalok }
48187117650Saalok 
48287117650Saalok /*
48387117650Saalok  * Compress a file
48487117650Saalok  */
48587117650Saalok static void
48687117650Saalok lofi_compress(int lfd, const char *filename, int compress_index,
48787117650Saalok     uint32_t segsize)
48887117650Saalok {
48987117650Saalok 	struct lofi_ioctl lic;
49087117650Saalok 	lofi_compress_info_t *li;
49187117650Saalok 	char tmpfilename[MAXPATHLEN];
49287117650Saalok 	char comp_filename[MAXPATHLEN];
49387117650Saalok 	char algorithm[MAXALGLEN];
49487117650Saalok 	char *dir = NULL, *file = NULL;
49587117650Saalok 	uchar_t *uncompressed_seg = NULL;
49687117650Saalok 	uchar_t *compressed_seg = NULL;
49787117650Saalok 	uint32_t compressed_segsize;
49887117650Saalok 	uint32_t len_compressed, count;
49987117650Saalok 	uint32_t index_entries, index_sz;
50087117650Saalok 	uint64_t *index = NULL;
50187117650Saalok 	uint64_t offset;
50287117650Saalok 	size_t real_segsize;
50387117650Saalok 	struct stat64 statbuf;
50487117650Saalok 	int compfd = -1, uncompfd = -1;
50587117650Saalok 	int tfd = -1;
50687117650Saalok 	ssize_t rbytes, wbytes, lastread;
50787117650Saalok 	int i, type;
50887117650Saalok 
50987117650Saalok 	/*
51087117650Saalok 	 * Disallow compressing the file if it is
51187117650Saalok 	 * already mapped
51287117650Saalok 	 */
51387117650Saalok 	lic.li_minor = 0;
51487117650Saalok 	(void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename));
51587117650Saalok 	if (ioctl(lfd, LOFI_GET_MINOR, &lic) != -1)
51687117650Saalok 		die(gettext("%s must be unmapped before compressing"),
51787117650Saalok 		    filename);
51887117650Saalok 
51987117650Saalok 	li = &lofi_compress_table[compress_index];
52087117650Saalok 
52187117650Saalok 	/*
52287117650Saalok 	 * The size of the buffer to hold compressed data must
52387117650Saalok 	 * be slightly larger than the compressed segment size.
52487117650Saalok 	 *
52587117650Saalok 	 * The compress functions use part of the buffer as
52687117650Saalok 	 * scratch space to do calculations.
52787117650Saalok 	 * Ref: http://www.zlib.net/manual.html#compress2
52887117650Saalok 	 */
52987117650Saalok 	compressed_segsize = segsize + (segsize >> 6);
53087117650Saalok 	compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR);
53187117650Saalok 	uncompressed_seg = (uchar_t *)malloc(segsize);
53287117650Saalok 
53387117650Saalok 	if (compressed_seg == NULL || uncompressed_seg == NULL)
53487117650Saalok 		die(gettext("No memory"));
53587117650Saalok 
53687117650Saalok 	if ((uncompfd = open64(filename, O_RDONLY|O_LARGEFILE, 0)) == -1)
53787117650Saalok 		die(gettext("open: %s"), filename);
53887117650Saalok 
53987117650Saalok 	if (fstat64(uncompfd, &statbuf) == -1) {
54087117650Saalok 		(void) close(uncompfd);
54187117650Saalok 		die(gettext("fstat: %s"), filename);
54287117650Saalok 	}
54387117650Saalok 
54487117650Saalok 	/* Zero length files don't need to be compressed */
54587117650Saalok 	if (statbuf.st_size == 0) {
54687117650Saalok 		(void) close(uncompfd);
54787117650Saalok 		return;
54887117650Saalok 	}
54987117650Saalok 
55087117650Saalok 	/*
55187117650Saalok 	 * Create temporary files in the same directory that
55287117650Saalok 	 * will hold the intermediate data
55387117650Saalok 	 */
55487117650Saalok 	dir = strdup(filename);
55587117650Saalok 	dir = dirname(dir);
55687117650Saalok 	file = strdup(filename);
55787117650Saalok 	file = basename(file);
55887117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
55987117650Saalok 	    "%s/.%sXXXXXX", dir, file);
56087117650Saalok 	(void) snprintf(comp_filename, sizeof (comp_filename),
56187117650Saalok 	    "%s/.%sXXXXXX", dir, file);
56287117650Saalok 
56387117650Saalok 	if ((tfd = mkstemp64(tmpfilename)) == -1)
56487117650Saalok 		goto cleanup;
56587117650Saalok 
56687117650Saalok 	if ((compfd = mkstemp64(comp_filename)) == -1)
56787117650Saalok 		goto cleanup;
56887117650Saalok 
56987117650Saalok 	/*
57087117650Saalok 	 * Set the mode bits and owner of the compressed
57187117650Saalok 	 * file to be that of the original uncompressed file
57287117650Saalok 	 */
57387117650Saalok 	(void) fchmod(compfd, statbuf.st_mode);
57487117650Saalok 
57587117650Saalok 	if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1)
57687117650Saalok 		goto cleanup;
57787117650Saalok 
57887117650Saalok 	/*
57987117650Saalok 	 * Calculate the number of index entries required.
58087117650Saalok 	 * index entries are stored as an array. adding
58187117650Saalok 	 * a '2' here accounts for the fact that the last
58287117650Saalok 	 * segment may not be a multiple of the segment size
58387117650Saalok 	 */
58487117650Saalok 	index_sz = (statbuf.st_size / segsize) + 2;
58587117650Saalok 	index = malloc(sizeof (*index) * index_sz);
58687117650Saalok 
58787117650Saalok 	if (index == NULL)
58887117650Saalok 		goto cleanup;
58987117650Saalok 
59087117650Saalok 	offset = 0;
59187117650Saalok 	lastread = segsize;
59287117650Saalok 	count = 0;
59387117650Saalok 
59487117650Saalok 	/*
59587117650Saalok 	 * Now read from the uncompressed file in 'segsize'
59687117650Saalok 	 * sized chunks, compress what was read in and
59787117650Saalok 	 * write it out to a temporary file
59887117650Saalok 	 */
59987117650Saalok 	for (;;) {
60087117650Saalok 		rbytes = read(uncompfd, uncompressed_seg, segsize);
60187117650Saalok 
60287117650Saalok 		if (rbytes <= 0)
60387117650Saalok 			break;
60487117650Saalok 
60587117650Saalok 		if (lastread < segsize)
60687117650Saalok 			goto cleanup;
60787117650Saalok 
60887117650Saalok 		/*
60987117650Saalok 		 * Account for the first byte that
61087117650Saalok 		 * indicates whether a segment is
61187117650Saalok 		 * compressed or not
61287117650Saalok 		 */
61387117650Saalok 		real_segsize = segsize - 1;
61487117650Saalok 		(void) li->l_compress(uncompressed_seg, rbytes,
61587117650Saalok 		    compressed_seg + SEGHDR, &real_segsize, li->l_level);
61687117650Saalok 
61787117650Saalok 		/*
61887117650Saalok 		 * If the length of the compressed data is more
61987117650Saalok 		 * than a threshold then there isn't any benefit
62087117650Saalok 		 * to be had from compressing this segment - leave
62187117650Saalok 		 * it uncompressed.
62287117650Saalok 		 *
62387117650Saalok 		 * NB. In case an error occurs during compression (above)
62487117650Saalok 		 * the 'real_segsize' isn't changed. The logic below
62587117650Saalok 		 * ensures that that segment is left uncompressed.
62687117650Saalok 		 */
62787117650Saalok 		len_compressed = real_segsize;
62887117650Saalok 		if (real_segsize > segsize - COMPRESS_THRESHOLD) {
62987117650Saalok 			(void) memcpy(compressed_seg + SEGHDR, uncompressed_seg,
63087117650Saalok 			    rbytes);
63187117650Saalok 			type = UNCOMPRESSED;
63287117650Saalok 			len_compressed = rbytes;
63387117650Saalok 		} else {
63487117650Saalok 			type = COMPRESSED;
63587117650Saalok 		}
63687117650Saalok 
63787117650Saalok 		/*
63887117650Saalok 		 * Set the first byte or the SEGHDR to
63987117650Saalok 		 * indicate if it's compressed or not
64087117650Saalok 		 */
64187117650Saalok 		*compressed_seg = type;
64287117650Saalok 		wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR);
64387117650Saalok 		if (wbytes != (len_compressed + SEGHDR)) {
64487117650Saalok 			rbytes = -1;
64587117650Saalok 			break;
64687117650Saalok 		}
64787117650Saalok 
64887117650Saalok 		index[count] = BE_64(offset);
64987117650Saalok 		offset += wbytes;
65087117650Saalok 		lastread = rbytes;
65187117650Saalok 		count++;
65287117650Saalok 	}
65387117650Saalok 
65487117650Saalok 	(void) close(uncompfd);
65587117650Saalok 
65687117650Saalok 	if (rbytes < 0)
65787117650Saalok 		goto cleanup;
65887117650Saalok 	/*
65987117650Saalok 	 * The last index entry is a sentinel entry. It does not point to
66087117650Saalok 	 * an actual compressed segment but helps in computing the size of
66187117650Saalok 	 * the compressed segment. The size of each compressed segment is
66287117650Saalok 	 * computed by subtracting the current index value from the next
66387117650Saalok 	 * one (the compressed blocks are stored sequentially)
66487117650Saalok 	 */
66587117650Saalok 	index[count++] = BE_64(offset);
66687117650Saalok 
66787117650Saalok 	/*
66887117650Saalok 	 * Now write the compressed data along with the
66987117650Saalok 	 * header information to this file which will
67087117650Saalok 	 * later be renamed to the original uncompressed
67187117650Saalok 	 * file name
67287117650Saalok 	 *
67387117650Saalok 	 * The header is as follows -
67487117650Saalok 	 *
67587117650Saalok 	 * Signature (name of the compression algorithm)
67687117650Saalok 	 * Compression segment size (a multiple of 512)
67787117650Saalok 	 * Number of index entries
67887117650Saalok 	 * Size of the last block
67987117650Saalok 	 * The array containing the index entries
68087117650Saalok 	 *
68187117650Saalok 	 * the header is always stored in network byte
68287117650Saalok 	 * order
68387117650Saalok 	 */
68487117650Saalok 	(void) bzero(algorithm, sizeof (algorithm));
68587117650Saalok 	(void) strlcpy(algorithm, li->l_name, sizeof (algorithm));
68687117650Saalok 	if (write(compfd, algorithm, sizeof (algorithm))
68787117650Saalok 	    != sizeof (algorithm))
68887117650Saalok 		goto cleanup;
68987117650Saalok 
69087117650Saalok 	segsize = htonl(segsize);
69187117650Saalok 	if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize))
69287117650Saalok 		goto cleanup;
69387117650Saalok 
69487117650Saalok 	index_entries = htonl(count);
69587117650Saalok 	if (write(compfd, &index_entries, sizeof (index_entries)) !=
69687117650Saalok 	    sizeof (index_entries))
69787117650Saalok 		goto cleanup;
69887117650Saalok 
69987117650Saalok 	lastread = htonl(lastread);
70087117650Saalok 	if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread))
70187117650Saalok 		goto cleanup;
70287117650Saalok 
70387117650Saalok 	for (i = 0; i < count; i++) {
70487117650Saalok 		if (write(compfd, index + i, sizeof (*index)) !=
70587117650Saalok 		    sizeof (*index))
70687117650Saalok 			goto cleanup;
70787117650Saalok 	}
70887117650Saalok 
70987117650Saalok 	/* Header is written, now write the compressed data */
71087117650Saalok 	if (lseek(tfd, 0, SEEK_SET) != 0)
71187117650Saalok 		goto cleanup;
71287117650Saalok 
71387117650Saalok 	rbytes = wbytes = 0;
71487117650Saalok 
71587117650Saalok 	for (;;) {
71687117650Saalok 		rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR);
71787117650Saalok 
71887117650Saalok 		if (rbytes <= 0)
71987117650Saalok 			break;
72087117650Saalok 
72187117650Saalok 		if (write(compfd, compressed_seg, rbytes) != rbytes)
72287117650Saalok 			goto cleanup;
72387117650Saalok 	}
72487117650Saalok 
72587117650Saalok 	if (fstat64(compfd, &statbuf) == -1)
72687117650Saalok 		goto cleanup;
72787117650Saalok 
72887117650Saalok 	/*
72987117650Saalok 	 * Round up the compressed file size to be a multiple of
73087117650Saalok 	 * DEV_BSIZE. lofi(7D) likes it that way.
73187117650Saalok 	 */
73287117650Saalok 	if ((offset = statbuf.st_size % DEV_BSIZE) > 0) {
73387117650Saalok 
73487117650Saalok 		offset = DEV_BSIZE - offset;
73587117650Saalok 
73687117650Saalok 		for (i = 0; i < offset; i++)
73787117650Saalok 			uncompressed_seg[i] = '\0';
73887117650Saalok 		if (write(compfd, uncompressed_seg, offset) != offset)
73987117650Saalok 			goto cleanup;
74087117650Saalok 	}
74187117650Saalok 	(void) close(compfd);
74287117650Saalok 	(void) close(tfd);
74387117650Saalok 	(void) unlink(tmpfilename);
74487117650Saalok cleanup:
74587117650Saalok 	if (rbytes < 0) {
74687117650Saalok 		if (tfd != -1)
74787117650Saalok 			(void) unlink(tmpfilename);
74887117650Saalok 		if (compfd != -1)
74987117650Saalok 			(void) unlink(comp_filename);
75087117650Saalok 		die(gettext("error compressing file %s"), filename);
75187117650Saalok 	} else {
75287117650Saalok 		/* Rename the compressed file to the actual file */
75387117650Saalok 		if (rename(comp_filename, filename) == -1) {
75487117650Saalok 			(void) unlink(comp_filename);
75587117650Saalok 			die(gettext("error compressing file %s"), filename);
75687117650Saalok 		}
75787117650Saalok 	}
75887117650Saalok 	if (compressed_seg != NULL)
75987117650Saalok 		free(compressed_seg);
76087117650Saalok 	if (uncompressed_seg != NULL)
76187117650Saalok 		free(uncompressed_seg);
76287117650Saalok 	if (dir != NULL)
76387117650Saalok 		free(dir);
76487117650Saalok 	if (file != NULL)
76587117650Saalok 		free(file);
76687117650Saalok 	if (index != NULL)
76787117650Saalok 		free(index);
76887117650Saalok 	if (compfd != -1)
76987117650Saalok 		(void) close(compfd);
77087117650Saalok 	if (uncompfd != -1)
77187117650Saalok 		(void) close(uncompfd);
77287117650Saalok 	if (tfd != -1)
77387117650Saalok 		(void) close(tfd);
77487117650Saalok }
77587117650Saalok 
77687117650Saalok static int
77787117650Saalok lofi_compress_select(const char *algname)
77887117650Saalok {
77987117650Saalok 	int i;
78087117650Saalok 
78187117650Saalok 	for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) {
78287117650Saalok 		if (strcmp(lofi_compress_table[i].l_name, algname) == 0)
78387117650Saalok 			return (i);
78487117650Saalok 	}
78587117650Saalok 	return (-1);
78687117650Saalok }
78787117650Saalok 
78887117650Saalok static void
78987117650Saalok check_algorithm_validity(const char *algname, int *compress_index)
79087117650Saalok {
79187117650Saalok 	*compress_index = lofi_compress_select(algname);
79287117650Saalok 	if (*compress_index < 0)
79387117650Saalok 		die(gettext("invalid algorithm name: %s\n"), algname);
79487117650Saalok }
79587117650Saalok 
79687117650Saalok static void
79787117650Saalok check_file_validity(const char *filename)
79887117650Saalok {
7997c478bd9Sstevel@tonic-gate 	struct stat64 buf;
80087117650Saalok 	int 	error;
8017c478bd9Sstevel@tonic-gate 	int	fd = -1;
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 	fd = open64(filename, O_RDONLY);
8047c478bd9Sstevel@tonic-gate 	if (fd == -1) {
8057c478bd9Sstevel@tonic-gate 		die(gettext("open: %s"), filename);
8067c478bd9Sstevel@tonic-gate 	}
8077c478bd9Sstevel@tonic-gate 	error = fstat64(fd, &buf);
8087c478bd9Sstevel@tonic-gate 	if (error == -1) {
8097c478bd9Sstevel@tonic-gate 		die(gettext("fstat: %s"), filename);
8107c478bd9Sstevel@tonic-gate 	} else if (!S_ISLOFIABLE(buf.st_mode)) {
8117c478bd9Sstevel@tonic-gate 		die(gettext("%s is not a regular file, "
8127c478bd9Sstevel@tonic-gate 		    "block, or character device\n"),
8137c478bd9Sstevel@tonic-gate 		    filename);
8147c478bd9Sstevel@tonic-gate 	} else if ((buf.st_size % DEV_BSIZE) != 0) {
8157c478bd9Sstevel@tonic-gate 		die(gettext("size of %s is not a multiple "
8167c478bd9Sstevel@tonic-gate 		    "of %d\n"),
8177c478bd9Sstevel@tonic-gate 		    filename, DEV_BSIZE);
8187c478bd9Sstevel@tonic-gate 	}
8197c478bd9Sstevel@tonic-gate 	(void) close(fd);
82087117650Saalok 
82187117650Saalok 	if (name_to_minor(filename) != 0) {
8227c478bd9Sstevel@tonic-gate 		die(gettext("cannot use " LOFI_DRIVER_NAME
82387117650Saalok 		    " on itself\n"), NULL);
8247c478bd9Sstevel@tonic-gate 	}
82587117650Saalok }
82687117650Saalok 
82787117650Saalok static uint32_t
82887117650Saalok convert_to_num(const char *str)
82987117650Saalok {
83087117650Saalok 	int len;
83187117650Saalok 	uint32_t segsize, mult = 1;
83287117650Saalok 
83387117650Saalok 	len = strlen(str);
83487117650Saalok 	if (len && isalpha(str[len - 1])) {
83587117650Saalok 		switch (str[len - 1]) {
83687117650Saalok 		case 'k':
83787117650Saalok 		case 'K':
83887117650Saalok 			mult = KILOBYTE;
83987117650Saalok 			break;
84087117650Saalok 		case 'b':
84187117650Saalok 		case 'B':
84287117650Saalok 			mult = BLOCK_SIZE;
84387117650Saalok 			break;
84487117650Saalok 		case 'm':
84587117650Saalok 		case 'M':
84687117650Saalok 			mult = MEGABYTE;
84787117650Saalok 			break;
84887117650Saalok 		case 'g':
84987117650Saalok 		case 'G':
85087117650Saalok 			mult = GIGABYTE;
85187117650Saalok 			break;
85287117650Saalok 		default:
85387117650Saalok 			die(gettext("invalid segment size %s\n"), str);
85487117650Saalok 		}
85587117650Saalok 	}
85687117650Saalok 
85787117650Saalok 	segsize = atol(str);
85887117650Saalok 	segsize *= mult;
85987117650Saalok 
86087117650Saalok 	return (segsize);
86187117650Saalok }
86287117650Saalok 
86387117650Saalok int
86487117650Saalok main(int argc, char *argv[])
86587117650Saalok {
86687117650Saalok 	int	lfd;
86787117650Saalok 	int	c;
86887117650Saalok 	const char *devicename = NULL;
86987117650Saalok 	const char *filename = NULL;
87087117650Saalok 	const char *algname = COMPRESS_ALGORITHM;
87187117650Saalok 	int	openflag;
87287117650Saalok 	int	minor;
87387117650Saalok 	int 	compress_index;
87487117650Saalok 	uint32_t segsize = SEGSIZE;
87587117650Saalok 	static char *lofictl = "/dev/" LOFI_CTL_NAME;
87687117650Saalok 	boolean_t force = B_FALSE;
87787117650Saalok 
87887117650Saalok 	pname = getpname(argv[0]);
87987117650Saalok 
88087117650Saalok 	(void) setlocale(LC_ALL, "");
88187117650Saalok 	(void) textdomain(TEXT_DOMAIN);
88287117650Saalok 
88387117650Saalok 	while ((c = getopt(argc, argv, "a:C:d:s:U:f")) != EOF) {
88487117650Saalok 		switch (c) {
88587117650Saalok 		case 'a':
88687117650Saalok 			addflag = 1;
88787117650Saalok 			filename = optarg;
88887117650Saalok 			check_file_validity(filename);
88987117650Saalok 
8907c478bd9Sstevel@tonic-gate 			if (((argc - optind) > 0) && (*argv[optind] != '-')) {
8917c478bd9Sstevel@tonic-gate 				/* optional device */
8927c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
8937c478bd9Sstevel@tonic-gate 				optind++;
8947c478bd9Sstevel@tonic-gate 			}
8957c478bd9Sstevel@tonic-gate 			break;
89687117650Saalok 		case 'C':
89787117650Saalok 			compressflag = 1;
89887117650Saalok 
89987117650Saalok 			if (((argc - optind) > 0) &&
90087117650Saalok 			    (*optarg == '-')) {
90187117650Saalok 				check_algorithm_validity(algname,
90287117650Saalok 				    &compress_index);
90387117650Saalok 				optind--;
90487117650Saalok 				break;
90587117650Saalok 			} else if (((argc - optind) == 1) &&
90687117650Saalok 			    (*argv[optind] != '-')) {
90787117650Saalok 				algname = optarg;
90887117650Saalok 				filename = argv[optind];
90987117650Saalok 				optind++;
91087117650Saalok 			} else if (((argc - optind) > 1) &&
91187117650Saalok 			    (*argv[optind] == '-')) {
91287117650Saalok 				algname = optarg;
91387117650Saalok 				check_algorithm_validity(algname,
91487117650Saalok 				    &compress_index);
91587117650Saalok 				break;
91687117650Saalok 			} else {
91787117650Saalok 				filename = optarg;
91887117650Saalok 			}
91987117650Saalok 
92087117650Saalok 			check_file_validity(filename);
92187117650Saalok 			check_algorithm_validity(algname, &compress_index);
92287117650Saalok 			break;
9237c478bd9Sstevel@tonic-gate 		case 'd':
9247c478bd9Sstevel@tonic-gate 			deleteflag = 1;
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 			minor = name_to_minor(optarg);
9277c478bd9Sstevel@tonic-gate 			if (minor != 0)
9287c478bd9Sstevel@tonic-gate 				devicename = optarg;
9297c478bd9Sstevel@tonic-gate 			else
9307c478bd9Sstevel@tonic-gate 				filename = optarg;
9317c478bd9Sstevel@tonic-gate 			break;
9323d7072f8Seschrock 		case 'f':
9333d7072f8Seschrock 			force = B_TRUE;
9343d7072f8Seschrock 			break;
93587117650Saalok 		case 's':
93687117650Saalok 			segsize = convert_to_num(optarg);
93787117650Saalok 
93887117650Saalok 			if (segsize == 0 || segsize % DEV_BSIZE)
93987117650Saalok 				die(gettext("segment size %s is invalid "
94087117650Saalok 				    "or not a multiple of minimum block "
94187117650Saalok 				    "size %ld\n"), optarg, DEV_BSIZE);
94287117650Saalok 
94387117650Saalok 			filename = argv[optind];
94487117650Saalok 			check_file_validity(filename);
94587117650Saalok 			optind++;
94687117650Saalok 			break;
94787117650Saalok 		case 'U':
94887117650Saalok 			uncompressflag = 1;
94987117650Saalok 			filename = optarg;
95087117650Saalok 			check_file_validity(filename);
95187117650Saalok 			break;
9527c478bd9Sstevel@tonic-gate 		case '?':
9537c478bd9Sstevel@tonic-gate 		default:
9547c478bd9Sstevel@tonic-gate 			errflag = 1;
9557c478bd9Sstevel@tonic-gate 			break;
9567c478bd9Sstevel@tonic-gate 		}
9577c478bd9Sstevel@tonic-gate 	}
95887117650Saalok 	if (errflag ||
95987117650Saalok 	    (addflag && deleteflag) ||
96087117650Saalok 	    ((compressflag || uncompressflag) && (addflag || deleteflag)))
9617c478bd9Sstevel@tonic-gate 		usage();
9627c478bd9Sstevel@tonic-gate 
9637c478bd9Sstevel@tonic-gate 	switch (argc - optind) {
9647c478bd9Sstevel@tonic-gate 	case 0: /* no more args */
9657c478bd9Sstevel@tonic-gate 		break;
9667c478bd9Sstevel@tonic-gate 	case 1: /* one arg without options means print the association */
9677c478bd9Sstevel@tonic-gate 		if (addflag || deleteflag)
9687c478bd9Sstevel@tonic-gate 			usage();
96987117650Saalok 		if (compressflag || uncompressflag)
97087117650Saalok 			usage();
9717c478bd9Sstevel@tonic-gate 		minor = name_to_minor(argv[optind]);
9727c478bd9Sstevel@tonic-gate 		if (minor != 0)
9737c478bd9Sstevel@tonic-gate 			devicename = argv[optind];
9747c478bd9Sstevel@tonic-gate 		else
9757c478bd9Sstevel@tonic-gate 			filename = argv[optind];
9767c478bd9Sstevel@tonic-gate 		break;
9777c478bd9Sstevel@tonic-gate 	default:
9787c478bd9Sstevel@tonic-gate 		usage();
9797c478bd9Sstevel@tonic-gate 		break;
9807c478bd9Sstevel@tonic-gate 	}
9817c478bd9Sstevel@tonic-gate 
9827c478bd9Sstevel@tonic-gate 	if (filename && !valid_abspath(filename))
9837c478bd9Sstevel@tonic-gate 		exit(E_ERROR);
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 	/*
9867c478bd9Sstevel@tonic-gate 	 * Here, we know the arguments are correct, the filename is an
9877c478bd9Sstevel@tonic-gate 	 * absolute path, it exists and is a regular file. We don't yet
9887c478bd9Sstevel@tonic-gate 	 * know that the device name is ok or not.
9897c478bd9Sstevel@tonic-gate 	 */
9907c478bd9Sstevel@tonic-gate 	/*
9917c478bd9Sstevel@tonic-gate 	 * Now to the real work.
9927c478bd9Sstevel@tonic-gate 	 */
9937c478bd9Sstevel@tonic-gate 	openflag = O_EXCL;
99487117650Saalok 	if (addflag || deleteflag || compressflag || uncompressflag)
9957c478bd9Sstevel@tonic-gate 		openflag |= O_RDWR;
9967c478bd9Sstevel@tonic-gate 	else
9977c478bd9Sstevel@tonic-gate 		openflag |= O_RDONLY;
9987c478bd9Sstevel@tonic-gate 	lfd = open(lofictl, openflag);
9997c478bd9Sstevel@tonic-gate 	if (lfd == -1) {
10007c478bd9Sstevel@tonic-gate 		if ((errno == EPERM) || (errno == EACCES)) {
10017c478bd9Sstevel@tonic-gate 			die("you do not have permission to perform "
10027c478bd9Sstevel@tonic-gate 			    "that operation.\n");
10037c478bd9Sstevel@tonic-gate 		} else {
10047c478bd9Sstevel@tonic-gate 			die("%s", lofictl);
10057c478bd9Sstevel@tonic-gate 		}
10067c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
10077c478bd9Sstevel@tonic-gate 	}
10087c478bd9Sstevel@tonic-gate 	if (addflag)
100987117650Saalok 		add_mapping(lfd, devicename, filename, NULL, 0);
101087117650Saalok 	else if (compressflag)
101187117650Saalok 		lofi_compress(lfd, filename, compress_index, segsize);
101287117650Saalok 	else if (uncompressflag)
101387117650Saalok 		lofi_uncompress(lfd, filename);
10147c478bd9Sstevel@tonic-gate 	else if (deleteflag)
10153d7072f8Seschrock 		delete_mapping(lfd, devicename, filename, force);
10167c478bd9Sstevel@tonic-gate 	else if (filename || devicename)
10177c478bd9Sstevel@tonic-gate 		print_one_mapping(lfd, devicename, filename);
10187c478bd9Sstevel@tonic-gate 	else
10197c478bd9Sstevel@tonic-gate 		print_mappings(lfd);
1020579df0adSaalok 
10217c478bd9Sstevel@tonic-gate 	(void) close(lfd);
1022579df0adSaalok 	closelib();
10237c478bd9Sstevel@tonic-gate 	return (E_SUCCESS);
10247c478bd9Sstevel@tonic-gate }
1025