xref: /dragonfly/sys/vfs/hammer2/hammer2_subr.c (revision 2b7dbe20)
1 /*
2  * Copyright (c) 2011-2018 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 #include <sys/cdefs.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/types.h>
39 #include <sys/lock.h>
40 #include <sys/uuid.h>
41 #include <sys/dirent.h>
42 
43 #include "hammer2.h"
44 
45 /*
46  * Return the directory entry type for an inode.
47  */
48 int
49 hammer2_get_dtype(uint8_t type)
50 {
51 	switch(type) {
52 	case HAMMER2_OBJTYPE_UNKNOWN:
53 		return (DT_UNKNOWN);
54 	case HAMMER2_OBJTYPE_DIRECTORY:
55 		return (DT_DIR);
56 	case HAMMER2_OBJTYPE_REGFILE:
57 		return (DT_REG);
58 	case HAMMER2_OBJTYPE_FIFO:
59 		return (DT_FIFO);
60 	case HAMMER2_OBJTYPE_CDEV:
61 		return (DT_CHR);
62 	case HAMMER2_OBJTYPE_BDEV:
63 		return (DT_BLK);
64 	case HAMMER2_OBJTYPE_SOFTLINK:
65 		return (DT_LNK);
66 	case HAMMER2_OBJTYPE_SOCKET:
67 		return (DT_SOCK);
68 	case HAMMER2_OBJTYPE_WHITEOUT:	/* not supported */
69 		return (DT_UNKNOWN);
70 	default:
71 		return (DT_UNKNOWN);
72 	}
73 	/* not reached */
74 }
75 
76 /*
77  * Return the directory entry type for an inode
78  */
79 int
80 hammer2_get_vtype(uint8_t type)
81 {
82 	switch(type) {
83 	case HAMMER2_OBJTYPE_UNKNOWN:
84 		return (VBAD);
85 	case HAMMER2_OBJTYPE_DIRECTORY:
86 		return (VDIR);
87 	case HAMMER2_OBJTYPE_REGFILE:
88 		return (VREG);
89 	case HAMMER2_OBJTYPE_FIFO:
90 		return (VFIFO);
91 	case HAMMER2_OBJTYPE_CDEV:
92 		return (VCHR);
93 	case HAMMER2_OBJTYPE_BDEV:
94 		return (VBLK);
95 	case HAMMER2_OBJTYPE_SOFTLINK:
96 		return (VLNK);
97 	case HAMMER2_OBJTYPE_SOCKET:
98 		return (VSOCK);
99 	case HAMMER2_OBJTYPE_WHITEOUT:	/* not supported */
100 		return (VBAD);
101 	default:
102 		return (VBAD);
103 	}
104 	/* not reached */
105 }
106 
107 uint8_t
108 hammer2_get_obj_type(enum vtype vtype)
109 {
110 	switch(vtype) {
111 	case VDIR:
112 		return(HAMMER2_OBJTYPE_DIRECTORY);
113 	case VREG:
114 		return(HAMMER2_OBJTYPE_REGFILE);
115 	case VFIFO:
116 		return(HAMMER2_OBJTYPE_FIFO);
117 	case VSOCK:
118 		return(HAMMER2_OBJTYPE_SOCKET);
119 	case VCHR:
120 		return(HAMMER2_OBJTYPE_CDEV);
121 	case VBLK:
122 		return(HAMMER2_OBJTYPE_BDEV);
123 	case VLNK:
124 		return(HAMMER2_OBJTYPE_SOFTLINK);
125 	default:
126 		return(HAMMER2_OBJTYPE_UNKNOWN);
127 	}
128 	/* not reached */
129 }
130 
131 /*
132  * Convert a hammer2 64-bit time to a timespec.
133  */
134 void
135 hammer2_time_to_timespec(uint64_t xtime, struct timespec *ts)
136 {
137 	ts->tv_sec = (unsigned long)(xtime / 1000000);
138 	ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L;
139 }
140 
141 uint64_t
142 hammer2_timespec_to_time(const struct timespec *ts)
143 {
144 	uint64_t xtime;
145 
146 	xtime = (unsigned)(ts->tv_nsec / 1000) +
147 		(unsigned long)ts->tv_sec * 1000000ULL;
148 	return(xtime);
149 }
150 
151 /*
152  * Convert a uuid to a unix uid or gid
153  */
154 uint32_t
155 hammer2_to_unix_xid(const uuid_t *uuid)
156 {
157 	return(*(const uint32_t *)&uuid->node[2]);
158 }
159 
160 void
161 hammer2_guid_to_uuid(uuid_t *uuid, uint32_t guid)
162 {
163 	bzero(uuid, sizeof(*uuid));
164 	*(uint32_t *)&uuid->node[2] = guid;
165 }
166 
167 /*
168  * Borrow HAMMER1's directory hash algorithm #1 with a few modifications.
169  * The filename is split into fields which are hashed separately and then
170  * added together.
171  *
172  * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets
173  * it to 0), this is because bit63=0 is used for hidden hardlinked inodes.
174  * (This means we do not need to do a 0-check/or-with-0x100000000 either).
175  *
176  * Also, the iscsi crc code is used instead of the old crc32 code.
177  */
178 hammer2_key_t
179 hammer2_dirhash(const unsigned char *name, size_t len)
180 {
181 	const unsigned char *aname = name;
182 	uint32_t crcx;
183 	uint64_t key;
184 	size_t i;
185 	size_t j;
186 
187 	key = 0;
188 
189 	/*
190 	 * m32
191 	 */
192 	crcx = 0;
193 	for (i = j = 0; i < len; ++i) {
194 		if (aname[i] == '.' ||
195 		    aname[i] == '-' ||
196 		    aname[i] == '_' ||
197 		    aname[i] == '~') {
198 			if (i != j)
199 				crcx += hammer2_icrc32(aname + j, i - j);
200 			j = i + 1;
201 		}
202 	}
203 	if (i != j)
204 		crcx += hammer2_icrc32(aname + j, i - j);
205 
206 	/*
207 	 * The directory hash utilizes the top 32 bits of the 64-bit key.
208 	 * Bit 63 must be set to 1.
209 	 */
210 	crcx |= 0x80000000U;
211 	key |= (uint64_t)crcx << 32;
212 
213 	/*
214 	 * l16 - crc of entire filename
215 	 *
216 	 * This crc reduces degenerate hash collision conditions
217 	 */
218 	crcx = hammer2_icrc32(aname, len);
219 	crcx = crcx ^ (crcx << 16);
220 	key |= crcx & 0xFFFF0000U;
221 
222 	/*
223 	 * Set bit 15.  This allows readdir to strip bit 63 so a positive
224 	 * 64-bit cookie/offset can always be returned, and still guarantee
225 	 * that the values 0x0000-0x7FFF are available for artificial entries.
226 	 * ('.' and '..').
227 	 */
228 	key |= 0x8000U;
229 
230 	return (key);
231 }
232 
233 /*
234  * Convert bytes to radix with no limitations.
235  *
236  * 0 bytes is special-cased to a radix of zero (which would normally
237  * translate to (1 << 0) == 1).
238  */
239 int
240 hammer2_getradix(size_t bytes)
241 {
242 	int radix;
243 
244 	/*
245 	 * Optimize the iteration by pre-checking commonly used radii.
246 	 */
247 	if (bytes == HAMMER2_PBUFSIZE)
248 		radix = HAMMER2_PBUFRADIX;
249 	else if (bytes >= HAMMER2_LBUFSIZE)
250 		radix = HAMMER2_LBUFRADIX;
251 	else if (bytes >= HAMMER2_ALLOC_MIN)	/* clamp */
252 		radix = HAMMER2_RADIX_MIN;
253 	else
254 		radix = 0;
255 
256 	/*
257 	 * Iterate as needed.  Note that bytes == 0 is expected to return
258 	 * a radix of 0 as a special case.
259 	 */
260 	while (((size_t)1 << radix) < bytes)
261 		++radix;
262 	return (radix);
263 }
264 
265 /*
266  * The logical block size is currently always PBUFSIZE.
267  */
268 int
269 hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff,
270 		     hammer2_key_t *lbasep, hammer2_key_t *leofp)
271 {
272 	KKASSERT(ip->flags & HAMMER2_INODE_METAGOOD);
273 	if (lbasep)
274 		*lbasep = uoff & ~HAMMER2_PBUFMASK64;
275 	if (leofp) {
276 		*leofp = (ip->meta.size + HAMMER2_PBUFMASK64) &
277 			 ~HAMMER2_PBUFMASK64;
278 	}
279 	return (HAMMER2_PBUFSIZE);
280 }
281 
282 /*
283  * Calculate the physical block size.  pblksize <= lblksize.  Primarily
284  * used to calculate a smaller physical block for the logical block
285  * containing the file EOF.
286  *
287  * Returns 0 if the requested base offset is beyond the file EOF.
288  */
289 int
290 hammer2_calc_physical(hammer2_inode_t *ip, hammer2_key_t lbase)
291 {
292 	int lblksize;
293 	int pblksize;
294 	int eofbytes;
295 
296 	KKASSERT(ip->flags & HAMMER2_INODE_METAGOOD);
297 	lblksize = hammer2_calc_logical(ip, lbase, NULL, NULL);
298 	if (lbase + lblksize <= ip->meta.size)
299 		return (lblksize);
300 	if (lbase >= ip->meta.size)
301 		return (0);
302 	eofbytes = (int)(ip->meta.size - lbase);
303 	pblksize = lblksize;
304 	while (pblksize >= eofbytes && pblksize >= HAMMER2_ALLOC_MIN)
305 		pblksize >>= 1;
306 	pblksize <<= 1;
307 
308 	return (pblksize);
309 }
310 
311 void
312 hammer2_update_time(uint64_t *timep)
313 {
314 	struct timespec ts;
315 
316 	vfs_timestamp(&ts);
317 	*timep = (unsigned long)ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
318 }
319 
320 void
321 hammer2_adjreadcounter(int btype, size_t bytes)
322 {
323 	long *counterp;
324 
325 	switch(btype) {
326 	case HAMMER2_BREF_TYPE_DATA:
327 		counterp = &hammer2_iod_file_read;
328 		break;
329 	case HAMMER2_BREF_TYPE_DIRENT:
330 	case HAMMER2_BREF_TYPE_INODE:
331 		counterp = &hammer2_iod_meta_read;
332 		break;
333 	case HAMMER2_BREF_TYPE_INDIRECT:
334 		counterp = &hammer2_iod_indr_read;
335 		break;
336 	case HAMMER2_BREF_TYPE_FREEMAP_NODE:
337 	case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
338 		counterp = &hammer2_iod_fmap_read;
339 		break;
340 	case HAMMER2_BREF_TYPE_FREEMAP:
341 	case HAMMER2_BREF_TYPE_VOLUME:
342 		counterp = &hammer2_iod_volu_read;
343 		break;
344 	case HAMMER2_BREF_TYPE_EMPTY:
345 	default:
346 		return;
347 	}
348 	*counterp += bytes;
349 }
350 
351 void
352 hammer2_adjwritecounter(int btype, size_t bytes)
353 {
354 	long *counterp;
355 
356 	switch(btype) {
357 	case HAMMER2_BREF_TYPE_DATA:
358 		counterp = &hammer2_iod_file_write;
359 		break;
360 	case HAMMER2_BREF_TYPE_DIRENT:
361 	case HAMMER2_BREF_TYPE_INODE:
362 		counterp = &hammer2_iod_meta_write;
363 		break;
364 	case HAMMER2_BREF_TYPE_INDIRECT:
365 		counterp = &hammer2_iod_indr_write;
366 		break;
367 	case HAMMER2_BREF_TYPE_FREEMAP_NODE:
368 	case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
369 		counterp = &hammer2_iod_fmap_write;
370 		break;
371 	case HAMMER2_BREF_TYPE_FREEMAP:
372 	case HAMMER2_BREF_TYPE_VOLUME:
373 		counterp = &hammer2_iod_volu_write;
374 		break;
375 	case HAMMER2_BREF_TYPE_EMPTY:
376 	default:
377 		return;
378 	}
379 	*counterp += bytes;
380 }
381 
382 /*
383  * Check for pending signal to allow interruption.  This function will
384  * return immediately if the calling thread is a kernel thread and not
385  * a user thread.
386  */
387 int
388 hammer2_signal_check(time_t *timep)
389 {
390 	thread_t td = curthread;
391 	int error = 0;
392 
393 	if (td->td_lwp) {
394 		lwkt_user_yield();
395 		if (*timep != time_second) {
396 			*timep = time_second;
397 			if (CURSIG_NOBLOCK(curthread->td_lwp) != 0)
398 				error = HAMMER2_ERROR_ABORTED;
399 		}
400 	} else {
401 		lwkt_yield();
402 	}
403 	return error;
404 }
405 
406 const char *
407 hammer2_error_str(int error)
408 {
409 	if (error & HAMMER2_ERROR_EIO)
410 		return("I/O Error");
411 	if (error & HAMMER2_ERROR_CHECK)
412 		return("Check Error");
413 	if (error & HAMMER2_ERROR_INCOMPLETE)
414 		return("Cluster Quorum Error");
415 	if (error & HAMMER2_ERROR_DEPTH)
416 		return("Chain Depth Error");
417 	if (error & HAMMER2_ERROR_BADBREF)
418 		return("Bad Blockref Error");
419 	if (error & HAMMER2_ERROR_ENOSPC)
420 		return("No Space on Device");
421 	if (error & HAMMER2_ERROR_ENOENT)
422 		return("Entry Not Found");
423 	if (error & HAMMER2_ERROR_ENOTEMPTY)
424 		return("Directory Not Empty");
425 	if (error & HAMMER2_ERROR_EAGAIN)
426 		return("EAGAIN");
427 	if (error & HAMMER2_ERROR_ENOTDIR)
428 		return("Not a Directory");
429 	if (error & HAMMER2_ERROR_EISDIR)
430 		return("Is a Directory");
431 	if (error & HAMMER2_ERROR_EINPROGRESS)
432 		return("Operation in Progress");
433 	if (error & HAMMER2_ERROR_ABORTED)
434 		return("Operation Aborted");
435 	if (error & HAMMER2_ERROR_EOF)
436 		return("Operation Complete");
437 	if (error & HAMMER2_ERROR_EINVAL)
438 		return("Invalid Operation");
439 	if (error & HAMMER2_ERROR_EEXIST)
440 		return("Object Exists");
441 	if (error & HAMMER2_ERROR_EDEADLK)
442 		return("Deadlock Detected");
443 	if (error & HAMMER2_ERROR_ESRCH)
444 		return("Object Not Found");
445 	if (error & HAMMER2_ERROR_ETIMEDOUT)
446 		return("Timeout");
447 	return("Unknown Error");
448 }
449 
450 const char *
451 hammer2_bref_type_str(int btype)
452 {
453 	switch(btype) {
454 	case HAMMER2_BREF_TYPE_EMPTY:
455 		return("empty");
456 	case HAMMER2_BREF_TYPE_INODE:
457 		return("inode");
458 	case HAMMER2_BREF_TYPE_INDIRECT:
459 		return("indirect");
460 	case HAMMER2_BREF_TYPE_DATA:
461 		return("data");
462 	case HAMMER2_BREF_TYPE_DIRENT:
463 		return("dirent");
464 	case HAMMER2_BREF_TYPE_FREEMAP_NODE:
465 		return("freemap_node");
466 	case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
467 		return("freemap_leaf");
468 	case HAMMER2_BREF_TYPE_FREEMAP:
469 		return("freemap");
470 	case HAMMER2_BREF_TYPE_VOLUME:
471 		return("volume");
472 	default:
473 		return("unknown");
474 	}
475 }
476