1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1999-2000 by Sun Microsystems, Inc. 24 * All rights reserved. 25 * 26 * lofiadm - administer lofi(7d). Very simple, add and remove file<->device 27 * associations, and display status. All the ioctls are private between 28 * lofi and lofiadm, and so are very simple - device information is 29 * communicated via a minor number. 30 */ 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 #include <sys/types.h> 35 #include <sys/param.h> 36 #include <sys/lofi.h> 37 #include <sys/stat.h> 38 #include <stdio.h> 39 #include <fcntl.h> 40 #include <locale.h> 41 #include <string.h> 42 #include <errno.h> 43 #include <stdlib.h> 44 #include <unistd.h> 45 #include <stropts.h> 46 #include "utils.h" 47 48 static const char USAGE[] = 49 "Usage: %s -a file [ device ]\n" 50 " %s -d file | device \n" 51 " %s [ device | file ]\n"; 52 53 static const char *pname; 54 static int addflag = 0; 55 static int deleteflag = 0; 56 static int errflag = 0; 57 58 #define FORMAT "%-20s %s\n" 59 60 /* 61 * Print the list of all the mappings. Including a header. 62 */ 63 static void 64 print_mappings(int fd) 65 { 66 struct lofi_ioctl li; 67 int minor; 68 int maxminor; 69 char path[MAXPATHLEN + 1]; 70 71 li.li_minor = 0; 72 if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) { 73 perror("ioctl"); 74 exit(E_ERROR); 75 } 76 77 maxminor = li.li_minor; 78 79 (void) printf(FORMAT, "Block Device", "File"); 80 for (minor = 1; minor <= maxminor; minor++) { 81 li.li_minor = minor; 82 if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) { 83 if (errno == ENXIO) 84 continue; 85 perror("ioctl"); 86 break; 87 } 88 (void) snprintf(path, sizeof (path), "/dev/%s/%d", 89 LOFI_BLOCK_NAME, minor); 90 (void) printf(FORMAT, path, li.li_filename); 91 } 92 } 93 94 static void 95 usage(void) 96 { 97 (void) fprintf(stderr, gettext(USAGE), pname, pname, pname); 98 exit(E_USAGE); 99 } 100 101 /* 102 * Translate a lofi device name to a minor number. We might be asked 103 * to do this when there is no association (such as when the user specifies 104 * a particular device), so we can only look at the string. 105 */ 106 static int 107 name_to_minor(const char *devicename) 108 { 109 int minor; 110 111 if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) { 112 return (minor); 113 } 114 if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) { 115 return (minor); 116 } 117 return (0); 118 } 119 120 /* 121 * This might be the first time we've used this minor number. If so, 122 * it might also be that the /dev links are in the process of being created 123 * by devfsadmd (or that they'll be created "soon"). We cannot return 124 * until they're there or the invoker of lofiadm might try to use them 125 * and not find them. This can happen if a shell script is running on 126 * an MP. 127 */ 128 static int sleeptime = 2; /* number of seconds to sleep between stat's */ 129 static int maxsleep = 120; /* maximum number of seconds to sleep */ 130 131 static void 132 wait_until_dev_complete(int minor) 133 { 134 struct stat64 buf; 135 int cursleep; 136 char blkpath[MAXPATHLEN + 1]; 137 char charpath[MAXPATHLEN + 1]; 138 139 (void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d", 140 LOFI_BLOCK_NAME, minor); 141 (void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d", 142 LOFI_CHAR_NAME, minor); 143 cursleep = 0; 144 while (cursleep < maxsleep) { 145 if ((stat64(blkpath, &buf) == -1) || 146 (stat64(charpath, &buf) == -1)) { 147 (void) sleep(sleeptime); 148 cursleep += sleeptime; 149 continue; 150 } 151 return; 152 } 153 /* one last try */ 154 if (stat64(blkpath, &buf) == -1) { 155 die(gettext("%s was not created"), blkpath); 156 } 157 if (stat64(charpath, &buf) == -1) { 158 die(gettext("%s was not created"), charpath); 159 } 160 } 161 162 /* 163 * Add a device association. If devicename is NULL, let the driver 164 * pick a device. 165 */ 166 static void 167 add_mapping(int lfd, const char *devicename, const char *filename) 168 { 169 struct lofi_ioctl li; 170 int minor; 171 172 if (devicename == NULL) { 173 /* pick one */ 174 li.li_minor = 0; 175 (void) strcpy(li.li_filename, filename); 176 minor = ioctl(lfd, LOFI_MAP_FILE, &li); 177 if (minor == -1) { 178 die(gettext("could not map file %s"), filename); 179 } 180 wait_until_dev_complete(minor); 181 /* print one picked */ 182 (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor); 183 return; 184 } 185 /* use device we were given */ 186 minor = name_to_minor(devicename); 187 if (minor == 0) { 188 die(gettext("malformed device name %s\n"), devicename); 189 } 190 (void) strcpy(li.li_filename, filename); 191 li.li_minor = minor; 192 if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) { 193 die(gettext("could not map file %s to %s"), filename, 194 devicename); 195 } 196 wait_until_dev_complete(minor); 197 } 198 199 /* 200 * Remove an association. Delete by device name if non-NULL, or by 201 * filename otherwise. 202 */ 203 static void 204 delete_mapping(int lfd, const char *devicename, const char *filename) 205 { 206 struct lofi_ioctl li; 207 208 if (devicename == NULL) { 209 /* delete by filename */ 210 (void) strcpy(li.li_filename, filename); 211 li.li_minor = 0; 212 if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) { 213 die(gettext("could not unmap file %s"), filename); 214 } 215 return; 216 } 217 /* delete by device */ 218 219 li.li_minor = name_to_minor(devicename); 220 if (li.li_minor == 0) { 221 die(gettext("malformed device name %s\n"), devicename); 222 } 223 if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) { 224 die(gettext("could not unmap device %s"), devicename); 225 } 226 } 227 228 static void 229 print_one_mapping(int lfd, const char *devicename, const char *filename) 230 { 231 struct lofi_ioctl li; 232 233 if (devicename == NULL) { 234 /* given filename, print devicename */ 235 li.li_minor = 0; 236 (void) strcpy(li.li_filename, filename); 237 if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) { 238 die(gettext("could not find device for %s"), filename); 239 } 240 (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor); 241 return; 242 } 243 244 /* given devicename, print filename */ 245 li.li_minor = name_to_minor(devicename); 246 if (li.li_minor == 0) { 247 die(gettext("malformed device name %s\n"), devicename); 248 } 249 if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) { 250 die(gettext("could not find filename for %s"), devicename); 251 } 252 (void) printf("%s\n", li.li_filename); 253 } 254 255 int 256 main(int argc, char *argv[]) 257 { 258 int lfd; 259 int c; 260 int error; 261 struct stat64 buf; 262 const char *devicename = NULL; 263 const char *filename = NULL; 264 int openflag; 265 int minor; 266 int fd = -1; 267 static char *lofictl = "/dev/" LOFI_CTL_NAME; 268 269 pname = getpname(argv[0]); 270 271 (void) setlocale(LC_ALL, ""); 272 (void) textdomain(TEXT_DOMAIN); 273 274 while ((c = getopt(argc, argv, "a:d:")) != EOF) { 275 switch (c) { 276 case 'a': 277 addflag = 1; 278 filename = optarg; 279 fd = open64(filename, O_RDONLY); 280 if (fd == -1) { 281 die(gettext("open: %s"), filename); 282 } 283 error = fstat64(fd, &buf); 284 if (error == -1) { 285 die(gettext("fstat: %s"), filename); 286 } else if (!S_ISLOFIABLE(buf.st_mode)) { 287 die(gettext("%s is not a regular file, " 288 "block, or character device\n"), 289 filename); 290 } else if ((buf.st_size % DEV_BSIZE) != 0) { 291 die(gettext("size of %s is not a multiple " 292 "of %d\n"), 293 filename, DEV_BSIZE); 294 } 295 (void) close(fd); 296 minor = name_to_minor(filename); 297 if (minor != 0) { 298 die(gettext("cannot use " LOFI_DRIVER_NAME 299 " on itself\n"), devicename); 300 } 301 if (((argc - optind) > 0) && (*argv[optind] != '-')) { 302 /* optional device */ 303 devicename = argv[optind]; 304 optind++; 305 } 306 break; 307 case 'd': 308 deleteflag = 1; 309 310 minor = name_to_minor(optarg); 311 if (minor != 0) 312 devicename = optarg; 313 else 314 filename = optarg; 315 break; 316 case '?': 317 default: 318 errflag = 1; 319 break; 320 } 321 } 322 if (errflag || (addflag && deleteflag)) 323 usage(); 324 325 switch (argc - optind) { 326 case 0: /* no more args */ 327 break; 328 case 1: /* one arg without options means print the association */ 329 if (addflag || deleteflag) 330 usage(); 331 minor = name_to_minor(argv[optind]); 332 if (minor != 0) 333 devicename = argv[optind]; 334 else 335 filename = argv[optind]; 336 break; 337 default: 338 usage(); 339 break; 340 } 341 342 if (filename && !valid_abspath(filename)) 343 exit(E_ERROR); 344 345 /* 346 * Here, we know the arguments are correct, the filename is an 347 * absolute path, it exists and is a regular file. We don't yet 348 * know that the device name is ok or not. 349 */ 350 /* 351 * Now to the real work. 352 */ 353 openflag = O_EXCL; 354 if (addflag || deleteflag) 355 openflag |= O_RDWR; 356 else 357 openflag |= O_RDONLY; 358 lfd = open(lofictl, openflag); 359 if (lfd == -1) { 360 if ((errno == EPERM) || (errno == EACCES)) { 361 die("you do not have permission to perform " 362 "that operation.\n"); 363 } else { 364 die("%s", lofictl); 365 } 366 /*NOTREACHED*/ 367 } 368 if (addflag) 369 add_mapping(lfd, devicename, filename); 370 else if (deleteflag) 371 delete_mapping(lfd, devicename, filename); 372 else if (filename || devicename) 373 print_one_mapping(lfd, devicename, filename); 374 else 375 print_mappings(lfd); 376 (void) close(lfd); 377 return (E_SUCCESS); 378 } 379