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