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