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 memcpy(in, extent_data->inline_data + offset, bytesRead); 304 if (bytesRead != (ssize_t)sizeof(in)) { 305 if (bytesRead <= 0) { 306 status = Z_STREAM_ERROR; 307 break; 308 } 309 } 310 311 zStream.avail_in = bytesRead; 312 zStream.next_in = (Bytef*)in; 313 314 if (!headerRead) { 315 headerRead = true; 316 317 zStream.avail_out = length; 318 zStream.next_out = (Bytef*)buffer; 319 320 status = inflateInit2(&zStream, 15); 321 if (status != Z_OK) { 322 return B_ERROR; 323 } 324 } 325 326 status = inflate(&zStream, Z_SYNC_FLUSH); 327 offset += bytesRead; 328 if (diff > 0) { 329 zStream.next_out -= max_c(bytesRead, diff); 330 diff -= max_c(bytesRead, diff); 331 } 332 333 if (zStream.avail_in != 0 && status != Z_STREAM_END) { 334 TRACE("Inode::ReadAt() didn't read whole block: %s\n", 335 zStream.msg); 336 } 337 } while (status == Z_OK); 338 339 inflateEnd(&zStream); 340 341 if (status != Z_STREAM_END) { 342 TRACE("Inode::ReadAt() inflating failed: %d!\n", status); 343 return B_BAD_DATA; 344 } 345 346 *_length = zStream.total_out; 347 348 } else { 349 panic("unknown extent compression; %d\n", compression); 350 return B_BAD_DATA; 351 } 352 return B_OK; 353 354 } 355 356 357 status_t 358 Inode::FindParent(ino_t* id) 359 { 360 btrfs_key search_key; 361 search_key.SetType(BTRFS_KEY_TYPE_INODE_REF); 362 search_key.SetObjectID(fID); 363 search_key.SetOffset(-1); 364 BTree::Path path(fVolume->FSTree()); 365 366 void* node_ref; 367 if (fVolume->FSTree()->FindPrevious(&path, search_key, &node_ref) != B_OK) { 368 ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO "\n", 369 fID); 370 return B_ERROR; 371 } 372 373 free(node_ref); 374 *id = search_key.Offset(); 375 TRACE("Inode::FindParent() for %" B_PRIdINO ": %" B_PRIdINO "\n", fID, 376 *id); 377 378 return B_OK; 379 } 380 381 382 uint64 383 Inode::FindNextIndex(BTree::Path* path) const 384 { 385 btrfs_key key; 386 key.SetObjectID(fID); 387 key.SetType(BTRFS_KEY_TYPE_DIR_INDEX); 388 key.SetOffset(-1); 389 390 if (fVolume->FSTree()->FindPrevious(path, key, NULL)) 391 return 2; // not found any dir index item 392 393 return key.Offset() + 1; 394 } 395 396 397 /* Insert inode_item 398 */ 399 status_t 400 Inode::Insert(Transaction& transaction, BTree::Path* path) 401 { 402 BTree* tree = path->Tree(); 403 404 btrfs_entry item; 405 item.key.SetObjectID(fID); 406 item.key.SetType(BTRFS_KEY_TYPE_INODE_ITEM); 407 item.key.SetOffset(0); 408 item.SetSize(sizeof(btrfs_inode)); 409 410 void* data[1]; 411 data[0] = (void*)&fNode; 412 status_t status = tree->InsertEntries(transaction, path, &item, data, 1); 413 if (status != B_OK) 414 return status; 415 416 return B_OK; 417 } 418 419 420 /* Remove inode_item 421 */ 422 status_t 423 Inode::Remove(Transaction& transaction, BTree::Path* path) 424 { 425 BTree* tree = path->Tree(); 426 btrfs_key key; 427 key.SetObjectID(fID); 428 key.SetType(BTRFS_KEY_TYPE_INODE_ITEM); 429 key.SetOffset(0); 430 status_t status = tree->RemoveEntries(transaction, path, key, NULL, 1); 431 if (status != B_OK) 432 return status; 433 434 return B_OK; 435 } 436 437 438 /* Insert 3 items: inode_ref, dir_item, dir_index 439 * Basically, make a link between name and its node (location) 440 */ 441 status_t 442 Inode::MakeReference(Transaction& transaction, BTree::Path* path, 443 Inode* parent, const char* name, int32 mode) 444 { 445 BTree* tree = fVolume->FSTree(); 446 uint16 nameLength = strlen(name); 447 uint64 index = parent->FindNextIndex(path); 448 449 // insert inode_ref 450 btrfs_inode_ref* inodeRef = (btrfs_inode_ref*)malloc(sizeof(btrfs_inode_ref) 451 + nameLength); 452 if (inodeRef == NULL) 453 return B_NO_MEMORY; 454 inodeRef->index = index; 455 inodeRef->SetName(name, nameLength); 456 457 btrfs_entry entry; 458 entry.key.SetObjectID(fID); 459 entry.key.SetType(BTRFS_KEY_TYPE_INODE_REF); 460 entry.key.SetOffset(parent->ID()); 461 entry.SetSize(inodeRef->Length()); 462 463 status_t status = tree->InsertEntries(transaction, path, &entry, 464 (void**)&inodeRef, 1); 465 free(inodeRef); 466 if (status != B_OK) 467 return status; 468 469 // insert dir_entry 470 uint32 hash = calculate_crc((uint32)~1, (uint8*)name, nameLength); 471 btrfs_dir_entry* directoryEntry = 472 (btrfs_dir_entry*)malloc(sizeof(btrfs_dir_entry) + nameLength); 473 if (directoryEntry == NULL) 474 return B_NO_MEMORY; 475 directoryEntry->location.SetObjectID(fID); 476 directoryEntry->location.SetType(BTRFS_KEY_TYPE_INODE_ITEM); 477 directoryEntry->location.SetOffset(0); 478 directoryEntry->SetTransactionID(transaction.SystemID()); 479 // TODO: xattribute, 0 for standard directory 480 directoryEntry->SetName(name, nameLength); 481 directoryEntry->SetAttributeData(NULL, 0); 482 directoryEntry->type = get_filetype(mode); 483 484 entry.key.SetObjectID(parent->ID()); 485 entry.key.SetType(BTRFS_KEY_TYPE_DIR_ITEM); 486 entry.key.SetOffset(hash); 487 entry.SetSize(directoryEntry->Length()); 488 489 status = tree->InsertEntries(transaction, path, &entry, 490 (void**)&directoryEntry, 1); 491 if (status != B_OK) { 492 free(directoryEntry); 493 return status; 494 } 495 496 // insert dir_index (has same data with dir_entry) 497 entry.key.SetType(BTRFS_KEY_TYPE_DIR_INDEX); 498 entry.key.SetOffset(index); 499 500 status = tree->InsertEntries(transaction, path, &entry, 501 (void**)&directoryEntry, 1); 502 if (status != B_OK) { 503 free(directoryEntry); 504 return status; 505 } 506 507 free(directoryEntry); 508 return B_OK; 509 } 510 511 512 // Remove the "name" and unlink it with inode. 513 status_t 514 Inode::Dereference(Transaction& transaction, BTree::Path* path, ino_t parentID, 515 const char* name) 516 { 517 BTree* tree = path->Tree(); 518 519 // remove inode_ref item 520 btrfs_key key; 521 key.SetObjectID(fID); 522 key.SetType(BTRFS_KEY_TYPE_INODE_REF); 523 key.SetOffset(parentID); 524 btrfs_inode_ref* inodeRef; 525 status_t status = tree->RemoveEntries(transaction, path, key, 526 (void**)&inodeRef, 1); 527 if (status != B_OK) 528 return status; 529 530 // remove dir_item 531 uint32 hash = calculate_crc((uint32)~1, (uint8*)name, strlen(name)); 532 key.SetObjectID(parentID); 533 key.SetType(BTRFS_KEY_TYPE_DIR_ITEM); 534 key.SetOffset(hash); 535 status = tree->RemoveEntries(transaction, path, key, NULL, 1); 536 if (status != B_OK) 537 return status; 538 539 // remove dir_index 540 uint64 index = inodeRef->Index(); 541 free(inodeRef); 542 key.SetType(BTRFS_KEY_TYPE_DIR_INDEX); 543 key.SetOffset(index); 544 status = tree->RemoveEntries(transaction, path, key, NULL, 1); 545 if (status != B_OK) 546 return status; 547 548 return B_OK; 549 } 550