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