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