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