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