Home | History | Annotate | Download | only in lib
      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, &sector_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