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