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