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)
212 - dotfiles (like "/dev/.tmp.md0") since they could be duplicates.
213 - dotdirs (like "/dev/.static") since they could contain duplicates. */
214 if (ent
->d_name
[0] == '.')
217 if (lstat (ent
->d_name
, &st
) < 0)
218 /* Ignore any error. */
221 if (S_ISLNK (st
.st_mode
))
222 /* Don't follow symbolic links. */
225 if (S_ISDIR (st
.st_mode
))
227 /* Find it recursively. */
230 res
= find_root_device (ent
->d_name
, dev
);
234 if (chdir (saved_cwd
) < 0)
235 grub_util_error ("Cannot restore the original directory");
243 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
244 if (S_ISCHR (st
.st_mode
) && st
.st_rdev
== dev
)
246 if (S_ISBLK (st
.st_mode
) && st
.st_rdev
== dev
)
250 /* Skip device names like /dev/dm-0, which are short-hand aliases
251 to more descriptive device names, e.g. those under /dev/mapper */
252 if (ent
->d_name
[0] == 'd' &&
253 ent
->d_name
[1] == 'm' &&
254 ent
->d_name
[2] == '-' &&
255 ent
->d_name
[3] >= '0' &&
256 ent
->d_name
[3] <= '9')
262 res
= xmalloc (strlen (cwd
) + strlen (ent
->d_name
) + 2);
263 sprintf (res
, "%s/%s", cwd
, ent
->d_name
);
264 strip_extra_slashes (res
);
267 /* /dev/root is not a real block device keep looking, takes care
268 of situation where root filesystem is on the same partition as
271 if (strcmp(res
, "/dev/root") == 0)
274 if (chdir (saved_cwd
) < 0)
275 grub_util_error ("Cannot restore the original directory");
283 if (chdir (saved_cwd
) < 0)
284 grub_util_error ("Cannot restore the original directory");
291 #else /* __CYGWIN__ */
293 /* Read drive/partition serial number from mbr/boot sector,
294 return 0 on read error, ~0 on unknown serial. */
296 get_bootsec_serial (const char *os_dev
, int mbr
)
298 /* Read boot sector. */
299 int fd
= open (os_dev
, O_RDONLY
);
302 unsigned char buf
[0x200];
303 int n
= read (fd
, buf
, sizeof (buf
));
305 if (n
!= sizeof(buf
))
308 /* Check signature. */
309 if (!(buf
[0x1fe] == 0x55 && buf
[0x1ff] == 0xaa))
312 /* Serial number offset depends on boot sector type. */
315 else if (memcmp (buf
+ 0x03, "NTFS", 4) == 0)
317 else if (memcmp (buf
+ 0x52, "FAT32", 5) == 0)
319 else if (memcmp (buf
+ 0x36, "FAT", 3) == 0)
324 unsigned serial
= *(unsigned *)(buf
+ n
);
331 find_cygwin_root_device (const char *path
, dev_t dev
)
333 /* No root device for /cygdrive. */
334 if (dev
== (DEV_CYGDRIVE_MAJOR
<< 16))
337 /* Convert to full POSIX and Win32 path. */
338 char fullpath
[PATH_MAX
], winpath
[PATH_MAX
];
339 cygwin_conv_to_full_posix_path (path
, fullpath
);
340 cygwin_conv_to_full_win32_path (fullpath
, winpath
);
342 /* If identical, this is no real filesystem path. */
343 if (strcmp (fullpath
, winpath
) == 0)
346 /* Check for floppy drive letter. */
347 if (winpath
[0] && winpath
[1] == ':' && strchr ("AaBb", winpath
[0]))
348 return xstrdup (strchr ("Aa", winpath
[0]) ? "/dev/fd0" : "/dev/fd1");
350 /* Cygwin returns the partition serial number in stat.st_dev.
351 This is never identical to the device number of the emulated
352 /dev/sdXN device, so above find_root_device () does not work.
353 Search the partition with the same serial in boot sector instead. */
354 char devpath
[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia. */
356 for (d
= 'a'; d
<= 'z'; d
++)
358 sprintf (devpath
, "/dev/sd%c", d
);
359 if (get_bootsec_serial (devpath
, 1) == 0)
362 for (p
= 1; p
<= 15; p
++)
364 sprintf (devpath
, "/dev/sd%c%d", d
, p
);
365 unsigned ser
= get_bootsec_serial (devpath
, 0);
368 if (ser
!= (unsigned)~0 && dev
== (dev_t
)ser
)
369 return xstrdup (devpath
);
375 #endif /* __CYGWIN__ */
378 grub_guess_root_device (const char *dir
)
383 if (stat (dir
, &st
) < 0)
384 grub_util_error ("Cannot stat `%s'", dir
);
387 /* Cygwin specific function. */
388 os_dev
= find_cygwin_root_device (dir
, st
.st_dev
);
392 /* This might be truly slow, but is there any better way? */
393 os_dev
= find_root_device ("/dev", st
.st_dev
);
400 grub_util_get_dev_abstraction (const char *os_dev UNUSED
)
404 if (!strncmp (os_dev
, "/dev/mapper/", 12))
405 return GRUB_DEV_ABSTRACTION_LVM
;
407 /* Check for RAID. */
408 if (!strncmp (os_dev
, "/dev/md", 7))
409 return GRUB_DEV_ABSTRACTION_RAID
;
412 /* No abstraction found. */
413 return GRUB_DEV_ABSTRACTION_NONE
;
417 grub_util_get_grub_dev (const char *os_dev
)
421 switch (grub_util_get_dev_abstraction (os_dev
))
423 case GRUB_DEV_ABSTRACTION_LVM
:
426 unsigned short i
, len
;
427 grub_size_t offset
= sizeof ("/dev/mapper/") - 1;
429 len
= strlen (os_dev
) - offset
+ 1;
430 grub_dev
= xmalloc (len
);
432 for (i
= 0; i
< len
; i
++, offset
++)
434 grub_dev
[i
] = os_dev
[offset
];
435 if (os_dev
[offset
] == '-' && os_dev
[offset
+ 1] == '-')
442 case GRUB_DEV_ABSTRACTION_RAID
:
444 if (os_dev
[7] == '_' && os_dev
[8] == 'd')
446 /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
450 p
= strdup (os_dev
+ sizeof ("/dev/md_d") - 1);
456 asprintf (&grub_dev
, "md%s", p
);
459 else if (os_dev
[7] == '/' && os_dev
[8] == 'd')
461 /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
465 p
= strdup (os_dev
+ sizeof ("/dev/md/d") - 1);
471 asprintf (&grub_dev
, "md%s", p
);
474 else if (os_dev
[7] >= '0' && os_dev
[7] <= '9')
478 p
= strdup (os_dev
+ sizeof ("/dev/md") - 1);
484 asprintf (&grub_dev
, "md%s", p
);
487 else if (os_dev
[7] == '/' && os_dev
[8] >= '0' && os_dev
[8] <= '9')
491 p
= strdup (os_dev
+ sizeof ("/dev/md/") - 1);
497 asprintf (&grub_dev
, "md%s", p
);
501 grub_util_error ("Unknown kind of RAID device `%s'", os_dev
);
505 default: /* GRUB_DEV_ABSTRACTION_NONE */
506 grub_dev
= grub_util_biosdisk_get_grub_dev (os_dev
);
513 grub_util_check_block_device (const char *blk_dev
)
517 if (stat (blk_dev
, &st
) < 0)
518 grub_util_error ("Cannot stat `%s'", blk_dev
);
520 if (S_ISBLK (st
.st_mode
))
527 grub_util_check_char_device (const char *blk_dev
)
531 if (stat (blk_dev
, &st
) < 0)
532 grub_util_error ("Cannot stat `%s'", blk_dev
);
534 if (S_ISCHR (st
.st_mode
))