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 = dkg.dkg_ncyl * dkg.dkg_nhead * dkg.dkg_nsect; 240 241 goto success; 242 } 243 244 #else 245 /* Notably, defined(__GNU__) */ 246 # warning "Automatic detection of geometries will be performed only \ 247 partially. This is not fatal." 248 #endif 249 250 fail: 251 { 252 struct stat st; 253 254 /* FIXME: It would be nice to somehow compute fake C/H/S settings, 255 given a proper st_blocks size. */ 256 if (drive & 0x80) 257 { 258 geom->cylinders = DEFAULT_HD_CYLINDERS; 259 geom->heads = DEFAULT_HD_HEADS; 260 geom->sectors = DEFAULT_HD_SECTORS; 261 } 262 else 263 { 264 geom->cylinders = DEFAULT_FD_CYLINDERS; 265 geom->heads = DEFAULT_FD_HEADS; 266 geom->sectors = DEFAULT_FD_SECTORS; 267 } 268 269 /* Set the total sectors properly, if we can. */ 270 if (! fstat (fd, &st) && st.st_blocks) 271 geom->total_sectors = st.st_blocks >> SECTOR_BITS; 272 else 273 geom->total_sectors = geom->cylinders * geom->heads * geom->sectors; 274 } 275 276 success: 277 if (geom->flags == -1) 278 close (fd); 279 } 280 281 #ifdef __linux__ 282 /* Check if we have devfs support. */ 283 static int 284 have_devfs (void) 285 { 286 static int dev_devfsd_exists = -1; 287 288 if (dev_devfsd_exists < 0) 289 { 290 struct stat st; 291 292 dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0; 293 } 294 295 return dev_devfsd_exists; 296 } 297 #endif /* __linux__ */ 298 299 /* These three functions are quite different among OSes. */ 300 static void 301 get_floppy_disk_name (char *name, int unit) 302 { 303 #if defined(__linux__) 304 /* GNU/Linux */ 305 if (have_devfs ()) 306 sprintf (name, "/dev/floppy/%d", unit); 307 else 308 sprintf (name, "/dev/fd%d", unit); 309 #elif defined(__GNU__) 310 /* GNU/Hurd */ 311 sprintf (name, "/dev/fd%d", unit); 312 #elif defined(__FreeBSD_kernel__) 313 /* kFreeBSD */ 314 if (get_kfreebsd_version () >= 400000) 315 sprintf (name, "/dev/fd%d", unit); 316 else 317 sprintf (name, "/dev/rfd%d", unit); 318 #elif defined(__NetBSD__) 319 /* NetBSD */ 320 /* opendisk() doesn't work for floppies. */ 321 sprintf (name, "/dev/rfd%da", unit); 322 #elif defined(__OpenBSD__) 323 /* OpenBSD */ 324 sprintf (name, "/dev/rfd%dc", unit); 325 #elif defined(__QNXNTO__) 326 /* QNX RTP */ 327 sprintf (name, "/dev/fd%d", unit); 328 #elif defined(__sun) 329 /* Solaris */ 330 sprintf (name, "/dev/rdiskette%d", unit); 331 #else 332 # warning "BIOS floppy drives cannot be guessed in your operating system." 333 /* Set NAME to a bogus string. */ 334 *name = 0; 335 #endif 336 } 337 338 static void 339 get_ide_disk_name (char *name, int unit) 340 { 341 #if defined(__linux__) 342 /* GNU/Linux */ 343 sprintf (name, "/dev/hd%c", unit + 'a'); 344 #elif defined(__GNU__) 345 /* GNU/Hurd */ 346 sprintf (name, "/dev/hd%d", unit); 347 #elif defined(__FreeBSD_kernel__) 348 /* kFreeBSD */ 349 if (get_kfreebsd_version () >= 400000) 350 sprintf (name, "/dev/ad%d", unit); 351 else 352 sprintf (name, "/dev/rwd%d", unit); 353 #elif defined(__NetBSD__) && defined(HAVE_OPENDISK) 354 /* NetBSD */ 355 char shortname[16]; 356 int fd; 357 358 sprintf (shortname, "wd%d", unit); 359 fd = opendisk (shortname, O_RDONLY, name, 360 16, /* length of NAME */ 361 0 /* char device */ 362 ); 363 close (fd); 364 #elif defined(__OpenBSD__) 365 /* OpenBSD */ 366 sprintf (name, "/dev/rwd%dc", unit); 367 #elif defined(__QNXNTO__) 368 /* QNX RTP */ 369 /* Actually, QNX RTP doesn't distinguish IDE from SCSI, so this could 370 contain SCSI disks. */ 371 sprintf (name, "/dev/hd%d", unit); 372 #elif defined(__sun) 373 *name = 0; /* FIXME */ 374 #else 375 # warning "BIOS IDE drives cannot be guessed in your operating system." 376 /* Set NAME to a bogus string. */ 377 *name = 0; 378 #endif 379 } 380 381 static void 382 get_scsi_disk_name (char *name, int unit) 383 { 384 #if defined(__linux__) 385 /* GNU/Linux */ 386 sprintf (name, "/dev/sd%c", unit + 'a'); 387 #elif defined(__GNU__) 388 /* GNU/Hurd */ 389 sprintf (name, "/dev/sd%d", unit); 390 #elif defined(__FreeBSD_kernel__) 391 /* kFreeBSD */ 392 if (get_kfreebsd_version () >= 400000) 393 sprintf (name, "/dev/da%d", unit); 394 else 395 sprintf (name, "/dev/rda%d", unit); 396 #elif defined(__NetBSD__) && defined(HAVE_OPENDISK) 397 /* NetBSD */ 398 char shortname[16]; 399 int fd; 400 401 sprintf (shortname, "sd%d", unit); 402 fd = opendisk (shortname, O_RDONLY, name, 403 16, /* length of NAME */ 404 0 /* char device */ 405 ); 406 close (fd); 407 #elif defined(__OpenBSD__) 408 /* OpenBSD */ 409 sprintf (name, "/dev/rsd%dc", unit); 410 #elif defined(__QNXNTO__) 411 /* QNX RTP */ 412 /* QNX RTP doesn't distinguish SCSI from IDE, so it is better to 413 disable the detection of SCSI disks here. */ 414 *name = 0; 415 #elif defined(__sun) 416 *name = 0; /* FIXME */ 417 #else 418 # warning "BIOS SCSI drives cannot be guessed in your operating system." 419 /* Set NAME to a bogus string. */ 420 *name = 0; 421 #endif 422 } 423 424 #ifdef __linux__ 425 static void 426 get_dac960_disk_name (char *name, int controller, int drive) 427 { 428 sprintf (name, "/dev/rd/c%dd%d", controller, drive); 429 } 430 431 static void 432 get_ataraid_disk_name (char *name, int unit) 433 { 434 sprintf (name, "/dev/ataraid/d%c", unit + '0'); 435 } 436 #endif 437 438 /* Check if DEVICE can be read. If an error occurs, return zero, 439 otherwise return non-zero. */ 440 int 441 check_device (const char *device) 442 { 443 char buf[512]; 444 FILE *fp; 445 446 /* If DEVICE is empty, just return 1. */ 447 if (*device == 0) 448 return 1; 449 450 fp = fopen (device, "r"); 451 if (! fp) 452 { 453 switch (errno) 454 { 455 #ifdef ENOMEDIUM 456 case ENOMEDIUM: 457 # if 0 458 /* At the moment, this finds only CDROMs, which can't be 459 read anyway, so leave it out. Code should be 460 reactivated if `removable disks' and CDROMs are 461 supported. */ 462 /* Accept it, it may be inserted. */ 463 return 1; 464 # endif 465 break; 466 #endif /* ENOMEDIUM */ 467 default: 468 /* Break case and leave. */ 469 break; 470 } 471 /* Error opening the device. */ 472 return 0; 473 } 474 475 /* Make sure CD-ROMs don't get assigned a BIOS disk number 476 before SCSI disks! */ 477 #ifdef __linux__ 478 # ifdef CDROM_GET_CAPABILITY 479 if (ioctl (fileno (fp), CDROM_GET_CAPABILITY, 0) >= 0) 480 return 0; 481 # else /* ! CDROM_GET_CAPABILITY */ 482 /* Check if DEVICE is a CD-ROM drive by the HDIO_GETGEO ioctl. */ 483 { 484 struct hd_geometry hdg; 485 struct stat st; 486 487 if (fstat (fileno (fp), &st)) 488 return 0; 489 490 /* If it is a block device and isn't a floppy, check if HDIO_GETGEO 491 succeeds. */ 492 if (S_ISBLK (st.st_mode) 493 && MAJOR (st.st_rdev) != FLOPPY_MAJOR 494 && ioctl (fileno (fp), HDIO_GETGEO, &hdg)) 495 return 0; 496 } 497 # endif /* ! CDROM_GET_CAPABILITY */ 498 #endif /* __linux__ */ 499 500 #if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) 501 # ifdef CDIOCCLRDEBUG 502 if (ioctl (fileno (fp), CDIOCCLRDEBUG, 0) >= 0) 503 return 0; 504 # endif /* CDIOCCLRDEBUG */ 505 #endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */ 506 507 /* Attempt to read the first sector. */ 508 if (fread (buf, 1, 512, fp) != 512) 509 { 510 fclose (fp); 511 return 0; 512 } 513 514 fclose (fp); 515 return 1; 516 } 517 518 /* Read mapping information from FP, and write it to MAP. */ 519 static int 520 read_device_map (FILE *fp, char **map, const char *map_file) 521 { 522 auto void show_error (int no, const char *msg); 523 auto void show_warning (int no, const char *msg, ...); 524 525 auto void show_error (int no, const char *msg) 526 { 527 fprintf (stderr, "%s:%d: error: %s\n", map_file, no, msg); 528 } 529 530 auto void show_warning (int no, const char *msg, ...) 531 { 532 va_list ap; 533 534 va_start (ap, msg); 535 fprintf (stderr, "%s:%d: warning: ", map_file, no); 536 vfprintf (stderr, msg, ap); 537 va_end (ap); 538 } 539 540 /* If there is the device map file, use the data in it instead of 541 probing devices. */ 542 char buf[1024]; /* XXX */ 543 int line_number = 0; 544 545 while (fgets (buf, sizeof (buf), fp)) 546 { 547 char *ptr, *eptr; 548 int drive; 549 int is_floppy = 0; 550 551 /* Increase the number of lines. */ 552 line_number++; 553 554 /* If the first character is '#', skip it. */ 555 if (buf[0] == '#') 556 continue; 557 558 ptr = buf; 559 /* Skip leading spaces. */ 560 while (*ptr && isspace (*ptr)) 561 ptr++; 562 563 /* Skip empty lines. */ 564 if (! *ptr) 565 continue; 566 567 if (*ptr != '(') 568 { 569 show_error (line_number, "No open parenthesis found"); 570 return 0; 571 } 572 573 ptr++; 574 if ((*ptr != 'f' && *ptr != 'h') || *(ptr + 1) != 'd') 575 { 576 show_error (line_number, "Bad drive name"); 577 return 0; 578 } 579 580 if (*ptr == 'f') 581 is_floppy = 1; 582 583 ptr += 2; 584 drive = strtoul (ptr, &ptr, 10); 585 if (drive < 0) 586 { 587 show_error (line_number, "Bad device number"); 588 return 0; 589 } 590 else if (drive > 127) 591 { 592 show_warning (line_number, 593 "Ignoring %cd%d due to a BIOS limitation", 594 is_floppy ? 'f' : 'h', drive); 595 continue; 596 } 597 598 if (! is_floppy) 599 drive += 0x80; 600 601 if (*ptr != ')') 602 { 603 show_error (line_number, "No close parenthesis found"); 604 return 0; 605 } 606 607 ptr++; 608 /* Skip spaces. */ 609 while (*ptr && isspace (*ptr)) 610 ptr++; 611 612 if (! *ptr) 613 { 614 show_error (line_number, "No filename found"); 615 return 0; 616 } 617 618 /* Terminate the filename. */ 619 eptr = ptr; 620 while (*eptr && ! isspace (*eptr)) 621 eptr++; 622 *eptr = 0; 623 624 /* Multiple entries for a given drive is not allowed. */ 625 if (map[drive]) 626 { 627 show_error (line_number, "Duplicated entry found"); 628 return 0; 629 } 630 631 map[drive] = strdup (ptr); 632 assert (map[drive]); 633 } 634 635 return 1; 636 } 637 638 /* Initialize the device map MAP. *MAP will be allocated from the heap 639 space. If MAP_FILE is not NULL, then read mappings from the file 640 MAP_FILE if it exists, otherwise, write guessed mappings to the file. 641 FLOPPY_DISKS is the number of floppy disk drives which will be probed. 642 If it is zero, don't probe any floppy at all. If it is one, probe one 643 floppy. If it is two, probe two floppies. And so on. */ 644 int 645 init_device_map (char ***map, const char *map_file, int floppy_disks) 646 { 647 int i; 648 int num_hd = 0; 649 FILE *fp = 0; 650 651 assert (map); 652 assert (*map == 0); 653 *map = malloc (NUM_DISKS * sizeof (char *)); 654 assert (*map); 655 656 /* Probe devices for creating the device map. */ 657 658 /* Initialize DEVICE_MAP. */ 659 for (i = 0; i < NUM_DISKS; i++) 660 (*map)[i] = 0; 661 662 if (map_file) 663 { 664 /* Open the device map file. */ 665 fp = fopen (map_file, "r"); 666 if (fp) 667 { 668 int ret; 669 670 ret = read_device_map (fp, *map, map_file); 671 fclose (fp); 672 return ret; 673 } 674 } 675 676 /* Print something so that the user does not think GRUB has been 677 crashed. */ 678 fprintf (stderr, 679 "Probing devices to guess BIOS drives. " 680 "This may take a long time.\n"); 681 682 if (map_file) 683 /* Try to open the device map file to write the probed data. */ 684 fp = fopen (map_file, "w"); 685 686 /* Floppies. */ 687 for (i = 0; i < floppy_disks; i++) 688 { 689 char name[16]; 690 691 get_floppy_disk_name (name, i); 692 /* In floppies, write the map, whether check_device succeeds 693 or not, because the user just does not insert floppies. */ 694 if (fp) 695 fprintf (fp, "(fd%d)\t%s\n", i, name); 696 697 if (check_device (name)) 698 { 699 (*map)[i] = strdup (name); 700 assert ((*map)[i]); 701 } 702 } 703 704 #ifdef __linux__ 705 if (have_devfs ()) 706 { 707 while (1) 708 { 709 char discn[32]; 710 char name[PATH_MAX]; 711 struct stat st; 712 713 /* Linux creates symlinks "/dev/discs/discN" for convenience. 714 The way to number disks is the same as GRUB's. */ 715 sprintf (discn, "/dev/discs/disc%d", num_hd); 716 if (stat (discn, &st) < 0) 717 break; 718 719 if (realpath (discn, name)) 720 { 721 strcat (name, "/disc"); 722 (*map)[num_hd + 0x80] = strdup (name); 723 assert ((*map)[num_hd + 0x80]); 724 725 /* If the device map file is opened, write the map. */ 726 if (fp) 727 fprintf (fp, "(hd%d)\t%s\n", num_hd, name); 728 } 729 730 num_hd++; 731 } 732 733 /* OK, close the device map file if opened. */ 734 if (fp) 735 fclose (fp); 736 737 return 1; 738 } 739 #endif /* __linux__ */ 740 741 /* IDE disks. */ 742 for (i = 0; i < 8; i++) 743 { 744 char name[16]; 745 746 get_ide_disk_name (name, i); 747 if (check_device (name)) 748 { 749 (*map)[num_hd + 0x80] = strdup (name); 750 assert ((*map)[num_hd + 0x80]); 751 752 /* If the device map file is opened, write the map. */ 753 if (fp) 754 fprintf (fp, "(hd%d)\t%s\n", num_hd, name); 755 756 num_hd++; 757 } 758 } 759 760 #ifdef __linux__ 761 /* ATARAID disks. */ 762 for (i = 0; i < 8; i++) 763 { 764 char name[20]; 765 766 get_ataraid_disk_name (name, i); 767 if (check_device (name)) 768 { 769 (*map)[num_hd + 0x80] = strdup (name); 770 assert ((*map)[num_hd + 0x80]); 771 772 /* If the device map file is opened, write the map. */ 773 if (fp) 774 fprintf (fp, "(hd%d)\t%s\n", num_hd, name); 775 776 num_hd++; 777 } 778 } 779 #endif /* __linux__ */ 780 781 /* The rest is SCSI disks. */ 782 for (i = 0; i < 16; i++) 783 { 784 char name[16]; 785 786 get_scsi_disk_name (name, i); 787 if (check_device (name)) 788 { 789 (*map)[num_hd + 0x80] = strdup (name); 790 assert ((*map)[num_hd + 0x80]); 791 792 /* If the device map file is opened, write the map. */ 793 if (fp) 794 fprintf (fp, "(hd%d)\t%s\n", num_hd, name); 795 796 num_hd++; 797 } 798 } 799 800 #ifdef __linux__ 801 /* This is for DAC960 - we have 802 /dev/rd/c<controller>d<logical drive>p<partition>. 803 804 DAC960 driver currently supports up to 8 controllers, 32 logical 805 drives, and 7 partitions. */ 806 { 807 int controller, drive; 808 809 for (controller = 0; controller < 8; controller++) 810 { 811 for (drive = 0; drive < 15; drive++) 812 { 813 char name[24]; 814 815 get_dac960_disk_name (name, controller, drive); 816 if (check_device (name)) 817 { 818 (*map)[num_hd + 0x80] = strdup (name); 819 assert ((*map)[num_hd + 0x80]); 820 821 /* If the device map file is opened, write the map. */ 822 if (fp) 823 fprintf (fp, "(hd%d)\t%s\n", num_hd, name); 824 825 num_hd++; 826 } 827 } 828 } 829 } 830 #endif /* __linux__ */ 831 832 /* OK, close the device map file if opened. */ 833 if (fp) 834 fclose (fp); 835 836 return 1; 837 } 838 839 /* Restore the memory consumed for MAP. */ 840 void 841 restore_device_map (char **map) 842 { 843 int i; 844 845 for (i = 0; i < NUM_DISKS; i++) 846 if (map[i]) 847 free (map[i]); 848 849 free (map); 850 } 851 852 #ifdef __linux__ 853 /* Linux-only functions, because Linux has a bug that the disk cache for 854 a whole disk is not consistent with the one for a partition of the 855 disk. */ 856 int 857 is_disk_device (char **map, int drive) 858 { 859 struct stat st; 860 861 assert (map[drive] != 0); 862 assert (stat (map[drive], &st) == 0); 863 /* For now, disk devices under Linux are all block devices. */ 864 return S_ISBLK (st.st_mode); 865 } 866 867 int 868 write_to_partition (char **map, int drive, int partition, 869 int sector, int size, const char *buf) 870 { 871 char dev[PATH_MAX]; /* XXX */ 872 int fd; 873 874 if ((partition & 0x00FF00) != 0x00FF00) 875 { 876 /* If the partition is a BSD partition, it is difficult to 877 obtain the representation in Linux. So don't support that. */ 878 errnum = ERR_DEV_VALUES; 879 return 1; 880 } 881 882 assert (map[drive] != 0); 883 884 strcpy (dev, map[drive]); 885 if (have_devfs ()) 886 { 887 if (strcmp (dev + strlen(dev) - 5, "/disc") == 0) 888 strcpy (dev + strlen(dev) - 5, "/part"); 889 } 890 sprintf (dev + strlen(dev), "%d", ((partition >> 16) & 0xFF) + 1); 891 892 /* Open the partition. */ 893 fd = open (dev, O_RDWR); 894 if (fd < 0) 895 { 896 errnum = ERR_NO_PART; 897 return 0; 898 } 899 900 #if defined(__linux__) && (!defined(__GLIBC__) || \ 901 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))) 902 /* Maybe libc doesn't have large file support. */ 903 { 904 loff_t offset, result; 905 static int _llseek (uint filedes, ulong hi, ulong lo, 906 loff_t *res, uint wh); 907 _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo, 908 loff_t *, res, uint, wh); 909 910 offset = (loff_t) sector * (loff_t) SECTOR_SIZE; 911 if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET)) 912 { 913 errnum = ERR_DEV_VALUES; 914 return 0; 915 } 916 } 917 #else 918 { 919 off_t offset = (off_t) sector * (off_t) SECTOR_SIZE; 920 921 if (lseek (fd, offset, SEEK_SET) != offset) 922 { 923 errnum = ERR_DEV_VALUES; 924 return 0; 925 } 926 } 927 #endif 928 929 if (write (fd, buf, size * SECTOR_SIZE) != (size * SECTOR_SIZE)) 930 { 931 close (fd); 932 errnum = ERR_WRITE; 933 return 0; 934 } 935 936 sync (); /* Paranoia. */ 937 close (fd); 938 939 return 1; 940 } 941 #endif /* __linux__ */ 942