diff options
Diffstat (limited to 'usr/src/cmd/lofiadm/main.c')
| -rw-r--r-- | usr/src/cmd/lofiadm/main.c | 378 |
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); +} |
