Path: blob/main/sys/contrib/openzfs/cmd/zinject/translate.c
48383 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* 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.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/21/*22* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.23* Copyright (c) 2012, 2020 by Delphix. All rights reserved.24*/2526#include <libzfs.h>2728#include <errno.h>29#include <fcntl.h>30#include <stdarg.h>31#include <stddef.h>32#include <stdio.h>33#include <stdlib.h>34#include <string.h>35#include <sys/file.h>36#include <sys/mntent.h>37#include <sys/mnttab.h>38#include <sys/param.h>39#include <sys/stat.h>4041#include <sys/dmu.h>42#include <sys/dmu_objset.h>43#include <sys/dnode.h>44#include <sys/vdev_impl.h>4546#include <sys/mkdev.h>4748#include "zinject.h"4950static int debug;5152static void53ziprintf(const char *fmt, ...)54{55va_list ap;5657if (!debug)58return;5960va_start(ap, fmt);61(void) vprintf(fmt, ap);62va_end(ap);63}6465static void66compress_slashes(const char *src, char *dest)67{68while (*src != '\0') {69*dest = *src++;70while (*dest == '/' && *src == '/')71++src;72++dest;73}74*dest = '\0';75}7677/*78* Given a full path to a file, translate into a dataset name and a relative79* path within the dataset. 'dataset' must be at least MAXNAMELEN characters,80* and 'relpath' must be at least MAXPATHLEN characters. We also pass a stat6481* buffer, which we need later to get the object ID.82*/83static int84parse_pathname(const char *inpath, char *dataset, char *relpath,85struct stat64 *statbuf)86{87struct extmnttab mp;88const char *rel;89char fullpath[MAXPATHLEN];9091compress_slashes(inpath, fullpath);9293if (fullpath[0] != '/') {94(void) fprintf(stderr, "invalid object '%s': must be full "95"path\n", fullpath);96usage();97return (-1);98}99100if (getextmntent(fullpath, &mp, statbuf) != 0) {101(void) fprintf(stderr, "cannot find mountpoint for '%s'\n",102fullpath);103return (-1);104}105106if (strcmp(mp.mnt_fstype, MNTTYPE_ZFS) != 0) {107(void) fprintf(stderr, "invalid path '%s': not a ZFS "108"filesystem\n", fullpath);109return (-1);110}111112if (strncmp(fullpath, mp.mnt_mountp, strlen(mp.mnt_mountp)) != 0) {113(void) fprintf(stderr, "invalid path '%s': mountpoint "114"doesn't match path\n", fullpath);115return (-1);116}117118(void) strlcpy(dataset, mp.mnt_special, MAXNAMELEN);119120rel = fullpath + strlen(mp.mnt_mountp);121if (rel[0] == '/')122rel++;123(void) strlcpy(relpath, rel, MAXPATHLEN);124125return (0);126}127128/*129* Convert from a dataset to a objset id. Note that130* we grab the object number from the inode number.131*/132static int133object_from_path(const char *dataset, uint64_t object, zinject_record_t *record)134{135zfs_handle_t *zhp;136137if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)138return (-1);139140record->zi_objset = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);141record->zi_object = object;142143zfs_close(zhp);144145return (0);146}147148/*149* Initialize the range based on the type, level, and range given.150*/151static int152initialize_range(err_type_t type, int level, char *range,153zinject_record_t *record)154{155/*156* Determine the numeric range from the string.157*/158if (range == NULL) {159/*160* If range is unspecified, set the range to [0,-1], which161* indicates that the whole object should be treated as an162* error.163*/164record->zi_start = 0;165record->zi_end = -1ULL;166} else {167char *end;168169/* XXX add support for suffixes */170record->zi_start = strtoull(range, &end, 10);171172173if (*end == '\0')174record->zi_end = record->zi_start + 1;175else if (*end == ',')176record->zi_end = strtoull(end + 1, &end, 10);177178if (*end != '\0') {179(void) fprintf(stderr, "invalid range '%s': must be "180"a numeric range of the form 'start[,end]'\n",181range);182return (-1);183}184}185186switch (type) {187default:188break;189190case TYPE_DATA:191break;192193case TYPE_DNODE:194/*195* If this is a request to inject faults into the dnode, then we196* must translate the current (objset,object) pair into an197* offset within the metadnode for the objset. Specifying any198* kind of range with type 'dnode' is illegal.199*/200if (range != NULL) {201(void) fprintf(stderr, "range cannot be specified when "202"type is 'dnode'\n");203return (-1);204}205206record->zi_start = record->zi_object * sizeof (dnode_phys_t);207record->zi_end = record->zi_start + sizeof (dnode_phys_t);208record->zi_object = 0;209break;210}211212record->zi_level = level;213214return (0);215}216217int218translate_record(err_type_t type, const char *object, const char *range,219int level, zinject_record_t *record, char *poolname, char *dataset)220{221char path[MAXPATHLEN];222char *slash;223struct stat64 statbuf;224int ret = -1;225226debug = (getenv("ZINJECT_DEBUG") != NULL);227228ziprintf("translating: %s\n", object);229230if (MOS_TYPE(type)) {231/*232* MOS objects are treated specially.233*/234switch (type) {235default:236break;237case TYPE_MOS:238record->zi_type = 0;239break;240case TYPE_MOSDIR:241record->zi_type = DMU_OT_OBJECT_DIRECTORY;242break;243case TYPE_METASLAB:244record->zi_type = DMU_OT_OBJECT_ARRAY;245break;246case TYPE_CONFIG:247record->zi_type = DMU_OT_PACKED_NVLIST;248break;249case TYPE_BPOBJ:250record->zi_type = DMU_OT_BPOBJ;251break;252case TYPE_SPACEMAP:253record->zi_type = DMU_OT_SPACE_MAP;254break;255case TYPE_ERRLOG:256record->zi_type = DMU_OT_ERROR_LOG;257break;258}259260dataset[0] = '\0';261(void) strlcpy(poolname, object, MAXNAMELEN);262return (0);263}264265/*266* Convert a full path into a (dataset, file) pair.267*/268if (parse_pathname(object, dataset, path, &statbuf) != 0)269goto err;270271ziprintf(" dataset: %s\n", dataset);272ziprintf(" path: %s\n", path);273274/*275* Convert (dataset, file) into (objset, object)276*/277if (object_from_path(dataset, statbuf.st_ino, record) != 0)278goto err;279280ziprintf("raw objset: %llu\n", record->zi_objset);281ziprintf("raw object: %llu\n", record->zi_object);282283/*284* For the given object, initialize the range in bytes285*/286if (initialize_range(type, level, (char *)range, record) != 0)287goto err;288289ziprintf(" objset: %llu\n", record->zi_objset);290ziprintf(" object: %llu\n", record->zi_object);291if (record->zi_start == 0 &&292record->zi_end == -1ULL)293ziprintf(" range: all\n");294else295ziprintf(" range: [%llu, %llu]\n", record->zi_start,296record->zi_end);297298/*299* Copy the pool name300*/301(void) strlcpy(poolname, dataset, MAXNAMELEN);302if ((slash = strchr(poolname, '/')) != NULL)303*slash = '\0';304305ret = 0;306307err:308return (ret);309}310311int312translate_raw(const char *str, zinject_record_t *record)313{314/*315* A raw bookmark of the form objset:object:level:blkid, where each316* number is a hexadecimal value.317*/318if (sscanf(str, "%llx:%llx:%x:%llx", (u_longlong_t *)&record->zi_objset,319(u_longlong_t *)&record->zi_object, &record->zi_level,320(u_longlong_t *)&record->zi_start) != 4) {321(void) fprintf(stderr, "bad raw spec '%s': must be of the form "322"'objset:object:level:blkid'\n", str);323return (-1);324}325326record->zi_end = record->zi_start;327328return (0);329}330331int332translate_device(const char *pool, const char *device, err_type_t label_type,333zinject_record_t *record)334{335char *end;336zpool_handle_t *zhp;337nvlist_t *tgt;338boolean_t isspare, iscache;339340/*341* Given a device name or GUID, create an appropriate injection record342* with zi_guid set.343*/344if ((zhp = zpool_open(g_zfs, pool)) == NULL)345return (-1);346347record->zi_guid = strtoull(device, &end, 0);348if (record->zi_guid == 0 || *end != '\0') {349tgt = zpool_find_vdev(zhp, device, &isspare, &iscache, NULL);350351if (tgt == NULL) {352(void) fprintf(stderr, "cannot find device '%s' in "353"pool '%s'\n", device, pool);354zpool_close(zhp);355return (-1);356}357358verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID,359&record->zi_guid) == 0);360}361362/*363* Device faults can take on three different forms:364* 1). delayed or hanging I/O365* 2). zfs label faults366* 3). generic disk faults367*/368if (record->zi_timer != 0) {369record->zi_cmd = ZINJECT_DELAY_IO;370} else if (label_type != TYPE_INVAL) {371record->zi_cmd = ZINJECT_LABEL_FAULT;372} else {373record->zi_cmd = ZINJECT_DEVICE_FAULT;374}375376switch (label_type) {377default:378break;379case TYPE_LABEL_UBERBLOCK:380record->zi_start = offsetof(vdev_label_t, vl_uberblock[0]);381record->zi_end = record->zi_start + VDEV_UBERBLOCK_RING - 1;382break;383case TYPE_LABEL_NVLIST:384record->zi_start = offsetof(vdev_label_t, vl_vdev_phys);385record->zi_end = record->zi_start + VDEV_PHYS_SIZE - 1;386break;387case TYPE_LABEL_PAD1:388record->zi_start = offsetof(vdev_label_t, vl_pad1);389record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;390break;391case TYPE_LABEL_PAD2:392record->zi_start = offsetof(vdev_label_t, vl_be);393record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;394break;395}396zpool_close(zhp);397return (0);398}399400401