1 /* getroot.c - Get root device */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008 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 (S_ISBLK (st
.st_mode
) && st
.st_rdev
== dev
)
244 /* Skip device names like /dev/dm-0, which are short-hand aliases
245 to more descriptive device names, e.g. those under /dev/mapper */
246 if (ent
->d_name
[0] == 'd' &&
247 ent
->d_name
[1] == 'm' &&
248 ent
->d_name
[2] == '-' &&
249 ent
->d_name
[3] >= '0' &&
250 ent
->d_name
[3] <= '9')
259 res
= xmalloc (strlen (cwd
) + strlen (ent
->d_name
) + 2);
260 sprintf (res
, "%s/%s", cwd
, ent
->d_name
);
261 strip_extra_slashes (res
);
264 /* /dev/root is not a real block device keep looking, takes care
265 of situation where root filesystem is on the same partition as
268 if (strcmp(res
, "/dev/root") == 0)
271 if (chdir (saved_cwd
) < 0)
272 grub_util_error ("Cannot restore the original directory");
280 if (chdir (saved_cwd
) < 0)
281 grub_util_error ("Cannot restore the original directory");
288 #else /* __CYGWIN__ */
290 /* Read drive/partition serial number from mbr/boot sector,
291 return 0 on read error, ~0 on unknown serial. */
293 get_bootsec_serial (const char *os_dev
, int mbr
)
295 /* Read boot sector. */
296 int fd
= open (os_dev
, O_RDONLY
);
299 unsigned char buf
[0x200];
300 int n
= read (fd
, buf
, sizeof (buf
));
302 if (n
!= sizeof(buf
))
305 /* Check signature. */
306 if (!(buf
[0x1fe] == 0x55 && buf
[0x1ff] == 0xaa))
309 /* Serial number offset depends on boot sector type. */
312 else if (memcmp (buf
+ 0x03, "NTFS", 4) == 0)
314 else if (memcmp (buf
+ 0x52, "FAT32", 5) == 0)
316 else if (memcmp (buf
+ 0x36, "FAT", 3) == 0)
321 unsigned serial
= *(unsigned *)(buf
+ n
);
328 find_cygwin_root_device (const char *path
, dev_t dev
)
330 /* No root device for /cygdrive. */
331 if (dev
== (DEV_CYGDRIVE_MAJOR
<< 16))
334 /* Convert to full POSIX and Win32 path. */
335 char fullpath
[PATH_MAX
], winpath
[PATH_MAX
];
336 cygwin_conv_to_full_posix_path (path
, fullpath
);
337 cygwin_conv_to_full_win32_path (fullpath
, winpath
);
339 /* If identical, this is no real filesystem path. */
340 if (strcmp (fullpath
, winpath
) == 0)
343 /* Check for floppy drive letter. */
344 if (winpath
[0] && winpath
[1] == ':' && strchr ("AaBb", winpath
[0]))
345 return xstrdup (strchr ("Aa", winpath
[0]) ? "/dev/fd0" : "/dev/fd1");
347 /* Cygwin returns the partition serial number in stat.st_dev.
348 This is never identical to the device number of the emulated
349 /dev/sdXN device, so above find_root_device () does not work.
350 Search the partion with the same serial in boot sector instead. */
351 char devpath
[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia. */
353 for (d
= 'a'; d
<= 'z'; d
++)
355 sprintf (devpath
, "/dev/sd%c", d
);
356 if (get_bootsec_serial (devpath
, 1) == 0)
359 for (p
= 1; p
<= 15; p
++)
361 sprintf (devpath
, "/dev/sd%c%d", d
, p
);
362 unsigned ser
= get_bootsec_serial (devpath
, 0);
365 if (ser
!= (unsigned)~0 && dev
== (dev_t
)ser
)
366 return xstrdup (devpath
);
372 #endif /* __CYGWIN__ */
375 grub_guess_root_device (const char *dir
)
380 if (stat (dir
, &st
) < 0)
381 grub_util_error ("Cannot stat `%s'", dir
);
384 /* Cygwin specific function. */
385 os_dev
= find_cygwin_root_device (dir
, st
.st_dev
);
389 /* This might be truly slow, but is there any better way? */
390 os_dev
= find_root_device ("/dev", st
.st_dev
);
397 grub_util_get_dev_abstraction (const char *os_dev
)
401 if (!strncmp (os_dev
, "/dev/mapper/", 12))
402 return GRUB_DEV_ABSTRACTION_LVM
;
404 /* Check for RAID. */
405 if (!strncmp (os_dev
, "/dev/md", 7))
406 return GRUB_DEV_ABSTRACTION_RAID
;
409 /* No abstraction found. */
410 return GRUB_DEV_ABSTRACTION_NONE
;
414 grub_util_get_grub_dev (const char *os_dev
)
418 switch (grub_util_get_dev_abstraction (os_dev
))
420 case GRUB_DEV_ABSTRACTION_LVM
:
423 unsigned short i
, len
;
424 grub_size_t offset
= sizeof ("/dev/mapper/") - 1;
426 len
= strlen (os_dev
) - offset
+ 1;
427 grub_dev
= xmalloc (len
);
429 for (i
= 0; i
< len
; i
++, offset
++)
431 grub_dev
[i
] = os_dev
[offset
];
432 if (os_dev
[offset
] == '-' && os_dev
[offset
+ 1] == '-')
439 case GRUB_DEV_ABSTRACTION_RAID
:
441 if (os_dev
[7] == '_' && os_dev
[8] == 'd')
443 /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
447 p
= strdup (os_dev
+ sizeof ("/dev/md_d") - 1);
453 asprintf (&grub_dev
, "md%s", p
);
456 else if (os_dev
[7] >= '0' && os_dev
[7] <= '9')
458 asprintf (&grub_dev
, "md%s", os_dev
+ sizeof ("/dev/md") - 1);
460 else if (os_dev
[7] == '/' && os_dev
[8] >= '0' && os_dev
[8] <= '9')
462 asprintf (&grub_dev
, "md%s", os_dev
+ sizeof ("/dev/md/") - 1);
465 grub_util_error ("Unknown kind of RAID device `%s'", os_dev
);
469 default: /* GRUB_DEV_ABSTRACTION_NONE */
470 grub_dev
= grub_util_biosdisk_get_grub_dev (os_dev
);
477 grub_util_check_block_device (const char *blk_dev
)
481 if (stat (blk_dev
, &st
) < 0)
482 grub_util_error ("Cannot stat `%s'", blk_dev
);
484 if (S_ISBLK (st
.st_mode
))