1 /* device.c - Some helper functions for OS devices and BIOS drives */ 2 /* 3 * GRUB -- GRand Unified Bootloader 4 * Copyright (C) 1999,2000,2001,2002,2003,2004,2005 Free Software Foundation, Inc. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 21 /* Try to use glibc's transparant LFS support. */ 22 #define _LARGEFILE_SOURCE 1 23 /* lseek becomes synonymous with lseek64. */ 24 #define _FILE_OFFSET_BITS 64 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <ctype.h> 30 #include <assert.h> 31 #include <unistd.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <fcntl.h> 35 #include <errno.h> 36 #include <limits.h> 37 #include <stdarg.h> 38 39 #ifdef __linux__ 40 # if !defined(__GLIBC__) || \ 41 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))) 42 /* Maybe libc doesn't have large file support. */ 43 # include <linux/unistd.h> /* _llseek */ 44 # endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */ 45 # include <sys/ioctl.h> /* ioctl */ 46 # ifndef HDIO_GETGEO 47 # define HDIO_GETGEO 0x0301 /* get device geometry */ 48 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is 49 defined. */ 50 struct hd_geometry 51 { 52 unsigned char heads; 53 unsigned char sectors; 54 unsigned short cylinders; 55 unsigned long start; 56 }; 57 # endif /* ! HDIO_GETGEO */ 58 # ifndef FLOPPY_MAJOR 59 # define FLOPPY_MAJOR 2 /* the major number for floppy */ 60 # endif /* ! FLOPPY_MAJOR */ 61 # ifndef MAJOR 62 # define MAJOR(dev) \ 63 ({ \ 64 unsigned long long __dev = (dev); \ 65 (unsigned) ((__dev >> 8) & 0xfff) \ 66 | ((unsigned int) (__dev >> 32) & ~0xfff); \ 67 }) 68 # endif /* ! MAJOR */ 69 # ifndef CDROM_GET_CAPABILITY 70 # define CDROM_GET_CAPABILITY 0x5331 /* get capabilities */ 71 # endif /* ! CDROM_GET_CAPABILITY */ 72 # ifndef BLKGETSIZE 73 # define BLKGETSIZE _IO(0x12,96) /* return device size */ 74 # endif /* ! BLKGETSIZE */ 75 #endif /* __linux__ */ 76 77 /* Use __FreeBSD_kernel__ instead of __FreeBSD__ for compatibility with 78 kFreeBSD-based non-FreeBSD systems (e.g. GNU/kFreeBSD) */ 79 #if defined(__FreeBSD__) && ! defined(__FreeBSD_kernel__) 80 # define __FreeBSD_kernel__ 81 #endif 82 #ifdef __FreeBSD_kernel__ 83 /* Obtain version of kFreeBSD headers */ 84 # include <osreldate.h> 85 # ifndef __FreeBSD_kernel_version 86 # define __FreeBSD_kernel_version __FreeBSD_version 87 # endif 88 89 /* Runtime detection of kernel */ 90 # include <sys/utsname.h> 91 int 92 get_kfreebsd_version () 93 { 94 struct utsname uts; 95 int major; int minor, v[2]; 96 97 uname (&uts); 98 sscanf (uts.release, "%d.%d", &major, &minor); 99 100 if (major >= 9) 101 major = 9; 102 if (major >= 5) 103 { 104 v[0] = minor/10; v[1] = minor%10; 105 } 106 else 107 { 108 v[0] = minor%10; v[1] = minor/10; 109 } 110 return major*100000+v[0]*10000+v[1]*1000; 111 } 112 #endif /* __FreeBSD_kernel__ */ 113 114 #if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) 115 # include <sys/ioctl.h> /* ioctl */ 116 # include <sys/disklabel.h> 117 # include <sys/cdio.h> /* CDIOCCLRDEBUG */ 118 # if defined(__FreeBSD_kernel__) 119 # include <sys/param.h> 120 # if __FreeBSD_kernel_version >= 500040 121 # include <sys/disk.h> 122 # endif 123 # endif /* __FreeBSD_kernel__ */ 124 #endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */ 125 126 #if defined(__sun) 127 # include <sys/dkio.h> 128 #endif /* __sun */ 129 130 #ifdef HAVE_OPENDISK 131 # include <util.h> 132 #endif /* HAVE_OPENDISK */ 133 134 #define WITHOUT_LIBC_STUBS 1 135 #include <shared.h> 136 #include <device.h> 137 138 /* Get the geometry of a drive DRIVE. */ 139 void 140 get_drive_geometry (struct geometry *geom, char **map, int drive) 141 { 142 int fd; 143 144 if (geom->flags == -1) 145 { 146 fd = open (map[drive], O_RDONLY); 147 assert (fd >= 0); 148 } 149 else 150 fd = geom->flags; 151 152 /* XXX This is the default size. */ 153 geom->sector_size = SECTOR_SIZE; 154 155 #if defined(__linux__) 156 /* Linux */ 157 { 158 struct hd_geometry hdg; 159 unsigned long nr; 160 161 if (ioctl (fd, HDIO_GETGEO, &hdg)) 162 goto fail; 163 164 if (ioctl (fd, BLKGETSIZE, &nr)) 165 goto fail; 166 167 /* Got the geometry, so save it. */ 168 geom->cylinders = hdg.cylinders; 169 geom->heads = hdg.heads; 170 geom->sectors = hdg.sectors; 171 geom->total_sectors = nr; 172 173 goto success; 174 } 175 176 #elif defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) 177 # if defined(__FreeBSD_kernel__) && __FreeBSD_kernel_version >= 500040 178 /* kFreeBSD version 5 or later */ 179 if (get_kfreebsd_version () >= 500040) 180 { 181 unsigned int sector_size; 182 off_t media_size; 183 unsigned int tmp; 184 185 if(ioctl (fd, DIOCGSECTORSIZE, §or_size) != 0) 186 sector_size = 512; 187 188 if (ioctl (fd, DIOCGMEDIASIZE, &media_size) != 0) 189 goto fail; 190 191 geom->total_sectors = media_size / sector_size; 192 193 if (ioctl (fd, DIOCGFWSECTORS, &tmp) == 0) 194 geom->sectors = tmp; 195 else 196 geom->sectors = 63; 197 if (ioctl (fd, DIOCGFWHEADS, &tmp) == 0) 198 geom->heads = tmp; 199 else if (geom->total_sectors <= 63 * 1 * 1024) 200 geom->heads = 1; 201 else if (geom->total_sectors <= 63 * 16 * 1024) 202 geom->heads = 16; 203 else 204 geom->heads = 255; 205 206 geom->cylinders = (geom->total_sectors 207 / geom->heads 208 / geom->sectors); 209 210 goto success; 211 } 212 else 213 #endif /* defined(__FreeBSD_kernel__) && __FreeBSD_kernel_version >= 500040 */ 214 215 /* kFreeBSD < 5, NetBSD or OpenBSD */ 216 { 217 struct disklabel hdg; 218 if (ioctl (fd, DIOCGDINFO, &hdg)) 219 goto fail; 220 221 geom->cylinders = hdg.d_ncylinders; 222 geom->heads = hdg.d_ntracks; 223 geom->sectors = hdg.d_nsectors; 224 geom->total_sectors = hdg.d_secperunit; 225 226 goto success; 227 } 228 229 #elif defined(__sun) 230 /* Solaris */ 231 { 232 struct dk_geom dkg; 233 234 if (ioctl(fd, DKIOCG_PHYGEOM, &dkg)) 235 goto fail; 236 geom->cylinders = dkg.dkg_ncyl; 237 geom->heads = dkg.dkg_nhead; 238 geom->sectors = dkg.dkg_nsect; 239 geom->total_sectors = (unsigned long long)dkg.dkg_ncyl * dkg.dkg_nhead 240 * dkg.dkg_nsect; 241 242 goto success; 243 } 244 245 #else 246 /* Notably, defined(__GNU__) */ 247 # warning "Automatic detection of geometries will be performed only \ 248 partially. This is not fatal." 249 #endif 250 251 fail: 252 { 253 struct stat st; 254 255 /* FIXME: It would be nice to somehow compute fake C/H/S settings, 256 given a proper st_blocks size. */ 257 if (drive & 0x80) 258 { 259 geom->cylinders = DEFAULT_HD_CYLINDERS; 260 geom->heads = DEFAULT_HD_HEADS; 261 geom->sectors = DEFAULT_HD_SECTORS; 262 } 263 else 264 { 265 geom->cylinders = DEFAULT_FD_CYLINDERS; 266 geom->heads = DEFAULT_FD_HEADS; 267 geom->sectors = DEFAULT_FD_SECTORS; 268 } 269 270 /* Set the total sectors properly, if we can. */ 271 if (! fstat (fd, &st) && st.st_blocks) 272 geom->total_sectors = st.st_blocks >> SECTOR_BITS; 273 else 274 geom->total_sectors = (unsigned long long)geom->cylinders * 275 geom->heads * geom->sectors; 276 } 277 278 success: 279 if (geom->flags == -1) 280 close (fd); 281 } 282 283 #ifdef __linux__ 284 /* Check if we have devfs support. */ 285 static int 286 have_devfs (void) 287 { 288 static int dev_devfsd_exists = -1; 289 290 if (dev_devfsd_exists < 0) 291 { 292 struct stat st; 293 294 dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0; 295 } 296 297 return dev_devfsd_exists; 298 } 299 #endif /* __linux__ */ 300 301 /* These three functions are quite different among OSes. */ 302 static void 303 get_floppy_disk_name (char *name, int unit) 304 { 305 #if defined(__linux__) 306 /* GNU/Linux */ 307 if (have_devfs ()) 308 sprintf (name, "/dev/floppy/%d", unit); 309 else 310 sprintf (name, "/dev/fd%d", unit); 311 #elif defined(__GNU__) 312 /* GNU/Hurd */ 313 sprintf (name, "/dev/fd%d", unit); 314 #elif defined(__FreeBSD_kernel__) 315 /* kFreeBSD */ 316 if (get_kfreebsd_version () >= 400000) 317 sprintf (name, "/dev/fd%d", unit); 318 else 319 sprintf (name, "/dev/rfd%d", unit); 320 #elif defined(__NetBSD__) 321 /* NetBSD */ 322 /* opendisk() doesn't work for floppies. */ 323 sprintf (name, "/dev/rfd%da", unit); 324 #elif defined(__OpenBSD__) 325 /* OpenBSD */ 326 sprintf (name, "/dev/rfd%dc", unit); 327 #elif defined(__QNXNTO__) 328 /* QNX RTP */ 329 sprintf (name, "/dev/fd%d", unit); 330 #elif defined(__sun) 331 /* Solaris */ 332 sprintf (name, "/dev/rdiskette%d", unit); 333 #else 334 # warning "BIOS floppy drives cannot be guessed in your operating system." 335 /* Set NAME to a bogus string. */ 336 *name = 0; 337 #endif 338 } 339 340 static void 341 get_ide_disk_name (char *name, int unit) 342 { 343 #if defined(__linux__) 344 /* GNU/Linux */ 345 sprintf (name, "/dev/hd%c", unit + 'a'); 346 #elif defined(__GNU__) 347 /* GNU/Hurd */ 348 sprintf (name, "/dev/hd%d", unit); 349 #elif defined(__FreeBSD_kernel__) 350 /* kFreeBSD */ 351 if (get_kfreebsd_version () >= 400000) 352 sprintf (name, "/dev/ad%d", unit); 353 else 354 sprintf (name, "/dev/rwd%d", unit); 355 #elif defined(__NetBSD__) && defined(HAVE_OPENDISK) 356 /* NetBSD */ 357 char shortname[16]; 358 int fd; 359 360 sprintf (shortname, "wd%d", unit); 361 fd = opendisk (shortname, O_RDONLY, name, 362 16, /* length of NAME */ 363 0 /* char device */ 364 ); 365 close (fd); 366 #elif defined(__OpenBSD__) 367 /* OpenBSD */ 368 sprintf (name, "/dev/rwd%dc", unit); 369 #elif defined(__QNXNTO__) 370 /* QNX RTP */ 371 /* Actually, QNX RTP doesn't distinguish IDE from SCSI, so this could 372 contain SCSI disks. */ 373 sprintf (name, "/dev/hd%d", unit); 374 #elif defined(__sun) 375 *name = 0; /* FIXME */ 376 #else 377 # warning "BIOS IDE drives cannot be guessed in your operating system." 378 /* Set NAME to a bogus string. */ 379 *name = 0; 380 #endif 381 } 382 383 static void 384 get_scsi_disk_name (char *name, int unit) 385 { 386 #if defined(__linux__) 387 /* GNU/Linux */ 388 sprintf (name, "/dev/sd%c", unit + 'a'); 389 #elif defined(__GNU__) 390 /* GNU/Hurd */ 391 sprintf (name, "/dev/sd%d", unit); 392 #elif defined(__FreeBSD_kernel__) 393 /* kFreeBSD */ 394 if (get_kfreebsd_version () >= 400000) 395 sprintf (name, "/dev/da%d", unit); 396 else 397 sprintf (name, "/dev/rda%d", unit); 398 #elif defined(__NetBSD__) && defined(HAVE_OPENDISK) 399 /* NetBSD */ 400 char shortname[16]; 401 int fd; 402 403 sprintf (shortname, "sd%d", unit); 404 fd = opendisk (shortname, O_RDONLY, name, 405 16, /* length of NAME */ 406 0 /* char device */ 407 ); 408 close (fd); 409 #elif defined(__OpenBSD__) 410 /* OpenBSD */ 411 sprintf (name, "/dev/rsd%dc", unit); 412 #elif defined(__QNXNTO__) 413 /* QNX RTP */ 414 /* QNX RTP doesn't distinguish SCSI from IDE, so it is better to 415 disable the detection of SCSI disks here. */ 416 *name = 0; 417 #elif defined(__sun) 418 *name = 0; /* FIXME */ 419 #else 420 # warning "BIOS SCSI drives cannot be guessed in your operating system." 421 /* Set NAME to a bogus string. */ 422 *name = 0; 423 #endif 424 } 425 426 #ifdef __linux__ 427 static void 428 get_dac960_disk_name (char *name, int controller, int drive) 429 { 430 sprintf (name, "/dev/rd/c%dd%d", controller, drive); 431 } 432 433 static void 434 get_ataraid_disk_name (char *name, int unit) 435 { 436 sprintf (name, "/dev/ataraid/d%c", unit + '0'); 437 } 438 #endif 439 440 /* Check if DEVICE can be read. If an error occurs, return zero, 441 otherwise return non-zero. */ 442 int 443 check_device (const char *device) 444 { 445 char buf[512]; 446 FILE *fp; 447 448 /* If DEVICE is empty, just return 1. */ 449 if (*device == 0) 450 return 1; 451 452 fp = fopen (device, "r"); 453 if (! fp) 454 { 455 switch (errno) 456 { 457 #ifdef ENOMEDIUM 458 case ENOMEDIUM: 459 # if 0 460 /* At the moment, this finds only CDROMs, which can't be 461 read anyway, so leave it out. Code should be 462 reactivated if `removable disks' and CDROMs are 463 supported. */ 464 /* Accept it, it may be inserted. */ 465 return 1; 466 # endif 467 break; 468 #endif /* ENOMEDIUM */ 469 default: 470 /* Break case and leave. */ 471 break; 472 } 473 /* Error opening the device. */ 474 return 0; 475 } 476 477 /* Make sure CD-ROMs don't get assigned a BIOS disk number 478 before SCSI disks! */ 479 #ifdef __linux__ 480 # ifdef CDROM_GET_CAPABILITY 481 if (ioctl (fileno (fp), CDROM_GET_CAPABILITY, 0) >= 0) 482 return 0; 483 # else /* ! CDROM_GET_CAPABILITY */ 484 /* Check if DEVICE is a CD-ROM drive by the HDIO_GETGEO ioctl. */ 485 { 486 struct hd_geometry hdg; 487 struct stat st; 488 489 if (fstat (fileno (fp), &st)) 490 return 0; 491 492 /* If it is a block device and isn't a floppy, check if HDIO_GETGEO 493 succeeds. */ 494 if (S_ISBLK (st.st_mode) 495 && MAJOR (st.st_rdev) != FLOPPY_MAJOR 496 && ioctl (fileno (fp), HDIO_GETGEO, &hdg)) 497 return 0; 498 } 499 # endif /* ! CDROM_GET_CAPABILITY */ 500 #endif /* __linux__ */ 501 502 #if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) 503 # ifdef CDIOCCLRDEBUG 504 if (ioctl (fileno (fp), CDIOCCLRDEBUG, 0) >= 0) 505 return 0; 506 # endif /* CDIOCCLRDEBUG */ 507 #endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */ 508 509 /* Attempt to read the first sector. */ 510 if (fread (buf, 1, 512, fp) != 512) 511 { 512 fclose (fp); 513 return 0; 514 } 515 516 fclose (fp); 517 return 1; 518 } 519 520 /* Read mapping information from FP, and write it to MAP. */ 521 static int 522 read_device_map (FILE *fp, char **map, const char *map_file) 523 { 524 auto void show_error (int no, const char *msg); 525 auto void show_warning (int no, const char *msg, ...); 526 527 auto void show_error (int no, const char *msg) 528 { 529 fprintf (stderr, "%s:%d: error: %s\n", map_file, no, msg); 530 } 531 532 auto void show_warning (int no, const char *msg, ...) 533 { 534 va_list ap; 535 536 va_start (ap, msg); 537 fprintf (stderr, "%s:%d: warning: ", map_file, no); 538 vfprintf (stderr, msg, ap); 539 va_end (ap); 540 } 541 542 /* If there is the device map file, use the data in it instead of 543 probing devices. */ 544 char buf[1024]; /* XXX */ 545 int line_number = 0; 546 547 while (fgets (buf, sizeof (buf), fp)) 548 { 549 char *ptr, *eptr; 550 int drive; 551 int is_floppy = 0; 552 553 /* Increase the number of lines. */ 554 line_number++; 555 556 /* If the first character is '#', skip it. */ 557 if (buf[0] == '#') 558 continue; 559 560 ptr = buf; 561 /* Skip leading spaces. */ 562 while (*ptr && isspace (*ptr)) 563 ptr++; 564 565 /* Skip empty lines. */ 566 if (! *ptr) 567 continue; 568 569 if (*ptr != '(') 570 { 571 show_error (line_number, "No open parenthesis found"); 572 return 0; 573 } 574 575 ptr++; 576 if ((*ptr != 'f' && *ptr != 'h') || *(ptr + 1) != 'd') 577 { 578 show_error (line_number, "Bad drive name"); 579 return 0; 580 } 581 582 if (*ptr == 'f') 583 is_floppy = 1; 584 585 ptr += 2; 586 drive = strtoul (ptr, &ptr, 10); 587 if (drive < 0) 588 { 589 show_error (line_number, "Bad device number"); 590 return 0; 591 } 592 else if (drive > 127) 593 { 594 show_warning (line_number, 595 "Ignoring %cd%d due to a BIOS limitation", 596 is_floppy ? 'f' : 'h', drive); 597 continue; 598 } 599 600 if (! is_floppy) 601 drive += 0x80; 602 603 if (*ptr != ')') 604 { 605 show_error (line_number, "No close parenthesis found"); 606 return 0; 607 } 608 609 ptr++; 610 /* Skip spaces. */ 611 while (*ptr && isspace (*ptr)) 612 ptr++; 613 614 if (! *ptr) 615 { 616 show_error (line_number, "No filename found"); 617 return 0; 618 } 619 620 /* Terminate the filename. */ 621 eptr = ptr; 622 while (*eptr && ! isspace (*eptr)) 623 eptr++; 624 *eptr = 0; 625 626 /* Multiple entries for a given drive is not allowed. */ 627 if (map[drive]) 628 { 629 show_error (line_number, "Duplicated entry found"); 630 return 0; 631 } 632 633 map[drive] = strdup (ptr); 634 assert (map[drive]); 635 } 636 637 return 1; 638 } 639 640 /* Initialize the device map MAP. *MAP will be allocated from the heap 641 space. If MAP_FILE is not NULL, then read mappings from the file 642 MAP_FILE if it exists, otherwise, write guessed mappings to the file. 643 FLOPPY_DISKS is the number of floppy disk drives which will be probed. 644 If it is zero, don't probe any floppy at all. If it is one, probe one 645 floppy. If it is two, probe two floppies. And so on. */ 646 int 647 init_device_map (char ***map, const char *map_file, int floppy_disks) 648 { 649 int i; 650 int num_hd = 0; 651 FILE *fp = 0; 652 653 assert (map); 654 assert (*map == 0); 655 *map = malloc (NUM_DISKS * sizeof (char *)); 656 assert (*map); 657 658 /* Probe devices for creating the device map. */ 659 660 /* Initialize DEVICE_MAP. */ 661 for (i = 0; i < NUM_DISKS; i++) 662 (*map)[i] = 0; 663 664 if (map_file) 665 { 666 /* Open the device map file. */ 667 fp = fopen (map_file, "r"); 668 if (fp) 669 { 670 int ret; 671 672 ret = read_device_map (fp, *map, map_file); 673 fclose (fp); 674 return ret; 675 } 676 } 677 678 /* Print something so that the user does not think GRUB has been 679 crashed. */ 680 fprintf (stderr, 681 "Probing devices to guess BIOS drives. " 682 "This may take a long time.\n"); 683 684 if (map_file) 685 /* Try to open the device map file to write the probed data. */ 686 fp = fopen (map_file, "w"); 687 688 /* Floppies. */ 689 for (i = 0; i < floppy_disks; i++) 690 { 691 char name[16]; 692 693 get_floppy_disk_name (name, i); 694 /* In floppies, write the map, whether check_device succeeds 695 or not, because the user just does not insert floppies. */ 696 if (fp) 697 fprintf (fp, "(fd%d)\t%s\n", i, name); 698 699 if (check_device (name)) 700 { 701 (*map)[i] = strdup (name); 702 assert ((*map)[i]); 703 } 704 } 705 706 #ifdef __linux__ 707 if (have_devfs ()) 708 { 709 while (1) 710 { 711 char discn[32]; 712 char name[PATH_MAX]; 713 struct stat st; 714 715 /* Linux creates symlinks "/dev/discs/discN" for convenience. 716 The way to number disks is the same as GRUB's. */ 717 sprintf (discn, "/dev/discs/disc%d", num_hd); 718 if (stat (discn, &st) < 0) 719 break; 720 721 if (realpath (discn, name)) 722 { 723 strcat (name, "/disc"); 724 (*map)[num_hd + 0x80] = strdup (name); 725 assert ((*map)[num_hd + 0x80]); 726 727 /* If the device map file is opened, write the map. */ 728 if (fp) 729 fprintf (fp, "(hd%d)\t%s\n", num_hd, name); 730 } 731 732 num_hd++; 733 } 734 735 /* OK, close the device map file if opened. */ 736 if (fp) 737 fclose (fp); 738 739 return 1; 740 } 741 #endif /* __linux__ */ 742 743 /* IDE disks. */ 744 for (i = 0; i < 8; i++) 745 { 746 char name[16]; 747 748 get_ide_disk_name (name, i); 749 if (check_device (name)) 750 { 751 (*map)[num_hd + 0x80] = strdup (name); 752 assert ((*map)[num_hd + 0x80]); 753 754 /* If the device map file is opened, write the map. */ 755 if (fp) 756 fprintf (fp, "(hd%d)\t%s\n", num_hd, name); 757 758 num_hd++; 759 } 760 } 761 762 #ifdef __linux__ 763 /* ATARAID disks. */ 764 for (i = 0; i < 8; i++) 765 { 766 char name[20]; 767 768 get_ataraid_disk_name (name, i); 769 if (check_device (name)) 770 { 771 (*map)[num_hd + 0x80] = strdup (name); 772 assert ((*map)[num_hd + 0x80]); 773 774 /* If the device map file is opened, write the map. */ 775 if (fp) 776 fprintf (fp, "(hd%d)\t%s\n", num_hd, name); 777 778 num_hd++; 779 } 780 } 781 #endif /* __linux__ */ 782 783 /* The rest is SCSI disks. */ 784 for (i = 0; i < 16; i++) 785 { 786 char name[16]; 787 788 get_scsi_disk_name (name, i); 789 if (check_device (name)) 790 { 791 (*map)[num_hd + 0x80] = strdup (name); 792 assert ((*map)[num_hd + 0x80]); 793 794 /* If the device map file is opened, write the map. */ 795 if (fp) 796 fprintf (fp, "(hd%d)\t%s\n", num_hd, name); 797 798 num_hd++; 799 } 800 } 801 802 #ifdef __linux__ 803 /* This is for DAC960 - we have 804 /dev/rd/c<controller>d<logical drive>p<partition>. 805 806 DAC960 driver currently supports up to 8 controllers, 32 logical 807 drives, and 7 partitions. */ 808 { 809 int controller, drive; 810 811 for (controller = 0; controller < 8; controller++) 812 { 813 for (drive = 0; drive < 15; drive++) 814 { 815 char name[24]; 816 817 get_dac960_disk_name (name, controller, drive); 818 if (check_device (name)) 819 { 820 (*map)[num_hd + 0x80] = strdup (name); 821 assert ((*map)[num_hd + 0x80]); 822 823 /* If the device map file is opened, write the map. */ 824 if (fp) 825 fprintf (fp, "(hd%d)\t%s\n", num_hd, name); 826 827 num_hd++; 828 } 829 } 830 } 831 } 832 #endif /* __linux__ */ 833 834 /* OK, close the device map file if opened. */ 835 if (fp) 836 fclose (fp); 837 838 return 1; 839 } 840 841 /* Restore the memory consumed for MAP. */ 842 void 843 restore_device_map (char **map) 844 { 845 int i; 846 847 for (i = 0; i < NUM_DISKS; i++) 848 if (map[i]) 849 free (map[i]); 850 851 free (map); 852 } 853 854 #ifdef __linux__ 855 /* Linux-only functions, because Linux has a bug that the disk cache for 856 a whole disk is not consistent with the one for a partition of the 857 disk. */ 858 int 859 is_disk_device (char **map, int drive) 860 { 861 struct stat st; 862 863 assert (map[drive] != 0); 864 assert (stat (map[drive], &st) == 0); 865 /* For now, disk devices under Linux are all block devices. */ 866 return S_ISBLK (st.st_mode); 867 } 868 869 int 870 write_to_partition (char **map, int drive, int partition, 871 int sector, int size, const char *buf) 872 { 873 char dev[PATH_MAX]; /* XXX */ 874 int fd; 875 876 if ((partition & 0x00FF00) != 0x00FF00) 877 { 878 /* If the partition is a BSD partition, it is difficult to 879 obtain the representation in Linux. So don't support that. */ 880 errnum = ERR_DEV_VALUES; 881 return 1; 882 } 883 884 assert (map[drive] != 0); 885 886 strcpy (dev, map[drive]); 887 if (have_devfs ()) 888 { 889 if (strcmp (dev + strlen(dev) - 5, "/disc") == 0) 890 strcpy (dev + strlen(dev) - 5, "/part"); 891 } 892 sprintf (dev + strlen(dev), "%d", ((partition >> 16) & 0xFF) + 1); 893 894 /* Open the partition. */ 895 fd = open (dev, O_RDWR); 896 if (fd < 0) 897 { 898 errnum = ERR_NO_PART; 899 return 0; 900 } 901 902 #if defined(__linux__) && (!defined(__GLIBC__) || \ 903 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) 904 /* Maybe libc doesn't have large file support. */ 905 { 906 loff_t offset, result; 907 static int _llseek (uint filedes, ulong hi, ulong lo, 908 loff_t *res, uint wh); 909 _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo, 910 loff_t *, res, uint, wh); 911 912 offset = (loff_t) sector * (loff_t) SECTOR_SIZE; 913 if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) 914 { 915 errnum = ERR_DEV_VALUES; 916 return 0; 917 } 918 } 919 #else 920 { 921 off_t offset = (off_t) sector * (off_t) SECTOR_SIZE; 922 923 if (lseek (fd, offset, SEEK_SET) != offset) 924 { 925 errnum = ERR_DEV_VALUES; 926 return 0; 927 } 928 } 929 #endif 930 931 if (write (fd, buf, size * SECTOR_SIZE) != (size * SECTOR_SIZE)) 932 { 933 close (fd); 934 errnum = ERR_WRITE; 935 return 0; 936 } 937 938 sync (); /* Paranoia. */ 939 close (fd); 940 941 return 1; 942 } 943 #endif /* __linux__ */ 944