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