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