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