// Volume.cpp
//
// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de)
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// You can alternatively use *this file* under the terms of the the MIT
// license included in this package.
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "Volume.h"
#include "Block.h"
#include "BlockCache.h"
#include "Debug.h"
#include "DirItem.h"
#include "IndirectItem.h"
#include "Iterators.h"
#include "reiserfs.h"
#include "Settings.h"
#include "StatItem.h"
#include "SuperBlock.h"
#include "Tree.h"
#include "VNode.h"
extern fs_vnode_ops gReiserFSVnodeOps;
// min and max
// We don't want to include <algobase.h> otherwise we also get <iostream.h>
// and other undesired things.
template<typename C>
static inline C min(const C &a, const C &b) { return (a < b ? a : b); }
template<typename C>
static inline C max(const C &a, const C &b) { return (a > b ? a : b); }
/*!
\class Volume
\brief Represents a volume.
The Volume class bundles all functionality related to a volume.
It knows the superblock and has some basic functionality needed
for handling VNodes. Actually it should be the layer that provides the
abstraction from VNodes. The design is not strict in this respect
(the whole thing evolved while I was in the process of understanding
the structures ;-), since the kernel interface does a good part of that
too (e.g. concerning dir iteration).
*/
// constructor
Volume::Volume()
: fFSVolume(NULL),
fDevice(-1),
fBlockCache(NULL),
fTree(NULL),
fSuperBlock(NULL),
fHashFunction(NULL),
fRootVNode(NULL),
fDeviceName(NULL),
fSettings(NULL),
fNegativeEntries()
{
fVolumeName[0] = '\0';
}
// destructor
Volume::~Volume()
{
Unmount();
}
// Identify
status_t
Volume::Identify(int fd, partition_data *partition)
{
// open disk
fDevice = dup(fd);
if (fDevice < 0)
return B_ERROR;
// read and analyze superblock
return _ReadSuperBlock();
}
// Mount
status_t
Volume::Mount(fs_volume *fsVolume, const char *path)
{
Unmount();
status_t error = (path ? B_OK : B_BAD_VALUE);
fFSVolume = fsVolume;
// load the settings
if (error == B_OK) {
fSettings = new(nothrow) Settings;
if (fSettings)
error = fSettings->SetTo(path);
else
error = B_NO_MEMORY;
}
// copy the device name
if (error == B_OK) {
fDeviceName = new(nothrow) char[strlen(path) + 1];
if (fDeviceName)
strcpy(fDeviceName, path);
else
error = B_NO_MEMORY;
}
// open disk
if (error == B_OK) {
fDevice = open(path, O_RDONLY);
if (fDevice < 0)
SET_ERROR(error, errno);
}
// read and analyze superblock
if (error == B_OK)
error = _ReadSuperBlock();
if (error == B_OK)
UpdateName(fsVolume->partition);
// create and init block cache
if (error == B_OK) {
fBlockCache = new(nothrow) BlockCache;
if (fBlockCache)
error = fBlockCache->Init(fDevice, CountBlocks(), GetBlockSize());
else
error = B_NO_MEMORY;
}
// create the tree
if (error == B_OK) {
fTree = new(nothrow) Tree;
if (!fTree)
error = B_NO_MEMORY;
}
// get the root node and init the tree
if (error == B_OK) {
Block *rootBlock = NULL;
error = fBlockCache->GetBlock(fSuperBlock->GetRootBlock(), &rootBlock);
REPORT_ERROR(error);
if (error == B_OK) {
rootBlock->SetKind(Block::KIND_FORMATTED);
error = fTree->Init(this, rootBlock->ToNode(),
fSuperBlock->GetTreeHeight());
REPORT_ERROR(error);
rootBlock->Put();
}
}
// get the root VNode (i.e. the root dir)
if (error == B_OK) {
fRootVNode = new(nothrow) VNode;
if (fRootVNode) {
error = FindVNode(REISERFS_ROOT_PARENT_OBJECTID,
REISERFS_ROOT_OBJECTID, fRootVNode);
REPORT_ERROR(error);
if (error == B_OK) {
error = publish_vnode(fFSVolume, fRootVNode->GetID(),
fRootVNode, &gReiserFSVnodeOps, S_IFDIR, 0);
}
REPORT_ERROR(error);
} else
error = B_NO_MEMORY;
}
// init the hash function
if (error == B_OK)
_InitHashFunction();
// init the negative entry list
if (error == B_OK)
_InitNegativeEntries();
// cleanup on error
if (error != B_OK)
Unmount();
RETURN_ERROR(error);
}
// Unmount
status_t
Volume::Unmount()
{
if (fRootVNode) {
delete fRootVNode;
fRootVNode = NULL;
}
if (fTree) {
delete fTree;
fTree = NULL;
}
if (fSuperBlock) {
delete fSuperBlock;
fSuperBlock = NULL;
}
if (fBlockCache) {
delete fBlockCache;
fBlockCache = NULL;
}
if (fDeviceName) {
delete[] fDeviceName;
fDeviceName = NULL;
}
if (fDevice >= 0) {
close(fDevice);
fDevice = -1;
}
if (fSettings) {
delete fSettings;
fSettings = NULL;
}
fNegativeEntries.MakeEmpty();
fHashFunction = NULL;
fFSVolume = NULL;
return B_OK;
}
// GetBlockSize
off_t
Volume::GetBlockSize() const
{
return fSuperBlock->GetBlockSize();
}
// CountBlocks
off_t
Volume::CountBlocks() const
{
return fSuperBlock->CountBlocks();
}
// CountFreeBlocks
off_t
Volume::CountFreeBlocks() const
{
return fSuperBlock->CountFreeBlocks();
}
// GetName
const char *
Volume::GetName() const
{
return fVolumeName;
}
// UpdateName
void
Volume::UpdateName(partition_id partitionID)
{
if (fSuperBlock->GetLabel(fVolumeName, sizeof(fVolumeName)) == B_OK)
return;
if (get_default_partition_content_name(partitionID, "ReiserFS",
fVolumeName, sizeof(fVolumeName)) == B_OK)
return;
strlcpy(fVolumeName, "ReiserFS Volume", sizeof(fVolumeName));
}
// GetDeviceName
const char *
Volume::GetDeviceName() const
{
return fDeviceName;
}
// GetKeyOffsetForName
status_t
Volume::GetKeyOffsetForName(const char *name, int len, uint64 *result)
{
status_t error = (name && result ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
if (fHashFunction)
*result = key_offset_for_name(fHashFunction, name, len);
else
error = B_ERROR;
}
return error;
}
// GetVNode
status_t
Volume::GetVNode(ino_t id, VNode **node)
{
return get_vnode(GetFSVolume(), id, (void**)node);
}
// PutVNode
status_t
Volume::PutVNode(ino_t id)
{
return put_vnode(GetFSVolume(), id);
}
// FindVNode
/*! \brief Finds the node identified by a ino_t.
\note The method does not initialize the parent ID for non-directory nodes.
\param id ID of the node to be found.
\param node pointer to a pre-allocated VNode to be initialized to
the found node.
\return \c B_OK, if everything went fine.
*/
status_t
Volume::FindVNode(ino_t id, VNode *node)
{
return FindVNode(VNode::GetDirIDFor(id), VNode::GetObjectIDFor(id), node);
}
// FindVNode
/*! \brief Finds the node identified by a directory ID, object ID pair.
\note The method does not initialize the parent ID for non-directory nodes.
\param dirID Directory ID of the node to be found.
\param objectID Object ID of the node to be found.
\param node pointer to a pre-allocated VNode to be initialized to
the found node.
\return \c B_OK, if everything went fine.
*/
status_t
Volume::FindVNode(uint32 dirID, uint32 objectID, VNode *node)
{
// NOTE: The node's parent dir ID is not initialized!
status_t error = (node ? B_OK : B_BAD_VALUE);
// init the node
if (error == B_OK)
error = node->SetTo(dirID, objectID);
// find the stat item
StatItem item;
if (error == B_OK) {
error = fTree->FindStatItem(dirID, objectID, &item);
if (error != B_OK) {
FATAL(("Couldn't find stat item for node (%lu, %lu)\n",
dirID, objectID));
}
}
// get the stat data
if (error == B_OK)
SET_ERROR(error, item.GetStatData(node->GetStatData(), true));
// for dirs get the ".." entry, since we need the parent dir ID
if (error == B_OK && node->IsDir()) {
DirItem dirItem;
int32 index = 0;
error = fTree->FindDirEntry(dirID, objectID, "..", &dirItem, &index);
if (error == B_OK) {
DirEntry *entry = dirItem.EntryAt(index);
node->SetParentID(entry->GetDirID(), entry->GetObjectID());
}
else {
FATAL(("failed to find `..' entry for dir node (%lu, %ld)\n",
dirID, objectID));
}
}
return error;
}
// FindDirEntry
/*! \brief Searches an entry in a directory.
\note Must not be called with \a entryName "." or ".."!
\param dir The directory.
\param entryName Name of the entry.
\param foundNode pointer to a pre-allocated VNode to be initialized to
the found entry.
\param failIfHidden The method shall fail, if the entry is hidden.
\return \c B_OK, if everything went fine.
*/
status_t
Volume::FindDirEntry(VNode *dir, const char *entryName, VNode *foundNode,
bool failIfHidden)
{
status_t error = (dir && foundNode ? B_OK : B_BAD_VALUE);
// find the DirEntry
DirItem item;
int32 entryIndex = 0;
if (error == B_OK) {
error = fTree->FindDirEntry(dir->GetDirID(), dir->GetObjectID(),
entryName, &item, &entryIndex);
}
// find the child node
if (error == B_OK) {
DirEntry *entry = item.EntryAt(entryIndex);
error = FindVNode(entry->GetDirID(), entry->GetObjectID(), foundNode);
if (error == B_OK && failIfHidden && entry->IsHidden())
error = B_ENTRY_NOT_FOUND;
}
return error;
}
// ReadObject
/*! \brief Reads data from an object.
For subsequent reads better use a StreamReader.
\param node The object to be read from.
\param offset The file offset at which to start with reading.
\param buffer The buffer into which the data shall be written.
\param bufferSize The number of bytes to be read.
\param bytesRead Pointer to a size_t to be set to the number of bytes
actually read.
\return \c B_OK, if everything went fine.
*/
status_t
Volume::ReadObject(VNode *node, off_t offset, void *buffer, size_t bufferSize,
size_t *bytesRead)
{
status_t error = (node && buffer && bytesRead && offset >= 0
? B_OK : B_BAD_VALUE);
// read files or symlinks only
if (error == B_OK && !(node->IsFile() || node->IsSymlink()))
error = B_BAD_VALUE;
// let a StreamReader do the actual work
if (error == B_OK) {
StreamReader reader(fTree, node->GetDirID(), node->GetObjectID());
error = reader.InitCheck();
if (error == B_OK)
error = reader.ReadAt(offset, buffer, bufferSize, bytesRead);
}
return error;
}
// ReadLink
/*! \brief Reads a symlink.
\note It is not check whether the object is a symlink or not! The caller
is responsible for that.
\param node The symlink to be read from.
\param buffer The buffer into which the data shall be written.
\param bufferSize The number of bytes to be read.
\param bytesRead Pointer to a size_t to be set to the number of bytes
actually read.
\return \c B_OK, if everything went fine.
*/
status_t
Volume::ReadLink(VNode *node, char *buffer, size_t bufferSize,
size_t *bytesRead)
{
return ReadObject(node, 0, buffer, bufferSize, bytesRead);
}
// FindEntry
status_t
Volume::FindEntry(const VNode *rootDir, const char *path, VNode *foundNode)
{
// Note: does not resolve links.
PRINT(("Volume::FindEntry(`%s')\n", path));
status_t error = (rootDir && path && foundNode ? B_OK : B_BAD_VALUE);
if (error == B_OK && (path[0] == '\0' || path[0] == '/'))
error = B_ENTRY_NOT_FOUND;
// start at the given root dir
if (error == B_OK)
*foundNode = *rootDir;
// resolve until we hit the end of the string
while (error == B_OK && path[0] != '\0') {
PRINT((" remaining path: `%s'\n", path));
int32 len = strlen(path);
// find the first `/'
const char *componentNameEnd = strchr(path, '/');
if (!componentNameEnd)
componentNameEnd = path + len;
// get the name of the first component
int32 componentLen = componentNameEnd - path;
if (componentLen >= B_FILE_NAME_LENGTH)
return B_NAME_TOO_LONG;
char component[B_FILE_NAME_LENGTH];
strncpy(component, path, componentLen);
component[componentLen] = '\0';
// get the component
PRINT((" looking for dir entry: `%s'\n", component));
error = FindDirEntry(foundNode, component, foundNode);
// skip trailing `/'s
if (error == B_OK) {
path = componentNameEnd;
while (*path == '/')
path++;
}
}
PRINT(("Volume::FindEntry(`%s') done: %s\n", path, strerror(error)));
return error;
}
// IsNegativeEntry
bool
Volume::IsNegativeEntry(ino_t id)
{
for (int32 i = fNegativeEntries.CountItems() - 1; i >= 0; i--) {
if (fNegativeEntries.ItemAt(i) == id)
return true;
}
return false;
}
// IsNegativeEntry
bool
Volume::IsNegativeEntry(uint32 dirID, uint32 objectID)
{
return IsNegativeEntry(VNode::GetIDFor(dirID, objectID));
}
// GetHideEsoteric
bool
Volume::GetHideEsoteric() const
{
return fSettings->GetHideEsoteric();
}
// _ReadSuperBlock
status_t
Volume::_ReadSuperBlock()
{
status_t error = B_OK;
fSuperBlock = new(nothrow) SuperBlock;
if (fSuperBlock)
error = fSuperBlock->Init(fDevice);
else
error = B_NO_MEMORY;
// check FS state
if (error == B_OK && fSuperBlock->GetState() != REISERFS_VALID_FS) {
FATAL(("The superblock indicates a non-valid FS! Bailing out."));
error = B_ERROR;
}
// check FS version
if (error == B_OK && fSuperBlock->GetVersion() > REISERFS_VERSION_2) {
FATAL(("The superblock indicates a version greater than 2 (%u)! "
"Bailing out.", fSuperBlock->GetVersion()));
error = B_ERROR;
}
RETURN_ERROR(error);
}
// hash_function_for_code
static
hash_function_t
hash_function_for_code(uint32 code)
{
hash_function_t function = NULL;
// find the hash function
switch (code) {
case TEA_HASH:
function = keyed_hash;
break;
case YURA_HASH:
function = yura_hash;
break;
case R5_HASH:
function = r5_hash;
break;
case UNSET_HASH:
default:
break;
}
return function;
}
// _InitHashFunction
void
Volume::_InitHashFunction()
{
// get the hash function
fHashFunction = hash_function_for_code(fSuperBlock->GetHashFunctionCode());
// try to detect it, if it is not set or is not the right one
if (!fHashFunction || !_VerifyHashFunction(fHashFunction)) {
INFORM(("No or wrong directory hash function. Try to detect...\n"));
uint32 code = _DetectHashFunction();
fHashFunction = hash_function_for_code(code);
// verify it
if (fHashFunction) {
if (_VerifyHashFunction(fHashFunction)) {
INFORM(("Directory hash function successfully detected: %lu\n",
code));
} else {
fHashFunction = NULL;
INFORM(("Detected directory hash function is not the right "
"one.\n"));
}
} else
INFORM(("Failed to detect the directory hash function.\n"));
}
}
// _DetectHashFunction
uint32
Volume::_DetectHashFunction()
{
// iterate over the entries in the root dir until we find an entry, that
// let us draw an unambiguous conclusion
DirEntryIterator iterator(fTree, fRootVNode->GetDirID(),
fRootVNode->GetObjectID(), DOT_DOT_OFFSET + 1);
uint32 foundCode = UNSET_HASH;
DirItem item;
int32 index = 0;
while (foundCode == UNSET_HASH
&& iterator.GetNext(&item, &index) == B_OK) {
DirEntry *entry = item.EntryAt(index);
uint64 offset = entry->GetOffset();
uint32 hashCodes[] = { TEA_HASH, YURA_HASH, R5_HASH };
int32 hashCodeCount = sizeof(hashCodes) / sizeof(uint32);
size_t nameLen = 0;
const char *name = item.EntryNameAt(index, &nameLen);
if (!name) // bad data!
continue;
// try each hash function -- if there's a single winner, we're done,
// otherwise the next entry must help
for (int32 i = 0; i < hashCodeCount; i++) {
hash_function_t function = hash_function_for_code(hashCodes[i]);
uint64 testOffset = key_offset_for_name(function, name, nameLen);
if (offset_hash_value(offset) == offset_hash_value(testOffset)) {
if (foundCode != UNSET_HASH) {
// ambiguous
foundCode = UNSET_HASH;
break;
} else
foundCode = hashCodes[i];
}
}
}
return foundCode;
}
// _VerifyHashFunction
bool
Volume::_VerifyHashFunction(hash_function_t function)
{
bool result = true;
// iterate over the entries in the root dir until we find an entry, that
// doesn't falsify the hash function
DirEntryIterator iterator(fTree, fRootVNode->GetDirID(),
fRootVNode->GetObjectID(), DOT_DOT_OFFSET + 1);
DirItem item;
int32 index = 0;
while (iterator.GetNext(&item, &index) == B_OK) {
DirEntry *entry = item.EntryAt(index);
uint64 offset = entry->GetOffset();
// try the hash function
size_t nameLen = 0;
if (const char *name = item.EntryNameAt(index, &nameLen)) {
uint64 testOffset = key_offset_for_name(function, name, nameLen);
if (offset_hash_value(offset) != offset_hash_value(testOffset)) {
result = false;
break;
}
} // else: bad data
}
return result;
}
// _InitNegativeEntries
void
Volume::_InitNegativeEntries()
{
// build a list of vnode IDs
for (int32 i = 0; const char *entry = fSettings->HiddenEntryAt(i); i++) {
if (entry && strlen(entry) > 0 && entry[0] != '/') {
VNode node;
if (FindEntry(fRootVNode, entry, &node) == B_OK
&& node.GetID() != fRootVNode->GetID()) {
fNegativeEntries.AddItem(node.GetID());
} else
INFORM(("WARNING: negative entry not found: `%s'\n", entry));
}
}
}
↑ V576 Incorrect format. Consider checking the second actual argument of the 'dprintf' function. The memsize type argument is expected.
↑ V576 Incorrect format. Consider checking the third actual argument of the 'dprintf' function. The memsize type argument is expected.
↑ V576 Incorrect format. Consider checking the second actual argument of the 'dprintf' function. The memsize type argument is expected.
↑ V576 Incorrect format. Consider checking the third actual argument of the 'dprintf' function. The memsize type argument is expected.
↑ V576 Incorrect format. Consider checking the second actual argument of the 'dprintf' function. The memsize type argument is expected.