📄 fsys_zfs.c
字号:
return (1); nvlist += 4; *out = nvlist; return (0);}static char *nvlist_array(char *nvlist, int index){ int i, encode_size; for (i = 0; i < index; i++) { /* skip the header, nvl_version, and nvl_nvflag */ nvlist = nvlist + 4 * 2; while ((encode_size = BSWAP_32(*(uint32_t *)nvlist))) nvlist += encode_size; /* goto the next nvpair */ nvlist = nvlist + 4 * 2; /* skip the ending 2 zeros - 8 bytes */ } return (nvlist);}static intnvlist_lookup_value(char *nvlist, char *name, void *val, int valtype, int *nelmp){ int name_len, type, slen, encode_size; char *nvpair, *nvp_name, *strval = val; uint64_t *intval = val; /* skip the header, nvl_version, and nvl_nvflag */ nvlist = nvlist + 4 * 2; /* * Loop thru the nvpair list * The XDR representation of an integer is in big-endian byte order. */ while ((encode_size = BSWAP_32(*(uint32_t *)nvlist))) { nvpair = nvlist + 4 * 2; /* skip the encode/decode size */ name_len = BSWAP_32(*(uint32_t *)nvpair); nvpair += 4; nvp_name = nvpair; nvpair = nvpair + ((name_len + 3) & ~3); /* align */ type = BSWAP_32(*(uint32_t *)nvpair); nvpair += 4; if (((strncmp(nvp_name, name, name_len) == 0) && type == valtype)) { int nelm; if (((nelm = BSWAP_32(*(uint32_t *)nvpair)) < 1)) return (1); nvpair += 4; switch (valtype) { case DATA_TYPE_STRING: slen = BSWAP_32(*(uint32_t *)nvpair); nvpair += 4; grub_memmove(strval, nvpair, slen); strval[slen] = '\0'; return (0); case DATA_TYPE_UINT64: *intval = BSWAP_64(*(uint64_t *)nvpair); return (0); case DATA_TYPE_NVLIST: *(void **)val = (void *)nvpair; return (0); case DATA_TYPE_NVLIST_ARRAY: *(void **)val = (void *)nvpair; if (nelmp) *nelmp = nelm; return (0); } } nvlist += encode_size; /* goto the next nvpair */ } return (1);}/* * Check if this vdev is online and is in a good state. */static intvdev_validate(char *nv){ uint64_t ival; if (nvlist_lookup_value(nv, ZPOOL_CONFIG_OFFLINE, &ival, DATA_TYPE_UINT64, NULL) == 0 || nvlist_lookup_value(nv, ZPOOL_CONFIG_FAULTED, &ival, DATA_TYPE_UINT64, NULL) == 0 || nvlist_lookup_value(nv, ZPOOL_CONFIG_DEGRADED, &ival, DATA_TYPE_UINT64, NULL) == 0 || nvlist_lookup_value(nv, ZPOOL_CONFIG_REMOVED, &ival, DATA_TYPE_UINT64, NULL) == 0) return (ERR_DEV_VALUES); return (0);}/* * Get a list of valid vdev pathname from the boot device. * The caller should already allocate MAXNAMELEN memory for bootpath. */static intvdev_get_bootpath(char *nv, char *bootpath){ char type[16]; bootpath[0] = '\0'; if (nvlist_lookup_value(nv, ZPOOL_CONFIG_TYPE, &type, DATA_TYPE_STRING, NULL)) return (ERR_FSYS_CORRUPT); if (strcmp(type, VDEV_TYPE_DISK) == 0) { if (vdev_validate(nv) != 0 || nvlist_lookup_value(nv, ZPOOL_CONFIG_PHYS_PATH, bootpath, DATA_TYPE_STRING, NULL) != 0) return (ERR_NO_BOOTPATH); } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0) { int nelm, i; char *child; if (nvlist_lookup_value(nv, ZPOOL_CONFIG_CHILDREN, &child, DATA_TYPE_NVLIST_ARRAY, &nelm)) return (ERR_FSYS_CORRUPT); for (i = 0; i < nelm; i++) { char tmp_path[MAXNAMELEN]; char *child_i; child_i = nvlist_array(child, i); if (vdev_validate(child_i) != 0) continue; if (nvlist_lookup_value(child_i, ZPOOL_CONFIG_PHYS_PATH, tmp_path, DATA_TYPE_STRING, NULL) != 0) return (ERR_NO_BOOTPATH); if ((strlen(bootpath) + strlen(tmp_path)) > MAXNAMELEN) return (ERR_WONT_FIT); if (strlen(bootpath) == 0) sprintf(bootpath, "%s", tmp_path); else sprintf(bootpath, "%s %s", bootpath, tmp_path); } } return (strlen(bootpath) > 0 ? 0 : ERR_NO_BOOTPATH);}/* * Check the disk label information and retrieve needed vdev name-value pairs. * * Return: * 0 - success * ERR_* - failure */static intcheck_pool_label(fsi_file_t *ffi, int label, char *stack){ vdev_phys_t *vdev; uint64_t sector, pool_state, txg = 0; char *nvlist, *nv; zfs_bootarea_t *zfs_ba = (zfs_bootarea_t *)ffi->ff_fsi->f_data; sector = (label * sizeof (vdev_label_t) + VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT; /* Read in the vdev name-value pair list (112K). */ if (devread(ffi, sector, 0, VDEV_PHYS_SIZE, stack) == 0) return (ERR_READ); vdev = (vdev_phys_t *)stack; if (nvlist_unpack(vdev->vp_nvlist, &nvlist)) return (ERR_FSYS_CORRUPT); if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_POOL_STATE, &pool_state, DATA_TYPE_UINT64, NULL)) return (ERR_FSYS_CORRUPT); if (pool_state == POOL_STATE_DESTROYED) return (ERR_FILESYSTEM_NOT_FOUND); if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_POOL_NAME, current_rootpool, DATA_TYPE_STRING, NULL)) return (ERR_FSYS_CORRUPT); if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_POOL_TXG, &txg, DATA_TYPE_UINT64, NULL)) return (ERR_FSYS_CORRUPT); /* not an active device */ if (txg == 0) return (ERR_NO_BOOTPATH); if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_VDEV_TREE, &nv, DATA_TYPE_NVLIST, NULL)) return (ERR_FSYS_CORRUPT); if (vdev_get_bootpath(nv, current_bootpath)) return (ERR_NO_BOOTPATH); return (0);}/* * zfs_mount() locates a valid uberblock of the root pool and read in its MOS * to the memory address MOS. * * Return: * 1 - success * 0 - failure */static intzfs_mount(fsi_file_t *ffi, const char *options){ char *stack; int label = 0; uberblock_phys_t *ub_array, *ubbest = NULL; objset_phys_t *osp; zfs_bootarea_t *zfs_ba; /* if zfs is already mounted, don't do it again */ if (is_zfs_mount == 1) return (1); /* get much bigger data block for zfs */ if (((zfs_ba = malloc(sizeof (zfs_bootarea_t))) == NULL)) { return (1); } bzero(zfs_ba, sizeof (zfs_bootarea_t)); /* replace small data area in fsi with big one */ free(ffi->ff_fsi->f_data); ffi->ff_fsi->f_data = (void *)zfs_ba; /* If an boot filesystem is passed in, set it to current_bootfs */ if (options != NULL) { if (strlen(options) < MAXNAMELEN) { strcpy(current_bootfs, options); } } stackbase = ZFS_SCRATCH; stack = stackbase; ub_array = (uberblock_phys_t *)stack; stack += VDEV_UBERBLOCK_RING; osp = (objset_phys_t *)stack; stack += sizeof (objset_phys_t); /* XXX add back labels support? */ for (label = 0; ubbest == NULL && label < (VDEV_LABELS/2); label++) { uint64_t sector = (label * sizeof (vdev_label_t) + VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE + VDEV_PHYS_SIZE) >> SPA_MINBLOCKSHIFT; /* Read in the uberblock ring (128K). */ if (devread(ffi, sector, 0, VDEV_UBERBLOCK_RING, (char *)ub_array) == 0) continue; if ((ubbest = find_bestub(ffi, ub_array, label)) != NULL && zio_read(ffi, &ubbest->ubp_uberblock.ub_rootbp, osp, stack) == 0) { VERIFY_OS_TYPE(osp, DMU_OST_META); /* Got the MOS. Save it at the memory addr MOS. */ grub_memmove(MOS, &osp->os_meta_dnode, DNODE_SIZE); if (check_pool_label(ffi, label, stack)) return (0); /* * Copy fsi->f_data to ffi->ff_data since * fsig_mount copies from ff_data to f_data * overwriting fsi->f_data. */ bcopy(zfs_ba, fsig_file_buf(ffi), FSYS_BUFLEN); is_zfs_mount = 1; return (1); } } return (0);}/* * zfs_open() locates a file in the rootpool by following the * MOS and places the dnode of the file in the memory address DNODE. * * Return: * 1 - success * 0 - failure */static intzfs_open(fsi_file_t *ffi, char *filename){ char *stack; dnode_phys_t *mdn; char *bootstring; zfs_bootarea_t *zfs_ba = (zfs_bootarea_t *)ffi->ff_fsi->f_data; file_buf = NULL; stackbase = ZFS_SCRATCH; stack = stackbase; mdn = (dnode_phys_t *)stack; stack += sizeof (dnode_phys_t); dnode_mdn = NULL; dnode_buf = (dnode_phys_t *)stack; stack += 1<<DNODE_BLOCK_SHIFT; /* * menu.lst is placed at the root pool filesystem level, * do not goto 'current_bootfs'. */ if (is_top_dataset_file(filename)) { if ((errnum = get_objset_mdn(ffi, MOS, NULL, NULL, mdn, stack))) return (0); current_bootfs_obj = 0; } else { if (current_bootfs[0] == '\0') { /* Get the default root filesystem object number */ if ((errnum = get_default_bootfsobj(ffi, MOS, ¤t_bootfs_obj, stack))) return (0); if ((errnum = get_objset_mdn(ffi, MOS, NULL, ¤t_bootfs_obj, mdn, stack))) return (0); } else { if ((errnum = get_objset_mdn(ffi, MOS, current_bootfs, ¤t_bootfs_obj, mdn, stack))) return (0); } /* * Put zfs rootpool and boot obj number into bootstring. */ if (is_zfs_open == 0) { char temp[25]; /* needs to hold long long */ int alloc_size; char zfs_bootstr[] = "zfs-bootfs="; char zfs_bootpath[] = ",bootpath='"; snprintf(temp, sizeof(temp), "%llu", (unsigned long long) current_bootfs_obj); alloc_size = strlen(zfs_bootstr) + strlen(current_rootpool) + strlen(temp) + strlen(zfs_bootpath) + strlen(current_bootpath) + 3; bootstring = fsi_bootstring_alloc(ffi->ff_fsi, alloc_size); if (bootstring != NULL) { strcpy(bootstring, zfs_bootstr); strcat(bootstring, current_rootpool); strcat(bootstring, "/"); strcat(bootstring, temp); strcat(bootstring, zfs_bootpath); strcat(bootstring, current_bootpath); strcat(bootstring, "'"); is_zfs_open = 1; } } } if (dnode_get_path(ffi, mdn, filename, DNODE, stack)) { errnum = ERR_FILE_NOT_FOUND; return (0); } /* get the file size and set the file position to 0 */ filemax = ((znode_phys_t *)DN_BONUS(DNODE))->zp_size; filepos = 0; dnode_buf = NULL; return (1);}/* * zfs_read reads in the data blocks pointed by the DNODE. * * Return: * len - the length successfully read in to the buffer * 0 - failure */static intzfs_read(fsi_file_t *ffi, char *buf, int len){ char *stack; int blksz, length, movesize; zfs_bootarea_t *zfs_ba = (zfs_bootarea_t *)ffi->ff_fsi->f_data; if (file_buf == NULL) { file_buf = stackbase; stackbase += SPA_MAXBLOCKSIZE; file_start = file_end = 0; } stack = stackbase; /* * If offset is in memory, move it into the buffer provided and return. */ if (filepos >= file_start && filepos+len <= file_end) { grub_memmove(buf, file_buf + filepos - file_start, len); filepos += len; return (len); } blksz = DNODE->dn_datablkszsec << SPA_MINBLOCKSHIFT; /* * Entire Dnode is too big to fit into the space available. We * will need to read it in chunks. This could be optimized to * read in as large a chunk as there is space available, but for * now, this only reads in one data block at a time. */ length = len; while (length) { /* * Find requested blkid and the offset within that block. */ uint64_t blkid = filepos / blksz; if ((errnum = dmu_read(ffi, DNODE, blkid, file_buf, stack))) return (0); file_start = blkid * blksz; file_end = file_start + blksz; movesize = MIN(length, file_end - filepos); grub_memmove(buf, file_buf + filepos - file_start, movesize); buf += movesize; length -= movesize; filepos += movesize; } return (len);}/* * No-Op */intzfs_embed(int *start_sector, int needed_sectors){ return (1);}fsi_plugin_ops_t *fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name){ static fsig_plugin_ops_t ops = { FSIMAGE_PLUGIN_VERSION, .fpo_mount = zfs_mount, .fpo_dir = zfs_open, .fpo_read = zfs_read }; *name = "zfs"; return (fsig_init(fp, &ops));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -