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.  */
50struct 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>
91int
92get_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.  */
139void
140get_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, &sector_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 \
248partially. 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.  */
285static int
286have_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.  */
302static void
303get_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
340static void
341get_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
383static void
384get_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__
427static void
428get_dac960_disk_name (char *name, int controller, int drive)
429{
430  sprintf (name, "/dev/rd/c%dd%d", controller, drive);
431}
432
433static void
434get_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.  */
442int
443check_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.  */
521static int
522read_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.  */
646int
647init_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.  */
842void
843restore_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.  */
858int
859is_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
869int
870write_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