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
36static const char* const kCheckSumFSModuleName	= "file_systems/checksumfs"
37	B_CURRENT_FS_API_VERSION;
38static const char* const kCheckSumFSShortName	= "checksumfs";
39
40static const bigtime_t kModifiedInterimUpdateInterval = 500000;
41	// wait at least 0.5s between interim modified updates
42
43
44// #pragma mark -
45
46
47struct 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
57	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
69	~FileCookie()
70	{
71		mutex_destroy(&lock);
72	}
73
74	status_t UpdateModifiedIfNecessary(Node* node, bool finalUpdate)
75	{
76		MutexLocker locker(lock);
77
78		return _UpdateModifiedIfNecessary(node, finalUpdate);
79	}
80
81	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
93private:
94	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
137struct DirCookie {
138	DirCookie(Directory* directory)
139		:
140		fDirectory(directory)
141	{
142		Rewind();
143	}
144
145	Directory* GetDirectory() const
146	{
147		return fDirectory;
148	}
149
150	void SetTo(Directory* directory, bool skipArtificialEntries)
151	{
152		fDirectory = directory;
153		Rewind(skipArtificialEntries);
154	}
155
156	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
208	void Rewind(bool skipArtificialEntries = false)
209	{
210		fIterationState = skipArtificialEntries ? OTHERS : DOT;
211		fEntryName[0] = '\0';
212	}
213
214private:
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
227struct AttrDirCookie {
228	AttrDirCookie(Node* node)
229		:
230		fNode(node),
231		fAttributeDirectory(NULL),
232		fDirCookie(NULL)
233	{
234	}
235
236	~AttrDirCookie()
237	{
238		if (fAttributeDirectory != NULL)
239			fNode->GetVolume()->PutNode(fAttributeDirectory);
240	}
241
242	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
257	void Rewind()
258	{
259		fDirCookie.Rewind(true);
260	}
261
262private:
263	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
306private:
307	Node*		fNode;
308	Directory*	fAttributeDirectory;
309	DirCookie	fDirCookie;
310};
311
312
313struct AttributeCookie {
314	char*		name;
315	Node*		attribute;
316	FileCookie*	fileCookie;
317
318	AttributeCookie(const char* name)
319		:
320		name(strdup(name)),
321		attribute(NULL),
322		fileCookie(NULL)
323	{
324	}
325
326	~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
339static void
340set_timespec(timespec& time, uint64 nanos)
341{
342	time.tv_sec = nanos / 1000000000;
343	time.tv_nsec = nanos % 1000000000;
344}
345
346
347static uint64
348timespec_to_nsecs(const timespec& time)
349{
350	return (uint64)time.tv_sec * 1000000000 + time.tv_nsec;
351}
352
353
354struct PutNode {
355	inline void operator()(Node* node)
356	{
357		if (node != NULL)
358			node->GetVolume()->PutNode(node);
359	}
360};
361
362typedef BPrivate::AutoDeleter<Node, PutNode> NodePutter;
363
364
365static bool
366is_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
379static status_t
380check_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
416static status_t
417remove_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*/
518static status_t
519open_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
595static status_t
596create_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*/
735static status_t
736get_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
792static float
793checksumfs_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
817static status_t
818checksumfs_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
834static void
835checksumfs_free_identify_partition_cookie(partition_data* partition,
836	void* cookie)
837{
838	SuperBlock* superBlock = (SuperBlock*)cookie;
839	delete superBlock;
840}
841
842
843static status_t
844checksumfs_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
868static status_t
869checksumfs_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
877static status_t
878checksumfs_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
915static status_t
916checksumfs_unmount(fs_volume* fsVolume)
917{
918	Volume* volume = (Volume*)fsVolume->private_volume;
919	volume->Unmount();
920	return B_OK;
921}
922
923
924static status_t
925checksumfs_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
933static status_t
934checksumfs_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
949static status_t
950checksumfs_sync(fs_volume* fsVolume)
951{
952	Volume* volume = (Volume*)fsVolume->private_volume;
953
954	return block_cache_sync(volume->BlockCache());
955}
956
957
958static status_t
959checksumfs_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
987static status_t
988checksumfs_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
1027static status_t
1028checksumfs_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
1036static status_t
1037checksumfs_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
1049static status_t
1050iterative_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
1060static status_t
1061iterative_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
1070static status_t
1071checksumfs_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
1105static status_t
1106checksumfs_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
1123static status_t
1124checksumfs_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
1137static status_t
1138checksumfs_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
1148static status_t
1149checksumfs_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
1164static status_t
1165checksumfs_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
1218static status_t
1219checksumfs_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
1268static status_t
1269checksumfs_unlink(fs_volume* fsVolume, fs_vnode* dir, const char* name)
1270{
1271	return remove_entry(fsVolume, dir, name, false);
1272}
1273
1274
1275static status_t
1276checksumfs_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
1392static status_t
1393checksumfs_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
1403static status_t
1404checksumfs_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
1429static status_t
1430checksumfs_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
1536static status_t
1537checksumfs_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
1565static status_t
1566checksumfs_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
1588static status_t
1589checksumfs_close(fs_volume* fsVolume, fs_vnode* vnode, void* cookie)
1590{
1591	return B_OK;
1592}
1593
1594
1595static status_t
1596checksumfs_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
1608static status_t
1609checksumfs_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
1628static status_t
1629checksumfs_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
1668status_t
1669checksumfs_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
1719status_t
1720checksumfs_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name)
1721{
1722	return remove_entry(volume, parent, name, true);
1723}
1724
1725
1726static status_t
1727checksumfs_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
1752static status_t
1753checksumfs_close_dir(fs_volume* fsVolume, fs_vnode* vnode, void* cookie)
1754{
1755	return B_OK;
1756}
1757
1758
1759static status_t
1760checksumfs_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
1768static status_t
1769checksumfs_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
1783static status_t
1784checksumfs_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
1798static status_t
1799checksumfs_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
1818static status_t
1819checksumfs_close_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
1820{
1821	return B_OK;
1822}
1823
1824
1825static status_t
1826checksumfs_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
1835static status_t
1836checksumfs_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
1851static status_t
1852checksumfs_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
1867static status_t
1868checksumfs_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
1939static status_t
1940checksumfs_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
2011static status_t
2012checksumfs_close_attr(fs_volume* fsVolume, fs_vnode* vnode, void* cookie)
2013{
2014	return B_OK;
2015}
2016
2017
2018static status_t
2019checksumfs_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
2027static status_t
2028checksumfs_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
2046static status_t
2047checksumfs_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
2104static status_t
2105checksumfs_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
2118static status_t
2119checksumfs_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
2207static status_t
2208checksumfs_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
2227static 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
2272const module_info* modules[] = {
2273	(module_info*)&sFSModule,
2274	NULL
2275};
2276
2277
2278fs_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
2312fs_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