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