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