summaryrefslogtreecommitdiff
path: root/usr/src/cmd/lofiadm/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/lofiadm/main.c')
-rw-r--r--usr/src/cmd/lofiadm/main.c378
1 files changed, 378 insertions, 0 deletions
diff --git a/usr/src/cmd/lofiadm/main.c b/usr/src/cmd/lofiadm/main.c
new file mode 100644
index 0000000000..5f5af6c0f6
--- /dev/null
+++ b/usr/src/cmd/lofiadm/main.c
@@ -0,0 +1,378 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * lofiadm - administer lofi(7d). Very simple, add and remove file<->device
+ * associations, and display status. All the ioctls are private between
+ * lofi and lofiadm, and so are very simple - device information is
+ * communicated via a minor number.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/lofi.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stropts.h>
+#include "utils.h"
+
+static const char USAGE[] =
+ "Usage: %s -a file [ device ]\n"
+ " %s -d file | device \n"
+ " %s [ device | file ]\n";
+
+static const char *pname;
+static int addflag = 0;
+static int deleteflag = 0;
+static int errflag = 0;
+
+#define FORMAT "%-20s %s\n"
+
+/*
+ * Print the list of all the mappings. Including a header.
+ */
+static void
+print_mappings(int fd)
+{
+ struct lofi_ioctl li;
+ int minor;
+ int maxminor;
+ char path[MAXPATHLEN + 1];
+
+ li.li_minor = 0;
+ if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) {
+ perror("ioctl");
+ exit(E_ERROR);
+ }
+
+ maxminor = li.li_minor;
+
+ (void) printf(FORMAT, "Block Device", "File");
+ for (minor = 1; minor <= maxminor; minor++) {
+ li.li_minor = minor;
+ if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) {
+ if (errno == ENXIO)
+ continue;
+ perror("ioctl");
+ break;
+ }
+ (void) snprintf(path, sizeof (path), "/dev/%s/%d",
+ LOFI_BLOCK_NAME, minor);
+ (void) printf(FORMAT, path, li.li_filename);
+ }
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, gettext(USAGE), pname, pname, pname);
+ exit(E_USAGE);
+}
+
+/*
+ * Translate a lofi device name to a minor number. We might be asked
+ * to do this when there is no association (such as when the user specifies
+ * a particular device), so we can only look at the string.
+ */
+static int
+name_to_minor(const char *devicename)
+{
+ int minor;
+
+ if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) {
+ return (minor);
+ }
+ if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) {
+ return (minor);
+ }
+ return (0);
+}
+
+/*
+ * This might be the first time we've used this minor number. If so,
+ * it might also be that the /dev links are in the process of being created
+ * by devfsadmd (or that they'll be created "soon"). We cannot return
+ * until they're there or the invoker of lofiadm might try to use them
+ * and not find them. This can happen if a shell script is running on
+ * an MP.
+ */
+static int sleeptime = 2; /* number of seconds to sleep between stat's */
+static int maxsleep = 120; /* maximum number of seconds to sleep */
+
+static void
+wait_until_dev_complete(int minor)
+{
+ struct stat64 buf;
+ int cursleep;
+ char blkpath[MAXPATHLEN + 1];
+ char charpath[MAXPATHLEN + 1];
+
+ (void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d",
+ LOFI_BLOCK_NAME, minor);
+ (void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d",
+ LOFI_CHAR_NAME, minor);
+ cursleep = 0;
+ while (cursleep < maxsleep) {
+ if ((stat64(blkpath, &buf) == -1) ||
+ (stat64(charpath, &buf) == -1)) {
+ (void) sleep(sleeptime);
+ cursleep += sleeptime;
+ continue;
+ }
+ return;
+ }
+ /* one last try */
+ if (stat64(blkpath, &buf) == -1) {
+ die(gettext("%s was not created"), blkpath);
+ }
+ if (stat64(charpath, &buf) == -1) {
+ die(gettext("%s was not created"), charpath);
+ }
+}
+
+/*
+ * Add a device association. If devicename is NULL, let the driver
+ * pick a device.
+ */
+static void
+add_mapping(int lfd, const char *devicename, const char *filename)
+{
+ struct lofi_ioctl li;
+ int minor;
+
+ if (devicename == NULL) {
+ /* pick one */
+ li.li_minor = 0;
+ (void) strcpy(li.li_filename, filename);
+ minor = ioctl(lfd, LOFI_MAP_FILE, &li);
+ if (minor == -1) {
+ die(gettext("could not map file %s"), filename);
+ }
+ wait_until_dev_complete(minor);
+ /* print one picked */
+ (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor);
+ return;
+ }
+ /* use device we were given */
+ minor = name_to_minor(devicename);
+ if (minor == 0) {
+ die(gettext("malformed device name %s\n"), devicename);
+ }
+ (void) strcpy(li.li_filename, filename);
+ li.li_minor = minor;
+ if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) {
+ die(gettext("could not map file %s to %s"), filename,
+ devicename);
+ }
+ wait_until_dev_complete(minor);
+}
+
+/*
+ * Remove an association. Delete by device name if non-NULL, or by
+ * filename otherwise.
+ */
+static void
+delete_mapping(int lfd, const char *devicename, const char *filename)
+{
+ struct lofi_ioctl li;
+
+ if (devicename == NULL) {
+ /* delete by filename */
+ (void) strcpy(li.li_filename, filename);
+ li.li_minor = 0;
+ if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) {
+ die(gettext("could not unmap file %s"), filename);
+ }
+ return;
+ }
+ /* delete by device */
+
+ li.li_minor = name_to_minor(devicename);
+ if (li.li_minor == 0) {
+ die(gettext("malformed device name %s\n"), devicename);
+ }
+ if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) {
+ die(gettext("could not unmap device %s"), devicename);
+ }
+}
+
+static void
+print_one_mapping(int lfd, const char *devicename, const char *filename)
+{
+ struct lofi_ioctl li;
+
+ if (devicename == NULL) {
+ /* given filename, print devicename */
+ li.li_minor = 0;
+ (void) strcpy(li.li_filename, filename);
+ if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) {
+ die(gettext("could not find device for %s"), filename);
+ }
+ (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor);
+ return;
+ }
+
+ /* given devicename, print filename */
+ li.li_minor = name_to_minor(devicename);
+ if (li.li_minor == 0) {
+ die(gettext("malformed device name %s\n"), devicename);
+ }
+ if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) {
+ die(gettext("could not find filename for %s"), devicename);
+ }
+ (void) printf("%s\n", li.li_filename);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int lfd;
+ int c;
+ int error;
+ struct stat64 buf;
+ const char *devicename = NULL;
+ const char *filename = NULL;
+ int openflag;
+ int minor;
+ int fd = -1;
+ static char *lofictl = "/dev/" LOFI_CTL_NAME;
+
+ pname = getpname(argv[0]);
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((c = getopt(argc, argv, "a:d:")) != EOF) {
+ switch (c) {
+ case 'a':
+ addflag = 1;
+ filename = optarg;
+ fd = open64(filename, O_RDONLY);
+ if (fd == -1) {
+ die(gettext("open: %s"), filename);
+ }
+ error = fstat64(fd, &buf);
+ if (error == -1) {
+ die(gettext("fstat: %s"), filename);
+ } else if (!S_ISLOFIABLE(buf.st_mode)) {
+ die(gettext("%s is not a regular file, "
+ "block, or character device\n"),
+ filename);
+ } else if ((buf.st_size % DEV_BSIZE) != 0) {
+ die(gettext("size of %s is not a multiple "
+ "of %d\n"),
+ filename, DEV_BSIZE);
+ }
+ (void) close(fd);
+ minor = name_to_minor(filename);
+ if (minor != 0) {
+ die(gettext("cannot use " LOFI_DRIVER_NAME
+ " on itself\n"), devicename);
+ }
+ if (((argc - optind) > 0) && (*argv[optind] != '-')) {
+ /* optional device */
+ devicename = argv[optind];
+ optind++;
+ }
+ break;
+ case 'd':
+ deleteflag = 1;
+
+ minor = name_to_minor(optarg);
+ if (minor != 0)
+ devicename = optarg;
+ else
+ filename = optarg;
+ break;
+ case '?':
+ default:
+ errflag = 1;
+ break;
+ }
+ }
+ if (errflag || (addflag && deleteflag))
+ usage();
+
+ switch (argc - optind) {
+ case 0: /* no more args */
+ break;
+ case 1: /* one arg without options means print the association */
+ if (addflag || deleteflag)
+ usage();
+ minor = name_to_minor(argv[optind]);
+ if (minor != 0)
+ devicename = argv[optind];
+ else
+ filename = argv[optind];
+ break;
+ default:
+ usage();
+ break;
+ }
+
+ if (filename && !valid_abspath(filename))
+ exit(E_ERROR);
+
+ /*
+ * Here, we know the arguments are correct, the filename is an
+ * absolute path, it exists and is a regular file. We don't yet
+ * know that the device name is ok or not.
+ */
+ /*
+ * Now to the real work.
+ */
+ openflag = O_EXCL;
+ if (addflag || deleteflag)
+ openflag |= O_RDWR;
+ else
+ openflag |= O_RDONLY;
+ lfd = open(lofictl, openflag);
+ if (lfd == -1) {
+ if ((errno == EPERM) || (errno == EACCES)) {
+ die("you do not have permission to perform "
+ "that operation.\n");
+ } else {
+ die("%s", lofictl);
+ }
+ /*NOTREACHED*/
+ }
+ if (addflag)
+ add_mapping(lfd, devicename, filename);
+ else if (deleteflag)
+ delete_mapping(lfd, devicename, filename);
+ else if (filename || devicename)
+ print_one_mapping(lfd, devicename, filename);
+ else
+ print_mappings(lfd);
+ (void) close(lfd);
+ return (E_SUCCESS);
+}