1ea8dc4beschrock/*
2ea8dc4beschrock * CDDL HEADER START
3ea8dc4beschrock *
4ea8dc4beschrock * The contents of this file are subject to the terms of the
5ea8dc4beschrock * Common Development and Distribution License (the "License").
6ea8dc4beschrock * You may not use this file except in compliance with the License.
7ea8dc4beschrock *
8ea8dc4beschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9ea8dc4beschrock * or http://www.opensolaris.org/os/licensing.
10ea8dc4beschrock * See the License for the specific language governing permissions
11ea8dc4beschrock * and limitations under the License.
12ea8dc4beschrock *
13ea8dc4beschrock * When distributing Covered Code, include this CDDL HEADER in each
14ea8dc4beschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15ea8dc4beschrock * If applicable, add the following below this CDDL HEADER, with the
16ea8dc4beschrock * fields enclosed by brackets "[]" replaced with your own identifying
17ea8dc4beschrock * information: Portions Copyright [yyyy] [name of copyright owner]
18ea8dc4beschrock *
19ea8dc4beschrock * CDDL HEADER END
20ea8dc4beschrock */
21ea8dc4beschrock/*
22f80ce22Chris Kirby * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23283b846George.Wilson * Copyright (c) 2012 by Delphix. All rights reserved.
24ea8dc4beschrock */
25ea8dc4beschrock
26ea8dc4beschrock#include <libzfs.h>
27ea8dc4beschrock
28ea8dc4beschrock#include <errno.h>
29ea8dc4beschrock#include <fcntl.h>
30ea8dc4beschrock#include <stdarg.h>
31ea8dc4beschrock#include <stddef.h>
32ea8dc4beschrock#include <stdio.h>
33ea8dc4beschrock#include <stdlib.h>
34ea8dc4beschrock#include <strings.h>
35ea8dc4beschrock#include <sys/file.h>
36ea8dc4beschrock#include <sys/mntent.h>
37ea8dc4beschrock#include <sys/mnttab.h>
38ea8dc4beschrock#include <sys/param.h>
39ea8dc4beschrock#include <sys/stat.h>
40ea8dc4beschrock
41ea8dc4beschrock#include <sys/dmu.h>
42ea8dc4beschrock#include <sys/dmu_objset.h>
43ea8dc4beschrock#include <sys/dnode.h>
4421bf64agw#include <sys/vdev_impl.h>
45ea8dc4beschrock
46ea8dc4beschrock#include <sys/mkdev.h>
47ea8dc4beschrock
48ea8dc4beschrock#include "zinject.h"
49ea8dc4beschrock
50ea8dc4beschrockstatic int debug;
51ea8dc4beschrock
52ea8dc4beschrockstatic void
53ea8dc4beschrockziprintf(const char *fmt, ...)
54ea8dc4beschrock{
55ea8dc4beschrock	va_list ap;
56ea8dc4beschrock
57ea8dc4beschrock	if (!debug)
58ea8dc4beschrock		return;
59ea8dc4beschrock
60ea8dc4beschrock	va_start(ap, fmt);
61ea8dc4beschrock	(void) vprintf(fmt, ap);
62ea8dc4beschrock	va_end(ap);
63ea8dc4beschrock}
64ea8dc4beschrock
65f80ce22Chris Kirbystatic void
66f80ce22Chris Kirbycompress_slashes(const char *src, char *dest)
67f80ce22Chris Kirby{
68f80ce22Chris Kirby	while (*src != '\0') {
69f80ce22Chris Kirby		*dest = *src++;
70f80ce22Chris Kirby		while (*dest == '/' && *src == '/')
71f80ce22Chris Kirby			++src;
72f80ce22Chris Kirby		++dest;
73f80ce22Chris Kirby	}
74f80ce22Chris Kirby	*dest = '\0';
75f80ce22Chris Kirby}
76f80ce22Chris Kirby
77ea8dc4beschrock/*
78ea8dc4beschrock * Given a full path to a file, translate into a dataset name and a relative
79ea8dc4beschrock * path within the dataset.  'dataset' must be at least MAXNAMELEN characters,
80ea8dc4beschrock * and 'relpath' must be at least MAXPATHLEN characters.  We also pass a stat64
81ea8dc4beschrock * buffer, which we need later to get the object ID.
82ea8dc4beschrock */
83ea8dc4beschrockstatic int
84f80ce22Chris Kirbyparse_pathname(const char *inpath, char *dataset, char *relpath,
85ea8dc4beschrock    struct stat64 *statbuf)
86ea8dc4beschrock{
87ea8dc4beschrock	struct extmnttab mp;
88ea8dc4beschrock	FILE *fp;
89ea8dc4beschrock	int match;
90ea8dc4beschrock	const char *rel;
91f80ce22Chris Kirby	char fullpath[MAXPATHLEN];
92f80ce22Chris Kirby
93f80ce22Chris Kirby	compress_slashes(inpath, fullpath);
94ea8dc4beschrock
95ea8dc4beschrock	if (fullpath[0] != '/') {
96ea8dc4beschrock		(void) fprintf(stderr, "invalid object '%s': must be full "
97ea8dc4beschrock		    "path\n", fullpath);
98ea8dc4beschrock		usage();
99ea8dc4beschrock		return (-1);
100ea8dc4beschrock	}
101ea8dc4beschrock
102ea8dc4beschrock	if (strlen(fullpath) >= MAXPATHLEN) {
103ea8dc4beschrock		(void) fprintf(stderr, "invalid object; pathname too long\n");
104ea8dc4beschrock		return (-1);
105ea8dc4beschrock	}
106ea8dc4beschrock
107ea8dc4beschrock	if (stat64(fullpath, statbuf) != 0) {
108ea8dc4beschrock		(void) fprintf(stderr, "cannot open '%s': %s\n",
109ea8dc4beschrock		    fullpath, strerror(errno));
110ea8dc4beschrock		return (-1);
111ea8dc4beschrock	}
112ea8dc4beschrock
113ea8dc4beschrock	if ((fp = fopen(MNTTAB, "r")) == NULL) {
114ea8dc4beschrock		(void) fprintf(stderr, "cannot open /etc/mnttab\n");
115ea8dc4beschrock		return (-1);
116ea8dc4beschrock	}
117ea8dc4beschrock
118ea8dc4beschrock	match = 0;
119ea8dc4beschrock	while (getextmntent(fp, &mp, sizeof (mp)) == 0) {
120ea8dc4beschrock		if (makedev(mp.mnt_major, mp.mnt_minor) == statbuf->st_dev) {
121ea8dc4beschrock			match = 1;
122ea8dc4beschrock			break;
123ea8dc4beschrock		}
124ea8dc4beschrock	}
125ea8dc4beschrock
126ea8dc4beschrock	if (!match) {
127ea8dc4beschrock		(void) fprintf(stderr, "cannot find mountpoint for '%s'\n",
128ea8dc4beschrock		    fullpath);
129ea8dc4beschrock		return (-1);
130ea8dc4beschrock	}
131ea8dc4beschrock
132ea8dc4beschrock	if (strcmp(mp.mnt_fstype, MNTTYPE_ZFS) != 0) {
133ea8dc4beschrock		(void) fprintf(stderr, "invalid path '%s': not a ZFS "
134ea8dc4beschrock		    "filesystem\n", fullpath);
135ea8dc4beschrock		return (-1);
136ea8dc4beschrock	}
137ea8dc4beschrock
138ea8dc4beschrock	if (strncmp(fullpath, mp.mnt_mountp, strlen(mp.mnt_mountp)) != 0) {
139ea8dc4beschrock		(void) fprintf(stderr, "invalid path '%s': mountpoint "
140ea8dc4beschrock		    "doesn't match path\n", fullpath);
141ea8dc4beschrock		return (-1);
142ea8dc4beschrock	}
143ea8dc4beschrock
144ea8dc4beschrock	(void) strcpy(dataset, mp.mnt_special);
145ea8dc4beschrock
146ea8dc4beschrock	rel = fullpath + strlen(mp.mnt_mountp);
147ea8dc4beschrock	if (rel[0] == '/')
148ea8dc4beschrock		rel++;
149ea8dc4beschrock	(void) strcpy(relpath, rel);
150ea8dc4beschrock
151ea8dc4beschrock	return (0);
152ea8dc4beschrock}
153ea8dc4beschrock
154ea8dc4beschrock/*
155d8ab6e1Don Brady * Convert from a dataset to a objset id. Note that
156d8ab6e1Don Brady * we grab the object number from the inode number.
157ea8dc4beschrock */
158ea8dc4beschrockstatic int
159d8ab6e1Don Bradyobject_from_path(const char *dataset, uint64_t object, zinject_record_t *record)
160ea8dc4beschrock{
161d8ab6e1Don Brady	zfs_handle_t *zhp;
162ea8dc4beschrock
163d8ab6e1Don Brady	if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)
164ea8dc4beschrock		return (-1);
165ea8dc4beschrock
166d8ab6e1Don Brady	record->zi_objset = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
167d8ab6e1Don Brady	record->zi_object = object;
168ea8dc4beschrock
169d8ab6e1Don Brady	zfs_close(zhp);
170ea8dc4beschrock
171ea8dc4beschrock	return (0);
172ea8dc4beschrock}
173ea8dc4beschrock
174ea8dc4beschrock/*
175d8ab6e1Don Brady * Initialize the range based on the type, level, and range given.
176ea8dc4beschrock */
177ea8dc4beschrockstatic int
178d8ab6e1Don Bradyinitialize_range(err_type_t type, int level, char *range,
179ea8dc4beschrock    zinject_record_t *record)
180ea8dc4beschrock{
181ea8dc4beschrock	/*
182ea8dc4beschrock	 * Determine the numeric range from the string.
183ea8dc4beschrock	 */
184ea8dc4beschrock	if (range == NULL) {
185ea8dc4beschrock		/*
186ea8dc4beschrock		 * If range is unspecified, set the range to [0,-1], which
187ea8dc4beschrock		 * indicates that the whole object should be treated as an
188ea8dc4beschrock		 * error.
189ea8dc4beschrock		 */
190ea8dc4beschrock		record->zi_start = 0;
191ea8dc4beschrock		record->zi_end = -1ULL;
192ea8dc4beschrock	} else {
193ea8dc4beschrock		char *end;
194ea8dc4beschrock
195ea8dc4beschrock		/* XXX add support for suffixes */
196ea8dc4beschrock		record->zi_start = strtoull(range, &end, 10);
197ea8dc4beschrock
198ea8dc4beschrock
199ea8dc4beschrock		if (*end == '\0')
200ea8dc4beschrock			record->zi_end = record->zi_start + 1;
201ea8dc4beschrock		else if (*end == ',')
202ea8dc4beschrock			record->zi_end = strtoull(end + 1, &end, 10);
203ea8dc4beschrock
204ea8dc4beschrock		if (*end != '\0') {
205