⛏️ index : haiku.git

/*
 * Copyright 2001-2012, Axel DΓΆrfler, axeld@pinc-software.de.
 * This file may be used under the terms of the MIT License.
 */
#ifndef VOLUME_H
#define VOLUME_H


#include "system_dependencies.h"

#include "bfs.h"
#include "BlockAllocator.h"


class CheckVisitor;
class Journal;
class Inode;
class Query;


enum volume_flags {
	VOLUME_READ_ONLY	= 0x0001
};

enum volume_initialize_flags {
	VOLUME_NO_INDICES	= 0x0001,
};

typedef DoublyLinkedList<Inode> InodeList;


class Volume {
public:
							Volume(fs_volume* volume);
							~Volume();

			status_t		Mount(const char* device, uint32 flags);
			status_t		Unmount();
			status_t		Initialize(int fd, const char* name,
								uint32 blockSize, uint32 flags);

			bool			IsInitializing() const { return fVolume == NULL; }

			bool			IsValidSuperBlock() const;
			bool			IsValidInodeBlock(off_t block) const;
			bool			IsReadOnly() const;
			void			Panic();
			mutex&			Lock();

			block_run		Root() const { return fSuperBlock.root_dir; }
			Inode*			RootNode() const { return fRootNode; }
			block_run		Indices() const { return fSuperBlock.indices; }
			Inode*			IndicesNode() const { return fIndicesNode; }
			block_run		Log() const { return fSuperBlock.log_blocks; }
			vint32&			LogStart() { return fLogStart; }
			vint32&			LogEnd() { return fLogEnd; }
			int				Device() const { return fDevice; }

			dev_t			ID() const { return fVolume ? fVolume->id : -1; }
			fs_volume*		FSVolume() const { return fVolume; }
			const char*		Name() const { return fSuperBlock.name; }

			off_t			NumBlocks() const
								{ return fSuperBlock.NumBlocks(); }
			off_t			UsedBlocks() const
								{ return fSuperBlock.UsedBlocks(); }
			off_t			FreeBlocks() const
								{ return NumBlocks() - UsedBlocks(); }
			off_t			NumBitmapBlocks() const
								{ return (NumBlocks() + fBlockSize * 8 - 1)
									/ (fBlockSize * 8); }

			uint32			DeviceBlockSize() const { return fDeviceBlockSize; }
			uint32			BlockSize() const { return fBlockSize; }
			uint32			BlockShift() const { return fBlockShift; }
			uint32			InodeSize() const
								{ return fSuperBlock.InodeSize(); }
			uint32			AllocationGroups() const
								{ return fSuperBlock.AllocationGroups(); }
			uint32			AllocationGroupShift() const
								{ return fAllocationGroupShift; }
			disk_super_block& SuperBlock() { return fSuperBlock; }

			off_t			ToOffset(block_run run) const
								{ return ToBlock(run) << BlockShift(); }
			off_t			ToBlock(block_run run) const
								{ return ((((off_t)run.AllocationGroup())
										<< AllocationGroupShift())
									| (off_t)run.Start()); }
			block_run		ToBlockRun(off_t block) const;
			status_t		ValidateBlockRun(block_run run);

			off_t			ToVnode(block_run run) const
								{ return ToBlock(run); }
			off_t			ToVnode(off_t block) const { return block; }
			off_t			VnodeToBlock(ino_t id) const { return (off_t)id; }

			status_t		CreateIndicesRoot(Transaction& transaction);

			status_t		CreateVolumeID(Transaction& transaction);

			InodeList&		RemovedInodes() { return fRemovedInodes; }
				// This list is guarded by the transaction lock

			// block bitmap
			BlockAllocator&	Allocator();
			status_t		AllocateForInode(Transaction& transaction,
								const Inode* parent, mode_t type,
								block_run& run);
			status_t		AllocateForInode(Transaction& transaction,
								const block_run* parent, mode_t type,
								block_run& run);
			status_t		Allocate(Transaction& transaction, Inode* inode,
								off_t numBlocks, block_run& run,
								uint16 minimum = 1);
			status_t		Free(Transaction& transaction, block_run run);
			void			SetCheckingThread(thread_id thread)
								{ fCheckingThread = thread; }
			bool			IsCheckingThread() const
								{ return find_thread(NULL) == fCheckingThread; }
			status_t		CreateCheckVisitor();
			void			DeleteCheckVisitor();
			::CheckVisitor*	CheckVisitor() { return fCheckVisitor; }

			// cache access
			status_t		WriteSuperBlock();
			status_t		FlushDevice();

			// queries
			void			UpdateLiveQueries(Inode* inode,
								const char* attribute, int32 type,
								const uint8* oldKey, size_t oldLength,
								const uint8* newKey, size_t newLength);
			void			UpdateLiveQueriesRenameMove(Inode* inode,
								ino_t oldDirectoryID, const char* oldName,
								ino_t newDirectoryID, const char* newName);

			bool			CheckForLiveQuery(const char* attribute);
			void			AddQuery(Query* query);
			void			RemoveQuery(Query* query);

			status_t		Sync();
			Journal*		GetJournal(off_t refBlock) const;

			void*			BlockCache() { return fBlockCache; }

	static	status_t		CheckSuperBlock(const uint8* data,
								uint32* _offset = NULL);
	static	status_t		Identify(int fd, disk_super_block* superBlock);

private:
			status_t		_EraseUnusedBootBlock();

protected:
			fs_volume*		fVolume;
			int				fDevice;
			disk_super_block fSuperBlock;

			uint32			fDeviceBlockSize;
			uint32			fBlockSize;
			uint32			fBlockShift;
			uint32			fAllocationGroupShift;

			BlockAllocator	fBlockAllocator;
			mutex			fLock;
			Journal*		fJournal;
			vint32			fLogStart;
			vint32			fLogEnd;

			Inode*			fRootNode;
			Inode*			fIndicesNode;

			vint32			fDirtyCachedBlocks;

			mutex			fQueryLock;
			DoublyLinkedList<Query> fQueries;

			uint32			fFlags;

			void*			fBlockCache;
			thread_id		fCheckingThread;
			::CheckVisitor*	fCheckVisitor;

			InodeList		fRemovedInodes;
};


// inline functions

inline bool
Volume::IsReadOnly() const
{
	 return fFlags & VOLUME_READ_ONLY;
}


inline mutex&
Volume::Lock()
{
	 return fLock;
}


inline BlockAllocator&
Volume::Allocator()
{
	 return fBlockAllocator;
}


inline status_t
Volume::AllocateForInode(Transaction& transaction, const block_run* parent,
	mode_t type, block_run& run)
{
	return fBlockAllocator.AllocateForInode(transaction, parent, type, run);
}


inline status_t
Volume::Allocate(Transaction& transaction, Inode* inode, off_t numBlocks,
	block_run& run, uint16 minimum)
{
	return fBlockAllocator.Allocate(transaction, inode, numBlocks, run,
		minimum);
}


inline status_t
Volume::Free(Transaction& transaction, block_run run)
{
	return fBlockAllocator.Free(transaction, run);
}


inline status_t
Volume::FlushDevice()
{
	return block_cache_sync(fBlockCache);
}


inline Journal*
Volume::GetJournal(off_t /*refBlock*/) const
{
	 return fJournal;
}


#endif	// VOLUME_H