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