1 /* 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org> 5 * Copyright (c) 2011-2022 The DragonFly Project. All rights reserved. 6 * 7 * This code is derived from software contributed to The DragonFly Project 8 * by Matthew Dillon <dillon@dragonflybsd.org> 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 3. Neither the name of The DragonFly Project nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific, prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 /* 38 #include <sys/cdefs.h> 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/types.h> 42 #include <sys/uuid.h> 43 */ 44 #include <sys/time.h> 45 #include <sys/dirent.h> 46 47 #include "hammer2.h" 48 #include "makefs.h" 49 50 /* 51 * Return the directory entry type for an inode. 52 */ 53 int 54 hammer2_get_dtype(uint8_t type) 55 { 56 switch(type) { 57 case HAMMER2_OBJTYPE_UNKNOWN: 58 return (DT_UNKNOWN); 59 case HAMMER2_OBJTYPE_DIRECTORY: 60 return (DT_DIR); 61 case HAMMER2_OBJTYPE_REGFILE: 62 return (DT_REG); 63 case HAMMER2_OBJTYPE_FIFO: 64 return (DT_FIFO); 65 case HAMMER2_OBJTYPE_CDEV: 66 return (DT_CHR); 67 case HAMMER2_OBJTYPE_BDEV: 68 return (DT_BLK); 69 case HAMMER2_OBJTYPE_SOFTLINK: 70 return (DT_LNK); 71 case HAMMER2_OBJTYPE_SOCKET: 72 return (DT_SOCK); 73 case HAMMER2_OBJTYPE_WHITEOUT: /* not supported */ 74 return (DT_UNKNOWN); 75 default: 76 return (DT_UNKNOWN); 77 } 78 /* not reached */ 79 } 80 81 /* 82 * Return the directory entry type for an inode 83 */ 84 int 85 hammer2_get_vtype(uint8_t type) 86 { 87 switch(type) { 88 case HAMMER2_OBJTYPE_UNKNOWN: 89 return (VBAD); 90 case HAMMER2_OBJTYPE_DIRECTORY: 91 return (VDIR); 92 case HAMMER2_OBJTYPE_REGFILE: 93 return (VREG); 94 case HAMMER2_OBJTYPE_FIFO: 95 return (VFIFO); 96 case HAMMER2_OBJTYPE_CDEV: 97 return (VCHR); 98 case HAMMER2_OBJTYPE_BDEV: 99 return (VBLK); 100 case HAMMER2_OBJTYPE_SOFTLINK: 101 return (VLNK); 102 case HAMMER2_OBJTYPE_SOCKET: 103 return (VSOCK); 104 case HAMMER2_OBJTYPE_WHITEOUT: /* not supported */ 105 return (VBAD); 106 default: 107 return (VBAD); 108 } 109 /* not reached */ 110 } 111 112 uint8_t 113 hammer2_get_obj_type(enum vtype vtype) 114 { 115 switch(vtype) { 116 case VDIR: 117 return(HAMMER2_OBJTYPE_DIRECTORY); 118 case VREG: 119 return(HAMMER2_OBJTYPE_REGFILE); 120 case VFIFO: 121 return(HAMMER2_OBJTYPE_FIFO); 122 case VSOCK: 123 return(HAMMER2_OBJTYPE_SOCKET); 124 case VCHR: 125 return(HAMMER2_OBJTYPE_CDEV); 126 case VBLK: 127 return(HAMMER2_OBJTYPE_BDEV); 128 case VLNK: 129 return(HAMMER2_OBJTYPE_SOFTLINK); 130 default: 131 return(HAMMER2_OBJTYPE_UNKNOWN); 132 } 133 /* not reached */ 134 } 135 136 /* 137 * Convert a hammer2 64-bit time to a timespec. 138 */ 139 void 140 hammer2_time_to_timespec(uint64_t xtime, struct timespec *ts) 141 { 142 ts->tv_sec = (unsigned long)(xtime / 1000000); 143 ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L; 144 } 145 146 uint64_t 147 hammer2_timespec_to_time(const struct timespec *ts) 148 { 149 uint64_t xtime; 150 151 xtime = (unsigned)(ts->tv_nsec / 1000) + 152 (unsigned long)ts->tv_sec * 1000000ULL; 153 return(xtime); 154 } 155 156 /* 157 * Convert a hammer2 64-bit time to a timeval. 158 */ 159 void 160 hammer2_time_to_timeval(uint64_t xtime, struct timeval *tv) 161 { 162 tv->tv_sec = (unsigned long)(xtime / 1000000); 163 tv->tv_usec = (unsigned int)(xtime % 1000000); 164 } 165 166 /* 167 * Convert a uuid to a unix uid or gid 168 */ 169 uint32_t 170 hammer2_to_unix_xid(const uuid_t *uuid) 171 { 172 return(*(const uint32_t *)&uuid->node[2]); 173 } 174 175 void 176 hammer2_guid_to_uuid(uuid_t *uuid, uint32_t guid) 177 { 178 bzero(uuid, sizeof(*uuid)); 179 *(uint32_t *)&uuid->node[2] = guid; 180 } 181 182 /* 183 * Borrow HAMMER1's directory hash algorithm #1 with a few modifications. 184 * The filename is split into fields which are hashed separately and then 185 * added together. 186 * 187 * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets 188 * it to 0), this is because bit63=0 is used for hidden hardlinked inodes. 189 * (This means we do not need to do a 0-check/or-with-0x100000000 either). 190 * 191 * Also, the iscsi crc code is used instead of the old crc32 code. 192 */ 193 hammer2_key_t 194 hammer2_dirhash(const char *aname, size_t len) 195 { 196 uint32_t crcx; 197 uint64_t key; 198 size_t i; 199 size_t j; 200 201 key = 0; 202 203 /* 204 * m32 205 */ 206 crcx = 0; 207 for (i = j = 0; i < len; ++i) { 208 if (aname[i] == '.' || 209 aname[i] == '-' || 210 aname[i] == '_' || 211 aname[i] == '~') { 212 if (i != j) 213 crcx += hammer2_icrc32(aname + j, i - j); 214 j = i + 1; 215 } 216 } 217 if (i != j) 218 crcx += hammer2_icrc32(aname + j, i - j); 219 220 /* 221 * The directory hash utilizes the top 32 bits of the 64-bit key. 222 * Bit 63 must be set to 1. 223 */ 224 crcx |= 0x80000000U; 225 key |= (uint64_t)crcx << 32; 226 227 /* 228 * l16 - crc of entire filename 229 * 230 * This crc reduces degenerate hash collision conditions. 231 */ 232 crcx = hammer2_icrc32(aname, len); 233 crcx = crcx ^ (crcx << 16); 234 key |= crcx & 0xFFFF0000U; 235 236 /* 237 * Set bit 15. This allows readdir to strip bit 63 so a positive 238 * 64-bit cookie/offset can always be returned, and still guarantee 239 * that the values 0x0000-0x7FFF are available for artificial entries. 240 * ('.' and '..'). 241 */ 242 key |= 0x8000U; 243 244 return (key); 245 } 246 247 /* 248 * Convert bytes to radix with no limitations. 249 * 250 * 0 bytes is special-cased to a radix of zero (which would normally 251 * translate to (1 << 0) == 1). 252 */ 253 int 254 hammer2_getradix(size_t bytes) 255 { 256 int radix; 257 258 /* 259 * Optimize the iteration by pre-checking commonly used radixes. 260 */ 261 if (bytes == HAMMER2_PBUFSIZE) 262 radix = HAMMER2_PBUFRADIX; 263 else if (bytes >= HAMMER2_LBUFSIZE) 264 radix = HAMMER2_LBUFRADIX; 265 else if (bytes >= HAMMER2_ALLOC_MIN) /* clamp */ 266 radix = HAMMER2_RADIX_MIN; 267 else 268 radix = 0; 269 270 /* 271 * Iterate as needed. Note that bytes == 0 is expected to return 272 * a radix of 0 as a special case. 273 */ 274 while (((size_t)1 << radix) < bytes) 275 ++radix; 276 return (radix); 277 } 278 279 /* 280 * The logical block size is currently always PBUFSIZE. 281 */ 282 int 283 hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff, 284 hammer2_key_t *lbasep, hammer2_key_t *leofp) 285 { 286 if (lbasep) 287 *lbasep = uoff & ~HAMMER2_PBUFMASK64; 288 if (leofp) { 289 *leofp = (ip->meta.size + HAMMER2_PBUFMASK64) & 290 ~HAMMER2_PBUFMASK64; 291 } 292 return (HAMMER2_PBUFSIZE); 293 } 294 295 /* 296 * Calculate the physical block size. pblksize <= lblksize. Primarily 297 * used to calculate a smaller physical block for the logical block 298 * containing the file EOF. 299 * 300 * Returns 0 if the requested base offset is beyond the file EOF. 301 */ 302 int 303 hammer2_calc_physical(hammer2_inode_t *ip, hammer2_key_t lbase) 304 { 305 int lblksize; 306 int pblksize; 307 int eofbytes; 308 309 lblksize = hammer2_calc_logical(ip, lbase, NULL, NULL); 310 if (lbase + lblksize <= ip->meta.size) 311 return (lblksize); 312 if (lbase >= ip->meta.size) 313 return (0); 314 eofbytes = (int)(ip->meta.size - lbase); 315 pblksize = lblksize; 316 while (pblksize >= eofbytes && pblksize >= HAMMER2_ALLOC_MIN) 317 pblksize >>= 1; 318 pblksize <<= 1; 319 320 return (pblksize); 321 } 322 323 extern fsnode *hammer2_curnode; 324 325 static void 326 hammer2_get_curtime(uint64_t *timep) 327 { 328 struct timeval ts; 329 int error; 330 331 error = gettimeofday(&ts, NULL); 332 KKASSERT(error == 0); 333 *timep = (unsigned long)ts.tv_sec * 1000000 + ts.tv_usec; 334 } 335 336 void 337 hammer2_update_time(uint64_t *timep, bool is_mtime) 338 { 339 struct timespec *ts; 340 struct stat *st; 341 342 /* HAMMER2 ioctl commands */ 343 if (hammer2_curnode == NULL) { 344 hammer2_get_curtime(timep); 345 return; 346 } 347 348 st = stampst.st_ino != 0 ? &stampst : &hammer2_curnode->inode->st; 349 ts = is_mtime ? &st->st_mtim : &st->st_ctim; 350 351 *timep = (uint64_t)ts->tv_sec * 1000000 + ts->tv_nsec / 1000; 352 } 353 354 void 355 hammer2_adjreadcounter(int btype, size_t bytes) 356 { 357 long *counterp; 358 359 switch(btype) { 360 case HAMMER2_BREF_TYPE_DATA: 361 counterp = &hammer2_iod_file_read; 362 break; 363 case HAMMER2_BREF_TYPE_DIRENT: 364 case HAMMER2_BREF_TYPE_INODE: 365 counterp = &hammer2_iod_meta_read; 366 break; 367 case HAMMER2_BREF_TYPE_INDIRECT: 368 counterp = &hammer2_iod_indr_read; 369 break; 370 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 371 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 372 counterp = &hammer2_iod_fmap_read; 373 break; 374 case HAMMER2_BREF_TYPE_FREEMAP: 375 case HAMMER2_BREF_TYPE_VOLUME: 376 counterp = &hammer2_iod_volu_read; 377 break; 378 case HAMMER2_BREF_TYPE_EMPTY: 379 default: 380 return; 381 } 382 *counterp += bytes; 383 } 384 385 void 386 hammer2_adjwritecounter(int btype, size_t bytes) 387 { 388 long *counterp; 389 390 switch(btype) { 391 case HAMMER2_BREF_TYPE_DATA: 392 counterp = &hammer2_iod_file_write; 393 break; 394 case HAMMER2_BREF_TYPE_DIRENT: 395 case HAMMER2_BREF_TYPE_INODE: 396 counterp = &hammer2_iod_meta_write; 397 break; 398 case HAMMER2_BREF_TYPE_INDIRECT: 399 counterp = &hammer2_iod_indr_write; 400 break; 401 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 402 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 403 counterp = &hammer2_iod_fmap_write; 404 break; 405 case HAMMER2_BREF_TYPE_FREEMAP: 406 case HAMMER2_BREF_TYPE_VOLUME: 407 counterp = &hammer2_iod_volu_write; 408 break; 409 case HAMMER2_BREF_TYPE_EMPTY: 410 default: 411 return; 412 } 413 *counterp += bytes; 414 } 415 416 /* 417 * Check for pending signal to allow interruption. This function will 418 * return immediately if the calling thread is a kernel thread and not 419 * a user thread. 420 */ 421 int 422 hammer2_signal_check(time_t *timep) 423 { 424 #if 0 425 thread_t td = curthread; 426 int error = 0; 427 428 if (td->td_lwp) { 429 lwkt_user_yield(); 430 if (*timep != time_second) { 431 *timep = time_second; 432 if (CURSIG_NOBLOCK(curthread->td_lwp) != 0) 433 error = HAMMER2_ERROR_ABORTED; 434 } 435 } else { 436 lwkt_yield(); 437 } 438 return error; 439 #endif 440 return 0; 441 } 442 443 const char * 444 hammer2_error_str(int error) 445 { 446 if (error & HAMMER2_ERROR_EIO) 447 return("I/O Error"); 448 if (error & HAMMER2_ERROR_CHECK) 449 return("Check Error"); 450 if (error & HAMMER2_ERROR_INCOMPLETE) 451 return("Cluster Quorum Error"); 452 if (error & HAMMER2_ERROR_DEPTH) 453 return("Chain Depth Error"); 454 if (error & HAMMER2_ERROR_BADBREF) 455 return("Bad Blockref Error"); 456 if (error & HAMMER2_ERROR_ENOSPC) 457 return("No Space on Device"); 458 if (error & HAMMER2_ERROR_ENOENT) 459 return("Entry Not Found"); 460 if (error & HAMMER2_ERROR_ENOTEMPTY) 461 return("Directory Not Empty"); 462 if (error & HAMMER2_ERROR_EAGAIN) 463 return("EAGAIN"); 464 if (error & HAMMER2_ERROR_ENOTDIR) 465 return("Not a Directory"); 466 if (error & HAMMER2_ERROR_EISDIR) 467 return("Is a Directory"); 468 if (error & HAMMER2_ERROR_EINPROGRESS) 469 return("Operation in Progress"); 470 if (error & HAMMER2_ERROR_ABORTED) 471 return("Operation Aborted"); 472 if (error & HAMMER2_ERROR_EOF) 473 return("Operation Complete"); 474 if (error & HAMMER2_ERROR_EINVAL) 475 return("Invalid Operation"); 476 if (error & HAMMER2_ERROR_EEXIST) 477 return("Object Exists"); 478 if (error & HAMMER2_ERROR_EDEADLK) 479 return("Deadlock Detected"); 480 if (error & HAMMER2_ERROR_ESRCH) 481 return("Object Not Found"); 482 if (error & HAMMER2_ERROR_ETIMEDOUT) 483 return("Timeout"); 484 return("Unknown Error"); 485 } 486 487 const char * 488 hammer2_bref_type_str(int btype) 489 { 490 switch(btype) { 491 case HAMMER2_BREF_TYPE_EMPTY: 492 return("empty"); 493 case HAMMER2_BREF_TYPE_INODE: 494 return("inode"); 495 case HAMMER2_BREF_TYPE_INDIRECT: 496 return("indirect"); 497 case HAMMER2_BREF_TYPE_DATA: 498 return("data"); 499 case HAMMER2_BREF_TYPE_DIRENT: 500 return("dirent"); 501 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 502 return("freemap_node"); 503 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 504 return("freemap_leaf"); 505 case HAMMER2_BREF_TYPE_INVALID: 506 return("invalid"); 507 case HAMMER2_BREF_TYPE_FREEMAP: 508 return("freemap"); 509 case HAMMER2_BREF_TYPE_VOLUME: 510 return("volume"); 511 default: 512 return("unknown"); 513 } 514 } 515