📄 fsys_zfs.c
字号:
/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. *//* * All files in the zfs directory are derived from the OpenSolaris * zfs grub files. All files in the zfs-include directory were * included without changes. *//* * The zfs plug-in routines for GRUB are: * * zfs_mount() - locates a valid uberblock of the root pool and reads * in its MOS at the memory address MOS. * * zfs_open() - locates a plain file object by following the MOS * and places its dnode at the memory address DNODE. * * zfs_read() - read in the data blocks pointed by the DNODE. * * ZFS_SCRATCH is used as a working area. * * (memory addr) MOS DNODE ZFS_SCRATCH * | | | * +-------V---------V----------V---------------+ * memory | | dnode | dnode | scratch | * | | 512B | 512B | area | * +--------------------------------------------+ */#include <stdio.h>#include <strings.h>/* From "shared.h" */#include "mb_info.h"/* Boot signature related defines for the findroot command */#define BOOTSIGN_DIR "/boot/grub/bootsign"#define BOOTSIGN_BACKUP "/etc/bootsign"/* Maybe redirect memory requests through grub_scratch_mem. */#define RAW_ADDR(x) (x)#define RAW_SEG(x) (x)/* ZFS will use the top 4 Meg of physical memory (below 4Gig) for sratch */#define ZFS_SCRATCH_SIZE 0x400000#define MIN(x, y) ((x) < (y) ? (x) : (y))/* End from shared.h */#include "fsys_zfs.h"/* cache for a file block of the currently zfs_open()-ed file */#define file_buf zfs_ba->zfs_file_buf#define file_start zfs_ba->zfs_file_start#define file_end zfs_ba->zfs_file_end/* cache for a dnode block */#define dnode_buf zfs_ba->zfs_dnode_buf#define dnode_mdn zfs_ba->zfs_dnode_mdn#define dnode_start zfs_ba->zfs_dnode_start#define dnode_end zfs_ba->zfs_dnode_end#define stackbase zfs_ba->zfs_stackbasedecomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] ={ {"noop", 0}, {"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */ {"off", 0}, {"lzjb", lzjb_decompress} /* ZIO_COMPRESS_LZJB */};/* From disk_io.c *//* ZFS root filesystem for booting */#define current_bootpath zfs_ba->zfs_current_bootpath#define current_rootpool zfs_ba->zfs_current_rootpool#define current_bootfs zfs_ba->zfs_current_bootfs#define current_bootfs_obj zfs_ba->zfs_current_bootfs_obj#define is_zfs_mount (*fsig_int1(ffi))/* End from disk_io.c */#define is_zfs_open zfs_ba->zfs_open/* * Our own version of bcmp(). */static intzfs_bcmp(const void *s1, const void *s2, size_t n){ const unsigned char *ps1 = s1; const unsigned char *ps2 = s2; if (s1 != s2 && n != 0) { do { if (*ps1++ != *ps2++) return (1); } while (--n != 0); } return (0);}/* * Our own version of log2(). Same thing as highbit()-1. */static intzfs_log2(uint64_t num){ int i = 0; while (num > 1) { i++; num = num >> 1; } return (i);}/* Checksum Functions */static voidzio_checksum_off(const void *buf, uint64_t size, zio_cksum_t *zcp){ ZIO_SET_CHECKSUM(zcp, 0, 0, 0, 0);}/* Checksum Table and Values */zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = { {{NULL, NULL}, 0, 0, "inherit"}, {{NULL, NULL}, 0, 0, "on"}, {{zio_checksum_off, zio_checksum_off}, 0, 0, "off"}, {{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 1, "label"}, {{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 1, "gang_header"}, {{fletcher_2_native, fletcher_2_byteswap}, 0, 1, "zilog"}, {{fletcher_2_native, fletcher_2_byteswap}, 0, 0, "fletcher2"}, {{fletcher_4_native, fletcher_4_byteswap}, 1, 0, "fletcher4"}, {{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 0, "SHA256"}};/* * zio_checksum_verify: Provides support for checksum verification. * * Fletcher2, Fletcher4, and SHA256 are supported. * * Return: * -1 = Failure * 0 = Success */static intzio_checksum_verify(blkptr_t *bp, char *data, int size){ zio_cksum_t zc = bp->blk_cksum; uint32_t checksum = BP_IS_GANG(bp) ? ZIO_CHECKSUM_GANG_HEADER : BP_GET_CHECKSUM(bp); int byteswap = BP_SHOULD_BYTESWAP(bp); zio_block_tail_t *zbt = (zio_block_tail_t *)(data + size) - 1; zio_checksum_info_t *ci = &zio_checksum_table[checksum]; zio_cksum_t actual_cksum, expected_cksum; /* byteswap is not supported */ if (byteswap) return (-1); if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func[0] == NULL) return (-1); if (ci->ci_zbt) { if (checksum == ZIO_CHECKSUM_GANG_HEADER) { /* * 'gang blocks' is not supported. */ return (-1); } if (zbt->zbt_magic == BSWAP_64(ZBT_MAGIC)) { /* byte swapping is not supported */ return (-1); } else { expected_cksum = zbt->zbt_cksum; zbt->zbt_cksum = zc; ci->ci_func[0](data, size, &actual_cksum); zbt->zbt_cksum = expected_cksum; } zc = expected_cksum; } else { if (BP_IS_GANG(bp)) return (-1); ci->ci_func[byteswap](data, size, &actual_cksum); } if ((actual_cksum.zc_word[0] - zc.zc_word[0]) | (actual_cksum.zc_word[1] - zc.zc_word[1]) | (actual_cksum.zc_word[2] - zc.zc_word[2]) | (actual_cksum.zc_word[3] - zc.zc_word[3])) return (-1); return (0);}/* * vdev_label_offset takes "offset" (the offset within a vdev_label) and * returns its physical disk offset (starting from the beginning of the vdev). * * Input: * psize : Physical size of this vdev * l : Label Number (0-3) * offset : The offset with a vdev_label in which we want the physical * address * Return: * Success : physical disk offset * Failure : errnum = ERR_BAD_ARGUMENT, return value is meaningless */static uint64_tvdev_label_offset(fsi_file_t *ffi, uint64_t psize, int l, uint64_t offset){ /* XXX Need to add back label support! */ if (l >= VDEV_LABELS/2 || offset > sizeof (vdev_label_t)) { errnum = ERR_BAD_ARGUMENT; return (0); } return (offset + l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ? 0 : psize - VDEV_LABELS * sizeof (vdev_label_t)));}/* * vdev_uberblock_compare takes two uberblock structures and returns an integer * indicating the more recent of the two. * Return Value = 1 if ub2 is more recent * Return Value = -1 if ub1 is more recent * The most recent uberblock is determined using its transaction number and * timestamp. The uberblock with the highest transaction number is * considered "newer". If the transaction numbers of the two blocks match, the * timestamps are compared to determine the "newer" of the two. */static intvdev_uberblock_compare(uberblock_t *ub1, uberblock_t *ub2){ if (ub1->ub_txg < ub2->ub_txg) return (-1); if (ub1->ub_txg > ub2->ub_txg) return (1); if (ub1->ub_timestamp < ub2->ub_timestamp) return (-1); if (ub1->ub_timestamp > ub2->ub_timestamp) return (1); return (0);}/* * Three pieces of information are needed to verify an uberblock: the magic * number, the version number, and the checksum. * * Currently Implemented: version number, magic number * Need to Implement: checksum * * Return: * 0 - Success * -1 - Failure */static intuberblock_verify(uberblock_phys_t *ub, int offset){ uberblock_t *uber = &ub->ubp_uberblock; blkptr_t bp; BP_ZERO(&bp); BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); BP_SET_BYTEORDER(&bp, ZFS_HOST_BYTEORDER); ZIO_SET_CHECKSUM(&bp.blk_cksum, offset, 0, 0, 0); if (zio_checksum_verify(&bp, (char *)ub, UBERBLOCK_SIZE) != 0) return (-1); if (uber->ub_magic == UBERBLOCK_MAGIC && uber->ub_version >= SPA_VERSION_1 && uber->ub_version <= SPA_VERSION) return (0); return (-1);}/* * Find the best uberblock. * Return: * Success - Pointer to the best uberblock. * Failure - NULL */static uberblock_phys_t *find_bestub(fsi_file_t *ffi, uberblock_phys_t *ub_array, int label){ uberblock_phys_t *ubbest = NULL; int i, offset; for (i = 0; i < (VDEV_UBERBLOCK_RING >> VDEV_UBERBLOCK_SHIFT); i++) { offset = vdev_label_offset(ffi, 0, label, VDEV_UBERBLOCK_OFFSET(i)); if (errnum == ERR_BAD_ARGUMENT) return (NULL); if (uberblock_verify(&ub_array[i], offset) == 0) { if (ubbest == NULL) { ubbest = &ub_array[i]; } else if (vdev_uberblock_compare( &(ub_array[i].ubp_uberblock), &(ubbest->ubp_uberblock)) > 0) { ubbest = &ub_array[i]; } } } return (ubbest);}/* * Read in a block and put its uncompressed data in buf. * * Return: * 0 - success * errnum - failure */static intzio_read(fsi_file_t *ffi, blkptr_t *bp, void *buf, char *stack){ uint64_t offset, sector; int psize, lsize; int i, comp, cksum; psize = BP_GET_PSIZE(bp); lsize = BP_GET_LSIZE(bp); comp = BP_GET_COMPRESS(bp); cksum = BP_GET_CHECKSUM(bp); if ((unsigned int)comp >= ZIO_COMPRESS_FUNCTIONS || (comp != ZIO_COMPRESS_OFF && decomp_table[comp].decomp_func == NULL)) return (ERR_FSYS_CORRUPT); /* pick a good dva from the block pointer */ for (i = 0; i < SPA_DVAS_PER_BP; i++) { if (bp->blk_dva[i].dva_word[0] == 0 && bp->blk_dva[i].dva_word[1] == 0) continue; /* read in a block */ offset = DVA_GET_OFFSET(&bp->blk_dva[i]); sector = DVA_OFFSET_TO_PHYS_SECTOR(offset); if (comp != ZIO_COMPRESS_OFF) { if (devread(ffi, sector, 0, psize, stack) == 0) continue; if (zio_checksum_verify(bp, stack, psize) != 0) continue; decomp_table[comp].decomp_func(stack, buf, psize, lsize); } else { if (devread(ffi, sector, 0, psize, buf) == 0) continue; if (zio_checksum_verify(bp, buf, psize) != 0) continue; } return (0); } return (ERR_FSYS_CORRUPT);}/* * Get the block from a block id. * push the block onto the stack. * * Return: * 0 - success * errnum - failure */static intdmu_read(fsi_file_t *ffi, dnode_phys_t *dn, uint64_t blkid, void *buf, char *stack){ int idx, level; blkptr_t *bp_array = dn->dn_blkptr; int epbs = dn->dn_indblkshift - SPA_BLKPTRSHIFT; blkptr_t *bp, *tmpbuf; bp = (blkptr_t *)stack; stack += sizeof (blkptr_t); tmpbuf = (blkptr_t *)stack; stack += 1<<dn->dn_indblkshift; for (level = dn->dn_nlevels - 1; level >= 0; level--) { idx = (blkid >> (epbs * level)) & ((1<<epbs)-1); *bp = bp_array[idx]; if (level == 0) tmpbuf = buf; if (BP_IS_HOLE(bp)) { grub_memset(buf, 0, dn->dn_datablkszsec << SPA_MINBLOCKSHIFT); break; } else if ((errnum = zio_read(ffi, bp, tmpbuf, stack))) { return (errnum); } bp_array = tmpbuf; } return (0);}/* * mzap_lookup: Looks up property described by "name" and returns the value * in "value". * * Return: * 0 - success * errnum - failure */static intmzap_lookup(mzap_phys_t *zapobj, int objsize, char *name, uint64_t *value){ int i, chunks; mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk; chunks = objsize/MZAP_ENT_LEN - 1; for (i = 0; i < chunks; i++) { if (strcmp(mzap_ent[i].mze_name, name) == 0) { *value = mzap_ent[i].mze_value; return (0); } } return (ERR_FSYS_CORRUPT);}static uint64_tzap_hash(fsi_file_t *ffi, uint64_t salt, const char *name){ static uint64_t table[256]; const uint8_t *cp; uint8_t c; uint64_t crc = salt; if (table[128] == 0) { uint64_t *ct; int i, j; for (i = 0; i < 256; i++) { for (ct = table + i, *ct = i, j = 8; j > 0; j--) *ct = (*ct >> 1) ^ (-(*ct & 1) & ZFS_CRC64_POLY); } } if (crc == 0 || table[128] != ZFS_CRC64_POLY) { errnum = ERR_FSYS_CORRUPT; return (0); } for (cp = (const uint8_t *)name; (c = *cp) != '\0'; cp++) crc = (crc >> 8) ^ table[(crc ^ c) & 0xFF];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -