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