The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/openzfs/cmd/zinject/translate.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 https://opensource.org/licenses/CDDL-1.0.
   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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
   23  * Copyright (c) 2012, 2020 by Delphix. All rights reserved.
   24  */
   25 
   26 #include <libzfs.h>
   27 
   28 #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>
   40 
   41 #include <sys/dmu.h>
   42 #include <sys/dmu_objset.h>
   43 #include <sys/dnode.h>
   44 #include <sys/vdev_impl.h>
   45 
   46 #include <sys/mkdev.h>
   47 
   48 #include "zinject.h"
   49 
   50 static int debug;
   51 
   52 static void
   53 ziprintf(const char *fmt, ...)
   54 {
   55         va_list ap;
   56 
   57         if (!debug)
   58                 return;
   59 
   60         va_start(ap, fmt);
   61         (void) vprintf(fmt, ap);
   62         va_end(ap);
   63 }
   64 
   65 static void
   66 compress_slashes(const char *src, char *dest)
   67 {
   68         while (*src != '\0') {
   69                 *dest = *src++;
   70                 while (*dest == '/' && *src == '/')
   71                         ++src;
   72                 ++dest;
   73         }
   74         *dest = '\0';
   75 }
   76 
   77 /*
   78  * Given a full path to a file, translate into a dataset name and a relative
   79  * path within the dataset.  'dataset' must be at least MAXNAMELEN characters,
   80  * and 'relpath' must be at least MAXPATHLEN characters.  We also pass a stat64
   81  * buffer, which we need later to get the object ID.
   82  */
   83 static int
   84 parse_pathname(const char *inpath, char *dataset, char *relpath,
   85     struct stat64 *statbuf)
   86 {
   87         struct extmnttab mp;
   88         const char *rel;
   89         char fullpath[MAXPATHLEN];
   90 
   91         compress_slashes(inpath, fullpath);
   92 
   93         if (fullpath[0] != '/') {
   94                 (void) fprintf(stderr, "invalid object '%s': must be full "
   95                     "path\n", fullpath);
   96                 usage();
   97                 return (-1);
   98         }
   99 
  100         if (getextmntent(fullpath, &mp, statbuf) != 0) {
  101                 (void) fprintf(stderr, "cannot find mountpoint for '%s'\n",
  102                     fullpath);
  103                 return (-1);
  104         }
  105 
  106         if (strcmp(mp.mnt_fstype, MNTTYPE_ZFS) != 0) {
  107                 (void) fprintf(stderr, "invalid path '%s': not a ZFS "
  108                     "filesystem\n", fullpath);
  109                 return (-1);
  110         }
  111 
  112         if (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);
  115                 return (-1);
  116         }
  117 
  118         (void) strlcpy(dataset, mp.mnt_special, MAXNAMELEN);
  119 
  120         rel = fullpath + strlen(mp.mnt_mountp);
  121         if (rel[0] == '/')
  122                 rel++;
  123         (void) strlcpy(relpath, rel, MAXPATHLEN);
  124 
  125         return (0);
  126 }
  127 
  128 /*
  129  * Convert from a dataset to a objset id. Note that
  130  * we grab the object number from the inode number.
  131  */
  132 static int
  133 object_from_path(const char *dataset, uint64_t object, zinject_record_t *record)
  134 {
  135         zfs_handle_t *zhp;
  136 
  137         if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)
  138                 return (-1);
  139 
  140         record->zi_objset = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
  141         record->zi_object = object;
  142 
  143         zfs_close(zhp);
  144 
  145         return (0);
  146 }
  147 
  148 /*
  149  * Initialize the range based on the type, level, and range given.
  150  */
  151 static int
  152 initialize_range(err_type_t type, int level, char *range,
  153     zinject_record_t *record)
  154 {
  155         /*
  156          * Determine the numeric range from the string.
  157          */
  158         if (range == NULL) {
  159                 /*
  160                  * If range is unspecified, set the range to [0,-1], which
  161                  * indicates that the whole object should be treated as an
  162                  * error.
  163                  */
  164                 record->zi_start = 0;
  165                 record->zi_end = -1ULL;
  166         } else {
  167                 char *end;
  168 
  169                 /* XXX add support for suffixes */
  170                 record->zi_start = strtoull(range, &end, 10);
  171 
  172 
  173                 if (*end == '\0')
  174                         record->zi_end = record->zi_start + 1;
  175                 else if (*end == ',')
  176                         record->zi_end = strtoull(end + 1, &end, 10);
  177 
  178                 if (*end != '\0') {
  179                         (void) fprintf(stderr, "invalid range '%s': must be "
  180                             "a numeric range of the form 'start[,end]'\n",
  181                             range);
  182                         return (-1);
  183                 }
  184         }
  185 
  186         switch (type) {
  187         default:
  188                 break;
  189 
  190         case TYPE_DATA:
  191                 break;
  192 
  193         case TYPE_DNODE:
  194                 /*
  195                  * If this is a request to inject faults into the dnode, then we
  196                  * must translate the current (objset,object) pair into an
  197                  * offset within the metadnode for the objset.  Specifying any
  198                  * kind of range with type 'dnode' is illegal.
  199                  */
  200                 if (range != NULL) {
  201                         (void) fprintf(stderr, "range cannot be specified when "
  202                             "type is 'dnode'\n");
  203                         return (-1);
  204                 }
  205 
  206                 record->zi_start = record->zi_object * sizeof (dnode_phys_t);
  207                 record->zi_end = record->zi_start + sizeof (dnode_phys_t);
  208                 record->zi_object = 0;
  209                 break;
  210         }
  211 
  212         record->zi_level = level;
  213 
  214         return (0);
  215 }
  216 
  217 int
  218 translate_record(err_type_t type, const char *object, const char *range,
  219     int level, zinject_record_t *record, char *poolname, char *dataset)
  220 {
  221         char path[MAXPATHLEN];
  222         char *slash;
  223         struct stat64 statbuf;
  224         int ret = -1;
  225 
  226         debug = (getenv("ZINJECT_DEBUG") != NULL);
  227 
  228         ziprintf("translating: %s\n", object);
  229 
  230         if (MOS_TYPE(type)) {
  231                 /*
  232                  * MOS objects are treated specially.
  233                  */
  234                 switch (type) {
  235                 default:
  236                         break;
  237                 case TYPE_MOS:
  238                         record->zi_type = 0;
  239                         break;
  240                 case TYPE_MOSDIR:
  241                         record->zi_type = DMU_OT_OBJECT_DIRECTORY;
  242                         break;
  243                 case TYPE_METASLAB:
  244                         record->zi_type = DMU_OT_OBJECT_ARRAY;
  245                         break;
  246                 case TYPE_CONFIG:
  247                         record->zi_type = DMU_OT_PACKED_NVLIST;
  248                         break;
  249                 case TYPE_BPOBJ:
  250                         record->zi_type = DMU_OT_BPOBJ;
  251                         break;
  252                 case TYPE_SPACEMAP:
  253                         record->zi_type = DMU_OT_SPACE_MAP;
  254                         break;
  255                 case TYPE_ERRLOG:
  256                         record->zi_type = DMU_OT_ERROR_LOG;
  257                         break;
  258                 }
  259 
  260                 dataset[0] = '\0';
  261                 (void) strlcpy(poolname, object, MAXNAMELEN);
  262                 return (0);
  263         }
  264 
  265         /*
  266          * Convert a full path into a (dataset, file) pair.
  267          */
  268         if (parse_pathname(object, dataset, path, &statbuf) != 0)
  269                 goto err;
  270 
  271         ziprintf("   dataset: %s\n", dataset);
  272         ziprintf("      path: %s\n", path);
  273 
  274         /*
  275          * Convert (dataset, file) into (objset, object)
  276          */
  277         if (object_from_path(dataset, statbuf.st_ino, record) != 0)
  278                 goto err;
  279 
  280         ziprintf("raw objset: %llu\n", record->zi_objset);
  281         ziprintf("raw object: %llu\n", record->zi_object);
  282 
  283         /*
  284          * For the given object, initialize the range in bytes
  285          */
  286         if (initialize_range(type, level, (char *)range, record) != 0)
  287                 goto err;
  288 
  289         ziprintf("    objset: %llu\n", record->zi_objset);
  290         ziprintf("    object: %llu\n", record->zi_object);
  291         if (record->zi_start == 0 &&
  292             record->zi_end == -1ULL)
  293                 ziprintf("     range: all\n");
  294         else
  295                 ziprintf("     range: [%llu, %llu]\n", record->zi_start,
  296                     record->zi_end);
  297 
  298         /*
  299          * Copy the pool name
  300          */
  301         (void) strlcpy(poolname, dataset, MAXNAMELEN);
  302         if ((slash = strchr(poolname, '/')) != NULL)
  303                 *slash = '\0';
  304 
  305         ret = 0;
  306 
  307 err:
  308         return (ret);
  309 }
  310 
  311 int
  312 translate_raw(const char *str, zinject_record_t *record)
  313 {
  314         /*
  315          * A raw bookmark of the form objset:object:level:blkid, where each
  316          * number is a hexadecimal value.
  317          */
  318         if (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);
  323                 return (-1);
  324         }
  325 
  326         record->zi_end = record->zi_start;
  327 
  328         return (0);
  329 }
  330 
  331 int
  332 translate_device(const char *pool, const char *device, err_type_t label_type,
  333     zinject_record_t *record)
  334 {
  335         char *end;
  336         zpool_handle_t *zhp;
  337         nvlist_t *tgt;
  338         boolean_t isspare, iscache;
  339 
  340         /*
  341          * Given a device name or GUID, create an appropriate injection record
  342          * with zi_guid set.
  343          */
  344         if ((zhp = zpool_open(g_zfs, pool)) == NULL)
  345                 return (-1);
  346 
  347         record->zi_guid = strtoull(device, &end, 0);
  348         if (record->zi_guid == 0 || *end != '\0') {
  349                 tgt = zpool_find_vdev(zhp, device, &isspare, &iscache, NULL);
  350 
  351                 if (tgt == NULL) {
  352                         (void) fprintf(stderr, "cannot find device '%s' in "
  353                             "pool '%s'\n", device, pool);
  354                         zpool_close(zhp);
  355                         return (-1);
  356                 }
  357 
  358                 verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID,
  359                     &record->zi_guid) == 0);
  360         }
  361 
  362         /*
  363          * Device faults can take on three different forms:
  364          * 1). delayed or hanging I/O
  365          * 2). zfs label faults
  366          * 3). generic disk faults
  367          */
  368         if (record->zi_timer != 0) {
  369                 record->zi_cmd = ZINJECT_DELAY_IO;
  370         } else if (label_type != TYPE_INVAL) {
  371                 record->zi_cmd = ZINJECT_LABEL_FAULT;
  372         } else {
  373                 record->zi_cmd = ZINJECT_DEVICE_FAULT;
  374         }
  375 
  376         switch (label_type) {
  377         default:
  378                 break;
  379         case TYPE_LABEL_UBERBLOCK:
  380                 record->zi_start = offsetof(vdev_label_t, vl_uberblock[0]);
  381                 record->zi_end = record->zi_start + VDEV_UBERBLOCK_RING - 1;
  382                 break;
  383         case TYPE_LABEL_NVLIST:
  384                 record->zi_start = offsetof(vdev_label_t, vl_vdev_phys);
  385                 record->zi_end = record->zi_start + VDEV_PHYS_SIZE - 1;
  386                 break;
  387         case TYPE_LABEL_PAD1:
  388                 record->zi_start = offsetof(vdev_label_t, vl_pad1);
  389                 record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;
  390                 break;
  391         case TYPE_LABEL_PAD2:
  392                 record->zi_start = offsetof(vdev_label_t, vl_be);
  393                 record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;
  394                 break;
  395         }
  396         zpool_close(zhp);
  397         return (0);
  398 }

Cache object: c740b3458419b546c2b083df91cddc15


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.