xref: /haiku/src/add-ons/kernel/file_systems/ext2/Inode.cpp (revision a07cdb6e9f8e484b6ba9f209fbeb144e906d3405)
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 	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 	// get node permissions
190 	mode_t mode = Mode();
191 	int userPermissions = (mode & S_IRWXU) >> 6;
192 	int groupPermissions = (mode & S_IRWXG) >> 3;
193 	int otherPermissions = mode & S_IRWXO;
194 
195 	// get the node permissions for this uid/gid
196 	int permissions = 0;
197 	uid_t uid = geteuid();
198 	gid_t gid = getegid();
199 
200 	if (uid == 0) {
201 		// user is root
202 		// root has always read/write permission, but at least one of the
203 		// X bits must be set for execute permission
204 		permissions = userPermissions | groupPermissions | otherPermissions
205 			| R_OK | W_OK;
206 	} else if (uid == (uid_t)fNode.UserID()) {
207 		// user is node owner
208 		permissions = userPermissions;
209 	} else if (gid == (gid_t)fNode.GroupID()) {
210 		// user is in owning group
211 		permissions = groupPermissions;
212 	} else {
213 		// user is one of the others
214 		permissions = otherPermissions;
215 	}
216 
217 	return (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
218 }
219 
220 
221 status_t
222 Inode::FindBlock(off_t offset, fsblock_t& block, uint32 *_count)
223 {
224 	if (Flags() & EXT2_INODE_EXTENTS) {
225 		ExtentStream stream(fVolume, &fNode.extent_stream, Size());
226 		return stream.FindBlock(offset, block, _count);
227 	}
228 	DataStream stream(fVolume, &fNode.stream, Size());
229 	return stream.FindBlock(offset, block, _count);
230 }
231 
232 
233 status_t
234 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
235 {
236 	size_t length = *_length;
237 
238 	// set/check boundaries for pos/length
239 	if (pos < 0) {
240 		ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %" B_PRIdOFF
241 			", length %" B_PRIuSIZE ")\n", ID(), pos, length);
242 		return B_BAD_VALUE;
243 	}
244 
245 	if (pos >= Size() || length == 0) {
246 		TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %" B_PRIdOFF ", length %"
247 			B_PRIuSIZE ")\n", ID(), pos, length);
248 		*_length = 0;
249 		return B_NO_ERROR;
250 	}
251 
252 	return file_cache_read(FileCache(), NULL, pos, buffer, _length);
253 }
254 
255 
256 status_t
257 Inode::WriteAt(Transaction& transaction, off_t pos, const uint8* buffer,
258 	size_t* _length)
259 {
260 	TRACE("Inode::WriteAt(%" B_PRIdOFF ", %p, *(%p) = %" B_PRIuSIZE ")\n", pos,
261 		buffer, _length, *_length);
262 	ReadLocker readLocker(fLock);
263 
264 	if (!HasFileCache())
265 		return B_BAD_VALUE;
266 
267 	if (pos < 0)
268 		return B_BAD_VALUE;
269 
270 	readLocker.Unlock();
271 
272 	TRACE("Inode::WriteAt(): Starting transaction\n");
273 	transaction.Start(fVolume->GetJournal());
274 
275 	WriteLocker writeLocker(fLock);
276 
277 	TRACE("Inode::WriteAt(): Updating modification time\n");
278 	struct timespec timespec;
279 	_BigtimeToTimespec(real_time_clock_usecs(), &timespec);
280 	SetModificationTime(&timespec);
281 
282 	// NOTE: Debugging info to find why sometimes resize doesn't happen
283 	size_t length = *_length;
284 #ifdef TRACE_EXT2
285 	off_t oldEnd = pos + length;
286 	TRACE("Inode::WriteAt(): Old calc for end? %x:%x\n",
287 		(int)(oldEnd >> 32), (int)(oldEnd & 0xFFFFFFFF));
288 #endif
289 
290 	off_t end = pos + (off_t)length;
291 	off_t oldSize = Size();
292 
293 	TRACE("Inode::WriteAt(): Old size: %" B_PRIdOFF ":%" B_PRIdOFF
294 		", new size: %" B_PRIdOFF ":%" B_PRIdOFF "\n",
295 		oldSize >> 32, oldSize & 0xFFFFFFFF,
296 		end >> 32, end & 0xFFFFFFFF);
297 
298 	if (end > oldSize) {
299 		status_t status = Resize(transaction, end);
300 		if (status != B_OK) {
301 			*_length = 0;
302 			WriteLockInTransaction(transaction);
303 			return status;
304 		}
305 
306 		status = WriteBack(transaction);
307 		if (status != B_OK) {
308 			*_length = 0;
309 			WriteLockInTransaction(transaction);
310 			return status;
311 		}
312 	}
313 
314 	writeLocker.Unlock();
315 
316 	if (oldSize < pos)
317 		FillGapWithZeros(oldSize, pos);
318 
319 	if (length == 0) {
320 		// Probably just changed the file size with the pos parameter
321 		return B_OK;
322 	}
323 
324 	TRACE("Inode::WriteAt(): Performing write: %p, %" B_PRIdOFF ", %p, %"
325 		B_PRIuSIZE "\n", FileCache(), pos, buffer, *_length);
326 	status_t status = file_cache_write(FileCache(), NULL, pos, buffer,
327 		_length);
328 
329 	WriteLockInTransaction(transaction);
330 
331 	TRACE("Inode::WriteAt(): Done\n");
332 
333 	return status;
334 }
335 
336 
337 status_t
338 Inode::FillGapWithZeros(off_t start, off_t end)
339 {
340 	TRACE("Inode::FileGapWithZeros(%" B_PRIdOFF " - %" B_PRIdOFF ")\n", start,
341 		end);
342 
343 	while (start < end) {
344 		size_t size;
345 
346 		if (end > start + 1024 * 1024 * 1024)
347 			size = 1024 * 1024 * 1024;
348 		else
349 			size = end - start;
350 
351 		TRACE("Inode::FillGapWithZeros(): Calling file_cache_write(%p, NULL, "
352 			"%" B_PRIdOFF ", NULL, &(%" B_PRIuSIZE ") = %p)\n", fCache, start,
353 			size, &size);
354 		status_t status = file_cache_write(fCache, NULL, start, NULL,
355 			&size);
356 		if (status != B_OK)
357 			return status;
358 
359 		start += size;
360 	}
361 
362 	return B_OK;
363 }
364 
365 
366 status_t
367 Inode::Resize(Transaction& transaction, off_t size)
368 {
369 	TRACE("Inode::Resize() ID:%" B_PRIdINO " size: %" B_PRIdOFF "\n", ID(),
370 		size);
371 	if (size < 0)
372 		return B_BAD_VALUE;
373 
374 	off_t oldSize = Size();
375 
376 	if (size == oldSize)
377 		return B_OK;
378 
379 	TRACE("Inode::Resize(): old size: %" B_PRIdOFF ", new size: %" B_PRIdOFF
380 		"\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: %" B_PRIdOFF
401 		"\n", Size());
402 
403 	return WriteBack(transaction);
404 }
405 
406 
407 status_t
408 Inode::InitDirectory(Transaction& transaction, Inode* parent)
409 {
410 	TRACE("Inode::InitDirectory()\n");
411 	uint32 blockSize = fVolume->BlockSize();
412 
413 	status_t status = Resize(transaction, blockSize);
414 	if (status != B_OK)
415 		return status;
416 
417 	fsblock_t blockNum;
418 	if (Flags() & EXT2_INODE_EXTENTS) {
419 		ExtentStream stream(fVolume, &fNode.extent_stream, Size());
420 		status = stream.FindBlock(0, blockNum);
421 	} else {
422 		DataStream stream(fVolume, &fNode.stream, Size());
423 		status = stream.FindBlock(0, blockNum);
424 	}
425 	if (status != B_OK)
426 		return status;
427 
428 	CachedBlock cached(fVolume);
429 	uint8* block = cached.SetToWritable(transaction, blockNum, true);
430 
431 	HTreeRoot* root = (HTreeRoot*)block;
432 	root->dot.inode_id = fID;
433 	root->dot.entry_length = 12;
434 	root->dot.name_length = 1;
435 	root->dot.file_type = EXT2_TYPE_DIRECTORY;
436 	root->dot_entry_name[0] = '.';
437 
438 	root->dotdot.inode_id = parent == NULL ? fID : parent->ID();
439 	root->dotdot.entry_length = blockSize - 12;
440 	root->dotdot.name_length = 2;
441 	root->dotdot.file_type = EXT2_TYPE_DIRECTORY;
442 	root->dotdot_entry_name[0] = '.';
443 	root->dotdot_entry_name[1] = '.';
444 
445 	parent->IncrementNumLinks(transaction);
446 
447 	return parent->WriteBack(transaction);
448 }
449 
450 
451 status_t
452 Inode::Unlink(Transaction& transaction)
453 {
454 	uint32 numLinks = fNode.NumLinks();
455 	TRACE("Inode::Unlink(): Current links: %" B_PRIu32 "\n", numLinks);
456 
457 	if (numLinks == 0)
458 		return B_BAD_VALUE;
459 
460 	if ((IsDirectory() && numLinks == 2) || (numLinks == 1))  {
461 		fUnlinked = true;
462 
463 		TRACE("Inode::Unlink(): Putting inode in orphan list\n");
464 		ino_t firstOrphanID;
465 		status_t status = fVolume->SaveOrphan(transaction, fID, firstOrphanID);
466 		if (status != B_OK)
467 			return status;
468 
469 		if (firstOrphanID != 0) {
470 			Vnode firstOrphan(fVolume, firstOrphanID);
471 			Inode* nextOrphan;
472 
473 			status = firstOrphan.Get(&nextOrphan);
474 			if (status != B_OK)
475 				return status;
476 
477 			fNode.SetNextOrphan(nextOrphan->ID());
478 		} else {
479 			// Next orphan link is stored in deletion time
480 			fNode.deletion_time = 0;
481 		}
482 
483 		fNode.num_links = 0;
484 
485 		status = remove_vnode(fVolume->FSVolume(), fID);
486 		if (status != B_OK)
487 			return status;
488 	} else
489 		fNode.SetNumLinks(--numLinks);
490 
491 	return WriteBack(transaction);
492 }
493 
494 
495 /*static*/ status_t
496 Inode::Create(Transaction& transaction, Inode* parent, const char* name,
497 	int32 mode, int openMode, uint8 type, bool* _created, ino_t* _id,
498 	Inode** _inode, fs_vnode_ops* vnodeOps, uint32 publishFlags)
499 {
500 	TRACE("Inode::Create()\n");
501 	Volume* volume = transaction.GetVolume();
502 
503 	DirectoryIterator* entries = NULL;
504 	ObjectDeleter<DirectoryIterator> entriesDeleter;
505 
506 	if (parent != NULL) {
507 		parent->WriteLockInTransaction(transaction);
508 
509 		TRACE("Inode::Create(): Looking up entry destination\n");
510 		HTree htree(volume, parent);
511 
512 		status_t status = htree.Lookup(name, &entries);
513 		if (status == B_ENTRY_NOT_FOUND) {
514 			panic("We need to add the first node.\n");
515 			return B_ERROR;
516 		}
517 		if (status != B_OK)
518 			return status;
519 		entriesDeleter.SetTo(entries);
520 
521 		TRACE("Inode::Create(): Looking up to see if file already exists\n");
522 		ino_t entryID;
523 
524 		status = entries->FindEntry(name, &entryID);
525 		if (status == B_OK) {
526 			// File already exists
527 			TRACE("Inode::Create(): File already exists\n");
528 			if (S_ISDIR(mode) || S_ISLNK(mode) || (openMode & O_EXCL) != 0)
529 				return B_FILE_EXISTS;
530 
531 			Vnode vnode(volume, entryID);
532 			Inode* inode;
533 
534 			status = vnode.Get(&inode);
535 			if (status != B_OK) {
536 				TRACE("Inode::Create() Failed to get the inode from the "
537 					"vnode\n");
538 				return B_ENTRY_NOT_FOUND;
539 			}
540 
541 			if (inode->IsDirectory() && (openMode & O_RWMASK) != O_RDONLY)
542 				return B_IS_A_DIRECTORY;
543 			if ((openMode & O_DIRECTORY) != 0 && !inode->IsDirectory())
544 				return B_NOT_A_DIRECTORY;
545 
546 			if (inode->CheckPermissions(open_mode_to_access(openMode)
547 					| ((openMode & O_TRUNC) != 0 ? W_OK : 0)) != B_OK)
548 				return B_NOT_ALLOWED;
549 
550 			if ((openMode & O_TRUNC) != 0) {
551 				// Truncate requested
552 				TRACE("Inode::Create(): Truncating file\n");
553 				inode->WriteLockInTransaction(transaction);
554 
555 				status = inode->Resize(transaction, 0);
556 				if (status != B_OK)
557 					return status;
558 			}
559 
560 			if (_created != NULL)
561 				*_created = false;
562 			if (_id != NULL)
563 				*_id = inode->ID();
564 			if (_inode != NULL)
565 				*_inode = inode;
566 
567 			if (_id != NULL || _inode != NULL)
568 				vnode.Keep();
569 
570 			TRACE("Inode::Create(): Done opening file\n");
571 			return B_OK;
572 		/*} else if ((mode & S_ATTR_DIR) == 0) {
573 			TRACE("Inode::Create(): (mode & S_ATTR_DIR) == 0\n");
574 			return B_BAD_VALUE;*/
575 		} else if ((openMode & O_DIRECTORY) != 0) {
576 			TRACE("Inode::Create(): (openMode & O_DIRECTORY) != 0\n");
577 			return B_ENTRY_NOT_FOUND;
578 		}
579 
580 		// Return to initial position
581 		TRACE("Inode::Create(): Restarting iterator\n");
582 		entries->Restart();
583 	}
584 
585 	status_t status;
586 	if (parent != NULL) {
587 		status = parent->CheckPermissions(W_OK);
588 		if (status != B_OK)
589 			return status;
590 	}
591 
592 	TRACE("Inode::Create(): Allocating inode\n");
593 	ino_t id;
594 	status = volume->AllocateInode(transaction, parent, mode, id);
595 	if (status != B_OK) {
596 		ERROR("Inode::Create(): AllocateInode() failed\n");
597 		return status;
598 	}
599 
600 	if (entries != NULL) {
601 		size_t nameLength = strlen(name);
602 		status = entries->AddEntry(transaction, name, nameLength, id, type);
603 		if (status != B_OK) {
604 			ERROR("Inode::Create(): AddEntry() failed\n");
605 			return status;
606 		}
607 	}
608 
609 	TRACE("Inode::Create(): Creating inode\n");
610 	Inode* inode = new(std::nothrow) Inode(volume);
611 	if (inode == NULL)
612 		return B_NO_MEMORY;
613 
614 	TRACE("Inode::Create(): Getting node structure\n");
615 	ext2_inode& node = inode->Node();
616 	TRACE("Inode::Create(): Initializing inode data\n");
617 	memset(&node, 0, sizeof(ext2_inode));
618 	node.SetMode(mode);
619 	node.SetUserID(geteuid());
620 	node.SetGroupID(parent != NULL ? parent->Node().GroupID() : getegid());
621 	node.SetNumLinks(inode->IsDirectory() ? 2 : 1);
622 	TRACE("Inode::Create(): Updating time\n");
623 	struct timespec timespec;
624 	_BigtimeToTimespec(real_time_clock_usecs(), &timespec);
625 	inode->SetAccessTime(&timespec);
626 	inode->SetCreationTime(&timespec);
627 	inode->SetModificationTime(&timespec);
628 	if (parent != NULL)
629 		node.SetFlags(parent->Flags() & EXT2_INODE_INHERITED);
630 	if (volume->HasExtentsFeature()
631 		&& (inode->IsDirectory() || inode->IsFile())) {
632 		node.SetFlag(EXT2_INODE_EXTENTS);
633 		ExtentStream stream(volume, &node.extent_stream, 0);
634 		stream.Init();
635 		ASSERT(stream.Check());
636 	}
637 
638 	if (sizeof(ext2_inode) < volume->InodeSize())
639 		node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE);
640 
641 	TRACE("Inode::Create(): Updating ID\n");
642 	inode->fID = id;
643 
644 	if (inode->IsDirectory()) {
645 		TRACE("Inode::Create(): Initializing directory\n");
646 		status = inode->InitDirectory(transaction, parent);
647 		if (status != B_OK) {
648 			ERROR("Inode::Create(): InitDirectory() failed\n");
649 			delete inode;
650 			return status;
651 		}
652 	}
653 
654 	// TODO: Maybe it can be better
655 	/*if (volume->HasExtendedAttributes()) {
656 		TRACE("Inode::Create(): Initializing extended attributes\n");
657 		uint32 blockGroup = 0;
658 		uint32 pos = 0;
659 		uint32 allocated;
660 
661 		status = volume->AllocateBlocks(transaction, 1, 1, blockGroup, pos,
662 			allocated);
663 		if (status != B_OK)
664 			return status;
665 
666 		// Clear the new block
667 		uint32 blockNum = volume->FirstDataBlock() + pos +
668 			volume->BlocksPerGroup() * blockGroup;
669 		CachedBlock cached(volume);
670 		cached.SetToWritable(transaction, blockNum, true);
671 
672 		node.SetExtendedAttributesBlock(blockNum);
673 	}*/
674 
675 	TRACE("Inode::Create(): Saving inode\n");
676 	status = inode->WriteBack(transaction);
677 	if (status != B_OK) {
678 		delete inode;
679 		return status;
680 	}
681 
682 	TRACE("Inode::Create(): Creating vnode\n");
683 
684 	Vnode vnode;
685 	status = vnode.Publish(transaction, inode, vnodeOps, publishFlags);
686 	if (status != B_OK)
687 		return status;
688 
689 	if (!inode->IsSymLink()) {
690 		// Vnode::Publish doesn't publish symlinks
691 		if (!inode->IsDirectory()) {
692 			status = inode->CreateFileCache();
693 			if (status != B_OK)
694 				return status;
695 		}
696 
697 		inode->WriteLockInTransaction(transaction);
698 	}
699 
700 	if (_created)
701 		*_created = true;
702 	if (_id != NULL)
703 		*_id = id;
704 	if (_inode != NULL)
705 		*_inode = inode;
706 
707 	if (_id != NULL || _inode != NULL)
708 		vnode.Keep();
709 
710 	TRACE("Inode::Create(): Deleting entries iterator\n");
711 	DirectoryIterator* iterator = entriesDeleter.Detach();
712 	TRACE("Inode::Create(): Entries iterator: %p\n", entries);
713 	delete iterator;
714 	TRACE("Inode::Create(): Done\n");
715 
716 	return B_OK;
717 }
718 
719 
720 status_t
721 Inode::CreateFileCache()
722 {
723 	TRACE("Inode::CreateFileCache()\n");
724 
725 	if (fCache != NULL)
726 		return B_OK;
727 
728 	TRACE("Inode::CreateFileCache(): Creating file cache: %" B_PRIu32 ", %"
729 		B_PRIdINO ", %" B_PRIdOFF "\n", fVolume->ID(), ID(), Size());
730 
731 	fCache = file_cache_create(fVolume->ID(), ID(), Size());
732 	if (fCache == NULL) {
733 		ERROR("Inode::CreateFileCache(): Failed to create file cache\n");
734 		return B_ERROR;
735 	}
736 
737 	fMap = file_map_create(fVolume->ID(), ID(), Size());
738 	if (fMap == NULL) {
739 		ERROR("Inode::CreateFileCache(): Failed to create file map\n");
740 		file_cache_delete(fCache);
741 		fCache = NULL;
742 		return B_ERROR;
743 	}
744 
745 	TRACE("Inode::CreateFileCache(): Done\n");
746 
747 	return B_OK;
748 }
749 
750 
751 void
752 Inode::DeleteFileCache()
753 {
754 	TRACE("Inode::DeleteFileCache()\n");
755 
756 	if (fCache == NULL)
757 		return;
758 
759 	file_cache_delete(fCache);
760 	file_map_delete(fMap);
761 
762 	fCache = NULL;
763 	fMap = NULL;
764 }
765 
766 
767 status_t
768 Inode::EnableFileCache()
769 {
770 	if (fCache == NULL)
771 		return B_BAD_VALUE;
772 
773 	file_cache_enable(fCache);
774 	return B_OK;
775 }
776 
777 
778 status_t
779 Inode::DisableFileCache()
780 {
781 	status_t error = file_cache_disable(fCache);
782 	if (error != B_OK)
783 		return error;
784 
785 	return B_OK;
786 }
787 
788 
789 status_t
790 Inode::Sync()
791 {
792 	if (HasFileCache())
793 		return file_cache_sync(fCache);
794 
795 	return B_OK;
796 }
797 
798 
799 void
800 Inode::TransactionDone(bool success)
801 {
802 	if (!success) {
803 		// Revert any changes to the inode
804 		if (fInitStatus == B_OK && UpdateNodeFromDisk() != B_OK)
805 			panic("Failed to reload inode from disk!\n");
806 		else if (fInitStatus == B_NO_INIT) {
807 			// TODO: Unpublish vnode?
808 			panic("Failed to finish creating inode\n");
809 		}
810 	} else {
811 		if (fInitStatus == B_NO_INIT) {
812 			TRACE("Inode::TransactionDone(): Inode creation succeeded\n");
813 			fInitStatus = B_OK;
814 		}
815 	}
816 }
817 
818 
819 void
820 Inode::RemovedFromTransaction()
821 {
822 	TRACE("Inode::RemovedFromTransaction(): Unlocking\n");
823 	rw_lock_write_unlock(&fLock);
824 
825 	put_vnode(fVolume->FSVolume(), ID());
826 }
827 
828 
829 status_t
830 Inode::_EnlargeDataStream(Transaction& transaction, off_t size)
831 {
832 	if (size < 0)
833 		return B_BAD_VALUE;
834 
835 	TRACE("Inode::_EnlargeDataStream()\n");
836 
837 	uint32 blockSize = fVolume->BlockSize();
838 	off_t oldSize = Size();
839 	off_t maxSize = oldSize;
840 	if (maxSize % blockSize != 0)
841 		maxSize += blockSize - maxSize % blockSize;
842 
843 	if (size <= maxSize) {
844 		// No need to allocate more blocks
845 		TRACE("Inode::_EnlargeDataStream(): No need to allocate more blocks\n");
846 		TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n",
847 			size);
848 		fNode.SetSize(size);
849 		return B_OK;
850 	}
851 
852 	off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1;
853 	if (Flags() & EXT2_INODE_EXTENTS) {
854 		ExtentStream stream(fVolume, &fNode.extent_stream, Size());
855 		stream.Enlarge(transaction, end);
856 		ASSERT(stream.Check());
857 	} else {
858 		DataStream stream(fVolume, &fNode.stream, oldSize);
859 		stream.Enlarge(transaction, end);
860 	}
861 	TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n",
862 		size);
863 	fNode.SetSize(size);
864 	TRACE("Inode::_EnlargeDataStream(): Setting allocated block count to %"
865 		B_PRIdOFF "\n", end);
866 	return _SetNumBlocks(_NumBlocks() + end * (fVolume->BlockSize() / 512));
867 }
868 
869 
870 status_t
871 Inode::_ShrinkDataStream(Transaction& transaction, off_t size)
872 {
873 	TRACE("Inode::_ShrinkDataStream()\n");
874 
875 	if (size < 0)
876 		return B_BAD_VALUE;
877 
878 	uint32 blockSize = fVolume->BlockSize();
879 	off_t oldSize = Size();
880 	off_t lastByte = oldSize == 0 ? 0 : oldSize - 1;
881 	off_t minSize = (lastByte / blockSize + 1) * blockSize;
882 		// Minimum size that doesn't require freeing blocks
883 
884 	if (size > minSize) {
885 		// No need to allocate more blocks
886 		TRACE("Inode::_ShrinkDataStream(): No need to allocate more blocks\n");
887 		TRACE("Inode::_ShrinkDataStream(): Setting size to %" B_PRIdOFF "\n",
888 			size);
889 		fNode.SetSize(size);
890 		return B_OK;
891 	}
892 
893 	off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1;
894 	if (Flags() & EXT2_INODE_EXTENTS) {
895 		ExtentStream stream(fVolume, &fNode.extent_stream, Size());
896 		stream.Shrink(transaction, end);
897 		ASSERT(stream.Check());
898 	} else {
899 		DataStream stream(fVolume, &fNode.stream, oldSize);
900 		stream.Shrink(transaction, end);
901 	}
902 
903 	fNode.SetSize(size);
904 	return _SetNumBlocks(_NumBlocks() - end * (fVolume->BlockSize() / 512));
905 }
906 
907 
908 uint64
909 Inode::_NumBlocks()
910 {
911 	if (fVolume->HugeFiles()) {
912 		if (fNode.Flags() & EXT2_INODE_HUGE_FILE)
913 			return fNode.NumBlocks64() * (fVolume->BlockSize() / 512);
914 		else
915 			return fNode.NumBlocks64();
916 	} else
917 		return fNode.NumBlocks();
918 }
919 
920 
921 status_t
922 Inode::_SetNumBlocks(uint64 numBlocks)
923 {
924 	if (numBlocks <= 0xffffffff) {
925 		fNode.SetNumBlocks(numBlocks);
926 		fNode.ClearFlag(EXT2_INODE_HUGE_FILE);
927 		return B_OK;
928 	}
929 	if (!fVolume->HugeFiles())
930 		return E2BIG;
931 
932 	if (numBlocks > 0xffffffffffffULL) {
933 		fNode.SetFlag(EXT2_INODE_HUGE_FILE);
934 		numBlocks /= (fVolume->BlockSize() / 512);
935 	} else
936 		fNode.ClearFlag(EXT2_INODE_HUGE_FILE);
937 
938 	fNode.SetNumBlocks64(numBlocks);
939 	return B_OK;
940 }
941 
942 
943 void
944 Inode::IncrementNumLinks(Transaction& transaction)
945 {
946 	fNode.SetNumLinks(fNode.NumLinks() + 1);
947 	if (IsIndexed() && (fNode.NumLinks() >= EXT2_INODE_MAX_LINKS
948 		|| fNode.NumLinks() == 2)) {
949 		fNode.SetNumLinks(1);
950 		fVolume->ActivateDirNLink(transaction);
951 	}
952 }
953