]> granicus.if.org Git - zfs/blob - cmd/zinject/translate.c
Fix for ARC sysctls ignored at runtime
[zfs] / cmd / zinject / translate.c
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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 2012 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 <strings.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) strcpy(dataset, mp.mnt_special);
119
120         rel = fullpath + strlen(mp.mnt_mountp);
121         if (rel[0] == '/')
122                 rel++;
123         (void) strcpy(relpath, rel);
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         case TYPE_DATA:
190                 break;
191
192         case TYPE_DNODE:
193                 /*
194                  * If this is a request to inject faults into the dnode, then we
195                  * must translate the current (objset,object) pair into an
196                  * offset within the metadnode for the objset.  Specifying any
197                  * kind of range with type 'dnode' is illegal.
198                  */
199                 if (range != NULL) {
200                         (void) fprintf(stderr, "range cannot be specified when "
201                             "type is 'dnode'\n");
202                         return (-1);
203                 }
204
205                 record->zi_start = record->zi_object * sizeof (dnode_phys_t);
206                 record->zi_end = record->zi_start + sizeof (dnode_phys_t);
207                 record->zi_object = 0;
208                 break;
209         }
210
211         record->zi_level = level;
212
213         return (0);
214 }
215
216 int
217 translate_record(err_type_t type, const char *object, const char *range,
218     int level, zinject_record_t *record, char *poolname, char *dataset)
219 {
220         char path[MAXPATHLEN];
221         char *slash;
222         struct stat64 statbuf;
223         int ret = -1;
224
225         debug = (getenv("ZINJECT_DEBUG") != NULL);
226
227         ziprintf("translating: %s\n", object);
228
229         if (MOS_TYPE(type)) {
230                 /*
231                  * MOS objects are treated specially.
232                  */
233                 switch (type) {
234                 default:
235                         break;
236                 case TYPE_MOS:
237                         record->zi_type = 0;
238                         break;
239                 case TYPE_MOSDIR:
240                         record->zi_type = DMU_OT_OBJECT_DIRECTORY;
241                         break;
242                 case TYPE_METASLAB:
243                         record->zi_type = DMU_OT_OBJECT_ARRAY;
244                         break;
245                 case TYPE_CONFIG:
246                         record->zi_type = DMU_OT_PACKED_NVLIST;
247                         break;
248                 case TYPE_BPOBJ:
249                         record->zi_type = DMU_OT_BPOBJ;
250                         break;
251                 case TYPE_SPACEMAP:
252                         record->zi_type = DMU_OT_SPACE_MAP;
253                         break;
254                 case TYPE_ERRLOG:
255                         record->zi_type = DMU_OT_ERROR_LOG;
256                         break;
257                 }
258
259                 dataset[0] = '\0';
260                 (void) strcpy(poolname, object);
261                 return (0);
262         }
263
264         /*
265          * Convert a full path into a (dataset, file) pair.
266          */
267         if (parse_pathname(object, dataset, path, &statbuf) != 0)
268                 goto err;
269
270         ziprintf("   dataset: %s\n", dataset);
271         ziprintf("      path: %s\n", path);
272
273         /*
274          * Convert (dataset, file) into (objset, object)
275          */
276         if (object_from_path(dataset, statbuf.st_ino, record) != 0)
277                 goto err;
278
279         ziprintf("raw objset: %llu\n", record->zi_objset);
280         ziprintf("raw object: %llu\n", record->zi_object);
281
282         /*
283          * For the given object, initialize the range in bytes
284          */
285         if (initialize_range(type, level, (char *)range, record) != 0)
286                 goto err;
287
288         ziprintf("    objset: %llu\n", record->zi_objset);
289         ziprintf("    object: %llu\n", record->zi_object);
290         if (record->zi_start == 0 &&
291             record->zi_end == -1ULL)
292                 ziprintf("     range: all\n");
293         else
294                 ziprintf("     range: [%llu, %llu]\n", record->zi_start,
295                     record->zi_end);
296
297         /*
298          * Copy the pool name
299          */
300         (void) strcpy(poolname, dataset);
301         if ((slash = strchr(poolname, '/')) != NULL)
302                 *slash = '\0';
303
304         ret = 0;
305
306 err:
307         return (ret);
308 }
309
310 int
311 translate_raw(const char *str, zinject_record_t *record)
312 {
313         /*
314          * A raw bookmark of the form objset:object:level:blkid, where each
315          * number is a hexadecimal value.
316          */
317         if (sscanf(str, "%llx:%llx:%x:%llx", (u_longlong_t *)&record->zi_objset,
318             (u_longlong_t *)&record->zi_object, &record->zi_level,
319             (u_longlong_t *)&record->zi_start) != 4) {
320                 (void) fprintf(stderr, "bad raw spec '%s': must be of the form "
321                     "'objset:object:level:blkid'\n", str);
322                 return (-1);
323         }
324
325         record->zi_end = record->zi_start;
326
327         return (0);
328 }
329
330 int
331 translate_device(const char *pool, const char *device, err_type_t label_type,
332     zinject_record_t *record)
333 {
334         char *end;
335         zpool_handle_t *zhp;
336         nvlist_t *tgt;
337         boolean_t isspare, iscache;
338
339         /*
340          * Given a device name or GUID, create an appropriate injection record
341          * with zi_guid set.
342          */
343         if ((zhp = zpool_open(g_zfs, pool)) == NULL)
344                 return (-1);
345
346         record->zi_guid = strtoull(device, &end, 0);
347         if (record->zi_guid == 0 || *end != '\0') {
348                 tgt = zpool_find_vdev(zhp, device, &isspare, &iscache, NULL);
349
350                 if (tgt == NULL) {
351                         (void) fprintf(stderr, "cannot find device '%s' in "
352                             "pool '%s'\n", device, pool);
353                         zpool_close(zhp);
354                         return (-1);
355                 }
356
357                 verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID,
358                     &record->zi_guid) == 0);
359         }
360
361         /*
362          * Device faults can take on three different forms:
363          * 1). delayed or hanging I/O
364          * 2). zfs label faults
365          * 3). generic disk faults
366          */
367         if (record->zi_timer != 0) {
368                 record->zi_cmd = ZINJECT_DELAY_IO;
369         } else if (label_type != TYPE_INVAL) {
370                 record->zi_cmd = ZINJECT_LABEL_FAULT;
371         } else {
372                 record->zi_cmd = ZINJECT_DEVICE_FAULT;
373         }
374
375         switch (label_type) {
376         default:
377                 break;
378         case TYPE_LABEL_UBERBLOCK:
379                 record->zi_start = offsetof(vdev_label_t, vl_uberblock[0]);
380                 record->zi_end = record->zi_start + VDEV_UBERBLOCK_RING - 1;
381                 break;
382         case TYPE_LABEL_NVLIST:
383                 record->zi_start = offsetof(vdev_label_t, vl_vdev_phys);
384                 record->zi_end = record->zi_start + VDEV_PHYS_SIZE - 1;
385                 break;
386         case TYPE_LABEL_PAD1:
387                 record->zi_start = offsetof(vdev_label_t, vl_pad1);
388                 record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;
389                 break;
390         case TYPE_LABEL_PAD2:
391                 record->zi_start = offsetof(vdev_label_t, vl_pad2);
392                 record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;
393                 break;
394         }
395         zpool_close(zhp);
396         return (0);
397 }