Home | History | Annotate | Download | only in zinject
      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 #include <libzfs.h>
     27 
     28 #undef verify	/* both libzfs.h and zfs_context.h want to define this */
     29 
     30 #include <sys/zfs_context.h>
     31 
     32 #include <errno.h>
     33 #include <fcntl.h>
     34 #include <stdarg.h>
     35 #include <stddef.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <strings.h>
     39 #include <sys/file.h>
     40 #include <sys/mntent.h>
     41 #include <sys/mnttab.h>
     42 #include <sys/param.h>
     43 #include <sys/stat.h>
     44 
     45 #include <sys/dmu.h>
     46 #include <sys/dmu_objset.h>
     47 #include <sys/dnode.h>
     48 #include <sys/vdev_impl.h>
     49 
     50 #include <sys/mkdev.h>
     51 
     52 #include "zinject.h"
     53 
     54 extern void kernel_init(int);
     55 extern void kernel_fini(void);
     56 
     57 static int debug;
     58 
     59 static void
     60 ziprintf(const char *fmt, ...)
     61 {
     62 	va_list ap;
     63 
     64 	if (!debug)
     65 		return;
     66 
     67 	va_start(ap, fmt);
     68 	(void) vprintf(fmt, ap);
     69 	va_end(ap);
     70 }
     71 
     72 /*
     73  * Given a full path to a file, translate into a dataset name and a relative
     74  * path within the dataset.  'dataset' must be at least MAXNAMELEN characters,
     75  * and 'relpath' must be at least MAXPATHLEN characters.  We also pass a stat64
     76  * buffer, which we need later to get the object ID.
     77  */
     78 static int
     79 parse_pathname(const char *fullpath, char *dataset, char *relpath,
     80     struct stat64 *statbuf)
     81 {
     82 	struct extmnttab mp;
     83 	FILE *fp;
     84 	int match;
     85 	const char *rel;
     86 
     87 	if (fullpath[0] != '/') {
     88 		(void) fprintf(stderr, "invalid object '%s': must be full "
     89 		    "path\n", fullpath);
     90 		usage();
     91 		return (-1);
     92 	}
     93 
     94 	if (strlen(fullpath) >= MAXPATHLEN) {
     95 		(void) fprintf(stderr, "invalid object; pathname too long\n");
     96 		return (-1);
     97 	}
     98 
     99 	if (stat64(fullpath, statbuf) != 0) {
    100 		(void) fprintf(stderr, "cannot open '%s': %s\n",
    101 		    fullpath, strerror(errno));
    102 		return (-1);
    103 	}
    104 
    105 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
    106 		(void) fprintf(stderr, "cannot open /etc/mnttab\n");
    107 		return (-1);
    108 	}
    109 
    110 	match = 0;
    111 	while (getextmntent(fp, &mp, sizeof (mp)) == 0) {
    112 		if (makedev(mp.mnt_major, mp.mnt_minor) == statbuf->st_dev) {
    113 			match = 1;
    114 			break;
    115 		}
    116 	}
    117 
    118 	if (!match) {
    119 		(void) fprintf(stderr, "cannot find mountpoint for '%s'\n",
    120 		    fullpath);
    121 		return (-1);
    122 	}
    123 
    124 	if (strcmp(mp.mnt_fstype, MNTTYPE_ZFS) != 0) {
    125 		(void) fprintf(stderr, "invalid path '%s': not a ZFS "
    126 		    "filesystem\n", fullpath);
    127 		return (-1);
    128 	}
    129 
    130 	if (strncmp(fullpath, mp.mnt_mountp, strlen(mp.mnt_mountp)) != 0) {
    131 		(void) fprintf(stderr, "invalid path '%s': mountpoint "
    132 		    "doesn't match path\n", fullpath);
    133 		return (-1);
    134 	}
    135 
    136 	(void) strcpy(dataset, mp.mnt_special);
    137 
    138 	rel = fullpath + strlen(mp.mnt_mountp);
    139 	if (rel[0] == '/')
    140 		rel++;
    141 	(void) strcpy(relpath, rel);
    142 
    143 	return (0);
    144 }
    145 
    146 /*
    147  * Convert from a (dataset, path) pair into a (objset, object) pair.  Note that
    148  * we grab the object number from the inode number, since looking this up via
    149  * libzpool is a real pain.
    150  */
    151 /* ARGSUSED */
    152 static int
    153 object_from_path(const char *dataset, const char *path, struct stat64 *statbuf,
    154     zinject_record_t *record)
    155 {
    156 	objset_t *os;
    157 	int err;
    158 
    159 	/*
    160 	 * Before doing any libzpool operations, call sync() to ensure that the
    161 	 * on-disk state is consistent with the in-core state.
    162 	 */
    163 	sync();
    164 
    165 	err = dmu_objset_own(dataset, DMU_OST_ZFS, B_TRUE, FTAG, &os);
    166 	if (err != 0) {
    167 		(void) fprintf(stderr, "cannot open dataset '%s': %s\n",
    168 		    dataset, strerror(err));
    169 		return (-1);
    170 	}
    171 
    172 	record->zi_objset = dmu_objset_id(os);
    173 	record->zi_object = statbuf->st_ino;
    174 
    175 	dmu_objset_disown(os, FTAG);
    176 
    177 	return (0);
    178 }
    179 
    180 /*
    181  * Calculate the real range based on the type, level, and range given.
    182  */
    183 static int
    184 calculate_range(const char *dataset, err_type_t type, int level, char *range,
    185     zinject_record_t *record)
    186 {
    187 	objset_t *os = NULL;
    188 	dnode_t *dn = NULL;
    189 	int err;
    190 	int ret = -1;
    191 
    192 	/*
    193 	 * Determine the numeric range from the string.
    194 	 */
    195 	if (range == NULL) {
    196 		/*
    197 		 * If range is unspecified, set the range to [0,-1], which
    198 		 * indicates that the whole object should be treated as an
    199 		 * error.
    200 		 */
    201 		record->zi_start = 0;
    202 		record->zi_end = -1ULL;
    203 	} else {
    204 		char *end;
    205 
    206 		/* XXX add support for suffixes */
    207 		record->zi_start = strtoull(range, &end, 10);
    208 
    209 
    210 		if (*end == '\0')
    211 			record->zi_end = record->zi_start + 1;
    212 		else if (*end == ',')
    213 			record->zi_end = strtoull(end + 1, &end, 10);
    214 
    215 		if (*end != '\0') {
    216 			(void) fprintf(stderr, "invalid range '%s': must be "
    217 			    "a numeric range of the form 'start[,end]'\n",
    218 			    range);
    219 			goto out;
    220 		}
    221 	}
    222 
    223 	switch (type) {
    224 	case TYPE_DATA:
    225 		break;
    226 
    227 	case TYPE_DNODE:
    228 		/*
    229 		 * If this is a request to inject faults into the dnode, then we
    230 		 * must translate the current (objset,object) pair into an
    231 		 * offset within the metadnode for the objset.  Specifying any
    232 		 * kind of range with type 'dnode' is illegal.
    233 		 */
    234 		if (range != NULL) {
    235 			(void) fprintf(stderr, "range cannot be specified when "
    236 			    "type is 'dnode'\n");
    237 			goto out;
    238 		}
    239 
    240 		record->zi_start = record->zi_object * sizeof (dnode_phys_t);
    241 		record->zi_end = record->zi_start + sizeof (dnode_phys_t);
    242 		record->zi_object = 0;
    243 		break;
    244 	}
    245 
    246 	/*
    247 	 * Get the dnode associated with object, so we can calculate the block
    248 	 * size.
    249 	 */
    250 	if ((err = dmu_objset_own(dataset, DMU_OST_ANY,
    251 	    B_TRUE, FTAG, &os)) != 0) {
    252 		(void) fprintf(stderr, "cannot open dataset '%s': %s\n",
    253 		    dataset, strerror(err));
    254 		goto out;
    255 	}
    256 
    257 	if (record->zi_object == 0) {
    258 		dn = os->os_meta_dnode;
    259 	} else {
    260 		err = dnode_hold(os, record->zi_object, FTAG, &dn);
    261 		if (err != 0) {
    262 			(void) fprintf(stderr, "failed to hold dnode "
    263 			    "for object %llu\n",
    264 			    (u_longlong_t)record->zi_object);
    265 			goto out;
    266 		}
    267 	}
    268 
    269 
    270 	ziprintf("data shift: %d\n", (int)dn->dn_datablkshift);
    271 	ziprintf(" ind shift: %d\n", (int)dn->dn_indblkshift);
    272 
    273 	/*
    274 	 * Translate range into block IDs.
    275 	 */
    276 	if (record->zi_start != 0 || record->zi_end != -1ULL) {
    277 		record->zi_start >>= dn->dn_datablkshift;
    278 		record->zi_end >>= dn->dn_datablkshift;
    279 	}
    280 
    281 	/*
    282 	 * Check level, and then translate level 0 blkids into ranges
    283 	 * appropriate for level of indirection.
    284 	 */
    285 	record->zi_level = level;
    286 	if (level > 0) {
    287 		ziprintf("level 0 blkid range: [%llu, %llu]\n",
    288 		    record->zi_start, record->zi_end);
    289 
    290 		if (level >= dn->dn_nlevels) {
    291 			(void) fprintf(stderr, "level %d exceeds max level "
    292 			    "of object (%d)\n", level, dn->dn_nlevels - 1);
    293 			goto out;
    294 		}
    295 
    296 		if (record->zi_start != 0 || record->zi_end != 0) {
    297 			int shift = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
    298 
    299 			for (; level > 0; level--) {
    300 				record->zi_start >>= shift;
    301 				record->zi_end >>= shift;
    302 			}
    303 		}
    304 	}
    305 
    306 	ret = 0;
    307 out:
    308 	if (dn) {
    309 		if (dn != os->os_meta_dnode)
    310 			dnode_rele(dn, FTAG);
    311 	}
    312 	if (os)
    313 		dmu_objset_disown(os, FTAG);
    314 
    315 	return (ret);
    316 }
    317 
    318 int
    319 translate_record(err_type_t type, const char *object, const char *range,
    320     int level, zinject_record_t *record, char *poolname, char *dataset)
    321 {
    322 	char path[MAXPATHLEN];
    323 	char *slash;
    324 	struct stat64 statbuf;
    325 	int ret = -1;
    326 
    327 	kernel_init(FREAD);
    328 
    329 	debug = (getenv("ZINJECT_DEBUG") != NULL);
    330 
    331 	ziprintf("translating: %s\n", object);
    332 
    333 	if (MOS_TYPE(type)) {
    334 		/*
    335 		 * MOS objects are treated specially.
    336 		 */
    337 		switch (type) {
    338 		case TYPE_MOS:
    339 			record->zi_type = 0;
    340 			break;
    341 		case TYPE_MOSDIR:
    342 			record->zi_type = DMU_OT_OBJECT_DIRECTORY;
    343 			break;
    344 		case TYPE_METASLAB:
    345 			record->zi_type = DMU_OT_OBJECT_ARRAY;
    346 			break;
    347 		case TYPE_CONFIG:
    348 			record->zi_type = DMU_OT_PACKED_NVLIST;
    349 			break;
    350 		case TYPE_BPLIST:
    351 			record->zi_type = DMU_OT_BPLIST;
    352 			break;
    353 		case TYPE_SPACEMAP:
    354 			record->zi_type = DMU_OT_SPACE_MAP;
    355 			break;
    356 		case TYPE_ERRLOG:
    357 			record->zi_type = DMU_OT_ERROR_LOG;
    358 			break;
    359 		}
    360 
    361 		dataset[0] = '\0';
    362 		(void) strcpy(poolname, object);
    363 		return (0);
    364 	}
    365 
    366 	/*
    367 	 * Convert a full path into a (dataset, file) pair.
    368 	 */
    369 	if (parse_pathname(object, dataset, path, &statbuf) != 0)
    370 		goto err;
    371 
    372 	ziprintf("   dataset: %s\n", dataset);
    373 	ziprintf("      path: %s\n", path);
    374 
    375 	/*
    376 	 * Convert (dataset, file) into (objset, object)
    377 	 */
    378 	if (object_from_path(dataset, path, &statbuf, record) != 0)
    379 		goto err;
    380 
    381 	ziprintf("raw objset: %llu\n", record->zi_objset);
    382 	ziprintf("raw object: %llu\n", record->zi_object);
    383 
    384 	/*
    385 	 * For the given object, calculate the real (type, level, range)
    386 	 */
    387 	if (calculate_range(dataset, type, level, (char *)range, record) != 0)
    388 		goto err;
    389 
    390 	ziprintf("    objset: %llu\n", record->zi_objset);
    391 	ziprintf("    object: %llu\n", record->zi_object);
    392 	if (record->zi_start == 0 &&
    393 	    record->zi_end == -1ULL)
    394 		ziprintf("     range: all\n");
    395 	else
    396 		ziprintf("     range: [%llu, %llu]\n", record->zi_start,
    397 		    record->zi_end);
    398 
    399 	/*
    400 	 * Copy the pool name
    401 	 */
    402 	(void) strcpy(poolname, dataset);
    403 	if ((slash = strchr(poolname, '/')) != NULL)
    404 		*slash = '\0';
    405 
    406 	ret = 0;
    407 
    408 err:
    409 	kernel_fini();
    410 	return (ret);
    411 }
    412 
    413 int
    414 translate_raw(const char *str, zinject_record_t *record)
    415 {
    416 	/*
    417 	 * A raw bookmark of the form objset:object:level:blkid, where each
    418 	 * number is a hexidecimal value.
    419 	 */
    420 	if (sscanf(str, "%llx:%llx:%x:%llx", (u_longlong_t *)&record->zi_objset,
    421 	    (u_longlong_t *)&record->zi_object, &record->zi_level,
    422 	    (u_longlong_t *)&record->zi_start) != 4) {
    423 		(void) fprintf(stderr, "bad raw spec '%s': must be of the form "
    424 		    "'objset:object:level:blkid'\n", str);
    425 		return (-1);
    426 	}
    427 
    428 	record->zi_end = record->zi_start;
    429 
    430 	return (0);
    431 }
    432 
    433 int
    434 translate_device(const char *pool, const char *device, err_type_t label_type,
    435     zinject_record_t *record)
    436 {
    437 	char *end;
    438 	zpool_handle_t *zhp;
    439 	nvlist_t *tgt;
    440 	boolean_t isspare, iscache;
    441 
    442 	/*
    443 	 * Given a device name or GUID, create an appropriate injection record
    444 	 * with zi_guid set.
    445 	 */
    446 	if ((zhp = zpool_open(g_zfs, pool)) == NULL)
    447 		return (-1);
    448 
    449 	record->zi_guid = strtoull(device, &end, 16);
    450 	if (record->zi_guid == 0 || *end != '\0') {
    451 		tgt = zpool_find_vdev(zhp, device, &isspare, &iscache, NULL);
    452 
    453 		if (tgt == NULL) {
    454 			(void) fprintf(stderr, "cannot find device '%s' in "
    455 			    "pool '%s'\n", device, pool);
    456 			return (-1);
    457 		}
    458 
    459 		verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID,
    460 		    &record->zi_guid) == 0);
    461 	}
    462 
    463 	switch (label_type) {
    464 	case TYPE_LABEL_UBERBLOCK:
    465 		record->zi_start = offsetof(vdev_label_t, vl_uberblock[0]);
    466 		record->zi_end = record->zi_start + VDEV_UBERBLOCK_RING - 1;
    467 		break;
    468 	case TYPE_LABEL_NVLIST:
    469 		record->zi_start = offsetof(vdev_label_t, vl_vdev_phys);
    470 		record->zi_end = record->zi_start + VDEV_PHYS_SIZE - 1;
    471 		break;
    472 	}
    473 	return (0);
    474 }
    475