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.
27 * Copyright 2015 Toomas Soome <tsoome@me.com>
31 * This file contains all the functions that manipulate the file
32 * system where the GRUB menu resides.
41 #include <sys/types.h>
43 #include <sys/mount.h>
44 #include <sys/mntent.h>
45 #include <sys/mnttab.h>
46 #include <sys/efi_partition.h>
48 #include <sys/fs/ufs_mount.h>
49 #include <sys/dktp/fdisk.h>
51 #if defined(i386) || defined(__amd64)
55 #include "libgrub_impl.h"
58 slice_match(const char *physpath
, int slice
)
62 /* always match whole disk slice */
63 if (slice
== SLCNUM_WHOLE_DISK
)
66 return ((pos
= strrchr(physpath
, slice
)) == NULL
||
67 pos
[1] != 0 || pos
[-1] != ':');
71 * Returns zero if path contains ufs
74 slice_ufs(const char *path
)
80 fd
= open(path
, O_RDONLY
);
81 if ((ret
= fstyp_init(fd
, 0, NULL
, &hdl
)) == 0) {
82 ret
= fstyp_ident(hdl
, "ufs", &id
);
91 get_sol_prtnum(const char *physpath
)
99 char rdev
[MAXNAMELEN
];
100 #if defined(i386) || defined(__amd64)
102 int ext_part_found
= 0;
105 (void) snprintf(rdev
, sizeof (rdev
), "/devices%s,raw", physpath
);
107 if ((pos
= strrchr(rdev
, ':')) == NULL
)
108 return (PRTNUM_INVALID
);
111 * first check for EFI partitioning, efi_alloc_and_read()
112 * will return partition number.
114 if ((fd
= open(rdev
, O_RDONLY
|O_NDELAY
)) >= 0) {
117 if ((i
= efi_alloc_and_read(fd
, &vtoc
)) >= 0) {
118 /* zfs is using V_USR */
119 if (vtoc
->efi_parts
[i
].p_tag
!= V_USR
)
120 i
= PRTNUM_INVALID
; /* error */
127 return (PRTNUM_INVALID
);
130 pos
[1] = SLCNUM_WHOLE_DISK
;
132 fd
= open(rdev
, O_RDONLY
);
133 sz
= read(fd
, boot_sect
, sizeof (boot_sect
));
136 if (sz
!= sizeof (boot_sect
))
137 return (PRTNUM_INVALID
);
139 /* parse fdisk table */
140 mb
= (struct mboot
*)(uintptr_t)boot_sect
;
141 ipart
= (struct ipart
*)(uintptr_t)mb
->parts
;
142 for (i
= 0; i
< FD_NUMPART
; ++i
) {
143 if (ipart
[i
].systid
== SUNIXOS
|| ipart
[i
].systid
== SUNIXOS2
)
146 #if defined(i386) || defined(__amd64)
147 if (!fdisk_is_dos_extended(ipart
[i
].systid
) ||
148 (ext_part_found
== 1))
153 if (libfdisk_init(&epp
, rdev
, NULL
, FDISK_READ_DISK
) ==
159 rval
= fdisk_get_solaris_part(epp
, &pno
, &begs
, &nums
);
163 if (rval
== FDISK_SUCCESS
)
168 return (PRTNUM_INVALID
);
172 * Get physpath, topfs and bootfs for ZFS root dataset.
173 * Return 0 on success, non-zero (not errno) on failure.
176 get_zfs_root(zfs_handle_t
*zfh
, grub_fs_t
*fs
, grub_root_t
*root
)
182 if (zfs_get_type(zfh
) != ZFS_TYPE_FILESYSTEM
||
183 (name
= zfs_get_name(zfh
)) == NULL
||
184 (zph
= zpool_open(fs
->gf_lzfh
, name
)) == NULL
)
187 if ((ret
= zpool_get_physpath(zph
, root
->gr_physpath
,
188 sizeof (root
->gr_physpath
))) == 0 &&
189 (ret
= zpool_get_prop(zph
, ZPOOL_PROP_BOOTFS
,
190 root
->gr_fs
[GRBM_ZFS_BOOTFS
].gfs_dev
,
191 sizeof (root
->gr_fs
[GRBM_ZFS_BOOTFS
].gfs_dev
), NULL
,
194 (void) strlcpy(root
->gr_fs
[GRBM_ZFS_TOPFS
].gfs_dev
, name
,
195 sizeof (root
->gr_fs
[GRBM_ZFS_TOPFS
].gfs_dev
));
196 (void) grub_fsd_get_mountp(root
->gr_fs
+ GRBM_ZFS_BOOTFS
,
198 (void) grub_fsd_get_mountp(root
->gr_fs
+ GRBM_ZFS_TOPFS
,
207 * On entry physpath parameter supposed to contain:
208 * <disk_physpath>[<space><disk_physpath>]*.
209 * Retrieves first <disk_physpath> that matches both partition and slice.
210 * If any partition and slice is acceptable, first <disk_physpath> is returned.
213 get_one_physpath(char *physpath
, uint_t prtnum
, uint_t slcnum
)
218 if (!IS_SLCNUM_VALID(slcnum
) && !IS_PRTNUM_VALID(prtnum
)) {
219 (void) strtok(physpath
, " ");
223 if ((tmp
= strdup(physpath
)) == NULL
)
227 for (tok
= strtok(tmp
, " "); tok
!= NULL
; tok
= strtok(NULL
, " ")) {
228 if ((ret
= (slice_match(tok
, slcnum
) != 0 ||
229 get_sol_prtnum(tok
) != prtnum
)) == 0) {
230 (void) strcpy(physpath
, tok
);
242 zfs_bootsign(zfs_handle_t
*zfh
, void *data
)
247 char path
[MAXPATHLEN
];
249 barg
= (grub_barg_t
*)data
;
250 menu
= barg
->gb_entry
->ge_menu
;
253 if (get_zfs_root(zfh
, &menu
->gm_fs
, &barg
->gb_root
) != 0 ||
254 get_one_physpath(barg
->gb_root
.gr_physpath
, barg
->gb_prtnum
,
255 barg
->gb_slcnum
) != 0)
259 * if top zfs dataset is not mounted, mount it now
261 if (barg
->gb_root
.gr_fs
[GRBM_ZFS_TOPFS
].gfs_mountp
[0] == 0) {
262 if (grub_fsd_mount_tmp(barg
->gb_root
.gr_fs
+
263 GRBM_ZFS_TOPFS
, MNTTYPE_ZFS
) != 0)
267 /* check that bootsign exists and it is a regular file */
268 (void) snprintf(path
, sizeof (path
), "%s%s",
269 barg
->gb_root
.gr_fs
[GRBM_ZFS_TOPFS
].gfs_mountp
,
272 if (lstat(path
, &st
) != 0 || S_ISREG(st
.st_mode
) == 0 ||
273 (st
.st_mode
& S_IRUSR
) == 0)
276 (void) strlcpy(barg
->gb_root
.gr_fstyp
, MNTTYPE_ZFS
,
277 sizeof (barg
->gb_root
.gr_fstyp
));
278 barg
->gb_walkret
= 0;
279 /* LINTED: E_CONSTANT_CONDITION */
282 grub_fsd_umount_tmp(barg
->gb_root
.gr_fs
+ GRBM_ZFS_TOPFS
);
285 /* return non-zero to terminate the walk */
286 return (barg
->gb_walkret
== 0);
290 get_devlink(di_devlink_t dl
, void *arg
)
295 barg
= (grub_barg_t
*)arg
;
296 if ((path
= di_devlink_path(dl
)) != NULL
)
297 (void) strlcpy(barg
->gb_root
.gr_fs
[GRBM_UFS
].gfs_dev
, path
,
298 sizeof (barg
->gb_root
.gr_fs
[GRBM_UFS
].gfs_dev
));
299 return (DI_WALK_TERMINATE
);
303 ufs_bootsign_check(grub_barg_t
*barg
)
308 char path
[MAXPATHLEN
];
310 mp
= barg
->gb_entry
->ge_menu
;
312 /* get /dev/dsk link */
313 if (di_devlink_walk(mp
->gm_fs
.gf_dvlh
, "^dsk/",
314 barg
->gb_root
.gr_physpath
, DI_PRIMARY_LINK
, barg
, get_devlink
) != 0)
317 * if disk is not mounted, mount it now
319 if (grub_fsd_get_mountp(barg
->gb_root
.gr_fs
+ GRBM_UFS
,
322 slice_ufs(barg
->gb_root
.gr_fs
[GRBM_UFS
].gfs_dev
)) != 0 ||
323 (ret
= grub_fsd_mount_tmp(barg
->gb_root
.gr_fs
+ GRBM_UFS
,
328 (void) snprintf(path
, sizeof (path
), "%s%s",
329 barg
->gb_root
.gr_fs
[GRBM_UFS
].gfs_mountp
, barg
->gb_bootsign
);
331 if (lstat(path
, &st
) == 0 && S_ISREG(st
.st_mode
) &&
332 (st
.st_mode
& S_IRUSR
) != 0) {
333 barg
->gb_walkret
= 0;
334 (void) strlcpy(barg
->gb_root
.gr_fstyp
, MNTTYPE_UFS
,
335 sizeof (barg
->gb_root
.gr_fstyp
));
338 grub_fsd_umount_tmp(barg
->gb_root
.gr_fs
+ GRBM_UFS
);
339 return (barg
->gb_walkret
);
343 ufs_bootsign(di_node_t node
, di_minor_t minor
, void *arg
)
349 barg
= (grub_barg_t
*)arg
;
351 if (di_minor_spectype(minor
) != S_IFBLK
)
352 return (DI_WALK_CONTINUE
);
354 name
= di_minor_name(minor
);
355 if (name
[0] != barg
->gb_slcnum
|| name
[1] != 0)
356 return (DI_WALK_CONTINUE
);
358 path
= di_devfs_path(node
);
359 (void) snprintf(barg
->gb_root
.gr_physpath
,
360 sizeof (barg
->gb_root
.gr_physpath
), "%s:%c", path
, barg
->gb_slcnum
);
361 di_devfs_path_free(path
);
363 prtnum
= get_sol_prtnum(barg
->gb_root
.gr_physpath
);
364 if (!IS_PRTNUM_VALID(prtnum
))
365 return (DI_WALK_CONTINUE
);
368 * check only specified partition, slice
371 if (IS_PRTNUM_VALID(barg
->gb_prtnum
)) {
372 if (prtnum
!= barg
->gb_prtnum
|| ufs_bootsign_check(barg
) != 0)
373 return (DI_WALK_CONTINUE
);
374 return (DI_WALK_TERMINATE
);
378 * Walk through all slices in found solaris partition
381 barg
->gb_prtnum
= prtnum
;
382 minor
= DI_MINOR_NIL
;
384 while ((minor
= di_minor_next(node
, minor
)) != DI_MINOR_NIL
) {
386 if (di_minor_spectype(minor
) != S_IFBLK
)
389 name
= di_minor_name(minor
);
390 if (!IS_SLCNUM_VALID(name
[0]) || name
[1] != 0)
393 barg
->gb_slcnum
= name
[0];
394 path
= strrchr(barg
->gb_root
.gr_physpath
, ':');
395 path
[1] = barg
->gb_slcnum
;
397 if (ufs_bootsign_check(barg
) == 0)
398 return (DI_WALK_TERMINATE
);
401 barg
->gb_prtnum
= (uint_t
)PRTNUM_INVALID
;
402 barg
->gb_slcnum
= (uint_t
)SLCNUM_WHOLE_DISK
;
403 return (DI_WALK_CONTINUE
);
407 * Differs from what GRUB is doing: GRUB searchs through all disks seen by bios
408 * for bootsign, if bootsign is found on ufs slice GRUB sets it as a root,
409 * if on zfs, then GRUB uses zfs slice as root only if bootsign wasn't found
411 * That function first searches through all top datasets of active zpools,
412 * then if bootsign still not found walks through all disks and tries to
413 * find ufs slice with the bootsign.
416 grub_find_bootsign(grub_barg_t
*barg
)
419 mp
= barg
->gb_entry
->ge_menu
;
421 /* try to find bootsign over zfs pools */
422 barg
->gb_walkret
= EG_BOOTSIGN
;
423 (void) zfs_iter_root(mp
->gm_fs
.gf_lzfh
, zfs_bootsign
, barg
);
426 if (barg
->gb_walkret
!= 0 && di_walk_minor(mp
->gm_fs
.gf_diroot
,
427 DDI_NT_BLOCK
, 0, barg
, ufs_bootsign
) != 0)
430 return (barg
->gb_walkret
);
434 * Get current root file system.
435 * Return 0 on success, errno code on failure.
438 grub_current_root(grub_fs_t
*fs
, grub_root_t
*root
)
443 zfs_handle_t
*zfh
= NULL
;
444 struct mnttab mp
= {0};
445 struct mnttab mpref
= {0};
446 char buf
[MAXNAMELEN
] = {0};
448 mpref
.mnt_mountp
= "/";
450 if ((fp
= fopen(MNTTAB
, "r")) == NULL
)
454 * getmntany returns non-zero for failure, and sets errno
456 rc
= getmntany(fp
, &mp
, &mpref
);
465 (void) strlcpy(root
->gr_fstyp
, mp
.mnt_fstype
, sizeof (root
->gr_fstyp
));
467 if (strcmp(root
->gr_fstyp
, MNTTYPE_ZFS
) == 0) {
469 (void) strlcpy(buf
, mp
.mnt_special
, sizeof (buf
));
470 if ((name
= strtok(buf
, "/")) == NULL
)
473 if ((zfh
= zfs_open(fs
->gf_lzfh
, name
, ZFS_TYPE_FILESYSTEM
)) ==
478 * get_zfs_root returns non-zero on failure, not errno.
480 if (get_zfs_root(zfh
, fs
, root
))
484 * For mirrored root physpath would contain the list of
485 * all bootable devices, pick up the first one.
487 rc
= get_one_physpath(root
->gr_physpath
, SLCNUM_INVALID
,
492 } else if (strcmp(mp
.mnt_fstype
, MNTTYPE_UFS
) == 0) {
493 (void) strlcpy(root
->gr_fs
[GRBM_UFS
].gfs_dev
, mp
.mnt_special
,
494 sizeof (root
->gr_fs
[GRBM_UFS
].gfs_dev
));
495 (void) strlcpy(root
->gr_fs
[GRBM_UFS
].gfs_mountp
, mp
.mnt_mountp
,
496 sizeof (root
->gr_fs
[GRBM_UFS
].gfs_mountp
));
505 grub_get_rootfsd(const grub_root_t
*root
)
507 grub_fsdesc_t
*fsd
= NULL
;
510 if (strcmp(MNTTYPE_UFS
, root
->gr_fstyp
) == 0)
511 fsd
= (grub_fsdesc_t
*)root
->gr_fs
+ GRBM_UFS
;
512 else if (strcmp(MNTTYPE_ZFS
, root
->gr_fstyp
) == 0)
513 fsd
= (grub_fsdesc_t
*)root
->gr_fs
+ GRBM_ZFS_BOOTFS
;
519 * Gets file systems mount point if any.
520 * Return 0 if filesystem is mounted, errno on failure.
523 grub_fsd_get_mountp(grub_fsdesc_t
*fsd
, char *fstyp
)
527 struct mnttab mp
= {0};
528 struct mnttab mpref
= {0};
530 fsd
->gfs_mountp
[0] = 0;
532 if ((fp
= fopen(MNTTAB
, "r")) == NULL
)
535 mpref
.mnt_special
= fsd
->gfs_dev
;
536 mpref
.mnt_fstype
= fstyp
;
538 if ((rc
= getmntany(fp
, &mp
, &mpref
)) == 0)
539 (void) strlcpy(fsd
->gfs_mountp
, mp
.mnt_mountp
,
540 sizeof (fsd
->gfs_mountp
));
548 static const char tmp_mountp
[] = "/tmp/.libgrubmgmt.%s.XXXXXX";
551 * Mount file system at tmp_mountp.
552 * Return 0 on success, errno on failure.
555 grub_fsd_mount_tmp(grub_fsdesc_t
*fsd
, const char *fstyp
)
560 struct ufs_args ufs_args
= {UFSMNT_LARGEFILES
};
561 char mntopts
[MNT_LINE_MAX
] = "";
565 assert(!fsd
->gfs_is_tmp_mounted
);
567 fsd
->gfs_mountp
[0] = 0;
569 if (strcmp(fstyp
, MNTTYPE_UFS
) == 0) {
570 (void) strlcpy(mntopts
, MNTOPT_LARGEFILES
, sizeof (mntopts
));
572 dtsz
= sizeof (ufs_args
);
573 } else if (strcmp(fstyp
, MNTTYPE_ZFS
) != 0) {
574 return (EG_UNKNOWNFS
);
577 /* construct name for temporary mount point */
578 pos
= strrchr(fsd
->gfs_dev
, '/');
579 pos
= (pos
== NULL
) ? fsd
->gfs_dev
: pos
+ 1;
581 (void) snprintf(fsd
->gfs_mountp
, sizeof (fsd
->gfs_mountp
),
583 if (mkdtemp(fsd
->gfs_mountp
) != NULL
) {
584 if ((rc
= mount(fsd
->gfs_dev
, fsd
->gfs_mountp
,
585 MS_DATA
| MS_OPTIONSTR
| MS_RDONLY
,
586 fstyp
, data
, dtsz
, mntopts
, sizeof (mntopts
))) != 0) {
588 * mount failed, collect errno and remove temp dir
591 (void) rmdir(fsd
->gfs_mountp
);
598 fsd
->gfs_mountp
[0] = 0;
601 * Note that valid values for gfs_is_tmp_mounted are 0,1.
602 * Any other value indicates that something bad happened.
603 * Probably grub_fsd_umount_tmp() wasn't called or didn't
606 fsd
->gfs_is_tmp_mounted
+= (rc
== 0);
611 * Unmount file system at tmp_mountp.
614 grub_fsd_umount_tmp(grub_fsdesc_t
*fsd
)
619 if (fsd
->gfs_is_tmp_mounted
) {
620 if (fsd
->gfs_mountp
[0] != 0) {
621 (void) umount2(fsd
->gfs_mountp
, 0);
622 (void) rmdir(fsd
->gfs_mountp
);
623 fsd
->gfs_mountp
[0] = 0;
625 fsd
->gfs_is_tmp_mounted
= 0;