4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
30 * This file contains all the functions that manipulate the file
31 * system where the GRUB menu resides.
40 #include <sys/types.h>
42 #include <sys/mount.h>
43 #include <sys/mntent.h>
44 #include <sys/mnttab.h>
45 #include <sys/fs/ufs_mount.h>
46 #include <sys/dktp/fdisk.h>
48 #if defined(i386) || defined(__amd64)
52 #include "libgrub_impl.h"
55 slice_match(const char *physpath
, int slice
)
59 return ((pos
= strrchr(physpath
, slice
)) == NULL
||
60 pos
[1] != 0 || pos
[-1] != ':');
64 * Returns zero if path contains ufs
67 slice_ufs(const char *path
)
73 fd
= open(path
, O_RDONLY
);
74 if ((ret
= fstyp_init(fd
, 0, NULL
, &hdl
)) == 0) {
75 ret
= fstyp_ident(hdl
, "ufs", &id
);
84 get_sol_prtnum(const char *physpath
)
92 char rdev
[MAXNAMELEN
];
93 #if defined(i386) || defined(__amd64)
95 int ext_part_found
= 0;
98 (void) snprintf(rdev
, sizeof (rdev
), "/devices%s,raw", physpath
);
100 if ((pos
= strrchr(rdev
, ':')) == NULL
)
101 return (PRTNUM_INVALID
);
103 pos
[1] = SLCNUM_WHOLE_DISK
;
105 fd
= open(rdev
, O_RDONLY
);
106 sz
= read(fd
, boot_sect
, sizeof (boot_sect
));
109 if (sz
!= sizeof (boot_sect
))
110 return (PRTNUM_INVALID
);
112 /* parse fdisk table */
113 mb
= (struct mboot
*)(uintptr_t)boot_sect
;
114 ipart
= (struct ipart
*)(uintptr_t)mb
->parts
;
115 for (i
= 0; i
< FD_NUMPART
; ++i
) {
116 if (ipart
[i
].systid
== SUNIXOS
|| ipart
[i
].systid
== SUNIXOS2
)
119 #if defined(i386) || defined(__amd64)
120 if (!fdisk_is_dos_extended(ipart
[i
].systid
) ||
121 (ext_part_found
== 1))
126 if (libfdisk_init(&epp
, rdev
, NULL
, FDISK_READ_DISK
) ==
132 rval
= fdisk_get_solaris_part(epp
, &pno
, &begs
, &nums
);
136 if (rval
== FDISK_SUCCESS
)
141 return (PRTNUM_INVALID
);
145 * Get physpath, topfs and bootfs for ZFS root dataset.
146 * Return 0 on success, non-zero (not errno) on failure.
149 get_zfs_root(zfs_handle_t
*zfh
, grub_fs_t
*fs
, grub_root_t
*root
)
155 if (zfs_get_type(zfh
) != ZFS_TYPE_FILESYSTEM
||
156 (name
= zfs_get_name(zfh
)) == NULL
||
157 (zph
= zpool_open(fs
->gf_lzfh
, name
)) == NULL
)
160 if ((ret
= zpool_get_physpath(zph
, root
->gr_physpath
,
161 sizeof (root
->gr_physpath
))) == 0 &&
162 (ret
= zpool_get_prop(zph
, ZPOOL_PROP_BOOTFS
,
163 root
->gr_fs
[GRBM_ZFS_BOOTFS
].gfs_dev
,
164 sizeof (root
->gr_fs
[GRBM_ZFS_BOOTFS
].gfs_dev
), NULL
)) == 0) {
166 (void) strlcpy(root
->gr_fs
[GRBM_ZFS_TOPFS
].gfs_dev
, name
,
167 sizeof (root
->gr_fs
[GRBM_ZFS_TOPFS
].gfs_dev
));
168 (void) grub_fsd_get_mountp(root
->gr_fs
+ GRBM_ZFS_BOOTFS
,
170 (void) grub_fsd_get_mountp(root
->gr_fs
+ GRBM_ZFS_TOPFS
,
179 * On entry physpath parameter supposed to contain:
180 * <disk_physpath>[<space><disk_physpath>]*.
181 * Retrieves first <disk_physpath> that matches both partition and slice.
182 * If any partition and slice is acceptable, first <disk_physpath> is returned.
185 get_one_physpath(char *physpath
, uint_t prtnum
, uint_t slcnum
)
190 if (!IS_SLCNUM_VALID(slcnum
) && !IS_PRTNUM_VALID(prtnum
)) {
191 (void) strtok(physpath
, " ");
195 if ((tmp
= strdup(physpath
)) == NULL
)
199 for (tok
= strtok(tmp
, " "); tok
!= NULL
; tok
= strtok(NULL
, " ")) {
200 if ((ret
= (slice_match(tok
, slcnum
) != 0 ||
201 get_sol_prtnum(tok
) != prtnum
)) == 0) {
202 (void) strcpy(physpath
, tok
);
214 zfs_bootsign(zfs_handle_t
*zfh
, void *data
)
219 char path
[MAXPATHLEN
];
221 barg
= (grub_barg_t
*)data
;
222 menu
= barg
->gb_entry
->ge_menu
;
225 if (get_zfs_root(zfh
, &menu
->gm_fs
, &barg
->gb_root
) != 0 ||
226 get_one_physpath(barg
->gb_root
.gr_physpath
, barg
->gb_prtnum
,
227 barg
->gb_slcnum
) != 0)
231 * if top zfs dataset is not mounted, mount it now
233 if (barg
->gb_root
.gr_fs
[GRBM_ZFS_TOPFS
].gfs_mountp
[0] == 0) {
234 if (grub_fsd_mount_tmp(barg
->gb_root
.gr_fs
+
235 GRBM_ZFS_TOPFS
, MNTTYPE_ZFS
) != 0)
239 /* check that bootsign exists and it is a regular file */
240 (void) snprintf(path
, sizeof (path
), "%s%s",
241 barg
->gb_root
.gr_fs
[GRBM_ZFS_TOPFS
].gfs_mountp
,
244 if (lstat(path
, &st
) != 0 || S_ISREG(st
.st_mode
) == 0 ||
245 (st
.st_mode
& S_IRUSR
) == 0)
248 (void) strlcpy(barg
->gb_root
.gr_fstyp
, MNTTYPE_ZFS
,
249 sizeof (barg
->gb_root
.gr_fstyp
));
250 barg
->gb_walkret
= 0;
251 /* LINTED: E_CONSTANT_CONDITION */
254 grub_fsd_umount_tmp(barg
->gb_root
.gr_fs
+ GRBM_ZFS_TOPFS
);
257 /* return non-zero to terminate the walk */
258 return (barg
->gb_walkret
== 0);
262 get_devlink(di_devlink_t dl
, void *arg
)
267 barg
= (grub_barg_t
*)arg
;
268 if ((path
= di_devlink_path(dl
)) != NULL
)
269 (void) strlcpy(barg
->gb_root
.gr_fs
[GRBM_UFS
].gfs_dev
, path
,
270 sizeof (barg
->gb_root
.gr_fs
[GRBM_UFS
].gfs_dev
));
271 return (DI_WALK_TERMINATE
);
275 ufs_bootsign_check(grub_barg_t
*barg
)
280 char path
[MAXPATHLEN
];
282 mp
= barg
->gb_entry
->ge_menu
;
284 /* get /dev/dsk link */
285 if (di_devlink_walk(mp
->gm_fs
.gf_dvlh
, "^dsk/",
286 barg
->gb_root
.gr_physpath
, DI_PRIMARY_LINK
, barg
, get_devlink
) != 0)
289 * if disk is not mounted, mount it now
291 if (grub_fsd_get_mountp(barg
->gb_root
.gr_fs
+ GRBM_UFS
,
294 slice_ufs(barg
->gb_root
.gr_fs
[GRBM_UFS
].gfs_dev
)) != 0 ||
295 (ret
= grub_fsd_mount_tmp(barg
->gb_root
.gr_fs
+ GRBM_UFS
,
300 (void) snprintf(path
, sizeof (path
), "%s%s",
301 barg
->gb_root
.gr_fs
[GRBM_UFS
].gfs_mountp
, barg
->gb_bootsign
);
303 if (lstat(path
, &st
) == 0 && S_ISREG(st
.st_mode
) &&
304 (st
.st_mode
& S_IRUSR
) != 0) {
305 barg
->gb_walkret
= 0;
306 (void) strlcpy(barg
->gb_root
.gr_fstyp
, MNTTYPE_UFS
,
307 sizeof (barg
->gb_root
.gr_fstyp
));
310 grub_fsd_umount_tmp(barg
->gb_root
.gr_fs
+ GRBM_UFS
);
311 return (barg
->gb_walkret
);
315 ufs_bootsign(di_node_t node
, di_minor_t minor
, void *arg
)
321 barg
= (grub_barg_t
*)arg
;
323 if (di_minor_spectype(minor
) != S_IFBLK
)
324 return (DI_WALK_CONTINUE
);
326 name
= di_minor_name(minor
);
327 if (name
[0] != barg
->gb_slcnum
|| name
[1] != 0)
328 return (DI_WALK_CONTINUE
);
330 path
= di_devfs_path(node
);
331 (void) snprintf(barg
->gb_root
.gr_physpath
,
332 sizeof (barg
->gb_root
.gr_physpath
), "%s:%c", path
, barg
->gb_slcnum
);
333 di_devfs_path_free(path
);
335 prtnum
= get_sol_prtnum(barg
->gb_root
.gr_physpath
);
336 if (!IS_PRTNUM_VALID(prtnum
))
337 return (DI_WALK_CONTINUE
);
340 * check only specified partition, slice
343 if (IS_PRTNUM_VALID(barg
->gb_prtnum
)) {
344 if (prtnum
!= barg
->gb_prtnum
|| ufs_bootsign_check(barg
) != 0)
345 return (DI_WALK_CONTINUE
);
346 return (DI_WALK_TERMINATE
);
350 * Walk through all slices in found solaris partition
353 barg
->gb_prtnum
= prtnum
;
354 minor
= DI_MINOR_NIL
;
356 while ((minor
= di_minor_next(node
, minor
)) != DI_MINOR_NIL
) {
358 if (di_minor_spectype(minor
) != S_IFBLK
)
361 name
= di_minor_name(minor
);
362 if (!IS_SLCNUM_VALID(name
[0]) || name
[1] != 0)
365 barg
->gb_slcnum
= name
[0];
366 path
= strrchr(barg
->gb_root
.gr_physpath
, ':');
367 path
[1] = barg
->gb_slcnum
;
369 if (ufs_bootsign_check(barg
) == 0)
370 return (DI_WALK_TERMINATE
);
373 barg
->gb_prtnum
= (uint_t
)PRTNUM_INVALID
;
374 barg
->gb_slcnum
= (uint_t
)SLCNUM_WHOLE_DISK
;
375 return (DI_WALK_CONTINUE
);
379 * Differs from what GRUB is doing: GRUB searchs through all disks seen by bios
380 * for bootsign, if bootsign is found on ufs slice GRUB sets it as a root,
381 * if on zfs, then GRUB uses zfs slice as root only if bootsign wasn't found
383 * That function first searches through all top datasets of active zpools,
384 * then if bootsign still not found walks through all disks and tries to
385 * find ufs slice with the bootsign.
388 grub_find_bootsign(grub_barg_t
*barg
)
391 mp
= barg
->gb_entry
->ge_menu
;
393 /* try to find bootsign over zfs pools */
394 barg
->gb_walkret
= EG_BOOTSIGN
;
395 (void) zfs_iter_root(mp
->gm_fs
.gf_lzfh
, zfs_bootsign
, barg
);
398 if (barg
->gb_walkret
!= 0 && di_walk_minor(mp
->gm_fs
.gf_diroot
,
399 DDI_NT_BLOCK
, 0, barg
, ufs_bootsign
) != 0)
402 return (barg
->gb_walkret
);
406 * Get current root file system.
407 * Return 0 on success, errno code on failure.
410 grub_current_root(grub_fs_t
*fs
, grub_root_t
*root
)
415 zfs_handle_t
*zfh
= NULL
;
416 struct mnttab mp
= {0};
417 struct mnttab mpref
= {0};
418 char buf
[MAXNAMELEN
] = {0};
420 mpref
.mnt_mountp
= "/";
422 if ((fp
= fopen(MNTTAB
, "r")) == NULL
)
426 * getmntany returns non-zero for failure, and sets errno
428 rc
= getmntany(fp
, &mp
, &mpref
);
437 (void) strlcpy(root
->gr_fstyp
, mp
.mnt_fstype
, sizeof (root
->gr_fstyp
));
439 if (strcmp(root
->gr_fstyp
, MNTTYPE_ZFS
) == 0) {
441 (void) strlcpy(buf
, mp
.mnt_special
, sizeof (buf
));
442 if ((name
= strtok(buf
, "/")) == NULL
)
445 if ((zfh
= zfs_open(fs
->gf_lzfh
, name
, ZFS_TYPE_FILESYSTEM
)) ==
450 * get_zfs_root returns non-zero on failure, not errno.
452 if (get_zfs_root(zfh
, fs
, root
))
456 * For mirrored root physpath would contain the list of
457 * all bootable devices, pick up the first one.
459 rc
= get_one_physpath(root
->gr_physpath
, SLCNUM_INVALID
,
464 } else if (strcmp(mp
.mnt_fstype
, MNTTYPE_UFS
) == 0) {
465 (void) strlcpy(root
->gr_fs
[GRBM_UFS
].gfs_dev
, mp
.mnt_special
,
466 sizeof (root
->gr_fs
[GRBM_UFS
].gfs_dev
));
467 (void) strlcpy(root
->gr_fs
[GRBM_UFS
].gfs_mountp
, mp
.mnt_mountp
,
468 sizeof (root
->gr_fs
[GRBM_UFS
].gfs_mountp
));
477 grub_get_rootfsd(const grub_root_t
*root
)
479 grub_fsdesc_t
*fsd
= NULL
;
482 if (strcmp(MNTTYPE_UFS
, root
->gr_fstyp
) == 0)
483 fsd
= (grub_fsdesc_t
*)root
->gr_fs
+ GRBM_UFS
;
484 else if (strcmp(MNTTYPE_ZFS
, root
->gr_fstyp
) == 0)
485 fsd
= (grub_fsdesc_t
*)root
->gr_fs
+ GRBM_ZFS_BOOTFS
;
491 * Gets file systems mount point if any.
492 * Return 0 if filesystem is mounted, errno on failure.
495 grub_fsd_get_mountp(grub_fsdesc_t
*fsd
, char *fstyp
)
499 struct mnttab mp
= {0};
500 struct mnttab mpref
= {0};
502 fsd
->gfs_mountp
[0] = 0;
504 if ((fp
= fopen(MNTTAB
, "r")) == NULL
)
507 mpref
.mnt_special
= fsd
->gfs_dev
;
508 mpref
.mnt_fstype
= fstyp
;
510 if ((rc
= getmntany(fp
, &mp
, &mpref
)) == 0)
511 (void) strlcpy(fsd
->gfs_mountp
, mp
.mnt_mountp
,
512 sizeof (fsd
->gfs_mountp
));
520 static const char tmp_mountp
[] = "/tmp/.libgrubmgmt.%s.XXXXXX";
523 * Mount file system at tmp_mountp.
524 * Return 0 on success, errno on failure.
527 grub_fsd_mount_tmp(grub_fsdesc_t
*fsd
, const char *fstyp
)
532 struct ufs_args ufs_args
= {UFSMNT_LARGEFILES
};
533 char mntopts
[MNT_LINE_MAX
] = "";
537 assert(!fsd
->gfs_is_tmp_mounted
);
539 fsd
->gfs_mountp
[0] = 0;
541 if (strcmp(fstyp
, MNTTYPE_UFS
) == 0) {
542 (void) strlcpy(mntopts
, MNTOPT_LARGEFILES
, sizeof (mntopts
));
544 dtsz
= sizeof (ufs_args
);
545 } else if (strcmp(fstyp
, MNTTYPE_ZFS
) != 0) {
546 return (EG_UNKNOWNFS
);
549 /* construct name for temporary mount point */
550 pos
= strrchr(fsd
->gfs_dev
, '/');
551 pos
= (pos
== NULL
) ? fsd
->gfs_dev
: pos
+ 1;
553 (void) snprintf(fsd
->gfs_mountp
, sizeof (fsd
->gfs_mountp
),
555 if (mkdtemp(fsd
->gfs_mountp
) != NULL
) {
556 if ((rc
= mount(fsd
->gfs_dev
, fsd
->gfs_mountp
,
557 MS_DATA
| MS_OPTIONSTR
| MS_RDONLY
,
558 fstyp
, data
, dtsz
, mntopts
, sizeof (mntopts
))) != 0) {
560 * mount failed, collect errno and remove temp dir
563 (void) rmdir(fsd
->gfs_mountp
);
570 fsd
->gfs_mountp
[0] = 0;
573 * Note that valid values for gfs_is_tmp_mounted are 0,1.
574 * Any other value indicates that something bad happened.
575 * Probably grub_fsd_umount_tmp() wasn't called or didn't
578 fsd
->gfs_is_tmp_mounted
+= (rc
== 0);
583 * Unmount file system at tmp_mountp.
586 grub_fsd_umount_tmp(grub_fsdesc_t
*fsd
)
591 if (fsd
->gfs_is_tmp_mounted
) {
592 if (fsd
->gfs_mountp
[0] != 0) {
593 (void) umount2(fsd
->gfs_mountp
, 0);
594 (void) rmdir(fsd
->gfs_mountp
);
595 fsd
->gfs_mountp
[0] = 0;
597 fsd
->gfs_is_tmp_mounted
= 0;