Merge branch 'vendor/GCC50'
[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 static void h2disk_check(const char *devpath,
37                     void (*callback1)(const char *, hammer2_blockref_t *, int));
38 static void h2pfs_check(int fd, hammer2_blockref_t *bref,
39                     void (*callback2)(const char *, hammer2_blockref_t *, int));
40
41 static void info_callback1(const char *, hammer2_blockref_t *, int);
42 static void info_callback2(const char *, hammer2_blockref_t *, int);
43
44 int
45 cmd_info(int ac, const char **av)
46 {
47         struct dirent *den;
48         char *devpath;
49         DIR *dir;
50         int i;
51
52         for (i = 0; i < ac; ++i)
53                 h2disk_check(av[i], info_callback1);
54         if (ac == 0 && (dir = opendir("/dev/serno")) != NULL) {
55                 while ((den = readdir(dir)) != NULL) {
56                         const char *ptr;
57                         int slice;
58                         char part;
59
60                         ptr = strrchr(den->d_name, '.');
61                         if (ptr && sscanf(ptr, ".s%d%c", &slice, &part) == 2) {
62                                 asprintf(&devpath, "/dev/serno/%s",
63                                          den->d_name);
64                                 h2disk_check(devpath, info_callback1);
65                                 free(devpath);
66                         }
67                 }
68                 closedir(dir);
69         }
70         return 0;
71 }
72
73 static
74 void
75 info_callback1(const char *path, hammer2_blockref_t *bref, int fd)
76 {
77         printf("%s:\n", path);
78         h2pfs_check(fd, bref, info_callback2);
79 }
80
81 static
82 void
83 info_callback2(const char *pfsname,
84                hammer2_blockref_t *bref __unused, int fd __unused)
85 {
86         printf("    %s\n", pfsname);
87 }
88
89 static void mount_callback1(const char *, hammer2_blockref_t *, int);
90 static void mount_callback2(const char *, hammer2_blockref_t *, int);
91 static void cmd_mountall_alarm(int signo);
92
93 static volatile sig_atomic_t DidAlarm;
94
95 int
96 cmd_mountall(int ac, const char **av)
97 {
98         struct dirent *den;
99         char *devpath;
100         DIR *dir;
101         int i;
102         pid_t pid;
103
104         for (i = 0; i < ac; ++i)
105                 h2disk_check(av[i], mount_callback1);
106         if (ac == 0 && (dir = opendir("/dev/serno")) != NULL) {
107                 while ((den = readdir(dir)) != NULL) {
108                         const char *ptr;
109                         int slice;
110                         char part;
111
112                         ptr = strrchr(den->d_name, '.');
113                         if (ptr && sscanf(ptr, ".s%d%c", &slice, &part) == 2) {
114                                 asprintf(&devpath, "/dev/serno/%s",
115                                          den->d_name);
116                                 h2disk_check(devpath, mount_callback1);
117                                 free(devpath);
118                         }
119                 }
120                 closedir(dir);
121         }
122         signal(SIGALRM, cmd_mountall_alarm);
123         for (;;) {
124                 alarm(15);
125                 pid = wait3(NULL, 0, NULL);
126                 if (pid < 0 && errno == ECHILD)
127                         break;
128                 if (pid < 0 && DidAlarm) {
129                         printf("Timeout waiting for mounts to complete\n");
130                         break;
131                 }
132         }
133         alarm(0);
134
135         return 0;
136 }
137
138 static
139 void
140 cmd_mountall_alarm(int signo __unused)
141 {
142         DidAlarm = 1;
143 }
144
145 static const char *mount_path;
146 static const char *mount_comp;
147
148 static
149 void
150 mount_callback1(const char *devpath, hammer2_blockref_t *bref, int fd)
151 {
152         mount_path = devpath;
153         mount_comp = strrchr(devpath, '/');
154         if (mount_comp) {
155                 ++mount_comp;
156                 h2pfs_check(fd, bref, mount_callback2);
157         }
158 }
159
160 static
161 void
162 mount_callback2(const char *pfsname,
163                 hammer2_blockref_t *bref __unused, int fd)
164 {
165         char *tmp_path;
166         char *label;
167         int tfd;
168
169         if (strcmp(pfsname, "LOCAL") == 0) {
170                 if ((tfd = open("/dev/null", O_RDONLY)) >= 0) {
171                         dup2(tfd, fd);
172                         close(tfd);
173                 } else {
174                         perror("open(/dev/null)");
175                         exit(1);
176                 }
177                 asprintf(&tmp_path, "/var/hammer2/LOCAL.%s", mount_comp);
178                 asprintf(&label, "%s@LOCAL", mount_path);
179                 mkdir("/var/hammer2", 0700);
180                 mkdir(tmp_path, 0700);
181                 printf("mount %s\n", tmp_path);
182                 if (fork() == 0) {
183                         execl("/sbin/mount_hammer2",
184                               "mount",
185                               label,
186                               tmp_path,
187                               NULL);
188                 }
189                 free(label);
190                 free(tmp_path);
191         }
192 }
193
194 /*
195  * Support
196  */
197 static
198 void
199 h2disk_check(const char *devpath,
200              void (*callback1)(const char *, hammer2_blockref_t *, int))
201 {
202         hammer2_blockref_t broot;
203         hammer2_blockref_t best;
204         hammer2_media_data_t media;
205         struct partinfo partinfo;
206         int fd;
207         int i;
208         int best_i;
209
210         fd = open(devpath, O_RDONLY);
211         if (fd < 0) {
212                 fprintf(stderr, "Unable to open \"%s\"\n", devpath);
213                 return;
214         }
215         if (ioctl(fd, DIOCGPART, &partinfo) == -1) {
216                 fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath);
217                 goto done;
218         }
219         if (partinfo.fstype != FS_HAMMER2)
220                 goto done;
221
222         /*
223          * Find the best volume header.
224          */
225         best_i = -1;
226         bzero(&best, sizeof(best));
227         for (i = 0; i < 4; ++i) {
228                 bzero(&broot, sizeof(broot));
229                 broot.type = HAMMER2_BREF_TYPE_VOLUME;
230                 broot.data_off = (i * HAMMER2_ZONE_BYTES64) |
231                                  HAMMER2_PBUFRADIX;
232                 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 0);
233                 if (read(fd, &media, HAMMER2_PBUFSIZE) ==
234                     (ssize_t)HAMMER2_PBUFSIZE &&
235                     media.voldata.magic == HAMMER2_VOLUME_ID_HBO) {
236                         broot.mirror_tid = media.voldata.mirror_tid;
237                         if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
238                                 best_i = i;
239                                 best = broot;
240                         }
241                 }
242         }
243         if (best_i >= 0)
244                 callback1(devpath, &best, fd);
245 done:
246         close(fd);
247 }
248
249 static
250 void
251 h2pfs_check(int fd, hammer2_blockref_t *bref,
252             void (*callback2)(const char *, hammer2_blockref_t *, int))
253 {
254         hammer2_media_data_t media;
255         hammer2_blockref_t *bscan;
256         int bcount;
257         int i;
258         size_t bytes;
259         uint32_t cv;
260         uint64_t cv64;
261
262         bytes = (size_t)1 << (bref->data_off & HAMMER2_OFF_MASK_RADIX);
263
264         {
265                 hammer2_off_t io_off;
266                 hammer2_off_t io_base;
267                 size_t io_bytes;
268                 size_t boff;
269
270                 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
271                 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
272                 io_bytes = bytes;
273                 boff = io_off - io_base;
274
275                 io_bytes = HAMMER2_MINIOSIZE;
276                 while (io_bytes + boff < bytes)
277                         io_bytes <<= 1;
278
279                 if (io_bytes > sizeof(media)) {
280                         printf("(bad block size %zd)\n", bytes);
281                         return;
282                 }
283                 if (bref->type != HAMMER2_BREF_TYPE_DATA) {
284                         lseek(fd, io_base, 0);
285                         if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) {
286                                 printf("(media read failed)\n");
287                                 return;
288                         }
289                         if (boff)
290                                 bcopy((char *)&media + boff, &media, bytes);
291                 }
292         }
293
294         bscan = NULL;
295         bcount = 0;
296
297         /*
298          * Check data integrity in verbose mode, otherwise we are just doing
299          * a quick meta-data scan.  Meta-data integrity is always checked.
300          * (Also see the check above that ensures the media data is loaded,
301          * otherwise there's no data to check!).
302          */
303         if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
304                 switch(HAMMER2_DEC_CHECK(bref->methods)) {
305                 case HAMMER2_CHECK_NONE:
306                         break;
307                 case HAMMER2_CHECK_DISABLED:
308                         break;
309                 case HAMMER2_CHECK_ISCSI32:
310                         cv = hammer2_icrc32(&media, bytes);
311                         if (bref->check.iscsi32.value != cv) {
312                                 printf("\t(icrc failed %02x:%08x/%08x)\n",
313                                        bref->methods,
314                                        bref->check.iscsi32.value,
315                                        cv);
316                         }
317                         break;
318                 case HAMMER2_CHECK_XXHASH64:
319                         cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
320                         if (bref->check.xxhash64.value != cv64) {
321                                 printf("\t(xxhash failed %02x:%016jx/%016jx)\n",
322                                        bref->methods,
323                                        bref->check.xxhash64.value,
324                                        cv64);
325                         }
326                         break;
327                 case HAMMER2_CHECK_SHA192:
328                         break;
329                 case HAMMER2_CHECK_FREEMAP:
330                         cv = hammer2_icrc32(&media, bytes);
331                         if (bref->check.freemap.icrc32 != cv) {
332                                 printf("\t(fcrc %02x:%08x/%08x)\n",
333                                         bref->methods,
334                                         bref->check.freemap.icrc32,
335                                         cv);
336                         }
337                         break;
338                 }
339         }
340
341         switch(bref->type) {
342         case HAMMER2_BREF_TYPE_EMPTY:
343                 break;
344         case HAMMER2_BREF_TYPE_INODE:
345                 if (media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
346                         if ((media.ipdata.meta.op_flags &
347                              HAMMER2_OPFLAG_DIRECTDATA) == 0) {
348                                 bscan = &media.ipdata.u.blockset.blockref[0];
349                                 bcount = HAMMER2_SET_COUNT;
350                         }
351                 } else
352                 if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
353                         callback2(media.ipdata.filename, bref, fd);
354                         bscan = NULL;
355                         bcount = 0;
356                 } else {
357                         bscan = NULL;
358                         bcount = 0;
359                 }
360                 break;
361         case HAMMER2_BREF_TYPE_INDIRECT:
362                 bscan = &media.npdata[0];
363                 bcount = bytes / sizeof(hammer2_blockref_t);
364                 break;
365         case HAMMER2_BREF_TYPE_DATA:
366                 break;
367         case HAMMER2_BREF_TYPE_VOLUME:
368                 bscan = &media.voldata.sroot_blockset.blockref[0];
369                 bcount = HAMMER2_SET_COUNT;
370                 break;
371         default:
372                 break;
373         }
374         for (i = 0; i < bcount; ++i) {
375                 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
376                         h2pfs_check(fd, &bscan[i], callback2);
377         }
378 }