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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * lofiadm - administer lofi(7d). Very simple, add and remove file<->device 26 * associations, and display status. All the ioctls are private between 27 * lofi and lofiadm, and so are very simple - device information is 28 * communicated via a minor number. 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <sys/lofi.h> 36 #include <sys/stat.h> 37 #include <stdio.h> 38 #include <fcntl.h> 39 #include <locale.h> 40 #include <string.h> 41 #include <errno.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <stropts.h> 45 #include <libdevinfo.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 di_devlink_handle_t hdl; 139 140 141 (void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d", 142 LOFI_BLOCK_NAME, minor); 143 (void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d", 144 LOFI_CHAR_NAME, minor); 145 146 /* Check if links already present */ 147 if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0) 148 return; 149 150 /* First use di_devlink_init() */ 151 if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) { 152 (void) di_devlink_fini(&hdl); 153 goto out; 154 } 155 156 /* 157 * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will 158 * only fail if the caller is non-root. In that case, wait for 159 * link creation via sysevents. 160 */ 161 cursleep = 0; 162 while (cursleep < maxsleep) { 163 if ((stat64(blkpath, &buf) == -1) || 164 (stat64(charpath, &buf) == -1)) { 165 (void) sleep(sleeptime); 166 cursleep += sleeptime; 167 continue; 168 } 169 return; 170 } 171 172 /* one last try */ 173 174 out: 175 if (stat64(blkpath, &buf) == -1) { 176 die(gettext("%s was not created"), blkpath); 177 } 178 if (stat64(charpath, &buf) == -1) { 179 die(gettext("%s was not created"), charpath); 180 } 181 } 182 183 /* 184 * Add a device association. If devicename is NULL, let the driver 185 * pick a device. 186 */ 187 static void 188 add_mapping(int lfd, const char *devicename, const char *filename) 189 { 190 struct lofi_ioctl li; 191 int minor; 192 193 if (devicename == NULL) { 194 /* pick one */ 195 li.li_minor = 0; 196 (void) strcpy(li.li_filename, filename); 197 minor = ioctl(lfd, LOFI_MAP_FILE, &li); 198 if (minor == -1) { 199 die(gettext("could not map file %s"), filename); 200 } 201 wait_until_dev_complete(minor); 202 /* print one picked */ 203 (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor); 204 return; 205 } 206 /* use device we were given */ 207 minor = name_to_minor(devicename); 208 if (minor == 0) { 209 die(gettext("malformed device name %s\n"), devicename); 210 } 211 (void) strcpy(li.li_filename, filename); 212 li.li_minor = minor; 213 if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) { 214 die(gettext("could not map file %s to %s"), filename, 215 devicename); 216 } 217 wait_until_dev_complete(minor); 218 } 219 220 /* 221 * Remove an association. Delete by device name if non-NULL, or by 222 * filename otherwise. 223 */ 224 static void 225 delete_mapping(int lfd, const char *devicename, const char *filename) 226 { 227 struct lofi_ioctl li; 228 229 if (devicename == NULL) { 230 /* delete by filename */ 231 (void) strcpy(li.li_filename, filename); 232 li.li_minor = 0; 233 if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) { 234 die(gettext("could not unmap file %s"), filename); 235 } 236 return; 237 } 238 /* delete by device */ 239 240 li.li_minor = name_to_minor(devicename); 241 if (li.li_minor == 0) { 242 die(gettext("malformed device name %s\n"), devicename); 243 } 244 if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) { 245 die(gettext("could not unmap device %s"), devicename); 246 } 247 } 248 249 static void 250 print_one_mapping(int lfd, const char *devicename, const char *filename) 251 { 252 struct lofi_ioctl li; 253 254 if (devicename == NULL) { 255 /* given filename, print devicename */ 256 li.li_minor = 0; 257 (void) strcpy(li.li_filename, filename); 258 if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) { 259 die(gettext("could not find device for %s"), filename); 260 } 261 (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor); 262 return; 263 } 264 265 /* given devicename, print filename */ 266 li.li_minor = name_to_minor(devicename); 267 if (li.li_minor == 0) { 268 die(gettext("malformed device name %s\n"), devicename); 269 } 270 if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) { 271 die(gettext("could not find filename for %s"), devicename); 272 } 273 (void) printf("%s\n", li.li_filename); 274 } 275 276 int 277 main(int argc, char *argv[]) 278 { 279 int lfd; 280 int c; 281 int error; 282 struct stat64 buf; 283 const char *devicename = NULL; 284 const char *filename = NULL; 285 int openflag; 286 int minor; 287 int fd = -1; 288 static char *lofictl = "/dev/" LOFI_CTL_NAME; 289 290 pname = getpname(argv[0]); 291 292 (void) setlocale(LC_ALL, ""); 293 (void) textdomain(TEXT_DOMAIN); 294 295 while ((c = getopt(argc, argv, "a:d:")) != EOF) { 296 switch (c) { 297 case 'a': 298 addflag = 1; 299 filename = optarg; 300 fd = open64(filename, O_RDONLY); 301 if (fd == -1) { 302 die(gettext("open: %s"), filename); 303 } 304 error = fstat64(fd, &buf); 305 if (error == -1) { 306 die(gettext("fstat: %s"), filename); 307 } else if (!S_ISLOFIABLE(buf.st_mode)) { 308 die(gettext("%s is not a regular file, " 309 "block, or character device\n"), 310 filename); 311 } else if ((buf.st_size % DEV_BSIZE) != 0) { 312 die(gettext("size of %s is not a multiple " 313 "of %d\n"), 314 filename, DEV_BSIZE); 315 } 316 (void) close(fd); 317 minor = name_to_minor(filename); 318 if (minor != 0) { 319 die(gettext("cannot use " LOFI_DRIVER_NAME 320 " on itself\n"), devicename); 321 } 322 if (((argc - optind) > 0) && (*argv[optind] != '-')) { 323 /* optional device */ 324 devicename = argv[optind]; 325 optind++; 326 } 327 break; 328 case 'd': 329 deleteflag = 1; 330 331 minor = name_to_minor(optarg); 332 if (minor != 0) 333 devicename = optarg; 334 else 335 filename = optarg; 336 break; 337 case '?': 338 default: 339 errflag = 1; 340 break; 341 } 342 } 343 if (errflag || (addflag && deleteflag)) 344 usage(); 345 346 switch (argc - optind) { 347 case 0: /* no more args */ 348 break; 349 case 1: /* one arg without options means print the association */ 350 if (addflag || deleteflag) 351 usage(); 352 minor = name_to_minor(argv[optind]); 353 if (minor != 0) 354 devicename = argv[optind]; 355 else 356 filename = argv[optind]; 357 break; 358 default: 359 usage(); 360 break; 361 } 362 363 if (filename && !valid_abspath(filename)) 364 exit(E_ERROR); 365 366 /* 367 * Here, we know the arguments are correct, the filename is an 368 * absolute path, it exists and is a regular file. We don't yet 369 * know that the device name is ok or not. 370 */ 371 /* 372 * Now to the real work. 373 */ 374 openflag = O_EXCL; 375 if (addflag || deleteflag) 376 openflag |= O_RDWR; 377 else 378 openflag |= O_RDONLY; 379 lfd = open(lofictl, openflag); 380 if (lfd == -1) { 381 if ((errno == EPERM) || (errno == EACCES)) { 382 die("you do not have permission to perform " 383 "that operation.\n"); 384 } else { 385 die("%s", lofictl); 386 } 387 /*NOTREACHED*/ 388 } 389 if (addflag) 390 add_mapping(lfd, devicename, filename); 391 else if (deleteflag) 392 delete_mapping(lfd, devicename, filename); 393 else if (filename || devicename) 394 print_one_mapping(lfd, devicename, filename); 395 else 396 print_mappings(lfd); 397 (void) close(lfd); 398 return (E_SUCCESS); 399 } 400