Merge branch 'master' of git://github.com/illumos/illumos-gate
[unleashed.git] / usr / src / grub / grub-0.97 / lib / device.c
blob082c2178dfc69c449a09061d07a8b1727c8135bf
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.
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.
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.
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.
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
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>
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
52 unsigned char heads;
53 unsigned char sectors;
54 unsigned short cylinders;
55 unsigned long start;
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); \
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__ */
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
89 /* Runtime detection of kernel */
90 # include <sys/utsname.h>
91 int
92 get_kfreebsd_version ()
94 struct utsname uts;
95 int major; int minor, v[2];
97 uname (&uts);
98 sscanf (uts.release, "%d.%d", &major, &minor);
100 if (major >= 9)
101 major = 9;
102 if (major >= 5)
104 v[0] = minor/10; v[1] = minor%10;
106 else
108 v[0] = minor%10; v[1] = minor/10;
110 return major*100000+v[0]*10000+v[1]*1000;
112 #endif /* __FreeBSD_kernel__ */
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__ */
126 #if defined(__sun)
127 # include <sys/dkio.h>
128 #endif /* __sun */
130 #ifdef HAVE_OPENDISK
131 # include <util.h>
132 #endif /* HAVE_OPENDISK */
134 #define WITHOUT_LIBC_STUBS 1
135 #include <shared.h>
136 #include <device.h>
138 /* Get the geometry of a drive DRIVE. */
139 void
140 get_drive_geometry (struct geometry *geom, char **map, int drive)
142 int fd;
144 if (geom->flags == -1)
146 fd = open (map[drive], O_RDONLY);
147 assert (fd >= 0);
149 else
150 fd = geom->flags;
152 /* XXX This is the default size. */
153 geom->sector_size = SECTOR_SIZE;
155 #if defined(__linux__)
156 /* Linux */
158 struct hd_geometry hdg;
159 unsigned long nr;
161 if (ioctl (fd, HDIO_GETGEO, &hdg))
162 goto fail;
164 if (ioctl (fd, BLKGETSIZE, &nr))
165 goto fail;
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;
173 goto success;
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)
181 unsigned int sector_size;
182 off_t media_size;
183 unsigned int tmp;
185 if(ioctl (fd, DIOCGSECTORSIZE, &sector_size) != 0)
186 sector_size = 512;
188 if (ioctl (fd, DIOCGMEDIASIZE, &media_size) != 0)
189 goto fail;
191 geom->total_sectors = media_size / sector_size;
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;
206 geom->cylinders = (geom->total_sectors
207 / geom->heads
208 / geom->sectors);
210 goto success;
212 else
213 #endif /* defined(__FreeBSD_kernel__) && __FreeBSD_kernel_version >= 500040 */
215 /* kFreeBSD < 5, NetBSD or OpenBSD */
217 struct disklabel hdg;
218 if (ioctl (fd, DIOCGDINFO, &hdg))
219 goto fail;
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;
226 goto success;
229 #elif defined(__sun)
230 /* Solaris */
232 struct dk_geom dkg;
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;
242 goto success;
245 #else
246 /* Notably, defined(__GNU__) */
247 # warning "Automatic detection of geometries will be performed only \
248 partially. This is not fatal."
249 #endif
251 fail:
253 struct stat st;
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)
259 geom->cylinders = DEFAULT_HD_CYLINDERS;
260 geom->heads = DEFAULT_HD_HEADS;
261 geom->sectors = DEFAULT_HD_SECTORS;
263 else
265 geom->cylinders = DEFAULT_FD_CYLINDERS;
266 geom->heads = DEFAULT_FD_HEADS;
267 geom->sectors = DEFAULT_FD_SECTORS;
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;
278 success:
279 if (geom->flags == -1)
280 close (fd);
283 #ifdef __linux__
284 /* Check if we have devfs support. */
285 static int
286 have_devfs (void)
288 static int dev_devfsd_exists = -1;
290 if (dev_devfsd_exists < 0)
292 struct stat st;
294 dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
297 return dev_devfsd_exists;
299 #endif /* __linux__ */
301 /* These three functions are quite different among OSes. */
302 static void
303 get_floppy_disk_name (char *name, int unit)
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
340 static void
341 get_ide_disk_name (char *name, int unit)
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;
360 sprintf (shortname, "wd%d", unit);
361 fd = opendisk (shortname, O_RDONLY, name,
362 16, /* length of NAME */
363 0 /* char device */
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
383 static void
384 get_scsi_disk_name (char *name, int unit)
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;
403 sprintf (shortname, "sd%d", unit);
404 fd = opendisk (shortname, O_RDONLY, name,
405 16, /* length of NAME */
406 0 /* char device */
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
426 #ifdef __linux__
427 static void
428 get_dac960_disk_name (char *name, int controller, int drive)
430 sprintf (name, "/dev/rd/c%dd%d", controller, drive);
433 static void
434 get_ataraid_disk_name (char *name, int unit)
436 sprintf (name, "/dev/ataraid/d%c", unit + '0');
438 #endif
440 /* Check if DEVICE can be read. If an error occurs, return zero,
441 otherwise return non-zero. */
443 check_device (const char *device)
445 char buf[512];
446 FILE *fp;
448 /* If DEVICE is empty, just return 1. */
449 if (*device == 0)
450 return 1;
452 fp = fopen (device, "r");
453 if (! fp)
455 switch (errno)
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;
473 /* Error opening the device. */
474 return 0;
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. */
486 struct hd_geometry hdg;
487 struct stat st;
489 if (fstat (fileno (fp), &st))
490 return 0;
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;
499 # endif /* ! CDROM_GET_CAPABILITY */
500 #endif /* __linux__ */
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__ */
509 /* Attempt to read the first sector. */
510 if (fread (buf, 1, 512, fp) != 512)
512 fclose (fp);
513 return 0;
516 fclose (fp);
517 return 1;
520 /* Read mapping information from FP, and write it to MAP. */
521 static int
522 read_device_map (FILE *fp, char **map, const char *map_file)
524 auto void show_error (int no, const char *msg);
525 auto void show_warning (int no, const char *msg, ...);
527 auto void show_error (int no, const char *msg)
529 fprintf (stderr, "%s:%d: error: %s\n", map_file, no, msg);
532 auto void show_warning (int no, const char *msg, ...)
534 va_list ap;
536 va_start (ap, msg);
537 fprintf (stderr, "%s:%d: warning: ", map_file, no);
538 vfprintf (stderr, msg, ap);
539 va_end (ap);
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;
547 while (fgets (buf, sizeof (buf), fp))
549 char *ptr, *eptr;
550 int drive;
551 int is_floppy = 0;
553 /* Increase the number of lines. */
554 line_number++;
556 /* If the first character is '#', skip it. */
557 if (buf[0] == '#')
558 continue;
560 ptr = buf;
561 /* Skip leading spaces. */
562 while (*ptr && isspace (*ptr))
563 ptr++;
565 /* Skip empty lines. */
566 if (! *ptr)
567 continue;
569 if (*ptr != '(')
571 show_error (line_number, "No open parenthesis found");
572 return 0;
575 ptr++;
576 if ((*ptr != 'f' && *ptr != 'h') || *(ptr + 1) != 'd')
578 show_error (line_number, "Bad drive name");
579 return 0;
582 if (*ptr == 'f')
583 is_floppy = 1;
585 ptr += 2;
586 drive = strtoul (ptr, &ptr, 10);
587 if (drive < 0)
589 show_error (line_number, "Bad device number");
590 return 0;
592 else if (drive > 127)
594 show_warning (line_number,
595 "Ignoring %cd%d due to a BIOS limitation",
596 is_floppy ? 'f' : 'h', drive);
597 continue;
600 if (! is_floppy)
601 drive += 0x80;
603 if (*ptr != ')')
605 show_error (line_number, "No close parenthesis found");
606 return 0;
609 ptr++;
610 /* Skip spaces. */
611 while (*ptr && isspace (*ptr))
612 ptr++;
614 if (! *ptr)
616 show_error (line_number, "No filename found");
617 return 0;
620 /* Terminate the filename. */
621 eptr = ptr;
622 while (*eptr && ! isspace (*eptr))
623 eptr++;
624 *eptr = 0;
626 /* Multiple entries for a given drive is not allowed. */
627 if (map[drive])
629 show_error (line_number, "Duplicated entry found");
630 return 0;
633 map[drive] = strdup (ptr);
634 assert (map[drive]);
637 return 1;
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. */
647 init_device_map (char ***map, const char *map_file, int floppy_disks)
649 int i;
650 int num_hd = 0;
651 FILE *fp = 0;
653 assert (map);
654 assert (*map == 0);
655 *map = malloc (NUM_DISKS * sizeof (char *));
656 assert (*map);
658 /* Probe devices for creating the device map. */
660 /* Initialize DEVICE_MAP. */
661 for (i = 0; i < NUM_DISKS; i++)
662 (*map)[i] = 0;
664 if (map_file)
666 /* Open the device map file. */
667 fp = fopen (map_file, "r");
668 if (fp)
670 int ret;
672 ret = read_device_map (fp, *map, map_file);
673 fclose (fp);
674 return ret;
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");
684 if (map_file)
685 /* Try to open the device map file to write the probed data. */
686 fp = fopen (map_file, "w");
688 /* Floppies. */
689 for (i = 0; i < floppy_disks; i++)
691 char name[16];
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);
699 if (check_device (name))
701 (*map)[i] = strdup (name);
702 assert ((*map)[i]);
706 #ifdef __linux__
707 if (have_devfs ())
709 while (1)
711 char discn[32];
712 char name[PATH_MAX];
713 struct stat st;
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;
721 if (realpath (discn, name))
723 strcat (name, "/disc");
724 (*map)[num_hd + 0x80] = strdup (name);
725 assert ((*map)[num_hd + 0x80]);
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);
732 num_hd++;
735 /* OK, close the device map file if opened. */
736 if (fp)
737 fclose (fp);
739 return 1;
741 #endif /* __linux__ */
743 /* IDE disks. */
744 for (i = 0; i < 8; i++)
746 char name[16];
748 get_ide_disk_name (name, i);
749 if (check_device (name))
751 (*map)[num_hd + 0x80] = strdup (name);
752 assert ((*map)[num_hd + 0x80]);
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);
758 num_hd++;
762 #ifdef __linux__
763 /* ATARAID disks. */
764 for (i = 0; i < 8; i++)
766 char name[20];
768 get_ataraid_disk_name (name, i);
769 if (check_device (name))
771 (*map)[num_hd + 0x80] = strdup (name);
772 assert ((*map)[num_hd + 0x80]);
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);
778 num_hd++;
781 #endif /* __linux__ */
783 /* The rest is SCSI disks. */
784 for (i = 0; i < 16; i++)
786 char name[16];
788 get_scsi_disk_name (name, i);
789 if (check_device (name))
791 (*map)[num_hd + 0x80] = strdup (name);
792 assert ((*map)[num_hd + 0x80]);
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);
798 num_hd++;
802 #ifdef __linux__
803 /* This is for DAC960 - we have
804 /dev/rd/c<controller>d<logical drive>p<partition>.
806 DAC960 driver currently supports up to 8 controllers, 32 logical
807 drives, and 7 partitions. */
809 int controller, drive;
811 for (controller = 0; controller < 8; controller++)
813 for (drive = 0; drive < 15; drive++)
815 char name[24];
817 get_dac960_disk_name (name, controller, drive);
818 if (check_device (name))
820 (*map)[num_hd + 0x80] = strdup (name);
821 assert ((*map)[num_hd + 0x80]);
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);
827 num_hd++;
832 #endif /* __linux__ */
834 /* OK, close the device map file if opened. */
835 if (fp)
836 fclose (fp);
838 return 1;
841 /* Restore the memory consumed for MAP. */
842 void
843 restore_device_map (char **map)
845 int i;
847 for (i = 0; i < NUM_DISKS; i++)
848 if (map[i])
849 free (map[i]);
851 free (map);
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. */
859 is_disk_device (char **map, int drive)
861 struct stat st;
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);
870 write_to_partition (char **map, int drive, int partition,
871 int sector, int size, const char *buf)
873 char dev[PATH_MAX]; /* XXX */
874 int fd;
876 if ((partition & 0x00FF00) != 0x00FF00)
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;
884 assert (map[drive] != 0);
886 strcpy (dev, map[drive]);
887 if (have_devfs ())
889 if (strcmp (dev + strlen(dev) - 5, "/disc") == 0)
890 strcpy (dev + strlen(dev) - 5, "/part");
892 sprintf (dev + strlen(dev), "%d", ((partition >> 16) & 0xFF) + 1);
894 /* Open the partition. */
895 fd = open (dev, O_RDWR);
896 if (fd < 0)
898 errnum = ERR_NO_PART;
899 return 0;
902 #if defined(__linux__) && (!defined(__GLIBC__) || \
903 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
904 /* Maybe libc doesn't have large file support. */
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);
912 offset = (loff_t) sector * (loff_t) SECTOR_SIZE;
913 if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
915 errnum = ERR_DEV_VALUES;
916 return 0;
919 #else
921 off_t offset = (off_t) sector * (off_t) SECTOR_SIZE;
923 if (lseek (fd, offset, SEEK_SET) != offset)
925 errnum = ERR_DEV_VALUES;
926 return 0;
929 #endif
931 if (write (fd, buf, size * SECTOR_SIZE) != (size * SECTOR_SIZE))
933 close (fd);
934 errnum = ERR_WRITE;
935 return 0;
938 sync (); /* Paranoia. */
939 close (fd);
941 return 1;
943 #endif /* __linux__ */