xref: /titanic_50/usr/src/cmd/lofiadm/main.c (revision 9525893d097ff95655358e1cee42f66471da30c7)
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) ||
407a423e759Saalok 	    (li.li_algorithm[0] == '\0')) {
40887117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
409a423e759Saalok 		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);
429a423e759Saalok 		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);
444a423e759Saalok 		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
486*9525893dSaalok 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;
491*9525893dSaalok 	struct flock lock;
49287117650Saalok 	char tmpfilename[MAXPATHLEN];
49387117650Saalok 	char comp_filename[MAXPATHLEN];
49487117650Saalok 	char algorithm[MAXALGLEN];
49587117650Saalok 	char *dir = NULL, *file = NULL;
49687117650Saalok 	uchar_t *uncompressed_seg = NULL;
49787117650Saalok 	uchar_t *compressed_seg = NULL;
49887117650Saalok 	uint32_t compressed_segsize;
49987117650Saalok 	uint32_t len_compressed, count;
50087117650Saalok 	uint32_t index_entries, index_sz;
50187117650Saalok 	uint64_t *index = NULL;
50287117650Saalok 	uint64_t offset;
50387117650Saalok 	size_t real_segsize;
50487117650Saalok 	struct stat64 statbuf;
50587117650Saalok 	int compfd = -1, uncompfd = -1;
50687117650Saalok 	int tfd = -1;
50787117650Saalok 	ssize_t rbytes, wbytes, lastread;
50887117650Saalok 	int i, type;
50987117650Saalok 
51087117650Saalok 	/*
51187117650Saalok 	 * Disallow compressing the file if it is
51287117650Saalok 	 * already mapped
51387117650Saalok 	 */
51487117650Saalok 	lic.li_minor = 0;
51587117650Saalok 	(void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename));
516*9525893dSaalok 	if (ioctl(*lfd, LOFI_GET_MINOR, &lic) != -1)
51787117650Saalok 		die(gettext("%s must be unmapped before compressing"),
51887117650Saalok 		    filename);
51987117650Saalok 
520*9525893dSaalok 	/*
521*9525893dSaalok 	 * Close the control device so other operations
522*9525893dSaalok 	 * can use it
523*9525893dSaalok 	 */
524*9525893dSaalok 	(void) close(*lfd);
525*9525893dSaalok 	*lfd = -1;
526*9525893dSaalok 
52787117650Saalok 	li = &lofi_compress_table[compress_index];
52887117650Saalok 
52987117650Saalok 	/*
53087117650Saalok 	 * The size of the buffer to hold compressed data must
53187117650Saalok 	 * be slightly larger than the compressed segment size.
53287117650Saalok 	 *
53387117650Saalok 	 * The compress functions use part of the buffer as
53487117650Saalok 	 * scratch space to do calculations.
53587117650Saalok 	 * Ref: http://www.zlib.net/manual.html#compress2
53687117650Saalok 	 */
53787117650Saalok 	compressed_segsize = segsize + (segsize >> 6);
53887117650Saalok 	compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR);
53987117650Saalok 	uncompressed_seg = (uchar_t *)malloc(segsize);
54087117650Saalok 
54187117650Saalok 	if (compressed_seg == NULL || uncompressed_seg == NULL)
54287117650Saalok 		die(gettext("No memory"));
54387117650Saalok 
544*9525893dSaalok 	if ((uncompfd = open64(filename, O_RDWR|O_LARGEFILE, 0)) == -1)
54587117650Saalok 		die(gettext("open: %s"), filename);
54687117650Saalok 
547*9525893dSaalok 	lock.l_type = F_WRLCK;
548*9525893dSaalok 	lock.l_whence = SEEK_SET;
549*9525893dSaalok 	lock.l_start = 0;
550*9525893dSaalok 	lock.l_len = 0;
551*9525893dSaalok 
552*9525893dSaalok 	/*
553*9525893dSaalok 	 * Use an advisory lock to ensure that only a
554*9525893dSaalok 	 * single lofiadm process compresses a given
555*9525893dSaalok 	 * file at any given time
556*9525893dSaalok 	 *
557*9525893dSaalok 	 * A close on the file descriptor automatically
558*9525893dSaalok 	 * closes all lock state on the file
559*9525893dSaalok 	 */
560*9525893dSaalok 	if (fcntl(uncompfd, F_SETLKW, &lock) == -1)
561*9525893dSaalok 		die(gettext("fcntl: %s"), filename);
562*9525893dSaalok 
56387117650Saalok 	if (fstat64(uncompfd, &statbuf) == -1) {
56487117650Saalok 		(void) close(uncompfd);
56587117650Saalok 		die(gettext("fstat: %s"), filename);
56687117650Saalok 	}
56787117650Saalok 
56887117650Saalok 	/* Zero length files don't need to be compressed */
56987117650Saalok 	if (statbuf.st_size == 0) {
57087117650Saalok 		(void) close(uncompfd);
57187117650Saalok 		return;
57287117650Saalok 	}
57387117650Saalok 
57487117650Saalok 	/*
57587117650Saalok 	 * Create temporary files in the same directory that
57687117650Saalok 	 * will hold the intermediate data
57787117650Saalok 	 */
57887117650Saalok 	dir = strdup(filename);
57987117650Saalok 	dir = dirname(dir);
58087117650Saalok 	file = strdup(filename);
58187117650Saalok 	file = basename(file);
58287117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
58387117650Saalok 	    "%s/.%sXXXXXX", dir, file);
58487117650Saalok 	(void) snprintf(comp_filename, sizeof (comp_filename),
58587117650Saalok 	    "%s/.%sXXXXXX", dir, file);
58687117650Saalok 
58787117650Saalok 	if ((tfd = mkstemp64(tmpfilename)) == -1)
58887117650Saalok 		goto cleanup;
58987117650Saalok 
59087117650Saalok 	if ((compfd = mkstemp64(comp_filename)) == -1)
59187117650Saalok 		goto cleanup;
59287117650Saalok 
59387117650Saalok 	/*
59487117650Saalok 	 * Set the mode bits and owner of the compressed
59587117650Saalok 	 * file to be that of the original uncompressed file
59687117650Saalok 	 */
59787117650Saalok 	(void) fchmod(compfd, statbuf.st_mode);
59887117650Saalok 
59987117650Saalok 	if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1)
60087117650Saalok 		goto cleanup;
60187117650Saalok 
60287117650Saalok 	/*
60387117650Saalok 	 * Calculate the number of index entries required.
60487117650Saalok 	 * index entries are stored as an array. adding
60587117650Saalok 	 * a '2' here accounts for the fact that the last
60687117650Saalok 	 * segment may not be a multiple of the segment size
60787117650Saalok 	 */
60887117650Saalok 	index_sz = (statbuf.st_size / segsize) + 2;
60987117650Saalok 	index = malloc(sizeof (*index) * index_sz);
61087117650Saalok 
61187117650Saalok 	if (index == NULL)
61287117650Saalok 		goto cleanup;
61387117650Saalok 
61487117650Saalok 	offset = 0;
61587117650Saalok 	lastread = segsize;
61687117650Saalok 	count = 0;
61787117650Saalok 
61887117650Saalok 	/*
61987117650Saalok 	 * Now read from the uncompressed file in 'segsize'
62087117650Saalok 	 * sized chunks, compress what was read in and
62187117650Saalok 	 * write it out to a temporary file
62287117650Saalok 	 */
62387117650Saalok 	for (;;) {
62487117650Saalok 		rbytes = read(uncompfd, uncompressed_seg, segsize);
62587117650Saalok 
62687117650Saalok 		if (rbytes <= 0)
62787117650Saalok 			break;
62887117650Saalok 
62987117650Saalok 		if (lastread < segsize)
63087117650Saalok 			goto cleanup;
63187117650Saalok 
63287117650Saalok 		/*
63387117650Saalok 		 * Account for the first byte that
63487117650Saalok 		 * indicates whether a segment is
63587117650Saalok 		 * compressed or not
63687117650Saalok 		 */
63787117650Saalok 		real_segsize = segsize - 1;
63887117650Saalok 		(void) li->l_compress(uncompressed_seg, rbytes,
63987117650Saalok 		    compressed_seg + SEGHDR, &real_segsize, li->l_level);
64087117650Saalok 
64187117650Saalok 		/*
64287117650Saalok 		 * If the length of the compressed data is more
64387117650Saalok 		 * than a threshold then there isn't any benefit
64487117650Saalok 		 * to be had from compressing this segment - leave
64587117650Saalok 		 * it uncompressed.
64687117650Saalok 		 *
64787117650Saalok 		 * NB. In case an error occurs during compression (above)
64887117650Saalok 		 * the 'real_segsize' isn't changed. The logic below
64987117650Saalok 		 * ensures that that segment is left uncompressed.
65087117650Saalok 		 */
65187117650Saalok 		len_compressed = real_segsize;
65287117650Saalok 		if (real_segsize > segsize - COMPRESS_THRESHOLD) {
65387117650Saalok 			(void) memcpy(compressed_seg + SEGHDR, uncompressed_seg,
65487117650Saalok 			    rbytes);
65587117650Saalok 			type = UNCOMPRESSED;
65687117650Saalok 			len_compressed = rbytes;
65787117650Saalok 		} else {
65887117650Saalok 			type = COMPRESSED;
65987117650Saalok 		}
66087117650Saalok 
66187117650Saalok 		/*
66287117650Saalok 		 * Set the first byte or the SEGHDR to
66387117650Saalok 		 * indicate if it's compressed or not
66487117650Saalok 		 */
66587117650Saalok 		*compressed_seg = type;
66687117650Saalok 		wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR);
66787117650Saalok 		if (wbytes != (len_compressed + SEGHDR)) {
66887117650Saalok 			rbytes = -1;
66987117650Saalok 			break;
67087117650Saalok 		}
67187117650Saalok 
67287117650Saalok 		index[count] = BE_64(offset);
67387117650Saalok 		offset += wbytes;
67487117650Saalok 		lastread = rbytes;
67587117650Saalok 		count++;
67687117650Saalok 	}
67787117650Saalok 
67887117650Saalok 	(void) close(uncompfd);
67987117650Saalok 
68087117650Saalok 	if (rbytes < 0)
68187117650Saalok 		goto cleanup;
68287117650Saalok 	/*
68387117650Saalok 	 * The last index entry is a sentinel entry. It does not point to
68487117650Saalok 	 * an actual compressed segment but helps in computing the size of
68587117650Saalok 	 * the compressed segment. The size of each compressed segment is
68687117650Saalok 	 * computed by subtracting the current index value from the next
68787117650Saalok 	 * one (the compressed blocks are stored sequentially)
68887117650Saalok 	 */
68987117650Saalok 	index[count++] = BE_64(offset);
69087117650Saalok 
69187117650Saalok 	/*
69287117650Saalok 	 * Now write the compressed data along with the
69387117650Saalok 	 * header information to this file which will
69487117650Saalok 	 * later be renamed to the original uncompressed
69587117650Saalok 	 * file name
69687117650Saalok 	 *
69787117650Saalok 	 * The header is as follows -
69887117650Saalok 	 *
69987117650Saalok 	 * Signature (name of the compression algorithm)
70087117650Saalok 	 * Compression segment size (a multiple of 512)
70187117650Saalok 	 * Number of index entries
70287117650Saalok 	 * Size of the last block
70387117650Saalok 	 * The array containing the index entries
70487117650Saalok 	 *
70587117650Saalok 	 * the header is always stored in network byte
70687117650Saalok 	 * order
70787117650Saalok 	 */
70887117650Saalok 	(void) bzero(algorithm, sizeof (algorithm));
70987117650Saalok 	(void) strlcpy(algorithm, li->l_name, sizeof (algorithm));
71087117650Saalok 	if (write(compfd, algorithm, sizeof (algorithm))
71187117650Saalok 	    != sizeof (algorithm))
71287117650Saalok 		goto cleanup;
71387117650Saalok 
71487117650Saalok 	segsize = htonl(segsize);
71587117650Saalok 	if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize))
71687117650Saalok 		goto cleanup;
71787117650Saalok 
71887117650Saalok 	index_entries = htonl(count);
71987117650Saalok 	if (write(compfd, &index_entries, sizeof (index_entries)) !=
72087117650Saalok 	    sizeof (index_entries))
72187117650Saalok 		goto cleanup;
72287117650Saalok 
72387117650Saalok 	lastread = htonl(lastread);
72487117650Saalok 	if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread))
72587117650Saalok 		goto cleanup;
72687117650Saalok 
72787117650Saalok 	for (i = 0; i < count; i++) {
72887117650Saalok 		if (write(compfd, index + i, sizeof (*index)) !=
72987117650Saalok 		    sizeof (*index))
73087117650Saalok 			goto cleanup;
73187117650Saalok 	}
73287117650Saalok 
73387117650Saalok 	/* Header is written, now write the compressed data */
73487117650Saalok 	if (lseek(tfd, 0, SEEK_SET) != 0)
73587117650Saalok 		goto cleanup;
73687117650Saalok 
73787117650Saalok 	rbytes = wbytes = 0;
73887117650Saalok 
73987117650Saalok 	for (;;) {
74087117650Saalok 		rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR);
74187117650Saalok 
74287117650Saalok 		if (rbytes <= 0)
74387117650Saalok 			break;
74487117650Saalok 
74587117650Saalok 		if (write(compfd, compressed_seg, rbytes) != rbytes)
74687117650Saalok 			goto cleanup;
74787117650Saalok 	}
74887117650Saalok 
74987117650Saalok 	if (fstat64(compfd, &statbuf) == -1)
75087117650Saalok 		goto cleanup;
75187117650Saalok 
75287117650Saalok 	/*
75387117650Saalok 	 * Round up the compressed file size to be a multiple of
75487117650Saalok 	 * DEV_BSIZE. lofi(7D) likes it that way.
75587117650Saalok 	 */
75687117650Saalok 	if ((offset = statbuf.st_size % DEV_BSIZE) > 0) {
75787117650Saalok 
75887117650Saalok 		offset = DEV_BSIZE - offset;
75987117650Saalok 
76087117650Saalok 		for (i = 0; i < offset; i++)
76187117650Saalok 			uncompressed_seg[i] = '\0';
76287117650Saalok 		if (write(compfd, uncompressed_seg, offset) != offset)
76387117650Saalok 			goto cleanup;
76487117650Saalok 	}
76587117650Saalok 	(void) close(compfd);
76687117650Saalok 	(void) close(tfd);
76787117650Saalok 	(void) unlink(tmpfilename);
76887117650Saalok cleanup:
76987117650Saalok 	if (rbytes < 0) {
77087117650Saalok 		if (tfd != -1)
77187117650Saalok 			(void) unlink(tmpfilename);
77287117650Saalok 		if (compfd != -1)
77387117650Saalok 			(void) unlink(comp_filename);
77487117650Saalok 		die(gettext("error compressing file %s"), filename);
77587117650Saalok 	} else {
77687117650Saalok 		/* Rename the compressed file to the actual file */
77787117650Saalok 		if (rename(comp_filename, filename) == -1) {
77887117650Saalok 			(void) unlink(comp_filename);
77987117650Saalok 			die(gettext("error compressing file %s"), filename);
78087117650Saalok 		}
78187117650Saalok 	}
78287117650Saalok 	if (compressed_seg != NULL)
78387117650Saalok 		free(compressed_seg);
78487117650Saalok 	if (uncompressed_seg != NULL)
78587117650Saalok 		free(uncompressed_seg);
78687117650Saalok 	if (dir != NULL)
78787117650Saalok 		free(dir);
78887117650Saalok 	if (file != NULL)
78987117650Saalok 		free(file);
79087117650Saalok 	if (index != NULL)
79187117650Saalok 		free(index);
79287117650Saalok 	if (compfd != -1)
79387117650Saalok 		(void) close(compfd);
79487117650Saalok 	if (uncompfd != -1)
79587117650Saalok 		(void) close(uncompfd);
79687117650Saalok 	if (tfd != -1)
79787117650Saalok 		(void) close(tfd);
79887117650Saalok }
79987117650Saalok 
80087117650Saalok static int
80187117650Saalok lofi_compress_select(const char *algname)
80287117650Saalok {
80387117650Saalok 	int i;
80487117650Saalok 
80587117650Saalok 	for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) {
80687117650Saalok 		if (strcmp(lofi_compress_table[i].l_name, algname) == 0)
80787117650Saalok 			return (i);
80887117650Saalok 	}
80987117650Saalok 	return (-1);
81087117650Saalok }
81187117650Saalok 
81287117650Saalok static void
81387117650Saalok check_algorithm_validity(const char *algname, int *compress_index)
81487117650Saalok {
81587117650Saalok 	*compress_index = lofi_compress_select(algname);
81687117650Saalok 	if (*compress_index < 0)
81787117650Saalok 		die(gettext("invalid algorithm name: %s\n"), algname);
81887117650Saalok }
81987117650Saalok 
82087117650Saalok static void
82187117650Saalok check_file_validity(const char *filename)
82287117650Saalok {
8237c478bd9Sstevel@tonic-gate 	struct stat64 buf;
82487117650Saalok 	int 	error;
8257c478bd9Sstevel@tonic-gate 	int	fd = -1;
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 	fd = open64(filename, O_RDONLY);
8287c478bd9Sstevel@tonic-gate 	if (fd == -1) {
8297c478bd9Sstevel@tonic-gate 		die(gettext("open: %s"), filename);
8307c478bd9Sstevel@tonic-gate 	}
8317c478bd9Sstevel@tonic-gate 	error = fstat64(fd, &buf);
8327c478bd9Sstevel@tonic-gate 	if (error == -1) {
8337c478bd9Sstevel@tonic-gate 		die(gettext("fstat: %s"), filename);
8347c478bd9Sstevel@tonic-gate 	} else if (!S_ISLOFIABLE(buf.st_mode)) {
8357c478bd9Sstevel@tonic-gate 		die(gettext("%s is not a regular file, "
8367c478bd9Sstevel@tonic-gate 		    "block, or character device\n"),
8377c478bd9Sstevel@tonic-gate 		    filename);
8387c478bd9Sstevel@tonic-gate 	} else if ((buf.st_size % DEV_BSIZE) != 0) {
8397c478bd9Sstevel@tonic-gate 		die(gettext("size of %s is not a multiple "
8407c478bd9Sstevel@tonic-gate 		    "of %d\n"),
8417c478bd9Sstevel@tonic-gate 		    filename, DEV_BSIZE);
8427c478bd9Sstevel@tonic-gate 	}
8437c478bd9Sstevel@tonic-gate 	(void) close(fd);
84487117650Saalok 
84587117650Saalok 	if (name_to_minor(filename) != 0) {
8467c478bd9Sstevel@tonic-gate 		die(gettext("cannot use " LOFI_DRIVER_NAME
84787117650Saalok 		    " on itself\n"), NULL);
8487c478bd9Sstevel@tonic-gate 	}
84987117650Saalok }
85087117650Saalok 
85187117650Saalok static uint32_t
85287117650Saalok convert_to_num(const char *str)
85387117650Saalok {
85487117650Saalok 	int len;
85587117650Saalok 	uint32_t segsize, mult = 1;
85687117650Saalok 
85787117650Saalok 	len = strlen(str);
85887117650Saalok 	if (len && isalpha(str[len - 1])) {
85987117650Saalok 		switch (str[len - 1]) {
86087117650Saalok 		case 'k':
86187117650Saalok 		case 'K':
86287117650Saalok 			mult = KILOBYTE;
86387117650Saalok 			break;
86487117650Saalok 		case 'b':
86587117650Saalok 		case 'B':
86687117650Saalok 			mult = BLOCK_SIZE;
86787117650Saalok 			break;
86887117650Saalok 		case 'm':
86987117650Saalok 		case 'M':
87087117650Saalok 			mult = MEGABYTE;
87187117650Saalok 			break;
87287117650Saalok 		case 'g':
87387117650Saalok 		case 'G':
87487117650Saalok 			mult = GIGABYTE;
87587117650Saalok 			break;
87687117650Saalok 		default:
87787117650Saalok 			die(gettext("invalid segment size %s\n"), str);
87887117650Saalok 		}
87987117650Saalok 	}
88087117650Saalok 
88187117650Saalok 	segsize = atol(str);
88287117650Saalok 	segsize *= mult;
88387117650Saalok 
88487117650Saalok 	return (segsize);
88587117650Saalok }
88687117650Saalok 
88787117650Saalok int
88887117650Saalok main(int argc, char *argv[])
88987117650Saalok {
89087117650Saalok 	int	lfd;
89187117650Saalok 	int	c;
89287117650Saalok 	const char *devicename = NULL;
89387117650Saalok 	const char *filename = NULL;
89487117650Saalok 	const char *algname = COMPRESS_ALGORITHM;
89587117650Saalok 	int	openflag;
89687117650Saalok 	int	minor;
89787117650Saalok 	int 	compress_index;
89887117650Saalok 	uint32_t segsize = SEGSIZE;
89987117650Saalok 	static char *lofictl = "/dev/" LOFI_CTL_NAME;
90087117650Saalok 	boolean_t force = B_FALSE;
90187117650Saalok 
90287117650Saalok 	pname = getpname(argv[0]);
90387117650Saalok 
90487117650Saalok 	(void) setlocale(LC_ALL, "");
90587117650Saalok 	(void) textdomain(TEXT_DOMAIN);
90687117650Saalok 
90787117650Saalok 	while ((c = getopt(argc, argv, "a:C:d:s:U:f")) != EOF) {
90887117650Saalok 		switch (c) {
90987117650Saalok 		case 'a':
91087117650Saalok 			addflag = 1;
91187117650Saalok 			filename = optarg;
91287117650Saalok 			check_file_validity(filename);
91387117650Saalok 
9147c478bd9Sstevel@tonic-gate 			if (((argc - optind) > 0) && (*argv[optind] != '-')) {
9157c478bd9Sstevel@tonic-gate 				/* optional device */
9167c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
9177c478bd9Sstevel@tonic-gate 				optind++;
9187c478bd9Sstevel@tonic-gate 			}
9197c478bd9Sstevel@tonic-gate 			break;
92087117650Saalok 		case 'C':
92187117650Saalok 			compressflag = 1;
92287117650Saalok 
92387117650Saalok 			if (((argc - optind) > 0) &&
92487117650Saalok 			    (*optarg == '-')) {
92587117650Saalok 				check_algorithm_validity(algname,
92687117650Saalok 				    &compress_index);
92787117650Saalok 				optind--;
92887117650Saalok 				break;
92987117650Saalok 			} else if (((argc - optind) == 1) &&
93087117650Saalok 			    (*argv[optind] != '-')) {
93187117650Saalok 				algname = optarg;
93287117650Saalok 				filename = argv[optind];
93387117650Saalok 				optind++;
93487117650Saalok 			} else if (((argc - optind) > 1) &&
93587117650Saalok 			    (*argv[optind] == '-')) {
93687117650Saalok 				algname = optarg;
93787117650Saalok 				check_algorithm_validity(algname,
93887117650Saalok 				    &compress_index);
93987117650Saalok 				break;
94087117650Saalok 			} else {
94187117650Saalok 				filename = optarg;
94287117650Saalok 			}
94387117650Saalok 
94487117650Saalok 			check_file_validity(filename);
94587117650Saalok 			check_algorithm_validity(algname, &compress_index);
94687117650Saalok 			break;
9477c478bd9Sstevel@tonic-gate 		case 'd':
9487c478bd9Sstevel@tonic-gate 			deleteflag = 1;
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 			minor = name_to_minor(optarg);
9517c478bd9Sstevel@tonic-gate 			if (minor != 0)
9527c478bd9Sstevel@tonic-gate 				devicename = optarg;
9537c478bd9Sstevel@tonic-gate 			else
9547c478bd9Sstevel@tonic-gate 				filename = optarg;
9557c478bd9Sstevel@tonic-gate 			break;
9563d7072f8Seschrock 		case 'f':
9573d7072f8Seschrock 			force = B_TRUE;
9583d7072f8Seschrock 			break;
95987117650Saalok 		case 's':
96087117650Saalok 			segsize = convert_to_num(optarg);
96187117650Saalok 
96287117650Saalok 			if (segsize == 0 || segsize % DEV_BSIZE)
96387117650Saalok 				die(gettext("segment size %s is invalid "
96487117650Saalok 				    "or not a multiple of minimum block "
96587117650Saalok 				    "size %ld\n"), optarg, DEV_BSIZE);
96687117650Saalok 
96787117650Saalok 			filename = argv[optind];
96887117650Saalok 			check_file_validity(filename);
96987117650Saalok 			optind++;
97087117650Saalok 			break;
97187117650Saalok 		case 'U':
97287117650Saalok 			uncompressflag = 1;
97387117650Saalok 			filename = optarg;
97487117650Saalok 			check_file_validity(filename);
97587117650Saalok 			break;
9767c478bd9Sstevel@tonic-gate 		case '?':
9777c478bd9Sstevel@tonic-gate 		default:
9787c478bd9Sstevel@tonic-gate 			errflag = 1;
9797c478bd9Sstevel@tonic-gate 			break;
9807c478bd9Sstevel@tonic-gate 		}
9817c478bd9Sstevel@tonic-gate 	}
98287117650Saalok 	if (errflag ||
98387117650Saalok 	    (addflag && deleteflag) ||
98487117650Saalok 	    ((compressflag || uncompressflag) && (addflag || deleteflag)))
9857c478bd9Sstevel@tonic-gate 		usage();
9867c478bd9Sstevel@tonic-gate 
9877c478bd9Sstevel@tonic-gate 	switch (argc - optind) {
9887c478bd9Sstevel@tonic-gate 	case 0: /* no more args */
9897c478bd9Sstevel@tonic-gate 		break;
9907c478bd9Sstevel@tonic-gate 	case 1: /* one arg without options means print the association */
9917c478bd9Sstevel@tonic-gate 		if (addflag || deleteflag)
9927c478bd9Sstevel@tonic-gate 			usage();
99387117650Saalok 		if (compressflag || uncompressflag)
99487117650Saalok 			usage();
9957c478bd9Sstevel@tonic-gate 		minor = name_to_minor(argv[optind]);
9967c478bd9Sstevel@tonic-gate 		if (minor != 0)
9977c478bd9Sstevel@tonic-gate 			devicename = argv[optind];
9987c478bd9Sstevel@tonic-gate 		else
9997c478bd9Sstevel@tonic-gate 			filename = argv[optind];
10007c478bd9Sstevel@tonic-gate 		break;
10017c478bd9Sstevel@tonic-gate 	default:
10027c478bd9Sstevel@tonic-gate 		usage();
10037c478bd9Sstevel@tonic-gate 		break;
10047c478bd9Sstevel@tonic-gate 	}
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate 	if (filename && !valid_abspath(filename))
10077c478bd9Sstevel@tonic-gate 		exit(E_ERROR);
10087c478bd9Sstevel@tonic-gate 
10097c478bd9Sstevel@tonic-gate 	/*
10107c478bd9Sstevel@tonic-gate 	 * Here, we know the arguments are correct, the filename is an
10117c478bd9Sstevel@tonic-gate 	 * absolute path, it exists and is a regular file. We don't yet
10127c478bd9Sstevel@tonic-gate 	 * know that the device name is ok or not.
10137c478bd9Sstevel@tonic-gate 	 */
10147c478bd9Sstevel@tonic-gate 	/*
10157c478bd9Sstevel@tonic-gate 	 * Now to the real work.
10167c478bd9Sstevel@tonic-gate 	 */
10177c478bd9Sstevel@tonic-gate 	openflag = O_EXCL;
101887117650Saalok 	if (addflag || deleteflag || compressflag || uncompressflag)
10197c478bd9Sstevel@tonic-gate 		openflag |= O_RDWR;
10207c478bd9Sstevel@tonic-gate 	else
10217c478bd9Sstevel@tonic-gate 		openflag |= O_RDONLY;
10227c478bd9Sstevel@tonic-gate 	lfd = open(lofictl, openflag);
10237c478bd9Sstevel@tonic-gate 	if (lfd == -1) {
10247c478bd9Sstevel@tonic-gate 		if ((errno == EPERM) || (errno == EACCES)) {
10257c478bd9Sstevel@tonic-gate 			die("you do not have permission to perform "
10267c478bd9Sstevel@tonic-gate 			    "that operation.\n");
10277c478bd9Sstevel@tonic-gate 		} else {
10287c478bd9Sstevel@tonic-gate 			die("%s", lofictl);
10297c478bd9Sstevel@tonic-gate 		}
10307c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
10317c478bd9Sstevel@tonic-gate 	}
10327c478bd9Sstevel@tonic-gate 	if (addflag)
103387117650Saalok 		add_mapping(lfd, devicename, filename, NULL, 0);
103487117650Saalok 	else if (compressflag)
1035*9525893dSaalok 		lofi_compress(&lfd, filename, compress_index, segsize);
103687117650Saalok 	else if (uncompressflag)
103787117650Saalok 		lofi_uncompress(lfd, filename);
10387c478bd9Sstevel@tonic-gate 	else if (deleteflag)
10393d7072f8Seschrock 		delete_mapping(lfd, devicename, filename, force);
10407c478bd9Sstevel@tonic-gate 	else if (filename || devicename)
10417c478bd9Sstevel@tonic-gate 		print_one_mapping(lfd, devicename, filename);
10427c478bd9Sstevel@tonic-gate 	else
10437c478bd9Sstevel@tonic-gate 		print_mappings(lfd);
1044579df0adSaalok 
1045*9525893dSaalok 	if (lfd != -1)
10467c478bd9Sstevel@tonic-gate 		(void) close(lfd);
1047579df0adSaalok 	closelib();
10487c478bd9Sstevel@tonic-gate 	return (E_SUCCESS);
10497c478bd9Sstevel@tonic-gate }
1050