xref: /haiku/src/tests/system/kernel/file_corruption/fs/checksumfs.cpp (revision 268f99dd7dc4bd7474a8bd2742d3f1ec1de6752a)
1 /*
2  * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <dirent.h>
8 #include <string.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 
12 #include <new>
13 
14 #include <fs_interface.h>
15 #include <io_requests.h>
16 #include <NodeMonitor.h>
17 
18 #include <AutoDeleter.h>
19 #include <AutoLocker.h>
20 
21 #include <debug.h>
22 #include <util/AutoLock.h>
23 
24 #include "checksumfs.h"
25 #include "checksumfs_private.h"
26 #include "DebugSupport.h"
27 #include "Directory.h"
28 #include "File.h"
29 #include "Notifications.h"
30 #include "SuperBlock.h"
31 #include "SymLink.h"
32 #include "Transaction.h"
33 #include "Volume.h"
34 
35 
36 static const char* const kCheckSumFSModuleName	= "file_systems/checksumfs"
37 	B_CURRENT_FS_API_VERSION;
38 static const char* const kCheckSumFSShortName	= "checksumfs";
39 
40 static const bigtime_t kModifiedInterimUpdateInterval = 500000;
41 	// wait at least 0.5s between interim modified updates
42 
43 
44 // #pragma mark -
45 
46 
47 struct FileCookie {
48 	mutex		lock;
49 	int			openMode;
50 	bigtime_t	lastModifiedUpdate;
51 	bool		modifiedNeedsUpdate;
52 	bool		sizeChangedSinceUpdate;
53 	bool		modifiedNeedsFinalUpdate;
54 	bool		finalSizeChanged;
55 
56 
FileCookieFileCookie57 	FileCookie(int openMode)
58 		:
59 		openMode(openMode),
60 		lastModifiedUpdate(0),
61 		modifiedNeedsUpdate(false),
62 		sizeChangedSinceUpdate(false),
63 		modifiedNeedsFinalUpdate(false),
64 		finalSizeChanged(false)
65 	{
66 		mutex_init(&lock, "checksumfs file cookie");
67 	}
68 
~FileCookieFileCookie69 	~FileCookie()
70 	{
71 		mutex_destroy(&lock);
72 	}
73 
UpdateModifiedIfNecessaryFileCookie74 	status_t UpdateModifiedIfNecessary(Node* node, bool finalUpdate)
75 	{
76 		MutexLocker locker(lock);
77 
78 		return _UpdateModifiedIfNecessary(node, finalUpdate);
79 	}
80 
FileModifiedFileCookie81 	status_t FileModified(Node* node, bool sizeChanged)
82 	{
83 		MutexLocker locker(lock);
84 
85 		modifiedNeedsUpdate = true;
86 		modifiedNeedsFinalUpdate = true;
87 		sizeChangedSinceUpdate |= sizeChanged;
88 		finalSizeChanged |= sizeChanged;
89 
90 		return _UpdateModifiedIfNecessary(node, false);
91 	}
92 
93 private:
_UpdateModifiedIfNecessaryFileCookie94 	status_t _UpdateModifiedIfNecessary(Node* node, bool finalUpdate)
95 	{
96 		uint32 statFlags = B_STAT_MODIFICATION_TIME | B_STAT_CHANGE_TIME;
97 
98 		if (finalUpdate) {
99 			if (!modifiedNeedsFinalUpdate)
100 				return B_OK;
101 			if (finalSizeChanged)
102 				statFlags |= B_STAT_SIZE;
103 		} else {
104 			if (!modifiedNeedsUpdate)
105 				return B_OK;
106 
107 			if (system_time()
108 					< lastModifiedUpdate + kModifiedInterimUpdateInterval) {
109 				// not enough time passed -- postpone update
110    				return B_OK;
111 			}
112 
113 			statFlags |= B_STAT_INTERIM_UPDATE
114 				| (sizeChangedSinceUpdate ? B_STAT_SIZE : 0);
115 		}
116 
117 		// do the update -- start a transaction, lock the node, and update
118 		Transaction transaction(node->GetVolume());
119 		status_t error = transaction.StartAndAddNode(node);
120 		if (error != B_OK)
121 			return error;
122 
123 		node->Touched(NODE_MODIFIED);
124 
125 		error = transaction.Commit(StatChangedNotification(node, statFlags));
126 		if (error != B_OK)
127 			return error;
128 
129 		modifiedNeedsUpdate = false;
130 		lastModifiedUpdate = system_time();
131 
132 		return B_OK;
133 	}
134 };
135 
136 
137 struct DirCookie {
DirCookieDirCookie138 	DirCookie(Directory* directory)
139 		:
140 		fDirectory(directory)
141 	{
142 		Rewind();
143 	}
144 
GetDirectoryDirCookie145 	Directory* GetDirectory() const
146 	{
147 		return fDirectory;
148 	}
149 
SetToDirCookie150 	void SetTo(Directory* directory, bool skipArtificialEntries)
151 	{
152 		fDirectory = directory;
153 		Rewind(skipArtificialEntries);
154 	}
155 
ReadNextEntryDirCookie156 	status_t ReadNextEntry(struct dirent* buffer, size_t size,
157 		uint32& _countRead)
158 	{
159 		const char* name;
160 		size_t nameLength;
161 		uint64 blockIndex;
162 
163 		int nextIterationState = OTHERS;
164 		switch (fIterationState) {
165 			case DOT:
166 				name = ".";
167 				nameLength = 1;
168 				blockIndex = fDirectory->BlockIndex();
169 				nextIterationState = DOT_DOT;
170 				break;
171 			case DOT_DOT:
172 				name = "..";
173 				nameLength = 2;
174 				blockIndex = fDirectory->ParentDirectory();
175 				break;
176 			default:
177 			{
178 				status_t error = fDirectory->LookupNextEntry(fEntryName,
179 					fEntryName, nameLength, blockIndex);
180 				if (error != B_OK) {
181 					if (error != B_ENTRY_NOT_FOUND)
182 						return error;
183 
184 					_countRead = 0;
185 					return B_OK;
186 				}
187 
188 				name = fEntryName;
189 				break;
190 			}
191 		}
192 
193 		size_t entrySize = sizeof(dirent) + nameLength + 1;
194 		if (entrySize > size)
195 			return B_BUFFER_OVERFLOW;
196 
197 		buffer->d_dev = fDirectory->GetVolume()->ID();
198 		buffer->d_ino = blockIndex;
199 		buffer->d_reclen = entrySize;
200 		strcpy(buffer->d_name, name);
201 
202 		fIterationState = nextIterationState;
203 
204 		_countRead = 1;
205 		return B_OK;
206 	}
207 
RewindDirCookie208 	void Rewind(bool skipArtificialEntries = false)
209 	{
210 		fIterationState = skipArtificialEntries ? OTHERS : DOT;
211 		fEntryName[0] = '\0';
212 	}
213 
214 private:
215 	enum {
216 		DOT,
217 		DOT_DOT,
218 		OTHERS
219 	};
220 
221 	Directory*	fDirectory;
222 	int			fIterationState;
223 	char		fEntryName[kCheckSumFSNameLength + 1];
224 };
225 
226 
227 struct AttrDirCookie {
AttrDirCookieAttrDirCookie228 	AttrDirCookie(Node* node)
229 		:
230 		fNode(node),
231 		fAttributeDirectory(NULL),
232 		fDirCookie(NULL)
233 	{
234 	}
235 
~AttrDirCookieAttrDirCookie236 	~AttrDirCookie()
237 	{
238 		if (fAttributeDirectory != NULL)
239 			fNode->GetVolume()->PutNode(fAttributeDirectory);
240 	}
241 
ReadNextEntryAttrDirCookie242 	status_t ReadNextEntry(struct dirent* buffer, size_t size,
243 		uint32& _countRead)
244 	{
245 		status_t error = _UpdateAttributeDirectory();
246 		if (error != B_OK)
247 			RETURN_ERROR(error);
248 
249 		if (fAttributeDirectory == NULL) {
250 			_countRead = 0;
251 			return B_OK;
252 		}
253 
254 		return fDirCookie.ReadNextEntry(buffer, size, _countRead);
255 	}
256 
RewindAttrDirCookie257 	void Rewind()
258 	{
259 		fDirCookie.Rewind(true);
260 	}
261 
262 private:
_UpdateAttributeDirectoryAttrDirCookie263 	status_t _UpdateAttributeDirectory()
264 	{
265 		uint64 blockIndex = fNode->AttributeDirectory();
266 		if (blockIndex == 0) {
267 			// no (empty) attribute directory
268 			if (fAttributeDirectory != NULL) {
269 				fNode->GetVolume()->PutNode(fAttributeDirectory);
270 				fAttributeDirectory = NULL;
271 			}
272 
273 			return B_OK;
274 		}
275 
276 		if (fAttributeDirectory != NULL) {
277 			if (blockIndex == fAttributeDirectory->BlockIndex())
278 				return B_OK;
279 
280 			// The attribute directory has changed in the meantime -- get rid
281 			// of the old one.
282 			fNode->GetVolume()->PutNode(fAttributeDirectory);
283 			fAttributeDirectory = NULL;
284 		}
285 
286 		// get the attribute directory node
287 		Node* node;
288 		status_t error = fNode->GetVolume()->GetNode(blockIndex, node);
289 		if (error != B_OK)
290 			RETURN_ERROR(error);
291 
292 		fAttributeDirectory = dynamic_cast<Directory*>(node);
293 		if (fAttributeDirectory == NULL) {
294 			fNode->GetVolume()->PutNode(node);
295 			ERROR("checksumfs: attribute directory (%" B_PRIu64 ") of node %"
296 				B_PRIu64 " is not a directory!\n", blockIndex,
297 				fNode->BlockIndex());
298 			RETURN_ERROR(B_BAD_DATA);
299 		}
300 
301 		fDirCookie.SetTo(fAttributeDirectory, true);
302 
303 		return B_OK;
304 	}
305 
306 private:
307 	Node*		fNode;
308 	Directory*	fAttributeDirectory;
309 	DirCookie	fDirCookie;
310 };
311 
312 
313 struct AttributeCookie {
314 	char*		name;
315 	Node*		attribute;
316 	FileCookie*	fileCookie;
317 
AttributeCookieAttributeCookie318 	AttributeCookie(const char* name)
319 		:
320 		name(strdup(name)),
321 		attribute(NULL),
322 		fileCookie(NULL)
323 	{
324 	}
325 
~AttributeCookieAttributeCookie326 	~AttributeCookie()
327 	{
328 		if (attribute != NULL)
329 			attribute->GetVolume()->PutNode(attribute);
330 		delete fileCookie;
331 		free(name);
332 	}
333 };
334 
335 
336 // #pragma mark -
337 
338 
339 static void
set_timespec(timespec & time,uint64 nanos)340 set_timespec(timespec& time, uint64 nanos)
341 {
342 	time.tv_sec = nanos / 1000000000;
343 	time.tv_nsec = nanos % 1000000000;
344 }
345 
346 
347 static uint64
timespec_to_nsecs(const timespec & time)348 timespec_to_nsecs(const timespec& time)
349 {
350 	return (uint64)time.tv_sec * 1000000000 + time.tv_nsec;
351 }
352 
353 
354 struct PutNode {
operator ()PutNode355 	inline void operator()(Node* node)
356 	{
357 		if (node != NULL)
358 			node->GetVolume()->PutNode(node);
359 	}
360 };
361 
362 typedef BPrivate::AutoDeleter<Node, PutNode> NodePutter;
363 
364 
365 static bool
is_user_in_group(gid_t gid)366 is_user_in_group(gid_t gid)
367 {
368 	gid_t groups[NGROUPS_MAX];
369 	int groupCount = getgroups(NGROUPS_MAX, groups);
370 	for (int i = 0; i < groupCount; i++) {
371 		if (gid == groups[i])
372 			return true;
373 	}
374 
375 	return gid == getegid();
376 }
377 
378 
379 static status_t
check_access(Node * node,uint32 accessFlags)380 check_access(Node* node, uint32 accessFlags)
381 {
382 	// Note: we assume that the access flags are compatible with the permission
383 	// bits.
384 	STATIC_ASSERT(R_OK == S_IROTH && W_OK == S_IWOTH && X_OK == S_IXOTH);
385 
386 	// get node permissions
387 	int userPermissions = (node->Mode() & S_IRWXU) >> 6;
388 	int groupPermissions = (node->Mode() & S_IRWXG) >> 3;
389 	int otherPermissions = node->Mode() & S_IRWXO;
390 
391 	// get the permissions for this uid/gid
392 	int permissions = 0;
393 	uid_t uid = geteuid();
394 
395 	if (uid == 0) {
396 		// user is root
397 		// root has always read/write permission, but at least one of the
398 		// X bits must be set for execute permission
399 		permissions = userPermissions | groupPermissions | otherPermissions
400 			| R_OK | W_OK;
401 	} else if (uid == node->UID()) {
402 		// user is node owner
403 		permissions = userPermissions;
404 	} else if (is_user_in_group(node->GID())) {
405 		// user is in owning group
406 		permissions = groupPermissions;
407 	} else {
408 		// user is one of the others
409 		permissions = otherPermissions;
410 	}
411 
412 	return (accessFlags & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
413 }
414 
415 
416 static status_t
remove_entry(fs_volume * fsVolume,fs_vnode * parent,const char * name,bool removeDirectory)417 remove_entry(fs_volume* fsVolume, fs_vnode* parent, const char* name,
418 	bool removeDirectory)
419 {
420 	Volume* volume = (Volume*)fsVolume->private_volume;
421 	Directory* directory
422 		= dynamic_cast<Directory*>((Node*)parent->private_node);
423 	if (directory == NULL)
424 		return B_NOT_A_DIRECTORY;
425 
426 	if (volume->IsReadOnly())
427 		return B_READ_ONLY_DEVICE;
428 
429 	// Since we need to lock both nodes (the directory and the entry's), this
430 	// is a bit cumbersome. We first look up the entry while having the
431 	// directory read-locked, then drop the read lock, write-lock both nodes
432 	// and check whether anything has changed.
433 	Transaction transaction(volume);
434 	Node* childNode;
435 	NodePutter childNodePutter;
436 
437 	while (true) {
438 		// look up the entry
439 		NodeReadLocker directoryLocker(directory);
440 
441 		uint64 blockIndex;
442 		status_t error = directory->LookupEntry(name, blockIndex);
443 		if (error != B_OK)
444 			RETURN_ERROR(error);
445 
446 		directoryLocker.Unlock();
447 
448 		// get the entry's node
449 		error = volume->GetNode(blockIndex, childNode);
450 		if (error != B_OK)
451 			RETURN_ERROR(error);
452 		childNodePutter.SetTo(childNode);
453 
454 		// start the transaction
455 		error = transaction.Start();
456 		if (error != B_OK)
457 			RETURN_ERROR(error);
458 
459 		// write-lock the nodes
460 		error = transaction.AddNodes(directory, childNode);
461 		if (error != B_OK)
462 			RETURN_ERROR(error);
463 
464 		// check the situation again
465 		error = directory->LookupEntry(name, blockIndex);
466 		if (error != B_OK)
467 			RETURN_ERROR(error);
468 		if (blockIndex != childNode->BlockIndex()) {
469 			transaction.Abort();
470 			continue;
471 		}
472 
473 		break;
474 	}
475 
476 	// check permissions
477 	status_t error = check_access(directory, W_OK);
478 	if (error != B_OK)
479 		return error;
480 
481 	// check whether the child node type agrees with our caller
482 	if (removeDirectory) {
483 		if (!S_ISDIR(childNode->Mode()))
484 			RETURN_ERROR(B_NOT_A_DIRECTORY);
485 
486 		// directory must be empty
487 		if (childNode->Size() > 0)
488 			RETURN_ERROR(B_DIRECTORY_NOT_EMPTY);
489 	} else if (S_ISDIR(childNode->Mode()))
490 		RETURN_ERROR(B_IS_A_DIRECTORY);
491 
492 	// remove the entry
493 	error = directory->RemoveEntry(name, transaction);
494 	if (error != B_OK)
495 		RETURN_ERROR(error);
496 
497 	// update stat data
498 	childNode->SetHardLinks(childNode->HardLinks() - 1);
499 
500 	directory->Touched(NODE_MODIFIED);
501 
502 	// remove the child node, if no longer referenced
503 	if (childNode->HardLinks() == 0) {
504 		error = volume->RemoveNode(childNode);
505 		if (error != B_OK)
506 			return error;
507 	}
508 
509 	// commit the transaction
510 	return transaction.Commit(EntryRemovedNotification(directory, name,
511 		childNode));
512 }
513 
514 
515 /*!	Opens the node according to the given open mode (if the permissions allow
516 	that) and creates a file cookie.
517 */
518 static status_t
open_file(Volume * volume,Node * node,int openMode,Transaction & transaction,bool commitTransaction,FileCookie * & _cookie)519 open_file(Volume* volume, Node* node, int openMode, Transaction& transaction,
520 	bool commitTransaction, FileCookie*& _cookie)
521 {
522 	// translate the open mode to required permissions
523 	uint32 accessFlags = 0;
524 	switch (openMode & O_RWMASK) {
525 		case O_RDONLY:
526 			accessFlags = R_OK;
527 			break;
528 		case O_WRONLY:
529 			accessFlags = W_OK;
530 			break;
531 		case O_RDWR:
532 			accessFlags = R_OK | W_OK;
533 			break;
534 	}
535 
536 	// We need to at least read-lock the node. If O_TRUNC is specified, we even
537 	// need a write lock and a transaction.
538 	NodeReadLocker nodeReadLocker;
539 
540 	if ((openMode & O_TRUNC) != 0) {
541 		accessFlags |= W_OK;
542 
543 		status_t error = transaction.IsActive()
544 			? transaction.AddNode(node) : transaction.StartAndAddNode(node);
545 		if (error != B_OK)
546 			RETURN_ERROR(error);
547 	} else if (!transaction.IsNodeLocked(node))
548 		nodeReadLocker.SetTo(node, false);
549 
550 	// check permissions
551 	if ((accessFlags & W_OK) != 0) {
552 		if (volume->IsReadOnly())
553 			return B_READ_ONLY_DEVICE;
554 		if (S_ISDIR(node->Mode()))
555 			return B_IS_A_DIRECTORY;
556 	}
557 
558 	if ((openMode & O_DIRECTORY) != 0 && !S_ISDIR(node->Mode()))
559 		return B_NOT_A_DIRECTORY;
560 
561 	status_t error = check_access(node, accessFlags);
562 	if (error != B_OK)
563 		return error;
564 
565 	// TODO: Support O_NOCACHE.
566 
567 	FileCookie* cookie = new(std::nothrow) FileCookie(openMode);
568 	if (cookie == NULL)
569 		return B_NO_MEMORY;
570 	ObjectDeleter<FileCookie> cookieDeleter(cookie);
571 
572 	// truncate the file, if requested
573 	if ((openMode & O_TRUNC) != 0 && node->Size() > 0) {
574 		error = node->Resize(0, false, transaction);
575 		if (error != B_OK)
576 			return error;
577 
578 		node->Touched(NODE_MODIFIED);
579 
580 		if (commitTransaction) {
581 			uint32 statFlags = B_STAT_MODIFICATION_TIME | B_STAT_CHANGE_TIME
582 				| B_STAT_SIZE;
583 			error = transaction.Commit(StatChangedNotification(node,
584 				statFlags));
585 			if (error != B_OK)
586 				return error;
587 		}
588 	}
589 
590 	_cookie = cookieDeleter.Detach();
591 	return B_OK;
592 }
593 
594 
595 static status_t
create_file(Volume * volume,Directory * directory,const char * name,int openMode,int permissions,Transaction & transaction,bool commitTransaction,FileCookie * & _cookie,Node * & _node,bool & _created)596 create_file(Volume* volume, Directory* directory, const char* name,
597 	int openMode, int permissions, Transaction& transaction,
598 	bool commitTransaction, FileCookie*& _cookie, Node*& _node, bool& _created)
599 {
600 	Node* childNode = NULL;
601 	NodePutter childNodePutter;
602 
603 	// Start the transaction and add the directory. We only need a read lock
604 	// for the lookup, but later we'll need a write lock, if we have to create
605 	// the file. So this is simpler.
606 	status_t error = B_OK;
607 	bool directoryLocked = false;
608 	if (transaction.IsActive()) {
609 		directoryLocked = transaction.IsNodeLocked(directory);
610 		if (!directoryLocked)
611 			error = transaction.AddNode(directory);
612 	} else
613 		error = transaction.StartAndAddNode(directory);
614 	if (error != B_OK)
615 		RETURN_ERROR(error);
616 
617 	// look up the entry
618 	uint64 blockIndex;
619 	error = directory->LookupEntry(name, blockIndex);
620 	if (error == B_OK) {
621 		// the entry already exists
622 		if ((openMode & O_EXCL) != 0)
623 			return B_FILE_EXISTS;
624 
625 		// get the entry's node
626 		error = volume->GetNode(blockIndex, childNode);
627 		if (error != B_OK)
628 			RETURN_ERROR(error);
629 		childNodePutter.SetTo(childNode);
630 
631 		// We can (must even) unlock the directory now. The file won't go
632 		// anywhere, since a transaction is already running.
633 		if (!directoryLocked)
634 			transaction.RemoveNode(directory);
635 
636 		error = open_file(volume, childNode, openMode, transaction,
637 			commitTransaction, _cookie);
638 		if (error != B_OK)
639 			RETURN_ERROR(error);
640 
641 		childNodePutter.Detach();
642 		_node = childNode;
643 		_created = false;
644 		return B_OK;
645 	}
646 
647 	if (error != B_ENTRY_NOT_FOUND)
648 		RETURN_ERROR(error);
649 
650 	// The entry doesn't exist yet. We have to create a new file.
651 
652 	// check the directory write permission
653 	error = check_access(directory, W_OK);
654 	if (error != B_OK)
655 		return error;
656 
657 	// don't create an entry in an unlinked directory
658 	if (directory->HardLinks() == 0)
659 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
660 
661 	// create a file
662 	File* newFile;
663 	error = volume->CreateFile(permissions, transaction, newFile);
664 	if (error != B_OK)
665 		return error;
666 
667 	// insert the new file
668 	error = directory->InsertEntry(name, newFile->BlockIndex(), transaction);
669 	if (error != B_OK)
670 		return error;
671 
672 	// open the file
673 	FileCookie* cookie;
674 	error = open_file(volume, newFile, openMode & ~O_TRUNC, transaction,
675 		false, cookie);
676 	if (error != B_OK)
677 		RETURN_ERROR(error);
678 	ObjectDeleter<FileCookie> cookieDeleter(cookie);
679 
680 	// update stat data
681 	newFile->SetHardLinks(1);
682 	newFile->SetParentDirectory(directory->BlockIndex());
683 
684 	directory->Touched(NODE_MODIFIED);
685 
686 	// announce the new vnode (needed for creating the file cache), but don't
687 	// publish it yet
688 	error = volume->NewNode(newFile);
689 	if (error != B_OK)
690 		RETURN_ERROR(error);
691 
692 	// There's a vnode now -- the File object will be deleted when that is
693 	// removed.
694 	transaction.UpdateNodeFlags(newFile, TRANSACTION_REMOVE_NODE_ON_ERROR);
695 
696 	// create the file cache
697 	error = newFile->InitForVFS();
698 	if (error != B_OK)
699 		return error;
700 
701 	// node is fully initialized -- publish the vnode
702 	error = volume->PublishNode(newFile, 0);
703 	if (error != B_OK) {
704 		// publish_vnode() deletes the vnode on error, but it doesn't call the
705 		// remove_vnode() hook. So we need to make sure the object is deleted.
706 		transaction.UpdateNodeFlags(newFile, TRANSACTION_DELETE_NODE);
707 		RETURN_ERROR(error);
708 	}
709 
710 	// commit the transaction
711 	if (commitTransaction) {
712 		error = transaction.Commit(EntryCreatedNotification(directory, name,
713 			newFile));
714 		if (error != B_OK) {
715 			volume->PutNode(newFile);
716 			RETURN_ERROR(error);
717 		}
718 	}
719 
720 	_cookie = cookieDeleter.Detach();
721 	_node = newFile;
722 	_created = true;
723 
724 	return B_OK;
725 }
726 
727 
728 /*!	Gets the node's attribute directory.
729 	If a transaction is given and the attribute directory doesn't exist, a new
730 	one is created and associate with the node.
731 	On success the caller gets a reference to the attribute directory and is
732 	responsible for putting it. If a transaction was given, the attribute
733 	directory must be put after committing/aborting the transaction.
734 */
735 static status_t
get_attribute_directory(Node * node,Transaction * transaction,Directory * & _attributeDirectory)736 get_attribute_directory(Node* node, Transaction* transaction,
737 	Directory*& _attributeDirectory)
738 {
739 	uint64 blockIndex = node->AttributeDirectory();
740 	Directory* attributeDirectory;
741 
742 	if (blockIndex != 0) {
743 		// get the attribute directory node
744 		Node* attrDirNode;
745 		status_t error = node->GetVolume()->GetNode(blockIndex, attrDirNode);
746 		if (error != B_OK)
747 			RETURN_ERROR(error);
748 
749 		attributeDirectory = dynamic_cast<Directory*>(attrDirNode);
750 		if (attributeDirectory == NULL) {
751 			node->GetVolume()->PutNode(node);
752 			ERROR("checksumfs: attribute directory (%" B_PRIu64 ") of node %"
753 				B_PRIu64 " is not a directory!\n", blockIndex,
754 				node->BlockIndex());
755 			RETURN_ERROR(B_BAD_DATA);
756 		}
757 	} else {
758 		// no (i.e. empty) attribute directory yet
759 		if (transaction == NULL)
760 			return B_ENTRY_NOT_FOUND;
761 
762 		// create a new one
763 		status_t error = node->GetVolume()->CreateDirectory(
764 			S_IRWXU | S_IRWXG | S_IRWXO, *transaction, attributeDirectory);
765 		if (error != B_OK)
766 			RETURN_ERROR(error);
767 
768 		attributeDirectory->SetMode(attributeDirectory->Mode() | S_ATTR_DIR);
769 		attributeDirectory->SetParentDirectory(node->BlockIndex());
770 		attributeDirectory->SetHardLinks(1);
771 		node->SetAttributeDirectory(attributeDirectory->BlockIndex());
772 
773 		// publish it
774 		error = node->GetVolume()->PublishNode(attributeDirectory, 0);
775 		if (error != B_OK)
776 			RETURN_ERROR(error);
777 
778 		// We have published the attribute directory, so don't delete it when
779 		// committing or aborting the transaction. Instead, on error remove it.
780 		transaction->UpdateNodeFlags(attributeDirectory,
781 			TRANSACTION_REMOVE_NODE_ON_ERROR);
782 	}
783 
784 	_attributeDirectory = attributeDirectory;
785 	return B_OK;
786 }
787 
788 
789 // #pragma mark - FS operations
790 
791 
792 static float
checksumfs_identify_partition(int fd,partition_data * partition,void ** _cookie)793 checksumfs_identify_partition(int fd, partition_data* partition,
794 	void** _cookie)
795 {
796 	if ((uint64)partition->size < kCheckSumFSMinSize)
797 		return -1;
798 
799 	SuperBlock* superBlock = new(std::nothrow) SuperBlock;
800 	if (superBlock == NULL)
801 		return -1;
802 	ObjectDeleter<SuperBlock> superBlockDeleter(superBlock);
803 
804 	if (pread(fd, superBlock, sizeof(*superBlock), kCheckSumFSSuperBlockOffset)
805 			!= sizeof(*superBlock)) {
806 		return -1;
807 	}
808 
809 	if (!superBlock->Check((uint64)partition->size / B_PAGE_SIZE))
810 		return -1;
811 
812 	*_cookie = superBlockDeleter.Detach();
813 	return 0.8f;
814 }
815 
816 
817 static status_t
checksumfs_scan_partition(int fd,partition_data * partition,void * cookie)818 checksumfs_scan_partition(int fd, partition_data* partition, void* cookie)
819 {
820 	SuperBlock* superBlock = (SuperBlock*)cookie;
821 
822 	partition->status = B_PARTITION_VALID;
823 	partition->flags |= B_PARTITION_FILE_SYSTEM;
824 	partition->content_size = superBlock->TotalBlocks() * B_PAGE_SIZE;
825 	partition->block_size = B_PAGE_SIZE;
826 	partition->content_name = strdup(superBlock->Name());
827 	if (partition->content_name == NULL)
828 		return B_NO_MEMORY;
829 
830 	return B_OK;
831 }
832 
833 
834 static void
checksumfs_free_identify_partition_cookie(partition_data * partition,void * cookie)835 checksumfs_free_identify_partition_cookie(partition_data* partition,
836 	void* cookie)
837 {
838 	SuperBlock* superBlock = (SuperBlock*)cookie;
839 	delete superBlock;
840 }
841 
842 
843 static status_t
checksumfs_mount(fs_volume * fsVolume,const char * device,uint32 flags,const char * args,ino_t * _rootVnodeID)844 checksumfs_mount(fs_volume* fsVolume, const char* device, uint32 flags,
845 	const char* args, ino_t* _rootVnodeID)
846 {
847 	Volume* volume = new(std::nothrow) Volume(flags);
848 	if (volume == NULL)
849 		RETURN_ERROR(B_NO_MEMORY);
850 	ObjectDeleter<Volume> volumeDeleter(volume);
851 
852 	status_t error = volume->Init(device);
853 	if (error != B_OK)
854 		RETURN_ERROR(error);
855 
856 	error = volume->Mount(fsVolume);
857 	if (error != B_OK)
858 		RETURN_ERROR(error);
859 
860 	fsVolume->private_volume = volumeDeleter.Detach();
861 	fsVolume->ops = &gCheckSumFSVolumeOps;
862 	*_rootVnodeID = volume->RootDirectory()->BlockIndex();
863 
864 	return B_OK;
865 }
866 
867 
868 static status_t
checksumfs_set_content_name(int fd,partition_id partition,const char * name,disk_job_id job)869 checksumfs_set_content_name(int fd, partition_id partition, const char* name,
870 	disk_job_id job)
871 {
872 	// TODO: Implement!
873 	return B_UNSUPPORTED;
874 }
875 
876 
877 static status_t
checksumfs_initialize(int fd,partition_id partition,const char * name,const char * parameters,off_t partitionSize,disk_job_id job)878 checksumfs_initialize(int fd, partition_id partition, const char* name,
879 	const char* parameters, off_t partitionSize, disk_job_id job)
880 {
881 	if (name == NULL || strlen(name) >= kCheckSumFSNameLength)
882 		return B_BAD_VALUE;
883 
884 	// TODO: Forcing a non-empty name here. Superfluous when the userland disk
885 	// system add-on has a parameter editor for it.
886 	if (*name == '\0')
887 		name = "Unnamed";
888 
889 	update_disk_device_job_progress(job, 0);
890 
891 	Volume volume(0);
892 
893 	status_t error = volume.Init(fd, partitionSize / B_PAGE_SIZE);
894 	if (error != B_OK)
895 		return error;
896 
897 	error = volume.Initialize(name);
898 	if (error != B_OK)
899 		return error;
900 
901 	// rescan partition
902 	error = scan_partition(partition);
903 	if (error != B_OK)
904 		return error;
905 
906 	update_disk_device_job_progress(job, 1);
907 
908 	return B_OK;
909 }
910 
911 
912 // #pragma mark - volume operations
913 
914 
915 static status_t
checksumfs_unmount(fs_volume * fsVolume)916 checksumfs_unmount(fs_volume* fsVolume)
917 {
918 	Volume* volume = (Volume*)fsVolume->private_volume;
919 	volume->Unmount();
920 	return B_OK;
921 }
922 
923 
924 static status_t
checksumfs_read_fs_info(fs_volume * fsVolume,struct fs_info * info)925 checksumfs_read_fs_info(fs_volume* fsVolume, struct fs_info* info)
926 {
927 	Volume* volume = (Volume*)fsVolume->private_volume;
928 	volume->GetInfo(*info);
929 	return B_OK;
930 }
931 
932 
933 static status_t
checksumfs_write_fs_info(fs_volume * fsVolume,const struct fs_info * info,uint32 mask)934 checksumfs_write_fs_info(fs_volume* fsVolume, const struct fs_info* info,
935 	uint32 mask)
936 {
937 	Volume* volume = (Volume*)fsVolume->private_volume;
938 
939 	if ((mask & FS_WRITE_FSINFO_NAME) != 0) {
940 		status_t error = volume->SetName(info->volume_name);
941 		if (error != B_OK)
942 			return error;
943 	}
944 
945 	return B_OK;
946 }
947 
948 
949 static status_t
checksumfs_sync(fs_volume * fsVolume)950 checksumfs_sync(fs_volume* fsVolume)
951 {
952 	Volume* volume = (Volume*)fsVolume->private_volume;
953 
954 	return block_cache_sync(volume->BlockCache());
955 }
956 
957 
958 static status_t
checksumfs_get_vnode(fs_volume * fsVolume,ino_t id,fs_vnode * vnode,int * _type,uint32 * _flags,bool reenter)959 checksumfs_get_vnode(fs_volume* fsVolume, ino_t id, fs_vnode* vnode,
960 	int* _type, uint32* _flags, bool reenter)
961 {
962 	Volume* volume = (Volume*)fsVolume->private_volume;
963 
964 	Node* node;
965 	status_t error = volume->ReadNode(id, node);
966 	if (error != B_OK)
967 		return error;
968 
969 	error = node->InitForVFS();
970 	if (error != B_OK) {
971 		delete node;
972 		return error;
973 	}
974 
975 	vnode->private_node = node;
976 	vnode->ops = &gCheckSumFSVnodeOps;
977 	*_type = node->Mode();
978 	*_flags = 0;
979 
980 	return B_OK;
981 }
982 
983 
984 // #pragma mark - vnode operations
985 
986 
987 static status_t
checksumfs_lookup(fs_volume * fsVolume,fs_vnode * fsDir,const char * name,ino_t * _id)988 checksumfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* name,
989 	ino_t* _id)
990 {
991 	Volume* volume = (Volume*)fsVolume->private_volume;
992 	Node* node = (Node*)fsDir->private_node;
993 
994 	Directory* directory = dynamic_cast<Directory*>(node);
995 	if (directory == NULL)
996 		return B_NOT_A_DIRECTORY;
997 
998 	status_t error = check_access(directory, X_OK);
999 	if (error != B_OK)
1000 		return error;
1001 
1002 	NodeReadLocker nodeLocker(node);
1003 
1004 	uint64 blockIndex;
1005 
1006 	if (strcmp(name, ".") == 0) {
1007 		blockIndex = directory->BlockIndex();
1008 	} else if (strcmp(name, "..") == 0) {
1009 		blockIndex = directory->ParentDirectory();
1010 	} else {
1011 		status_t error = directory->LookupEntry(name, blockIndex);
1012 		if (error != B_OK)
1013 			return error;
1014 	}
1015 
1016 	// get the node
1017 	Node* childNode;
1018 	error = volume->GetNode(blockIndex, childNode);
1019 	if (error != B_OK)
1020 		return error;
1021 
1022 	*_id = blockIndex;
1023 	return B_OK;
1024 }
1025 
1026 
1027 static status_t
checksumfs_put_vnode(fs_volume * fsVolume,fs_vnode * vnode,bool reenter)1028 checksumfs_put_vnode(fs_volume* fsVolume, fs_vnode* vnode, bool reenter)
1029 {
1030 	Node* node = (Node*)vnode->private_node;
1031 	delete node;
1032 	return B_OK;
1033 }
1034 
1035 
1036 static status_t
checksumfs_remove_vnode(fs_volume * fsVolume,fs_vnode * vnode,bool reenter)1037 checksumfs_remove_vnode(fs_volume* fsVolume, fs_vnode* vnode, bool reenter)
1038 {
1039 	Volume* volume = (Volume*)fsVolume->private_volume;
1040 	Node* node = (Node*)vnode->private_node;
1041 	return volume->DeleteNode(node);
1042 }
1043 
1044 
1045 
1046 // #pragma mark - asynchronous I/O
1047 
1048 
1049 static status_t
iterative_io_get_vecs_hook(void * cookie,io_request * request,off_t offset,size_t size,file_io_vec * vecs,size_t * _count)1050 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
1051 	size_t size, file_io_vec* vecs, size_t* _count)
1052 {
1053 	File* file = (File*)cookie;
1054 
1055 	RETURN_ERROR(file_map_translate(file->FileMap(), offset, size, vecs, _count,
1056 		B_PAGE_SIZE));
1057 }
1058 
1059 
1060 static status_t
iterative_io_finished_hook(void * cookie,io_request * request,status_t status,bool partialTransfer,size_t bytesTransferred)1061 iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
1062 	bool partialTransfer, size_t bytesTransferred)
1063 {
1064 	File* file = (File*)cookie;
1065 	file->ReadUnlock();
1066 	return B_OK;
1067 }
1068 
1069 
1070 static status_t
checksumfs_io(fs_volume * fsVolume,fs_vnode * vnode,void * cookie,io_request * request)1071 checksumfs_io(fs_volume* fsVolume, fs_vnode* vnode, void* cookie,
1072 	io_request* request)
1073 {
1074 	Volume* volume = (Volume*)fsVolume->private_volume;
1075 	File* file = dynamic_cast<File*>((Node*)vnode->private_node);
1076 	if (file == NULL) {
1077         notify_io_request(request, B_READ_ONLY_DEVICE);
1078 		RETURN_ERROR(B_BAD_VALUE);
1079 	}
1080 
1081 	if (io_request_is_write(request) && volume->IsReadOnly()) {
1082         notify_io_request(request, B_READ_ONLY_DEVICE);
1083 		RETURN_ERROR(B_READ_ONLY_DEVICE);
1084 	}
1085 
1086 	// Read-lock the file -- we'll unlock it in the finished hook.
1087 	if (io_request_is_vip(request)) {
1088 		// We cannot wait for the node lock indefinitely. So try read-locking
1089 		// with a timeout (0.1 s).
1090 		if (!file->ReadLockWithTimeout(B_RELATIVE_TIMEOUT, 100000)) {
1091 	        notify_io_request(request, B_BUSY);
1092 			RETURN_ERROR(B_BUSY);
1093 		}
1094 	} else
1095 		file->ReadLock();
1096 
1097 	RETURN_ERROR(do_iterative_fd_io(volume->FD(), request,
1098 		iterative_io_get_vecs_hook, iterative_io_finished_hook, file));
1099 }
1100 
1101 
1102 // #pragma mark - cache file access
1103 
1104 
1105 static status_t
checksumfs_get_file_map(fs_volume * fsVolume,fs_vnode * vnode,off_t offset,size_t size,struct file_io_vec * vecs,size_t * _count)1106 checksumfs_get_file_map(fs_volume* fsVolume, fs_vnode* vnode, off_t offset,
1107 	size_t size, struct file_io_vec* vecs, size_t* _count)
1108 {
1109 	if (offset < 0)
1110 		RETURN_ERROR(B_BAD_VALUE);
1111 
1112 	File* file = dynamic_cast<File*>((Node*)vnode->private_node);
1113 	if (file == NULL)
1114 		RETURN_ERROR(B_BAD_VALUE);
1115 
1116 	RETURN_ERROR(file->GetFileVecs(offset, size, vecs, *_count, *_count));
1117 }
1118 
1119 
1120 // #pragma mark - common operations
1121 
1122 
1123 static status_t
checksumfs_set_flags(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,int flags)1124 checksumfs_set_flags(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
1125 	int flags)
1126 {
1127 	FileCookie* cookie = (FileCookie*)_cookie;
1128 
1129 	cookie->openMode = (cookie->openMode & ~O_APPEND) | (flags & O_APPEND);
1130 
1131 	// TODO: Also support O_NOCACHE!
1132 
1133 	return B_OK;
1134 }
1135 
1136 
1137 static status_t
checksumfs_fsync(fs_volume * fsVolume,fs_vnode * vnode)1138 checksumfs_fsync(fs_volume* fsVolume, fs_vnode* vnode)
1139 {
1140 	Node* node = (Node*)vnode->private_node;
1141 
1142 	NodeReadLocker nodeLocker(node);
1143 
1144 	return node->Sync();
1145 }
1146 
1147 
1148 static status_t
checksumfs_read_symlink(fs_volume * fsVolume,fs_vnode * vnode,char * buffer,size_t * _bufferSize)1149 checksumfs_read_symlink(fs_volume* fsVolume, fs_vnode* vnode, char* buffer,
1150 	size_t* _bufferSize)
1151 {
1152 	SymLink* symLink = dynamic_cast<SymLink*>((Node*)vnode->private_node);
1153 	if (symLink == NULL)
1154 		RETURN_ERROR(B_BAD_VALUE);
1155 
1156 	status_t error = check_access(symLink, R_OK);
1157 	if (error != B_OK)
1158 		return error;
1159 
1160 	return symLink->ReadSymLink(buffer, *_bufferSize, *_bufferSize);
1161 }
1162 
1163 
1164 static status_t
checksumfs_create_symlink(fs_volume * fsVolume,fs_vnode * parent,const char * name,const char * path,int mode)1165 checksumfs_create_symlink(fs_volume* fsVolume, fs_vnode* parent,
1166 	const char* name, const char* path, int mode)
1167 {
1168 	Volume* volume = (Volume*)fsVolume->private_volume;
1169 	Directory* directory
1170 		= dynamic_cast<Directory*>((Node*)parent->private_node);
1171 	if (directory == NULL)
1172 		return B_NOT_A_DIRECTORY;
1173 
1174 	if (volume->IsReadOnly())
1175 		return B_READ_ONLY_DEVICE;
1176 
1177 	status_t error = check_access(directory, W_OK);
1178 	if (error != B_OK)
1179 		return error;
1180 
1181 	// start a transaction and add the directory (write locks it, too)
1182 	Transaction transaction(volume);
1183 	error = transaction.StartAndAddNode(directory);
1184 	if (error != B_OK)
1185 		return error;
1186 
1187 	// don't create an entry in an unlinked directory
1188 	if (directory->HardLinks() == 0)
1189 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
1190 
1191 	// create a symlink node
1192 	SymLink* newSymLink;
1193 	error = volume->CreateSymLink(mode, transaction, newSymLink);
1194 	if (error != B_OK)
1195 		return error;
1196 
1197 	// write it
1198 	error = newSymLink->WriteSymLink(path, strlen(path), transaction);
1199 	if (error != B_OK)
1200 		return error;
1201 
1202 	// insert the new symlink
1203 	error = directory->InsertEntry(name, newSymLink->BlockIndex(), transaction);
1204 	if (error != B_OK)
1205 		return error;
1206 
1207 	// update stat data
1208 	newSymLink->SetHardLinks(1);
1209 
1210 	directory->Touched(NODE_MODIFIED);
1211 
1212 	// commit the transaction
1213 	return transaction.Commit(EntryCreatedNotification(directory, name,
1214 		newSymLink));
1215 }
1216 
1217 
1218 static status_t
checksumfs_link(fs_volume * fsVolume,fs_vnode * dir,const char * name,fs_vnode * vnode)1219 checksumfs_link(fs_volume* fsVolume, fs_vnode* dir, const char* name,
1220 	fs_vnode* vnode)
1221 {
1222 	Volume* volume = (Volume*)fsVolume->private_volume;
1223 	Node* node = (Node*)vnode->private_node;
1224 	Directory* directory = dynamic_cast<Directory*>((Node*)dir->private_node);
1225 	if (directory == NULL)
1226 		return B_NOT_A_DIRECTORY;
1227 
1228 	if (volume->IsReadOnly())
1229 		return B_READ_ONLY_DEVICE;
1230 
1231 	// don't allow hardlinking directories
1232 	if (S_ISDIR(node->Mode()))
1233 		RETURN_ERROR(B_NOT_ALLOWED);
1234 
1235 	// start a transaction and lock the nodes
1236 	Transaction transaction(volume);
1237 	status_t error = transaction.Start();
1238 	if (error != B_OK)
1239 		RETURN_ERROR(error);
1240 
1241 	error = transaction.AddNodes(directory, node);
1242 	if (error != B_OK)
1243 		RETURN_ERROR(error);
1244 
1245 	// check permissions
1246 	error = check_access(directory, W_OK);
1247 	if (error != B_OK)
1248 		RETURN_ERROR(error);
1249 
1250 	// don't create an entry in an unlinked directory
1251 	if (directory->HardLinks() == 0)
1252 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
1253 
1254 	// insert the new entry
1255 	error = directory->InsertEntry(name, node->BlockIndex(), transaction);
1256 	if (error != B_OK)
1257 		RETURN_ERROR(error);
1258 
1259 	// update stat data
1260 	node->SetHardLinks(node->HardLinks() + 1);
1261 	directory->Touched(NODE_MODIFIED);
1262 
1263 	// commit the transaction
1264 	return transaction.Commit(EntryCreatedNotification(directory, name, node));
1265 }
1266 
1267 
1268 static status_t
checksumfs_unlink(fs_volume * fsVolume,fs_vnode * dir,const char * name)1269 checksumfs_unlink(fs_volume* fsVolume, fs_vnode* dir, const char* name)
1270 {
1271 	return remove_entry(fsVolume, dir, name, false);
1272 }
1273 
1274 
1275 static status_t
checksumfs_rename(fs_volume * fsVolume,fs_vnode * fromDir,const char * fromName,fs_vnode * toDir,const char * toName)1276 checksumfs_rename(fs_volume* fsVolume, fs_vnode* fromDir, const char* fromName,
1277 	fs_vnode* toDir, const char* toName)
1278 {
1279 	Volume* volume = (Volume*)fsVolume->private_volume;
1280 	Directory* fromDirectory
1281 		= dynamic_cast<Directory*>((Node*)fromDir->private_node);
1282 	Directory* toDirectory
1283 		= dynamic_cast<Directory*>((Node*)toDir->private_node);
1284 	if (fromDirectory == NULL || toDirectory == NULL)
1285 		return B_NOT_A_DIRECTORY;
1286 
1287 	if (volume->IsReadOnly())
1288 		return B_READ_ONLY_DEVICE;
1289 
1290 	// We need to write-lock all three nodes (both directories and the moved
1291 	// node). To make that atomic, we have to lock the source directory, look up
1292 	// the entry, unlock the directory, get the node, re-lock all, and look up
1293 	// the entry again to check for changes.
1294 	Transaction transaction(volume);
1295 	Node* node;
1296 	NodePutter nodePutter;
1297 
1298 	while (true) {
1299 		// look up the entry
1300 		NodeReadLocker directoryLocker(fromDirectory);
1301 
1302 		uint64 blockIndex;
1303 		status_t error = fromDirectory->LookupEntry(fromName, blockIndex);
1304 		if (error != B_OK)
1305 			RETURN_ERROR(error);
1306 
1307 		directoryLocker.Unlock();
1308 
1309 		// get the entry's node
1310 		error = volume->GetNode(blockIndex, node);
1311 		if (error != B_OK)
1312 			RETURN_ERROR(error);
1313 		nodePutter.SetTo(node);
1314 
1315 		// start the transaction
1316 		error = transaction.Start();
1317 		if (error != B_OK)
1318 			RETURN_ERROR(error);
1319 
1320 		// write-lock the nodes
1321 		error = fromDirectory != toDirectory
1322 			? transaction.AddNodes(fromDirectory, toDirectory, node)
1323 			: transaction.AddNodes(fromDirectory, node);
1324 		if (error != B_OK)
1325 			RETURN_ERROR(error);
1326 
1327 		// check the situation again
1328 		error = fromDirectory->LookupEntry(fromName, blockIndex);
1329 		if (error != B_OK)
1330 			RETURN_ERROR(error);
1331 		if (blockIndex != node->BlockIndex()) {
1332 			transaction.Abort();
1333 			continue;
1334 		}
1335 
1336 		break;
1337 	}
1338 
1339 	// check permissions
1340 	status_t error = check_access(fromDirectory, W_OK);
1341 	if (error != B_OK)
1342 		RETURN_ERROR(error);
1343 
1344 	error = check_access(toDirectory, W_OK);
1345 	if (error != B_OK)
1346 		RETURN_ERROR(error);
1347 
1348 	// don't create an entry in an unlinked directory
1349 	if (toDirectory->HardLinks() == 0)
1350 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
1351 
1352 	// Check whether this operation would move a directory into one of its
1353 	// descendents. We iterate upwards, checking whether any ancestor of the
1354 	// target directory is the moved directory (if it is a directory that is).
1355 	if (fromDirectory != toDirectory && S_ISDIR(node->Mode())) {
1356 		NodePutter ancestorPutter;
1357 		Node* ancestor = toDirectory;
1358 
1359 		while (ancestor != volume->RootDirectory()
1360 			|| ancestor == fromDirectory) {
1361 			if (ancestor == node)
1362 				RETURN_ERROR(B_BAD_VALUE);
1363 
1364 			error = volume->GetNode(ancestor->ParentDirectory(), ancestor);
1365 			if (error != B_OK)
1366 				RETURN_ERROR(error);
1367 			ancestorPutter.SetTo(ancestor);
1368 		}
1369 	}
1370 
1371 	// Everything looks good -- insert a new entry in the target directory and
1372 	// remove the old entry from the source directory.
1373 	error = toDirectory->InsertEntry(toName, node->BlockIndex(), transaction);
1374 	if (error != B_OK)
1375 		RETURN_ERROR(error);
1376 
1377 	error = fromDirectory->RemoveEntry(fromName, transaction);
1378 	if (error != B_OK)
1379 		RETURN_ERROR(error);
1380 
1381 	// update stat data
1382 	node->SetParentDirectory(toDirectory->BlockIndex());
1383 	fromDirectory->Touched(NODE_MODIFIED);
1384 	toDirectory->Touched(NODE_MODIFIED);
1385 
1386 	// commit the transaction
1387 	return transaction.Commit(EntryMovedNotification(fromDirectory, fromName,
1388 		toDirectory, toName, node));
1389 }
1390 
1391 
1392 static status_t
checksumfs_access(fs_volume * fsVolume,fs_vnode * vnode,int mode)1393 checksumfs_access(fs_volume* fsVolume, fs_vnode* vnode, int mode)
1394 {
1395 	Node* node = (Node*)vnode->private_node;
1396 
1397 	NodeReadLocker nodeLocker(node);
1398 
1399 	return check_access(node, mode);
1400 }
1401 
1402 
1403 static status_t
checksumfs_read_stat(fs_volume * fsVolume,fs_vnode * vnode,struct stat * st)1404 checksumfs_read_stat(fs_volume* fsVolume, fs_vnode* vnode, struct stat* st)
1405 {
1406 	Node* node = (Node*)vnode->private_node;
1407 
1408 	NodeReadLocker nodeLocker(node);
1409 
1410     st->st_mode = node->Mode();
1411     st->st_nlink = node->HardLinks();
1412     st->st_uid = node->UID();
1413     st->st_gid = node->GID();
1414     st->st_size = node->Size();
1415     st->st_blksize = B_PAGE_SIZE * 16;	// random number
1416     set_timespec(st->st_mtim, node->ModificationTime());
1417     set_timespec(st->st_ctim, node->ChangeTime());
1418     set_timespec(st->st_crtim, node->CreationTime());
1419     set_timespec(st->st_atim, node->AccessedTime());
1420     st->st_type = 0;        /* attribute/index type */
1421     st->st_blocks = 1 + (st->st_size + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
1422 		// TODO: That does neither count management structures for the content
1423 		// (for files) nor attributes.
1424 
1425 	return B_OK;
1426 }
1427 
1428 
1429 static status_t
checksumfs_write_stat(fs_volume * fsVolume,fs_vnode * vnode,const struct stat * st,uint32 statMask)1430 checksumfs_write_stat(fs_volume* fsVolume, fs_vnode* vnode,
1431 	const struct stat* st, uint32 statMask)
1432 {
1433 	Volume* volume = (Volume*)fsVolume->private_volume;
1434 	Node* node = (Node*)vnode->private_node;
1435 
1436 	if (volume->IsReadOnly())
1437 		return B_READ_ONLY_DEVICE;
1438 
1439 	// start a transaction and add the node to it (write locks the node, too)
1440 	Transaction transaction(volume);
1441 	status_t error = transaction.StartAndAddNode(node);
1442 	if (error != B_OK)
1443 		return error;
1444 
1445 	uid_t uid = geteuid();
1446 	bool isOwnerOrRoot = uid == 0 || uid == node->UID();
1447 	bool hasWriteAccess = check_access(node, W_OK) == B_OK;
1448 
1449 	bool updateModified = false;
1450 	bool updateChanged = false;
1451 
1452 	if ((statMask & B_STAT_SIZE) != 0 && (uint64)st->st_size != node->Size()) {
1453 		if (!hasWriteAccess)
1454 			RETURN_ERROR(B_NOT_ALLOWED);
1455 
1456 		error = node->Resize(st->st_size, true, transaction);
1457 		if (error != B_OK)
1458 			RETURN_ERROR(error);
1459 
1460 		updateModified = updateChanged = true;
1461 	}
1462 
1463 	if ((statMask & B_STAT_UID) != 0 && st->st_uid != node->UID()) {
1464 		// only root can do that
1465 		if (uid != 0)
1466 			RETURN_ERROR(B_NOT_ALLOWED);
1467 
1468 		node->SetUID(st->st_uid);
1469 		updateChanged = true;
1470 	}
1471 
1472 	if ((statMask & B_STAT_GID) != 0 && st->st_gid != node->GID()) {
1473 		// only the user or root can do that
1474 		if (!isOwnerOrRoot)
1475 			RETURN_ERROR(B_NOT_ALLOWED);
1476 
1477 		node->SetGID(st->st_gid);
1478 		updateChanged = true;
1479 	}
1480 
1481 	if ((statMask & B_STAT_MODE) != 0) {
1482 		// only the user or root can do that
1483 		if (!isOwnerOrRoot)
1484 			RETURN_ERROR(B_NOT_ALLOWED);
1485 
1486 		node->SetMode((node->Mode() & ~(mode_t)S_IUMSK)
1487 			| (st->st_mode & S_IUMSK));
1488 		updateChanged = true;
1489 	}
1490 
1491 	if ((statMask & B_STAT_CREATION_TIME) != 0) {
1492 		// the user or root can do that or any user with write access
1493 		if (!isOwnerOrRoot && !hasWriteAccess)
1494 			RETURN_ERROR(B_NOT_ALLOWED);
1495 
1496 		node->SetCreationTime(timespec_to_nsecs(st->st_crtim));
1497 		updateChanged = true;
1498 	}
1499 
1500 	if ((statMask & B_STAT_MODIFICATION_TIME) != 0) {
1501 		// the user or root can do that or any user with write access
1502 		if (!isOwnerOrRoot && !hasWriteAccess)
1503 			RETURN_ERROR(B_NOT_ALLOWED);
1504 
1505 		node->SetModificationTime(timespec_to_nsecs(st->st_mtim));
1506 		updateModified = false;
1507 		updateChanged = true;
1508 	}
1509 
1510 	if ((statMask & B_STAT_CHANGE_TIME) != 0) {
1511 		// the user or root can do that or any user with write access
1512 		if (!isOwnerOrRoot && !hasWriteAccess)
1513 			RETURN_ERROR(B_NOT_ALLOWED);
1514 
1515 		node->SetModificationTime(timespec_to_nsecs(st->st_mtim));
1516 		updateModified = false;
1517 		updateChanged = false;
1518 	}
1519 
1520 	// update access/change/modification time
1521 	if (updateModified)
1522 		node->Touched(NODE_MODIFIED);
1523 	else if (updateChanged)
1524 		node->Touched(NODE_STAT_CHANGED);
1525 	else
1526 		node->Touched(NODE_ACCESSED);
1527 
1528 	// commit the transaction
1529 	return transaction.Commit(StatChangedNotification(node, statMask));
1530 }
1531 
1532 
1533 // #pragma mark - file operations
1534 
1535 
1536 static status_t
checksumfs_create(fs_volume * fsVolume,fs_vnode * parent,const char * name,int openMode,int permissions,void ** _cookie,ino_t * _newVnodeID)1537 checksumfs_create(fs_volume* fsVolume, fs_vnode* parent, const char* name,
1538 	int openMode, int permissions, void** _cookie, ino_t* _newVnodeID)
1539 {
1540 	Volume* volume = (Volume*)fsVolume->private_volume;
1541 	Directory* directory
1542 		= dynamic_cast<Directory*>((Node*)parent->private_node);
1543 	if (directory == NULL)
1544 		return B_NOT_A_DIRECTORY;
1545 
1546 	if (volume->IsReadOnly())
1547 		return B_READ_ONLY_DEVICE;
1548 
1549 	Transaction transaction(volume);
1550 	FileCookie* cookie;
1551 	Node* node;
1552 	bool created;
1553 	status_t error = create_file(volume, directory, name, openMode, permissions,
1554 		transaction, true, cookie, node, created);
1555 	if (error != B_OK)
1556 		RETURN_ERROR(error);
1557 
1558 	*_cookie = cookie;
1559 	*_newVnodeID = node->BlockIndex();
1560 
1561 	return B_OK;
1562 }
1563 
1564 
1565 static status_t
checksumfs_open(fs_volume * fsVolume,fs_vnode * vnode,int openMode,void ** _cookie)1566 checksumfs_open(fs_volume* fsVolume, fs_vnode* vnode, int openMode,
1567 	void** _cookie)
1568 {
1569 	Volume* volume = (Volume*)fsVolume->private_volume;
1570 	Node* node = (Node*)vnode->private_node;
1571 
1572 	// don't allow opening an attribute this way
1573 	if ((node->Mode() & S_ATTR) != 0)
1574 		RETURN_ERROR(B_BAD_VALUE);
1575 
1576 	Transaction transaction(volume);
1577 	FileCookie* cookie;
1578 	status_t error = open_file(volume, node, openMode, transaction, true,
1579 		cookie);
1580 	if (error != B_OK)
1581 		RETURN_ERROR(error);
1582 
1583 	*_cookie = cookie;
1584 	return B_OK;
1585 }
1586 
1587 
1588 static status_t
checksumfs_close(fs_volume * fsVolume,fs_vnode * vnode,void * cookie)1589 checksumfs_close(fs_volume* fsVolume, fs_vnode* vnode, void* cookie)
1590 {
1591 	return B_OK;
1592 }
1593 
1594 
1595 static status_t
checksumfs_free_cookie(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie)1596 checksumfs_free_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
1597 {
1598 	FileCookie* cookie = (FileCookie*)_cookie;
1599 	Node* node = (Node*)vnode->private_node;
1600 
1601 	cookie->UpdateModifiedIfNecessary(node, true);
1602 
1603 	delete cookie;
1604 	return B_OK;
1605 }
1606 
1607 
1608 static status_t
checksumfs_read(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,off_t pos,void * buffer,size_t * _length)1609 checksumfs_read(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, off_t pos,
1610 	void* buffer, size_t* _length)
1611 {
1612 	FileCookie* cookie = (FileCookie*)_cookie;
1613 	Node* node = (Node*)vnode->private_node;
1614 
1615 	switch (cookie->openMode & O_RWMASK) {
1616 		case O_RDONLY:
1617 		case O_RDWR:
1618 			break;
1619 		case O_WRONLY:
1620 		default:
1621 			RETURN_ERROR(EBADF);
1622 	}
1623 
1624 	return node->Read(pos, buffer, *_length, *_length);
1625 }
1626 
1627 
1628 static status_t
checksumfs_write(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,off_t pos,const void * buffer,size_t * _length)1629 checksumfs_write(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, off_t pos,
1630 	const void* buffer, size_t* _length)
1631 {
1632 	FileCookie* cookie = (FileCookie*)_cookie;
1633 	Node* node = (Node*)vnode->private_node;
1634 
1635 	switch (cookie->openMode & O_RWMASK) {
1636 		case O_WRONLY:
1637 		case O_RDWR:
1638 			break;
1639 		case O_RDONLY:
1640 		default:
1641 			RETURN_ERROR(EBADF);
1642 	}
1643 
1644 	if (pos < 0)
1645 		RETURN_ERROR(B_BAD_VALUE);
1646 
1647 	if ((cookie->openMode & O_APPEND) != 0) {
1648 		pos = -1;
1649 			// special value handled by Write()
1650 	}
1651 
1652 	bool sizeChanged;
1653 	status_t error = node->Write(pos, buffer, *_length, *_length, sizeChanged);
1654 	if (error != B_OK)
1655 		RETURN_ERROR(error);
1656 
1657 	// update the modification time and send out a notification from time to
1658 	// time
1659 	cookie->FileModified(node, sizeChanged);
1660 
1661 	return B_OK;
1662 }
1663 
1664 
1665 // #pragma mark - directory operations
1666 
1667 
1668 status_t
checksumfs_create_dir(fs_volume * fsVolume,fs_vnode * parent,const char * name,int perms)1669 checksumfs_create_dir(fs_volume* fsVolume, fs_vnode* parent, const char* name,
1670 	int perms)
1671 {
1672 	Volume* volume = (Volume*)fsVolume->private_volume;
1673 	Directory* directory
1674 		= dynamic_cast<Directory*>((Node*)parent->private_node);
1675 	if (directory == NULL)
1676 		return B_NOT_A_DIRECTORY;
1677 
1678 	if (volume->IsReadOnly())
1679 		return B_READ_ONLY_DEVICE;
1680 
1681 	status_t error = check_access(directory, W_OK);
1682 	if (error != B_OK)
1683 		return error;
1684 
1685 	// start a transaction and attach the directory (write locks it, too)
1686 	Transaction transaction(volume);
1687 	error = transaction.StartAndAddNode(directory);
1688 	if (error != B_OK)
1689 		return error;
1690 
1691 	// don't create an entry in an unlinked directory
1692 	if (directory->HardLinks() == 0)
1693 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
1694 
1695 	// create a directory node
1696 	Directory* newDirectory;
1697 	error = volume->CreateDirectory(perms, transaction, newDirectory);
1698 	if (error != B_OK)
1699 		return error;
1700 
1701 	// insert the new directory
1702 	error = directory->InsertEntry(name, newDirectory->BlockIndex(),
1703 		transaction);
1704 	if (error != B_OK)
1705 		return error;
1706 
1707 	// update stat data
1708 	newDirectory->SetHardLinks(1);
1709 	newDirectory->SetParentDirectory(directory->BlockIndex());
1710 
1711 	directory->Touched(NODE_MODIFIED);
1712 
1713 	// commit the transaction
1714 	return transaction.Commit(EntryCreatedNotification(directory, name,
1715 		newDirectory));
1716 }
1717 
1718 
1719 status_t
checksumfs_remove_dir(fs_volume * volume,fs_vnode * parent,const char * name)1720 checksumfs_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name)
1721 {
1722 	return remove_entry(volume, parent, name, true);
1723 }
1724 
1725 
1726 static status_t
checksumfs_open_dir(fs_volume * fsVolume,fs_vnode * vnode,void ** _cookie)1727 checksumfs_open_dir(fs_volume* fsVolume, fs_vnode* vnode, void** _cookie)
1728 {
1729 	Directory* directory = dynamic_cast<Directory*>((Node*)vnode->private_node);
1730 	if (directory == NULL)
1731 		return B_NOT_A_DIRECTORY;
1732 
1733 	NodeReadLocker nodeLocker(directory);
1734 
1735 	// don't allow opening an attribute directory this way
1736 	if ((directory->Mode() & S_ATTR_DIR) != 0)
1737 		RETURN_ERROR(B_BAD_VALUE);
1738 
1739 	status_t error = check_access(directory, R_OK);
1740 	if (error != B_OK)
1741 		return error;
1742 
1743 	DirCookie* cookie = new(std::nothrow) DirCookie(directory);
1744 	if (cookie == NULL)
1745 		return B_NO_MEMORY;
1746 
1747 	*_cookie = cookie;
1748 	return B_OK;
1749 }
1750 
1751 
1752 static status_t
checksumfs_close_dir(fs_volume * fsVolume,fs_vnode * vnode,void * cookie)1753 checksumfs_close_dir(fs_volume* fsVolume, fs_vnode* vnode, void* cookie)
1754 {
1755 	return B_OK;
1756 }
1757 
1758 
1759 static status_t
checksumfs_free_dir_cookie(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie)1760 checksumfs_free_dir_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
1761 {
1762 	DirCookie* cookie = (DirCookie*)_cookie;
1763 	delete cookie;
1764 	return B_OK;
1765 }
1766 
1767 
1768 static status_t
checksumfs_read_dir(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,struct dirent * buffer,size_t bufferSize,uint32 * _num)1769 checksumfs_read_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
1770 	struct dirent* buffer, size_t bufferSize, uint32* _num)
1771 {
1772 	if (*_num == 0)
1773 		return B_OK;
1774 
1775 	DirCookie* cookie = (DirCookie*)_cookie;
1776 
1777 	NodeReadLocker nodeLocker(cookie->GetDirectory());
1778 
1779 	return cookie->ReadNextEntry(buffer, bufferSize, *_num);
1780 }
1781 
1782 
1783 static status_t
checksumfs_rewind_dir(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie)1784 checksumfs_rewind_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
1785 {
1786 	DirCookie* cookie = (DirCookie*)_cookie;
1787 
1788 	NodeReadLocker nodeLocker(cookie->GetDirectory());
1789 
1790 	cookie->Rewind();
1791 	return B_OK;
1792 }
1793 
1794 
1795 // #pragma mark - attribute directory operations
1796 
1797 
1798 static status_t
checksumfs_open_attr_dir(fs_volume * volume,fs_vnode * vnode,void ** _cookie)1799 checksumfs_open_attr_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
1800 {
1801 	Node* node = (Node*)vnode->private_node;
1802 
1803 	NodeReadLocker nodeLocker(node);
1804 
1805 	status_t error = check_access(node, R_OK);
1806 	if (error != B_OK)
1807 		return error;
1808 
1809 	AttrDirCookie* cookie = new(std::nothrow) AttrDirCookie(node);
1810 	if (cookie == NULL)
1811 		return B_NO_MEMORY;
1812 
1813 	*_cookie = cookie;
1814 	return B_OK;
1815 }
1816 
1817 
1818 static status_t
checksumfs_close_attr_dir(fs_volume * volume,fs_vnode * vnode,void * cookie)1819 checksumfs_close_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
1820 {
1821 	return B_OK;
1822 }
1823 
1824 
1825 static status_t
checksumfs_free_attr_dir_cookie(fs_volume * volume,fs_vnode * vnode,void * _cookie)1826 checksumfs_free_attr_dir_cookie(fs_volume* volume, fs_vnode* vnode,
1827 	void* _cookie)
1828 {
1829 	AttrDirCookie* cookie = (AttrDirCookie*)_cookie;
1830 	delete cookie;
1831 	return B_OK;
1832 }
1833 
1834 
1835 static status_t
checksumfs_read_attr_dir(fs_volume * volume,fs_vnode * vnode,void * _cookie,struct dirent * buffer,size_t bufferSize,uint32 * _num)1836 checksumfs_read_attr_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1837 	struct dirent* buffer, size_t bufferSize, uint32* _num)
1838 {
1839 	if (*_num == 0)
1840 		return B_OK;
1841 
1842 	Node* node = (Node*)vnode->private_node;
1843 	AttrDirCookie* cookie = (AttrDirCookie*)_cookie;
1844 
1845 	NodeReadLocker nodeLocker(node);
1846 
1847 	return cookie->ReadNextEntry(buffer, bufferSize, *_num);
1848 }
1849 
1850 
1851 static status_t
checksumfs_rewind_attr_dir(fs_volume * volume,fs_vnode * vnode,void * _cookie)1852 checksumfs_rewind_attr_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie)
1853 {
1854 	Node* node = (Node*)vnode->private_node;
1855 	AttrDirCookie* cookie = (AttrDirCookie*)_cookie;
1856 
1857 	NodeReadLocker nodeLocker(node);
1858 
1859 	cookie->Rewind();
1860 	return B_OK;
1861 }
1862 
1863 
1864 // #pragma mark - attribute operations
1865 
1866 
1867 static status_t
checksumfs_create_attr(fs_volume * fsVolume,fs_vnode * vnode,const char * name,uint32 type,int openMode,void ** _cookie)1868 checksumfs_create_attr(fs_volume* fsVolume, fs_vnode* vnode, const char* name,
1869 	uint32 type, int openMode, void** _cookie)
1870 {
1871 	Volume* volume = (Volume*)fsVolume->private_volume;
1872 	Node* node = (Node*)vnode->private_node;
1873 	if (node == NULL)
1874 		return B_NOT_A_DIRECTORY;
1875 
1876 	if (volume->IsReadOnly())
1877 		return B_READ_ONLY_DEVICE;
1878 
1879 	// create the attribute cookie
1880 	AttributeCookie* cookie = new(std::nothrow) AttributeCookie(name);
1881 	if (cookie == NULL || cookie->name == NULL) {
1882 		delete cookie;
1883 		return B_NO_MEMORY;
1884 	}
1885 	ObjectDeleter<AttributeCookie> cookieDeleter(cookie);
1886 
1887 	// Start a transaction and lock the node.
1888 	// Note: Other than for ordinary nodes the locking order when attributes
1889 	// are involved is: node -> attribute directory -> attribute.
1890 	Transaction transaction(volume);
1891 	status_t error = transaction.StartAndAddNode(node);
1892 	if (error != B_OK)
1893 		RETURN_ERROR(error);
1894 
1895 	// check permissions
1896 	error = check_access(node, W_OK);
1897 	if (error != B_OK)
1898 		return error;
1899 
1900 	// get the attribute directory (create, if necessary)
1901 	Directory* attributeDirectory;
1902 	error = get_attribute_directory(node, &transaction, attributeDirectory);
1903 	if (error != B_OK)
1904 		RETURN_ERROR(error);
1905 	NodePutter attributeDirectoryPutter(attributeDirectory);
1906 
1907 	// open/create the attribute
1908 	bool created;
1909 	error = create_file(volume, attributeDirectory, name, openMode,
1910 		S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, transaction,
1911 		false, cookie->fileCookie, cookie->attribute, created);
1912 	if (error != B_OK)
1913 		RETURN_ERROR(error);
1914 
1915 	if (created) {
1916 		cookie->attribute->SetMode(cookie->attribute->Mode() | S_ATTR);
1917 		cookie->attribute->SetAttributeType(type);
1918 	}
1919 
1920 	// commit the transaction
1921 	if (transaction.IsActive()) {
1922 		if (created || (openMode & O_TRUNC) != 0) {
1923 			node->Touched(NODE_STAT_CHANGED);
1924 
1925 			AttributeChangedNotification attributeNotification(node, name,
1926 				created ? B_ATTR_CREATED : B_ATTR_CHANGED);
1927 			StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME);
1928 			error = transaction.Commit(&attributeNotification,
1929 				&statNotification);
1930 		} else
1931 			error = transaction.Commit();
1932 	}
1933 
1934 	*_cookie = cookieDeleter.Detach();
1935 	return B_OK;
1936 }
1937 
1938 
1939 static status_t
checksumfs_open_attr(fs_volume * fsVolume,fs_vnode * vnode,const char * name,int openMode,void ** _cookie)1940 checksumfs_open_attr(fs_volume* fsVolume, fs_vnode* vnode, const char* name,
1941 	int openMode, void** _cookie)
1942 {
1943 	Volume* volume = (Volume*)fsVolume->private_volume;
1944 	Node* node = (Node*)vnode->private_node;
1945 
1946 	// create the attribute cookie
1947 	AttributeCookie* cookie = new(std::nothrow) AttributeCookie(name);
1948 	if (cookie == NULL || cookie->name == NULL) {
1949 		delete cookie;
1950 		return B_NO_MEMORY;
1951 	}
1952 	ObjectDeleter<AttributeCookie> cookieDeleter(cookie);
1953 
1954 	// Get the node's attribute directory (don't create it, if it doesn't exist
1955 	// yet). We only need to read-lock the node for that, but when O_TRUNC is
1956 	// given, we already start the transaction and write-lock the node, so we
1957 	// don't get a locking order inversion later. The locking order when
1958 	// attributes are involved is: node -> attribute directory -> attribute.
1959 	Transaction transaction(volume);
1960 	NodeReadLocker readLocker;
1961 	if ((openMode & O_TRUNC) != 0) {
1962 		status_t error = transaction.StartAndAddNode(node);
1963 		if (error != B_OK)
1964 			RETURN_ERROR(error);
1965 	} else
1966 		readLocker.SetTo(node, false);
1967 
1968 	Directory* attributeDirectory;
1969 	status_t error = get_attribute_directory(node, NULL, attributeDirectory);
1970 	if (error != B_OK)
1971 		RETURN_ERROR(error);
1972 	NodePutter attributeDirectoryPutter(attributeDirectory);
1973 
1974 	// look up the attribute
1975 	readLocker.SetTo(attributeDirectory, false);
1976 	uint64 blockIndex;
1977 	error = attributeDirectory->LookupEntry(name, blockIndex);
1978 	if (error != B_OK)
1979 		RETURN_ERROR(error);
1980 
1981 	error = volume->GetNode(blockIndex, cookie->attribute);
1982 		// the vnode reference directly goes to the cookie in case of success
1983 	if (error != B_OK)
1984 		RETURN_ERROR(error);
1985 
1986 	// open the attribute
1987 	error = open_file(volume, cookie->attribute, openMode, transaction, false,
1988 		cookie->fileCookie);
1989 	if (error != B_OK)
1990 		RETURN_ERROR(error);
1991 
1992 	// commit the transaction
1993 	if (transaction.IsActive()) {
1994 		if ((openMode & O_TRUNC) != 0) {
1995 			node->Touched(NODE_STAT_CHANGED);
1996 
1997 			AttributeChangedNotification attributeNotification(node, name,
1998 				B_ATTR_CHANGED);
1999 			StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME);
2000 			error = transaction.Commit(&attributeNotification,
2001 				&statNotification);
2002 		} else
2003 			error = transaction.Commit();
2004 	}
2005 
2006 	*_cookie = cookieDeleter.Detach();
2007 	return B_OK;
2008 }
2009 
2010 
2011 static status_t
checksumfs_close_attr(fs_volume * fsVolume,fs_vnode * vnode,void * cookie)2012 checksumfs_close_attr(fs_volume* fsVolume, fs_vnode* vnode, void* cookie)
2013 {
2014 	return B_OK;
2015 }
2016 
2017 
2018 static status_t
checksumfs_free_attr_cookie(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie)2019 checksumfs_free_attr_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
2020 {
2021 	AttributeCookie* cookie = (AttributeCookie*)_cookie;
2022 	delete cookie;
2023 	return B_OK;
2024 }
2025 
2026 
2027 static status_t
checksumfs_read_attr(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,off_t pos,void * buffer,size_t * _length)2028 checksumfs_read_attr(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
2029 	off_t pos, void* buffer, size_t* _length)
2030 {
2031 	AttributeCookie* cookie = (AttributeCookie*)_cookie;
2032 
2033 	switch (cookie->fileCookie->openMode & O_RWMASK) {
2034 		case O_RDONLY:
2035 		case O_RDWR:
2036 			break;
2037 		case O_WRONLY:
2038 		default:
2039 			RETURN_ERROR(EBADF);
2040 	}
2041 
2042 	return cookie->attribute->Read(pos, buffer, *_length, *_length);
2043 }
2044 
2045 
2046 static status_t
checksumfs_write_attr(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,off_t pos,const void * buffer,size_t * _length)2047 checksumfs_write_attr(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
2048 	off_t pos, const void* buffer, size_t* _length)
2049 {
2050 	Volume* volume = (Volume*)fsVolume->private_volume;
2051 	Node* node = (Node*)vnode->private_node;
2052 	AttributeCookie* cookie = (AttributeCookie*)_cookie;
2053 
2054 	switch (cookie->fileCookie->openMode & O_RWMASK) {
2055 		case O_WRONLY:
2056 		case O_RDWR:
2057 			break;
2058 		case O_RDONLY:
2059 		default:
2060 			RETURN_ERROR(EBADF);
2061 	}
2062 
2063 	if (pos < 0)
2064 		RETURN_ERROR(B_BAD_VALUE);
2065 
2066 	if ((cookie->fileCookie->openMode & O_APPEND) != 0) {
2067 		pos = -1;
2068 			// special value handled by Write()
2069 	}
2070 
2071 	bool sizeChanged;
2072 	status_t error = cookie->attribute->Write(pos, buffer, *_length, *_length,
2073 		sizeChanged);
2074 	if (error != B_OK)
2075 		RETURN_ERROR(error);
2076 
2077 	// update the node changed time and send out a notifications (don't fail,
2078 	// if any of this fails)
2079 	Transaction transaction(volume);
2080 	if (transaction.StartAndAddNode(node) != B_OK)
2081 		return B_OK;
2082 	if (transaction.AddNode(cookie->attribute) != B_OK)
2083 		return B_OK;
2084 
2085 	cookie->attribute->Touched(NODE_MODIFIED);
2086 
2087 	// commit the transaction
2088 	if (cookie->attribute->ParentDirectory() != 0) {
2089 		node->Touched(NODE_STAT_CHANGED);
2090 
2091 		AttributeChangedNotification attributeNotification(node, cookie->name,
2092 			B_ATTR_CHANGED);
2093 		StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME);
2094 		transaction.Commit(&attributeNotification, &statNotification);
2095 	} else {
2096 		// attribute has been removed -- no notifications needed
2097 		transaction.Commit();
2098 	}
2099 
2100 	return B_OK;
2101 }
2102 
2103 
2104 static status_t
checksumfs_read_attr_stat(fs_volume * fsVolume,fs_vnode * vnode,void * _cookie,struct stat * st)2105 checksumfs_read_attr_stat(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
2106 	struct stat* st)
2107 {
2108 	AttributeCookie* cookie = (AttributeCookie*)_cookie;
2109 
2110 	// not many fields needed ATM
2111 	st->st_size = cookie->attribute->Size();
2112     st->st_type = cookie->attribute->AttributeType();
2113 
2114 	return B_OK;
2115 }
2116 
2117 
2118 static status_t
checksumfs_remove_attr(fs_volume * fsVolume,fs_vnode * vnode,const char * name)2119 checksumfs_remove_attr(fs_volume* fsVolume, fs_vnode* vnode, const char* name)
2120 {
2121 	Volume* volume = (Volume*)fsVolume->private_volume;
2122 	Node* node = (Node*)vnode->private_node;
2123 
2124 	if (volume->IsReadOnly())
2125 		return B_READ_ONLY_DEVICE;
2126 
2127 	// start a transaction
2128 	Transaction transaction(volume);
2129 	status_t error = transaction.StartAndAddNode(node);
2130 	if (error != B_OK)
2131 		RETURN_ERROR(error);
2132 
2133 	// check permissions
2134 	error = check_access(node, W_OK);
2135 	if (error != B_OK)
2136 		RETURN_ERROR(error);
2137 
2138 	// get the attribute directory
2139 	Directory* attributeDirectory;
2140 	error = get_attribute_directory(node, NULL, attributeDirectory);
2141 	if (error != B_OK)
2142 		RETURN_ERROR(error);
2143 	NodePutter attributeDirectoryPutter(attributeDirectory);
2144 
2145 	error = transaction.AddNode(attributeDirectory);
2146 	if (error != B_OK)
2147 		RETURN_ERROR(error);
2148 
2149 	// look up the entry
2150 	uint64 blockIndex;
2151 	error = attributeDirectory->LookupEntry(name, blockIndex);
2152 	if (error != B_OK)
2153 		RETURN_ERROR(error);
2154 
2155 	// get the attribute node
2156 	Node* attribute;
2157 	error = volume->GetNode(blockIndex, attribute);
2158 	if (error != B_OK)
2159 		RETURN_ERROR(error);
2160 	NodePutter attributePutter(attribute);
2161 
2162 	error = transaction.AddNode(attribute);
2163 	if (error != B_OK) {
2164 		volume->PutNode(attribute);
2165 		RETURN_ERROR(error);
2166 	}
2167 
2168 	// remove the entry
2169 	bool attrDirEmpty;
2170 	error = attributeDirectory->RemoveEntry(name, transaction, &attrDirEmpty);
2171 	if (error != B_OK)
2172 		RETURN_ERROR(error);
2173 
2174 	// remove the attribute node
2175 	error = volume->RemoveNode(attribute);
2176 	if (error != B_OK)
2177 		return error;
2178 	transaction.UpdateNodeFlags(attribute, TRANSACTION_UNREMOVE_NODE_ON_ERROR);
2179 
2180 	// if the attribute directory is empty now, remove it too
2181 	if (attrDirEmpty) {
2182 		error = volume->RemoveNode(attributeDirectory);
2183 		if (error != B_OK)
2184 			return error;
2185 		transaction.UpdateNodeFlags(attributeDirectory,
2186 			TRANSACTION_UNREMOVE_NODE_ON_ERROR);
2187 
2188 		node->SetAttributeDirectory(0);
2189 	}
2190 
2191 	// update stat data
2192 	attribute->SetHardLinks(0);
2193 
2194 	node->Touched(NODE_STAT_CHANGED);
2195 
2196 	// commit the transaction
2197 	AttributeChangedNotification attributeNotification(node, name,
2198 		B_ATTR_REMOVED);
2199 	StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME);
2200 	return transaction.Commit(&attributeNotification, &statNotification);
2201 }
2202 
2203 
2204 // #pragma mark - module
2205 
2206 
2207 static status_t
checksumfs_std_ops(int32 operation,...)2208 checksumfs_std_ops(int32 operation, ...)
2209 {
2210 	switch (operation) {
2211 		case B_MODULE_INIT:
2212 			init_debugging();
2213 			PRINT("checksumfs_std_ops(): B_MODULE_INIT\n");
2214 			return B_OK;
2215 
2216 		case B_MODULE_UNINIT:
2217 			PRINT("checksumfs_std_ops(): B_MODULE_UNINIT\n");
2218 			exit_debugging();
2219 			return B_OK;
2220 
2221 		default:
2222 			return B_BAD_VALUE;
2223 	}
2224 }
2225 
2226 
2227 static file_system_module_info sFSModule = {
2228 	{
2229 		kCheckSumFSModuleName,
2230 		0,
2231 		checksumfs_std_ops
2232 	},
2233 	kCheckSumFSShortName,
2234 	CHECK_SUM_FS_PRETTY_NAME,
2235 	// DDM flags
2236 	B_DISK_SYSTEM_SUPPORTS_INITIALIZING
2237 		| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
2238 		| B_DISK_SYSTEM_SUPPORTS_WRITING,
2239 
2240 	/* scanning (the device is write locked) */
2241 	checksumfs_identify_partition,
2242 	checksumfs_scan_partition,
2243 	checksumfs_free_identify_partition_cookie,
2244 	NULL,	// free_partition_content_cookie
2245 
2246 	/* general operations */
2247 	checksumfs_mount,
2248 
2249 	/* capability querying (the device is read locked) */
2250 	NULL,	// get_supported_operations
2251 
2252 	NULL,	// validate_resize
2253 	NULL,	// validate_move
2254 	NULL,	// validate_set_content_name
2255 	NULL,	// validate_set_content_parameters
2256 	NULL,	// validate_initialize
2257 
2258 	/* shadow partition modification (device is write locked) */
2259 	NULL,	// shadow_changed
2260 
2261 	/* writing (the device is NOT locked) */
2262 	NULL,	// defragment
2263 	NULL,	// repair
2264 	NULL,	// resize
2265 	NULL,	// move
2266 	checksumfs_set_content_name,
2267 	NULL,	// set_content_parameters
2268 	checksumfs_initialize
2269 };
2270 
2271 
2272 const module_info* modules[] = {
2273 	(module_info*)&sFSModule,
2274 	NULL
2275 };
2276 
2277 
2278 fs_volume_ops gCheckSumFSVolumeOps = {
2279 	checksumfs_unmount,
2280 
2281 	checksumfs_read_fs_info,
2282 	checksumfs_write_fs_info,
2283 	checksumfs_sync,
2284 
2285 	checksumfs_get_vnode,
2286 
2287 	/* index directory & index operations */
2288 	NULL,	// open_index_dir
2289 	NULL,	// close_index_dir
2290 	NULL,	// free_index_dir_cookie
2291 	NULL,	// read_index_dir
2292 	NULL,	// rewind_index_dir
2293 
2294 	NULL,	// create_index
2295 	NULL,	// remove_index
2296 	NULL,	// read_index_stat
2297 
2298 	/* query operations */
2299 	NULL,	// open_query
2300 	NULL,	// close_query
2301 	NULL,	// free_query_cookie
2302 	NULL,	// read_query
2303 	NULL,	// rewind_query
2304 
2305 	/* support for FS layers */
2306 	NULL,	// all_layers_mounted
2307 	NULL,	// create_sub_vnode
2308 	NULL,	// delete_sub_vnode
2309 };
2310 
2311 
2312 fs_vnode_ops gCheckSumFSVnodeOps = {
2313 	/* vnode operations */
2314 	checksumfs_lookup,
2315 	NULL,	// get_vnode_name
2316 
2317 	checksumfs_put_vnode,
2318 	checksumfs_remove_vnode,
2319 
2320 	/* VM file access */
2321 	NULL,	// can_page
2322 	NULL,	// read_pages
2323 	NULL,	// write_pages
2324 
2325 	/* asynchronous I/O */
2326 	checksumfs_io,
2327 	NULL,	// cancel_io
2328 
2329 	/* cache file access */
2330 	checksumfs_get_file_map,
2331 
2332 	/* common operations */
2333 	NULL,	// ioctl
2334 	checksumfs_set_flags,
2335 	NULL,	// select
2336 	NULL,	// deselect
2337 	checksumfs_fsync,
2338 
2339 	checksumfs_read_symlink,
2340 	checksumfs_create_symlink,
2341 
2342 	checksumfs_link,
2343 	checksumfs_unlink,
2344 	checksumfs_rename,
2345 
2346 	checksumfs_access,
2347 	checksumfs_read_stat,
2348 	checksumfs_write_stat,
2349 	NULL,	// preallocate
2350 
2351 	/* file operations */
2352 	checksumfs_create,
2353 	checksumfs_open,
2354 	checksumfs_close,
2355 	checksumfs_free_cookie,
2356 	checksumfs_read,
2357 	checksumfs_write,
2358 
2359 	/* directory operations */
2360 	checksumfs_create_dir,
2361 	checksumfs_remove_dir,
2362 	checksumfs_open_dir,
2363 	checksumfs_close_dir,
2364 	checksumfs_free_dir_cookie,
2365 	checksumfs_read_dir,
2366 	checksumfs_rewind_dir,
2367 
2368 	/* attribute directory operations */
2369 	checksumfs_open_attr_dir,
2370 	checksumfs_close_attr_dir,
2371 	checksumfs_free_attr_dir_cookie,
2372 	checksumfs_read_attr_dir,
2373 	checksumfs_rewind_attr_dir,
2374 
2375 	/* attribute operations */
2376 	checksumfs_create_attr,
2377 	checksumfs_open_attr,
2378 	checksumfs_close_attr,
2379 	checksumfs_free_attr_cookie,
2380 	checksumfs_read_attr,
2381 	checksumfs_write_attr,
2382 
2383 	checksumfs_read_attr_stat,
2384 	NULL,	// write_attr_stat
2385 	NULL,	// rename_attr
2386 	checksumfs_remove_attr,
2387 
2388 	/* support for node and FS layers */
2389 	NULL,	// create_special_node
2390 	NULL	// get_super_vnode
2391 };
2392