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