1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 27 */ 28 29 /* 30 * This file contains all the functions that manipulate the file 31 * system where the GRUB menu resides. 32 */ 33 #include <stdio.h> 34 #include <errno.h> 35 #include <stdlib.h> 36 #include <strings.h> 37 #include <unistd.h> 38 #include <fcntl.h> 39 #include <assert.h> 40 #include <sys/types.h> 41 #include <sys/stat.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> 47 #include <libfstyp.h> 48 #if defined(i386) || defined(__amd64) 49 #include <libfdisk.h> 50 #endif 51 52 #include "libgrub_impl.h" 53 54 static int 55 slice_match(const char *physpath, int slice) 56 { 57 const char *pos; 58 59 return ((pos = strrchr(physpath, slice)) == NULL || 60 pos[1] != 0 || pos[-1] != ':'); 61 } 62 63 /* 64 * Returns zero if path contains ufs 65 */ 66 static int 67 slice_ufs(const char *path) 68 { 69 int fd, ret; 70 const char *id; 71 fstyp_handle_t hdl; 72 73 fd = open(path, O_RDONLY); 74 if ((ret = fstyp_init(fd, 0, NULL, &hdl)) == 0) { 75 ret = fstyp_ident(hdl, "ufs", &id); 76 fstyp_fini(hdl); 77 } 78 (void) close(fd); 79 return (ret); 80 } 81 82 83 static int 84 get_sol_prtnum(const char *physpath) 85 { 86 int i, fd; 87 char *pos; 88 size_t sz; 89 struct mboot *mb; 90 struct ipart *ipart; 91 char boot_sect[512]; 92 char rdev[MAXNAMELEN]; 93 #if defined(i386) || defined(__amd64) 94 ext_part_t *epp; 95 int ext_part_found = 0; 96 #endif 97 98 (void) snprintf(rdev, sizeof (rdev), "/devices%s,raw", physpath); 99 100 if ((pos = strrchr(rdev, ':')) == NULL) 101 return (PRTNUM_INVALID); 102 103 pos[1] = SLCNUM_WHOLE_DISK; 104 105 fd = open(rdev, O_RDONLY); 106 sz = read(fd, boot_sect, sizeof (boot_sect)); 107 (void) close(fd); 108 109 if (sz != sizeof (boot_sect)) 110 return (PRTNUM_INVALID); 111 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) 117 return (i); 118 119 #if defined(i386) || defined(__amd64) 120 if (!fdisk_is_dos_extended(ipart[i].systid) || 121 (ext_part_found == 1)) 122 continue; 123 124 ext_part_found = 1; 125 126 if (libfdisk_init(&epp, rdev, NULL, FDISK_READ_DISK) == 127 FDISK_SUCCESS) { 128 uint32_t begs, nums; 129 int pno; 130 int rval; 131 132 rval = fdisk_get_solaris_part(epp, &pno, &begs, &nums); 133 134 libfdisk_fini(&epp); 135 136 if (rval == FDISK_SUCCESS) 137 return (pno - 1); 138 } 139 #endif 140 } 141 return (PRTNUM_INVALID); 142 } 143 144 /* 145 * Get physpath, topfs and bootfs for ZFS root dataset. 146 * Return 0 on success, non-zero (not errno) on failure. 147 */ 148 static int 149 get_zfs_root(zfs_handle_t *zfh, grub_fs_t *fs, grub_root_t *root) 150 { 151 int ret; 152 zpool_handle_t *zph; 153 const char *name; 154 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) 158 return (-1); 159 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, 165 B_FALSE)) == 0) { 166 167 (void) strlcpy(root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev, name, 168 sizeof (root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev)); 169 (void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_BOOTFS, 170 MNTTYPE_ZFS); 171 (void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_TOPFS, 172 MNTTYPE_ZFS); 173 } 174 175 zpool_close(zph); 176 return (ret); 177 } 178 179 /* 180 * On entry physpath parameter supposed to contain: 181 * <disk_physpath>[<space><disk_physpath>]*. 182 * Retrieves first <disk_physpath> that matches both partition and slice. 183 * If any partition and slice is acceptable, first <disk_physpath> is returned. 184 */ 185 static int 186 get_one_physpath(char *physpath, uint_t prtnum, uint_t slcnum) 187 { 188 int ret; 189 char *tmp, *tok; 190 191 if (!IS_SLCNUM_VALID(slcnum) && !IS_PRTNUM_VALID(prtnum)) { 192 (void) strtok(physpath, " "); 193 return (0); 194 } 195 196 if ((tmp = strdup(physpath)) == NULL) 197 return (errno); 198 199 ret = ENODEV; 200 for (tok = strtok(tmp, " "); tok != NULL; tok = strtok(NULL, " ")) { 201 if ((ret = (slice_match(tok, slcnum) != 0 || 202 get_sol_prtnum(tok) != prtnum)) == 0) { 203 (void) strcpy(physpath, tok); 204 break; 205 } 206 } 207 208 free(tmp); 209 if (ret) 210 ret = ENODEV; 211 return (ret); 212 } 213 214 static int 215 zfs_bootsign(zfs_handle_t *zfh, void *data) 216 { 217 grub_barg_t *barg; 218 grub_menu_t *menu; 219 struct stat st; 220 char path[MAXPATHLEN]; 221 222 barg = (grub_barg_t *)data; 223 menu = barg->gb_entry->ge_menu; 224 225 do { 226 if (get_zfs_root(zfh, &menu->gm_fs, &barg->gb_root) != 0 || 227 get_one_physpath(barg->gb_root.gr_physpath, barg->gb_prtnum, 228 barg->gb_slcnum) != 0) 229 break; 230 231 /* 232 * if top zfs dataset is not mounted, mount it now 233 */ 234 if (barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp[0] == 0) { 235 if (grub_fsd_mount_tmp(barg->gb_root.gr_fs + 236 GRBM_ZFS_TOPFS, MNTTYPE_ZFS) != 0) 237 break; 238 } 239 240 /* check that bootsign exists and it is a regular file */ 241 (void) snprintf(path, sizeof (path), "%s%s", 242 barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp, 243 barg->gb_bootsign); 244 245 if (lstat(path, &st) != 0 || S_ISREG(st.st_mode) == 0 || 246 (st.st_mode & S_IRUSR) == 0) 247 break; 248 249 (void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_ZFS, 250 sizeof (barg->gb_root.gr_fstyp)); 251 barg->gb_walkret = 0; 252 /* LINTED: E_CONSTANT_CONDITION */ 253 } while (0); 254 255 grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_ZFS_TOPFS); 256 zfs_close(zfh); 257 258 /* return non-zero to terminate the walk */ 259 return (barg->gb_walkret == 0); 260 } 261 262 static int 263 get_devlink(di_devlink_t dl, void *arg) 264 { 265 const char *path; 266 grub_barg_t *barg; 267 268 barg = (grub_barg_t *)arg; 269 if ((path = di_devlink_path(dl)) != NULL) 270 (void) strlcpy(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev, path, 271 sizeof (barg->gb_root.gr_fs[GRBM_UFS].gfs_dev)); 272 return (DI_WALK_TERMINATE); 273 } 274 275 static int 276 ufs_bootsign_check(grub_barg_t *barg) 277 { 278 int ret; 279 struct stat st; 280 grub_menu_t *mp; 281 char path[MAXPATHLEN]; 282 283 mp = barg->gb_entry->ge_menu; 284 285 /* get /dev/dsk link */ 286 if (di_devlink_walk(mp->gm_fs.gf_dvlh, "^dsk/", 287 barg->gb_root.gr_physpath, DI_PRIMARY_LINK, barg, get_devlink) != 0) 288 return (errno); 289 /* 290 * if disk is not mounted, mount it now 291 */ 292 if (grub_fsd_get_mountp(barg->gb_root.gr_fs + GRBM_UFS, 293 MNTTYPE_UFS) != 0) { 294 if ((ret = 295 slice_ufs(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev)) != 0 || 296 (ret = grub_fsd_mount_tmp(barg->gb_root.gr_fs + GRBM_UFS, 297 MNTTYPE_UFS)) != 0) 298 return (ret); 299 } 300 301 (void) snprintf(path, sizeof (path), "%s%s", 302 barg->gb_root.gr_fs[GRBM_UFS].gfs_mountp, barg->gb_bootsign); 303 304 if (lstat(path, &st) == 0 && S_ISREG(st.st_mode) && 305 (st.st_mode & S_IRUSR) != 0) { 306 barg->gb_walkret = 0; 307 (void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_UFS, 308 sizeof (barg->gb_root.gr_fstyp)); 309 } 310 311 grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_UFS); 312 return (barg->gb_walkret); 313 } 314 315 static int 316 ufs_bootsign(di_node_t node, di_minor_t minor, void *arg) 317 { 318 uint_t prtnum; 319 char *name, *path; 320 grub_barg_t *barg; 321 322 barg = (grub_barg_t *)arg; 323 324 if (di_minor_spectype(minor) != S_IFBLK) 325 return (DI_WALK_CONTINUE); 326 327 name = di_minor_name(minor); 328 if (name[0] != barg->gb_slcnum || name[1] != 0) 329 return (DI_WALK_CONTINUE); 330 331 path = di_devfs_path(node); 332 (void) snprintf(barg->gb_root.gr_physpath, 333 sizeof (barg->gb_root.gr_physpath), "%s:%c", path, barg->gb_slcnum); 334 di_devfs_path_free(path); 335 336 prtnum = get_sol_prtnum(barg->gb_root.gr_physpath); 337 if (!IS_PRTNUM_VALID(prtnum)) 338 return (DI_WALK_CONTINUE); 339 340 /* 341 * check only specified partition, slice 342 */ 343 344 if (IS_PRTNUM_VALID(barg->gb_prtnum)) { 345 if (prtnum != barg->gb_prtnum || ufs_bootsign_check(barg) != 0) 346 return (DI_WALK_CONTINUE); 347 return (DI_WALK_TERMINATE); 348 } 349 350 /* 351 * Walk through all slices in found solaris partition 352 */ 353 354 barg->gb_prtnum = prtnum; 355 minor = DI_MINOR_NIL; 356 357 while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) { 358 359 if (di_minor_spectype(minor) != S_IFBLK) 360 continue; 361 362 name = di_minor_name(minor); 363 if (!IS_SLCNUM_VALID(name[0]) || name[1] != 0) 364 continue; 365 366 barg->gb_slcnum = name[0]; 367 path = strrchr(barg->gb_root.gr_physpath, ':'); 368 path[1] = barg->gb_slcnum; 369 370 if (ufs_bootsign_check(barg) == 0) 371 return (DI_WALK_TERMINATE); 372 } 373 374 barg->gb_prtnum = (uint_t)PRTNUM_INVALID; 375 barg->gb_slcnum = (uint_t)SLCNUM_WHOLE_DISK; 376 return (DI_WALK_CONTINUE); 377 } 378 379 /* 380 * Differs from what GRUB is doing: GRUB searchs through all disks seen by bios 381 * for bootsign, if bootsign is found on ufs slice GRUB sets it as a root, 382 * if on zfs, then GRUB uses zfs slice as root only if bootsign wasn't found 383 * on other slices. 384 * That function first searches through all top datasets of active zpools, 385 * then if bootsign still not found walks through all disks and tries to 386 * find ufs slice with the bootsign. 387 */ 388 int 389 grub_find_bootsign(grub_barg_t *barg) 390 { 391 grub_menu_t *mp; 392 mp = barg->gb_entry->ge_menu; 393 394 /* try to find bootsign over zfs pools */ 395 barg->gb_walkret = EG_BOOTSIGN; 396 (void) zfs_iter_root(mp->gm_fs.gf_lzfh, zfs_bootsign, barg); 397 398 /* try ufs now */ 399 if (barg->gb_walkret != 0 && di_walk_minor(mp->gm_fs.gf_diroot, 400 DDI_NT_BLOCK, 0, barg, ufs_bootsign) != 0) 401 return (errno); 402 403 return (barg->gb_walkret); 404 } 405 406 /* 407 * Get current root file system. 408 * Return 0 on success, errno code on failure. 409 */ 410 int 411 grub_current_root(grub_fs_t *fs, grub_root_t *root) 412 { 413 int rc = 0; 414 FILE *fp = NULL; 415 char *name = NULL; 416 zfs_handle_t *zfh = NULL; 417 struct mnttab mp = {0}; 418 struct mnttab mpref = {0}; 419 char buf[MAXNAMELEN] = {0}; 420 421 mpref.mnt_mountp = "/"; 422 423 if ((fp = fopen(MNTTAB, "r")) == NULL) 424 return (errno); 425 426 /* 427 * getmntany returns non-zero for failure, and sets errno 428 */ 429 rc = getmntany(fp, &mp, &mpref); 430 if (rc != 0) 431 rc = errno; 432 433 (void) fclose(fp); 434 435 if (rc != 0) 436 return (rc); 437 438 (void) strlcpy(root->gr_fstyp, mp.mnt_fstype, sizeof (root->gr_fstyp)); 439 440 if (strcmp(root->gr_fstyp, MNTTYPE_ZFS) == 0) { 441 442 (void) strlcpy(buf, mp.mnt_special, sizeof (buf)); 443 if ((name = strtok(buf, "/")) == NULL) 444 return (EG_CURROOT); 445 446 if ((zfh = zfs_open(fs->gf_lzfh, name, ZFS_TYPE_FILESYSTEM)) == 447 NULL) 448 return (EG_OPENZFS); 449 450 /* 451 * get_zfs_root returns non-zero on failure, not errno. 452 */ 453 if (get_zfs_root(zfh, fs, root)) 454 rc = EG_CURROOT; 455 else 456 /* 457 * For mirrored root physpath would contain the list of 458 * all bootable devices, pick up the first one. 459 */ 460 rc = get_one_physpath(root->gr_physpath, SLCNUM_INVALID, 461 PRTNUM_INVALID); 462 463 zfs_close(zfh); 464 465 } else if (strcmp(mp.mnt_fstype, MNTTYPE_UFS) == 0) { 466 (void) strlcpy(root->gr_fs[GRBM_UFS].gfs_dev, mp.mnt_special, 467 sizeof (root->gr_fs[GRBM_UFS].gfs_dev)); 468 (void) strlcpy(root->gr_fs[GRBM_UFS].gfs_mountp, mp.mnt_mountp, 469 sizeof (root->gr_fs[GRBM_UFS].gfs_mountp)); 470 } else { 471 rc = EG_UNKNOWNFS; 472 } 473 474 return (rc); 475 } 476 477 grub_fsdesc_t * 478 grub_get_rootfsd(const grub_root_t *root) 479 { 480 grub_fsdesc_t *fsd = NULL; 481 482 assert(root); 483 if (strcmp(MNTTYPE_UFS, root->gr_fstyp) == 0) 484 fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_UFS; 485 else if (strcmp(MNTTYPE_ZFS, root->gr_fstyp) == 0) 486 fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_ZFS_BOOTFS; 487 488 return (fsd); 489 } 490 491 /* 492 * Gets file systems mount point if any. 493 * Return 0 if filesystem is mounted, errno on failure. 494 */ 495 int 496 grub_fsd_get_mountp(grub_fsdesc_t *fsd, char *fstyp) 497 { 498 int rc; 499 FILE *fp = NULL; 500 struct mnttab mp = {0}; 501 struct mnttab mpref = {0}; 502 503 fsd->gfs_mountp[0] = 0; 504 505 if ((fp = fopen(MNTTAB, "r")) == NULL) 506 return (errno); 507 508 mpref.mnt_special = fsd->gfs_dev; 509 mpref.mnt_fstype = fstyp; 510 511 if ((rc = getmntany(fp, &mp, &mpref)) == 0) 512 (void) strlcpy(fsd->gfs_mountp, mp.mnt_mountp, 513 sizeof (fsd->gfs_mountp)); 514 else 515 rc = EG_GETMNTTAB; 516 517 (void) fclose(fp); 518 return (rc); 519 } 520 521 static const char tmp_mountp[] = "/tmp/.libgrubmgmt.%s.XXXXXX"; 522 523 /* 524 * Mount file system at tmp_mountp. 525 * Return 0 on success, errno on failure. 526 */ 527 int 528 grub_fsd_mount_tmp(grub_fsdesc_t *fsd, const char *fstyp) 529 { 530 const char *pos; 531 void *data = NULL; 532 int dtsz = 0; 533 struct ufs_args ufs_args = {UFSMNT_LARGEFILES}; 534 char mntopts[MNT_LINE_MAX] = ""; 535 int rc = 0; 536 537 assert(fsd); 538 assert(!fsd->gfs_is_tmp_mounted); 539 540 fsd->gfs_mountp[0] = 0; 541 542 if (strcmp(fstyp, MNTTYPE_UFS) == 0) { 543 (void) strlcpy(mntopts, MNTOPT_LARGEFILES, sizeof (mntopts)); 544 data = &ufs_args; 545 dtsz = sizeof (ufs_args); 546 } else if (strcmp(fstyp, MNTTYPE_ZFS) != 0) { 547 return (EG_UNKNOWNFS); 548 } 549 550 /* construct name for temporary mount point */ 551 pos = strrchr(fsd->gfs_dev, '/'); 552 pos = (pos == NULL) ? fsd->gfs_dev : pos + 1; 553 554 (void) snprintf(fsd->gfs_mountp, sizeof (fsd->gfs_mountp), 555 tmp_mountp, pos); 556 if (mkdtemp(fsd->gfs_mountp) != NULL) { 557 if ((rc = mount(fsd->gfs_dev, fsd->gfs_mountp, 558 MS_DATA | MS_OPTIONSTR | MS_RDONLY, 559 fstyp, data, dtsz, mntopts, sizeof (mntopts))) != 0) { 560 /* 561 * mount failed, collect errno and remove temp dir 562 */ 563 rc = errno; 564 (void) rmdir(fsd->gfs_mountp); 565 } 566 } else { 567 rc = errno; 568 } 569 570 if (rc != 0) 571 fsd->gfs_mountp[0] = 0; 572 573 /* 574 * Note that valid values for gfs_is_tmp_mounted are 0,1. 575 * Any other value indicates that something bad happened. 576 * Probably grub_fsd_umount_tmp() wasn't called or didn't 577 * work as expected. 578 */ 579 fsd->gfs_is_tmp_mounted += (rc == 0); 580 return (rc); 581 } 582 583 /* 584 * Unmount file system at tmp_mountp. 585 */ 586 void 587 grub_fsd_umount_tmp(grub_fsdesc_t *fsd) 588 { 589 if (fsd == NULL) 590 return; 591 592 if (fsd->gfs_is_tmp_mounted) { 593 if (fsd->gfs_mountp[0] != 0) { 594 (void) umount2(fsd->gfs_mountp, 0); 595 (void) rmdir(fsd->gfs_mountp); 596 fsd->gfs_mountp[0] = 0; 597 } 598 fsd->gfs_is_tmp_mounted = 0; 599 } 600 } 601