xref: /haiku/src/add-ons/kernel/file_systems/ext2/Inode.cpp (revision 46b7da1f4f40f7157d74fc7fb26ff9ec7f2416f2)
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 
Inode(Volume * volume,ino_t id)39 Inode::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 
Inode(Volume * volume)73 Inode::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 
~Inode()93 Inode::~Inode()
94 {
95 	TRACE("Inode destructor\n");
96 
97 	DeleteFileCache();
98 
99 	TRACE("Inode destructor: Done\n");
100 }
101 
102 
103 status_t
InitCheck()104 Inode::InitCheck()
105 {
106 	return fInitStatus;
107 }
108 
109 
110 void
WriteLockInTransaction(Transaction & transaction)111 Inode::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 
122 status_t
WriteBack(Transaction & transaction)123 Inode::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 
165 status_t
UpdateNodeFromDisk()166 Inode::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 
214 status_t
CheckPermissions(int accessMode) const215 Inode::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 
226 status_t
FindBlock(off_t offset,fsblock_t & block,uint32 * _count)227 Inode::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 
238 status_t
ReadAt(off_t pos,uint8 * buffer,size_t * _length)239 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
240 {
241 	return file_cache_read(FileCache(), NULL, pos, buffer, _length);
242 }
243 
244 
245 status_t
WriteAt(Transaction & transaction,off_t pos,const uint8 * buffer,size_t * _length)246 Inode::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 
326 status_t
FillGapWithZeros(off_t start,off_t end)327 Inode::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 
355 status_t
Resize(Transaction & transaction,off_t size)356 Inode::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 
396 status_t
InitDirectory(Transaction & transaction,Inode * parent)397 Inode::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 
445 status_t
Unlink(Transaction & transaction)446 Inode::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
Create(Transaction & transaction,Inode * parent,const char * name,int32 mode,int openMode,uint8 type,bool * _created,ino_t * _id,Inode ** _inode,fs_vnode_ops * vnodeOps,uint32 publishFlags)492 Inode::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 
723 status_t
CreateFileCache()724 Inode::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 
754 void
DeleteFileCache()755 Inode::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 
770 status_t
EnableFileCache()771 Inode::EnableFileCache()
772 {
773 	if (fCache == NULL)
774 		return B_BAD_VALUE;
775 
776 	file_cache_enable(fCache);
777 	return B_OK;
778 }
779 
780 
781 status_t
DisableFileCache()782 Inode::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 
792 status_t
Sync()793 Inode::Sync()
794 {
795 	if (HasFileCache())
796 		return file_cache_sync(fCache);
797 
798 	return B_OK;
799 }
800 
801 
802 void
TransactionDone(bool success)803 Inode::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 
813 void
RemovedFromTransaction()814 Inode::RemovedFromTransaction()
815 {
816 	TRACE("Inode::RemovedFromTransaction(): Unlocking\n");
817 	rw_lock_write_unlock(&fLock);
818 
819 	put_vnode(fVolume->FSVolume(), ID());
820 }
821 
822 
823 status_t
_EnlargeDataStream(Transaction & transaction,off_t size)824 Inode::_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 
864 status_t
_ShrinkDataStream(Transaction & transaction,off_t size)865 Inode::_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 
902 uint64
NumBlocks()903 Inode::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 
915 status_t
_SetNumBlocks(uint64 numBlocks)916 Inode::_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 
937 void
IncrementNumLinks(Transaction & transaction)938 Inode::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 
949 uint32
_InodeChecksum(ext2_inode * inode)950 Inode::_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 
980 ext2_dir_entry_tail*
_DirEntryTail(uint8 * block) const981 Inode::_DirEntryTail(uint8* block) const
982 {
983 	return (ext2_dir_entry_tail*)(block + fVolume->BlockSize()
984 		- sizeof(ext2_dir_entry_tail));
985 }
986 
987 
988 uint32
_DirEntryChecksum(uint8 * block,uint32 id,uint32 gen) const989 Inode::_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 
1001 void
SetDirEntryChecksum(uint8 * block,uint32 id,uint32 gen)1002 Inode::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 
1013 void
SetDirEntryChecksum(uint8 * block)1014 Inode::SetDirEntryChecksum(uint8* block)
1015 {
1016 	SetDirEntryChecksum(block, fID, fNode.generation);
1017 }
1018 
1019 
1020 uint32
_ExtentLength(ext2_extent_stream * stream) const1021 Inode::_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 
1029 uint32
_ExtentChecksum(ext2_extent_stream * stream) const1030 Inode::_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 
1043 void
SetExtentChecksum(ext2_extent_stream * stream)1044 Inode::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 
1055 bool
VerifyExtentChecksum(ext2_extent_stream * stream)1056 Inode::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