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 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /*LINTLIBRARY*/ 28 29 #include <sys/types.h> 30 #include <stdio.h> 31 #include <errno.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <stdlib.h> 35 #include <sys/param.h> 36 #include <sys/stat.h> 37 #include <sys/sysmacros.h> 38 #include <sys/vfstab.h> 39 #include <sys/lofi.h> 40 #include <sys/ramdisk.h> 41 #include <sys/fssnap_if.h> 42 #include "libadm.h" 43 44 /* 45 * Globals: 46 * getfullrawname - returns a fully-qualified raw device name 47 * getfullblkname - returns a fully-qualified block device name 48 * 49 * These two routines take a device pathname and return corresponding 50 * the raw or block device name. 51 * 52 * First the device name is fully qualified: 53 * If the device name does not start with a '/' or starts with 54 * './' then the current working directory is added to the beginning 55 * of the pathname. 56 * 57 * If the device name starts with a '../' then all but the last 58 * sub-directory of the current working directory is added to the 59 * the beginning of the pathname. 60 * 61 * Second if the fully-qualified device name given is the raw/block 62 * device that is being asked for then the fully-qualified device name is 63 * returned. 64 * 65 * Third if an entry is found in /etc/vfstab which matches the given name 66 * then the corresponding raw/block device is returned. This allows 67 * non-standard names to be converted (.i.e., block device "/dev/joe" can 68 * be converted to raw device "/dev/fred", via this mechanism). 69 * 70 * Last standard names are converted. Standard names are those 71 * with a '/dsk/' for block or '/rdsk/' for raw sub-directory components 72 * in the device name. Or, the filename component has an 'r' for raw or 73 * no 'r' for block (e.g., rsd0a <=> sd0a). 74 * 75 * Caveat: 76 * It is assumed that the block and raw devices have the 77 * same device number, and this is used to verify the conversion 78 * happened corretly. If this happens not to be true, due to mapping 79 * of minor numbers or sometheing, then entries can be put in the 80 * the '/etc/vfstab' file to over-ride this checking. 81 * 82 * 83 * Return Values: 84 * raw/block device name - (depending on which routine is used) 85 * null string - When the conversion failed 86 * null pointer - malloc problems 87 * 88 * It is up to the user of these routines to free the memory, of 89 * the device name or null string returned by these library routines, 90 * when appropriate by the application. 91 */ 92 #define GET_BLK 0 93 #define GET_RAW 1 94 95 static int test_if_blk(char *, dev_t); 96 static int test_if_raw(char *, dev_t); 97 static char *getblkcomplete(char *, struct stat64 *); 98 static char *getrawcomplete(char *, struct stat64 *); 99 100 /* 101 * getfullname() - Builds a fully qualified pathname. 102 * This handles . and .. as well. 103 * NOTE: This is different from realpath(3C) because 104 * it does not follow links. 105 */ 106 static char * 107 getfullname(char *path) 108 { 109 char cwd[MAXPATHLEN]; 110 char *c; 111 char *wa; 112 size_t len; 113 114 if (*path == '/') 115 return (strdup(path)); 116 117 if (getcwd(cwd, sizeof (cwd)) == NULL) 118 return (strdup("")); 119 120 /* handle . and .. */ 121 if (strncmp(path, "./", 2) == 0) { 122 /* strip the ./ from the given path */ 123 path += 2; 124 } else if (strncmp(path, "../", 3) == 0) { 125 /* strip the last directory component from cwd */ 126 c = strrchr(cwd, '/'); 127 *c = '\0'; 128 129 /* strip the ../ from the given path */ 130 path += 3; 131 } 132 133 /* 134 * Adding 2 takes care of slash and null terminator. 135 */ 136 len = strlen(cwd) + strlen(path) + 2; 137 if ((wa = malloc(len)) == NULL) 138 return (NULL); 139 140 (void) strcpy(wa, cwd); 141 (void) strcat(wa, "/"); 142 (void) strcat(wa, path); 143 144 return (wa); 145 } 146 147 /* 148 * test the path/fname to see if is blk special 149 */ 150 static int 151 test_if_blk(char *new_path, dev_t raw_dev) 152 { 153 struct stat64 buf; 154 155 /* check if we got a char special file */ 156 if (stat64(new_path, &buf) != 0) 157 return (0); 158 159 if (!S_ISBLK(buf.st_mode)) 160 return (0); 161 162 if (raw_dev != buf.st_rdev) 163 return (0); 164 165 return (1); 166 } 167 168 /* 169 * test the path/fname to see if is char special 170 */ 171 static int 172 test_if_raw(char *new_path, dev_t blk_dev) 173 { 174 struct stat64 buf; 175 176 /* check if we got a char special file */ 177 if (stat64(new_path, &buf) != 0) 178 return (0); 179 180 if (!S_ISCHR(buf.st_mode)) 181 return (0); 182 183 if (blk_dev != buf.st_rdev) 184 return (0); 185 186 return (1); 187 } 188 189 /* 190 * complete getblkrawname() for blk->raw to handle volmgt devices 191 */ 192 193 static char * 194 getblkcomplete(char *cp, struct stat64 *dat) 195 { 196 char *dp; 197 char *new_path; 198 char c; 199 200 /* ok, so we either have a bad device or a floppy */ 201 202 /* try the rfd# form */ 203 if ((dp = strstr(cp, "/rfd")) != NULL) { 204 if ((new_path = malloc(strlen(cp))) == NULL) 205 return (NULL); 206 207 c = *++dp; /* save the 'r' */ 208 *dp = '\0'; /* replace it with a null */ 209 (void) strcpy(new_path, cp); /* save first part of it */ 210 *dp++ = c; /* give the 'r' back */ 211 (void) strcat(new_path, dp); /* copy, skipping the 'r' */ 212 213 if (test_if_blk(new_path, dat->st_rdev)) 214 return (new_path); 215 216 free(new_path); 217 return (strdup("")); 218 } 219 220 /* try the rdiskette form */ 221 if ((dp = strstr(cp, "/rdiskette")) != NULL) { 222 if ((new_path = malloc(strlen(cp))) == NULL) 223 return (NULL); 224 225 c = *++dp; /* save the 'r' */ 226 *dp = '\0'; /* replace it with a null */ 227 (void) strcpy(new_path, cp); /* save first part of it */ 228 *dp++ = c; /* give the 'r' back */ 229 (void) strcat(new_path, dp); /* copy, skipping the 'r' */ 230 231 if (test_if_blk(new_path, dat->st_rdev)) 232 return (new_path); 233 234 free(new_path); 235 return (strdup("")); 236 } 237 238 /* no match found */ 239 return (strdup("")); 240 } 241 242 /* 243 * complete getfullrawname() for raw->blk to handle volmgt devices 244 */ 245 246 static char * 247 getrawcomplete(char *cp, struct stat64 *dat) 248 { 249 char *dp; 250 char *new_path; 251 char c; 252 253 /* ok, so we either have a bad device or a floppy */ 254 255 /* try the fd# form */ 256 if ((dp = strstr(cp, "/fd")) != NULL) { 257 /* malloc path for new_path to hold raw */ 258 if ((new_path = malloc(strlen(cp)+2)) == NULL) 259 return (NULL); 260 261 c = *++dp; /* save the 'f' */ 262 *dp = '\0'; /* replace it with a null */ 263 (void) strcpy(new_path, cp); /* save first part of it */ 264 *dp = c; /* put the 'f' back */ 265 (void) strcat(new_path, "r"); /* insert an 'r' */ 266 (void) strcat(new_path, dp); /* copy the rest */ 267 268 if (test_if_raw(new_path, dat->st_rdev)) 269 return (new_path); 270 271 free(new_path); 272 } 273 274 /* try the diskette form */ 275 if ((dp = strstr(cp, "/diskette")) != NULL) { 276 /* malloc path for new_path to hold raw */ 277 if ((new_path = malloc(strlen(cp)+2)) == NULL) 278 return (NULL); 279 280 c = *++dp; /* save at 'd' */ 281 *dp = '\0'; /* replace it with a null */ 282 (void) strcpy(new_path, cp); /* save first part */ 283 *dp = c; /* put the 'd' back */ 284 (void) strcat(new_path, "r"); /* insert an 'r' */ 285 (void) strcat(new_path, dp); /* copy the rest */ 286 287 if (test_if_raw(new_path, dat->st_rdev)) 288 return (new_path); 289 290 free(new_path); 291 return (strdup("")); 292 } 293 294 /* failed to build raw name, return null string */ 295 return (strdup("")); 296 297 298 299 } 300 301 static char * 302 getvfsspecial(char *path, int raw_special) 303 { 304 FILE *fp; 305 struct vfstab vp; 306 struct vfstab ref_vp; 307 308 if ((fp = fopen("/etc/vfstab", "r")) == NULL) 309 return (NULL); 310 311 (void) memset(&ref_vp, 0, sizeof (struct vfstab)); 312 313 if (raw_special) 314 ref_vp.vfs_special = path; 315 else 316 ref_vp.vfs_fsckdev = path; 317 318 if (getvfsany(fp, &vp, &ref_vp)) { 319 (void) fclose(fp); 320 return (NULL); 321 } 322 323 (void) fclose(fp); 324 325 if (raw_special) 326 return (vp.vfs_fsckdev); 327 328 return (vp.vfs_special); 329 } 330 331 /* 332 * change the device name to a block device name 333 */ 334 char * 335 getfullblkname(char *cp) 336 { 337 struct stat64 buf; 338 char *dp; 339 char *new_path; 340 dev_t raw_dev; 341 342 if (cp == NULL) 343 return (strdup("")); 344 345 /* 346 * Create a fully qualified name. 347 */ 348 if ((cp = getfullname(cp)) == NULL) 349 return (NULL); 350 351 if (*cp == '\0') 352 return (cp); 353 354 if (stat64(cp, &buf) != 0) { 355 free(cp); 356 return (strdup("")); 357 } 358 359 if (S_ISBLK(buf.st_mode)) 360 return (cp); 361 362 if (!S_ISCHR(buf.st_mode)) { 363 free(cp); 364 return (strdup("")); 365 } 366 367 if ((dp = getvfsspecial(cp, GET_BLK)) != NULL) { 368 free(cp); 369 return (strdup(dp)); 370 } 371 372 raw_dev = buf.st_rdev; 373 374 /* 375 * We have a raw device name, go find the block name. 376 */ 377 if ((dp = strstr(cp, "/rdsk/")) == NULL && 378 (dp = strstr(cp, "/" LOFI_CHAR_NAME "/")) == NULL && 379 (dp = strstr(cp, "/" RD_CHAR_NAME "/")) == NULL && 380 (dp = strstr(cp, "/" SNAP_CHAR_NAME "/")) == NULL && 381 (dp = strrchr(cp, '/')) == NULL) { 382 /* this is not really possible */ 383 free(cp); 384 return (strdup("")); 385 } 386 dp++; 387 if (*dp != 'r') { 388 dp = getblkcomplete(cp, &buf); 389 free(cp); 390 return (dp); 391 } 392 if ((new_path = malloc(strlen(cp))) == NULL) { 393 free(cp); 394 return (NULL); 395 } 396 (void) strncpy(new_path, cp, dp - cp); 397 398 /* fill in the rest of the unraw name */ 399 (void) strcpy(new_path + (dp - cp), dp + 1); 400 401 if (test_if_blk(new_path, raw_dev)) { 402 free(cp); 403 /* block name was found, return it here */ 404 return (new_path); 405 } 406 free(new_path); 407 408 dp = getblkcomplete(cp, &buf); 409 free(cp); 410 return (dp); 411 } 412 413 /* 414 * change the device name to a raw devname 415 */ 416 char * 417 getfullrawname(char *cp) 418 { 419 struct stat64 buf; 420 char *dp; 421 char *new_path; 422 dev_t blk_dev; 423 424 if (cp == NULL) 425 return (strdup("")); 426 427 /* 428 * Create a fully qualified name. 429 */ 430 if ((cp = getfullname(cp)) == NULL) 431 return (NULL); 432 433 if (*cp == '\0') 434 return (cp); 435 436 if (stat64(cp, &buf) != 0) { 437 free(cp); 438 return (strdup("")); 439 } 440 441 if (S_ISCHR(buf.st_mode)) 442 return (cp); 443 444 if (!S_ISBLK(buf.st_mode)) { 445 free(cp); 446 return (strdup("")); 447 } 448 449 blk_dev = buf.st_rdev; 450 451 if ((dp = getvfsspecial(cp, GET_RAW)) != NULL) { 452 free(cp); 453 return (strdup(dp)); 454 } 455 456 /* 457 * We have a block device name, go find the raw name. 458 */ 459 if ((dp = strstr(cp, "/dsk/")) == NULL && 460 (dp = strstr(cp, "/" LOFI_BLOCK_NAME "/")) == NULL && 461 (dp = strstr(cp, "/" RD_BLOCK_NAME "/")) == NULL && 462 (dp = strstr(cp, "/" SNAP_BLOCK_NAME "/")) == NULL && 463 (dp = strrchr(cp, '/')) == NULL) { 464 /* this is not really possible */ 465 free(cp); 466 return (strdup("")); 467 } 468 dp++; 469 470 if ((new_path = malloc(strlen(cp)+2)) == NULL) { 471 free(cp); 472 return (NULL); 473 } 474 (void) strncpy(new_path, cp, dp - cp); 475 /* fill in the rest of the raw name */ 476 new_path[dp - cp] = 'r'; 477 (void) strcpy(new_path + (dp - cp) + 1, dp); 478 479 if (test_if_raw(new_path, blk_dev)) { 480 free(cp); 481 return (new_path); 482 } 483 free(new_path); 484 485 dp = getrawcomplete(cp, &buf); 486 free(cp); 487 return (dp); 488 } 489