xref: /titanic_50/usr/src/cmd/lofiadm/main.c (revision 8711765099ae73ec6eb3cd5d1134031d8f11d4f7)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51d8ed0f0Svikram  * Common Development and Distribution License (the "License").
61d8ed0f0Svikram  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
223d7072f8Seschrock  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
231d8ed0f0Svikram  * Use is subject to license terms.
243d7072f8Seschrock  */
253d7072f8Seschrock 
263d7072f8Seschrock /*
277c478bd9Sstevel@tonic-gate  * lofiadm - administer lofi(7d). Very simple, add and remove file<->device
287c478bd9Sstevel@tonic-gate  * associations, and display status. All the ioctls are private between
297c478bd9Sstevel@tonic-gate  * lofi and lofiadm, and so are very simple - device information is
307c478bd9Sstevel@tonic-gate  * communicated via a minor number.
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <sys/param.h>
377c478bd9Sstevel@tonic-gate #include <sys/lofi.h>
387c478bd9Sstevel@tonic-gate #include <sys/stat.h>
39*87117650Saalok #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>
44*87117650Saalok #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>
50*87117650Saalok #include <libgen.h>
51*87117650Saalok #include <ctype.h>
52*87117650Saalok #include <zlib.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"
58*87117650Saalok 	"       %s -C [algorithm] [-s segment_size] file \n"
59*87117650Saalok 	"       %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;
66*87117650Saalok static int	compressflag = 0;
67*87117650Saalok static int 	uncompressflag = 0;
687c478bd9Sstevel@tonic-gate 
69*87117650Saalok static int gzip_compress(void *src, size_t srclen, void *dst,
70*87117650Saalok 	size_t *destlen, int level);
71*87117650Saalok 
72*87117650Saalok lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = {
73*87117650Saalok 	{NULL,  gzip_compress,  6,	"gzip"}, /* default */
74*87117650Saalok 	{NULL,	gzip_compress,	6,	"gzip-6"},
75*87117650Saalok 	{NULL,	gzip_compress,	9, 	"gzip-9"}
76*87117650Saalok };
77*87117650Saalok 
78*87117650Saalok #define	FORMAT 			"%-20s     %-30s	%s\n"
79*87117650Saalok #define	NONE			"-"
80*87117650Saalok #define	COMPRESS		"Compressed"
81*87117650Saalok #define	COMPRESS_ALGORITHM	"gzip"
82*87117650Saalok #define	COMPRESS_THRESHOLD	2048
83*87117650Saalok #define	SEGSIZE			131072
84*87117650Saalok #define	BLOCK_SIZE		512
85*87117650Saalok #define	KILOBYTE		1024
86*87117650Saalok #define	MEGABYTE		(KILOBYTE * KILOBYTE)
87*87117650Saalok #define	GIGABYTE		(KILOBYTE * MEGABYTE)
88*87117650Saalok 
89*87117650Saalok static int gzip_compress(void *src, size_t srclen, void *dst,
90*87117650Saalok 	size_t *dstlen, int level)
91*87117650Saalok {
92*87117650Saalok 	if (compress2(dst, (ulong_t *)dstlen, src, srclen, level) != Z_OK)
93*87117650Saalok 		return (-1);
94*87117650Saalok 
95*87117650Saalok 	return (0);
96*87117650Saalok }
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate /*
997c478bd9Sstevel@tonic-gate  * Print the list of all the mappings. Including a header.
1007c478bd9Sstevel@tonic-gate  */
1017c478bd9Sstevel@tonic-gate static void
1027c478bd9Sstevel@tonic-gate print_mappings(int fd)
1037c478bd9Sstevel@tonic-gate {
1047c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
1057c478bd9Sstevel@tonic-gate 	int	minor;
1067c478bd9Sstevel@tonic-gate 	int	maxminor;
107*87117650Saalok 	char	path[MAXPATHLEN];
108*87117650Saalok 	char	options[MAXPATHLEN];
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	li.li_minor = 0;
1117c478bd9Sstevel@tonic-gate 	if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) {
1127c478bd9Sstevel@tonic-gate 		perror("ioctl");
1137c478bd9Sstevel@tonic-gate 		exit(E_ERROR);
1147c478bd9Sstevel@tonic-gate 	}
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 	maxminor = li.li_minor;
1177c478bd9Sstevel@tonic-gate 
118*87117650Saalok 	(void) printf(FORMAT, "Block Device", "File", "Options");
1197c478bd9Sstevel@tonic-gate 	for (minor = 1; minor <= maxminor; minor++) {
1207c478bd9Sstevel@tonic-gate 		li.li_minor = minor;
1217c478bd9Sstevel@tonic-gate 		if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) {
1227c478bd9Sstevel@tonic-gate 			if (errno == ENXIO)
1237c478bd9Sstevel@tonic-gate 				continue;
1247c478bd9Sstevel@tonic-gate 			perror("ioctl");
1257c478bd9Sstevel@tonic-gate 			break;
1267c478bd9Sstevel@tonic-gate 		}
1277c478bd9Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "/dev/%s/%d",
1287c478bd9Sstevel@tonic-gate 		    LOFI_BLOCK_NAME, minor);
129*87117650Saalok 		if (li.li_algorithm[0] == '\0')
130*87117650Saalok 			(void) snprintf(options, sizeof (options), "%s", NONE);
131*87117650Saalok 		else
132*87117650Saalok 			(void) snprintf(options, sizeof (options),
133*87117650Saalok 			    COMPRESS "(%s)", li.li_algorithm);
134*87117650Saalok 
135*87117650Saalok 		(void) printf(FORMAT, path, li.li_filename, options);
1367c478bd9Sstevel@tonic-gate 	}
1377c478bd9Sstevel@tonic-gate }
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate static void
1407c478bd9Sstevel@tonic-gate usage(void)
1417c478bd9Sstevel@tonic-gate {
142*87117650Saalok 	(void) fprintf(stderr, gettext(USAGE), pname, pname,
143*87117650Saalok 	    pname, pname, pname);
1447c478bd9Sstevel@tonic-gate 	exit(E_USAGE);
1457c478bd9Sstevel@tonic-gate }
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate /*
1487c478bd9Sstevel@tonic-gate  * Translate a lofi device name to a minor number. We might be asked
1497c478bd9Sstevel@tonic-gate  * to do this when there is no association (such as when the user specifies
1507c478bd9Sstevel@tonic-gate  * a particular device), so we can only look at the string.
1517c478bd9Sstevel@tonic-gate  */
1527c478bd9Sstevel@tonic-gate static int
1537c478bd9Sstevel@tonic-gate name_to_minor(const char *devicename)
1547c478bd9Sstevel@tonic-gate {
1557c478bd9Sstevel@tonic-gate 	int	minor;
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 	if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) {
1587c478bd9Sstevel@tonic-gate 		return (minor);
1597c478bd9Sstevel@tonic-gate 	}
1607c478bd9Sstevel@tonic-gate 	if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) {
1617c478bd9Sstevel@tonic-gate 		return (minor);
1627c478bd9Sstevel@tonic-gate 	}
1637c478bd9Sstevel@tonic-gate 	return (0);
1647c478bd9Sstevel@tonic-gate }
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate /*
1677c478bd9Sstevel@tonic-gate  * This might be the first time we've used this minor number. If so,
1687c478bd9Sstevel@tonic-gate  * it might also be that the /dev links are in the process of being created
1697c478bd9Sstevel@tonic-gate  * by devfsadmd (or that they'll be created "soon"). We cannot return
1707c478bd9Sstevel@tonic-gate  * until they're there or the invoker of lofiadm might try to use them
1717c478bd9Sstevel@tonic-gate  * and not find them. This can happen if a shell script is running on
1727c478bd9Sstevel@tonic-gate  * an MP.
1737c478bd9Sstevel@tonic-gate  */
1747c478bd9Sstevel@tonic-gate static int sleeptime = 2;	/* number of seconds to sleep between stat's */
1757c478bd9Sstevel@tonic-gate static int maxsleep = 120;	/* maximum number of seconds to sleep */
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate static void
1787c478bd9Sstevel@tonic-gate wait_until_dev_complete(int minor)
1797c478bd9Sstevel@tonic-gate {
1807c478bd9Sstevel@tonic-gate 	struct stat64 buf;
1817c478bd9Sstevel@tonic-gate 	int	cursleep;
182*87117650Saalok 	char	blkpath[MAXPATHLEN];
183*87117650Saalok 	char	charpath[MAXPATHLEN];
1841d8ed0f0Svikram 	di_devlink_handle_t hdl;
1851d8ed0f0Svikram 
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 	(void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d",
1887c478bd9Sstevel@tonic-gate 	    LOFI_BLOCK_NAME, minor);
1897c478bd9Sstevel@tonic-gate 	(void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d",
1907c478bd9Sstevel@tonic-gate 	    LOFI_CHAR_NAME, minor);
1911d8ed0f0Svikram 
1921d8ed0f0Svikram 	/* Check if links already present */
1931d8ed0f0Svikram 	if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
1941d8ed0f0Svikram 		return;
1951d8ed0f0Svikram 
1961d8ed0f0Svikram 	/* First use di_devlink_init() */
1971d8ed0f0Svikram 	if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) {
1981d8ed0f0Svikram 		(void) di_devlink_fini(&hdl);
1991d8ed0f0Svikram 		goto out;
2001d8ed0f0Svikram 	}
2011d8ed0f0Svikram 
2021d8ed0f0Svikram 	/*
2031d8ed0f0Svikram 	 * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will
2041d8ed0f0Svikram 	 * only fail if the caller is non-root. In that case, wait for
2051d8ed0f0Svikram 	 * link creation via sysevents.
2061d8ed0f0Svikram 	 */
2077c478bd9Sstevel@tonic-gate 	cursleep = 0;
2087c478bd9Sstevel@tonic-gate 	while (cursleep < maxsleep) {
2097c478bd9Sstevel@tonic-gate 		if ((stat64(blkpath, &buf) == -1) ||
2107c478bd9Sstevel@tonic-gate 		    (stat64(charpath, &buf) == -1)) {
2117c478bd9Sstevel@tonic-gate 			(void) sleep(sleeptime);
2127c478bd9Sstevel@tonic-gate 			cursleep += sleeptime;
2137c478bd9Sstevel@tonic-gate 			continue;
2147c478bd9Sstevel@tonic-gate 		}
2157c478bd9Sstevel@tonic-gate 		return;
2167c478bd9Sstevel@tonic-gate 	}
2171d8ed0f0Svikram 
2187c478bd9Sstevel@tonic-gate 	/* one last try */
2191d8ed0f0Svikram 
2201d8ed0f0Svikram out:
2217c478bd9Sstevel@tonic-gate 	if (stat64(blkpath, &buf) == -1) {
2227c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), blkpath);
2237c478bd9Sstevel@tonic-gate 	}
2247c478bd9Sstevel@tonic-gate 	if (stat64(charpath, &buf) == -1) {
2257c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), charpath);
2267c478bd9Sstevel@tonic-gate 	}
2277c478bd9Sstevel@tonic-gate }
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate /*
2307c478bd9Sstevel@tonic-gate  * Add a device association. If devicename is NULL, let the driver
2317c478bd9Sstevel@tonic-gate  * pick a device.
2327c478bd9Sstevel@tonic-gate  */
2337c478bd9Sstevel@tonic-gate static void
234*87117650Saalok add_mapping(int lfd, const char *devicename, const char *filename,
235*87117650Saalok     int *minor_created, int suppress)
2367c478bd9Sstevel@tonic-gate {
2377c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
2387c478bd9Sstevel@tonic-gate 	int	minor;
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
2417c478bd9Sstevel@tonic-gate 		/* pick one */
2427c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
243*87117650Saalok 		(void) strlcpy(li.li_filename, filename,
244*87117650Saalok 		    sizeof (li.li_filename));
2457c478bd9Sstevel@tonic-gate 		minor = ioctl(lfd, LOFI_MAP_FILE, &li);
2467c478bd9Sstevel@tonic-gate 		if (minor == -1) {
2477c478bd9Sstevel@tonic-gate 			die(gettext("could not map file %s"), filename);
2487c478bd9Sstevel@tonic-gate 		}
2497c478bd9Sstevel@tonic-gate 		wait_until_dev_complete(minor);
2507c478bd9Sstevel@tonic-gate 		/* print one picked */
251*87117650Saalok 		if (!suppress)
2527c478bd9Sstevel@tonic-gate 			(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor);
253*87117650Saalok 
254*87117650Saalok 		/* fill in the minor if needed */
255*87117650Saalok 		if (minor_created != NULL) {
256*87117650Saalok 			*minor_created = minor;
257*87117650Saalok 		}
2587c478bd9Sstevel@tonic-gate 		return;
2597c478bd9Sstevel@tonic-gate 	}
2607c478bd9Sstevel@tonic-gate 	/* use device we were given */
2617c478bd9Sstevel@tonic-gate 	minor = name_to_minor(devicename);
2627c478bd9Sstevel@tonic-gate 	if (minor == 0) {
2637c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
2647c478bd9Sstevel@tonic-gate 	}
265*87117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
2667c478bd9Sstevel@tonic-gate 	li.li_minor = minor;
2677c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) {
2687c478bd9Sstevel@tonic-gate 		die(gettext("could not map file %s to %s"), filename,
2697c478bd9Sstevel@tonic-gate 		    devicename);
2707c478bd9Sstevel@tonic-gate 	}
2717c478bd9Sstevel@tonic-gate 	wait_until_dev_complete(minor);
2727c478bd9Sstevel@tonic-gate }
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate /*
2757c478bd9Sstevel@tonic-gate  * Remove an association. Delete by device name if non-NULL, or by
2767c478bd9Sstevel@tonic-gate  * filename otherwise.
2777c478bd9Sstevel@tonic-gate  */
2787c478bd9Sstevel@tonic-gate static void
2793d7072f8Seschrock delete_mapping(int lfd, const char *devicename, const char *filename,
2803d7072f8Seschrock     boolean_t force)
2817c478bd9Sstevel@tonic-gate {
2827c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
2837c478bd9Sstevel@tonic-gate 
2843d7072f8Seschrock 	li.li_force = force;
2857c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
2867c478bd9Sstevel@tonic-gate 		/* delete by filename */
287*87117650Saalok 		(void) strlcpy(li.li_filename, filename,
288*87117650Saalok 		    sizeof (li.li_filename));
2897c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
2907c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) {
2917c478bd9Sstevel@tonic-gate 			die(gettext("could not unmap file %s"), filename);
2927c478bd9Sstevel@tonic-gate 		}
2937c478bd9Sstevel@tonic-gate 		return;
2947c478bd9Sstevel@tonic-gate 	}
2957c478bd9Sstevel@tonic-gate 	/* delete by device */
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	li.li_minor = name_to_minor(devicename);
2987c478bd9Sstevel@tonic-gate 	if (li.li_minor == 0) {
2997c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
3007c478bd9Sstevel@tonic-gate 	}
3017c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) {
3027c478bd9Sstevel@tonic-gate 		die(gettext("could not unmap device %s"), devicename);
3037c478bd9Sstevel@tonic-gate 	}
3047c478bd9Sstevel@tonic-gate }
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate static void
3077c478bd9Sstevel@tonic-gate print_one_mapping(int lfd, const char *devicename, const char *filename)
3087c478bd9Sstevel@tonic-gate {
3097c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
3127c478bd9Sstevel@tonic-gate 		/* given filename, print devicename */
3137c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
314*87117650Saalok 		(void) strlcpy(li.li_filename, filename,
315*87117650Saalok 		    sizeof (li.li_filename));
3167c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) {
3177c478bd9Sstevel@tonic-gate 			die(gettext("could not find device for %s"), filename);
3187c478bd9Sstevel@tonic-gate 		}
3197c478bd9Sstevel@tonic-gate 		(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor);
3207c478bd9Sstevel@tonic-gate 		return;
3217c478bd9Sstevel@tonic-gate 	}
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	/* given devicename, print filename */
3247c478bd9Sstevel@tonic-gate 	li.li_minor = name_to_minor(devicename);
3257c478bd9Sstevel@tonic-gate 	if (li.li_minor == 0) {
3267c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
3277c478bd9Sstevel@tonic-gate 	}
3287c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) {
3297c478bd9Sstevel@tonic-gate 		die(gettext("could not find filename for %s"), devicename);
3307c478bd9Sstevel@tonic-gate 	}
3317c478bd9Sstevel@tonic-gate 	(void) printf("%s\n", li.li_filename);
3327c478bd9Sstevel@tonic-gate }
3337c478bd9Sstevel@tonic-gate 
334*87117650Saalok /*
335*87117650Saalok  * Uncompress a file.
336*87117650Saalok  *
337*87117650Saalok  * First map the file in to establish a device
338*87117650Saalok  * association, then read from it. On-the-fly
339*87117650Saalok  * decompression will automatically uncompress
340*87117650Saalok  * the file if it's compressed
341*87117650Saalok  *
342*87117650Saalok  * If the file is mapped and a device association
343*87117650Saalok  * has been established, disallow uncompressing
344*87117650Saalok  * the file until it is unmapped.
345*87117650Saalok  */
346*87117650Saalok static void
347*87117650Saalok lofi_uncompress(int lfd, const char *filename)
3487c478bd9Sstevel@tonic-gate {
349*87117650Saalok 	struct lofi_ioctl li;
350*87117650Saalok 	char buf[MAXBSIZE];
351*87117650Saalok 	char devicename[32];
352*87117650Saalok 	char tmpfilename[MAXPATHLEN];
353*87117650Saalok 	char *dir = NULL;
354*87117650Saalok 	char *file = NULL;
355*87117650Saalok 	int minor = 0;
356*87117650Saalok 	struct stat64 statbuf;
357*87117650Saalok 	int compfd = -1;
358*87117650Saalok 	int uncompfd = -1;
359*87117650Saalok 	ssize_t rbytes;
360*87117650Saalok 
361*87117650Saalok 	/*
362*87117650Saalok 	 * Disallow uncompressing the file if it is
363*87117650Saalok 	 * already mapped.
364*87117650Saalok 	 */
365*87117650Saalok 	li.li_minor = 0;
366*87117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
367*87117650Saalok 	if (ioctl(lfd, LOFI_GET_MINOR, &li) != -1)
368*87117650Saalok 		die(gettext("%s must be unmapped before uncompressing"),
369*87117650Saalok 		    filename);
370*87117650Saalok 
371*87117650Saalok 	/* Zero length files don't need to be uncompressed */
372*87117650Saalok 	if (stat64(filename, &statbuf) == -1)
373*87117650Saalok 		die(gettext("stat: %s"), filename);
374*87117650Saalok 	if (statbuf.st_size == 0)
375*87117650Saalok 		return;
376*87117650Saalok 
377*87117650Saalok 	add_mapping(lfd, NULL, filename, &minor, 1);
378*87117650Saalok 	(void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d",
379*87117650Saalok 	    LOFI_BLOCK_NAME, minor);
380*87117650Saalok 
381*87117650Saalok 	/* If the file isn't compressed, we just return */
382*87117650Saalok 	if ((ioctl(lfd, LOFI_CHECK_COMPRESSED, &li) == -1) ||
383*87117650Saalok 	    (li.li_algorithm == '\0')) {
384*87117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
385*87117650Saalok 		return;
386*87117650Saalok 	}
387*87117650Saalok 
388*87117650Saalok 	if ((compfd = open64(devicename, O_RDONLY | O_NONBLOCK)) == -1) {
389*87117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
390*87117650Saalok 		die(gettext("open: %s"), filename);
391*87117650Saalok 	}
392*87117650Saalok 	/* Create a temp file in the same directory */
393*87117650Saalok 	dir = strdup(filename);
394*87117650Saalok 	dir = dirname(dir);
395*87117650Saalok 	file = strdup(filename);
396*87117650Saalok 	file = basename(file);
397*87117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
398*87117650Saalok 	    "%s/.%sXXXXXX", dir, file);
399*87117650Saalok 
400*87117650Saalok 	if ((uncompfd = mkstemp64(tmpfilename)) == -1) {
401*87117650Saalok 		(void) close(compfd);
402*87117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
403*87117650Saalok 		free(dir);
404*87117650Saalok 		free(file);
405*87117650Saalok 		return;
406*87117650Saalok 	}
407*87117650Saalok 
408*87117650Saalok 	/*
409*87117650Saalok 	 * Set the mode bits and the owner of this temporary
410*87117650Saalok 	 * file to be that of the original uncompressed file
411*87117650Saalok 	 */
412*87117650Saalok 	(void) fchmod(uncompfd, statbuf.st_mode);
413*87117650Saalok 
414*87117650Saalok 	if (fchown(uncompfd, statbuf.st_uid, statbuf.st_gid) == -1) {
415*87117650Saalok 		(void) close(compfd);
416*87117650Saalok 		(void) close(uncompfd);
417*87117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
418*87117650Saalok 		free(dir);
419*87117650Saalok 		free(file);
420*87117650Saalok 		return;
421*87117650Saalok 	}
422*87117650Saalok 
423*87117650Saalok 	/* Now read from the device in MAXBSIZE-sized chunks */
424*87117650Saalok 	for (;;) {
425*87117650Saalok 		rbytes = read(compfd, buf, sizeof (buf));
426*87117650Saalok 
427*87117650Saalok 		if (rbytes <= 0)
428*87117650Saalok 			break;
429*87117650Saalok 
430*87117650Saalok 		if (write(uncompfd, buf, rbytes) != rbytes) {
431*87117650Saalok 			rbytes = -1;
432*87117650Saalok 			break;
433*87117650Saalok 		}
434*87117650Saalok 	}
435*87117650Saalok 
436*87117650Saalok 	(void) close(compfd);
437*87117650Saalok 	(void) close(uncompfd);
438*87117650Saalok 	free(dir);
439*87117650Saalok 	free(file);
440*87117650Saalok 
441*87117650Saalok 	/* Delete the mapping */
442*87117650Saalok 	delete_mapping(lfd, devicename, filename, B_TRUE);
443*87117650Saalok 
444*87117650Saalok 	/*
445*87117650Saalok 	 * If an error occured while reading or writing, rbytes will
446*87117650Saalok 	 * be negative
447*87117650Saalok 	 */
448*87117650Saalok 	if (rbytes < 0) {
449*87117650Saalok 		(void) unlink(tmpfilename);
450*87117650Saalok 		die(gettext("could not read from %s"), filename);
451*87117650Saalok 	}
452*87117650Saalok 
453*87117650Saalok 	/* Rename the temp file to the actual file */
454*87117650Saalok 	if (rename(tmpfilename, filename) == -1)
455*87117650Saalok 		(void) unlink(tmpfilename);
456*87117650Saalok }
457*87117650Saalok 
458*87117650Saalok /*
459*87117650Saalok  * Compress a file
460*87117650Saalok  */
461*87117650Saalok static void
462*87117650Saalok lofi_compress(int lfd, const char *filename, int compress_index,
463*87117650Saalok     uint32_t segsize)
464*87117650Saalok {
465*87117650Saalok 	struct lofi_ioctl lic;
466*87117650Saalok 	lofi_compress_info_t *li;
467*87117650Saalok 	char tmpfilename[MAXPATHLEN];
468*87117650Saalok 	char comp_filename[MAXPATHLEN];
469*87117650Saalok 	char algorithm[MAXALGLEN];
470*87117650Saalok 	char *dir = NULL, *file = NULL;
471*87117650Saalok 	uchar_t *uncompressed_seg = NULL;
472*87117650Saalok 	uchar_t *compressed_seg = NULL;
473*87117650Saalok 	uint32_t compressed_segsize;
474*87117650Saalok 	uint32_t len_compressed, count;
475*87117650Saalok 	uint32_t index_entries, index_sz;
476*87117650Saalok 	uint64_t *index = NULL;
477*87117650Saalok 	uint64_t offset;
478*87117650Saalok 	size_t real_segsize;
479*87117650Saalok 	struct stat64 statbuf;
480*87117650Saalok 	int compfd = -1, uncompfd = -1;
481*87117650Saalok 	int tfd = -1;
482*87117650Saalok 	ssize_t rbytes, wbytes, lastread;
483*87117650Saalok 	int i, type;
484*87117650Saalok 
485*87117650Saalok 	/*
486*87117650Saalok 	 * Disallow compressing the file if it is
487*87117650Saalok 	 * already mapped
488*87117650Saalok 	 */
489*87117650Saalok 	lic.li_minor = 0;
490*87117650Saalok 	(void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename));
491*87117650Saalok 	if (ioctl(lfd, LOFI_GET_MINOR, &lic) != -1)
492*87117650Saalok 		die(gettext("%s must be unmapped before compressing"),
493*87117650Saalok 		    filename);
494*87117650Saalok 
495*87117650Saalok 	li = &lofi_compress_table[compress_index];
496*87117650Saalok 
497*87117650Saalok 	/*
498*87117650Saalok 	 * The size of the buffer to hold compressed data must
499*87117650Saalok 	 * be slightly larger than the compressed segment size.
500*87117650Saalok 	 *
501*87117650Saalok 	 * The compress functions use part of the buffer as
502*87117650Saalok 	 * scratch space to do calculations.
503*87117650Saalok 	 * Ref: http://www.zlib.net/manual.html#compress2
504*87117650Saalok 	 */
505*87117650Saalok 	compressed_segsize = segsize + (segsize >> 6);
506*87117650Saalok 	compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR);
507*87117650Saalok 	uncompressed_seg = (uchar_t *)malloc(segsize);
508*87117650Saalok 
509*87117650Saalok 	if (compressed_seg == NULL || uncompressed_seg == NULL)
510*87117650Saalok 		die(gettext("No memory"));
511*87117650Saalok 
512*87117650Saalok 	if ((uncompfd = open64(filename, O_RDONLY|O_LARGEFILE, 0)) == -1)
513*87117650Saalok 		die(gettext("open: %s"), filename);
514*87117650Saalok 
515*87117650Saalok 	if (fstat64(uncompfd, &statbuf) == -1) {
516*87117650Saalok 		(void) close(uncompfd);
517*87117650Saalok 		die(gettext("fstat: %s"), filename);
518*87117650Saalok 	}
519*87117650Saalok 
520*87117650Saalok 	/* Zero length files don't need to be compressed */
521*87117650Saalok 	if (statbuf.st_size == 0) {
522*87117650Saalok 		(void) close(uncompfd);
523*87117650Saalok 		return;
524*87117650Saalok 	}
525*87117650Saalok 
526*87117650Saalok 	/*
527*87117650Saalok 	 * Create temporary files in the same directory that
528*87117650Saalok 	 * will hold the intermediate data
529*87117650Saalok 	 */
530*87117650Saalok 	dir = strdup(filename);
531*87117650Saalok 	dir = dirname(dir);
532*87117650Saalok 	file = strdup(filename);
533*87117650Saalok 	file = basename(file);
534*87117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
535*87117650Saalok 	    "%s/.%sXXXXXX", dir, file);
536*87117650Saalok 	(void) snprintf(comp_filename, sizeof (comp_filename),
537*87117650Saalok 	    "%s/.%sXXXXXX", dir, file);
538*87117650Saalok 
539*87117650Saalok 	if ((tfd = mkstemp64(tmpfilename)) == -1)
540*87117650Saalok 		goto cleanup;
541*87117650Saalok 
542*87117650Saalok 	if ((compfd = mkstemp64(comp_filename)) == -1)
543*87117650Saalok 		goto cleanup;
544*87117650Saalok 
545*87117650Saalok 	/*
546*87117650Saalok 	 * Set the mode bits and owner of the compressed
547*87117650Saalok 	 * file to be that of the original uncompressed file
548*87117650Saalok 	 */
549*87117650Saalok 	(void) fchmod(compfd, statbuf.st_mode);
550*87117650Saalok 
551*87117650Saalok 	if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1)
552*87117650Saalok 		goto cleanup;
553*87117650Saalok 
554*87117650Saalok 	/*
555*87117650Saalok 	 * Calculate the number of index entries required.
556*87117650Saalok 	 * index entries are stored as an array. adding
557*87117650Saalok 	 * a '2' here accounts for the fact that the last
558*87117650Saalok 	 * segment may not be a multiple of the segment size
559*87117650Saalok 	 */
560*87117650Saalok 	index_sz = (statbuf.st_size / segsize) + 2;
561*87117650Saalok 	index = malloc(sizeof (*index) * index_sz);
562*87117650Saalok 
563*87117650Saalok 	if (index == NULL)
564*87117650Saalok 		goto cleanup;
565*87117650Saalok 
566*87117650Saalok 	offset = 0;
567*87117650Saalok 	lastread = segsize;
568*87117650Saalok 	count = 0;
569*87117650Saalok 
570*87117650Saalok 	/*
571*87117650Saalok 	 * Now read from the uncompressed file in 'segsize'
572*87117650Saalok 	 * sized chunks, compress what was read in and
573*87117650Saalok 	 * write it out to a temporary file
574*87117650Saalok 	 */
575*87117650Saalok 	for (;;) {
576*87117650Saalok 		rbytes = read(uncompfd, uncompressed_seg, segsize);
577*87117650Saalok 
578*87117650Saalok 		if (rbytes <= 0)
579*87117650Saalok 			break;
580*87117650Saalok 
581*87117650Saalok 		if (lastread < segsize)
582*87117650Saalok 			goto cleanup;
583*87117650Saalok 
584*87117650Saalok 		/*
585*87117650Saalok 		 * Account for the first byte that
586*87117650Saalok 		 * indicates whether a segment is
587*87117650Saalok 		 * compressed or not
588*87117650Saalok 		 */
589*87117650Saalok 		real_segsize = segsize - 1;
590*87117650Saalok 		(void) li->l_compress(uncompressed_seg, rbytes,
591*87117650Saalok 		    compressed_seg + SEGHDR, &real_segsize, li->l_level);
592*87117650Saalok 
593*87117650Saalok 		/*
594*87117650Saalok 		 * If the length of the compressed data is more
595*87117650Saalok 		 * than a threshold then there isn't any benefit
596*87117650Saalok 		 * to be had from compressing this segment - leave
597*87117650Saalok 		 * it uncompressed.
598*87117650Saalok 		 *
599*87117650Saalok 		 * NB. In case an error occurs during compression (above)
600*87117650Saalok 		 * the 'real_segsize' isn't changed. The logic below
601*87117650Saalok 		 * ensures that that segment is left uncompressed.
602*87117650Saalok 		 */
603*87117650Saalok 		len_compressed = real_segsize;
604*87117650Saalok 		if (real_segsize > segsize - COMPRESS_THRESHOLD) {
605*87117650Saalok 			(void) memcpy(compressed_seg + SEGHDR, uncompressed_seg,
606*87117650Saalok 			    rbytes);
607*87117650Saalok 			type = UNCOMPRESSED;
608*87117650Saalok 			len_compressed = rbytes;
609*87117650Saalok 		} else {
610*87117650Saalok 			type = COMPRESSED;
611*87117650Saalok 		}
612*87117650Saalok 
613*87117650Saalok 		/*
614*87117650Saalok 		 * Set the first byte or the SEGHDR to
615*87117650Saalok 		 * indicate if it's compressed or not
616*87117650Saalok 		 */
617*87117650Saalok 		*compressed_seg = type;
618*87117650Saalok 		wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR);
619*87117650Saalok 		if (wbytes != (len_compressed + SEGHDR)) {
620*87117650Saalok 			rbytes = -1;
621*87117650Saalok 			break;
622*87117650Saalok 		}
623*87117650Saalok 
624*87117650Saalok 		index[count] = BE_64(offset);
625*87117650Saalok 		offset += wbytes;
626*87117650Saalok 		lastread = rbytes;
627*87117650Saalok 		count++;
628*87117650Saalok 	}
629*87117650Saalok 
630*87117650Saalok 	(void) close(uncompfd);
631*87117650Saalok 
632*87117650Saalok 	if (rbytes < 0)
633*87117650Saalok 		goto cleanup;
634*87117650Saalok 	/*
635*87117650Saalok 	 * The last index entry is a sentinel entry. It does not point to
636*87117650Saalok 	 * an actual compressed segment but helps in computing the size of
637*87117650Saalok 	 * the compressed segment. The size of each compressed segment is
638*87117650Saalok 	 * computed by subtracting the current index value from the next
639*87117650Saalok 	 * one (the compressed blocks are stored sequentially)
640*87117650Saalok 	 */
641*87117650Saalok 	index[count++] = BE_64(offset);
642*87117650Saalok 
643*87117650Saalok 	/*
644*87117650Saalok 	 * Now write the compressed data along with the
645*87117650Saalok 	 * header information to this file which will
646*87117650Saalok 	 * later be renamed to the original uncompressed
647*87117650Saalok 	 * file name
648*87117650Saalok 	 *
649*87117650Saalok 	 * The header is as follows -
650*87117650Saalok 	 *
651*87117650Saalok 	 * Signature (name of the compression algorithm)
652*87117650Saalok 	 * Compression segment size (a multiple of 512)
653*87117650Saalok 	 * Number of index entries
654*87117650Saalok 	 * Size of the last block
655*87117650Saalok 	 * The array containing the index entries
656*87117650Saalok 	 *
657*87117650Saalok 	 * the header is always stored in network byte
658*87117650Saalok 	 * order
659*87117650Saalok 	 */
660*87117650Saalok 	(void) bzero(algorithm, sizeof (algorithm));
661*87117650Saalok 	(void) strlcpy(algorithm, li->l_name, sizeof (algorithm));
662*87117650Saalok 	if (write(compfd, algorithm, sizeof (algorithm))
663*87117650Saalok 	    != sizeof (algorithm))
664*87117650Saalok 		goto cleanup;
665*87117650Saalok 
666*87117650Saalok 	segsize = htonl(segsize);
667*87117650Saalok 	if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize))
668*87117650Saalok 		goto cleanup;
669*87117650Saalok 
670*87117650Saalok 	index_entries = htonl(count);
671*87117650Saalok 	if (write(compfd, &index_entries, sizeof (index_entries)) !=
672*87117650Saalok 	    sizeof (index_entries))
673*87117650Saalok 		goto cleanup;
674*87117650Saalok 
675*87117650Saalok 	lastread = htonl(lastread);
676*87117650Saalok 	if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread))
677*87117650Saalok 		goto cleanup;
678*87117650Saalok 
679*87117650Saalok 	for (i = 0; i < count; i++) {
680*87117650Saalok 		if (write(compfd, index + i, sizeof (*index)) !=
681*87117650Saalok 		    sizeof (*index))
682*87117650Saalok 			goto cleanup;
683*87117650Saalok 	}
684*87117650Saalok 
685*87117650Saalok 	/* Header is written, now write the compressed data */
686*87117650Saalok 	if (lseek(tfd, 0, SEEK_SET) != 0)
687*87117650Saalok 		goto cleanup;
688*87117650Saalok 
689*87117650Saalok 	rbytes = wbytes = 0;
690*87117650Saalok 
691*87117650Saalok 	for (;;) {
692*87117650Saalok 		rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR);
693*87117650Saalok 
694*87117650Saalok 		if (rbytes <= 0)
695*87117650Saalok 			break;
696*87117650Saalok 
697*87117650Saalok 		if (write(compfd, compressed_seg, rbytes) != rbytes)
698*87117650Saalok 			goto cleanup;
699*87117650Saalok 	}
700*87117650Saalok 
701*87117650Saalok 	if (fstat64(compfd, &statbuf) == -1)
702*87117650Saalok 		goto cleanup;
703*87117650Saalok 
704*87117650Saalok 	/*
705*87117650Saalok 	 * Round up the compressed file size to be a multiple of
706*87117650Saalok 	 * DEV_BSIZE. lofi(7D) likes it that way.
707*87117650Saalok 	 */
708*87117650Saalok 	if ((offset = statbuf.st_size % DEV_BSIZE) > 0) {
709*87117650Saalok 
710*87117650Saalok 		offset = DEV_BSIZE - offset;
711*87117650Saalok 
712*87117650Saalok 		for (i = 0; i < offset; i++)
713*87117650Saalok 			uncompressed_seg[i] = '\0';
714*87117650Saalok 		if (write(compfd, uncompressed_seg, offset) != offset)
715*87117650Saalok 			goto cleanup;
716*87117650Saalok 	}
717*87117650Saalok 	(void) close(compfd);
718*87117650Saalok 	(void) close(tfd);
719*87117650Saalok 	(void) unlink(tmpfilename);
720*87117650Saalok cleanup:
721*87117650Saalok 	if (rbytes < 0) {
722*87117650Saalok 		if (tfd != -1)
723*87117650Saalok 			(void) unlink(tmpfilename);
724*87117650Saalok 		if (compfd != -1)
725*87117650Saalok 			(void) unlink(comp_filename);
726*87117650Saalok 		die(gettext("error compressing file %s"), filename);
727*87117650Saalok 	} else {
728*87117650Saalok 		/* Rename the compressed file to the actual file */
729*87117650Saalok 		if (rename(comp_filename, filename) == -1) {
730*87117650Saalok 			(void) unlink(comp_filename);
731*87117650Saalok 			die(gettext("error compressing file %s"), filename);
732*87117650Saalok 		}
733*87117650Saalok 	}
734*87117650Saalok 	if (compressed_seg != NULL)
735*87117650Saalok 		free(compressed_seg);
736*87117650Saalok 	if (uncompressed_seg != NULL)
737*87117650Saalok 		free(uncompressed_seg);
738*87117650Saalok 	if (dir != NULL)
739*87117650Saalok 		free(dir);
740*87117650Saalok 	if (file != NULL)
741*87117650Saalok 		free(file);
742*87117650Saalok 	if (index != NULL)
743*87117650Saalok 		free(index);
744*87117650Saalok 	if (compfd != -1)
745*87117650Saalok 		(void) close(compfd);
746*87117650Saalok 	if (uncompfd != -1)
747*87117650Saalok 		(void) close(uncompfd);
748*87117650Saalok 	if (tfd != -1)
749*87117650Saalok 		(void) close(tfd);
750*87117650Saalok }
751*87117650Saalok 
752*87117650Saalok static int
753*87117650Saalok lofi_compress_select(const char *algname)
754*87117650Saalok {
755*87117650Saalok 	int i;
756*87117650Saalok 
757*87117650Saalok 	for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) {
758*87117650Saalok 		if (strcmp(lofi_compress_table[i].l_name, algname) == 0)
759*87117650Saalok 			return (i);
760*87117650Saalok 	}
761*87117650Saalok 	return (-1);
762*87117650Saalok }
763*87117650Saalok 
764*87117650Saalok static void
765*87117650Saalok check_algorithm_validity(const char *algname, int *compress_index)
766*87117650Saalok {
767*87117650Saalok 	*compress_index = lofi_compress_select(algname);
768*87117650Saalok 	if (*compress_index < 0)
769*87117650Saalok 		die(gettext("invalid algorithm name: %s\n"), algname);
770*87117650Saalok }
771*87117650Saalok 
772*87117650Saalok static void
773*87117650Saalok check_file_validity(const char *filename)
774*87117650Saalok {
7757c478bd9Sstevel@tonic-gate 	struct stat64 buf;
776*87117650Saalok 	int 	error;
7777c478bd9Sstevel@tonic-gate 	int	fd = -1;
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 	fd = open64(filename, O_RDONLY);
7807c478bd9Sstevel@tonic-gate 	if (fd == -1) {
7817c478bd9Sstevel@tonic-gate 		die(gettext("open: %s"), filename);
7827c478bd9Sstevel@tonic-gate 	}
7837c478bd9Sstevel@tonic-gate 	error = fstat64(fd, &buf);
7847c478bd9Sstevel@tonic-gate 	if (error == -1) {
7857c478bd9Sstevel@tonic-gate 		die(gettext("fstat: %s"), filename);
7867c478bd9Sstevel@tonic-gate 	} else if (!S_ISLOFIABLE(buf.st_mode)) {
7877c478bd9Sstevel@tonic-gate 		die(gettext("%s is not a regular file, "
7887c478bd9Sstevel@tonic-gate 		    "block, or character device\n"),
7897c478bd9Sstevel@tonic-gate 		    filename);
7907c478bd9Sstevel@tonic-gate 	} else if ((buf.st_size % DEV_BSIZE) != 0) {
7917c478bd9Sstevel@tonic-gate 		die(gettext("size of %s is not a multiple "
7927c478bd9Sstevel@tonic-gate 		    "of %d\n"),
7937c478bd9Sstevel@tonic-gate 		    filename, DEV_BSIZE);
7947c478bd9Sstevel@tonic-gate 	}
7957c478bd9Sstevel@tonic-gate 	(void) close(fd);
796*87117650Saalok 
797*87117650Saalok 	if (name_to_minor(filename) != 0) {
7987c478bd9Sstevel@tonic-gate 		die(gettext("cannot use " LOFI_DRIVER_NAME
799*87117650Saalok 		    " on itself\n"), NULL);
8007c478bd9Sstevel@tonic-gate 	}
801*87117650Saalok }
802*87117650Saalok 
803*87117650Saalok static uint32_t
804*87117650Saalok convert_to_num(const char *str)
805*87117650Saalok {
806*87117650Saalok 	int len;
807*87117650Saalok 	uint32_t segsize, mult = 1;
808*87117650Saalok 
809*87117650Saalok 	len = strlen(str);
810*87117650Saalok 	if (len && isalpha(str[len - 1])) {
811*87117650Saalok 		switch (str[len - 1]) {
812*87117650Saalok 		case 'k':
813*87117650Saalok 		case 'K':
814*87117650Saalok 			mult = KILOBYTE;
815*87117650Saalok 			break;
816*87117650Saalok 		case 'b':
817*87117650Saalok 		case 'B':
818*87117650Saalok 			mult = BLOCK_SIZE;
819*87117650Saalok 			break;
820*87117650Saalok 		case 'm':
821*87117650Saalok 		case 'M':
822*87117650Saalok 			mult = MEGABYTE;
823*87117650Saalok 			break;
824*87117650Saalok 		case 'g':
825*87117650Saalok 		case 'G':
826*87117650Saalok 			mult = GIGABYTE;
827*87117650Saalok 			break;
828*87117650Saalok 		default:
829*87117650Saalok 			die(gettext("invalid segment size %s\n"), str);
830*87117650Saalok 		}
831*87117650Saalok 	}
832*87117650Saalok 
833*87117650Saalok 	segsize = atol(str);
834*87117650Saalok 	segsize *= mult;
835*87117650Saalok 
836*87117650Saalok 	return (segsize);
837*87117650Saalok }
838*87117650Saalok 
839*87117650Saalok int
840*87117650Saalok main(int argc, char *argv[])
841*87117650Saalok {
842*87117650Saalok 	int	lfd;
843*87117650Saalok 	int	c;
844*87117650Saalok 	const char *devicename = NULL;
845*87117650Saalok 	const char *filename = NULL;
846*87117650Saalok 	const char *algname = COMPRESS_ALGORITHM;
847*87117650Saalok 	int	openflag;
848*87117650Saalok 	int	minor;
849*87117650Saalok 	int 	compress_index;
850*87117650Saalok 	uint32_t segsize = SEGSIZE;
851*87117650Saalok 	static char *lofictl = "/dev/" LOFI_CTL_NAME;
852*87117650Saalok 	boolean_t force = B_FALSE;
853*87117650Saalok 
854*87117650Saalok 	pname = getpname(argv[0]);
855*87117650Saalok 
856*87117650Saalok 	(void) setlocale(LC_ALL, "");
857*87117650Saalok 	(void) textdomain(TEXT_DOMAIN);
858*87117650Saalok 
859*87117650Saalok 	while ((c = getopt(argc, argv, "a:C:d:s:U:f")) != EOF) {
860*87117650Saalok 		switch (c) {
861*87117650Saalok 		case 'a':
862*87117650Saalok 			addflag = 1;
863*87117650Saalok 			filename = optarg;
864*87117650Saalok 			check_file_validity(filename);
865*87117650Saalok 
8667c478bd9Sstevel@tonic-gate 			if (((argc - optind) > 0) && (*argv[optind] != '-')) {
8677c478bd9Sstevel@tonic-gate 				/* optional device */
8687c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
8697c478bd9Sstevel@tonic-gate 				optind++;
8707c478bd9Sstevel@tonic-gate 			}
8717c478bd9Sstevel@tonic-gate 			break;
872*87117650Saalok 		case 'C':
873*87117650Saalok 			compressflag = 1;
874*87117650Saalok 
875*87117650Saalok 			if (((argc - optind) > 0) &&
876*87117650Saalok 			    (*optarg == '-')) {
877*87117650Saalok 				check_algorithm_validity(algname,
878*87117650Saalok 				    &compress_index);
879*87117650Saalok 				optind--;
880*87117650Saalok 				break;
881*87117650Saalok 			} else if (((argc - optind) == 1) &&
882*87117650Saalok 			    (*argv[optind] != '-')) {
883*87117650Saalok 				algname = optarg;
884*87117650Saalok 				filename = argv[optind];
885*87117650Saalok 				optind++;
886*87117650Saalok 			} else if (((argc - optind) > 1) &&
887*87117650Saalok 			    (*argv[optind] == '-')) {
888*87117650Saalok 				algname = optarg;
889*87117650Saalok 				check_algorithm_validity(algname,
890*87117650Saalok 				    &compress_index);
891*87117650Saalok 				break;
892*87117650Saalok 			} else {
893*87117650Saalok 				filename = optarg;
894*87117650Saalok 			}
895*87117650Saalok 
896*87117650Saalok 			check_file_validity(filename);
897*87117650Saalok 			check_algorithm_validity(algname, &compress_index);
898*87117650Saalok 			break;
8997c478bd9Sstevel@tonic-gate 		case 'd':
9007c478bd9Sstevel@tonic-gate 			deleteflag = 1;
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 			minor = name_to_minor(optarg);
9037c478bd9Sstevel@tonic-gate 			if (minor != 0)
9047c478bd9Sstevel@tonic-gate 				devicename = optarg;
9057c478bd9Sstevel@tonic-gate 			else
9067c478bd9Sstevel@tonic-gate 				filename = optarg;
9077c478bd9Sstevel@tonic-gate 			break;
9083d7072f8Seschrock 		case 'f':
9093d7072f8Seschrock 			force = B_TRUE;
9103d7072f8Seschrock 			break;
911*87117650Saalok 		case 's':
912*87117650Saalok 			segsize = convert_to_num(optarg);
913*87117650Saalok 
914*87117650Saalok 			if (segsize == 0 || segsize % DEV_BSIZE)
915*87117650Saalok 				die(gettext("segment size %s is invalid "
916*87117650Saalok 				    "or not a multiple of minimum block "
917*87117650Saalok 				    "size %ld\n"), optarg, DEV_BSIZE);
918*87117650Saalok 
919*87117650Saalok 			filename = argv[optind];
920*87117650Saalok 			check_file_validity(filename);
921*87117650Saalok 			optind++;
922*87117650Saalok 			break;
923*87117650Saalok 		case 'U':
924*87117650Saalok 			uncompressflag = 1;
925*87117650Saalok 			filename = optarg;
926*87117650Saalok 			check_file_validity(filename);
927*87117650Saalok 			break;
9287c478bd9Sstevel@tonic-gate 		case '?':
9297c478bd9Sstevel@tonic-gate 		default:
9307c478bd9Sstevel@tonic-gate 			errflag = 1;
9317c478bd9Sstevel@tonic-gate 			break;
9327c478bd9Sstevel@tonic-gate 		}
9337c478bd9Sstevel@tonic-gate 	}
934*87117650Saalok 	if (errflag ||
935*87117650Saalok 	    (addflag && deleteflag) ||
936*87117650Saalok 	    ((compressflag || uncompressflag) && (addflag || deleteflag)))
9377c478bd9Sstevel@tonic-gate 		usage();
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 	switch (argc - optind) {
9407c478bd9Sstevel@tonic-gate 	case 0: /* no more args */
9417c478bd9Sstevel@tonic-gate 		break;
9427c478bd9Sstevel@tonic-gate 	case 1: /* one arg without options means print the association */
9437c478bd9Sstevel@tonic-gate 		if (addflag || deleteflag)
9447c478bd9Sstevel@tonic-gate 			usage();
945*87117650Saalok 		if (compressflag || uncompressflag)
946*87117650Saalok 			usage();
9477c478bd9Sstevel@tonic-gate 		minor = name_to_minor(argv[optind]);
9487c478bd9Sstevel@tonic-gate 		if (minor != 0)
9497c478bd9Sstevel@tonic-gate 			devicename = argv[optind];
9507c478bd9Sstevel@tonic-gate 		else
9517c478bd9Sstevel@tonic-gate 			filename = argv[optind];
9527c478bd9Sstevel@tonic-gate 		break;
9537c478bd9Sstevel@tonic-gate 	default:
9547c478bd9Sstevel@tonic-gate 		usage();
9557c478bd9Sstevel@tonic-gate 		break;
9567c478bd9Sstevel@tonic-gate 	}
9577c478bd9Sstevel@tonic-gate 
9587c478bd9Sstevel@tonic-gate 	if (filename && !valid_abspath(filename))
9597c478bd9Sstevel@tonic-gate 		exit(E_ERROR);
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 	/*
9627c478bd9Sstevel@tonic-gate 	 * Here, we know the arguments are correct, the filename is an
9637c478bd9Sstevel@tonic-gate 	 * absolute path, it exists and is a regular file. We don't yet
9647c478bd9Sstevel@tonic-gate 	 * know that the device name is ok or not.
9657c478bd9Sstevel@tonic-gate 	 */
9667c478bd9Sstevel@tonic-gate 	/*
9677c478bd9Sstevel@tonic-gate 	 * Now to the real work.
9687c478bd9Sstevel@tonic-gate 	 */
9697c478bd9Sstevel@tonic-gate 	openflag = O_EXCL;
970*87117650Saalok 	if (addflag || deleteflag || compressflag || uncompressflag)
9717c478bd9Sstevel@tonic-gate 		openflag |= O_RDWR;
9727c478bd9Sstevel@tonic-gate 	else
9737c478bd9Sstevel@tonic-gate 		openflag |= O_RDONLY;
9747c478bd9Sstevel@tonic-gate 	lfd = open(lofictl, openflag);
9757c478bd9Sstevel@tonic-gate 	if (lfd == -1) {
9767c478bd9Sstevel@tonic-gate 		if ((errno == EPERM) || (errno == EACCES)) {
9777c478bd9Sstevel@tonic-gate 			die("you do not have permission to perform "
9787c478bd9Sstevel@tonic-gate 			    "that operation.\n");
9797c478bd9Sstevel@tonic-gate 		} else {
9807c478bd9Sstevel@tonic-gate 			die("%s", lofictl);
9817c478bd9Sstevel@tonic-gate 		}
9827c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
9837c478bd9Sstevel@tonic-gate 	}
9847c478bd9Sstevel@tonic-gate 	if (addflag)
985*87117650Saalok 		add_mapping(lfd, devicename, filename, NULL, 0);
986*87117650Saalok 	else if (compressflag)
987*87117650Saalok 		lofi_compress(lfd, filename, compress_index, segsize);
988*87117650Saalok 	else if (uncompressflag)
989*87117650Saalok 		lofi_uncompress(lfd, filename);
9907c478bd9Sstevel@tonic-gate 	else if (deleteflag)
9913d7072f8Seschrock 		delete_mapping(lfd, devicename, filename, force);
9927c478bd9Sstevel@tonic-gate 	else if (filename || devicename)
9937c478bd9Sstevel@tonic-gate 		print_one_mapping(lfd, devicename, filename);
9947c478bd9Sstevel@tonic-gate 	else
9957c478bd9Sstevel@tonic-gate 		print_mappings(lfd);
9967c478bd9Sstevel@tonic-gate 	(void) close(lfd);
9977c478bd9Sstevel@tonic-gate 	return (E_SUCCESS);
9987c478bd9Sstevel@tonic-gate }
999