1 /* 2 * Copyright 1999-2001, Be Incorporated. All Rights Reserved. 3 * Copyright 2024, Haiku, Inc. All rights reserved. 4 * This file may be used under the terms of the Be Sample Code License. 5 */ 6 7 8 /* 9 The FAT file system has no good way of assigning unique persistent values to 10 nodes. The only obvious choice, storing the starting cluster number of the 11 file, is unusable because 0 byte files exist as directory entries only. 12 Further, even if it were usable, it would potentially require a full directory 13 tree traversal to locate an arbitrary node. We must resort to some ickiness 14 in order to make persistent vnode id's (at least across a given mount) work. 15 16 There are three ways to encode a vnode id: 17 18 1. Combine the starting cluster of the entry with the starting cluster of the 19 directory it appears in. This is used for files with data. 20 2. Combine the starting cluster of the directory the entry appears in with the 21 index of the entry in the directory. This is used for 0-byte files. 22 3. A unique number that doesn't match any possible values from encodings 1 or 23 2. 24 25 With the first encoding, the vnode id is invalidated (i.e. no longer describes 26 the file's location) when the file moves to a different directory or when 27 its starting cluster changes (this can occur if the file is truncated and data 28 is subsequently written to it). 29 30 With the second encoding, the vnode id is invalidated when the file position 31 is moved within a directory (as a result of a renaming), when it's moved to a 32 different directory, or when data is written to it. 33 34 The third encoding doesn't describe the file's location on disk, and so it is 35 invalid from the start. 36 37 Since we can't change vnode id's once they are assigned, we have to create a 38 mapping table to translate vnode id's to locations. This file serves this 39 purpose. 40 41 It also allows the FS to check whether a node with a given inode number has been constructed, 42 without calling get_vnode(). 43 */ 44 45 46 #include "vcache.h" 47 48 #ifdef FS_SHELL 49 #include "fssh_api_wrapper.h" 50 #else // !FS_SHELL 51 #include <new> 52 #include <stdint.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #endif // !FS_SHELL 56 57 #ifndef FS_SHELL 58 #include <fs_cache.h> 59 #include <fs_interface.h> 60 #endif // !FS_SHELL 61 62 #define _KERNEL 63 #include "sys/vnode.h" 64 65 #include "debug.h" 66 #include "dosfs.h" 67 68 69 #define LOCK_CACHE_R \ 70 rw_lock_read_lock(&vol->vcache.lock) 71 72 #define LOCK_CACHE_W \ 73 rw_lock_write_lock(&vol->vcache.lock) 74 75 #define UNLOCK_CACHE_R \ 76 rw_lock_read_unlock(&vol->vcache.lock) 77 78 #define UNLOCK_CACHE_W \ 79 rw_lock_write_unlock(&vol->vcache.lock) 80 81 #define hash(v) ((v) & (vol->vcache.cache_size-1)) 82 83 84 struct vcache_entry { 85 ino_t vnid; /* originally reported vnid */ 86 ino_t loc; /* where the file is now */ 87 bool constructed; /* has the node been set up by _dosfs_read_vnode */ 88 struct vcache_entry *next_vnid; /* next entry in vnid hash table */ 89 struct vcache_entry *next_loc; /* next entry in location hash table */ 90 #ifdef DEBUG 91 struct vnode* node; 92 #endif 93 }; 94 95 96 void 97 dump_vcache(const mount* vol) 98 { 99 uint32 i; 100 struct vcache_entry *c; 101 #if defined DEBUG && defined _KERNEL_MODE 102 kprintf("vnid cache size %" B_PRIu32 ", cur vnid = %" B_PRIdINO "\n" 103 "vnid loc %-*s\n", vol->vcache.cache_size, vol->vcache.cur_vnid, 104 B_PRINTF_POINTER_WIDTH, "struct vnode"); 105 for (i = 0; i < vol->vcache.cache_size; i++) { 106 for (c = vol->vcache.by_vnid[i]; c ; c = c->next_vnid) 107 kprintf("%19" B_PRIdINO " %16" B_PRIdINO " %p\n", c->vnid, c->loc, c->node); 108 } 109 #else 110 kprintf("vnid cache size %" B_PRIu32 ", cur vnid = %" B_PRIdINO "\n" 111 "vnid loc\n", vol->vcache.cache_size, vol->vcache.cur_vnid); 112 for (i = 0; i < vol->vcache.cache_size; i++) { 113 for (c = vol->vcache.by_vnid[i]; c ; c = c->next_vnid) 114 kprintf("%19" B_PRIdINO " %16" B_PRIdINO "\n", c->vnid, c->loc); 115 } 116 #endif // !DEBUG 117 } 118 119 120 status_t 121 init_vcache(mount* vol) 122 { 123 char name[16]; 124 FUNCTION(); 125 126 vol->vcache.cur_vnid = ARTIFICIAL_VNID_BITS; 127 #if DEBUG 128 vol->vcache.cache_size = 1; 129 #else 130 vol->vcache.cache_size = 512; /* must be power of 2 */ 131 #endif 132 133 vol->vcache.by_vnid = (vcache_entry**)calloc(sizeof(struct vache_entry *), 134 vol->vcache.cache_size); 135 if (vol->vcache.by_vnid == NULL) { 136 INFORM("init_vcache: out of memory\n"); 137 return ENOMEM; 138 } 139 140 vol->vcache.by_loc = (vcache_entry**)calloc(sizeof(struct vache_entry *), 141 vol->vcache.cache_size); 142 if (vol->vcache.by_loc == NULL) { 143 INFORM("init_vcache: out of memory\n"); 144 free(vol->vcache.by_vnid); 145 vol->vcache.by_vnid = NULL; 146 return ENOMEM; 147 } 148 149 sprintf(name, "fat cache %" B_PRIdDEV, vol->mnt_fsvolume->id); 150 rw_lock_init(&vol->vcache.lock, "fat cache"); 151 152 PRINT("init_vcache: initialized vnid cache with %" B_PRIu32 153 " entries\n", vol->vcache.cache_size); 154 155 return 0; 156 } 157 158 159 status_t 160 uninit_vcache(mount* vol) 161 { 162 uint32 i, count = 0; 163 struct vcache_entry *c, *n; 164 FUNCTION(); 165 166 LOCK_CACHE_W; 167 168 /* free entries */ 169 for (i = 0; i < vol->vcache.cache_size; i++) { 170 c = vol->vcache.by_vnid[i]; 171 while (c) { 172 count++; 173 n = c->next_vnid; 174 free(c); 175 c = n; 176 } 177 } 178 179 PRINT("%" B_PRIu32 " vcache entries removed\n", count); 180 181 free(vol->vcache.by_vnid); vol->vcache.by_vnid = NULL; 182 free(vol->vcache.by_loc); vol->vcache.by_loc = NULL; 183 184 rw_lock_destroy(&vol->vcache.lock); 185 return B_OK; 186 } 187 188 189 ino_t 190 generate_unique_vnid(mount* vol) 191 { 192 FUNCTION(); 193 194 #ifdef FS_SHELL 195 LOCK_CACHE_W; 196 ino_t current = vol->vcache.cur_vnid++; 197 UNLOCK_CACHE_W; 198 199 return current; 200 #else 201 return atomic_add64(&vol->vcache.cur_vnid, 1); 202 #endif // !FS_SHELL 203 } 204 205 206 static status_t 207 _add_to_vcache_(mount* vol, ino_t vnid, ino_t loc) 208 { 209 int hash1 = hash(vnid), hash2 = hash(loc); 210 struct vcache_entry *e, *c, *p; 211 212 FUNCTION_START("%" B_PRIdINO "/%" B_PRIdINO "\n", vnid, loc); 213 214 e = (vcache_entry*)malloc(sizeof(struct vcache_entry)); 215 if (e == NULL) 216 return ENOMEM; 217 218 e->vnid = vnid; e->loc = loc; e->constructed = false; e->next_vnid = NULL; e->next_loc = NULL; 219 220 c = p = vol->vcache.by_vnid[hash1]; 221 while (c) { 222 if (vnid < c->vnid) 223 break; 224 ASSERT(vnid != c->vnid); ASSERT(loc != c->loc); 225 p = c; 226 c = c->next_vnid; 227 } 228 ASSERT(!c || (vnid != c->vnid)); 229 230 e->next_vnid = c; 231 if (p == c) 232 vol->vcache.by_vnid[hash1] = e; 233 else 234 p->next_vnid = e; 235 236 c = p = vol->vcache.by_loc[hash2]; 237 while (c) { 238 if (loc < c->loc) 239 break; 240 ASSERT(vnid != c->vnid); ASSERT(loc != c->loc); 241 p = c; 242 c = c->next_loc; 243 } 244 ASSERT(!c || (loc != c->loc)); 245 246 e->next_loc = c; 247 if (p == c) 248 vol->vcache.by_loc[hash2] = e; 249 else 250 p->next_loc = e; 251 252 return B_OK; 253 } 254 255 256 static status_t 257 _remove_from_vcache_(mount* vol, ino_t vnid) 258 { 259 int hash1 = hash(vnid), hash2; 260 struct vcache_entry *c, *p, *e; 261 262 FUNCTION_START("%" B_PRIdINO "\n", vnid); 263 264 c = p = vol->vcache.by_vnid[hash1]; 265 while (c) { 266 if (vnid == c->vnid) 267 break; 268 ASSERT(c->vnid < vnid); 269 p = c; 270 c = c->next_vnid; 271 } 272 ASSERT(c); 273 if (!c) return ENOENT; 274 275 if (p == c) 276 vol->vcache.by_vnid[hash1] = c->next_vnid; 277 else 278 p->next_vnid = c->next_vnid; 279 280 e = c; 281 282 hash2 = hash(c->loc); 283 c = p = vol->vcache.by_loc[hash2]; 284 285 while (c) { 286 if (vnid == c->vnid) 287 break; 288 ASSERT(c->loc < e->loc); 289 p = c; 290 c = c->next_loc; 291 } 292 ASSERT(c); 293 if (!c) return ENOENT; 294 if (p == c) 295 vol->vcache.by_loc[hash2] = c->next_loc; 296 else 297 p->next_loc = c->next_loc; 298 299 free(c); 300 301 return 0; 302 } 303 304 305 static struct vcache_entry * 306 _find_vnid_in_vcache_(mount* vol, ino_t vnid) 307 { 308 int hash1 = hash(vnid); 309 struct vcache_entry *c; 310 c = vol->vcache.by_vnid[hash1]; 311 while (c) { 312 if (c->vnid == vnid) 313 break; 314 if (c->vnid > vnid) 315 return NULL; 316 c = c->next_vnid; 317 } 318 319 return c; 320 } 321 322 323 static struct vcache_entry * 324 _find_loc_in_vcache_(mount* vol, ino_t loc) 325 { 326 int hash2 = hash(loc); 327 struct vcache_entry *c; 328 c = vol->vcache.by_loc[hash2]; 329 while (c) { 330 if (c->loc == loc) 331 break; 332 if (c->loc > loc) 333 return NULL; 334 c = c->next_loc; 335 } 336 337 return c; 338 } 339 340 341 status_t 342 add_to_vcache(mount* vol, ino_t vnid, ino_t loc) 343 { 344 status_t result; 345 346 LOCK_CACHE_W; 347 result = _add_to_vcache_(vol,vnid,loc); 348 UNLOCK_CACHE_W; 349 350 if (result < 0) INFORM("add_to_vcache failed (%s)\n", strerror(result)); 351 return result; 352 } 353 354 355 static status_t 356 _update_loc_in_vcache_(mount* vol, ino_t vnid, ino_t loc) 357 { 358 status_t result; 359 bool constructed; 360 result = vcache_get_constructed(vol, vnid, &constructed); 361 if (result != B_OK) 362 return result; 363 #if DEBUG 364 struct vnode *node; 365 result = vcache_get_node(vol, vnid, &node); 366 if (result != B_OK) 367 return result; 368 #endif // DEBUG 369 370 result = _remove_from_vcache_(vol, vnid); 371 if (result == 0) { 372 result = _add_to_vcache_(vol, vnid, loc); 373 if (result == B_OK && constructed == true) 374 result = vcache_set_constructed(vol, vnid); 375 #if DEBUG 376 if (result == B_OK) 377 result = vcache_set_node(vol, vnid, node); 378 #endif // DEBUG 379 } 380 381 return result; 382 } 383 384 385 status_t 386 remove_from_vcache(mount* vol, ino_t vnid) 387 { 388 status_t result; 389 390 LOCK_CACHE_W; 391 result = _remove_from_vcache_(vol, vnid); 392 UNLOCK_CACHE_W; 393 394 if (result < 0) INFORM("remove_from_vcache failed (%s)\n", strerror(result)); 395 return result; 396 } 397 398 399 status_t 400 vcache_vnid_to_loc(mount* vol, ino_t vnid, ino_t* loc) 401 { 402 struct vcache_entry *e; 403 404 FUNCTION_START("%" B_PRIdINO " %p\n", vnid, loc); 405 406 LOCK_CACHE_R; 407 e = _find_vnid_in_vcache_(vol, vnid); 408 if (loc && e) 409 *loc = e->loc; 410 UNLOCK_CACHE_R; 411 412 return (e) ? B_OK : ENOENT; 413 } 414 415 416 status_t 417 vcache_loc_to_vnid(mount* vol, ino_t loc, ino_t* vnid) 418 { 419 struct vcache_entry *e; 420 421 FUNCTION_START("%" B_PRIdINO " %p\n", loc, vnid); 422 423 LOCK_CACHE_R; 424 e = _find_loc_in_vcache_(vol, loc); 425 if (vnid && e) 426 *vnid = e->vnid; 427 UNLOCK_CACHE_R; 428 429 return (e) ? B_OK : ENOENT; 430 } 431 432 433 status_t 434 vcache_set_entry(mount* vol, ino_t vnid, ino_t loc) 435 { 436 struct vcache_entry *e; 437 status_t result = B_OK; 438 439 FUNCTION_START("vcache_set_entry: %" B_PRIdINO " -> %" B_PRIdINO "\n", vnid, loc); 440 441 LOCK_CACHE_W; 442 443 e = _find_vnid_in_vcache_(vol, vnid); 444 445 if (e) 446 result = _update_loc_in_vcache_(vol, vnid, loc); 447 else 448 result = _add_to_vcache_(vol,vnid,loc); 449 450 UNLOCK_CACHE_W; 451 452 return result; 453 } 454 455 456 status_t 457 vcache_set_constructed(mount* vol, ino_t vnid, bool constructed) 458 { 459 struct vcache_entry* cEntry; 460 status_t result = B_OK; 461 462 FUNCTION_START("%" B_PRIdINO "\n", vnid); 463 464 LOCK_CACHE_W; 465 466 cEntry = _find_vnid_in_vcache_(vol, vnid); 467 468 if (cEntry != NULL) 469 cEntry->constructed = constructed; 470 else 471 result = B_BAD_VALUE; 472 473 UNLOCK_CACHE_W; 474 475 return result; 476 } 477 478 479 status_t 480 vcache_get_constructed(mount* vol, ino_t vnid, bool* constructed) 481 { 482 struct vcache_entry* cEntry; 483 status_t result = B_OK; 484 485 FUNCTION_START("%" B_PRIdINO "\n", vnid); 486 487 LOCK_CACHE_W; 488 489 cEntry = _find_vnid_in_vcache_(vol, vnid); 490 491 if (cEntry != NULL) 492 *constructed = cEntry->constructed; 493 else 494 result = B_BAD_VALUE; 495 496 UNLOCK_CACHE_W; 497 498 return result; 499 } 500 501 502 status_t 503 sync_all_files(mount* vol) 504 { 505 uint32 count = 0, errors = 0; 506 vnode* bsdNode = NULL; 507 status_t status = B_OK; 508 509 FUNCTION_START("volume @ %p\n", vol); 510 511 LOCK_CACHE_R; 512 513 for (uint32 i = 0; i < vol->vcache.cache_size; i++) { 514 for (vcache_entry* cEntry = vol->vcache.by_vnid[i]; cEntry != NULL ; 515 cEntry = cEntry->next_vnid) { 516 if (cEntry->constructed == true) { 517 status = get_vnode(vol->mnt_fsvolume, cEntry->vnid, 518 reinterpret_cast<void**>(&bsdNode)); 519 if (status != B_OK) { 520 INFORM("get_vnode: %s\n", strerror(status)); 521 errors++; 522 } else { 523 if (bsdNode->v_type == VREG) { 524 status = file_cache_sync(bsdNode->v_cache); 525 if (status != B_OK) 526 errors++; 527 else 528 count++; 529 } 530 put_vnode(vol->mnt_fsvolume, cEntry->vnid); 531 } 532 } 533 } 534 } 535 536 UNLOCK_CACHE_R; 537 538 PRINT("sync'd %" B_PRIu32 " files with %" B_PRIu32 " errors\n", count, errors); 539 540 return (errors == 0) ? B_OK : B_ERROR; 541 } 542 543 544 #if DEBUG 545 status_t 546 vcache_set_node(mount* vol, ino_t vnid, struct vnode* node) 547 { 548 vcache_entry* cEntry; 549 status_t result = B_OK; 550 551 FUNCTION_START("%" B_PRIdINO "\n", vnid); 552 553 LOCK_CACHE_W; 554 555 cEntry = _find_vnid_in_vcache_(vol, vnid); 556 557 if (cEntry != NULL) 558 cEntry->node = node; 559 else 560 result = B_BAD_VALUE; 561 562 UNLOCK_CACHE_W; 563 564 return result; 565 } 566 567 568 status_t 569 vcache_get_node(mount* vol, ino_t vnid, struct vnode** node) 570 { 571 vcache_entry* cEntry; 572 status_t result = B_OK; 573 574 FUNCTION_START("%" B_PRIdINO "\n", vnid); 575 576 LOCK_CACHE_W; 577 578 cEntry = _find_vnid_in_vcache_(vol, vnid); 579 580 if (cEntry != NULL) 581 *node = cEntry->node; 582 else 583 result = B_BAD_VALUE; 584 585 UNLOCK_CACHE_W; 586 587 return result; 588 } 589 590 591 int 592 debug_dfvnid(int argc, char **argv) 593 { 594 int i; 595 mount* vol; 596 597 if (argc < 3) { 598 kprintf("dfvnid volume vnid\n"); 599 return B_OK; 600 } 601 602 vol = reinterpret_cast<mount*>(strtoul(argv[1], NULL, 0)); 603 if (vol == NULL) 604 return B_OK; 605 606 for (i = 2; i < argc; i++) { 607 ino_t vnid = strtoull(argv[i], NULL, 0); 608 struct vcache_entry *e; 609 if ((e = _find_vnid_in_vcache_(vol, vnid)) != NULL) { 610 kprintf("vnid %" B_PRIdINO " -> loc %" B_PRIdINO " @ %p\n", vnid, 611 e->loc, e); 612 } else { 613 kprintf("vnid %" B_PRIdINO " not found in vnid cache\n", vnid); 614 } 615 } 616 617 return B_OK; 618 } 619 620 621 int 622 debug_dfloc(int argc, char **argv) 623 { 624 int i; 625 mount* vol; 626 627 if (argc < 3) { 628 kprintf("dfloc volume vnid\n"); 629 return B_OK; 630 } 631 632 vol = reinterpret_cast<mount*>(strtoul(argv[1], NULL, 0)); 633 if (vol == NULL) 634 return B_OK; 635 636 for (i = 2; i < argc; i++) { 637 ino_t loc = strtoull(argv[i], NULL, 0); 638 struct vcache_entry *e; 639 if ((e = _find_loc_in_vcache_(vol, loc)) != NULL) { 640 kprintf("loc %" B_PRIdINO " -> vnid %" B_PRIdINO " @ %p\n", loc, 641 e->vnid, e); 642 } else { 643 kprintf("loc %" B_PRIdINO " not found in vnid cache\n", loc); 644 } 645 } 646 647 return B_OK; 648 } 649 #endif // DEBUG 650