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