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