xref: /haiku/src/add-ons/kernel/file_systems/ext2/Inode.cpp (revision 4e3137c085bae361922078f123dceb92da700640)
1 /*
2  * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
3  * Copyright 2008-2014, Axel Dörfler, axeld@pinc-software.de.
4  * This file may be used under the terms of the MIT License.
5  */
6 
7 
8 #include "Inode.h"
9 
10 #include <string.h>
11 #include <util/AutoLock.h>
12 #include <NodeMonitor.h>
13 
14 #include "CachedBlock.h"
15 #include "DataStream.h"
16 #include "DirectoryIterator.h"
17 #include "ExtentStream.h"
18 #include "HTree.h"
19 #include "Utility.h"
20 
21 
22 #undef ASSERT
23 //#define TRACE_EXT2
24 #ifdef TRACE_EXT2
25 #	define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
26 #	define ASSERT(x) { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); }
27 #else
28 #	define TRACE(x...) ;
29 #	define ASSERT(x) ;
30 #endif
31 #define ERROR(x...) dprintf("\33[34mext2:\33[0m " x)
32 
33 
34 Inode::Inode(Volume* volume, ino_t id)
35 	:
36 	fVolume(volume),
37 	fID(id),
38 	fCache(NULL),
39 	fMap(NULL),
40 	fHasExtraAttributes(false)
41 {
42 	rw_lock_init(&fLock, "ext2 inode");
43 	recursive_lock_init(&fSmallDataLock, "ext2 inode small data");
44 
45 	TRACE("Inode::Inode(): ext2_inode: %lu, disk inode: %" B_PRIu32
46 		"\n", sizeof(ext2_inode), fVolume->InodeSize());
47 	fNodeSize = sizeof(ext2_inode) > fVolume->InodeSize()
48 		? fVolume->InodeSize() : sizeof(ext2_inode);
49 
50 	fInitStatus = UpdateNodeFromDisk();
51 	if (fInitStatus == B_OK) {
52 		fHasExtraAttributes = (fNodeSize == sizeof(ext2_inode)
53 			&& fNode.ExtraInodeSize() + EXT2_INODE_NORMAL_SIZE
54 				== sizeof(ext2_inode));
55 
56 		if (IsDirectory() || (IsSymLink() && Size() < 60)) {
57 			TRACE("Inode::Inode(): Not creating the file cache\n");
58 			fInitStatus = B_OK;
59 		} else
60 			fInitStatus = CreateFileCache();
61 	} else
62 		TRACE("Inode: Failed initialization\n");
63 }
64 
65 
66 Inode::Inode(Volume* volume)
67 	:
68 	fVolume(volume),
69 	fID(0),
70 	fCache(NULL),
71 	fMap(NULL),
72 	fInitStatus(B_NO_INIT)
73 {
74 	rw_lock_init(&fLock, "ext2 inode");
75 	recursive_lock_init(&fSmallDataLock, "ext2 inode small data");
76 
77 	TRACE("Inode::Inode(): ext2_inode: %lu, disk inode: %" B_PRIu32 "\n",
78 		sizeof(ext2_inode), fVolume->InodeSize());
79 	fNodeSize = sizeof(ext2_inode) > fVolume->InodeSize()
80 		? fVolume->InodeSize() : sizeof(ext2_inode);
81 }
82 
83 
84 Inode::~Inode()
85 {
86 	TRACE("Inode destructor\n");
87 
88 	DeleteFileCache();
89 
90 	TRACE("Inode destructor: Done\n");
91 }
92 
93 
94 status_t
95 Inode::InitCheck()
96 {
97 	return fInitStatus;
98 }
99 
100 
101 void
102 Inode::WriteLockInTransaction(Transaction& transaction)
103 {
104 	acquire_vnode(fVolume->FSVolume(), ID());
105 
106 	TRACE("Inode::WriteLockInTransaction(): Locking\n");
107 	rw_lock_write_lock(&fLock);
108 
109 	transaction.AddListener(this);
110 }
111 
112 
113 status_t
114 Inode::WriteBack(Transaction& transaction)
115 {
116 	off_t blockNum;
117 
118 	status_t status = fVolume->GetInodeBlock(fID, blockNum);
119 	if (status != B_OK)
120 		return status;
121 
122  	if (Node().Size() > 0x7fffffffLL) {
123 		status = fVolume->ActivateLargeFiles(transaction);
124 		 if (status != B_OK)
125 			  return status;
126 	}
127 
128 	CachedBlock cached(fVolume);
129 	uint8* inodeBlockData = cached.SetToWritable(transaction, blockNum);
130 	if (inodeBlockData == NULL)
131 		return B_IO_ERROR;
132 
133 	TRACE("Inode::WriteBack(): Inode ID: %" B_PRIdINO ", inode block: %"
134 		B_PRIdOFF ", data: %p, index: %" B_PRIu32 ", inode size: %" B_PRIu32
135 		", node size: %" B_PRIu32 ", this: %p, node: %p\n",
136 		fID, blockNum, inodeBlockData, fVolume->InodeBlockIndex(fID),
137 		fVolume->InodeSize(), fNodeSize, this, &fNode);
138 	memcpy(inodeBlockData +
139 			fVolume->InodeBlockIndex(fID) * fVolume->InodeSize(),
140 		(uint8*)&fNode, fNodeSize);
141 
142 	TRACE("Inode::WriteBack() finished %" B_PRId32 "\n", Node().stream.direct[0]);
143 
144 	return B_OK;
145 }
146 
147 
148 status_t
149 Inode::UpdateNodeFromDisk()
150 {
151 	off_t blockNum;
152 
153 	status_t status = fVolume->GetInodeBlock(fID, blockNum);
154 	if (status != B_OK)
155 		return status;
156 
157 	TRACE("inode %" B_PRIdINO " at block %" B_PRIdOFF "\n", fID, blockNum);
158 
159 	CachedBlock cached(fVolume);
160 	const uint8* inodeBlock = cached.SetTo(blockNum);
161 
162 	if (inodeBlock == NULL)
163 		return B_IO_ERROR;
164 
165 	TRACE("Inode size: %" B_PRIu32 ", inode index: %" B_PRIu32 "\n",
166 		fVolume->InodeSize(), fVolume->InodeBlockIndex(fID));
167 	ext2_inode* inode = (ext2_inode*)(inodeBlock
168 		+ fVolume->InodeBlockIndex(fID) * fVolume->InodeSize());
169 
170 	TRACE("Attempting to copy inode data from %p to %p, ext2_inode "
171 		"size: %" B_PRIu32 "\n", inode, &fNode, fNodeSize);
172 
173 	memcpy(&fNode, inode, fNodeSize);
174 
175 	uint32 numLinks = fNode.NumLinks();
176 	fUnlinked = numLinks == 0 || (IsDirectory() && numLinks == 1);
177 
178 	return B_OK;
179 }
180 
181 
182 status_t
183 Inode::CheckPermissions(int accessMode) const
184 {
185 	// you never have write access to a read-only volume
186 	if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly())
187 		return B_READ_ONLY_DEVICE;
188 
189 	return check_access_permissions(accessMode, Mode(), (gid_t)fNode.GroupID(),
190 		(uid_t)fNode.UserID());
191 }
192 
193 
194 status_t
195 Inode::FindBlock(off_t offset, fsblock_t& block, uint32 *_count)
196 {
197 	if (Flags() & EXT2_INODE_EXTENTS) {
198 		ExtentStream stream(fVolume, &fNode.extent_stream, Size());
199 		return stream.FindBlock(offset, block, _count);
200 	}
201 	DataStream stream(fVolume, &fNode.stream, Size());
202 	return stream.FindBlock(offset, block, _count);
203 }
204 
205 
206 status_t
207 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
208 {
209 	size_t length = *_length;
210 
211 	// set/check boundaries for pos/length
212 	if (pos < 0) {
213 		ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %" B_PRIdOFF
214 			", length %" B_PRIuSIZE ")\n", ID(), pos, length);
215 		return B_BAD_VALUE;
216 	}
217 
218 	if (pos >= Size() || length == 0) {
219 		TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %" B_PRIdOFF ", length %"
220 			B_PRIuSIZE ")\n", ID(), pos, length);
221 		*_length = 0;
222 		return B_NO_ERROR;
223 	}
224 
225 	return file_cache_read(FileCache(), NULL, pos, buffer, _length);
226 }
227 
228 
229 status_t
230 Inode::WriteAt(Transaction& transaction, off_t pos, const uint8* buffer,
231 	size_t* _length)
232 {
233 	TRACE("Inode::WriteAt(%" B_PRIdOFF ", %p, *(%p) = %" B_PRIuSIZE ")\n", pos,
234 		buffer, _length, *_length);
235 	ReadLocker readLocker(fLock);
236 
237 	if (!HasFileCache())
238 		return B_BAD_VALUE;
239 
240 	if (pos < 0)
241 		return B_BAD_VALUE;
242 
243 	readLocker.Unlock();
244 
245 	TRACE("Inode::WriteAt(): Starting transaction\n");
246 	transaction.Start(fVolume->GetJournal());
247 
248 	WriteLocker writeLocker(fLock);
249 
250 	TRACE("Inode::WriteAt(): Updating modification time\n");
251 	struct timespec timespec;
252 	_BigtimeToTimespec(real_time_clock_usecs(), &timespec);
253 	SetModificationTime(&timespec);
254 
255 	// NOTE: Debugging info to find why sometimes resize doesn't happen
256 	size_t length = *_length;
257 #ifdef TRACE_EXT2
258 	off_t oldEnd = pos + length;
259 	TRACE("Inode::WriteAt(): Old calc for end? %x:%x\n",
260 		(int)(oldEnd >> 32), (int)(oldEnd & 0xFFFFFFFF));
261 #endif
262 
263 	off_t end = pos + (off_t)length;
264 	off_t oldSize = Size();
265 
266 	TRACE("Inode::WriteAt(): Old size: %" B_PRIdOFF ":%" B_PRIdOFF
267 		", new size: %" B_PRIdOFF ":%" B_PRIdOFF "\n",
268 		oldSize >> 32, oldSize & 0xFFFFFFFF,
269 		end >> 32, end & 0xFFFFFFFF);
270 
271 	if (end > oldSize) {
272 		status_t status = Resize(transaction, end);
273 		if (status != B_OK) {
274 			*_length = 0;
275 			WriteLockInTransaction(transaction);
276 			return status;
277 		}
278 
279 		status = WriteBack(transaction);
280 		if (status != B_OK) {
281 			*_length = 0;
282 			WriteLockInTransaction(transaction);
283 			return status;
284 		}
285 	}
286 
287 	writeLocker.Unlock();
288 
289 	if (oldSize < pos)
290 		FillGapWithZeros(oldSize, pos);
291 
292 	if (length == 0) {
293 		// Probably just changed the file size with the pos parameter
294 		return B_OK;
295 	}
296 
297 	TRACE("Inode::WriteAt(): Performing write: %p, %" B_PRIdOFF ", %p, %"
298 		B_PRIuSIZE "\n", FileCache(), pos, buffer, *_length);
299 	status_t status = file_cache_write(FileCache(), NULL, pos, buffer,
300 		_length);
301 
302 	WriteLockInTransaction(transaction);
303 
304 	TRACE("Inode::WriteAt(): Done\n");
305 
306 	return status;
307 }
308 
309 
310 status_t
311 Inode::FillGapWithZeros(off_t start, off_t end)
312 {
313 	TRACE("Inode::FileGapWithZeros(%" B_PRIdOFF " - %" B_PRIdOFF ")\n", start,
314 		end);
315 
316 	while (start < end) {
317 		size_t size;
318 
319 		if (end > start + 1024 * 1024 * 1024)
320 			size = 1024 * 1024 * 1024;
321 		else
322 			size = end - start;
323 
324 		TRACE("Inode::FillGapWithZeros(): Calling file_cache_write(%p, NULL, "
325 			"%" B_PRIdOFF ", NULL, &(%" B_PRIuSIZE ") = %p)\n", fCache, start,
326 			size, &size);
327 		status_t status = file_cache_write(fCache, NULL, start, NULL,
328 			&size);
329 		if (status != B_OK)
330 			return status;
331 
332 		start += size;
333 	}
334 
335 	return B_OK;
336 }
337 
338 
339 status_t
340 Inode::Resize(Transaction& transaction, off_t size)
341 {
342 	TRACE("Inode::Resize() ID:%" B_PRIdINO " size: %" B_PRIdOFF "\n", ID(),
343 		size);
344 	if (size < 0)
345 		return B_BAD_VALUE;
346 
347 	off_t oldSize = Size();
348 
349 	if (size == oldSize)
350 		return B_OK;
351 
352 	TRACE("Inode::Resize(): old size: %" B_PRIdOFF ", new size: %" B_PRIdOFF
353 		"\n", oldSize, size);
354 
355 	status_t status;
356 	if (size > oldSize) {
357 		status = _EnlargeDataStream(transaction, size);
358 		if (status != B_OK) {
359 			// Restore original size
360 			_ShrinkDataStream(transaction, oldSize);
361 		}
362 	} else
363 		status = _ShrinkDataStream(transaction, size);
364 
365 	TRACE("Inode::Resize(): Updating file map and cache\n");
366 
367 	if (status != B_OK)
368 		return status;
369 
370 	file_cache_set_size(FileCache(), size);
371 	file_map_set_size(Map(), size);
372 
373 	TRACE("Inode::Resize(): Writing back inode changes. Size: %" B_PRIdOFF
374 		"\n", Size());
375 
376 	return WriteBack(transaction);
377 }
378 
379 
380 status_t
381 Inode::InitDirectory(Transaction& transaction, Inode* parent)
382 {
383 	TRACE("Inode::InitDirectory()\n");
384 	uint32 blockSize = fVolume->BlockSize();
385 
386 	status_t status = Resize(transaction, blockSize);
387 	if (status != B_OK)
388 		return status;
389 
390 	fsblock_t blockNum;
391 	if (Flags() & EXT2_INODE_EXTENTS) {
392 		ExtentStream stream(fVolume, &fNode.extent_stream, Size());
393 		status = stream.FindBlock(0, blockNum);
394 	} else {
395 		DataStream stream(fVolume, &fNode.stream, Size());
396 		status = stream.FindBlock(0, blockNum);
397 	}
398 	if (status != B_OK)
399 		return status;
400 
401 	CachedBlock cached(fVolume);
402 	uint8* block = cached.SetToWritable(transaction, blockNum, true);
403 
404 	HTreeRoot* root = (HTreeRoot*)block;
405 	root->dot.inode_id = fID;
406 	root->dot.entry_length = 12;
407 	root->dot.name_length = 1;
408 	root->dot.file_type = EXT2_TYPE_DIRECTORY;
409 	root->dot_entry_name[0] = '.';
410 
411 	root->dotdot.inode_id = parent == NULL ? fID : parent->ID();
412 	root->dotdot.entry_length = blockSize - 12;
413 	root->dotdot.name_length = 2;
414 	root->dotdot.file_type = EXT2_TYPE_DIRECTORY;
415 	root->dotdot_entry_name[0] = '.';
416 	root->dotdot_entry_name[1] = '.';
417 
418 	parent->IncrementNumLinks(transaction);
419 
420 	return parent->WriteBack(transaction);
421 }
422 
423 
424 status_t
425 Inode::Unlink(Transaction& transaction)
426 {
427 	uint32 numLinks = fNode.NumLinks();
428 	TRACE("Inode::Unlink(): Current links: %" B_PRIu32 "\n", numLinks);
429 
430 	if (numLinks == 0)
431 		return B_BAD_VALUE;
432 
433 	if ((IsDirectory() && numLinks == 2) || (numLinks == 1))  {
434 		fUnlinked = true;
435 
436 		TRACE("Inode::Unlink(): Putting inode in orphan list\n");
437 		ino_t firstOrphanID;
438 		status_t status = fVolume->SaveOrphan(transaction, fID, firstOrphanID);
439 		if (status != B_OK)
440 			return status;
441 
442 		if (firstOrphanID != 0) {
443 			Vnode firstOrphan(fVolume, firstOrphanID);
444 			Inode* nextOrphan;
445 
446 			status = firstOrphan.Get(&nextOrphan);
447 			if (status != B_OK)
448 				return status;
449 
450 			fNode.SetNextOrphan(nextOrphan->ID());
451 		} else {
452 			// Next orphan link is stored in deletion time
453 			fNode.deletion_time = 0;
454 		}
455 
456 		fNode.num_links = 0;
457 
458 		status = remove_vnode(fVolume->FSVolume(), fID);
459 		if (status != B_OK)
460 			return status;
461 	} else
462 		fNode.SetNumLinks(--numLinks);
463 
464 	return WriteBack(transaction);
465 }
466 
467 
468 /*static*/ status_t
469 Inode::Create(Transaction& transaction, Inode* parent, const char* name,
470 	int32 mode, int openMode, uint8 type, bool* _created, ino_t* _id,
471 	Inode** _inode, fs_vnode_ops* vnodeOps, uint32 publishFlags)
472 {
473 	TRACE("Inode::Create()\n");
474 	Volume* volume = transaction.GetVolume();
475 
476 	DirectoryIterator* entries = NULL;
477 	ObjectDeleter<DirectoryIterator> entriesDeleter;
478 
479 	if (parent != NULL) {
480 		parent->WriteLockInTransaction(transaction);
481 
482 		TRACE("Inode::Create(): Looking up entry destination\n");
483 		HTree htree(volume, parent);
484 
485 		status_t status = htree.Lookup(name, &entries);
486 		if (status == B_ENTRY_NOT_FOUND) {
487 			panic("We need to add the first node.\n");
488 			return B_ERROR;
489 		}
490 		if (status != B_OK)
491 			return status;
492 		entriesDeleter.SetTo(entries);
493 
494 		TRACE("Inode::Create(): Looking up to see if file already exists\n");
495 		ino_t entryID;
496 
497 		status = entries->FindEntry(name, &entryID);
498 		if (status == B_OK) {
499 			// File already exists
500 			TRACE("Inode::Create(): File already exists\n");
501 			if (S_ISDIR(mode) || S_ISLNK(mode) || (openMode & O_EXCL) != 0)
502 				return B_FILE_EXISTS;
503 
504 			Vnode vnode(volume, entryID);
505 			Inode* inode;
506 
507 			status = vnode.Get(&inode);
508 			if (status != B_OK) {
509 				TRACE("Inode::Create() Failed to get the inode from the "
510 					"vnode\n");
511 				return B_ENTRY_NOT_FOUND;
512 			}
513 
514 			if (inode->IsDirectory() && (openMode & O_RWMASK) != O_RDONLY)
515 				return B_IS_A_DIRECTORY;
516 			if ((openMode & O_DIRECTORY) != 0 && !inode->IsDirectory())
517 				return B_NOT_A_DIRECTORY;
518 
519 			if (inode->CheckPermissions(open_mode_to_access(openMode)
520 					| ((openMode & O_TRUNC) != 0 ? W_OK : 0)) != B_OK)
521 				return B_NOT_ALLOWED;
522 
523 			if ((openMode & O_TRUNC) != 0) {
524 				// Truncate requested
525 				TRACE("Inode::Create(): Truncating file\n");
526 				inode->WriteLockInTransaction(transaction);
527 
528 				status = inode->Resize(transaction, 0);
529 				if (status != B_OK)
530 					return status;
531 			}
532 
533 			if (_created != NULL)
534 				*_created = false;
535 			if (_id != NULL)
536 				*_id = inode->ID();
537 			if (_inode != NULL)
538 				*_inode = inode;
539 
540 			if (_id != NULL || _inode != NULL)
541 				vnode.Keep();
542 
543 			TRACE("Inode::Create(): Done opening file\n");
544 			return B_OK;
545 		/*} else if ((mode & S_ATTR_DIR) == 0) {
546 			TRACE("Inode::Create(): (mode & S_ATTR_DIR) == 0\n");
547 			return B_BAD_VALUE;*/
548 		} else if ((openMode & O_DIRECTORY) != 0) {
549 			TRACE("Inode::Create(): (openMode & O_DIRECTORY) != 0\n");
550 			return B_ENTRY_NOT_FOUND;
551 		}
552 
553 		// Return to initial position
554 		TRACE("Inode::Create(): Restarting iterator\n");
555 		entries->Restart();
556 	}
557 
558 	status_t status;
559 	if (parent != NULL) {
560 		status = parent->CheckPermissions(W_OK);
561 		if (status != B_OK)
562 			return status;
563 	}
564 
565 	TRACE("Inode::Create(): Allocating inode\n");
566 	ino_t id;
567 	status = volume->AllocateInode(transaction, parent, mode, id);
568 	if (status != B_OK) {
569 		ERROR("Inode::Create(): AllocateInode() failed\n");
570 		return status;
571 	}
572 
573 	if (entries != NULL) {
574 		size_t nameLength = strlen(name);
575 		status = entries->AddEntry(transaction, name, nameLength, id, type);
576 		if (status != B_OK) {
577 			ERROR("Inode::Create(): AddEntry() failed\n");
578 			return status;
579 		}
580 	}
581 
582 	TRACE("Inode::Create(): Creating inode\n");
583 	Inode* inode = new(std::nothrow) Inode(volume);
584 	if (inode == NULL)
585 		return B_NO_MEMORY;
586 
587 	TRACE("Inode::Create(): Getting node structure\n");
588 	ext2_inode& node = inode->Node();
589 	TRACE("Inode::Create(): Initializing inode data\n");
590 	memset(&node, 0, sizeof(ext2_inode));
591 	node.SetMode(mode);
592 	node.SetUserID(geteuid());
593 	node.SetGroupID(parent != NULL ? parent->Node().GroupID() : getegid());
594 	node.SetNumLinks(inode->IsDirectory() ? 2 : 1);
595 	TRACE("Inode::Create(): Updating time\n");
596 	struct timespec timespec;
597 	_BigtimeToTimespec(real_time_clock_usecs(), &timespec);
598 	inode->SetAccessTime(&timespec);
599 	inode->SetCreationTime(&timespec);
600 	inode->SetModificationTime(&timespec);
601 	if (parent != NULL)
602 		node.SetFlags(parent->Flags() & EXT2_INODE_INHERITED);
603 	if (volume->HasExtentsFeature()
604 		&& (inode->IsDirectory() || inode->IsFile())) {
605 		node.SetFlag(EXT2_INODE_EXTENTS);
606 		ExtentStream stream(volume, &node.extent_stream, 0);
607 		stream.Init();
608 		ASSERT(stream.Check());
609 	}
610 
611 	if (sizeof(ext2_inode) < volume->InodeSize())
612 		node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE);
613 
614 	TRACE("Inode::Create(): Updating ID\n");
615 	inode->fID = id;
616 
617 	if (inode->IsDirectory()) {
618 		TRACE("Inode::Create(): Initializing directory\n");
619 		status = inode->InitDirectory(transaction, parent);
620 		if (status != B_OK) {
621 			ERROR("Inode::Create(): InitDirectory() failed\n");
622 			delete inode;
623 			return status;
624 		}
625 	}
626 
627 	// TODO: Maybe it can be better
628 	/*if (volume->HasExtendedAttributes()) {
629 		TRACE("Inode::Create(): Initializing extended attributes\n");
630 		uint32 blockGroup = 0;
631 		uint32 pos = 0;
632 		uint32 allocated;
633 
634 		status = volume->AllocateBlocks(transaction, 1, 1, blockGroup, pos,
635 			allocated);
636 		if (status != B_OK)
637 			return status;
638 
639 		// Clear the new block
640 		uint32 blockNum = volume->FirstDataBlock() + pos +
641 			volume->BlocksPerGroup() * blockGroup;
642 		CachedBlock cached(volume);
643 		cached.SetToWritable(transaction, blockNum, true);
644 
645 		node.SetExtendedAttributesBlock(blockNum);
646 	}*/
647 
648 	TRACE("Inode::Create(): Saving inode\n");
649 	status = inode->WriteBack(transaction);
650 	if (status != B_OK) {
651 		delete inode;
652 		return status;
653 	}
654 
655 	TRACE("Inode::Create(): Creating vnode\n");
656 
657 	Vnode vnode;
658 	status = vnode.Publish(transaction, inode, vnodeOps, publishFlags);
659 	if (status != B_OK)
660 		return status;
661 
662 	if (!inode->IsSymLink()) {
663 		// Vnode::Publish doesn't publish symlinks
664 		if (!inode->IsDirectory()) {
665 			status = inode->CreateFileCache();
666 			if (status != B_OK)
667 				return status;
668 		}
669 
670 		inode->WriteLockInTransaction(transaction);
671 	}
672 
673 	if (_created)
674 		*_created = true;
675 	if (_id != NULL)
676 		*_id = id;
677 	if (_inode != NULL)
678 		*_inode = inode;
679 
680 	if (_id != NULL || _inode != NULL)
681 		vnode.Keep();
682 
683 	TRACE("Inode::Create(): Deleting entries iterator\n");
684 	DirectoryIterator* iterator = entriesDeleter.Detach();
685 	TRACE("Inode::Create(): Entries iterator: %p\n", entries);
686 	delete iterator;
687 	TRACE("Inode::Create(): Done\n");
688 
689 	return B_OK;
690 }
691 
692 
693 status_t
694 Inode::CreateFileCache()
695 {
696 	TRACE("Inode::CreateFileCache()\n");
697 
698 	if (fCache != NULL)
699 		return B_OK;
700 
701 	TRACE("Inode::CreateFileCache(): Creating file cache: %" B_PRIu32 ", %"
702 		B_PRIdINO ", %" B_PRIdOFF "\n", fVolume->ID(), ID(), Size());
703 
704 	fCache = file_cache_create(fVolume->ID(), ID(), Size());
705 	if (fCache == NULL) {
706 		ERROR("Inode::CreateFileCache(): Failed to create file cache\n");
707 		return B_ERROR;
708 	}
709 
710 	fMap = file_map_create(fVolume->ID(), ID(), Size());
711 	if (fMap == NULL) {
712 		ERROR("Inode::CreateFileCache(): Failed to create file map\n");
713 		file_cache_delete(fCache);
714 		fCache = NULL;
715 		return B_ERROR;
716 	}
717 
718 	TRACE("Inode::CreateFileCache(): Done\n");
719 
720 	return B_OK;
721 }
722 
723 
724 void
725 Inode::DeleteFileCache()
726 {
727 	TRACE("Inode::DeleteFileCache()\n");
728 
729 	if (fCache == NULL)
730 		return;
731 
732 	file_cache_delete(fCache);
733 	file_map_delete(fMap);
734 
735 	fCache = NULL;
736 	fMap = NULL;
737 }
738 
739 
740 status_t
741 Inode::EnableFileCache()
742 {
743 	if (fCache == NULL)
744 		return B_BAD_VALUE;
745 
746 	file_cache_enable(fCache);
747 	return B_OK;
748 }
749 
750 
751 status_t
752 Inode::DisableFileCache()
753 {
754 	status_t error = file_cache_disable(fCache);
755 	if (error != B_OK)
756 		return error;
757 
758 	return B_OK;
759 }
760 
761 
762 status_t
763 Inode::Sync()
764 {
765 	if (HasFileCache())
766 		return file_cache_sync(fCache);
767 
768 	return B_OK;
769 }
770 
771 
772 void
773 Inode::TransactionDone(bool success)
774 {
775 	if (!success) {
776 		// Revert any changes to the inode
777 		if (fInitStatus == B_OK && UpdateNodeFromDisk() != B_OK)
778 			panic("Failed to reload inode from disk!\n");
779 		else if (fInitStatus == B_NO_INIT) {
780 			// TODO: Unpublish vnode?
781 			panic("Failed to finish creating inode\n");
782 		}
783 	} else {
784 		if (fInitStatus == B_NO_INIT) {
785 			TRACE("Inode::TransactionDone(): Inode creation succeeded\n");
786 			fInitStatus = B_OK;
787 		}
788 	}
789 }
790 
791 
792 void
793 Inode::RemovedFromTransaction()
794 {
795 	TRACE("Inode::RemovedFromTransaction(): Unlocking\n");
796 	rw_lock_write_unlock(&fLock);
797 
798 	put_vnode(fVolume->FSVolume(), ID());
799 }
800 
801 
802 status_t
803 Inode::_EnlargeDataStream(Transaction& transaction, off_t size)
804 {
805 	if (size < 0)
806 		return B_BAD_VALUE;
807 
808 	TRACE("Inode::_EnlargeDataStream()\n");
809 
810 	uint32 blockSize = fVolume->BlockSize();
811 	off_t oldSize = Size();
812 	off_t maxSize = oldSize;
813 	if (maxSize % blockSize != 0)
814 		maxSize += blockSize - maxSize % blockSize;
815 
816 	if (size <= maxSize) {
817 		// No need to allocate more blocks
818 		TRACE("Inode::_EnlargeDataStream(): No need to allocate more blocks\n");
819 		TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n",
820 			size);
821 		fNode.SetSize(size);
822 		return B_OK;
823 	}
824 
825 	off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1;
826 	if (Flags() & EXT2_INODE_EXTENTS) {
827 		ExtentStream stream(fVolume, &fNode.extent_stream, Size());
828 		stream.Enlarge(transaction, end);
829 		ASSERT(stream.Check());
830 	} else {
831 		DataStream stream(fVolume, &fNode.stream, oldSize);
832 		stream.Enlarge(transaction, end);
833 	}
834 	TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n",
835 		size);
836 	fNode.SetSize(size);
837 	TRACE("Inode::_EnlargeDataStream(): Setting allocated block count to %"
838 		B_PRIdOFF "\n", end);
839 	return _SetNumBlocks(_NumBlocks() + end * (fVolume->BlockSize() / 512));
840 }
841 
842 
843 status_t
844 Inode::_ShrinkDataStream(Transaction& transaction, off_t size)
845 {
846 	TRACE("Inode::_ShrinkDataStream()\n");
847 
848 	if (size < 0)
849 		return B_BAD_VALUE;
850 
851 	uint32 blockSize = fVolume->BlockSize();
852 	off_t oldSize = Size();
853 	off_t lastByte = oldSize == 0 ? 0 : oldSize - 1;
854 	off_t minSize = (lastByte / blockSize + 1) * blockSize;
855 		// Minimum size that doesn't require freeing blocks
856 
857 	if (size > minSize) {
858 		// No need to allocate more blocks
859 		TRACE("Inode::_ShrinkDataStream(): No need to allocate more blocks\n");
860 		TRACE("Inode::_ShrinkDataStream(): Setting size to %" B_PRIdOFF "\n",
861 			size);
862 		fNode.SetSize(size);
863 		return B_OK;
864 	}
865 
866 	off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1;
867 	if (Flags() & EXT2_INODE_EXTENTS) {
868 		ExtentStream stream(fVolume, &fNode.extent_stream, Size());
869 		stream.Shrink(transaction, end);
870 		ASSERT(stream.Check());
871 	} else {
872 		DataStream stream(fVolume, &fNode.stream, oldSize);
873 		stream.Shrink(transaction, end);
874 	}
875 
876 	fNode.SetSize(size);
877 	return _SetNumBlocks(_NumBlocks() - end * (fVolume->BlockSize() / 512));
878 }
879 
880 
881 uint64
882 Inode::_NumBlocks()
883 {
884 	if (fVolume->HugeFiles()) {
885 		if (fNode.Flags() & EXT2_INODE_HUGE_FILE)
886 			return fNode.NumBlocks64() * (fVolume->BlockSize() / 512);
887 		else
888 			return fNode.NumBlocks64();
889 	} else
890 		return fNode.NumBlocks();
891 }
892 
893 
894 status_t
895 Inode::_SetNumBlocks(uint64 numBlocks)
896 {
897 	if (numBlocks <= 0xffffffff) {
898 		fNode.SetNumBlocks(numBlocks);
899 		fNode.ClearFlag(EXT2_INODE_HUGE_FILE);
900 		return B_OK;
901 	}
902 	if (!fVolume->HugeFiles())
903 		return E2BIG;
904 
905 	if (numBlocks > 0xffffffffffffULL) {
906 		fNode.SetFlag(EXT2_INODE_HUGE_FILE);
907 		numBlocks /= (fVolume->BlockSize() / 512);
908 	} else
909 		fNode.ClearFlag(EXT2_INODE_HUGE_FILE);
910 
911 	fNode.SetNumBlocks64(numBlocks);
912 	return B_OK;
913 }
914 
915 
916 void
917 Inode::IncrementNumLinks(Transaction& transaction)
918 {
919 	fNode.SetNumLinks(fNode.NumLinks() + 1);
920 	if (IsIndexed() && (fNode.NumLinks() >= EXT2_INODE_MAX_LINKS
921 		|| fNode.NumLinks() == 2)) {
922 		fNode.SetNumLinks(1);
923 		fVolume->ActivateDirNLink(transaction);
924 	}
925 }
926