BTRFS: Implement Dereference() in Inode that remove the "name" and unlink it with...
[haiku.git] / src / add-ons / kernel / file_systems / btrfs / Inode.cpp
blob0b212aa5b69a40349846f7fbb9c55eadd02bc5ad
1 /*
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.
6 */
9 #include "Inode.h"
10 #include "CachedBlock.h"
11 #include "CRCTable.h"
12 #include "Utility.h"
15 #undef ASSERT
16 //#define TRACE_BTRFS
17 #ifdef TRACE_BTRFS
18 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
19 # define ASSERT(x) { if (!(x)) kernel_debugger("btrfs: assert failed: " #x "\n"); }
20 #else
21 # define TRACE(x...) ;
22 # define ASSERT(x) ;
23 #endif
24 #define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
27 Inode::Inode(Volume* volume, ino_t id)
29 fVolume(volume),
30 fID(id),
31 fCache(NULL),
32 fMap(NULL)
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)
48 fVolume(volume),
49 fID(id),
50 fCache(NULL),
51 fMap(NULL),
52 fInitStatus(B_OK),
53 fNode(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)
64 fVolume(volume),
65 fID(0),
66 fCache(NULL),
67 fMap(NULL),
68 fInitStatus(B_NO_INIT)
70 rw_lock_init(&fLock, "btrfs inode");
74 Inode::~Inode()
76 TRACE("Inode destructor\n");
77 file_cache_delete(FileCache());
78 file_map_delete(Map());
79 TRACE("Inode destructor: Done\n");
83 status_t
84 Inode::InitCheck()
86 return fInitStatus;
90 status_t
91 Inode::UpdateNodeFromDisk()
93 btrfs_key search_key;
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());
99 btrfs_inode* node;
100 if (fVolume->FSTree()->FindExact(&path, search_key, (void**)&node)
101 != B_OK) {
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));
108 free(node);
109 return B_OK;
114 * Create new Inode object with inode_item
116 Inode*
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();
128 btrfs_inode inode;
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);
156 status_t
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());
168 status_t
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
182 "\n", status);
183 return status;
186 TRACE("Inode::FindBlock(%" B_PRIdINO ") key.Offset() %" B_PRId64 "\n",
187 ID(), search_key.Offset());
189 off_t diff = pos - search_key.Offset();
190 off_t logical = 0;
191 if (extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR)
192 logical = diff + extent_data->disk_offset;
193 else
194 panic("unknown extent type; %d\n", extent_data->Type());
195 status = fVolume->FindBlock(logical, physical);
196 if (_length != NULL)
197 *_length = extent_data->Size() - diff;
198 TRACE("Inode::FindBlock(%" B_PRIdINO ") %" B_PRIdOFF " physical %"
199 B_PRIdOFF "\n", ID(), pos, physical);
201 free(extent_data);
202 return status;
206 status_t
207 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
209 size_t length = *_length;
211 // set/check boundaries for pos/length
212 if (pos < 0) {
213 ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %" B_PRIdOFF
214 ", length %lu)\n", ID(), pos, length);
215 return B_BAD_VALUE;
218 if (pos >= Size() || length == 0) {
219 TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %" B_PRIdOFF
220 ", length %lu)\n", ID(), pos, length);
221 *_length = 0;
222 return B_NO_ERROR;
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());
233 uint32 item_size;
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
239 "\n", status);
240 return status;
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",
247 ID(), pos, length);
248 free(extent_data);
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");
253 else
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) {
268 char in[2048];
269 z_stream zStream = {
270 (Bytef*)in, // next in
271 sizeof(in), // avail in
272 0, // total in
273 NULL, // next out
274 0, // avail out
275 0, // total out
276 0, // msg
277 0, // state
278 Z_NULL, // zalloc
279 Z_NULL, // zfree
280 Z_NULL, // opaque
281 0, // data type
282 0, // adler
283 0, // reserved
286 int status;
287 ssize_t offset = 0;
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);
294 do {
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;
300 break;
304 zStream.avail_in = bytesRead;
305 zStream.next_in = (Bytef*)in;
307 if (!headerRead) {
308 headerRead = true;
310 zStream.avail_out = length;
311 zStream.next_out = (Bytef*)buffer;
313 status = inflateInit2(&zStream, 15);
314 if (status != Z_OK) {
315 free(extent_data);
316 return B_ERROR;
320 status = inflate(&zStream, Z_SYNC_FLUSH);
321 offset += bytesRead;
322 if (diff > 0) {
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",
329 zStream.msg);
331 } while (status == Z_OK);
333 inflateEnd(&zStream);
335 if (status != Z_STREAM_END) {
336 TRACE("Inode::ReadAt() inflating failed: %d!\n", status);
337 free(extent_data);
338 return B_BAD_DATA;
341 *_length = zStream.total_out;
343 } else
344 panic("unknown extent compression; %d\n", compression);
345 free(extent_data);
346 return B_OK;
351 status_t
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());
360 void* node_ref;
361 if (fVolume->FSTree()->FindPrevious(&path, search_key, &node_ref) != B_OK) {
362 ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO "\n",
363 fID);
364 return B_ERROR;
367 free(node_ref);
368 *id = search_key.Offset();
369 TRACE("Inode::FindParent() for %" B_PRIdINO ": %" B_PRIdINO "\n", fID,
370 *id);
372 return B_OK;
376 uint64
377 Inode::FindNextIndex(BTree::Path* path) const
379 btrfs_key key;
380 key.SetObjectID(fID);
381 key.SetType(BTRFS_KEY_TYPE_DIR_INDEX);
382 key.SetOffset(-1);
384 if (fVolume->FSTree()->FindPrevious(path, key, NULL))
385 return 2; // not found any dir index item
387 return key.Offset() + 1;
391 /* Insert inode_item
393 status_t
394 Inode::Insert(Transaction& transaction, BTree::Path* path)
396 BTree* tree = path->Tree();
398 btrfs_entry item;
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));
404 void* data[1];
405 data[0] = (void*)&fNode;
406 status_t status = tree->InsertEntries(transaction, path, &item, data, 1);
407 if (status != B_OK)
408 return status;
410 return B_OK;
414 /* Remove inode_item
416 status_t
417 Inode::Remove(Transaction& transaction, BTree::Path* path)
419 BTree* tree = path->Tree();
420 btrfs_key key;
421 key.SetObjectID(fID);
422 key.SetType(BTRFS_KEY_TYPE_INODE_ITEM);
423 key.SetOffset(0);
424 status_t status = tree->RemoveEntries(transaction, path, key, NULL, 1);
425 if (status != B_OK)
426 return status;
428 return B_OK;
432 /* Insert 3 items: inode_ref, dir_item, dir_index
433 * Basically, make a link between name and its node (location)
435 status_t
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);
442 void* data[1];
444 // insert inode_ref
445 btrfs_entry entry;
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);
458 if (status != B_OK)
459 return status;
461 // insert dir_entry
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);
480 if (status != B_OK)
481 return status;
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);
488 if (status != B_OK)
489 return status;
491 return B_OK;
495 // Remove the "name" and unlink it with inode.
496 status_t
497 Inode::Dereference(Transaction& transaction, BTree::Path* path, ino_t parentID,
498 const char* name)
500 BTree* tree = path->Tree();
502 // remove inode_ref item
503 btrfs_key key;
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);
510 if (status != B_OK)
511 return status;
513 // remove dir_item
514 uint32 hash = calculate_crc((uint32)~1, (uint8*)name, strlen(name));
515 key.SetObjectID(parentID);
516 key.SetType(BTRFS_KEY_TYPE_DIR_ITEM);
517 key.SetOffset(hash);
518 status = tree->RemoveEntries(transaction, path, key, NULL, 1);
519 if (status != B_OK)
520 return status;
522 // remove dir_index
523 uint64 index = inodeRef->Index();
524 free(inodeRef);
525 key.SetType(BTRFS_KEY_TYPE_DIR_INDEX);
526 key.SetOffset(index);
527 status = tree->RemoveEntries(transaction, path, key, NULL, 1);
528 if (status != B_OK)
529 return status;
531 return B_OK;