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