nrelease - fix/improve livecd
[dragonfly.git] / sbin / hammer2 / cmd_info.c
1 /*
2  * Copyright (c) 2015 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #include "hammer2.h"
35
36 typedef void (*cmd_callback)(const void *, hammer2_blockref_t *, int);
37
38 static void h2disk_check(const char *devpath, cmd_callback callback1);
39 static void h2pfs_check(int fd, hammer2_blockref_t *bref,
40                     cmd_callback callback2);
41
42 static void info_callback1(const void *, hammer2_blockref_t *, int);
43 static void info_callback2(const void *, hammer2_blockref_t *, int);
44
45 static
46 void
47 h2disk_check_serno(cmd_callback fn)
48 {
49         DIR *dir;
50
51         if ((dir = opendir("/dev/serno")) != NULL) {
52                 struct dirent *den;
53
54                 while ((den = readdir(dir)) != NULL) {
55                         const char *ptr;
56                         int slice;
57                         char part;
58                         char *devpath;
59
60                         if (!strcmp(den->d_name, ".") ||
61                             !strcmp(den->d_name, ".."))
62                                 continue;
63                         ptr = strrchr(den->d_name, '.');
64                         if (ptr && sscanf(ptr, ".s%d%c", &slice, &part) == 2) {
65                                 asprintf(&devpath, "/dev/serno/%s",
66                                          den->d_name);
67                                 h2disk_check(devpath, fn);
68                                 free(devpath);
69                         }
70                 }
71                 closedir(dir);
72         }
73 }
74
75 static
76 void
77 h2disk_check_dm(cmd_callback fn)
78 {
79         DIR *dir;
80
81         if ((dir = opendir("/dev/mapper")) != NULL) {
82                 struct dirent *den;
83
84                 while ((den = readdir(dir)) != NULL) {
85                         char *devpath;
86
87                         if (!strcmp(den->d_name, ".") ||
88                             !strcmp(den->d_name, "..") ||
89                             !strcmp(den->d_name, "control"))
90                                 continue;
91                         asprintf(&devpath, "/dev/mapper/%s",
92                                  den->d_name);
93                         h2disk_check(devpath, fn);
94                         free(devpath);
95                 }
96                 closedir(dir);
97         }
98 }
99
100 static
101 void
102 h2disk_check_misc(cmd_callback fn)
103 {
104         DIR *dir;
105
106         if ((dir = opendir("/dev")) != NULL) {
107                 struct dirent *den;
108
109                 while ((den = readdir(dir)) != NULL) {
110                         char *devpath;
111
112                         if (!strcmp(den->d_name, ".") ||
113                             !strcmp(den->d_name, ".."))
114                                 continue;
115                         if (strncmp(den->d_name, "ad", 2) &&
116                             strncmp(den->d_name, "vn", 2))
117                                 continue;
118                         if (!strcmp(den->d_name, "vn"))
119                                 continue;
120                         if (!strncmp(den->d_name, "ad", 2) &&
121                             den->d_namlen <= 3)
122                                 continue;
123                         asprintf(&devpath, "/dev/%s", den->d_name);
124                         h2disk_check(devpath, fn);
125                         free(devpath);
126                 }
127                 closedir(dir);
128         }
129 }
130
131 int
132 cmd_info(int ac, const char **av)
133 {
134         int i;
135
136         for (i = 0; i < ac; ++i)
137                 h2disk_check(av[i], info_callback1);
138         if (ac == 0) {
139                 h2disk_check_serno(info_callback1);
140                 h2disk_check_dm(info_callback1);
141                 h2disk_check_misc(info_callback1);
142         }
143         return 0;
144 }
145
146 struct pfs_entry {
147         TAILQ_ENTRY(pfs_entry) entry;
148         char name[NAME_MAX+1];
149         char s[NAME_MAX+1];
150 };
151
152 static TAILQ_HEAD(, pfs_entry) head;
153
154 static
155 void
156 info_callback1(const void *path, hammer2_blockref_t *bref, int fd)
157 {
158         struct pfs_entry *p;
159
160         printf("%s:\n", (const char*)path);
161
162         TAILQ_INIT(&head);
163         h2pfs_check(fd, bref, info_callback2);
164
165         printf("    Type        "
166                "ClusterId (pfs_clid)                 "
167                "Label\n");
168         while ((p = TAILQ_FIRST(&head)) != NULL) {
169                 printf("    %s %s\n", p->s, p->name);
170                 TAILQ_REMOVE(&head, p, entry);
171                 free(p);
172         }
173 }
174
175 static
176 void
177 info_callback2(const void *data,
178                hammer2_blockref_t *bref __unused, int fd __unused)
179 {
180         const hammer2_inode_data_t *ipdata = data;
181         const hammer2_inode_meta_t *meta = &ipdata->meta;
182         char *pfs_id_str = NULL;
183         const char *type_str;
184         struct pfs_entry *p, *e;
185         uuid_t uuid;
186
187         uuid = meta->pfs_clid;
188         hammer2_uuid_to_str(&uuid, &pfs_id_str);
189         if (meta->pfs_type == HAMMER2_PFSTYPE_MASTER) {
190                 if (meta->pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
191                         type_str = "MASTER";
192                 else
193                         type_str = hammer2_pfssubtype_to_str(meta->pfs_subtype);
194         } else {
195                 type_str = hammer2_pfstype_to_str(meta->pfs_type);
196         }
197         e = calloc(1, sizeof(*e));
198         snprintf(e->name, sizeof(e->name), "%s", ipdata->filename);
199         snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str);
200         free(pfs_id_str);
201
202         p = TAILQ_FIRST(&head);
203         while (p) {
204                 if (strcmp(e->name, p->name) <= 0) {
205                         TAILQ_INSERT_BEFORE(p, e, entry);
206                         break;
207                 }
208                 p = TAILQ_NEXT(p, entry);
209         }
210         if (!p)
211                 TAILQ_INSERT_TAIL(&head, e, entry);
212 }
213
214 static void mount_callback1(const void *, hammer2_blockref_t *, int);
215 static void mount_callback2(const void *, hammer2_blockref_t *, int);
216 static void cmd_mountall_alarm(int signo);
217
218 static volatile sig_atomic_t DidAlarm;
219
220 int
221 cmd_mountall(int ac, const char **av)
222 {
223         int i;
224         pid_t pid;
225
226         for (i = 0; i < ac; ++i)
227                 h2disk_check(av[i], mount_callback1);
228         if (ac == 0) {
229                 h2disk_check_serno(mount_callback1);
230                 h2disk_check_dm(mount_callback1);
231                 h2disk_check_misc(mount_callback1);
232         }
233         signal(SIGALRM, cmd_mountall_alarm);
234         for (;;) {
235                 alarm(15);
236                 pid = wait3(NULL, 0, NULL);
237                 if (pid < 0 && errno == ECHILD)
238                         break;
239                 if (pid < 0 && DidAlarm) {
240                         printf("Timeout waiting for mounts to complete\n");
241                         break;
242                 }
243         }
244         alarm(0);
245
246         return 0;
247 }
248
249 static
250 void
251 cmd_mountall_alarm(int signo __unused)
252 {
253         DidAlarm = 1;
254 }
255
256 static const char *mount_path;
257 static const char *mount_comp;
258
259 static
260 void
261 mount_callback1(const void *devpath, hammer2_blockref_t *bref, int fd)
262 {
263         mount_path = devpath;
264         mount_comp = strrchr(devpath, '/');
265         if (mount_comp) {
266                 ++mount_comp;
267                 h2pfs_check(fd, bref, mount_callback2);
268         }
269 }
270
271 static
272 void
273 mount_callback2(const void *data,
274                 hammer2_blockref_t *bref __unused, int fd)
275 {
276         const hammer2_inode_data_t *ipdata = data;
277         char *tmp_path;
278         char *label;
279         int tfd;
280
281         if (strcmp(ipdata->filename, "LOCAL") == 0) {
282                 if ((tfd = open("/dev/null", O_RDONLY)) >= 0) {
283                         dup2(tfd, fd);
284                         close(tfd);
285                 } else {
286                         perror("open(/dev/null)");
287                         exit(1);
288                 }
289                 asprintf(&tmp_path, "/var/hammer2/LOCAL.%s", mount_comp);
290                 asprintf(&label, "%s@LOCAL", mount_path);
291                 mkdir("/var/hammer2", 0700);
292                 mkdir(tmp_path, 0700);
293                 printf("mount %s\n", tmp_path);
294                 if (fork() == 0) {
295                         execl("/sbin/mount_hammer2",
296                               "mount",
297                               label,
298                               tmp_path,
299                               NULL);
300                 }
301                 free(label);
302                 free(tmp_path);
303         }
304 }
305
306 static
307 void
308 h2disk_check(const char *devpath, cmd_callback callback1)
309 {
310         hammer2_blockref_t broot;
311         hammer2_blockref_t best;
312         hammer2_media_data_t media;
313         hammer2_volume_data_t *voldata;
314         struct partinfo partinfo;
315         int fd;
316         int i;
317         int best_i;
318
319         fd = open(devpath, O_RDONLY);
320         if (fd < 0) {
321                 fprintf(stderr, "Unable to open \"%s\"\n", devpath);
322                 return;
323         }
324         if (ioctl(fd, DIOCGPART, &partinfo) == -1) {
325                 fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath);
326                 goto done;
327         }
328
329         /*
330          * Check partition or slice for HAMMER2 designation.  Validate the
331          * designation either from the fstype (typically set for disklabel
332          * partitions), or the fstype_uuid (typically set for direct-mapped
333          * hammer2 GPT slices).
334          */
335         if (partinfo.fstype != FS_HAMMER2) {
336                 uint32_t status;
337                 uuid_t h2uuid;
338                 int is_nil = uuid_is_nil(&partinfo.fstype_uuid, NULL);
339
340                 uuid_from_string(HAMMER2_UUID_STRING, &h2uuid, &status);
341                 if (!is_nil && (status != uuid_s_ok ||
342                     uuid_compare(&partinfo.fstype_uuid, &h2uuid, NULL) != 0)) {
343                         goto done;
344                 }
345         }
346
347         /*
348          * Find the best volume header.
349          */
350         best_i = -1;
351         bzero(&best, sizeof(best));
352         for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
353                 bzero(&broot, sizeof(broot));
354                 broot.type = HAMMER2_BREF_TYPE_VOLUME;
355                 broot.data_off = (i * HAMMER2_ZONE_BYTES64) |
356                                  HAMMER2_PBUFRADIX;
357                 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
358                 if (read(fd, &media, HAMMER2_PBUFSIZE) !=
359                     (ssize_t)HAMMER2_PBUFSIZE)
360                         continue;
361                 voldata = &media.voldata;
362                 if (voldata->magic != HAMMER2_VOLUME_ID_HBO)
363                         continue;
364                 /* XXX multiple volumes currently unsupported */
365                 if (voldata->nvolumes > 1)
366                         break;
367                 broot.mirror_tid = voldata->mirror_tid;
368                 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
369                         best_i = i;
370                         best = broot;
371                 }
372         }
373         if (best_i >= 0)
374                 callback1(devpath, &best, fd);
375 done:
376         close(fd);
377 }
378
379 static
380 void
381 h2pfs_check(int fd, hammer2_blockref_t *bref, cmd_callback callback2)
382 {
383         hammer2_media_data_t media;
384         hammer2_blockref_t *bscan;
385         int bcount;
386         int i;
387         size_t bytes;
388         size_t io_bytes;
389         size_t boff;
390         uint32_t cv;
391         uint64_t cv64;
392         hammer2_off_t io_off;
393         hammer2_off_t io_base;
394
395         bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
396         if (bytes)
397                 bytes = (size_t)1 << bytes;
398
399         io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
400         io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
401         io_bytes = bytes;
402         boff = io_off - io_base;
403
404         io_bytes = HAMMER2_LBUFSIZE;
405         while (io_bytes + boff < bytes)
406                 io_bytes <<= 1;
407
408         if (io_bytes > sizeof(media)) {
409                 printf("(bad block size %zu)\n", bytes);
410                 return;
411         }
412         if (bref->type != HAMMER2_BREF_TYPE_DATA) {
413                 lseek(fd, io_base, SEEK_SET);
414                 if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) {
415                         printf("(media read failed)\n");
416                         return;
417                 }
418                 if (boff)
419                         bcopy((char *)&media + boff, &media, bytes);
420         }
421
422         bscan = NULL;
423         bcount = 0;
424
425         /*
426          * Check data integrity in verbose mode, otherwise we are just doing
427          * a quick meta-data scan.  Meta-data integrity is always checked.
428          * (Also see the check above that ensures the media data is loaded,
429          * otherwise there's no data to check!).
430          */
431         if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
432                 switch(HAMMER2_DEC_CHECK(bref->methods)) {
433                 case HAMMER2_CHECK_NONE:
434                         break;
435                 case HAMMER2_CHECK_DISABLED:
436                         break;
437                 case HAMMER2_CHECK_ISCSI32:
438                         cv = hammer2_icrc32(&media, bytes);
439                         if (bref->check.iscsi32.value != cv) {
440                                 printf("\t(icrc failed %02x:%08x/%08x)\n",
441                                        bref->methods,
442                                        bref->check.iscsi32.value,
443                                        cv);
444                         }
445                         break;
446                 case HAMMER2_CHECK_XXHASH64:
447                         cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
448                         if (bref->check.xxhash64.value != cv64) {
449                                 printf("\t(xxhash failed %02x:%016jx/%016jx)\n",
450                                        bref->methods,
451                                        bref->check.xxhash64.value,
452                                        cv64);
453                         }
454                         break;
455                 case HAMMER2_CHECK_SHA192:
456                         break;
457                 case HAMMER2_CHECK_FREEMAP:
458                         cv = hammer2_icrc32(&media, bytes);
459                         if (bref->check.freemap.icrc32 != cv) {
460                                 printf("\t(fcrc %02x:%08x/%08x)\n",
461                                         bref->methods,
462                                         bref->check.freemap.icrc32,
463                                         cv);
464                         }
465                         break;
466                 }
467         }
468
469         switch(bref->type) {
470         case HAMMER2_BREF_TYPE_INODE:
471                 if (media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
472                         if ((media.ipdata.meta.op_flags &
473                              HAMMER2_OPFLAG_DIRECTDATA) == 0) {
474                                 bscan = &media.ipdata.u.blockset.blockref[0];
475                                 bcount = HAMMER2_SET_COUNT;
476                         }
477                 } else if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
478                         callback2(&media.ipdata, bref, fd);
479                         bscan = NULL;
480                         bcount = 0;
481                 } else {
482                         bscan = NULL;
483                         bcount = 0;
484                 }
485                 break;
486         case HAMMER2_BREF_TYPE_INDIRECT:
487                 bscan = &media.npdata[0];
488                 bcount = bytes / sizeof(hammer2_blockref_t);
489                 break;
490         case HAMMER2_BREF_TYPE_VOLUME:
491                 bscan = &media.voldata.sroot_blockset.blockref[0];
492                 bcount = HAMMER2_SET_COUNT;
493                 break;
494         default:
495                 break;
496         }
497         for (i = 0; i < bcount; ++i) {
498                 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
499                         h2pfs_check(fd, &bscan[i], callback2);
500         }
501 }