2 * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2008-2014, Axel Dörfler, axeld@pinc-software.de.
4 * Copyright 2005-2007, Ingo Weinhold, bonefish@cs.tu-berlin.de.
5 * This file may be used under the terms of the MIT License.
10 #include "CachedBlock.h"
18 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
19 # define ASSERT(x) { if (!(x)) kernel_debugger("btrfs: assert failed: " #x "\n"); }
21 # define TRACE(x...) ;
24 #define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
27 Inode::Inode(Volume
* volume
, ino_t id
)
34 rw_lock_init(&fLock
, "btrfs inode");
36 fInitStatus
= UpdateNodeFromDisk();
37 if (fInitStatus
== B_OK
) {
38 if (!IsDirectory() && !IsSymLink()) {
39 fCache
= file_cache_create(fVolume
->ID(), ID(), Size());
40 fMap
= file_map_create(fVolume
->ID(), ID(), Size());
46 Inode::Inode(Volume
* volume
, ino_t id
, const btrfs_inode
& item
)
55 if (!IsDirectory() && !IsSymLink()) {
56 fCache
= file_cache_create(fVolume
->ID(), ID(), Size());
57 fMap
= file_map_create(fVolume
->ID(), ID(), Size());
62 Inode::Inode(Volume
* volume
)
68 fInitStatus(B_NO_INIT
)
70 rw_lock_init(&fLock
, "btrfs inode");
76 TRACE("Inode destructor\n");
77 file_cache_delete(FileCache());
78 file_map_delete(Map());
79 TRACE("Inode destructor: Done\n");
91 Inode::UpdateNodeFromDisk()
94 search_key
.SetType(BTRFS_KEY_TYPE_INODE_ITEM
);
95 search_key
.SetObjectID(fID
);
96 search_key
.SetOffset(0);
97 BTree::Path
path(fVolume
->FSTree());
100 if (fVolume
->FSTree()->FindExact(&path
, search_key
, (void**)&node
)
102 ERROR("Inode::UpdateNodeFromDisk(): Couldn't find inode %"
103 B_PRIdINO
"\n", fID
);
104 return B_ENTRY_NOT_FOUND
;
107 memcpy(&fNode
, node
, sizeof(btrfs_inode
));
114 * Create new Inode object with inode_item
117 Inode::Create(Transaction
& transaction
, ino_t id
, Inode
* parent
, int32 mode
,
118 uint64 size
, uint64 flags
)
120 TRACE("Inode::Create() id % " B_PRIu64
" mode %" B_PRId32
" flags %"
121 B_PRIu64
"\n", id
, flags
, mode
);
123 Volume
* volume
= parent
->GetVolume();
124 uint64 nbytes
= size
; // allocated size
125 if (size
> volume
->MaxInlineSize())
126 nbytes
= (size
/ volume
->SectorSize() + 1) * volume
->SectorSize();
130 inode
.generation
= B_HOST_TO_LENDIAN_INT64(transaction
.SystemID());
131 inode
.transaction_id
= B_HOST_TO_LENDIAN_INT64(transaction
.SystemID());
132 inode
.size
= B_HOST_TO_LENDIAN_INT64(size
);
133 inode
.nbytes
= B_HOST_TO_LENDIAN_INT64(nbytes
);
134 inode
.blockgroup
= 0; // normal inode only
135 inode
.num_links
= B_HOST_TO_LENDIAN_INT32(1);
136 inode
.uid
= B_HOST_TO_LENDIAN_INT32(geteuid());
137 inode
.gid
= B_HOST_TO_LENDIAN_INT32(parent
? parent
->GroupID() : getegid());
138 inode
.mode
= B_HOST_TO_LENDIAN_INT32(mode
);;
139 inode
.rdev
= 0; // normal file only
140 inode
.flags
= B_HOST_TO_LENDIAN_INT64(flags
);
141 inode
.sequence
= 0; // incremented each time mtime value is changed
143 uint64 now
= real_time_clock_usecs();
144 struct timespec timespec
;
145 timespec
.tv_sec
= now
/ 1000000;
146 timespec
.tv_nsec
= (now
% 1000000) * 1000;
147 btrfs_inode::SetTime(inode
.access_time
, timespec
);
148 btrfs_inode::SetTime(inode
.creation_time
, timespec
);
149 btrfs_inode::SetTime(inode
.change_time
, timespec
);
150 btrfs_inode::SetTime(inode
.modification_time
, timespec
);
152 return new Inode(volume
, id
, inode
);
157 Inode::CheckPermissions(int accessMode
) const
159 // you never have write access to a read-only volume
160 if ((accessMode
& W_OK
) != 0 && fVolume
->IsReadOnly())
161 return B_READ_ONLY_DEVICE
;
163 return check_access_permissions(accessMode
, Mode(), (gid_t
)fNode
.GroupID(),
164 (uid_t
)fNode
.UserID());
169 Inode::FindBlock(off_t pos
, off_t
& physical
, off_t
* _length
)
171 btrfs_key search_key
;
172 search_key
.SetType(BTRFS_KEY_TYPE_EXTENT_DATA
);
173 search_key
.SetObjectID(fID
);
174 search_key
.SetOffset(pos
+ 1);
175 BTree::Path
path(fVolume
->FSTree());
177 btrfs_extent_data
* extent_data
;
178 status_t status
= fVolume
->FSTree()->FindPrevious(&path
, search_key
,
179 (void**)&extent_data
);
180 if (status
!= B_OK
) {
181 ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%" B_PRIx32
186 TRACE("Inode::FindBlock(%" B_PRIdINO
") key.Offset() %" B_PRId64
"\n",
187 ID(), search_key
.Offset());
189 off_t diff
= pos
- search_key
.Offset();
191 if (extent_data
->Type() == BTRFS_EXTENT_DATA_REGULAR
)
192 logical
= diff
+ extent_data
->disk_offset
;
194 panic("unknown extent type; %d\n", extent_data
->Type());
195 status
= fVolume
->FindBlock(logical
, physical
);
197 *_length
= extent_data
->Size() - diff
;
198 TRACE("Inode::FindBlock(%" B_PRIdINO
") %" B_PRIdOFF
" physical %"
199 B_PRIdOFF
"\n", ID(), pos
, physical
);
207 Inode::ReadAt(off_t pos
, uint8
* buffer
, size_t* _length
)
209 size_t length
= *_length
;
211 // set/check boundaries for pos/length
213 ERROR("inode %" B_PRIdINO
": ReadAt failed(pos %" B_PRIdOFF
214 ", length %lu)\n", ID(), pos
, length
);
218 if (pos
>= Size() || length
== 0) {
219 TRACE("inode %" B_PRIdINO
": ReadAt 0 (pos %" B_PRIdOFF
220 ", length %lu)\n", ID(), pos
, length
);
225 // the file cache doesn't seem to like non block aligned file offset
226 // so we avoid the file cache for inline extents
227 btrfs_key search_key
;
228 search_key
.SetType(BTRFS_KEY_TYPE_EXTENT_DATA
);
229 search_key
.SetObjectID(fID
);
230 search_key
.SetOffset(pos
+ 1);
231 BTree::Path
path(fVolume
->FSTree());
234 btrfs_extent_data
* extent_data
;
235 status_t status
= fVolume
->FSTree()->FindPrevious(&path
, search_key
,
236 (void**)&extent_data
, &item_size
);
237 if (status
!= B_OK
) {
238 ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%" B_PRIx32
243 uint8 compression
= extent_data
->Compression();
244 if (FileCache() != NULL
245 && extent_data
->Type() == BTRFS_EXTENT_DATA_REGULAR
) {
246 TRACE("inode %" B_PRIdINO
": ReadAt cache (pos %" B_PRIdOFF
", length %lu)\n",
249 if (compression
== BTRFS_EXTENT_COMPRESS_NONE
)
250 return file_cache_read(FileCache(), NULL
, pos
, buffer
, _length
);
251 else if (compression
== BTRFS_EXTENT_COMPRESS_ZLIB
)
252 panic("zlib isn't unsupported for regular extent\n");
254 panic("unknown extent compression; %d\n", compression
);
257 TRACE("Inode::ReadAt(%" B_PRIdINO
") key.Offset() %" B_PRId64
"\n", ID(),
258 search_key
.Offset());
260 off_t diff
= pos
- search_key
.Offset();
261 if (extent_data
->Type() != BTRFS_EXTENT_DATA_INLINE
)
262 panic("unknown extent type; %d\n", extent_data
->Type());
264 *_length
= min_c(extent_data
->Size() - diff
, *_length
);
265 if (compression
== BTRFS_EXTENT_COMPRESS_NONE
)
266 memcpy(buffer
, extent_data
->inline_data
, *_length
);
267 else if (compression
== BTRFS_EXTENT_COMPRESS_ZLIB
) {
270 (Bytef
*)in
, // next in
271 sizeof(in
), // avail in
288 uint32 inline_size
= item_size
- 13;
289 bool headerRead
= false;
291 TRACE("Inode::ReadAt(%" B_PRIdINO
") diff %" B_PRIdOFF
" size %"
292 B_PRIuSIZE
"\n", ID(), diff
, item_size
);
295 ssize_t bytesRead
= min_c(sizeof(in
), inline_size
- offset
);
296 memcpy(in
, extent_data
->inline_data
+ offset
, bytesRead
);
297 if (bytesRead
!= (ssize_t
)sizeof(in
)) {
298 if (bytesRead
<= 0) {
299 status
= Z_STREAM_ERROR
;
304 zStream
.avail_in
= bytesRead
;
305 zStream
.next_in
= (Bytef
*)in
;
310 zStream
.avail_out
= length
;
311 zStream
.next_out
= (Bytef
*)buffer
;
313 status
= inflateInit2(&zStream
, 15);
314 if (status
!= Z_OK
) {
320 status
= inflate(&zStream
, Z_SYNC_FLUSH
);
323 zStream
.next_out
-= max_c(bytesRead
, diff
);
324 diff
-= max_c(bytesRead
, diff
);
327 if (zStream
.avail_in
!= 0 && status
!= Z_STREAM_END
) {
328 TRACE("Inode::ReadAt() didn't read whole block: %s\n",
331 } while (status
== Z_OK
);
333 inflateEnd(&zStream
);
335 if (status
!= Z_STREAM_END
) {
336 TRACE("Inode::ReadAt() inflating failed: %d!\n", status
);
341 *_length
= zStream
.total_out
;
344 panic("unknown extent compression; %d\n", compression
);
352 Inode::FindParent(ino_t
* id
)
354 btrfs_key search_key
;
355 search_key
.SetType(BTRFS_KEY_TYPE_INODE_REF
);
356 search_key
.SetObjectID(fID
);
357 search_key
.SetOffset(-1);
358 BTree::Path
path(fVolume
->FSTree());
361 if (fVolume
->FSTree()->FindPrevious(&path
, search_key
, &node_ref
) != B_OK
) {
362 ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO
"\n",
368 *id
= search_key
.Offset();
369 TRACE("Inode::FindParent() for %" B_PRIdINO
": %" B_PRIdINO
"\n", fID
,
377 Inode::FindNextIndex(BTree::Path
* path
) const
380 key
.SetObjectID(fID
);
381 key
.SetType(BTRFS_KEY_TYPE_DIR_INDEX
);
384 if (fVolume
->FSTree()->FindPrevious(path
, key
, NULL
))
385 return 2; // not found any dir index item
387 return key
.Offset() + 1;
394 Inode::Insert(Transaction
& transaction
, BTree::Path
* path
)
396 BTree
* tree
= path
->Tree();
399 item
.key
.SetObjectID(fID
);
400 item
.key
.SetType(BTRFS_KEY_TYPE_INODE_ITEM
);
401 item
.key
.SetOffset(0);
402 item
.SetSize(sizeof(btrfs_inode
));
405 data
[0] = (void*)&fNode
;
406 status_t status
= tree
->InsertEntries(transaction
, path
, &item
, data
, 1);
417 Inode::Remove(Transaction
& transaction
, BTree::Path
* path
)
419 BTree
* tree
= path
->Tree();
421 key
.SetObjectID(fID
);
422 key
.SetType(BTRFS_KEY_TYPE_INODE_ITEM
);
424 status_t status
= tree
->RemoveEntries(transaction
, path
, key
, NULL
, 1);
432 /* Insert 3 items: inode_ref, dir_item, dir_index
433 * Basically, make a link between name and its node (location)
436 Inode::MakeReference(Transaction
& transaction
, BTree::Path
* path
,
437 Inode
* parent
, const char* name
, int32 mode
)
439 BTree
* tree
= fVolume
->FSTree();
440 uint16 nameLength
= strlen(name
);
441 uint64 index
= parent
->FindNextIndex(path
);
446 btrfs_inode_ref inodeRef
;
448 inodeRef
.index
= index
;
449 inodeRef
.SetName(name
, nameLength
);
450 data
[0] = (void*)&inodeRef
;
452 entry
.key
.SetObjectID(fID
);
453 entry
.key
.SetType(BTRFS_KEY_TYPE_INODE_REF
);
454 entry
.key
.SetOffset(parent
->ID());
455 entry
.SetSize(inodeRef
.Length());
457 status_t status
= tree
->InsertEntries(transaction
, path
, &entry
, data
, 1);
462 uint32 hash
= calculate_crc((uint32
)~1, (uint8
*)name
, nameLength
);
463 btrfs_dir_entry directoryEntry
;
464 directoryEntry
.location
.SetObjectID(fID
);
465 directoryEntry
.location
.SetType(BTRFS_KEY_TYPE_INODE_ITEM
);
466 directoryEntry
.location
.SetOffset(0);
467 directoryEntry
.SetTransactionID(transaction
.SystemID());
468 // TODO: xattribute, 0 for standard directory
469 directoryEntry
.SetAttributeData(NULL
, 0);
470 directoryEntry
.SetName(name
, nameLength
);
471 directoryEntry
.type
= get_filetype(mode
);
472 data
[0] = (void*)&directoryEntry
;
474 entry
.key
.SetObjectID(parent
->ID());
475 entry
.key
.SetType(BTRFS_KEY_TYPE_DIR_ITEM
);
476 entry
.key
.SetOffset(hash
);
477 entry
.SetSize(directoryEntry
.Length());
479 status
= tree
->InsertEntries(transaction
, path
, &entry
, data
, 1);
483 // insert dir_index (has same data with dir_entry)
484 entry
.key
.SetType(BTRFS_KEY_TYPE_DIR_INDEX
);
485 entry
.key
.SetOffset(index
);
487 status
= tree
->InsertEntries(transaction
, path
, &entry
, data
, 1);
495 // Remove the "name" and unlink it with inode.
497 Inode::Dereference(Transaction
& transaction
, BTree::Path
* path
, ino_t parentID
,
500 BTree
* tree
= path
->Tree();
502 // remove inode_ref item
504 key
.SetObjectID(fID
);
505 key
.SetType(BTRFS_KEY_TYPE_INODE_REF
);
506 key
.SetOffset(parentID
);
507 btrfs_inode_ref
* inodeRef
;
508 status_t status
= tree
->RemoveEntries(transaction
, path
, key
,
509 (void**)&inodeRef
, 1);
514 uint32 hash
= calculate_crc((uint32
)~1, (uint8
*)name
, strlen(name
));
515 key
.SetObjectID(parentID
);
516 key
.SetType(BTRFS_KEY_TYPE_DIR_ITEM
);
518 status
= tree
->RemoveEntries(transaction
, path
, key
, NULL
, 1);
523 uint64 index
= inodeRef
->Index();
525 key
.SetType(BTRFS_KEY_TYPE_DIR_INDEX
);
526 key
.SetOffset(index
);
527 status
= tree
->RemoveEntries(transaction
, path
, key
, NULL
, 1);