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