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 <stdint.h>
48 #include <string.h>
49 #include <errno.h>
50 #include <err.h>
51 #include <uuid.h>
52
53 #include <vfs/hammer2/hammer2_disk.h>
54 #include <vfs/hammer2/hammer2_ioctl.h>
55
56 #include "hammer2_subs.h"
57
58 /*
59 * Obtain a file descriptor that the caller can execute ioctl()'s on.
60 */
61 int
hammer2_ioctl_handle(const char * sel_path)62 hammer2_ioctl_handle(const char *sel_path)
63 {
64 struct hammer2_ioc_version info;
65 int fd;
66
67 if (sel_path == NULL)
68 sel_path = ".";
69
70 fd = open(sel_path, O_RDONLY, 0);
71 if (fd < 0) {
72 fprintf(stderr, "hammer2: Unable to open %s: %s\n",
73 sel_path, strerror(errno));
74 return(-1);
75 }
76 if (ioctl(fd, HAMMER2IOC_VERSION_GET, &info) < 0) {
77 fprintf(stderr, "hammer2: '%s' is not a hammer2 filesystem\n",
78 sel_path);
79 close(fd);
80 return(-1);
81 }
82 return (fd);
83 }
84
85 const char *
hammer2_time64_to_str(uint64_t htime64,char ** strp)86 hammer2_time64_to_str(uint64_t htime64, char **strp)
87 {
88 struct tm *tp;
89 time_t t;
90
91 if (*strp) {
92 free(*strp);
93 *strp = NULL;
94 }
95 *strp = malloc(64);
96 t = htime64 / 1000000;
97 tp = localtime(&t);
98 strftime(*strp, 64, "%d-%b-%Y %H:%M:%S", tp);
99 return (*strp);
100 }
101
102 const char *
hammer2_uuid_to_str(const uuid_t * uuid,char ** strp)103 hammer2_uuid_to_str(const uuid_t *uuid, char **strp)
104 {
105 uint32_t status;
106 if (*strp) {
107 free(*strp);
108 *strp = NULL;
109 }
110 uuid_to_string(uuid, strp, &status);
111 return (*strp);
112 }
113
114 const char *
hammer2_iptype_to_str(uint8_t type)115 hammer2_iptype_to_str(uint8_t type)
116 {
117 switch(type) {
118 case HAMMER2_OBJTYPE_UNKNOWN:
119 return("UNKNOWN");
120 case HAMMER2_OBJTYPE_DIRECTORY:
121 return("DIR");
122 case HAMMER2_OBJTYPE_REGFILE:
123 return("FILE");
124 case HAMMER2_OBJTYPE_FIFO:
125 return("FIFO");
126 case HAMMER2_OBJTYPE_CDEV:
127 return("CDEV");
128 case HAMMER2_OBJTYPE_BDEV:
129 return("BDEV");
130 case HAMMER2_OBJTYPE_SOFTLINK:
131 return("SOFTLINK");
132 case HAMMER2_OBJTYPE_SOCKET:
133 return("SOCKET");
134 case HAMMER2_OBJTYPE_WHITEOUT:
135 return("WHITEOUT");
136 default:
137 return("ILLEGAL");
138 }
139 }
140
141 const char *
hammer2_pfstype_to_str(uint8_t type)142 hammer2_pfstype_to_str(uint8_t type)
143 {
144 switch(type) {
145 case HAMMER2_PFSTYPE_NONE:
146 return("NONE");
147 case HAMMER2_PFSTYPE_SUPROOT:
148 return("SUPROOT");
149 case HAMMER2_PFSTYPE_DUMMY:
150 return("DUMMY");
151 case HAMMER2_PFSTYPE_CACHE:
152 return("CACHE");
153 case HAMMER2_PFSTYPE_SLAVE:
154 return("SLAVE");
155 case HAMMER2_PFSTYPE_SOFT_SLAVE:
156 return("SOFT_SLAVE");
157 case HAMMER2_PFSTYPE_SOFT_MASTER:
158 return("SOFT_MASTER");
159 case HAMMER2_PFSTYPE_MASTER:
160 return("MASTER");
161 default:
162 return("ILLEGAL");
163 }
164 }
165
166 const char *
hammer2_pfssubtype_to_str(uint8_t subtype)167 hammer2_pfssubtype_to_str(uint8_t subtype)
168 {
169 switch(subtype) {
170 case HAMMER2_PFSSUBTYPE_NONE:
171 return("NONE");
172 case HAMMER2_PFSSUBTYPE_SNAPSHOT:
173 return("SNAPSHOT");
174 case HAMMER2_PFSSUBTYPE_AUTOSNAP:
175 return("AUTOSNAP");
176 default:
177 return("ILLEGAL");
178 }
179 }
180
181 const char *
hammer2_breftype_to_str(uint8_t type)182 hammer2_breftype_to_str(uint8_t type)
183 {
184 switch(type) {
185 case HAMMER2_BREF_TYPE_EMPTY:
186 return("empty");
187 case HAMMER2_BREF_TYPE_INODE:
188 return("inode");
189 case HAMMER2_BREF_TYPE_INDIRECT:
190 return("indirect");
191 case HAMMER2_BREF_TYPE_DATA:
192 return("data");
193 case HAMMER2_BREF_TYPE_DIRENT:
194 return("dirent");
195 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
196 return("freemap_node");
197 case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
198 return("freemap_leaf");
199 case HAMMER2_BREF_TYPE_INVALID:
200 return("invalid");
201 case HAMMER2_BREF_TYPE_FREEMAP:
202 return("freemap");
203 case HAMMER2_BREF_TYPE_VOLUME:
204 return("volume");
205 default:
206 return("unknown");
207 }
208 }
209
210 const char *
hammer2_compmode_to_str(uint8_t comp_algo)211 hammer2_compmode_to_str(uint8_t comp_algo)
212 {
213 static char buf[64];
214 static const char *comps[] = HAMMER2_COMP_STRINGS;
215 int comp = HAMMER2_DEC_ALGO(comp_algo);
216 int level = HAMMER2_DEC_LEVEL(comp_algo);
217
218 if (level) {
219 if (comp >= 0 && comp < HAMMER2_COMP_STRINGS_COUNT)
220 snprintf(buf, sizeof(buf), "%s:%d",
221 comps[comp], level);
222 else
223 snprintf(buf, sizeof(buf), "unknown(%d):%d",
224 comp, level);
225 } else {
226 if (comp >= 0 && comp < HAMMER2_COMP_STRINGS_COUNT)
227 snprintf(buf, sizeof(buf), "%s:default",
228 comps[comp]);
229 else
230 snprintf(buf, sizeof(buf), "unknown(%d):default",
231 comp);
232 }
233 return (buf);
234 }
235
236 const char *
hammer2_checkmode_to_str(uint8_t check_algo)237 hammer2_checkmode_to_str(uint8_t check_algo)
238 {
239 static char buf[64];
240 static const char *checks[] = HAMMER2_CHECK_STRINGS;
241 int check = HAMMER2_DEC_ALGO(check_algo);
242 int level = HAMMER2_DEC_LEVEL(check_algo);
243
244 /*
245 * NOTE: Check algorithms normally do not encode any level.
246 */
247 if (level) {
248 if (check >= 0 && check < HAMMER2_CHECK_STRINGS_COUNT)
249 snprintf(buf, sizeof(buf), "%s:%d",
250 checks[check], level);
251 else
252 snprintf(buf, sizeof(buf), "unknown(%d):%d",
253 check, level);
254 } else {
255 if (check >= 0 && check < HAMMER2_CHECK_STRINGS_COUNT)
256 snprintf(buf, sizeof(buf), "%s", checks[check]);
257 else
258 snprintf(buf, sizeof(buf), "unknown(%d)", check);
259 }
260 return (buf);
261 }
262
263 const char *
sizetostr(hammer2_off_t size)264 sizetostr(hammer2_off_t size)
265 {
266 static char buf[32];
267
268 if (size < 1024 / 2) {
269 snprintf(buf, sizeof(buf), "%6.2fB", (double)size);
270 } else if (size < 1024 * 1024 / 2) {
271 snprintf(buf, sizeof(buf), "%6.2fKB",
272 (double)size / 1024);
273 } else if (size < 1024 * 1024 * 1024LL / 2) {
274 snprintf(buf, sizeof(buf), "%6.2fMB",
275 (double)size / (1024 * 1024));
276 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
277 snprintf(buf, sizeof(buf), "%6.2fGB",
278 (double)size / (1024 * 1024 * 1024LL));
279 } else {
280 snprintf(buf, sizeof(buf), "%6.2fTB",
281 (double)size / (1024 * 1024 * 1024LL * 1024LL));
282 }
283 return(buf);
284 }
285
286 const char *
counttostr(hammer2_off_t size)287 counttostr(hammer2_off_t size)
288 {
289 static char buf[32];
290
291 if (size < 1024 / 2) {
292 snprintf(buf, sizeof(buf), "%jd",
293 (intmax_t)size);
294 } else if (size < 1024 * 1024 / 2) {
295 snprintf(buf, sizeof(buf), "%jd",
296 (intmax_t)size);
297 } else if (size < 1024 * 1024 * 1024LL / 2) {
298 snprintf(buf, sizeof(buf), "%6.2fM",
299 (double)size / (1024 * 1024));
300 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
301 snprintf(buf, sizeof(buf), "%6.2fG",
302 (double)(size / (1024 * 1024 * 1024LL)));
303 } else {
304 snprintf(buf, sizeof(buf), "%6.2fT",
305 (double)(size / (1024 * 1024 * 1024LL * 1024LL)));
306 }
307 return(buf);
308 }
309
310 hammer2_off_t
check_volume(int fd)311 check_volume(int fd)
312 {
313 struct partinfo pinfo;
314 struct stat st;
315 hammer2_off_t size;
316
317 /*
318 * Get basic information about the volume
319 */
320 if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
321 /*
322 * Allow the formatting of regular files as HAMMER2 volumes
323 */
324 if (fstat(fd, &st) < 0)
325 err(1, "Unable to stat fd %d", fd);
326 if (!S_ISREG(st.st_mode))
327 errx(1, "Unsupported file type for fd %d", fd);
328 size = st.st_size;
329 } else {
330 /*
331 * When formatting a block device as a HAMMER2 volume the
332 * sector size must be compatible. HAMMER2 uses 64K
333 * filesystem buffers but logical buffers for direct I/O
334 * can be as small as HAMMER2_LOGSIZE (16KB).
335 */
336 if (pinfo.reserved_blocks) {
337 errx(1, "HAMMER2 cannot be placed in a partition "
338 "which overlaps the disklabel or MBR");
339 }
340 if (pinfo.media_blksize > HAMMER2_PBUFSIZE ||
341 HAMMER2_PBUFSIZE % pinfo.media_blksize) {
342 errx(1, "A media sector size of %d is not supported",
343 pinfo.media_blksize);
344 }
345 size = pinfo.media_size;
346 }
347 return(size);
348 }
349
350 /*
351 * Borrow HAMMER1's directory hash algorithm #1 with a few modifications.
352 * The filename is split into fields which are hashed separately and then
353 * added together.
354 *
355 * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets
356 * it to 0), this is because bit63=0 is used for hidden hardlinked inodes.
357 * (This means we do not need to do a 0-check/or-with-0x100000000 either).
358 *
359 * Also, the iscsi crc code is used instead of the old crc32 code.
360 */
361 hammer2_key_t
dirhash(const char * aname,size_t len)362 dirhash(const char *aname, size_t len)
363 {
364 uint32_t crcx;
365 uint64_t key;
366 size_t i;
367 size_t j;
368
369 key = 0;
370
371 /*
372 * m32
373 */
374 crcx = 0;
375 for (i = j = 0; i < len; ++i) {
376 if (aname[i] == '.' ||
377 aname[i] == '-' ||
378 aname[i] == '_' ||
379 aname[i] == '~') {
380 if (i != j)
381 crcx += hammer2_icrc32(aname + j, i - j);
382 j = i + 1;
383 }
384 }
385 if (i != j)
386 crcx += hammer2_icrc32(aname + j, i - j);
387
388 /*
389 * The directory hash utilizes the top 32 bits of the 64-bit key.
390 * Bit 63 must be set to 1.
391 */
392 crcx |= 0x80000000U;
393 key |= (uint64_t)crcx << 32;
394
395 /*
396 * l16 - crc of entire filename
397 *
398 * This crc reduces degenerate hash collision conditions.
399 */
400 crcx = hammer2_icrc32(aname, len);
401 crcx = crcx ^ (crcx << 16);
402 key |= crcx & 0xFFFF0000U;
403
404 /*
405 * Set bit 15. This allows readdir to strip bit 63 so a positive
406 * 64-bit cookie/offset can always be returned, and still guarantee
407 * that the values 0x0000-0x7FFF are available for artificial entries.
408 * ('.' and '..').
409 */
410 key |= 0x8000U;
411
412 return (key);
413 }
414
415 char **
get_hammer2_mounts(int * acp)416 get_hammer2_mounts(int *acp)
417 {
418 struct statfs *fs;
419 char **av;
420 int n;
421 int w;
422 int i;
423
424 /*
425 * Get a stable list of mount points
426 */
427 again:
428 n = getfsstat(NULL, 0, MNT_NOWAIT);
429 av = calloc(n, sizeof(char *));
430 fs = calloc(n, sizeof(struct statfs));
431 if (getfsstat(fs, sizeof(*fs) * n, MNT_NOWAIT) != n) {
432 free(av);
433 free(fs);
434 goto again;
435 }
436
437 /*
438 * Pull out hammer2 filesystems only
439 */
440 for (i = w = 0; i < n; ++i) {
441 if (strcmp(fs[i].f_fstypename, "hammer2") != 0)
442 continue;
443 av[w++] = strdup(fs[i].f_mntonname);
444 }
445 *acp = w;
446 free(fs);
447
448 return av;
449 }
450
451 void
put_hammer2_mounts(int ac,char ** av)452 put_hammer2_mounts(int ac, char **av)
453 {
454 while (--ac >= 0)
455 free(av[ac]);
456 free(av);
457 }
458