Merge branch 'vendor/BMAKE'
[dragonfly.git] / sbin / hammer2 / subs.c
1 /*
2  * Copyright (c) 2011-2012 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  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 #include <sys/ioctl.h>
40 #include <sys/mount.h>
41 #include <sys/statvfs.h>
42 #include <sys/diskslice.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <errno.h>
49 #include <err.h>
50 #include <uuid.h>
51
52 #include <vfs/hammer2/hammer2_disk.h>
53 #include <vfs/hammer2/hammer2_ioctl.h>
54
55 #include "hammer2_subs.h"
56
57 /*
58  * Obtain a file descriptor that the caller can execute ioctl()'s on.
59  */
60 int
61 hammer2_ioctl_handle(const char *sel_path)
62 {
63         struct hammer2_ioc_version info;
64         int fd;
65
66         if (sel_path == NULL)
67                 sel_path = ".";
68
69         fd = open(sel_path, O_RDONLY, 0);
70         if (fd < 0) {
71                 fprintf(stderr, "hammer2: Unable to open %s: %s\n",
72                         sel_path, strerror(errno));
73                 return(-1);
74         }
75         if (ioctl(fd, HAMMER2IOC_VERSION_GET, &info) < 0) {
76                 fprintf(stderr, "hammer2: '%s' is not a hammer2 filesystem\n",
77                         sel_path);
78                 close(fd);
79                 return(-1);
80         }
81         return (fd);
82 }
83
84 const char *
85 hammer2_time64_to_str(uint64_t htime64, char **strp)
86 {
87         struct tm *tp;
88         time_t t;
89
90         if (*strp) {
91                 free(*strp);
92                 *strp = NULL;
93         }
94         *strp = malloc(64);
95         t = htime64 / 1000000;
96         tp = localtime(&t);
97         strftime(*strp, 64, "%d-%b-%Y %H:%M:%S", tp);
98         return (*strp);
99 }
100
101 const char *
102 hammer2_uuid_to_str(const uuid_t *uuid, char **strp)
103 {
104         uint32_t status;
105         if (*strp) {
106                 free(*strp);
107                 *strp = NULL;
108         }
109         uuid_to_string(uuid, strp, &status);
110         return (*strp);
111 }
112
113 const char *
114 hammer2_iptype_to_str(uint8_t type)
115 {
116         switch(type) {
117         case HAMMER2_OBJTYPE_UNKNOWN:
118                 return("UNKNOWN");
119         case HAMMER2_OBJTYPE_DIRECTORY:
120                 return("DIR");
121         case HAMMER2_OBJTYPE_REGFILE:
122                 return("FILE");
123         case HAMMER2_OBJTYPE_FIFO:
124                 return("FIFO");
125         case HAMMER2_OBJTYPE_CDEV:
126                 return("CDEV");
127         case HAMMER2_OBJTYPE_BDEV:
128                 return("BDEV");
129         case HAMMER2_OBJTYPE_SOFTLINK:
130                 return("SOFTLINK");
131         case HAMMER2_OBJTYPE_SOCKET:
132                 return("SOCKET");
133         case HAMMER2_OBJTYPE_WHITEOUT:
134                 return("WHITEOUT");
135         default:
136                 return("ILLEGAL");
137         }
138 }
139
140 const char *
141 hammer2_pfstype_to_str(uint8_t type)
142 {
143         switch(type) {
144         case HAMMER2_PFSTYPE_NONE:
145                 return("NONE");
146         case HAMMER2_PFSTYPE_SUPROOT:
147                 return("SUPROOT");
148         case HAMMER2_PFSTYPE_DUMMY:
149                 return("DUMMY");
150         case HAMMER2_PFSTYPE_CACHE:
151                 return("CACHE");
152         case HAMMER2_PFSTYPE_SLAVE:
153                 return("SLAVE");
154         case HAMMER2_PFSTYPE_SOFT_SLAVE:
155                 return("SOFT_SLAVE");
156         case HAMMER2_PFSTYPE_SOFT_MASTER:
157                 return("SOFT_MASTER");
158         case HAMMER2_PFSTYPE_MASTER:
159                 return("MASTER");
160         default:
161                 return("ILLEGAL");
162         }
163 }
164
165 const char *
166 hammer2_pfssubtype_to_str(uint8_t subtype)
167 {
168         switch(subtype) {
169         case HAMMER2_PFSSUBTYPE_NONE:
170                 return("NONE");
171         case HAMMER2_PFSSUBTYPE_SNAPSHOT:
172                 return("SNAPSHOT");
173         case HAMMER2_PFSSUBTYPE_AUTOSNAP:
174                 return("AUTOSNAP");
175         default:
176                 return("ILLEGAL");
177         }
178 }
179
180 const char *
181 hammer2_breftype_to_str(uint8_t type)
182 {
183         switch(type) {
184         case HAMMER2_BREF_TYPE_EMPTY:
185                 return("empty");
186         case HAMMER2_BREF_TYPE_INODE:
187                 return("inode");
188         case HAMMER2_BREF_TYPE_INDIRECT:
189                 return("indirect");
190         case HAMMER2_BREF_TYPE_DATA:
191                 return("data");
192         case HAMMER2_BREF_TYPE_DIRENT:
193                 return("dirent");
194         case HAMMER2_BREF_TYPE_FREEMAP_NODE:
195                 return("freemap_node");
196         case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
197                 return("freemap_leaf");
198         case HAMMER2_BREF_TYPE_INVALID:
199                 return("invalid");
200         case HAMMER2_BREF_TYPE_FREEMAP:
201                 return("freemap");
202         case HAMMER2_BREF_TYPE_VOLUME:
203                 return("volume");
204         default:
205                 return("unknown");
206         }
207 }
208
209 const char *
210 sizetostr(hammer2_off_t size)
211 {
212         static char buf[32];
213
214         if (size < 1024 / 2) {
215                 snprintf(buf, sizeof(buf), "%6.2fB", (double)size);
216         } else if (size < 1024 * 1024 / 2) {
217                 snprintf(buf, sizeof(buf), "%6.2fKB",
218                         (double)size / 1024);
219         } else if (size < 1024 * 1024 * 1024LL / 2) {
220                 snprintf(buf, sizeof(buf), "%6.2fMB",
221                         (double)size / (1024 * 1024));
222         } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
223                 snprintf(buf, sizeof(buf), "%6.2fGB",
224                         (double)size / (1024 * 1024 * 1024LL));
225         } else {
226                 snprintf(buf, sizeof(buf), "%6.2fTB",
227                         (double)size / (1024 * 1024 * 1024LL * 1024LL));
228         }
229         return(buf);
230 }
231
232 const char *
233 counttostr(hammer2_off_t size)
234 {
235         static char buf[32];
236
237         if (size < 1024 / 2) {
238                 snprintf(buf, sizeof(buf), "%jd",
239                          (intmax_t)size);
240         } else if (size < 1024 * 1024 / 2) {
241                 snprintf(buf, sizeof(buf), "%jd",
242                          (intmax_t)size);
243         } else if (size < 1024 * 1024 * 1024LL / 2) {
244                 snprintf(buf, sizeof(buf), "%6.2fM",
245                          (double)size / (1024 * 1024));
246         } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
247                 snprintf(buf, sizeof(buf), "%6.2fG",
248                          (double)(size / (1024 * 1024 * 1024LL)));
249         } else {
250                 snprintf(buf, sizeof(buf), "%6.2fT",
251                          (double)(size / (1024 * 1024 * 1024LL * 1024LL)));
252         }
253         return(buf);
254 }
255
256 hammer2_off_t
257 check_volume(int fd)
258 {
259         struct partinfo pinfo;
260         struct stat st;
261         hammer2_off_t size;
262
263         /*
264          * Get basic information about the volume
265          */
266         if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
267                 /*
268                  * Allow the formatting of regular files as HAMMER2 volumes
269                  */
270                 if (fstat(fd, &st) < 0)
271                         err(1, "Unable to stat fd %d", fd);
272                 if (!S_ISREG(st.st_mode))
273                         errx(1, "Unsupported file type for fd %d", fd);
274                 size = st.st_size;
275         } else {
276                 /*
277                  * When formatting a block device as a HAMMER2 volume the
278                  * sector size must be compatible.  HAMMER2 uses 64K
279                  * filesystem buffers but logical buffers for direct I/O
280                  * can be as small as HAMMER2_LOGSIZE (16KB).
281                  */
282                 if (pinfo.reserved_blocks) {
283                         errx(1, "HAMMER2 cannot be placed in a partition "
284                                 "which overlaps the disklabel or MBR");
285                 }
286                 if (pinfo.media_blksize > HAMMER2_PBUFSIZE ||
287                     HAMMER2_PBUFSIZE % pinfo.media_blksize) {
288                         errx(1, "A media sector size of %d is not supported",
289                              pinfo.media_blksize);
290                 }
291                 size = pinfo.media_size;
292         }
293         return(size);
294 }
295
296 /*
297  * Borrow HAMMER1's directory hash algorithm #1 with a few modifications.
298  * The filename is split into fields which are hashed separately and then
299  * added together.
300  *
301  * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets
302  * it to 0), this is because bit63=0 is used for hidden hardlinked inodes.
303  * (This means we do not need to do a 0-check/or-with-0x100000000 either).
304  *
305  * Also, the iscsi crc code is used instead of the old crc32 code.
306  */
307 hammer2_key_t
308 dirhash(const unsigned char *name, size_t len)
309 {
310         const unsigned char *aname = name;
311         uint32_t crcx;
312         uint64_t key;
313         size_t i;
314         size_t j;
315
316         /*
317          * Filesystem version 6 or better will create directories
318          * using the ALG1 dirhash.  This hash breaks the filename
319          * up into domains separated by special characters and
320          * hashes each domain independently.
321          *
322          * We also do a simple sub-sort using the first character
323          * of the filename in the top 5-bits.
324          */
325         key = 0;
326
327         /*
328          * m32
329          */
330         crcx = 0;
331         for (i = j = 0; i < len; ++i) {
332                 if (aname[i] == '.' ||
333                     aname[i] == '-' ||
334                     aname[i] == '_' ||
335                     aname[i] == '~') {
336                         if (i != j)
337                                 crcx += hammer2_icrc32(aname + j, i - j);
338                         j = i + 1;
339                 }
340         }
341         if (i != j)
342                 crcx += hammer2_icrc32(aname + j, i - j);
343
344         /*
345          * The directory hash utilizes the top 32 bits of the 64-bit key.
346          * Bit 63 must be set to 1.
347          */
348         crcx |= 0x80000000U;
349         key |= (uint64_t)crcx << 32;
350
351         /*
352          * l16 - crc of entire filename
353          *
354          * This crc reduces degenerate hash collision conditions
355          */
356         crcx = hammer2_icrc32(aname, len);
357         crcx = crcx ^ (crcx << 16);
358         key |= crcx & 0xFFFF0000U;
359
360         /*
361          * Set bit 15.  This allows readdir to strip bit 63 so a positive
362          * 64-bit cookie/offset can always be returned, and still guarantee
363          * that the values 0x0000-0x7FFF are available for artificial entries.
364          * ('.' and '..').
365          */
366         key |= 0x8000U;
367
368         return (key);
369 }
370
371 char **
372 get_hammer2_mounts(int *acp)
373 {
374         struct statfs *fs;
375         char **av;
376         int n;
377         int w;
378         int i;
379
380         /*
381          * Get a stable list of mount points
382          */
383 again:
384         n = getfsstat(NULL, 0, MNT_NOWAIT);
385         av = calloc(n, sizeof(char *));
386         fs = calloc(n, sizeof(struct statfs));
387         if (getfsstat(fs, sizeof(*fs) * n, MNT_NOWAIT) != n) {
388                 free(av);
389                 free(fs);
390                 goto again;
391         }
392
393         /*
394          * Pull out hammer2 filesystems only
395          */
396         for (i = w = 0; i < n; ++i) {
397                 if (strcmp(fs[i].f_fstypename, "hammer2") != 0)
398                         continue;
399                 av[w++] = strdup(fs[i].f_mntonname);
400         }
401         *acp = w;
402         free(fs);
403
404         return av;
405 }
406
407 void
408 put_hammer2_mounts(int ac, char **av)
409 {
410         while (--ac >= 0)
411                 free(av[ac]);
412         free(av);
413 }