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, §or_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