root/src/add-ons/kernel/file_systems/shared/DeviceOpener.cpp
/*
 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de.
 * All rights reserved. Distributed under the terms of the MIT License.
 */


#include "file_systems/DeviceOpener.h"

#ifndef FS_SHELL
#       include <errno.h>
#       include <Drivers.h>
#       include <fs_cache.h>
#endif


DeviceOpener::DeviceOpener(const char* device, int mode)
        :
        fBlockCache(NULL)
{
        Open(device, mode);
}


DeviceOpener::DeviceOpener(int fd, int mode)
        :
        fBlockCache(NULL)
{
        Open(fd, mode);
}


DeviceOpener::~DeviceOpener()
{
        if (fDevice >= 0){
                RemoveCache(false);
                close(fDevice);
        }
}


int
DeviceOpener::Open(const char* device, int mode)
{
        fDevice = open(device, mode | O_NOCACHE);
        if (fDevice < 0)
                fDevice = errno;

        if (fDevice < 0 && _IsReadWrite(mode)) {
                return Open(device, O_RDONLY | O_NOCACHE);
        }

        if (fDevice >= 0) {
                fMode = mode;
                if (_IsReadWrite(mode)) {
                        device_geometry geometry;
                        if (!ioctl(fDevice, B_GET_GEOMETRY, &geometry,
                                                sizeof(device_geometry)))
                        {
                                if (geometry.read_only) {
                                        close(fDevice);
                                        return Open(device, O_RDONLY | O_NOCACHE);
                                }
                        }
                }
        }
        return fDevice;
}


int
DeviceOpener::Open(int fd, int mode)
{
        fDevice = dup(fd);
        if (fDevice < 0)
                return errno;

        fMode = mode;

        return fDevice;
}


void*
DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
{
        return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
                IsReadOnly());
}


void
DeviceOpener::RemoveCache(bool allowWrites)
{
        if (fBlockCache == NULL)
                return;

        block_cache_delete(fBlockCache, allowWrites);
        fBlockCache = NULL;
}


void
DeviceOpener::Keep()
{
        fDevice = -1;
}


status_t
DeviceOpener::GetSize(off_t* _size, uint32* _blockSize)
{
        device_geometry geometry;
        if (ioctl(fDevice, B_GET_GEOMETRY, &geometry,
                                sizeof(device_geometry)) < 0) {
                // maybe it's just a file
                struct stat stat;
                if (fstat(fDevice, &stat) < 0)
                        return B_ERROR;

                if (_size)
                        *_size = stat.st_size;
                if (_blockSize) // that shouldn't cause us any problems
                        *_blockSize = 512;

                return B_OK;
        }

        if (_size) {
                *_size = 1ULL * geometry.head_count * geometry.cylinder_count
                        * geometry.sectors_per_track * geometry.bytes_per_sector;
        }
        if (_blockSize)
                *_blockSize = geometry.bytes_per_sector;

        return B_OK;
}