root/src/add-ons/kernel/file_systems/xfs/Volume.cpp
/*
 * Copyright 2001 - 2017, Axel Dörfler, axeld @pinc - software.de.
 * Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com
 * All rights reserved. Distributed under the terms of the MIT License.
 */


#include "Volume.h"

#include "Checksum.h"
#include "Inode.h"


Volume::Volume(fs_volume *volume)
        : fFSVolume(volume)
{
        fFlags = 0;
        mutex_init(&fLock, "xfs volume");
        TRACE("Volume::Volume() : Initialising volume");
}


Volume::~Volume()
{
        mutex_destroy(&fLock);
        TRACE("Volume::Destructor : Removing Volume");
}


bool
Volume::IsValidSuperBlock() const
{
        return fSuperBlock.IsValid();
}


status_t
Volume::Identify(int fd, XfsSuperBlock *superBlock)
{

        TRACE("Volume::Identify() : Identifying Volume in progress");

        //Create a buffer of 512 bytes for Crc verification
        char buf[512];

        if(read_pos(fd, 0, buf, 512) != 512)
                return B_IO_ERROR;

        memcpy(superBlock, buf, sizeof(XfsSuperBlock));

        int version = B_BENDIAN_TO_HOST_INT16(superBlock->Version()) & XFS_SB_VERSION_NUMBITS;

        // if its V5 filesystem check for superblock checksum
        if (superBlock->MagicNum() == B_HOST_TO_BENDIAN_INT32(XFS_SB_MAGIC)
                && (version == 5 || superBlock->Crc() != 0)) {

                TRACE("Superblock Crc: (%" B_PRIu32 ")\n", superBlock->Crc());

                if(!xfs_verify_cksum(buf, 512, XfsSuperBlock::Offset_crc())) {
                         ERROR("Filesystem is corrupted");
                         return B_BAD_VALUE;
                }

        }

        superBlock->SwapEndian();

        if (!superBlock->IsValid()) {
                ERROR("Volume::Identify(): Invalid Superblock!\n");
                return B_BAD_VALUE;
        }
        return B_OK;
}


status_t
Volume::Mount(const char *deviceName, uint32 flags)
{
        TRACE("Volume::Mount() : Mounting in progress");

        flags |= B_MOUNT_READ_ONLY;

        if ((flags & B_MOUNT_READ_ONLY) != 0) {
                TRACE("Volume::Mount(): Read only\n");
        } else {
                TRACE("Volume::Mount(): Read write\n");
        }

        DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
                                                                                ? O_RDONLY
                                                                                : O_RDWR);
        fDevice = opener.Device();
        if (fDevice < B_OK) {
                ERROR("Volume::Mount(): couldn't open device\n");
                return fDevice;
        }

        if (opener.IsReadOnly())
                fFlags |= VOLUME_READ_ONLY;

        // read the superblock
        status_t status = Identify(fDevice, &fSuperBlock);
        if (status != B_OK) {
                ERROR("Volume::Mount(): Invalid super block!\n");
                return B_BAD_VALUE;
        }

        if ((fSuperBlock.Version() & XFS_SB_VERSION_NUMBITS) == 5)
                TRACE("Volume::Mount(): Valid Version 5 SuperBlock.\n");
        else
                TRACE("Volume::Mount(): Valid Version 4 SuperBlock.\n");


        // check if the device size is large enough to hold the file system
        off_t diskSize;
        if (opener.GetSize(&diskSize) != B_OK) {
                ERROR("Volume:Mount() Unable to get diskSize");
                return B_ERROR;
        }

        opener.Keep();

        //publish the root inode
        Inode* rootInode = new(std::nothrow) Inode(this, Root());
        if (rootInode == NULL)
                return B_NO_MEMORY;

        status = rootInode->Init();
        if (status != B_OK)
                return status;

        status = publish_vnode(FSVolume(), Root(),
                (void*)rootInode, &gxfsVnodeOps, rootInode->Mode(), 0);
        if (status != B_OK)
                return B_BAD_VALUE;

        return B_OK;
}


status_t
Volume::Unmount()
{
        TRACE("Volume::Unmount(): Unmounting");

        TRACE("Volume::Unmount(): Closing device");
        close(fDevice);

        return B_OK;
}