• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
get_kfreebsd_version()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
get_drive_geometry(struct geometry * geom,char ** map,int drive)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
have_devfs(void)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
get_floppy_disk_name(char * name,int unit)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
get_ide_disk_name(char * name,int unit)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
get_scsi_disk_name(char * name,int unit)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
get_dac960_disk_name(char * name,int controller,int drive)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
get_ataraid_disk_name(char * name,int unit)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
check_device(const char * device)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
read_device_map(FILE * fp,char ** map,const char * map_file)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
init_device_map(char *** map,const char * map_file,int floppy_disks)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
restore_device_map(char ** map)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
is_disk_device(char ** map,int drive)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
write_to_partition(char ** map,int drive,int partition,int sector,int size,const char * buf)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