]> git.proxmox.com Git - grub2.git/blob - grub-core/kern/emu/hostdisk.c
8f30c8a09e8b2348c48d162020ac70bea9678c0f
[grub2.git] / grub-core / kern / emu / hostdisk.c
1 /* hostdisk.c - emulate biosdisk */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2004,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
5 *
6 * GRUB 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 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/disk.h>
21 #include <grub/partition.h>
22 #include <grub/msdos_partition.h>
23 #include <grub/types.h>
24 #include <grub/err.h>
25 #include <grub/emu/misc.h>
26 #include <grub/emu/hostdisk.h>
27 #include <grub/emu/getroot.h>
28 #include <grub/misc.h>
29 #include <grub/i18n.h>
30 #include <grub/list.h>
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <assert.h>
37 #include <unistd.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <limits.h>
43
44 #ifdef __linux__
45 # include <sys/ioctl.h> /* ioctl */
46 # if !defined(__GLIBC__) || \
47 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
48 /* Maybe libc doesn't have large file support. */
49 # include <linux/unistd.h> /* _llseek */
50 # endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
51 # ifndef BLKFLSBUF
52 # define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
53 # endif /* ! BLKFLSBUF */
54 # include <sys/ioctl.h> /* ioctl */
55 # ifndef HDIO_GETGEO
56 # define HDIO_GETGEO 0x0301 /* get device geometry */
57 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
58 defined. */
59 struct hd_geometry
60 {
61 unsigned char heads;
62 unsigned char sectors;
63 unsigned short cylinders;
64 unsigned long start;
65 };
66 # endif /* ! HDIO_GETGEO */
67 # ifndef BLKGETSIZE64
68 # define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
69 # endif /* ! BLKGETSIZE64 */
70 # ifndef MAJOR
71 # ifndef MINORBITS
72 # define MINORBITS 8
73 # endif /* ! MINORBITS */
74 # define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
75 # endif /* ! MAJOR */
76 # ifndef FLOPPY_MAJOR
77 # define FLOPPY_MAJOR 2
78 # endif /* ! FLOPPY_MAJOR */
79 # ifndef LOOP_MAJOR
80 # define LOOP_MAJOR 7
81 # endif /* ! LOOP_MAJOR */
82 #endif /* __linux__ */
83
84 #ifdef __CYGWIN__
85 # include <sys/ioctl.h>
86 # include <cygwin/fs.h> /* BLKGETSIZE64 */
87 # include <cygwin/hdreg.h> /* HDIO_GETGEO */
88 # define MAJOR(dev) ((unsigned) ((dev) >> 16))
89 # define FLOPPY_MAJOR 2
90 #endif
91
92 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
93 # include <sys/disk.h> /* DIOCGMEDIASIZE */
94 # include <sys/param.h>
95 # include <sys/sysctl.h>
96 # define MAJOR(dev) major(dev)
97 # define FLOPPY_MAJOR 2
98 #endif
99
100 #if defined(__APPLE__)
101 # include <sys/disk.h>
102 #endif
103
104 #ifdef HAVE_DEVICE_MAPPER
105 # include <libdevmapper.h>
106 #endif
107
108 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
109 #include <libgeom.h>
110 #elif defined(__NetBSD__)
111 # define HAVE_DIOCGDINFO
112 # include <sys/ioctl.h>
113 # include <sys/disklabel.h> /* struct disklabel */
114 #else /* !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) */
115 # undef HAVE_DIOCGDINFO
116 #endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
117
118 #if defined(__NetBSD__)
119 # ifdef HAVE_GETRAWPARTITION
120 # include <util.h> /* getrawpartition */
121 # endif /* HAVE_GETRAWPARTITION */
122 # include <sys/fdio.h>
123 # ifndef FLOPPY_MAJOR
124 # define FLOPPY_MAJOR 2
125 # endif /* ! FLOPPY_MAJOR */
126 # ifndef RAW_FLOPPY_MAJOR
127 # define RAW_FLOPPY_MAJOR 9
128 # endif /* ! RAW_FLOPPY_MAJOR */
129 #endif /* defined(__NetBSD__) */
130
131 struct
132 {
133 char *drive;
134 char *device;
135 int device_map;
136 } map[256];
137
138 struct grub_util_biosdisk_data
139 {
140 char *dev;
141 int access_mode;
142 int fd;
143 int is_disk;
144 int device_map;
145 };
146
147 #ifdef __linux__
148 /* Check if we have devfs support. */
149 static int
150 have_devfs (void)
151 {
152 static int dev_devfsd_exists = -1;
153
154 if (dev_devfsd_exists < 0)
155 {
156 struct stat st;
157
158 dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
159 }
160
161 return dev_devfsd_exists;
162 }
163 #endif /* __linux__ */
164
165 #if defined(__NetBSD__)
166 /* Adjust device driver parameters. This function should be called just
167 after successfully opening the device. For now, it simply prevents the
168 floppy driver from retrying operations on failure, as otherwise the
169 driver takes a while to abort when there is no floppy in the drive. */
170 static void
171 configure_device_driver (int fd)
172 {
173 struct stat st;
174
175 if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
176 return;
177 if (major(st.st_rdev) == RAW_FLOPPY_MAJOR)
178 {
179 int floppy_opts;
180
181 if (ioctl (fd, FDIOCGETOPTS, &floppy_opts) == -1)
182 return;
183 floppy_opts |= FDOPT_NORETRY;
184 if (ioctl (fd, FDIOCSETOPTS, &floppy_opts) == -1)
185 return;
186 }
187 }
188 #endif /* defined(__NetBSD__) */
189
190 static int
191 find_grub_drive (const char *name)
192 {
193 unsigned int i;
194
195 if (name)
196 {
197 for (i = 0; i < ARRAY_SIZE (map); i++)
198 if (map[i].drive && ! strcmp (map[i].drive, name))
199 return i;
200 }
201
202 return -1;
203 }
204
205 static int
206 find_free_slot (void)
207 {
208 unsigned int i;
209
210 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
211 if (! map[i].drive)
212 return i;
213
214 return -1;
215 }
216
217 static int
218 grub_util_biosdisk_iterate (int (*hook) (const char *name))
219 {
220 unsigned i;
221
222 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
223 if (map[i].drive && hook (map[i].drive))
224 return 1;
225
226 return 0;
227 }
228
229 static grub_err_t
230 grub_util_biosdisk_open (const char *name, grub_disk_t disk)
231 {
232 int drive;
233 struct stat st;
234 struct grub_util_biosdisk_data *data;
235
236 drive = find_grub_drive (name);
237 if (drive < 0)
238 return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
239 "no mapping exists for `%s'", name);
240
241 disk->id = drive;
242 disk->data = data = xmalloc (sizeof (struct grub_util_biosdisk_data));
243 data->dev = NULL;
244 data->access_mode = 0;
245 data->fd = -1;
246 data->is_disk = 0;
247 data->device_map = map[drive].device_map;
248
249 /* Get the size. */
250 #if defined(__MINGW32__)
251 {
252 grub_uint64_t size;
253
254 size = grub_util_get_disk_size (map[drive].device);
255
256 if (size % 512)
257 grub_util_error ("unaligned device size");
258
259 disk->total_sectors = size >> 9;
260
261 grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
262
263 return GRUB_ERR_NONE;
264 }
265 #elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \
266 defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
267 {
268 # if defined(__NetBSD__)
269 struct disklabel label;
270 # else
271 unsigned long long nr;
272 # endif
273 int fd;
274
275 fd = open (map[drive].device, O_RDONLY);
276 if (fd == -1)
277 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device);
278
279 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
280 if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
281 # else
282 if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
283 # endif
284 {
285 close (fd);
286 goto fail;
287 }
288 data->is_disk = 1;
289
290 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
291 if (ioctl (fd, DIOCGMEDIASIZE, &nr))
292 # elif defined(__APPLE__)
293 if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr))
294 # elif defined(__NetBSD__)
295 configure_device_driver (fd);
296 if (ioctl (fd, DIOCGDINFO, &label) == -1)
297 # else
298 if (ioctl (fd, BLKGETSIZE64, &nr))
299 # endif
300 {
301 close (fd);
302 goto fail;
303 }
304
305 close (fd);
306
307 # if defined (__APPLE__)
308 disk->total_sectors = nr;
309 # elif defined(__NetBSD__)
310 disk->total_sectors = label.d_secperunit;
311 # else
312 disk->total_sectors = nr / 512;
313
314 if (nr % 512)
315 grub_util_error ("unaligned device size");
316 # endif
317
318 grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
319
320 return GRUB_ERR_NONE;
321 }
322
323 fail:
324 /* In GNU/Hurd, stat() will return the right size. */
325 #elif !defined (__GNU__)
326 # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
327 #endif
328 if (stat (map[drive].device, &st) < 0)
329 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot stat `%s'", map[drive].device);
330
331 disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS;
332
333 grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
334
335 return GRUB_ERR_NONE;
336 }
337
338 int
339 grub_util_device_is_mapped (const char *dev)
340 {
341 #ifdef HAVE_DEVICE_MAPPER
342 struct stat st;
343
344 if (!grub_device_mapper_supported ())
345 return 0;
346
347 if (stat (dev, &st) < 0)
348 return 0;
349
350 return dm_is_dm_major (major (st.st_rdev));
351 #else
352 return 0;
353 #endif /* HAVE_DEVICE_MAPPER */
354 }
355
356 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
357 /* FIXME: geom actually gives us the whole container hierarchy.
358 It can be used more efficiently than this. */
359 static void
360 follow_geom_up (const char *name, grub_disk_addr_t *off_out, char **name_out)
361 {
362 struct gmesh mesh;
363 struct gclass *class;
364 int error;
365 struct ggeom *geom;
366
367 grub_util_info ("following geom '%s'", name);
368
369 error = geom_gettree (&mesh);
370 if (error != 0)
371 grub_util_error ("couldn't open geom");
372
373 LIST_FOREACH (class, &mesh.lg_class, lg_class)
374 if (strcasecmp (class->lg_name, "part") == 0)
375 break;
376 if (!class)
377 grub_util_error ("couldn't open geom part");
378
379 LIST_FOREACH (geom, &class->lg_geom, lg_geom)
380 {
381 struct gprovider *provider;
382 LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
383 if (strcmp (provider->lg_name, name) == 0)
384 {
385 char *name_tmp = xstrdup (geom->lg_name);
386 grub_disk_addr_t off = 0;
387 struct gconfig *config;
388 grub_util_info ("geom '%s' has parent '%s'", name, geom->lg_name);
389
390 follow_geom_up (name_tmp, &off, name_out);
391 free (name_tmp);
392 LIST_FOREACH (config, &provider->lg_config, lg_config)
393 if (strcasecmp (config->lg_name, "start") == 0)
394 off += strtoull (config->lg_val, 0, 10);
395 if (off_out)
396 *off_out = off;
397 return;
398 }
399 }
400 grub_util_info ("geom '%s' has no parent", name);
401 if (name_out)
402 *name_out = xstrdup (name);
403 if (off_out)
404 *off_out = 0;
405 }
406
407 static grub_disk_addr_t
408 find_partition_start (const char *dev)
409 {
410 grub_disk_addr_t out;
411 if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0)
412 return 0;
413 follow_geom_up (dev + sizeof ("/dev/") - 1, &out, NULL);
414
415 return out;
416 }
417 #elif defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO)
418 static grub_disk_addr_t
419 find_partition_start (const char *dev)
420 {
421 int fd;
422 # if !defined(HAVE_DIOCGDINFO)
423 struct hd_geometry hdg;
424 # else /* defined(HAVE_DIOCGDINFO) */
425 struct disklabel label;
426 int p_index;
427 # endif /* !defined(HAVE_DIOCGDINFO) */
428
429 # ifdef HAVE_DEVICE_MAPPER
430 if (grub_util_device_is_mapped (dev)) {
431 struct dm_task *task = NULL;
432 grub_uint64_t start, length;
433 char *target_type, *params, *space;
434 grub_disk_addr_t partition_start;
435
436 /* If any device-mapper operation fails, we fall back silently to
437 HDIO_GETGEO. */
438 task = dm_task_create (DM_DEVICE_TABLE);
439 if (! task)
440 {
441 grub_dprintf ("hostdisk", "dm_task_create failed\n");
442 goto devmapper_fail;
443 }
444
445 if (! dm_task_set_name (task, dev))
446 {
447 grub_dprintf ("hostdisk", "dm_task_set_name failed\n");
448 goto devmapper_fail;
449 }
450
451 if (! dm_task_run (task))
452 {
453 grub_dprintf ("hostdisk", "dm_task_run failed\n");
454 goto devmapper_fail;
455 }
456
457 dm_get_next_target (task, NULL, &start, &length, &target_type, &params);
458 if (! target_type)
459 {
460 grub_dprintf ("hostdisk", "no dm target\n");
461 goto devmapper_fail;
462 }
463 if (strcmp (target_type, "linear") != 0)
464 {
465 grub_dprintf ("hostdisk", "ignoring dm target %s (not linear)\n",
466 target_type);
467 goto devmapper_fail;
468 }
469 if (! params)
470 {
471 grub_dprintf ("hostdisk", "no dm params\n");
472 goto devmapper_fail;
473 }
474
475 /* The params string for a linear target looks like this:
476 DEVICE-NAME START-SECTOR
477 Parse this out. */
478 space = strchr (params, ' ');
479 if (! space)
480 goto devmapper_fail;
481 errno = 0;
482 partition_start = strtoull (space + 1, NULL, 10);
483 if (errno == 0)
484 {
485 grub_dprintf ("hostdisk", "dm %s starts at %llu\n",
486 dev, (unsigned long long) partition_start);
487 dm_task_destroy (task);
488 return partition_start;
489 }
490
491 devmapper_fail:
492 if (task)
493 dm_task_destroy (task);
494 }
495 # endif /* HAVE_DEVICE_MAPPER */
496
497 fd = open (dev, O_RDONLY);
498 if (fd == -1)
499 {
500 grub_error (GRUB_ERR_BAD_DEVICE,
501 # if !defined(HAVE_DIOCGDINFO)
502 "cannot open `%s' while attempting to get disk geometry", dev);
503 # else /* defined(HAVE_DIOCGDINFO) */
504 "cannot open `%s' while attempting to get disk label", dev);
505 # endif /* !defined(HAVE_DIOCGDINFO) */
506 return 0;
507 }
508
509 # if !defined(HAVE_DIOCGDINFO)
510 if (ioctl (fd, HDIO_GETGEO, &hdg))
511 # else /* defined(HAVE_DIOCGDINFO) */
512 # if defined(__NetBSD__)
513 configure_device_driver (fd);
514 # endif /* defined(__NetBSD__) */
515 if (ioctl (fd, DIOCGDINFO, &label) == -1)
516 # endif /* !defined(HAVE_DIOCGDINFO) */
517 {
518 grub_error (GRUB_ERR_BAD_DEVICE,
519 # if !defined(HAVE_DIOCGDINFO)
520 "cannot get disk geometry of `%s'", dev);
521 # else /* defined(HAVE_DIOCGDINFO) */
522 "cannot get disk label of `%s'", dev);
523 # endif /* !defined(HAVE_DIOCGDINFO) */
524 close (fd);
525 return 0;
526 }
527
528 close (fd);
529
530 # if !defined(HAVE_DIOCGDINFO)
531 return hdg.start;
532 # else /* defined(HAVE_DIOCGDINFO) */
533 if (dev[0])
534 p_index = dev[strlen(dev) - 1] - 'a';
535 else
536 p_index = -1;
537
538 if (p_index >= label.d_npartitions || p_index < 0)
539 {
540 grub_error (GRUB_ERR_BAD_DEVICE,
541 "no disk label entry for `%s'", dev);
542 return 0;
543 }
544 return (grub_disk_addr_t) label.d_partitions[p_index].p_offset;
545 # endif /* !defined(HAVE_DIOCGDINFO) */
546 }
547 #endif /* __linux__ || __CYGWIN__ || HAVE_DIOCGDINFO */
548
549 #ifdef __linux__
550 /* Cache of partition start sectors for each disk. */
551 struct linux_partition_cache
552 {
553 struct linux_partition_cache *next;
554 char *dev;
555 unsigned long start;
556 int partno;
557 };
558
559 struct linux_partition_cache *linux_partition_cache_list;
560
561 static int
562 linux_find_partition (char *dev, grub_disk_addr_t sector)
563 {
564 size_t len = strlen (dev);
565 const char *format;
566 char *p;
567 int i;
568 char real_dev[PATH_MAX];
569 struct linux_partition_cache *cache;
570
571 strcpy(real_dev, dev);
572
573 if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
574 {
575 p = real_dev + len - 4;
576 format = "part%d";
577 }
578 else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
579 {
580 p = real_dev + len;
581 format = "p%d";
582 }
583 else
584 {
585 p = real_dev + len;
586 format = "%d";
587 }
588
589 for (cache = linux_partition_cache_list; cache; cache = cache->next)
590 {
591 if (strcmp (cache->dev, dev) == 0 && cache->start == sector)
592 {
593 sprintf (p, format, cache->partno);
594 strcpy (dev, real_dev);
595 return 1;
596 }
597 }
598
599 for (i = 1; i < 10000; i++)
600 {
601 int fd;
602 grub_disk_addr_t start;
603
604 sprintf (p, format, i);
605
606 fd = open (real_dev, O_RDONLY);
607 if (fd == -1)
608 continue;
609 close (fd);
610
611 start = find_partition_start (real_dev);
612 /* We don't care about errors here. */
613 grub_errno = GRUB_ERR_NONE;
614
615 if (start == sector)
616 {
617 struct linux_partition_cache *new_cache_item;
618
619 new_cache_item = xmalloc (sizeof *new_cache_item);
620 new_cache_item->dev = xstrdup (dev);
621 new_cache_item->start = start;
622 new_cache_item->partno = i;
623 grub_list_push (GRUB_AS_LIST_P (&linux_partition_cache_list),
624 GRUB_AS_LIST (new_cache_item));
625
626 strcpy (dev, real_dev);
627 return 1;
628 }
629 }
630
631 return 0;
632 }
633 #endif /* __linux__ */
634
635 static int
636 open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
637 {
638 int fd;
639 struct grub_util_biosdisk_data *data = disk->data;
640
641 #ifdef O_LARGEFILE
642 flags |= O_LARGEFILE;
643 #endif
644 #ifdef O_SYNC
645 flags |= O_SYNC;
646 #endif
647 #ifdef O_FSYNC
648 flags |= O_FSYNC;
649 #endif
650 #ifdef O_BINARY
651 flags |= O_BINARY;
652 #endif
653
654 #ifdef __linux__
655 /* Linux has a bug that the disk cache for a whole disk is not consistent
656 with the one for a partition of the disk. */
657 {
658 int is_partition = 0;
659 char dev[PATH_MAX];
660 grub_disk_addr_t part_start = 0;
661
662 part_start = grub_partition_get_start (disk->partition);
663
664 strcpy (dev, map[disk->id].device);
665 if (disk->partition && sector >= part_start
666 && strncmp (map[disk->id].device, "/dev/", 5) == 0)
667 is_partition = linux_find_partition (dev, part_start);
668
669 if (data->dev && strcmp (data->dev, dev) == 0 &&
670 data->access_mode == (flags & O_ACCMODE))
671 {
672 grub_dprintf ("hostdisk", "reusing open device `%s'\n", dev);
673 fd = data->fd;
674 }
675 else
676 {
677 free (data->dev);
678 if (data->fd != -1)
679 {
680 if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
681 {
682 fsync (data->fd);
683 #ifdef __linux__
684 if (data->is_disk)
685 ioctl (data->fd, BLKFLSBUF, 0);
686 #endif
687 }
688
689 close (data->fd);
690 data->fd = -1;
691 }
692
693 /* Open the partition. */
694 grub_dprintf ("hostdisk", "opening the device `%s' in open_device()\n", dev);
695 fd = open (dev, flags);
696 if (fd < 0)
697 {
698 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev);
699 return -1;
700 }
701
702 data->dev = xstrdup (dev);
703 data->access_mode = (flags & O_ACCMODE);
704 data->fd = fd;
705 }
706
707 if (is_partition)
708 sector -= part_start;
709 }
710 #else /* ! __linux__ */
711 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
712 int sysctl_flags, sysctl_oldflags;
713 size_t sysctl_size = sizeof (sysctl_flags);
714
715 if (sysctlbyname ("kern.geom.debugflags", &sysctl_oldflags, &sysctl_size, NULL, 0))
716 {
717 grub_error (GRUB_ERR_BAD_DEVICE, "cannot get current flags of sysctl kern.geom.debugflags");
718 return -1;
719 }
720 sysctl_flags = sysctl_oldflags | 0x10;
721 if (! (sysctl_oldflags & 0x10)
722 && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_flags, sysctl_size))
723 {
724 grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags of sysctl kern.geom.debugflags");
725 return -1;
726 }
727 #endif
728
729 if (data->dev && strcmp (data->dev, map[disk->id].device) == 0 &&
730 data->access_mode == (flags & O_ACCMODE))
731 {
732 grub_dprintf ("hostdisk", "reusing open device `%s'\n", data->dev);
733 fd = data->fd;
734 }
735 else
736 {
737 free (data->dev);
738 if (data->fd != -1)
739 {
740 if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
741 {
742 fsync (data->fd);
743 #ifdef __linux__
744 if (data->is_disk)
745 ioctl (data->fd, BLKFLSBUF, 0);
746 #endif
747 }
748 close (data->fd);
749 data->fd = -1;
750 }
751
752 fd = open (map[disk->id].device, flags);
753 if (fd >= 0)
754 {
755 data->dev = xstrdup (map[disk->id].device);
756 data->access_mode = (flags & O_ACCMODE);
757 data->fd = fd;
758 }
759 }
760
761 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
762 if (! (sysctl_oldflags & 0x10)
763 && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_oldflags, sysctl_size))
764 {
765 grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags back to the old value for sysctl kern.geom.debugflags");
766 return -1;
767 }
768 #endif
769
770 #if defined(__APPLE__)
771 /* If we can't have exclusive access, try shared access */
772 if (fd < 0)
773 fd = open(map[disk->id].device, flags | O_SHLOCK);
774 #endif
775
776 if (fd < 0)
777 {
778 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' in open_device()", map[disk->id].device);
779 return -1;
780 }
781 #endif /* ! __linux__ */
782
783 #if defined(__NetBSD__)
784 configure_device_driver (fd);
785 #endif /* defined(__NetBSD__) */
786
787 #if defined(__linux__) && (!defined(__GLIBC__) || \
788 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
789 /* Maybe libc doesn't have large file support. */
790 {
791 loff_t offset, result;
792 static int _llseek (uint filedes, ulong hi, ulong lo,
793 loff_t *res, uint wh);
794 _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
795 loff_t *, res, uint, wh);
796
797 offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
798 if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
799 {
800 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
801 close (fd);
802 return -1;
803 }
804 }
805 #else
806 {
807 off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
808
809 if (lseek (fd, offset, SEEK_SET) != offset)
810 {
811 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
812 close (fd);
813 return -1;
814 }
815 }
816 #endif
817
818 return fd;
819 }
820
821 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
822 error occurs, otherwise return LEN. */
823 static ssize_t
824 nread (int fd, char *buf, size_t len)
825 {
826 ssize_t size = len;
827
828 while (len)
829 {
830 ssize_t ret = read (fd, buf, len);
831
832 if (ret <= 0)
833 {
834 if (errno == EINTR)
835 continue;
836 else
837 return ret;
838 }
839
840 len -= ret;
841 buf += ret;
842 }
843
844 return size;
845 }
846
847 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
848 error occurs, otherwise return LEN. */
849 static ssize_t
850 nwrite (int fd, const char *buf, size_t len)
851 {
852 ssize_t size = len;
853
854 while (len)
855 {
856 ssize_t ret = write (fd, buf, len);
857
858 if (ret <= 0)
859 {
860 if (errno == EINTR)
861 continue;
862 else
863 return ret;
864 }
865
866 len -= ret;
867 buf += ret;
868 }
869
870 return size;
871 }
872
873 static grub_err_t
874 grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
875 grub_size_t size, char *buf)
876 {
877 int fd;
878
879 /* Split pre-partition and partition reads. */
880 if (disk->partition && sector < disk->partition->start
881 && sector + size > disk->partition->start)
882 {
883 grub_err_t err;
884 err = grub_util_biosdisk_read (disk, sector,
885 disk->partition->start - sector,
886 buf);
887 if (err)
888 return err;
889
890 return grub_util_biosdisk_read (disk, disk->partition->start,
891 size - (disk->partition->start - sector),
892 buf + ((disk->partition->start - sector)
893 << GRUB_DISK_SECTOR_BITS));
894 }
895
896 fd = open_device (disk, sector, O_RDONLY);
897 if (fd < 0)
898 return grub_errno;
899
900 #ifdef __linux__
901 if (sector == 0 && size > 1)
902 {
903 /* Work around a bug in Linux ez remapping. Linux remaps all
904 sectors that are read together with the MBR in one read. It
905 should only remap the MBR, so we split the read in two
906 parts. -jochen */
907 if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
908 {
909 grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device);
910 return grub_errno;
911 }
912
913 buf += GRUB_DISK_SECTOR_SIZE;
914 size--;
915 }
916 #endif /* __linux__ */
917
918 if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS)
919 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
920 grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device);
921
922 return grub_errno;
923 }
924
925 static grub_err_t
926 grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
927 grub_size_t size, const char *buf)
928 {
929 int fd;
930
931 /* Split pre-partition and partition writes. */
932 if (disk->partition && sector < disk->partition->start
933 && sector + size > disk->partition->start)
934 {
935 grub_err_t err;
936 err = grub_util_biosdisk_write (disk, sector,
937 disk->partition->start - sector,
938 buf);
939 if (err)
940 return err;
941
942 return grub_util_biosdisk_write (disk, disk->partition->start,
943 size - (disk->partition->start - sector),
944 buf + ((disk->partition->start - sector)
945 << GRUB_DISK_SECTOR_BITS));
946 }
947
948 fd = open_device (disk, sector, O_WRONLY);
949 if (fd < 0)
950 return grub_errno;
951
952 if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS)
953 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
954 grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device);
955
956 return grub_errno;
957 }
958
959 grub_err_t
960 grub_util_biosdisk_flush (struct grub_disk *disk)
961 {
962 struct grub_util_biosdisk_data *data = disk->data;
963
964 if (disk->dev->id != GRUB_DISK_DEVICE_BIOSDISK_ID)
965 return GRUB_ERR_NONE;
966 if (data->fd == -1)
967 {
968 data->fd = open_device (disk, 0, O_RDONLY);
969 if (data->fd < 0)
970 return grub_errno;
971 }
972 fsync (data->fd);
973 #ifdef __linux__
974 if (data->is_disk)
975 ioctl (data->fd, BLKFLSBUF, 0);
976 #endif
977 return GRUB_ERR_NONE;
978 }
979
980 static void
981 grub_util_biosdisk_close (struct grub_disk *disk)
982 {
983 struct grub_util_biosdisk_data *data = disk->data;
984
985 free (data->dev);
986 if (data->fd != -1)
987 {
988 if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
989 grub_util_biosdisk_flush (disk);
990 close (data->fd);
991 }
992 free (data);
993 }
994
995 static struct grub_disk_dev grub_util_biosdisk_dev =
996 {
997 .name = "biosdisk",
998 .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
999 .iterate = grub_util_biosdisk_iterate,
1000 .open = grub_util_biosdisk_open,
1001 .close = grub_util_biosdisk_close,
1002 .read = grub_util_biosdisk_read,
1003 .write = grub_util_biosdisk_write,
1004 .next = 0
1005 };
1006
1007 static void
1008 read_device_map (const char *dev_map)
1009 {
1010 FILE *fp;
1011 char buf[1024]; /* XXX */
1012 int lineno = 0;
1013 struct stat st;
1014
1015 auto void show_error (const char *msg);
1016 void show_error (const char *msg)
1017 {
1018 grub_util_error ("%s:%d: %s", dev_map, lineno, msg);
1019 }
1020
1021 if (dev_map[0] == '\0')
1022 {
1023 grub_util_info (_("no device.map"));
1024 return;
1025 }
1026
1027 fp = fopen (dev_map, "r");
1028 if (! fp)
1029 {
1030 grub_util_info (_("cannot open `%s'"), dev_map);
1031 return;
1032 }
1033
1034 while (fgets (buf, sizeof (buf), fp))
1035 {
1036 char *p = buf;
1037 char *e;
1038 int drive;
1039
1040 lineno++;
1041
1042 /* Skip leading spaces. */
1043 while (*p && isspace (*p))
1044 p++;
1045
1046 /* If the first character is `#' or NUL, skip this line. */
1047 if (*p == '\0' || *p == '#')
1048 continue;
1049
1050 if (*p != '(')
1051 show_error ("No open parenthesis found");
1052
1053 p++;
1054 /* Find a free slot. */
1055 drive = find_free_slot ();
1056 if (drive < 0)
1057 show_error ("Map table size exceeded");
1058
1059 e = p;
1060 p = strchr (p, ')');
1061 if (! p)
1062 show_error ("No close parenthesis found");
1063
1064 map[drive].drive = xmalloc (p - e + sizeof ('\0'));
1065 strncpy (map[drive].drive, e, p - e + sizeof ('\0'));
1066 map[drive].drive[p - e] = '\0';
1067 map[drive].device_map = 1;
1068
1069 p++;
1070 /* Skip leading spaces. */
1071 while (*p && isspace (*p))
1072 p++;
1073
1074 if (*p == '\0')
1075 show_error ("No filename found");
1076
1077 /* NUL-terminate the filename. */
1078 e = p;
1079 while (*e && ! isspace (*e))
1080 e++;
1081 *e = '\0';
1082
1083 #ifdef __MINGW32__
1084 (void) st;
1085 if (grub_util_get_disk_size (p) == -1LL)
1086 #else
1087 if (stat (p, &st) == -1)
1088 #endif
1089 {
1090 free (map[drive].drive);
1091 map[drive].drive = NULL;
1092 grub_util_info ("Cannot stat `%s', skipping", p);
1093 continue;
1094 }
1095
1096 #ifdef __linux__
1097 /* On Linux, the devfs uses symbolic links horribly, and that
1098 confuses the interface very much, so use realpath to expand
1099 symbolic links. Leave /dev/mapper/ alone, though. */
1100 if (strncmp (p, "/dev/mapper/", 12) != 0)
1101 {
1102 map[drive].device = xmalloc (PATH_MAX);
1103 if (! realpath (p, map[drive].device))
1104 grub_util_error ("cannot get the real path of `%s'", p);
1105 }
1106 else
1107 #endif
1108 map[drive].device = xstrdup (p);
1109 }
1110
1111 fclose (fp);
1112 }
1113
1114 void
1115 grub_util_biosdisk_init (const char *dev_map)
1116 {
1117 read_device_map (dev_map);
1118 grub_disk_dev_register (&grub_util_biosdisk_dev);
1119 }
1120
1121 void
1122 grub_util_biosdisk_fini (void)
1123 {
1124 unsigned i;
1125
1126 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
1127 {
1128 if (map[i].drive)
1129 free (map[i].drive);
1130 if (map[i].device)
1131 free (map[i].device);
1132 map[i].drive = map[i].device = NULL;
1133 }
1134
1135 grub_disk_dev_unregister (&grub_util_biosdisk_dev);
1136 }
1137
1138 /*
1139 * Note: we do not use the new partition naming scheme as dos_part does not
1140 * necessarily correspond to an msdos partition.
1141 */
1142 static char *
1143 make_device_name (int drive, int dos_part, int bsd_part)
1144 {
1145 char *ret;
1146 char *dos_part_str = NULL;
1147 char *bsd_part_str = NULL;
1148
1149 if (dos_part >= 0)
1150 dos_part_str = xasprintf (",%d", dos_part + 1);
1151
1152 if (bsd_part >= 0)
1153 bsd_part_str = xasprintf (",%d", bsd_part + 1);
1154
1155 ret = xasprintf ("%s%s%s", map[drive].drive,
1156 dos_part_str ? : "",
1157 bsd_part_str ? : "");
1158
1159 if (dos_part_str)
1160 free (dos_part_str);
1161
1162 if (bsd_part_str)
1163 free (bsd_part_str);
1164
1165 return ret;
1166 }
1167
1168 #ifdef HAVE_DEVICE_MAPPER
1169 static int
1170 grub_util_get_dm_node_linear_info (const char *dev,
1171 int *maj, int *min)
1172 {
1173 struct dm_task *dmt;
1174 void *next = NULL;
1175 uint64_t length, start;
1176 char *target, *params;
1177 char *ptr;
1178 int major, minor;
1179
1180 dmt = dm_task_create(DM_DEVICE_TABLE);
1181 if (!dmt)
1182 return 0;
1183
1184 if (!dm_task_set_name(dmt, dev))
1185 return 0;
1186 dm_task_no_open_count(dmt);
1187 if (!dm_task_run(dmt))
1188 return 0;
1189 next = dm_get_next_target(dmt, next, &start, &length,
1190 &target, &params);
1191 if (grub_strcmp (target, "linear") != 0)
1192 return 0;
1193 major = grub_strtoul (params, &ptr, 10);
1194 if (grub_errno)
1195 {
1196 grub_errno = GRUB_ERR_NONE;
1197 return 0;
1198 }
1199 if (*ptr != ':')
1200 return 0;
1201 ptr++;
1202 minor = grub_strtoul (ptr, 0, 10);
1203 if (grub_errno)
1204 {
1205 grub_errno = GRUB_ERR_NONE;
1206 return 0;
1207 }
1208 if (maj)
1209 *maj = major;
1210 if (min)
1211 *min = minor;
1212 return 1;
1213 }
1214 #endif
1215
1216 static char *
1217 convert_system_partition_to_system_disk (const char *os_dev, struct stat *st)
1218 {
1219 #if defined(__linux__)
1220 char *path = xmalloc (PATH_MAX);
1221 if (! realpath (os_dev, path))
1222 return NULL;
1223
1224 if (strncmp ("/dev/", path, 5) == 0)
1225 {
1226 char *p = path + 5;
1227
1228 /* If this is an IDE disk. */
1229 if (strncmp ("ide/", p, 4) == 0)
1230 {
1231 p = strstr (p, "part");
1232 if (p)
1233 strcpy (p, "disc");
1234
1235 return path;
1236 }
1237
1238 /* If this is a SCSI disk. */
1239 if (strncmp ("scsi/", p, 5) == 0)
1240 {
1241 p = strstr (p, "part");
1242 if (p)
1243 strcpy (p, "disc");
1244
1245 return path;
1246 }
1247
1248 /* If this is a DAC960 disk. */
1249 if (strncmp ("rd/c", p, 4) == 0)
1250 {
1251 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
1252 p = strchr (p, 'p');
1253 if (p)
1254 *p = '\0';
1255
1256 return path;
1257 }
1258
1259 /* If this is a Mylex AcceleRAID Array. */
1260 if (strncmp ("rs/c", p, 4) == 0)
1261 {
1262 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
1263 p = strchr (p, 'p');
1264 if (p)
1265 *p = '\0';
1266
1267 return path;
1268 }
1269 /* If this is a CCISS disk. */
1270 if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
1271 {
1272 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
1273 p = strchr (p, 'p');
1274 if (p)
1275 *p = '\0';
1276
1277 return path;
1278 }
1279
1280 /* If this is a Compaq Intelligent Drive Array. */
1281 if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
1282 {
1283 /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
1284 p = strchr (p, 'p');
1285 if (p)
1286 *p = '\0';
1287
1288 return path;
1289 }
1290
1291 /* If this is an I2O disk. */
1292 if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
1293 {
1294 /* /dev/i2o/hd[a-z]([0-9]+)? */
1295 p[sizeof ("i2o/hda") - 1] = '\0';
1296 return path;
1297 }
1298
1299 /* If this is a MultiMediaCard (MMC). */
1300 if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
1301 {
1302 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
1303 p = strchr (p, 'p');
1304 if (p)
1305 *p = '\0';
1306
1307 return path;
1308 }
1309
1310 if (strncmp ("md", p, 2) == 0
1311 && p[2] >= '0' && p[2] <= '9')
1312 {
1313 char *ptr = p + 2;
1314 while (*ptr >= '0' && *ptr <= '9')
1315 ptr++;
1316 *ptr = 0;
1317 return path;
1318 }
1319
1320 /* If this is an IDE, SCSI or Virtio disk. */
1321 if (strncmp ("vdisk", p, 5) == 0
1322 && p[5] >= 'a' && p[5] <= 'z')
1323 {
1324 /* /dev/vdisk[a-z][0-9]* */
1325 p[6] = '\0';
1326 return path;
1327 }
1328 if ((strncmp ("hd", p, 2) == 0
1329 || strncmp ("vd", p, 2) == 0
1330 || strncmp ("sd", p, 2) == 0)
1331 && p[2] >= 'a' && p[2] <= 'z')
1332 {
1333 char *pp = p + 2;
1334 while (*pp >= 'a' && *pp <= 'z')
1335 pp++;
1336 /* /dev/[hsv]d[a-z]+[0-9]* */
1337 *pp = '\0';
1338 return path;
1339 }
1340
1341 /* If this is a Xen virtual block device. */
1342 if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
1343 {
1344 char *pp = p + 3;
1345 while (*pp >= 'a' && *pp <= 'z')
1346 pp++;
1347 /* /dev/xvd[a-z]+[0-9]* */
1348 *pp = '\0';
1349 return path;
1350 }
1351
1352 #ifdef HAVE_DEVICE_MAPPER
1353 /* If this is a DM-RAID device.
1354 Compare os_dev rather than path here, since nodes under
1355 /dev/mapper/ are often symlinks. */
1356 if ((strncmp ("/dev/mapper/", os_dev, 12) == 0))
1357 {
1358 struct dm_tree *tree;
1359 uint32_t maj, min;
1360 struct dm_tree_node *node = NULL, *child;
1361 void *handle;
1362 const char *node_uuid, *mapper_name = NULL, *child_uuid, *child_name;
1363
1364 tree = dm_tree_create ();
1365 if (! tree)
1366 {
1367 grub_dprintf ("hostdisk", "dm_tree_create failed\n");
1368 goto devmapper_out;
1369 }
1370
1371 maj = major (st->st_rdev);
1372 min = minor (st->st_rdev);
1373 if (! dm_tree_add_dev (tree, maj, min))
1374 {
1375 grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
1376 goto devmapper_out;
1377 }
1378
1379 node = dm_tree_find_node (tree, maj, min);
1380 if (! node)
1381 {
1382 grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
1383 goto devmapper_out;
1384 }
1385 node_uuid = dm_tree_node_get_uuid (node);
1386 if (! node_uuid)
1387 {
1388 grub_dprintf ("hostdisk", "%s has no DM uuid\n", path);
1389 node = NULL;
1390 goto devmapper_out;
1391 }
1392 if (strncmp (node_uuid, "LVM-", 4) == 0)
1393 {
1394 grub_dprintf ("hostdisk", "%s is an LVM\n", path);
1395 node = NULL;
1396 goto devmapper_out;
1397 }
1398 if (strncmp (node_uuid, "mpath-", 6) == 0)
1399 {
1400 /* Multipath partitions have partN-mpath-* UUIDs, and are
1401 linear mappings so are handled by
1402 grub_util_get_dm_node_linear_info. Multipath disks are not
1403 linear mappings and must be handled specially. */
1404 grub_dprintf ("hostdisk", "%s is a multipath disk\n", path);
1405 mapper_name = dm_tree_node_get_name (node);
1406 goto devmapper_out;
1407 }
1408 if (strncmp (node_uuid, "DMRAID-", 7) != 0)
1409 {
1410 int major, minor;
1411 const char *node_name;
1412 grub_dprintf ("hostdisk", "%s is not DM-RAID\n", path);
1413
1414 if ((node_name = dm_tree_node_get_name (node))
1415 && grub_util_get_dm_node_linear_info (node_name,
1416 &major, &minor))
1417 {
1418 if (tree)
1419 dm_tree_free (tree);
1420 free (path);
1421 char *ret = grub_find_device (NULL, (major << 8) | minor);
1422 return ret;
1423 }
1424
1425 node = NULL;
1426 goto devmapper_out;
1427 }
1428
1429 handle = NULL;
1430 /* Counter-intuitively, device-mapper refers to the disk-like
1431 device containing a DM-RAID partition device as a "child" of
1432 the partition device. */
1433 child = dm_tree_next_child (&handle, node, 0);
1434 if (! child)
1435 {
1436 grub_dprintf ("hostdisk", "%s has no DM children\n", path);
1437 goto devmapper_out;
1438 }
1439 child_uuid = dm_tree_node_get_uuid (child);
1440 if (! child_uuid)
1441 {
1442 grub_dprintf ("hostdisk", "%s child has no DM uuid\n", path);
1443 goto devmapper_out;
1444 }
1445 else if (strncmp (child_uuid, "DMRAID-", 7) != 0)
1446 {
1447 grub_dprintf ("hostdisk", "%s child is not DM-RAID\n", path);
1448 goto devmapper_out;
1449 }
1450 child_name = dm_tree_node_get_name (child);
1451 if (! child_name)
1452 {
1453 grub_dprintf ("hostdisk", "%s child has no DM name\n", path);
1454 goto devmapper_out;
1455 }
1456 mapper_name = child_name;
1457
1458 devmapper_out:
1459 if (! mapper_name && node)
1460 {
1461 /* This is a DM-RAID disk, not a partition. */
1462 mapper_name = dm_tree_node_get_name (node);
1463 if (! mapper_name)
1464 grub_dprintf ("hostdisk", "%s has no DM name\n", path);
1465 }
1466 if (tree)
1467 dm_tree_free (tree);
1468 free (path);
1469 if (mapper_name)
1470 return xasprintf ("/dev/mapper/%s", mapper_name);
1471 else
1472 return NULL;
1473 }
1474 #endif /* HAVE_DEVICE_MAPPER */
1475 }
1476
1477 return path;
1478
1479 #elif defined(__GNU__)
1480 char *path = xstrdup (os_dev);
1481 if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
1482 {
1483 char *p = strchr (path + 7, 's');
1484 if (p)
1485 *p = '\0';
1486 }
1487 return path;
1488
1489 #elif defined(__CYGWIN__)
1490 char *path = xstrdup (os_dev);
1491 if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z')
1492 path[8] = 0;
1493 return path;
1494
1495 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1496 char *out, *out2;
1497 if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
1498 return xstrdup (os_dev);
1499 follow_geom_up (os_dev + sizeof ("/dev/") - 1, NULL, &out);
1500
1501 out2 = xasprintf ("/dev/%s", out);
1502 free (out);
1503
1504 return out2;
1505 #elif defined(__APPLE__)
1506 char *path = xstrdup (os_dev);
1507 if (strncmp ("/dev/", path, 5) == 0)
1508 {
1509 char *p;
1510 for (p = path + 5; *p; ++p)
1511 if (grub_isdigit(*p))
1512 {
1513 p = strpbrk (p, "sp");
1514 if (p)
1515 *p = '\0';
1516 break;
1517 }
1518 }
1519 return path;
1520
1521 #elif defined(__NetBSD__)
1522 /* NetBSD uses "/dev/r[a-z]+[0-9][a-z]". */
1523 char *path = xstrdup (os_dev);
1524 if (strncmp ("/dev/r", path, sizeof("/dev/r") - 1) == 0 &&
1525 (path[sizeof("/dev/r") - 1] >= 'a' && path[sizeof("/dev/r") - 1] <= 'z') &&
1526 strncmp ("fd", path + sizeof("/dev/r") - 1, sizeof("fd") - 1) != 0) /* not a floppy device name */
1527 {
1528 char *p;
1529 for (p = path + sizeof("/dev/r"); *p >= 'a' && *p <= 'z'; p++);
1530 if (grub_isdigit(*p))
1531 {
1532 p++;
1533 if ((*p >= 'a' && *p <= 'z') && (*(p+1) == '\0'))
1534 {
1535 /* path matches the required regular expression and
1536 p points to its last character. */
1537 int rawpart = -1;
1538 # ifdef HAVE_GETRAWPARTITION
1539 rawpart = getrawpartition();
1540 # endif /* HAVE_GETRAWPARTITION */
1541 if (rawpart >= 0)
1542 *p = 'a' + rawpart;
1543 }
1544 }
1545 }
1546 return path;
1547
1548 #else
1549 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
1550 return xstrdup (os_dev);
1551 #endif
1552 }
1553
1554 #if defined(__linux__) || defined(__CYGWIN__)
1555 static int
1556 device_is_wholedisk (const char *os_dev)
1557 {
1558 int len = strlen (os_dev);
1559
1560 if (os_dev[len - 1] < '0' || os_dev[len - 1] > '9')
1561 return 1;
1562 return 0;
1563 }
1564 #endif
1565
1566 #if defined(__NetBSD__)
1567 /* Try to determine whether a given device name corresponds to a whole disk.
1568 This function should give in most cases a definite answer, but it may
1569 actually give an approximate one in the following sense: if the return
1570 value is 0 then the device name does not correspond to a whole disk. */
1571 static int
1572 device_is_wholedisk (const char *os_dev)
1573 {
1574 int len = strlen (os_dev);
1575 int rawpart = -1;
1576
1577 # ifdef HAVE_GETRAWPARTITION
1578 rawpart = getrawpartition();
1579 # endif /* HAVE_GETRAWPARTITION */
1580 if (rawpart < 0)
1581 return 1;
1582 return (os_dev[len - 1] == ('a' + rawpart));
1583 }
1584 #endif /* defined(__NetBSD__) */
1585
1586 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1587 static int
1588 device_is_wholedisk (const char *os_dev)
1589 {
1590 const char *p;
1591
1592 if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
1593 return 0;
1594
1595 for (p = os_dev + sizeof ("/dev/") - 1; *p; ++p)
1596 if (grub_isdigit (*p))
1597 {
1598 if (strchr (p, 's'))
1599 return 0;
1600 break;
1601 }
1602
1603 return 1;
1604 }
1605 #endif /* defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
1606
1607 static int
1608 find_system_device (const char *os_dev, struct stat *st, int convert, int add)
1609 {
1610 unsigned int i;
1611 char *os_disk;
1612
1613 if (convert)
1614 os_disk = convert_system_partition_to_system_disk (os_dev, st);
1615 else
1616 os_disk = xstrdup (os_dev);
1617 if (! os_disk)
1618 return -1;
1619
1620 for (i = 0; i < ARRAY_SIZE (map); i++)
1621 if (! map[i].device)
1622 break;
1623 else if (strcmp (map[i].device, os_disk) == 0)
1624 {
1625 free (os_disk);
1626 return i;
1627 }
1628
1629 if (!add)
1630 return -1;
1631
1632 if (i == ARRAY_SIZE (map))
1633 grub_util_error (_("device count exceeds limit"));
1634
1635 map[i].device = os_disk;
1636 map[i].drive = xmalloc (sizeof ("hostdisk/") + strlen (os_disk));
1637 strcpy (map[i].drive, "hostdisk/");
1638 strcpy (map[i].drive + sizeof ("hostdisk/") - 1, os_disk);
1639 map[i].device_map = 0;
1640
1641 return i;
1642 }
1643
1644 int
1645 grub_util_biosdisk_is_present (const char *os_dev)
1646 {
1647 struct stat st;
1648
1649 if (stat (os_dev, &st) < 0)
1650 return 0;
1651
1652 return find_system_device (os_dev, &st, 1, 0) != -1;
1653 }
1654
1655 char *
1656 grub_util_biosdisk_get_grub_dev (const char *os_dev)
1657 {
1658 struct stat st;
1659 int drive;
1660
1661 if (stat (os_dev, &st) < 0)
1662 {
1663 grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
1664 grub_util_info ("cannot stat `%s'", os_dev);
1665 return 0;
1666 }
1667
1668 drive = find_system_device (os_dev, &st, 1, 1);
1669 if (drive < 0)
1670 {
1671 grub_error (GRUB_ERR_UNKNOWN_DEVICE,
1672 "no mapping exists for `%s'", os_dev);
1673 grub_util_info ("no mapping exists for `%s'", os_dev);
1674 return 0;
1675 }
1676
1677 if (grub_strcmp (os_dev,
1678 convert_system_partition_to_system_disk (os_dev, &st)) == 0)
1679 return make_device_name (drive, -1, -1);
1680
1681 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
1682 if (! S_ISCHR (st.st_mode))
1683 #else
1684 if (! S_ISBLK (st.st_mode))
1685 #endif
1686 return make_device_name (drive, -1, -1);
1687
1688 #if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1689
1690 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
1691 partition, so mapping them to GRUB devices is not trivial.
1692 Here, get the start sector of a partition by HDIO_GETGEO, and
1693 compare it with each partition GRUB recognizes.
1694
1695 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
1696 does not count the extended partition and missing primary
1697 partitions. Use same method as on Linux here.
1698
1699 For NetBSD and FreeBSD, proceed as for Linux, except that the start
1700 sector is obtained from the disk label. */
1701 {
1702 char *name, *partname;
1703 grub_disk_t disk;
1704 grub_disk_addr_t start;
1705 auto int find_partition (grub_disk_t dsk,
1706 const grub_partition_t partition);
1707
1708 int find_partition (grub_disk_t dsk __attribute__ ((unused)),
1709 const grub_partition_t partition)
1710 {
1711 grub_disk_addr_t part_start = 0;
1712 grub_util_info ("Partition %d starts from %lu",
1713 partition->number, partition->start);
1714
1715 part_start = grub_partition_get_start (partition);
1716
1717 if (start == part_start)
1718 {
1719 partname = grub_partition_get_name (partition);
1720 return 1;
1721 }
1722
1723 return 0;
1724 }
1725
1726 name = make_device_name (drive, -1, -1);
1727
1728 # if !defined(HAVE_DIOCGDINFO)
1729 if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
1730 return name;
1731 # else /* defined(HAVE_DIOCGDINFO) */
1732 /* Since os_dev and convert_system_partition_to_system_disk (os_dev) are
1733 * different, we know that os_dev cannot be a floppy device. */
1734 # endif /* !defined(HAVE_DIOCGDINFO) */
1735
1736 start = find_partition_start (os_dev);
1737 if (grub_errno != GRUB_ERR_NONE)
1738 {
1739 free (name);
1740 return 0;
1741 }
1742
1743 grub_util_info ("%s starts from %lu", os_dev, start);
1744
1745 if (start == 0 && device_is_wholedisk (os_dev))
1746 return name;
1747
1748 grub_util_info ("opening the device %s", name);
1749 disk = grub_disk_open (name);
1750 free (name);
1751
1752 if (! disk)
1753 {
1754 /* We already know that the partition exists. Given that we already
1755 checked the device map above, we can only get
1756 GRUB_ERR_UNKNOWN_DEVICE at this point if the disk does not exist.
1757 This can happen on Xen, where disk images in the host can be
1758 assigned to devices that have partition-like names in the guest
1759 but are really more like disks. */
1760 if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
1761 {
1762 grub_util_warn
1763 ("disk does not exist, so falling back to partition device %s",
1764 os_dev);
1765
1766 drive = find_system_device (os_dev, &st, 0, 1);
1767 if (drive < 0)
1768 {
1769 grub_error (GRUB_ERR_UNKNOWN_DEVICE,
1770 "no mapping exists for `%s'", os_dev);
1771 return 0;
1772 }
1773
1774 return make_device_name (drive, -1, -1);
1775 }
1776 else
1777 return 0;
1778 }
1779
1780 partname = NULL;
1781 grub_partition_iterate (disk, find_partition);
1782 if (grub_errno != GRUB_ERR_NONE)
1783 {
1784 grub_disk_close (disk);
1785 return 0;
1786 }
1787
1788 if (partname == NULL)
1789 {
1790 grub_disk_close (disk);
1791 grub_error (GRUB_ERR_BAD_DEVICE,
1792 "cannot find the partition of `%s'", os_dev);
1793 return 0;
1794 }
1795
1796 name = grub_xasprintf ("%s,%s", disk->name, partname);
1797 free (partname);
1798 return name;
1799 }
1800
1801 #elif defined(__GNU__)
1802 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
1803 {
1804 char *p;
1805 int dos_part = -1;
1806 int bsd_part = -1;
1807
1808 p = strrchr (os_dev, 's');
1809 if (p)
1810 {
1811 long int n;
1812 char *q;
1813
1814 p++;
1815 n = strtol (p, &q, 10);
1816 if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
1817 {
1818 dos_part = (int) n - 1;
1819
1820 if (*q >= 'a' && *q <= 'g')
1821 bsd_part = *q - 'a';
1822 }
1823 }
1824
1825 return make_device_name (drive, dos_part, bsd_part);
1826 }
1827
1828 #else
1829 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
1830 return make_device_name (drive, -1, -1);
1831 #endif
1832 }
1833
1834 const char *
1835 grub_util_biosdisk_get_compatibility_hint (grub_disk_t disk)
1836 {
1837 if (disk->dev != &grub_util_biosdisk_dev || map[disk->id].device_map)
1838 return disk->name;
1839 return 0;
1840 }
1841
1842 const char *
1843 grub_util_biosdisk_get_osdev (grub_disk_t disk)
1844 {
1845 return map[disk->id].device;
1846 }
1847
1848 int
1849 grub_util_biosdisk_is_floppy (grub_disk_t disk)
1850 {
1851 struct stat st;
1852 int fd;
1853
1854 fd = open (map[disk->id].device, O_RDONLY);
1855 /* Shouldn't happen. */
1856 if (fd == -1)
1857 return 0;
1858
1859 /* Shouldn't happen either. */
1860 if (fstat (fd, &st) < 0)
1861 return 0;
1862
1863 #if defined(__NetBSD__)
1864 if (major(st.st_rdev) == RAW_FLOPPY_MAJOR)
1865 return 1;
1866 #endif
1867
1868 #if defined(FLOPPY_MAJOR)
1869 if (major(st.st_rdev) == FLOPPY_MAJOR)
1870 #else
1871 /* Some kernels (e.g. kFreeBSD) don't have a static major number
1872 for floppies, but they still use a "fd[0-9]" pathname. */
1873 if (map[disk->id].device[5] == 'f'
1874 && map[disk->id].device[6] == 'd'
1875 && map[disk->id].device[7] >= '0'
1876 && map[disk->id].device[7] <= '9')
1877 #endif
1878 return 1;
1879
1880 return 0;
1881 }