xref: /dflybsd-src/sys/vfs/hammer2/hammer2_subr.c (revision 5ca0a96d6c3bf50926197b4bb92af7969ed3528a)
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_INVALID:
469  		return("invalid");
470  	case HAMMER2_BREF_TYPE_FREEMAP:
471  		return("freemap");
472  	case HAMMER2_BREF_TYPE_VOLUME:
473  		return("volume");
474  	default:
475  		return("unknown");
476  	}
477  }
478