1 /* getroot.c - Get root device */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009 Free Software Foundation, Inc.
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.
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.
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/>.
27 # include <sys/fcntl.h>
28 # include <sys/cygwin.h>
30 # define DEV_CYGDRIVE_MAJOR 98
35 #include <hurd/lookup.h>
39 #include <grub/util/misc.h>
40 #include <grub/util/hostdisk.h>
41 #include <grub/util/getroot.h>
44 strip_extra_slashes (char *dir
)
48 while ((p
= strchr (p
, '/')) != 0)
52 memmove (p
, p
+ 1, strlen (p
));
55 else if (p
[1] == '\0')
72 path
= xmalloc (size
);
73 while (! getcwd (path
, size
))
76 path
= xrealloc (path
, size
);
83 /* Convert POSIX path to Win32 path,
84 remove drive letter, replace backslashes. */
86 get_win32_path (const char *path
)
88 char winpath
[PATH_MAX
];
89 cygwin_conv_to_full_win32_path (path
, winpath
);
91 int len
= strlen (winpath
);
92 if (len
> 2 && winpath
[1] == ':')
95 memmove (winpath
, winpath
+ 2, len
+ 1);
99 for (i
= 0; i
< len
; i
++)
100 if (winpath
[i
] == '\\')
102 return xstrdup (winpath
);
107 grub_get_prefix (const char *dir
)
110 char *abs_dir
, *prev_dir
;
112 struct stat st
, prev_st
;
114 /* Save the current directory. */
115 saved_cwd
= xgetcwd ();
118 grub_util_error ("cannot change directory to `%s'", dir
);
120 abs_dir
= xgetcwd ();
121 strip_extra_slashes (abs_dir
);
122 prev_dir
= xstrdup (abs_dir
);
124 if (stat (".", &prev_st
) < 0)
125 grub_util_error ("cannot stat `%s'", dir
);
127 if (! S_ISDIR (prev_st
.st_mode
))
128 grub_util_error ("`%s' is not a directory", dir
);
132 if (chdir ("..") < 0)
133 grub_util_error ("cannot change directory to the parent");
135 if (stat (".", &st
) < 0)
136 grub_util_error ("cannot stat current directory");
138 if (! S_ISDIR (st
.st_mode
))
139 grub_util_error ("current directory is not a directory???");
141 if (prev_st
.st_dev
!= st
.st_dev
|| prev_st
.st_ino
== st
.st_ino
)
145 prev_dir
= xgetcwd ();
149 strip_extra_slashes (prev_dir
);
150 prefix
= xmalloc (strlen (abs_dir
) - strlen (prev_dir
) + 2);
152 strcpy (prefix
+ 1, abs_dir
+ strlen (prev_dir
));
153 strip_extra_slashes (prefix
);
155 if (chdir (saved_cwd
) < 0)
156 grub_util_error ("cannot change directory to `%s'", dir
);
159 if (st
.st_dev
!= (DEV_CYGDRIVE_MAJOR
<< 16))
161 /* Reached some mount point not below /cygdrive.
162 GRUB does not know Cygwin's emulated mounts,
163 convert to Win32 path. */
164 grub_util_info ("Cygwin prefix = %s", prefix
);
165 char * wprefix
= get_win32_path (prefix
);
175 grub_util_info ("prefix = %s", prefix
);
182 find_root_device (const char *dir
__attribute__ ((unused
)),
183 dev_t dev
__attribute__ ((unused
)))
188 #elif ! defined(__CYGWIN__)
191 find_root_device (const char *dir
, dev_t dev
)
201 saved_cwd
= xgetcwd ();
203 grub_util_info ("changing current directory to %s", dir
);
211 while ((ent
= readdir (dp
)) != 0)
216 - dotfiles (like "/dev/.tmp.md0") since they could be duplicates.
217 - dotdirs (like "/dev/.static") since they could contain duplicates. */
218 if (ent
->d_name
[0] == '.')
221 if (lstat (ent
->d_name
, &st
) < 0)
222 /* Ignore any error. */
225 if (S_ISLNK (st
.st_mode
))
226 /* Don't follow symbolic links. */
229 if (S_ISDIR (st
.st_mode
))
231 /* Find it recursively. */
234 res
= find_root_device (ent
->d_name
, dev
);
238 if (chdir (saved_cwd
) < 0)
239 grub_util_error ("cannot restore the original directory");
247 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
248 if (S_ISCHR (st
.st_mode
) && st
.st_rdev
== dev
)
250 if (S_ISBLK (st
.st_mode
) && st
.st_rdev
== dev
)
254 /* Skip device names like /dev/dm-0, which are short-hand aliases
255 to more descriptive device names, e.g. those under /dev/mapper */
256 if (ent
->d_name
[0] == 'd' &&
257 ent
->d_name
[1] == 'm' &&
258 ent
->d_name
[2] == '-' &&
259 ent
->d_name
[3] >= '0' &&
260 ent
->d_name
[3] <= '9')
269 res
= xmalloc (strlen (cwd
) + strlen (ent
->d_name
) + 2);
270 sprintf (res
, "%s/%s", cwd
, ent
->d_name
);
271 strip_extra_slashes (res
);
274 /* /dev/root is not a real block device keep looking, takes care
275 of situation where root filesystem is on the same partition as
278 if (strcmp(res
, "/dev/root") == 0)
281 if (chdir (saved_cwd
) < 0)
282 grub_util_error ("cannot restore the original directory");
290 if (chdir (saved_cwd
) < 0)
291 grub_util_error ("cannot restore the original directory");
298 #else /* __CYGWIN__ */
300 /* Read drive/partition serial number from mbr/boot sector,
301 return 0 on read error, ~0 on unknown serial. */
303 get_bootsec_serial (const char *os_dev
, int mbr
)
305 /* Read boot sector. */
306 int fd
= open (os_dev
, O_RDONLY
);
309 unsigned char buf
[0x200];
310 int n
= read (fd
, buf
, sizeof (buf
));
312 if (n
!= sizeof(buf
))
315 /* Check signature. */
316 if (!(buf
[0x1fe] == 0x55 && buf
[0x1ff] == 0xaa))
319 /* Serial number offset depends on boot sector type. */
322 else if (memcmp (buf
+ 0x03, "NTFS", 4) == 0)
324 else if (memcmp (buf
+ 0x52, "FAT32", 5) == 0)
326 else if (memcmp (buf
+ 0x36, "FAT", 3) == 0)
331 unsigned serial
= *(unsigned *)(buf
+ n
);
338 find_cygwin_root_device (const char *path
, dev_t dev
)
340 /* No root device for /cygdrive. */
341 if (dev
== (DEV_CYGDRIVE_MAJOR
<< 16))
344 /* Convert to full POSIX and Win32 path. */
345 char fullpath
[PATH_MAX
], winpath
[PATH_MAX
];
346 cygwin_conv_to_full_posix_path (path
, fullpath
);
347 cygwin_conv_to_full_win32_path (fullpath
, winpath
);
349 /* If identical, this is no real filesystem path. */
350 if (strcmp (fullpath
, winpath
) == 0)
353 /* Check for floppy drive letter. */
354 if (winpath
[0] && winpath
[1] == ':' && strchr ("AaBb", winpath
[0]))
355 return xstrdup (strchr ("Aa", winpath
[0]) ? "/dev/fd0" : "/dev/fd1");
357 /* Cygwin returns the partition serial number in stat.st_dev.
358 This is never identical to the device number of the emulated
359 /dev/sdXN device, so above find_root_device () does not work.
360 Search the partition with the same serial in boot sector instead. */
361 char devpath
[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia. */
363 for (d
= 'a'; d
<= 'z'; d
++)
365 sprintf (devpath
, "/dev/sd%c", d
);
366 if (get_bootsec_serial (devpath
, 1) == 0)
369 for (p
= 1; p
<= 15; p
++)
371 sprintf (devpath
, "/dev/sd%c%d", d
, p
);
372 unsigned ser
= get_bootsec_serial (devpath
, 0);
375 if (ser
!= (unsigned)~0 && dev
== (dev_t
)ser
)
376 return xstrdup (devpath
);
382 #endif /* __CYGWIN__ */
385 grub_guess_root_device (const char *dir
)
395 mach_msg_type_number_t num_ports
= 0, num_ints
= 0, num_offsets
= 0, data_len
= 0;
398 file
= file_name_lookup (dir
, 0, 0);
399 if (file
== MACH_PORT_NULL
)
402 err
= file_get_storage_info (file
,
405 &offsets
, &num_offsets
,
409 grub_util_error ("Storage info for `%s' does not include type", dir
);
410 if (ints
[0] != STORAGE_DEVICE
)
411 grub_util_error ("Filesystem of `%s' is not stored on local disk", dir
);
414 grub_util_error ("Storage info for `%s' does not include name", dir
);
416 if (name_len
< data_len
)
417 grub_util_error ("Bogus name length for storage info for `%s'", dir
);
418 if (data
[name_len
- 1] != '\0')
419 grub_util_error ("Storage name for `%s' not NUL-terminated", dir
);
421 os_dev
= xmalloc (strlen ("/dev/") + data_len
);
422 memcpy (os_dev
, "/dev/", strlen ("/dev/"));
423 memcpy (os_dev
+ strlen ("/dev/"), data
, data_len
);
425 if (ports
&& num_ports
> 0)
427 mach_msg_type_number_t i
;
428 for (i
= 0; i
< num_ports
; i
++)
430 mach_port_t port
= ports
[i
];
431 if (port
!= MACH_PORT_NULL
)
432 mach_port_deallocate (mach_task_self(), port
);
434 munmap ((caddr_t
) ports
, num_ports
* sizeof (*ports
));
437 if (ints
&& num_ints
> 0)
438 munmap ((caddr_t
) ints
, num_ints
* sizeof (*ints
));
439 if (offsets
&& num_offsets
> 0)
440 munmap ((caddr_t
) offsets
, num_offsets
* sizeof (*offsets
));
441 if (data
&& data_len
> 0)
442 munmap (data
, data_len
);
443 mach_port_deallocate (mach_task_self (), file
);
447 if (stat (dir
, &st
) < 0)
448 grub_util_error ("cannot stat `%s'", dir
);
451 /* Cygwin specific function. */
452 os_dev
= find_cygwin_root_device (dir
, st
.st_dev
);
456 /* This might be truly slow, but is there any better way? */
457 os_dev
= find_root_device ("/dev", st
.st_dev
);
459 #endif /* !__GNU__ */
465 grub_util_is_dmraid (const char *os_dev
)
467 if (! strncmp (os_dev
, "/dev/mapper/nvidia_", 19))
469 else if (! strncmp (os_dev
, "/dev/mapper/isw_", 16))
471 else if (! strncmp (os_dev
, "/dev/mapper/hpt37x_", 19))
473 else if (! strncmp (os_dev
, "/dev/mapper/hpt45x_", 19))
475 else if (! strncmp (os_dev
, "/dev/mapper/via_", 16))
477 else if (! strncmp (os_dev
, "/dev/mapper/lsi_", 16))
479 else if (! strncmp (os_dev
, "/dev/mapper/pdc_", 16))
481 else if (! strncmp (os_dev
, "/dev/mapper/jmicron_", 20))
483 else if (! strncmp (os_dev
, "/dev/mapper/asr_", 16))
485 else if (! strncmp (os_dev
, "/dev/mapper/sil_", 16))
492 grub_util_get_dev_abstraction (const char *os_dev
__attribute__((unused
)))
496 if (!strncmp (os_dev
, "/dev/mapper/", 12)
497 && ! grub_util_is_dmraid (os_dev
)
498 && strncmp (os_dev
, "/dev/mapper/mpath", 17) != 0)
499 return GRUB_DEV_ABSTRACTION_LVM
;
501 /* Check for RAID. */
502 if (!strncmp (os_dev
, "/dev/md", 7))
503 return GRUB_DEV_ABSTRACTION_RAID
;
506 /* No abstraction found. */
507 return GRUB_DEV_ABSTRACTION_NONE
;
511 grub_util_get_grub_dev (const char *os_dev
)
515 switch (grub_util_get_dev_abstraction (os_dev
))
517 case GRUB_DEV_ABSTRACTION_LVM
:
520 unsigned short i
, len
;
521 grub_size_t offset
= sizeof ("/dev/mapper/") - 1;
523 len
= strlen (os_dev
) - offset
+ 1;
524 grub_dev
= xmalloc (len
);
526 for (i
= 0; i
< len
; i
++, offset
++)
528 grub_dev
[i
] = os_dev
[offset
];
529 if (os_dev
[offset
] == '-' && os_dev
[offset
+ 1] == '-')
536 case GRUB_DEV_ABSTRACTION_RAID
:
538 if (os_dev
[7] == '_' && os_dev
[8] == 'd')
540 /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
544 p
= strdup (os_dev
+ sizeof ("/dev/md_d") - 1);
550 grub_dev
= xasprintf ("md%s", p
);
553 else if (os_dev
[7] == '/' && os_dev
[8] == 'd')
555 /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
559 p
= strdup (os_dev
+ sizeof ("/dev/md/d") - 1);
565 grub_dev
= xasprintf ("md%s", p
);
568 else if (os_dev
[7] >= '0' && os_dev
[7] <= '9')
572 p
= strdup (os_dev
+ sizeof ("/dev/md") - 1);
578 grub_dev
= xasprintf ("md%s", p
);
581 else if (os_dev
[7] == '/' && os_dev
[8] >= '0' && os_dev
[8] <= '9')
585 p
= strdup (os_dev
+ sizeof ("/dev/md/") - 1);
591 grub_dev
= xasprintf ("md%s", p
);
595 grub_util_error ("unknown kind of RAID device `%s'", os_dev
);
599 default: /* GRUB_DEV_ABSTRACTION_NONE */
600 grub_dev
= grub_util_biosdisk_get_grub_dev (os_dev
);
607 grub_util_check_block_device (const char *blk_dev
)
611 if (stat (blk_dev
, &st
) < 0)
612 grub_util_error ("cannot stat `%s'", blk_dev
);
614 if (S_ISBLK (st
.st_mode
))
621 grub_util_check_char_device (const char *blk_dev
)
625 if (stat (blk_dev
, &st
) < 0)
626 grub_util_error ("cannot stat `%s'", blk_dev
);
628 if (S_ISCHR (st
.st_mode
))