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