1 /* 2 * Copyright 2017, Chế Vũ Gia Hy, cvghy116@gmail.com. 3 * Copyright 2011, Jérôme Duval, korli@users.berlios.de. 4 * Copyright 2008-2014, Axel Dörfler, axeld@pinc-software.de. 5 * Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. 6 * This file may be used under the terms of the MIT License. 7 */ 8 9 10 #include "Inode.h" 11 #include "CachedBlock.h" 12 #include "CRCTable.h" 13 #include "Utility.h" 14 15 16 #undef ASSERT 17 //#define TRACE_BTRFS 18 #ifdef TRACE_BTRFS 19 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x) 20 # define ASSERT(x) { if (!(x)) kernel_debugger("btrfs: assert failed: " #x "\n"); } 21 #else 22 # define TRACE(x...) ; 23 # define ASSERT(x) ; 24 #endif 25 #define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x) 26 27 28 Inode::Inode(Volume* volume, ino_t id) 29 : 30 fVolume(volume), 31 fID(id), 32 fCache(NULL), 33 fMap(NULL) 34 { 35 rw_lock_init(&fLock, "btrfs inode"); 36 37 fInitStatus = UpdateNodeFromDisk(); 38 if (fInitStatus == B_OK) { 39 if (!IsDirectory() && !IsSymLink()) { 40 fCache = file_cache_create(fVolume->ID(), ID(), Size()); 41 fMap = file_map_create(fVolume->ID(), ID(), Size()); 42 } 43 } 44 } 45 46 47 Inode::Inode(Volume* volume, ino_t id, const btrfs_inode& item) 48 : 49 fVolume(volume), 50 fID(id), 51 fCache(NULL), 52 fMap(NULL), 53 fInitStatus(B_OK), 54 fNode(item) 55 { 56 if (!IsDirectory() && !IsSymLink()) { 57 fCache = file_cache_create(fVolume->ID(), ID(), Size()); 58 fMap = file_map_create(fVolume->ID(), ID(), Size()); 59 } 60 } 61 62 63 Inode::Inode(Volume* volume) 64 : 65 fVolume(volume), 66 fID(0), 67 fCache(NULL), 68 fMap(NULL), 69 fInitStatus(B_NO_INIT) 70 { 71 rw_lock_init(&fLock, "btrfs inode"); 72 } 73 74 75 Inode::~Inode() 76 { 77 TRACE("Inode destructor\n"); 78 file_cache_delete(FileCache()); 79 file_map_delete(Map()); 80 TRACE("Inode destructor: Done\n"); 81 } 82 83 84 status_t 85 Inode::InitCheck() 86 { 87 return fInitStatus; 88 } 89 90 91 status_t 92 Inode::UpdateNodeFromDisk() 93 { 94 btrfs_key search_key; 95 search_key.SetType(BTRFS_KEY_TYPE_INODE_ITEM); 96 search_key.SetObjectID(fID); 97 search_key.SetOffset(0); 98 BTree::Path path(fVolume->FSTree()); 99 100 btrfs_inode* node; 101 if (fVolume->FSTree()->FindExact(&path, search_key, (void**)&node) 102 != B_OK) { 103 ERROR("Inode::UpdateNodeFromDisk(): Couldn't find inode %" 104 B_PRIdINO "\n", fID); 105 return B_ENTRY_NOT_FOUND; 106 } 107 108 memcpy(&fNode, node, sizeof(btrfs_inode)); 109 free(node); 110 return B_OK; 111 } 112 113 114 /* 115 * Create new Inode object with inode_item 116 */ 117 Inode* 118 Inode::Create(Transaction& transaction, ino_t id, Inode* parent, int32 mode, 119 uint64 size, uint64 flags) 120 { 121 TRACE("Inode::Create() id % " B_PRIu64 " mode %" B_PRId32 " flags %" 122 B_PRIu64"\n", id, flags, mode); 123 124 Volume* volume = parent != NULL ? 125 parent->GetVolume() : transaction.GetJournal()->GetVolume(); 126 uint64 nbytes = size; // allocated size 127 if (size > volume->MaxInlineSize()) 128 nbytes = (size / volume->SectorSize() + 1) * volume->SectorSize(); 129 130 btrfs_inode inode; 131 132 inode.generation = B_HOST_TO_LENDIAN_INT64(transaction.SystemID()); 133 inode.transaction_id = B_HOST_TO_LENDIAN_INT64(transaction.SystemID()); 134 inode.size = B_HOST_TO_LENDIAN_INT64(size); 135 inode.nbytes = B_HOST_TO_LENDIAN_INT64(nbytes); 136 inode.blockgroup = 0; // normal inode only 137 inode.num_links = B_HOST_TO_LENDIAN_INT32(1); 138 inode.uid = B_HOST_TO_LENDIAN_INT32(geteuid()); 139 inode.gid = B_HOST_TO_LENDIAN_INT32(parent != NULL ? 140 parent->GroupID() : getegid()); 141 inode.mode = B_HOST_TO_LENDIAN_INT32(mode);; 142 inode.rdev = 0; // normal file only 143 inode.flags = B_HOST_TO_LENDIAN_INT64(flags); 144 inode.sequence = 0; // incremented each time mtime value is changed 145 146 uint64 now = real_time_clock_usecs(); 147 struct timespec timespec; 148 timespec.tv_sec = now / 1000000; 149 timespec.tv_nsec = (now % 1000000) * 1000; 150 btrfs_inode::SetTime(inode.access_time, timespec); 151 btrfs_inode::SetTime(inode.creation_time, timespec); 152 btrfs_inode::SetTime(inode.change_time, timespec); 153 btrfs_inode::SetTime(inode.modification_time, timespec); 154 155 return new Inode(volume, id, inode); 156 } 157 158 159 status_t 160 Inode::CheckPermissions(int accessMode) const 161 { 162 // you never have write access to a read-only volume 163 if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly()) 164 return B_READ_ONLY_DEVICE; 165 166 return check_access_permissions(accessMode, Mode(), (gid_t)fNode.GroupID(), 167 (uid_t)fNode.UserID()); 168 } 169 170 171 status_t 172 Inode::FindBlock(off_t pos, off_t& physical, off_t* _length) 173 { 174 btrfs_key search_key; 175 search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA); 176 search_key.SetObjectID(fID); 177 search_key.SetOffset(pos + 1); 178 BTree::Path path(fVolume->FSTree()); 179 180 btrfs_extent_data* extent_data; 181 status_t status = fVolume->FSTree()->FindPrevious(&path, search_key, 182 (void**)&extent_data); 183 if (status != B_OK) { 184 ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%" B_PRIx32 185 "\n", status); 186 return status; 187 } 188 189 TRACE("Inode::FindBlock(%" B_PRIdINO ") key.Offset() %" B_PRId64 "\n", 190 ID(), search_key.Offset()); 191 192 off_t diff = pos - search_key.Offset(); 193 off_t logical = 0; 194 if (extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR) 195 logical = diff + extent_data->disk_offset; 196 else 197 panic("unknown extent type; %d\n", extent_data->Type()); 198 status = fVolume->FindBlock(logical, physical); 199 if (_length != NULL) 200 *_length = extent_data->Size() - diff; 201 TRACE("Inode::FindBlock(%" B_PRIdINO ") %" B_PRIdOFF " physical %" 202 B_PRIdOFF "\n", ID(), pos, physical); 203 204 free(extent_data); 205 return status; 206 } 207 208 209 status_t 210 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) 211 { 212 size_t length = *_length; 213 214 // set/check boundaries for pos/length 215 if (pos < 0) { 216 ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %" B_PRIdOFF 217 ", length %lu)\n", ID(), pos, length); 218 return B_BAD_VALUE; 219 } 220 221 if (pos >= Size() || length == 0) { 222 TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %" B_PRIdOFF 223 ", length %lu)\n", ID(), pos, length); 224 *_length = 0; 225 return B_NO_ERROR; 226 } 227 228 // the file cache doesn't seem to like non block aligned file offset 229 // so we avoid the file cache for inline extents 230 btrfs_key search_key; 231 search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA); 232 search_key.SetObjectID(fID); 233 search_key.SetOffset(pos + 1); 234 BTree::Path path(fVolume->FSTree()); 235 236 uint32 item_size; 237 btrfs_extent_data* extent_data; 238 status_t status = fVolume->FSTree()->FindPrevious(&path, search_key, 239 (void**)&extent_data, &item_size); 240 if (status != B_OK) { 241 ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%" B_PRIx32 242 "\n", status); 243 return status; 244 } 245 MemoryDeleter deleter(extent_data); 246 247 248 uint8 compression = extent_data->Compression(); 249 if (FileCache() != NULL 250 && extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR) { 251 TRACE("inode %" B_PRIdINO ": ReadAt cache (pos %" B_PRIdOFF ", length %lu)\n", 252 ID(), pos, length); 253 if (compression == BTRFS_EXTENT_COMPRESS_NONE) 254 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 255 else if (compression == BTRFS_EXTENT_COMPRESS_ZLIB) 256 panic("zlib isn't unsupported for regular extent\n"); 257 else 258 panic("unknown extent compression; %d\n", compression); 259 return B_BAD_DATA; 260 } 261 262 TRACE("Inode::ReadAt(%" B_PRIdINO ") key.Offset() %" B_PRId64 "\n", ID(), 263 search_key.Offset()); 264 265 off_t diff = pos - search_key.Offset(); 266 if (extent_data->Type() != BTRFS_EXTENT_DATA_INLINE) { 267 panic("unknown extent type; %d\n", extent_data->Type()); 268 return B_BAD_DATA; 269 } 270 271 *_length = min_c(extent_data->Size() - diff, *_length); 272 if (compression == BTRFS_EXTENT_COMPRESS_NONE) 273 memcpy(buffer, extent_data->inline_data, *_length); 274 else if (compression == BTRFS_EXTENT_COMPRESS_ZLIB) { 275 char in[2048]; 276 z_stream zStream = { 277 (Bytef*)in, // next in 278 sizeof(in), // avail in 279 0, // total in 280 NULL, // next out 281 0, // avail out 282 0, // total out 283 0, // msg 284 0, // state 285 Z_NULL, // zalloc 286 Z_NULL, // zfree 287 Z_NULL, // opaque 288 0, // data type 289 0, // adler 290 0, // reserved 291 }; 292 293 int status; 294 ssize_t offset = 0; 295 uint32 inline_size = item_size - 13; 296 bool headerRead = false; 297 298 TRACE("Inode::ReadAt(%" B_PRIdINO ") diff %" B_PRIdOFF " size %" 299 B_PRIuSIZE "\n", ID(), diff, item_size); 300 301 do { 302 ssize_t bytesRead = min_c(sizeof(in), inline_size - offset); 303 if (bytesRead <= 0) { 304 status = Z_STREAM_ERROR; 305 break; 306 } 307 memcpy(in, extent_data->inline_data + offset, bytesRead); 308 309 zStream.avail_in = bytesRead; 310 zStream.next_in = (Bytef*)in; 311 312 if (!headerRead) { 313 headerRead = true; 314 315 zStream.avail_out = length; 316 zStream.next_out = (Bytef*)buffer; 317 318 status = inflateInit2(&zStream, 15); 319 if (status != Z_OK) { 320 return B_ERROR; 321 } 322 } 323 324 status = inflate(&zStream, Z_SYNC_FLUSH); 325 offset += bytesRead; 326 if (diff > 0) { 327 zStream.next_out -= max_c(bytesRead, diff); 328 diff -= max_c(bytesRead, diff); 329 } 330 331 if (zStream.avail_in != 0 && status != Z_STREAM_END) { 332 TRACE("Inode::ReadAt() didn't read whole block: %s\n", 333 zStream.msg); 334 } 335 } while (status == Z_OK); 336 337 inflateEnd(&zStream); 338 339 if (status != Z_STREAM_END) { 340 TRACE("Inode::ReadAt() inflating failed: %d!\n", status); 341 return B_BAD_DATA; 342 } 343 344 *_length = zStream.total_out; 345 346 } else { 347 panic("unknown extent compression; %d\n", compression); 348 return B_BAD_DATA; 349 } 350 return B_OK; 351 352 } 353 354 355 status_t 356 Inode::FindParent(ino_t* id) 357 { 358 btrfs_key search_key; 359 search_key.SetType(BTRFS_KEY_TYPE_INODE_REF); 360 search_key.SetObjectID(fID); 361 search_key.SetOffset(-1); 362 BTree::Path path(fVolume->FSTree()); 363 364 void* node_ref; 365 if (fVolume->FSTree()->FindPrevious(&path, search_key, &node_ref) != B_OK) { 366 ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO "\n", 367 fID); 368 return B_ERROR; 369 } 370 371 free(node_ref); 372 *id = search_key.Offset(); 373 TRACE("Inode::FindParent() for %" B_PRIdINO ": %" B_PRIdINO "\n", fID, 374 *id); 375 376 return B_OK; 377 } 378 379 380 uint64 381 Inode::FindNextIndex(BTree::Path* path) const 382 { 383 btrfs_key key; 384 key.SetObjectID(fID); 385 key.SetType(BTRFS_KEY_TYPE_DIR_INDEX); 386 key.SetOffset(-1); 387 388 if (fVolume->FSTree()->FindPrevious(path, key, NULL)) 389 return 2; // not found any dir index item 390 391 return key.Offset() + 1; 392 } 393 394 395 /* Insert inode_item 396 */ 397 status_t 398 Inode::Insert(Transaction& transaction, BTree::Path* path) 399 { 400 BTree* tree = path->Tree(); 401 402 btrfs_entry item; 403 item.key.SetObjectID(fID); 404 item.key.SetType(BTRFS_KEY_TYPE_INODE_ITEM); 405 item.key.SetOffset(0); 406 item.SetSize(sizeof(btrfs_inode)); 407 408 void* data[1]; 409 data[0] = (void*)&fNode; 410 status_t status = tree->InsertEntries(transaction, path, &item, data, 1); 411 if (status != B_OK) 412 return status; 413 414 return B_OK; 415 } 416 417 418 /* Remove inode_item 419 */ 420 status_t 421 Inode::Remove(Transaction& transaction, BTree::Path* path) 422 { 423 BTree* tree = path->Tree(); 424 btrfs_key key; 425 key.SetObjectID(fID); 426 key.SetType(BTRFS_KEY_TYPE_INODE_ITEM); 427 key.SetOffset(0); 428 status_t status = tree->RemoveEntries(transaction, path, key, NULL, 1); 429 if (status != B_OK) 430 return status; 431 432 return B_OK; 433 } 434 435 436 /* Insert 3 items: inode_ref, dir_item, dir_index 437 * Basically, make a link between name and its node (location) 438 */ 439 status_t 440 Inode::MakeReference(Transaction& transaction, BTree::Path* path, 441 Inode* parent, const char* name, int32 mode) 442 { 443 BTree* tree = fVolume->FSTree(); 444 uint16 nameLength = strlen(name); 445 uint64 index = parent->FindNextIndex(path); 446 447 // insert inode_ref 448 btrfs_inode_ref* inodeRef = (btrfs_inode_ref*)malloc(sizeof(btrfs_inode_ref) 449 + nameLength); 450 if (inodeRef == NULL) 451 return B_NO_MEMORY; 452 inodeRef->index = index; 453 inodeRef->SetName(name, nameLength); 454 455 btrfs_entry entry; 456 entry.key.SetObjectID(fID); 457 entry.key.SetType(BTRFS_KEY_TYPE_INODE_REF); 458 entry.key.SetOffset(parent->ID()); 459 entry.SetSize(inodeRef->Length()); 460 461 status_t status = tree->InsertEntries(transaction, path, &entry, 462 (void**)&inodeRef, 1); 463 free(inodeRef); 464 if (status != B_OK) 465 return status; 466 467 // insert dir_entry 468 uint32 hash = calculate_crc((uint32)~1, (uint8*)name, nameLength); 469 btrfs_dir_entry* directoryEntry = 470 (btrfs_dir_entry*)malloc(sizeof(btrfs_dir_entry) + nameLength); 471 if (directoryEntry == NULL) 472 return B_NO_MEMORY; 473 directoryEntry->location.SetObjectID(fID); 474 directoryEntry->location.SetType(BTRFS_KEY_TYPE_INODE_ITEM); 475 directoryEntry->location.SetOffset(0); 476 directoryEntry->SetTransactionID(transaction.SystemID()); 477 // TODO: xattribute, 0 for standard directory 478 directoryEntry->SetName(name, nameLength); 479 directoryEntry->SetAttributeData(NULL, 0); 480 directoryEntry->type = get_filetype(mode); 481 482 entry.key.SetObjectID(parent->ID()); 483 entry.key.SetType(BTRFS_KEY_TYPE_DIR_ITEM); 484 entry.key.SetOffset(hash); 485 entry.SetSize(directoryEntry->Length()); 486 487 status = tree->InsertEntries(transaction, path, &entry, 488 (void**)&directoryEntry, 1); 489 if (status != B_OK) { 490 free(directoryEntry); 491 return status; 492 } 493 494 // insert dir_index (has same data with dir_entry) 495 entry.key.SetType(BTRFS_KEY_TYPE_DIR_INDEX); 496 entry.key.SetOffset(index); 497 498 status = tree->InsertEntries(transaction, path, &entry, 499 (void**)&directoryEntry, 1); 500 if (status != B_OK) { 501 free(directoryEntry); 502 return status; 503 } 504 505 free(directoryEntry); 506 return B_OK; 507 } 508 509 510 // Remove the "name" and unlink it with inode. 511 status_t 512 Inode::Dereference(Transaction& transaction, BTree::Path* path, ino_t parentID, 513 const char* name) 514 { 515 BTree* tree = path->Tree(); 516 517 // remove inode_ref item 518 btrfs_key key; 519 key.SetObjectID(fID); 520 key.SetType(BTRFS_KEY_TYPE_INODE_REF); 521 key.SetOffset(parentID); 522 btrfs_inode_ref* inodeRef; 523 status_t status = tree->RemoveEntries(transaction, path, key, 524 (void**)&inodeRef, 1); 525 if (status != B_OK) 526 return status; 527 528 // remove dir_item 529 uint32 hash = calculate_crc((uint32)~1, (uint8*)name, strlen(name)); 530 key.SetObjectID(parentID); 531 key.SetType(BTRFS_KEY_TYPE_DIR_ITEM); 532 key.SetOffset(hash); 533 status = tree->RemoveEntries(transaction, path, key, NULL, 1); 534 if (status != B_OK) 535 return status; 536 537 // remove dir_index 538 uint64 index = inodeRef->Index(); 539 free(inodeRef); 540 key.SetType(BTRFS_KEY_TYPE_DIR_INDEX); 541 key.SetOffset(index); 542 status = tree->RemoveEntries(transaction, path, key, NULL, 1); 543 if (status != B_OK) 544 return status; 545 546 return B_OK; 547 } 548