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
33 #include <grub/util/misc.h>
34 #include <grub/util/hostdisk.h>
35 #include <grub/util/getroot.h>
38 strip_extra_slashes (char *dir
)
42 while ((p
= strchr (p
, '/')) != 0)
46 memmove (p
, p
+ 1, strlen (p
));
49 else if (p
[1] == '\0')
66 path
= xmalloc (size
);
67 while (! getcwd (path
, size
))
70 path
= xrealloc (path
, size
);
77 /* Convert POSIX path to Win32 path,
78 remove drive letter, replace backslashes. */
80 get_win32_path (const char *path
)
82 char winpath
[PATH_MAX
];
83 cygwin_conv_to_full_win32_path (path
, winpath
);
85 int len
= strlen (winpath
);
86 if (len
> 2 && winpath
[1] == ':')
89 memmove (winpath
, winpath
+ 2, len
+ 1);
93 for (i
= 0; i
< len
; i
++)
94 if (winpath
[i
] == '\\')
96 return xstrdup (winpath
);
101 grub_get_prefix (const char *dir
)
104 char *abs_dir
, *prev_dir
;
106 struct stat st
, prev_st
;
108 /* Save the current directory. */
109 saved_cwd
= xgetcwd ();
112 grub_util_error ("Cannot change directory to `%s'", dir
);
114 abs_dir
= xgetcwd ();
115 strip_extra_slashes (abs_dir
);
116 prev_dir
= xstrdup (abs_dir
);
118 if (stat (".", &prev_st
) < 0)
119 grub_util_error ("Cannot stat `%s'", dir
);
121 if (! S_ISDIR (prev_st
.st_mode
))
122 grub_util_error ("`%s' is not a directory", dir
);
126 if (chdir ("..") < 0)
127 grub_util_error ("Cannot change directory to the parent");
129 if (stat (".", &st
) < 0)
130 grub_util_error ("Cannot stat current directory");
132 if (! S_ISDIR (st
.st_mode
))
133 grub_util_error ("Current directory is not a directory???");
135 if (prev_st
.st_dev
!= st
.st_dev
|| prev_st
.st_ino
== st
.st_ino
)
139 prev_dir
= xgetcwd ();
143 strip_extra_slashes (prev_dir
);
144 prefix
= xmalloc (strlen (abs_dir
) - strlen (prev_dir
) + 2);
146 strcpy (prefix
+ 1, abs_dir
+ strlen (prev_dir
));
147 strip_extra_slashes (prefix
);
149 if (chdir (saved_cwd
) < 0)
150 grub_util_error ("Cannot change directory to `%s'", dir
);
153 if (st
.st_dev
!= (DEV_CYGDRIVE_MAJOR
<< 16))
155 /* Reached some mount point not below /cygdrive.
156 GRUB does not know Cygwin's emulated mounts,
157 convert to Win32 path. */
158 grub_util_info ("Cygwin prefix = %s", prefix
);
159 char * wprefix
= get_win32_path (prefix
);
169 grub_util_info ("prefix = %s", prefix
);
176 find_root_device (const char *dir
__attribute__ ((unused
)),
177 dev_t dev
__attribute__ ((unused
)))
182 #elif ! defined(__CYGWIN__)
185 find_root_device (const char *dir
, dev_t dev
)
195 saved_cwd
= xgetcwd ();
197 grub_util_info ("changing current directory to %s", dir
);
205 while ((ent
= readdir (dp
)) != 0)
210 - dotfiles (like "/dev/.tmp.md0") since they could be duplicates.
211 - dotdirs (like "/dev/.static") since they could contain duplicates. */
212 if (ent
->d_name
[0] == '.')
215 if (lstat (ent
->d_name
, &st
) < 0)
216 /* Ignore any error. */
219 if (S_ISLNK (st
.st_mode
))
220 /* Don't follow symbolic links. */
223 if (S_ISDIR (st
.st_mode
))
225 /* Find it recursively. */
228 res
= find_root_device (ent
->d_name
, dev
);
232 if (chdir (saved_cwd
) < 0)
233 grub_util_error ("Cannot restore the original directory");
241 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
242 if (S_ISCHR (st
.st_mode
) && st
.st_rdev
== dev
)
244 if (S_ISBLK (st
.st_mode
) && st
.st_rdev
== dev
)
248 /* Skip device names like /dev/dm-0, which are short-hand aliases
249 to more descriptive device names, e.g. those under /dev/mapper */
250 if (ent
->d_name
[0] == 'd' &&
251 ent
->d_name
[1] == 'm' &&
252 ent
->d_name
[2] == '-' &&
253 ent
->d_name
[3] >= '0' &&
254 ent
->d_name
[3] <= '9')
263 res
= xmalloc (strlen (cwd
) + strlen (ent
->d_name
) + 2);
264 sprintf (res
, "%s/%s", cwd
, ent
->d_name
);
265 strip_extra_slashes (res
);
268 /* /dev/root is not a real block device keep looking, takes care
269 of situation where root filesystem is on the same partition as
272 if (strcmp(res
, "/dev/root") == 0)
275 if (chdir (saved_cwd
) < 0)
276 grub_util_error ("Cannot restore the original directory");
284 if (chdir (saved_cwd
) < 0)
285 grub_util_error ("Cannot restore the original directory");
292 #else /* __CYGWIN__ */
294 /* Read drive/partition serial number from mbr/boot sector,
295 return 0 on read error, ~0 on unknown serial. */
297 get_bootsec_serial (const char *os_dev
, int mbr
)
299 /* Read boot sector. */
300 int fd
= open (os_dev
, O_RDONLY
);
303 unsigned char buf
[0x200];
304 int n
= read (fd
, buf
, sizeof (buf
));
306 if (n
!= sizeof(buf
))
309 /* Check signature. */
310 if (!(buf
[0x1fe] == 0x55 && buf
[0x1ff] == 0xaa))
313 /* Serial number offset depends on boot sector type. */
316 else if (memcmp (buf
+ 0x03, "NTFS", 4) == 0)
318 else if (memcmp (buf
+ 0x52, "FAT32", 5) == 0)
320 else if (memcmp (buf
+ 0x36, "FAT", 3) == 0)
325 unsigned serial
= *(unsigned *)(buf
+ n
);
332 find_cygwin_root_device (const char *path
, dev_t dev
)
334 /* No root device for /cygdrive. */
335 if (dev
== (DEV_CYGDRIVE_MAJOR
<< 16))
338 /* Convert to full POSIX and Win32 path. */
339 char fullpath
[PATH_MAX
], winpath
[PATH_MAX
];
340 cygwin_conv_to_full_posix_path (path
, fullpath
);
341 cygwin_conv_to_full_win32_path (fullpath
, winpath
);
343 /* If identical, this is no real filesystem path. */
344 if (strcmp (fullpath
, winpath
) == 0)
347 /* Check for floppy drive letter. */
348 if (winpath
[0] && winpath
[1] == ':' && strchr ("AaBb", winpath
[0]))
349 return xstrdup (strchr ("Aa", winpath
[0]) ? "/dev/fd0" : "/dev/fd1");
351 /* Cygwin returns the partition serial number in stat.st_dev.
352 This is never identical to the device number of the emulated
353 /dev/sdXN device, so above find_root_device () does not work.
354 Search the partition with the same serial in boot sector instead. */
355 char devpath
[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia. */
357 for (d
= 'a'; d
<= 'z'; d
++)
359 sprintf (devpath
, "/dev/sd%c", d
);
360 if (get_bootsec_serial (devpath
, 1) == 0)
363 for (p
= 1; p
<= 15; p
++)
365 sprintf (devpath
, "/dev/sd%c%d", d
, p
);
366 unsigned ser
= get_bootsec_serial (devpath
, 0);
369 if (ser
!= (unsigned)~0 && dev
== (dev_t
)ser
)
370 return xstrdup (devpath
);
376 #endif /* __CYGWIN__ */
379 grub_guess_root_device (const char *dir
)
384 if (stat (dir
, &st
) < 0)
385 grub_util_error ("Cannot stat `%s'", dir
);
388 /* Cygwin specific function. */
389 os_dev
= find_cygwin_root_device (dir
, st
.st_dev
);
393 /* This might be truly slow, but is there any better way? */
394 os_dev
= find_root_device ("/dev", st
.st_dev
);
401 grub_util_get_dev_abstraction (const char *os_dev UNUSED
)
405 if (!strncmp (os_dev
, "/dev/mapper/", 12))
406 return GRUB_DEV_ABSTRACTION_LVM
;
408 /* Check for RAID. */
409 if (!strncmp (os_dev
, "/dev/md", 7))
410 return GRUB_DEV_ABSTRACTION_RAID
;
413 /* No abstraction found. */
414 return GRUB_DEV_ABSTRACTION_NONE
;
418 grub_util_get_grub_dev (const char *os_dev
)
422 switch (grub_util_get_dev_abstraction (os_dev
))
424 case GRUB_DEV_ABSTRACTION_LVM
:
427 unsigned short i
, len
;
428 grub_size_t offset
= sizeof ("/dev/mapper/") - 1;
430 len
= strlen (os_dev
) - offset
+ 1;
431 grub_dev
= xmalloc (len
);
433 for (i
= 0; i
< len
; i
++, offset
++)
435 grub_dev
[i
] = os_dev
[offset
];
436 if (os_dev
[offset
] == '-' && os_dev
[offset
+ 1] == '-')
443 case GRUB_DEV_ABSTRACTION_RAID
:
445 if (os_dev
[7] == '_' && os_dev
[8] == 'd')
447 /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
451 p
= strdup (os_dev
+ sizeof ("/dev/md_d") - 1);
457 asprintf (&grub_dev
, "md%s", p
);
460 else if (os_dev
[7] == '/' && os_dev
[8] == 'd')
462 /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
466 p
= strdup (os_dev
+ sizeof ("/dev/md/d") - 1);
472 asprintf (&grub_dev
, "md%s", p
);
475 else if (os_dev
[7] >= '0' && os_dev
[7] <= '9')
479 p
= strdup (os_dev
+ sizeof ("/dev/md") - 1);
485 asprintf (&grub_dev
, "md%s", p
);
488 else if (os_dev
[7] == '/' && os_dev
[8] >= '0' && os_dev
[8] <= '9')
492 p
= strdup (os_dev
+ sizeof ("/dev/md/") - 1);
498 asprintf (&grub_dev
, "md%s", p
);
502 grub_util_error ("Unknown kind of RAID device `%s'", os_dev
);
506 default: /* GRUB_DEV_ABSTRACTION_NONE */
507 grub_dev
= grub_util_biosdisk_get_grub_dev (os_dev
);
514 grub_util_check_block_device (const char *blk_dev
)
518 if (stat (blk_dev
, &st
) < 0)
519 grub_util_error ("Cannot stat `%s'", blk_dev
);
521 if (S_ISBLK (st
.st_mode
))
528 grub_util_check_char_device (const char *blk_dev
)
532 if (stat (blk_dev
, &st
) < 0)
533 grub_util_error ("Cannot stat `%s'", blk_dev
);
535 if (S_ISCHR (st
.st_mode
))