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