xref: /dragonfly/sbin/hammer2/subs.c (revision a765cedf)
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 char *aname, size_t len)
309 {
310 	uint32_t crcx;
311 	uint64_t key;
312 	size_t i;
313 	size_t j;
314 
315 	key = 0;
316 
317 	/*
318 	 * m32
319 	 */
320 	crcx = 0;
321 	for (i = j = 0; i < len; ++i) {
322 		if (aname[i] == '.' ||
323 		    aname[i] == '-' ||
324 		    aname[i] == '_' ||
325 		    aname[i] == '~') {
326 			if (i != j)
327 				crcx += hammer2_icrc32(aname + j, i - j);
328 			j = i + 1;
329 		}
330 	}
331 	if (i != j)
332 		crcx += hammer2_icrc32(aname + j, i - j);
333 
334 	/*
335 	 * The directory hash utilizes the top 32 bits of the 64-bit key.
336 	 * Bit 63 must be set to 1.
337 	 */
338 	crcx |= 0x80000000U;
339 	key |= (uint64_t)crcx << 32;
340 
341 	/*
342 	 * l16 - crc of entire filename
343 	 *
344 	 * This crc reduces degenerate hash collision conditions.
345 	 */
346 	crcx = hammer2_icrc32(aname, len);
347 	crcx = crcx ^ (crcx << 16);
348 	key |= crcx & 0xFFFF0000U;
349 
350 	/*
351 	 * Set bit 15.  This allows readdir to strip bit 63 so a positive
352 	 * 64-bit cookie/offset can always be returned, and still guarantee
353 	 * that the values 0x0000-0x7FFF are available for artificial entries.
354 	 * ('.' and '..').
355 	 */
356 	key |= 0x8000U;
357 
358 	return (key);
359 }
360 
361 char **
362 get_hammer2_mounts(int *acp)
363 {
364 	struct statfs *fs;
365 	char **av;
366 	int n;
367 	int w;
368 	int i;
369 
370 	/*
371 	 * Get a stable list of mount points
372 	 */
373 again:
374 	n = getfsstat(NULL, 0, MNT_NOWAIT);
375 	av = calloc(n, sizeof(char *));
376 	fs = calloc(n, sizeof(struct statfs));
377 	if (getfsstat(fs, sizeof(*fs) * n, MNT_NOWAIT) != n) {
378 		free(av);
379 		free(fs);
380 		goto again;
381 	}
382 
383 	/*
384 	 * Pull out hammer2 filesystems only
385 	 */
386 	for (i = w = 0; i < n; ++i) {
387 		if (strcmp(fs[i].f_fstypename, "hammer2") != 0)
388 			continue;
389 		av[w++] = strdup(fs[i].f_mntonname);
390 	}
391 	*acp = w;
392 	free(fs);
393 
394 	return av;
395 }
396 
397 void
398 put_hammer2_mounts(int ac, char **av)
399 {
400 	while (--ac >= 0)
401 		free(av[ac]);
402 	free(av);
403 }
404