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