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