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