* Implemented a read-only exFAT file system, tested with a 4GB image.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40409 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -4,6 +4,7 @@ SubInclude HAIKU_TOP src add-ons kernel file_systems bfs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems bindfs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems btrfs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems cdda ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems exfat ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems ext2 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems fat ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems googlefs ;
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2001-2008, Axel Dörfler, [email protected].
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CACHED_BLOCK_H
|
||||
#define CACHED_BLOCK_H
|
||||
|
||||
//! interface for the block cache
|
||||
|
||||
#include <fs_cache.h>
|
||||
|
||||
#include "Volume.h"
|
||||
|
||||
|
||||
class CachedBlock {
|
||||
public:
|
||||
CachedBlock(Volume* volume);
|
||||
CachedBlock(Volume* volume, off_t block);
|
||||
~CachedBlock();
|
||||
|
||||
void Keep();
|
||||
void Unset();
|
||||
|
||||
const uint8* SetTo(off_t block);
|
||||
|
||||
const uint8* Block() const { return fBlock; }
|
||||
off_t BlockNumber() const { return fBlockNumber; }
|
||||
|
||||
private:
|
||||
CachedBlock(const CachedBlock &);
|
||||
CachedBlock &operator=(const CachedBlock &);
|
||||
// no implementation
|
||||
|
||||
protected:
|
||||
Volume* fVolume;
|
||||
off_t fBlockNumber;
|
||||
uint8* fBlock;
|
||||
};
|
||||
|
||||
|
||||
// inlines
|
||||
|
||||
|
||||
inline
|
||||
CachedBlock::CachedBlock(Volume* volume)
|
||||
:
|
||||
fVolume(volume),
|
||||
fBlockNumber(0),
|
||||
fBlock(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
CachedBlock::CachedBlock(Volume* volume, off_t block)
|
||||
:
|
||||
fVolume(volume),
|
||||
fBlockNumber(0),
|
||||
fBlock(NULL)
|
||||
{
|
||||
SetTo(block);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
CachedBlock::~CachedBlock()
|
||||
{
|
||||
Unset();
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
CachedBlock::Keep()
|
||||
{
|
||||
fBlock = NULL;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
CachedBlock::Unset()
|
||||
{
|
||||
if (fBlock != NULL) {
|
||||
block_cache_put(fVolume->BlockCache(), fBlockNumber);
|
||||
fBlock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline const uint8 *
|
||||
CachedBlock::SetTo(off_t block)
|
||||
{
|
||||
Unset();
|
||||
fBlockNumber = block;
|
||||
return fBlock = (uint8 *)block_cache_get(fVolume->BlockCache(), block);
|
||||
}
|
||||
|
||||
#endif // CACHED_BLOCK_H
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2011, Haiku Inc. All rights reserved.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Jérôme Duval
|
||||
*/
|
||||
|
||||
|
||||
#include "DataStream.h"
|
||||
|
||||
#include "Volume.h"
|
||||
|
||||
|
||||
//#define TRACE_EXFAT
|
||||
#ifdef TRACE_EXFAT
|
||||
# define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
|
||||
#else
|
||||
# define TRACE(x...) ;
|
||||
#endif
|
||||
#define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
|
||||
|
||||
|
||||
DataStream::DataStream(Volume* volume, Inode* inode, off_t size)
|
||||
:
|
||||
kBlockSize(volume->BlockSize()),
|
||||
fVolume(volume),
|
||||
fInode(inode),
|
||||
fSize(size)
|
||||
{
|
||||
fNumBlocks = size == 0 ? 0 : ((size - 1) / kBlockSize) + 1;
|
||||
}
|
||||
|
||||
|
||||
DataStream::~DataStream()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DataStream::FindBlock(off_t pos, off_t& physical, off_t *_length)
|
||||
{
|
||||
if (pos >= fSize) {
|
||||
TRACE("FindBlock: offset larger than size\n");
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
cluster_t clusterIndex = pos / fVolume->ClusterSize();
|
||||
uint32 offset = pos % fVolume->ClusterSize();
|
||||
|
||||
cluster_t cluster = fInode->StartCluster();
|
||||
for (uint32 i = 0; i < clusterIndex; i++)
|
||||
cluster = fInode->NextCluster(cluster);
|
||||
fsblock_t block;
|
||||
fVolume->ClusterToBlock(cluster, block);
|
||||
physical = block * kBlockSize + offset;
|
||||
*_length = min_c(kBlockSize, fSize - pos);
|
||||
TRACE("inode %" B_PRIdINO ": cluster %ld, pos %lld, %lld\n",
|
||||
fInode->ID(), fInode->StartCluster(), pos, physical);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2011, Haiku Inc. All rights reserved.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Jérôme Duval
|
||||
*/
|
||||
#ifndef DATASTREAM_H
|
||||
#define DATASTREAM_H
|
||||
|
||||
|
||||
#include "exfat.h"
|
||||
#include "Inode.h"
|
||||
|
||||
|
||||
class Volume;
|
||||
|
||||
|
||||
class DataStream
|
||||
{
|
||||
public:
|
||||
DataStream(Volume* volume, Inode* inode,
|
||||
off_t size);
|
||||
~DataStream();
|
||||
|
||||
status_t FindBlock(off_t pos, off_t& physical,
|
||||
off_t *_length = NULL);
|
||||
private:
|
||||
const uint32 kBlockSize;
|
||||
Volume* fVolume;
|
||||
Inode* fInode;
|
||||
off_t fNumBlocks;
|
||||
off_t fSize;
|
||||
};
|
||||
|
||||
#endif // DATASTREAM_H
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright 2011, Jérôme Duval, [email protected].
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "DirectoryIterator.h"
|
||||
|
||||
#include "encodings.h"
|
||||
#include "Inode.h"
|
||||
|
||||
|
||||
//#define TRACE_EXFAT
|
||||
#ifdef TRACE_EXFAT
|
||||
# define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
|
||||
#else
|
||||
# define TRACE(x...) ;
|
||||
#endif
|
||||
# define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
|
||||
|
||||
|
||||
DirectoryIterator::DirectoryIterator(Inode* inode)
|
||||
:
|
||||
fOffset(-2),
|
||||
fCluster(inode->StartCluster()),
|
||||
fInode(inode),
|
||||
fBlock(inode->GetVolume()),
|
||||
fCurrent(NULL)
|
||||
{
|
||||
TRACE("DirectoryIterator::DirectoryIterator() %ld\n", fCluster);
|
||||
}
|
||||
|
||||
|
||||
DirectoryIterator::~DirectoryIterator()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryIterator::InitCheck()
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id,
|
||||
EntryVisitor* visitor)
|
||||
{
|
||||
if (fOffset == -2) {
|
||||
*_nameLength = 3;
|
||||
strlcpy(name, "..", *_nameLength);
|
||||
if (fInode->ID() == 1)
|
||||
*_id = fInode->ID();
|
||||
else
|
||||
*_id = fInode->Parent();
|
||||
fOffset = -1;
|
||||
TRACE("DirectoryIterator::GetNext() found ..\n");
|
||||
return B_OK;
|
||||
} else if (fOffset == -1) {
|
||||
*_nameLength = 2;
|
||||
strlcpy(name, ".", *_nameLength);
|
||||
*_id = fInode->ID();
|
||||
fOffset = 0;
|
||||
TRACE("DirectoryIterator::GetNext() found .\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH];
|
||||
size_t nameLength = EXFAT_FILENAME_MAX_LENGTH;
|
||||
status_t status = _GetNext(unicodeName, &nameLength, _id, visitor);
|
||||
if (status == B_OK && name != NULL) {
|
||||
unicode_to_utf8(unicodeName, nameLength, (uint8 *)name , _nameLength);
|
||||
TRACE("DirectoryIterator::GetNext() %ld %s, %" B_PRIdINO "\n",
|
||||
fInode->Cluster(), name, *_id);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id)
|
||||
{
|
||||
if (strcmp(name, ".") == 0) {
|
||||
*_id = fInode->ID();
|
||||
return B_OK;
|
||||
} else if (strcmp(name, "..") == 0) {
|
||||
if (fInode->ID() == 1)
|
||||
*_id = fInode->ID();
|
||||
else
|
||||
*_id = fInode->Parent();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
Rewind();
|
||||
fOffset = 0;
|
||||
|
||||
uchar currentName[EXFAT_FILENAME_MAX_LENGTH];
|
||||
size_t currentLength = EXFAT_FILENAME_MAX_LENGTH;
|
||||
while (_GetNext((uchar*)currentName, ¤tLength, _id) == B_OK) {
|
||||
char utfName[EXFAT_FILENAME_MAX_LENGTH];
|
||||
size_t utfLength = EXFAT_FILENAME_MAX_LENGTH;
|
||||
unicode_to_utf8(currentName, currentLength, (uint8*)utfName, &utfLength);
|
||||
if (nameLength == utfLength
|
||||
&& strncmp(utfName, name, nameLength) == 0) {
|
||||
TRACE("DirectoryIterator::Lookup() found ID %" B_PRIdINO "\n", *_id);
|
||||
return B_OK;
|
||||
}
|
||||
currentLength = EXFAT_FILENAME_MAX_LENGTH;
|
||||
}
|
||||
|
||||
TRACE("DirectoryIterator::Lookup() not found %s\n", name);
|
||||
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryIterator::LookupEntry(EntryVisitor* visitor)
|
||||
{
|
||||
fCluster = fInode->Cluster();
|
||||
fOffset = fInode->Offset();
|
||||
|
||||
uchar unicodeName[EXFAT_FILENAME_MAX_LENGTH];
|
||||
size_t nameLength = EXFAT_FILENAME_MAX_LENGTH;
|
||||
return _GetNext(unicodeName, &nameLength, NULL, visitor);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryIterator::Rewind()
|
||||
{
|
||||
fOffset = -2;
|
||||
fCluster = fInode->StartCluster();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DirectoryIterator::Iterate(EntryVisitor &visitor)
|
||||
{
|
||||
Rewind();
|
||||
|
||||
while (_NextEntry() != B_ENTRY_NOT_FOUND) {
|
||||
switch (fCurrent->type) {
|
||||
case EXFAT_ENTRY_TYPE_BITMAP:
|
||||
visitor.VisitBitmap(fCurrent);
|
||||
break;
|
||||
case EXFAT_ENTRY_TYPE_UPPERCASE:
|
||||
visitor.VisitUppercase(fCurrent);
|
||||
break;
|
||||
case EXFAT_ENTRY_TYPE_LABEL:
|
||||
visitor.VisitLabel(fCurrent);
|
||||
break;
|
||||
case EXFAT_ENTRY_TYPE_FILE:
|
||||
visitor.VisitFile(fCurrent);
|
||||
break;
|
||||
case EXFAT_ENTRY_TYPE_FILEINFO:
|
||||
visitor.VisitFileInfo(fCurrent);
|
||||
break;
|
||||
case EXFAT_ENTRY_TYPE_FILENAME:
|
||||
visitor.VisitFilename(fCurrent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryIterator::_GetNext(uchar* name, size_t* _nameLength, ino_t* _id,
|
||||
EntryVisitor* visitor)
|
||||
{
|
||||
size_t nameMax = *_nameLength;
|
||||
size_t nameIndex = 0;
|
||||
status_t status;
|
||||
int32 chunkCount = 1;
|
||||
while ((status = _NextEntry()) == B_OK) {
|
||||
TRACE("DirectoryIterator::_GetNext() %ld/%p, type 0x%x, offset %lld\n",
|
||||
fInode->Cluster(), fCurrent, fCurrent->type, fOffset);
|
||||
if (fCurrent->type == EXFAT_ENTRY_TYPE_FILE) {
|
||||
chunkCount = fCurrent->file.chunkCount;
|
||||
if (_id != NULL) {
|
||||
*_id = fInode->GetVolume()->GetIno(fCluster, fOffset - 1,
|
||||
fInode->ID());
|
||||
}
|
||||
if (visitor != NULL)
|
||||
visitor->VisitFile(fCurrent);
|
||||
TRACE("DirectoryIterator::_GetNext() File chunkCount %ld\n",
|
||||
chunkCount);
|
||||
} else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILEINFO) {
|
||||
chunkCount--;
|
||||
TRACE("DirectoryIterator::_GetNext() Filename length %d\n",
|
||||
fCurrent->file_info.name_length);
|
||||
*_nameLength = fCurrent->file_info.name_length * 2;
|
||||
if (visitor != NULL)
|
||||
visitor->VisitFileInfo(fCurrent);
|
||||
} else if (fCurrent->type == EXFAT_ENTRY_TYPE_FILENAME) {
|
||||
TRACE("DirectoryIterator::_GetNext() Filename\n");
|
||||
memcpy((uint8*)name + nameIndex, fCurrent->name_label.name,
|
||||
sizeof(fCurrent->name_label.name));
|
||||
nameIndex += sizeof(fCurrent->name_label.name);
|
||||
name[nameIndex] = '\0';
|
||||
chunkCount--;
|
||||
if (visitor != NULL)
|
||||
visitor->VisitFilename(fCurrent);
|
||||
}
|
||||
|
||||
if (chunkCount == 0 || nameIndex >= nameMax)
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == B_OK) {
|
||||
//*_nameLength = nameIndex;
|
||||
#ifdef TRACE_EXFAT
|
||||
char utfName[EXFAT_FILENAME_MAX_LENGTH];
|
||||
size_t utfLen = EXFAT_FILENAME_MAX_LENGTH;
|
||||
unicode_to_utf8(name, nameIndex, (uint8*)utfName, &utfLen);
|
||||
TRACE("DirectoryIterator::_GetNext() Found %s %ld\n", utfName,
|
||||
*_nameLength);
|
||||
#endif
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryIterator::_NextEntry()
|
||||
{
|
||||
if (fCurrent == NULL) {
|
||||
fsblock_t block;
|
||||
fInode->GetVolume()->ClusterToBlock(fCluster, block);
|
||||
block += (fOffset / fInode->GetVolume()->EntriesPerBlock())
|
||||
% (1 << fInode->GetVolume()->SuperBlock().BlocksPerClusterShift());
|
||||
TRACE("DirectoryIterator::_NextEntry() init to block %lld\n", block);
|
||||
fCurrent = (struct exfat_entry*)fBlock.SetTo(block)
|
||||
+ fOffset % fInode->GetVolume()->EntriesPerBlock();
|
||||
} else if ((fOffset % fInode->GetVolume()->EntriesPerBlock()) == 0) {
|
||||
fsblock_t block;
|
||||
if ((fOffset % fInode->GetVolume()->EntriesPerCluster()) == 0) {
|
||||
fCluster = fInode->NextCluster(fCluster);
|
||||
if (fCluster == EXFAT_CLUSTER_END)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
fInode->GetVolume()->ClusterToBlock(fCluster, block);
|
||||
} else
|
||||
block = fBlock.BlockNumber() + 1;
|
||||
TRACE("DirectoryIterator::_NextEntry() block %lld\n", block);
|
||||
fCurrent = (struct exfat_entry*)fBlock.SetTo(block);
|
||||
} else
|
||||
fCurrent++;
|
||||
fOffset++;
|
||||
|
||||
return fCurrent->type == 0 ? B_ENTRY_NOT_FOUND : B_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2011, Jérôme Duval, [email protected].
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef DIRECTORYITERATOR_H
|
||||
#define DIRECTORYITERATOR_H
|
||||
|
||||
|
||||
#include "CachedBlock.h"
|
||||
#include "exfat.h"
|
||||
|
||||
class Inode;
|
||||
|
||||
class EntryVisitor {
|
||||
public:
|
||||
EntryVisitor() {};
|
||||
virtual ~EntryVisitor() {};
|
||||
virtual bool VisitBitmap(struct exfat_entry*)
|
||||
{ return false; }
|
||||
virtual bool VisitUppercase(struct exfat_entry*)
|
||||
{ return false; }
|
||||
virtual bool VisitLabel(struct exfat_entry*)
|
||||
{ return false; }
|
||||
virtual bool VisitFilename(struct exfat_entry*)
|
||||
{ return false; }
|
||||
virtual bool VisitFile(struct exfat_entry*)
|
||||
{ return false; }
|
||||
virtual bool VisitFileInfo(struct exfat_entry*)
|
||||
{ return false; }
|
||||
};
|
||||
|
||||
|
||||
class DirectoryIterator {
|
||||
public:
|
||||
DirectoryIterator(Inode* inode);
|
||||
~DirectoryIterator();
|
||||
|
||||
status_t InitCheck();
|
||||
|
||||
status_t GetNext(char* name, size_t* _nameLength,
|
||||
ino_t* _id, EntryVisitor* visitor = NULL);
|
||||
status_t Lookup(const char* name, size_t nameLength,
|
||||
ino_t* _id);
|
||||
status_t LookupEntry(EntryVisitor* visitor);
|
||||
status_t Rewind();
|
||||
|
||||
void Iterate(EntryVisitor &visitor);
|
||||
private:
|
||||
status_t _GetNext(uchar* unicodename,
|
||||
size_t* _nameLength, ino_t* _id,
|
||||
EntryVisitor* visitor = NULL);
|
||||
status_t _NextEntry();
|
||||
|
||||
int64 fOffset;
|
||||
cluster_t fCluster;
|
||||
Inode* fInode;
|
||||
CachedBlock fBlock;
|
||||
struct exfat_entry* fCurrent;
|
||||
};
|
||||
|
||||
|
||||
#endif // DIRECTORYITERATOR_H
|
||||
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright 2011, Jérôme Duval, [email protected].
|
||||
* Copyright 2008, Axel Dörfler, [email protected].
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "Inode.h"
|
||||
|
||||
#include <real_time_clock.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "CachedBlock.h"
|
||||
#include "DataStream.h"
|
||||
#include "Utility.h"
|
||||
|
||||
|
||||
#undef ASSERT
|
||||
//#define TRACE_EXFAT
|
||||
#ifdef TRACE_EXFAT
|
||||
# define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
|
||||
# define ASSERT(x) { if (!(x)) kernel_debugger("exfat: assert failed: " #x "\n"); }
|
||||
#else
|
||||
# define TRACE(x...) ;
|
||||
# define ASSERT(x) ;
|
||||
#endif
|
||||
#define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
|
||||
|
||||
|
||||
Inode::Inode(Volume* volume, cluster_t cluster, uint32 offset)
|
||||
:
|
||||
fVolume(volume),
|
||||
fID(volume->GetIno(cluster, offset, 0)),
|
||||
fCluster(cluster),
|
||||
fOffset(offset),
|
||||
fCache(NULL),
|
||||
fMap(NULL)
|
||||
{
|
||||
TRACE("Inode::Inode(%ld, %d) inode %" B_PRIdINO "\n", Cluster(), Offset(),
|
||||
ID());
|
||||
_Init();
|
||||
|
||||
if (ID() == 1) {
|
||||
fFileEntry.file.SetAttribs(EXFAT_ENTRY_ATTRIB_SUBDIR);
|
||||
fFileEntry.file_info.SetStartCluster(Cluster());
|
||||
fFileInfoEntry.file_info.SetFlag(0);
|
||||
} else
|
||||
fInitStatus = UpdateNodeFromDisk();
|
||||
if (fInitStatus == B_OK) {
|
||||
if (!IsDirectory() && !IsSymLink()) {
|
||||
fCache = file_cache_create(fVolume->ID(), ID(), Size());
|
||||
fMap = file_map_create(fVolume->ID(), ID(), Size());
|
||||
}
|
||||
}
|
||||
TRACE("Inode::Inode(%" B_PRIdINO ") end\n", ID());
|
||||
}
|
||||
|
||||
|
||||
Inode::Inode(Volume* volume, ino_t ino)
|
||||
:
|
||||
fVolume(volume),
|
||||
fID(ino),
|
||||
fCluster(0),
|
||||
fOffset(0),
|
||||
fCache(NULL),
|
||||
fMap(NULL),
|
||||
fInitStatus(B_NO_INIT)
|
||||
{
|
||||
struct node_key *key = volume->GetNode(ino, fParent);
|
||||
if (key != NULL) {
|
||||
fCluster = key->cluster;
|
||||
fOffset = key->offset;
|
||||
fInitStatus = B_OK;
|
||||
}
|
||||
TRACE("Inode::Inode(%" B_PRIdINO ") cluster %ld\n", ID(), Cluster());
|
||||
_Init();
|
||||
|
||||
if (fInitStatus == B_OK && ID() != 1)
|
||||
fInitStatus = UpdateNodeFromDisk();
|
||||
else if (fInitStatus == B_OK && ID() == 1) {
|
||||
fFileEntry.file.SetAttribs(EXFAT_ENTRY_ATTRIB_SUBDIR);
|
||||
fFileInfoEntry.file_info.SetStartCluster(Cluster());
|
||||
fFileInfoEntry.file_info.SetFlag(0);
|
||||
}
|
||||
if (fInitStatus == B_OK) {
|
||||
if (!IsDirectory() && !IsSymLink()) {
|
||||
fCache = file_cache_create(fVolume->ID(), ID(), Size());
|
||||
fMap = file_map_create(fVolume->ID(), ID(), Size());
|
||||
}
|
||||
}
|
||||
TRACE("Inode::Inode(%" B_PRIdINO ") end\n", ID());
|
||||
}
|
||||
|
||||
|
||||
Inode::Inode(Volume* volume)
|
||||
:
|
||||
fVolume(volume),
|
||||
fID(0),
|
||||
fCache(NULL),
|
||||
fMap(NULL),
|
||||
fInitStatus(B_NO_INIT)
|
||||
{
|
||||
_Init();
|
||||
}
|
||||
|
||||
|
||||
Inode::~Inode()
|
||||
{
|
||||
TRACE("Inode destructor\n");
|
||||
file_cache_delete(FileCache());
|
||||
file_map_delete(Map());
|
||||
TRACE("Inode destructor: Done\n");
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::InitCheck()
|
||||
{
|
||||
return fInitStatus;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::UpdateNodeFromDisk()
|
||||
{
|
||||
|
||||
DirectoryIterator iterator(this);
|
||||
iterator.LookupEntry(this);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
cluster_t
|
||||
Inode::NextCluster(cluster_t cluster) const
|
||||
{
|
||||
if (!IsContiguous())
|
||||
return GetVolume()->NextCluster(cluster);
|
||||
return cluster + 1;
|
||||
}
|
||||
|
||||
|
||||
mode_t
|
||||
Inode::Mode() const
|
||||
{
|
||||
mode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
|
||||
if (!fVolume->IsReadOnly())
|
||||
mode |= S_IWUSR | S_IWGRP | S_IWOTH;
|
||||
if (fFileEntry.file.Attribs() & EXFAT_ENTRY_ATTRIB_SUBDIR)
|
||||
mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
|
||||
else
|
||||
mode |= S_IFREG;
|
||||
return mode;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::CheckPermissions(int accessMode) const
|
||||
{
|
||||
// you never have write access to a read-only volume
|
||||
if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly())
|
||||
return B_READ_ONLY_DEVICE;
|
||||
|
||||
// get node permissions
|
||||
mode_t mode = Mode();
|
||||
int userPermissions = (mode & S_IRWXU) >> 6;
|
||||
int groupPermissions = (mode & S_IRWXG) >> 3;
|
||||
int otherPermissions = mode & S_IRWXO;
|
||||
|
||||
// get the node permissions for this uid/gid
|
||||
int permissions = 0;
|
||||
uid_t uid = geteuid();
|
||||
gid_t gid = getegid();
|
||||
|
||||
if (uid == 0) {
|
||||
// user is root
|
||||
// root has always read/write permission, but at least one of the
|
||||
// X bits must be set for execute permission
|
||||
permissions = userPermissions | groupPermissions | otherPermissions
|
||||
| R_OK | W_OK;
|
||||
} else if (uid == (uid_t)UserID()) {
|
||||
// user is node owner
|
||||
permissions = userPermissions;
|
||||
} else if (gid == (gid_t)GroupID()) {
|
||||
// user is in owning group
|
||||
permissions = groupPermissions;
|
||||
} else {
|
||||
// user is one of the others
|
||||
permissions = otherPermissions;
|
||||
}
|
||||
|
||||
return (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::FindBlock(off_t pos, off_t& physical, off_t *_length)
|
||||
{
|
||||
DataStream stream(fVolume, this, Size());
|
||||
return stream.FindBlock(pos, physical, _length);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
|
||||
{
|
||||
size_t length = *_length;
|
||||
|
||||
// set/check boundaries for pos/length
|
||||
if (pos < 0) {
|
||||
ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %lld, length %lu)\n",
|
||||
ID(), pos, length);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
if (pos >= Size() || length == 0) {
|
||||
TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %lld, length %lu)\n",
|
||||
ID(), pos, length);
|
||||
*_length = 0;
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
return file_cache_read(FileCache(), NULL, pos, buffer, _length);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Inode::VisitFile(struct exfat_entry* entry)
|
||||
{
|
||||
fFileEntry = *entry;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Inode::VisitFileInfo(struct exfat_entry* entry)
|
||||
{
|
||||
fFileInfoEntry = *entry;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Inode::_Init()
|
||||
{
|
||||
memset(&fFileEntry, 0, sizeof(fFileEntry));
|
||||
memset(&fFileInfoEntry, 0, sizeof(fFileInfoEntry));
|
||||
rw_lock_init(&fLock, "exfat inode");
|
||||
}
|
||||
|
||||
|
||||
// If divisible by 4, but not divisible by 100, but divisible by 400, it's a leap year
|
||||
// 1996 is leap, 1900 is not, 2000 is, 2100 is not
|
||||
#define IS_LEAP_YEAR(y) ((((y) % 4) == 0) && (((y) % 100) || ((((y)) % 400) == 0)))
|
||||
|
||||
/* returns leap days since 1970 */
|
||||
static int leaps(int yr, int mon)
|
||||
{
|
||||
// yr is 1970-based, mon 0-based
|
||||
int result = (yr+2)/4 - (yr + 70) / 100;
|
||||
if((yr+70) >= 100) result++; // correct for 2000
|
||||
if (IS_LEAP_YEAR(yr + 1970))
|
||||
if (mon < 2) result--;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int daze[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334,0,0,0 };
|
||||
|
||||
void
|
||||
Inode::_GetTimespec(uint16 date, uint16 time, struct timespec ×pec) const
|
||||
{
|
||||
static int32 tzoffset = -1; /* in minutes */
|
||||
if (tzoffset == -1)
|
||||
tzoffset = get_timezone_offset() / 60;
|
||||
|
||||
time_t days = daze[(date>>5)&15] + ((date>>9)+10)*365 + leaps((date>>9)+10,((date>>5)&15)-1)+(date&31)-1;
|
||||
|
||||
timespec.tv_sec = ((days * 24 + (time >> 11)) * 60 + ((time>>5)&63) + tzoffset) * 60 + 2*(time&31);
|
||||
timespec.tv_nsec = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright 2011, Jérôme Duval, [email protected].
|
||||
* Copyright 2008, Axel Dörfler, [email protected].
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef INODE_H
|
||||
#define INODE_H
|
||||
|
||||
|
||||
#include <fs_cache.h>
|
||||
#include <lock.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "DirectoryIterator.h"
|
||||
#include "exfat.h"
|
||||
#include "SplayTree.h"
|
||||
#include "Volume.h"
|
||||
|
||||
|
||||
//#define TRACE_EXFAT
|
||||
#ifdef TRACE_EXFAT
|
||||
# define TRACEI(x...) dprintf("\33[34mexfat:\33[0m " x)
|
||||
#else
|
||||
# define TRACEI(x...) ;
|
||||
#endif
|
||||
|
||||
|
||||
struct InodesTreeDefinition;
|
||||
|
||||
|
||||
class Inode : EntryVisitor {
|
||||
public:
|
||||
Inode(Volume* volume, cluster_t cluster,
|
||||
uint32 offset);
|
||||
Inode::Inode(Volume* volume, ino_t ino);
|
||||
~Inode();
|
||||
|
||||
status_t InitCheck();
|
||||
|
||||
ino_t ID() const { return fID; }
|
||||
ino_t Parent() const { return fParent; }
|
||||
cluster_t Cluster() const { return fCluster; }
|
||||
uint32_t Offset() const { return fOffset; }
|
||||
cluster_t StartCluster() const
|
||||
{ return fFileInfoEntry.file_info.StartCluster(); }
|
||||
bool IsContiguous() const
|
||||
{ return fFileInfoEntry.file_info.IsContiguous(); }
|
||||
cluster_t NextCluster(cluster_t cluster) const;
|
||||
|
||||
rw_lock* Lock() { return &fLock; }
|
||||
|
||||
status_t UpdateNodeFromDisk();
|
||||
|
||||
bool IsDirectory() const
|
||||
{ return S_ISDIR(Mode()); }
|
||||
bool IsFile() const
|
||||
{ return S_ISREG(Mode()); }
|
||||
bool IsSymLink() const
|
||||
{ return S_ISLNK(Mode()); }
|
||||
status_t CheckPermissions(int accessMode) const;
|
||||
|
||||
mode_t Mode() const;
|
||||
off_t Size() const { return fFileInfoEntry.file_info.Size(); }
|
||||
uid_t UserID() const { return 0;/*fNode.UserID();*/ }
|
||||
gid_t GroupID() const { return 0;/*fNode.GroupID();*/ }
|
||||
void GetChangeTime(struct timespec ×pec) const
|
||||
{ GetModificationTime(timespec); }
|
||||
void GetModificationTime(struct timespec ×pec) const
|
||||
{ _GetTimespec(fFileEntry.file.ModificationDate(),
|
||||
fFileEntry.file.ModificationTime(), timespec); }
|
||||
void GetCreationTime(struct timespec ×pec) const
|
||||
{ _GetTimespec(fFileEntry.file.CreationDate(),
|
||||
fFileEntry.file.CreationTime(), timespec); }
|
||||
void GetAccessTime(struct timespec ×pec) const
|
||||
{ _GetTimespec(fFileEntry.file.AccessDate(),
|
||||
fFileEntry.file.AccessTime(), timespec); }
|
||||
|
||||
Volume* GetVolume() const { return fVolume; }
|
||||
|
||||
status_t FindBlock(off_t logical, off_t& physical,
|
||||
off_t *_length = NULL);
|
||||
status_t ReadAt(off_t pos, uint8 *buffer, size_t *length);
|
||||
status_t FillGapWithZeros(off_t start, off_t end);
|
||||
|
||||
void* FileCache() const { return fCache; }
|
||||
void* Map() const { return fMap; }
|
||||
|
||||
bool VisitFile(struct exfat_entry*);
|
||||
bool VisitFileInfo(struct exfat_entry*);
|
||||
|
||||
private:
|
||||
friend struct InodesInoTreeDefinition;
|
||||
friend struct InodesClusterTreeDefinition;
|
||||
|
||||
Inode(Volume* volume);
|
||||
Inode(const Inode&);
|
||||
Inode &operator=(const Inode&);
|
||||
// no implementation
|
||||
|
||||
void _GetTimespec(uint16 date, uint16 time,
|
||||
struct timespec ×pec) const;
|
||||
void _Init();
|
||||
|
||||
rw_lock fLock;
|
||||
::Volume* fVolume;
|
||||
ino_t fID;
|
||||
ino_t fParent;
|
||||
cluster_t fCluster;
|
||||
uint32 fOffset;
|
||||
uint32 fFlags;
|
||||
void* fCache;
|
||||
void* fMap;
|
||||
status_t fInitStatus;
|
||||
|
||||
SplayTreeLink<Inode> fInoTreeLink;
|
||||
Inode* fInoTreeNext;
|
||||
SplayTreeLink<Inode> fClusterTreeLink;
|
||||
Inode* fClusterTreeNext;
|
||||
|
||||
struct exfat_entry fFileEntry;
|
||||
struct exfat_entry fFileInfoEntry;
|
||||
};
|
||||
|
||||
|
||||
// The Vnode class provides a convenience layer upon get_vnode(), so that
|
||||
// you don't have to call put_vnode() anymore, which may make code more
|
||||
// readable in some cases
|
||||
|
||||
class Vnode {
|
||||
public:
|
||||
Vnode(Volume* volume, ino_t id)
|
||||
:
|
||||
fInode(NULL)
|
||||
{
|
||||
SetTo(volume, id);
|
||||
}
|
||||
|
||||
Vnode()
|
||||
:
|
||||
fStatus(B_NO_INIT),
|
||||
fInode(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
~Vnode()
|
||||
{
|
||||
Unset();
|
||||
}
|
||||
|
||||
status_t InitCheck()
|
||||
{
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
void Unset()
|
||||
{
|
||||
if (fInode != NULL) {
|
||||
put_vnode(fInode->GetVolume()->FSVolume(), fInode->ID());
|
||||
fInode = NULL;
|
||||
fStatus = B_NO_INIT;
|
||||
}
|
||||
}
|
||||
|
||||
status_t SetTo(Volume* volume, ino_t id)
|
||||
{
|
||||
Unset();
|
||||
|
||||
return fStatus = get_vnode(volume->FSVolume(), id, (void**)&fInode);
|
||||
}
|
||||
|
||||
status_t Get(Inode** _inode)
|
||||
{
|
||||
*_inode = fInode;
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
void Keep()
|
||||
{
|
||||
TRACEI("Vnode::Keep()\n");
|
||||
fInode = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
status_t fStatus;
|
||||
Inode* fInode;
|
||||
};
|
||||
|
||||
|
||||
struct InodesInoTreeDefinition {
|
||||
typedef ino_t KeyType;
|
||||
typedef Inode NodeType;
|
||||
|
||||
static KeyType GetKey(const NodeType* node)
|
||||
{
|
||||
return node->ID();
|
||||
}
|
||||
|
||||
static SplayTreeLink<NodeType>* GetLink(NodeType* node)
|
||||
{
|
||||
return &node->fInoTreeLink;
|
||||
}
|
||||
|
||||
static int Compare(KeyType key, const NodeType* node)
|
||||
{
|
||||
return key == node->ID() ? 0
|
||||
: (key < node->ID() ? -1 : 1);
|
||||
}
|
||||
|
||||
static NodeType** GetListLink(NodeType* node)
|
||||
{
|
||||
return &node->fInoTreeNext;
|
||||
}
|
||||
};
|
||||
|
||||
typedef IteratableSplayTree<InodesInoTreeDefinition> InodesInoTree;
|
||||
|
||||
struct InodesClusterTreeDefinition {
|
||||
typedef cluster_t KeyType;
|
||||
typedef Inode NodeType;
|
||||
|
||||
static KeyType GetKey(const NodeType* node)
|
||||
{
|
||||
return node->Cluster();
|
||||
}
|
||||
|
||||
static SplayTreeLink<NodeType>* GetLink(NodeType* node)
|
||||
{
|
||||
return &node->fClusterTreeLink;
|
||||
}
|
||||
|
||||
static int Compare(KeyType key, const NodeType* node)
|
||||
{
|
||||
return key == node->Cluster() ? 0
|
||||
: (key < node->Cluster() ? -1 : 1);
|
||||
}
|
||||
|
||||
static NodeType** GetListLink(NodeType* node)
|
||||
{
|
||||
return &node->fClusterTreeNext;
|
||||
}
|
||||
};
|
||||
|
||||
typedef IteratableSplayTree<InodesClusterTreeDefinition> InodesClusterTree;
|
||||
|
||||
#endif // INODE_H
|
||||
@@ -0,0 +1,14 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel file_systems exfat ;
|
||||
|
||||
UsePrivateHeaders [ FDirName kernel util ] ;
|
||||
UsePrivateHeaders shared storage ;
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
KernelAddon exfat :
|
||||
DataStream.cpp
|
||||
DirectoryIterator.cpp
|
||||
encodings.cpp
|
||||
Inode.cpp
|
||||
kernel_interface.cpp
|
||||
Volume.cpp
|
||||
;
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2001-2009, Axel Dörfler, [email protected].
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef UTILITY_H
|
||||
#define UTILITY_H
|
||||
|
||||
|
||||
#include "exfat.h"
|
||||
|
||||
|
||||
enum inode_type {
|
||||
S_DIRECTORY = S_IFDIR,
|
||||
S_FILE = S_IFREG,
|
||||
S_SYMLINK = S_IFLNK,
|
||||
|
||||
S_INDEX_TYPES = (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX
|
||||
| S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX
|
||||
| S_FLOAT_INDEX | S_DOUBLE_INDEX),
|
||||
|
||||
S_EXTENDED_TYPES = (S_ATTR_DIR | S_ATTR | S_INDEX_DIR)
|
||||
};
|
||||
|
||||
|
||||
/*! Converts the open mode, the open flags given to bfs_open(), into
|
||||
access modes, e.g. since O_RDONLY requires read access to the
|
||||
file, it will be converted to R_OK.
|
||||
*/
|
||||
inline int
|
||||
open_mode_to_access(int openMode)
|
||||
{
|
||||
openMode &= O_RWMASK;
|
||||
if (openMode == O_RDONLY)
|
||||
return R_OK;
|
||||
if (openMode == O_WRONLY)
|
||||
return W_OK;
|
||||
|
||||
return R_OK | W_OK;
|
||||
}
|
||||
|
||||
#endif // UTILITY_H
|
||||
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* Copyright 2011, Jérôme Duval, [email protected].
|
||||
* Copyright 2008-2010, Axel Dörfler, [email protected].
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
//! Super block, mounting, etc.
|
||||
|
||||
|
||||
#include "Volume.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <fs_cache.h>
|
||||
#include <fs_volume.h>
|
||||
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "CachedBlock.h"
|
||||
#include "Inode.h"
|
||||
|
||||
|
||||
//#define TRACE_EXFAT
|
||||
#ifdef TRACE_EXFAT
|
||||
# define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
|
||||
#else
|
||||
# define TRACE(x...) ;
|
||||
#endif
|
||||
# define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
|
||||
|
||||
|
||||
class DeviceOpener {
|
||||
public:
|
||||
DeviceOpener(int fd, int mode);
|
||||
DeviceOpener(const char* device, int mode);
|
||||
~DeviceOpener();
|
||||
|
||||
int Open(const char* device, int mode);
|
||||
int Open(int fd, int mode);
|
||||
void* InitCache(off_t numBlocks, uint32 blockSize);
|
||||
void RemoveCache(bool allowWrites);
|
||||
|
||||
void Keep();
|
||||
|
||||
int Device() const { return fDevice; }
|
||||
int Mode() const { return fMode; }
|
||||
bool IsReadOnly() const
|
||||
{ return _IsReadOnly(fMode); }
|
||||
|
||||
status_t GetSize(off_t* _size,
|
||||
uint32* _blockSize = NULL);
|
||||
|
||||
private:
|
||||
static bool _IsReadOnly(int mode)
|
||||
{ return (mode & O_RWMASK) == O_RDONLY;}
|
||||
static bool _IsReadWrite(int mode)
|
||||
{ return (mode & O_RWMASK) == O_RDWR;}
|
||||
|
||||
int fDevice;
|
||||
int fMode;
|
||||
void* fBlockCache;
|
||||
};
|
||||
|
||||
|
||||
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)) {
|
||||
// try again to open read-only (don't rely on a specific error code)
|
||||
return Open(device, O_RDONLY | O_NOCACHE);
|
||||
}
|
||||
|
||||
if (fDevice >= 0) {
|
||||
// opening succeeded
|
||||
fMode = mode;
|
||||
if (_IsReadWrite(mode)) {
|
||||
// check out if the device really allows for read/write access
|
||||
device_geometry geometry;
|
||||
if (!ioctl(fDevice, B_GET_GEOMETRY, &geometry)) {
|
||||
if (geometry.read_only) {
|
||||
// reopen device 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;
|
||||
}
|
||||
|
||||
|
||||
/*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY
|
||||
to compute the size, or fstat() if that failed.
|
||||
*/
|
||||
status_t
|
||||
DeviceOpener::GetSize(off_t* _size, uint32* _blockSize)
|
||||
{
|
||||
device_geometry geometry;
|
||||
if (ioctl(fDevice, B_GET_GEOMETRY, &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;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
bool
|
||||
exfat_super_block::IsValid()
|
||||
{
|
||||
// TODO: check some more values!
|
||||
if (strncmp(filesystem, EXFAT_SUPER_BLOCK_MAGIC, sizeof(filesystem)) != 0)
|
||||
return false;
|
||||
if (signature != 0xaa55)
|
||||
return false;
|
||||
if (jump_boot[0] != 0xeb || jump_boot[1] != 0x76 || jump_boot[2] != 0x90)
|
||||
return false;
|
||||
if (version_minor != 0 || version_major != 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
Volume::Volume(fs_volume* volume)
|
||||
:
|
||||
fFSVolume(volume),
|
||||
fFlags(0),
|
||||
fRootNode(NULL),
|
||||
fNextId(1)
|
||||
{
|
||||
mutex_init(&fLock, "exfat volume");
|
||||
fInodesClusterTree = new InodesClusterTree;
|
||||
fInodesInoTree = new InodesInoTree;
|
||||
}
|
||||
|
||||
|
||||
Volume::~Volume()
|
||||
{
|
||||
TRACE("Volume destructor.\n");
|
||||
delete fInodesClusterTree;
|
||||
delete fInodesInoTree;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Volume::IsValidSuperBlock()
|
||||
{
|
||||
return fSuperBlock.IsValid();
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
Volume::Name() const
|
||||
{
|
||||
/* TODO volume name is in the root directory */
|
||||
return fName;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Volume::Mount(const char* deviceName, uint32 flags)
|
||||
{
|
||||
flags |= B_MOUNT_READ_ONLY;
|
||||
// we only support read-only for now
|
||||
|
||||
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 super block
|
||||
status_t status = Identify(fDevice, &fSuperBlock);
|
||||
if (status != B_OK) {
|
||||
ERROR("Volume::Mount(): Identify() failed\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
fBlockSize = 1 << fSuperBlock.BlockShift();
|
||||
TRACE("block size %ld\n", fBlockSize);
|
||||
fEntriesPerBlock = (fBlockSize / sizeof(struct exfat_entry));
|
||||
|
||||
// check if the device size is large enough to hold the file system
|
||||
off_t diskSize;
|
||||
status = opener.GetSize(&diskSize);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
if (diskSize < (off_t)fSuperBlock.NumBlocks() << fSuperBlock.BlockShift())
|
||||
return B_BAD_VALUE;
|
||||
|
||||
fBlockCache = opener.InitCache(fSuperBlock.NumBlocks(), fBlockSize);
|
||||
if (fBlockCache == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
|
||||
|
||||
ino_t rootIno;
|
||||
// ready
|
||||
{
|
||||
Inode rootNode(this, fSuperBlock.RootDirCluster(), 0);
|
||||
rootIno = rootNode.ID();
|
||||
}
|
||||
|
||||
status = get_vnode(fFSVolume, rootIno, (void**)&fRootNode);
|
||||
if (status != B_OK) {
|
||||
ERROR("could not create root node: get_vnode() failed!\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
TRACE("Volume::Mount(): Found root node: %lld (%s)\n", fRootNode->ID(),
|
||||
strerror(fRootNode->InitCheck()));
|
||||
|
||||
// all went fine
|
||||
opener.Keep();
|
||||
|
||||
/*if (!fSuperBlock.label[0]) {*/
|
||||
// generate a more or less descriptive volume name
|
||||
off_t divisor = 1ULL << 40;
|
||||
char unit = 'T';
|
||||
if (diskSize < divisor) {
|
||||
divisor = 1UL << 30;
|
||||
unit = 'G';
|
||||
if (diskSize < divisor) {
|
||||
divisor = 1UL << 20;
|
||||
unit = 'M';
|
||||
}
|
||||
}
|
||||
|
||||
double size = double((10 * diskSize + divisor - 1) / divisor);
|
||||
// %g in the kernel does not support precision...
|
||||
|
||||
snprintf(fName, sizeof(fName), "%g %cB ExFAT Volume",
|
||||
size / 10, unit);
|
||||
//}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Volume::Unmount()
|
||||
{
|
||||
TRACE("Volume::Unmount()\n");
|
||||
|
||||
TRACE("Volume::Unmount(): Putting root node\n");
|
||||
put_vnode(fFSVolume, RootNode()->ID());
|
||||
TRACE("Volume::Unmount(): Deleting the block cache\n");
|
||||
block_cache_delete(fBlockCache, !IsReadOnly());
|
||||
TRACE("Volume::Unmount(): Closing device\n");
|
||||
close(fDevice);
|
||||
|
||||
TRACE("Volume::Unmount(): Done\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Volume::LoadSuperBlock()
|
||||
{
|
||||
CachedBlock cached(this);
|
||||
const uint8* block = cached.SetTo(EXFAT_SUPER_BLOCK_OFFSET / fBlockSize);
|
||||
|
||||
if (block == NULL)
|
||||
return B_IO_ERROR;
|
||||
|
||||
memcpy(&fSuperBlock, block + EXFAT_SUPER_BLOCK_OFFSET % fBlockSize,
|
||||
sizeof(fSuperBlock));
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Volume::ClusterToBlock(cluster_t cluster, fsblock_t &block)
|
||||
{
|
||||
block = ((cluster - 2) << SuperBlock().BlocksPerClusterShift())
|
||||
+ SuperBlock().FirstDataBlock();
|
||||
TRACE("Volume::ClusterToBlock() cluster %lu %u %lu: %llu, %lu\n", cluster,
|
||||
SuperBlock().BlocksPerClusterShift(), SuperBlock().FirstDataBlock(),
|
||||
block, SuperBlock().FirstFatBlock());
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
cluster_t
|
||||
Volume::NextCluster(cluster_t _cluster)
|
||||
{
|
||||
uint32 clusterPerBlock = fBlockSize / sizeof(cluster_t);
|
||||
CachedBlock block(this);
|
||||
fsblock_t blockNum = SuperBlock().FirstFatBlock()
|
||||
+ _cluster / clusterPerBlock;
|
||||
cluster_t *cluster = (cluster_t *)block.SetTo(blockNum);
|
||||
cluster += _cluster % clusterPerBlock;
|
||||
TRACE("Volume::NextCluster() cluster %lu next %lu\n", _cluster, *cluster);
|
||||
return *cluster;
|
||||
}
|
||||
|
||||
|
||||
Inode*
|
||||
Volume::FindInode(ino_t id)
|
||||
{
|
||||
return fInodesInoTree->Lookup(id);
|
||||
}
|
||||
|
||||
|
||||
Inode*
|
||||
Volume::FindInode(cluster_t cluster)
|
||||
{
|
||||
return fInodesClusterTree->Lookup(cluster);
|
||||
}
|
||||
|
||||
|
||||
ino_t
|
||||
Volume::GetIno(cluster_t cluster, uint32 offset, ino_t parent)
|
||||
{
|
||||
struct node_key key;
|
||||
key.cluster = cluster;
|
||||
key.offset = offset;
|
||||
struct node* node = fNodeTree.Lookup(key);
|
||||
if (node != NULL) {
|
||||
TRACE("Volume::GetIno() cached cluster %lu offset %lu ino %" B_PRIdINO
|
||||
"\n", cluster, offset, node->ino);
|
||||
return node->ino;
|
||||
}
|
||||
node = new struct node();
|
||||
node->key = key;
|
||||
node->ino = _NextID();
|
||||
node->parent = parent;
|
||||
fNodeTree.Insert(node);
|
||||
fInoTree.Insert(node);
|
||||
TRACE("Volume::GetIno() new cluster %lu offset %lu ino %" B_PRIdINO "\n",
|
||||
cluster, offset, node->ino);
|
||||
return node->ino;
|
||||
}
|
||||
|
||||
|
||||
struct node_key*
|
||||
Volume::GetNode(ino_t ino, ino_t &parent)
|
||||
{
|
||||
struct node* node = fInoTree.Lookup(ino);
|
||||
if (node != NULL) {
|
||||
parent = node->parent;
|
||||
return &node->key;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Disk scanning and initialization
|
||||
|
||||
|
||||
/*static*/ status_t
|
||||
Volume::Identify(int fd, exfat_super_block* superBlock)
|
||||
{
|
||||
if (read_pos(fd, EXFAT_SUPER_BLOCK_OFFSET, superBlock,
|
||||
sizeof(exfat_super_block)) != sizeof(exfat_super_block))
|
||||
return B_IO_ERROR;
|
||||
|
||||
if (!superBlock->IsValid()) {
|
||||
ERROR("invalid super block!\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2011, Jérôme Duval, [email protected].
|
||||
* Copyright 2008-2010, Axel Dörfler, [email protected].
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef VOLUME_H
|
||||
#define VOLUME_H
|
||||
|
||||
|
||||
#include <lock.h>
|
||||
|
||||
#include "exfat.h"
|
||||
#include "SplayTree.h"
|
||||
|
||||
|
||||
struct node_key {
|
||||
cluster_t cluster;
|
||||
uint32 offset;
|
||||
};
|
||||
|
||||
struct node {
|
||||
struct node_key key;
|
||||
ino_t ino;
|
||||
ino_t parent;
|
||||
SplayTreeLink<struct node> nodeTreeLink;
|
||||
SplayTreeLink<struct node> inoTreeLink;
|
||||
};
|
||||
|
||||
|
||||
struct NodeTreeDefinition {
|
||||
typedef struct node_key KeyType;
|
||||
typedef struct node NodeType;
|
||||
|
||||
static KeyType GetKey(const NodeType* node)
|
||||
{
|
||||
return node->key;
|
||||
}
|
||||
|
||||
static SplayTreeLink<NodeType>* GetLink(NodeType* node)
|
||||
{
|
||||
return &node->nodeTreeLink;
|
||||
}
|
||||
|
||||
static int Compare(KeyType key, const NodeType* node)
|
||||
{
|
||||
if (key.cluster == node->key.cluster) {
|
||||
if (key.offset == node->key.offset)
|
||||
return 0;
|
||||
return key.offset < node->key.offset ? -1 : 1;
|
||||
}
|
||||
return key.cluster < node->key.cluster ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
struct InoTreeDefinition {
|
||||
typedef ino_t KeyType;
|
||||
typedef struct node NodeType;
|
||||
|
||||
static KeyType GetKey(const NodeType* node)
|
||||
{
|
||||
return node->ino;
|
||||
}
|
||||
|
||||
static SplayTreeLink<NodeType>* GetLink(NodeType* node)
|
||||
{
|
||||
return &node->inoTreeLink;
|
||||
}
|
||||
|
||||
static int Compare(KeyType key, const NodeType* node)
|
||||
{
|
||||
if (key != node->ino)
|
||||
return key < node->ino ? -1 : 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef SplayTree<NodeTreeDefinition> NodeTree;
|
||||
typedef SplayTree<InoTreeDefinition> InoTree;
|
||||
class Inode;
|
||||
struct InodesInoTreeDefinition;
|
||||
typedef IteratableSplayTree<InodesInoTreeDefinition> InodesInoTree;
|
||||
struct InodesClusterTreeDefinition;
|
||||
typedef IteratableSplayTree<InodesClusterTreeDefinition> InodesClusterTree;
|
||||
|
||||
|
||||
enum volume_flags {
|
||||
VOLUME_READ_ONLY = 0x0001
|
||||
};
|
||||
|
||||
|
||||
class Volume {
|
||||
public:
|
||||
Volume(fs_volume* volume);
|
||||
~Volume();
|
||||
|
||||
status_t Mount(const char* device, uint32 flags);
|
||||
status_t Unmount();
|
||||
|
||||
bool IsValidSuperBlock();
|
||||
bool IsReadOnly() const
|
||||
{ return (fFlags & VOLUME_READ_ONLY) != 0; }
|
||||
|
||||
Inode* RootNode() const { return fRootNode; }
|
||||
int Device() const { return fDevice; }
|
||||
|
||||
dev_t ID() const
|
||||
{ return fFSVolume ? fFSVolume->id : -1; }
|
||||
fs_volume* FSVolume() const { return fFSVolume; }
|
||||
const char* Name() const;
|
||||
|
||||
uint32 BlockSize() const { return fBlockSize; }
|
||||
uint32 EntriesPerBlock() const
|
||||
{ return fEntriesPerBlock; }
|
||||
uint32 EntriesPerCluster()
|
||||
{ return fEntriesPerBlock
|
||||
<< SuperBlock().BlocksPerClusterShift(); }
|
||||
size_t ClusterSize() { return fBlockSize
|
||||
<< SuperBlock().BlocksPerClusterShift(); }
|
||||
exfat_super_block& SuperBlock() { return fSuperBlock; }
|
||||
|
||||
status_t LoadSuperBlock();
|
||||
|
||||
// cache access
|
||||
void* BlockCache() { return fBlockCache; }
|
||||
|
||||
static status_t Identify(int fd, exfat_super_block* superBlock);
|
||||
|
||||
status_t ClusterToBlock(cluster_t cluster,
|
||||
fsblock_t &block);
|
||||
Inode * FindInode(ino_t id);
|
||||
Inode * FindInode(cluster_t cluster);
|
||||
cluster_t NextCluster(cluster_t cluster);
|
||||
ino_t GetIno(cluster_t cluster, uint32 offset, ino_t parent);
|
||||
struct node_key* GetNode(ino_t ino, ino_t &parent);
|
||||
private:
|
||||
ino_t _NextID() { return fNextId++; }
|
||||
|
||||
mutex fLock;
|
||||
fs_volume* fFSVolume;
|
||||
int fDevice;
|
||||
exfat_super_block fSuperBlock;
|
||||
char fName[32];
|
||||
|
||||
uint16 fFlags;
|
||||
uint32 fBlockSize;
|
||||
uint32 fEntriesPerBlock;
|
||||
Inode* fRootNode;
|
||||
ino_t fNextId;
|
||||
|
||||
void* fBlockCache;
|
||||
InodesInoTree* fInodesInoTree;
|
||||
InodesClusterTree* fInodesClusterTree;
|
||||
NodeTree fNodeTree;
|
||||
InoTree fInoTree;
|
||||
};
|
||||
|
||||
#endif // VOLUME_H
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
|
||||
|
||||
#include <ByteOrder.h>
|
||||
#include <ctype.h>
|
||||
#include <KernelExport.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "encodings.h"
|
||||
|
||||
|
||||
// Pierre's Uber Macro
|
||||
#define u_lendian_to_utf8(str, uni_str)\
|
||||
{\
|
||||
if ((B_LENDIAN_TO_HOST_INT16(uni_str[0])&0xff80) == 0)\
|
||||
*str++ = B_LENDIAN_TO_HOST_INT16(*uni_str++);\
|
||||
else if ((B_LENDIAN_TO_HOST_INT16(uni_str[0])&0xf800) == 0) {\
|
||||
str[0] = 0xc0|(B_LENDIAN_TO_HOST_INT16(uni_str[0])>>6);\
|
||||
str[1] = 0x80|(B_LENDIAN_TO_HOST_INT16(*uni_str++)&0x3f);\
|
||||
str += 2;\
|
||||
} else if ((B_LENDIAN_TO_HOST_INT16(uni_str[0])&0xfc00) != 0xd800) {\
|
||||
str[0] = 0xe0|(B_LENDIAN_TO_HOST_INT16(uni_str[0])>>12);\
|
||||
str[1] = 0x80|((B_LENDIAN_TO_HOST_INT16(uni_str[0])>>6)&0x3f);\
|
||||
str[2] = 0x80|(B_LENDIAN_TO_HOST_INT16(*uni_str++)&0x3f);\
|
||||
str += 3;\
|
||||
} else {\
|
||||
int val;\
|
||||
val = ((B_LENDIAN_TO_HOST_INT16(uni_str[0])-0xd7c0)<<10) | (B_LENDIAN_TO_HOST_INT16(uni_str[1])&0x3ff);\
|
||||
str[0] = 0xf0 | (val>>18);\
|
||||
str[1] = 0x80 | ((val>>12)&0x3f);\
|
||||
str[2] = 0x80 | ((val>>6)&0x3f);\
|
||||
str[3] = 0x80 | (val&0x3f);\
|
||||
uni_str += 2; str += 4;\
|
||||
}\
|
||||
}
|
||||
|
||||
// Pierre's Uber Macro
|
||||
#define u_hostendian_to_utf8(str, uni_str)\
|
||||
{\
|
||||
if ((uni_str[0]&0xff80) == 0)\
|
||||
*str++ = *uni_str++;\
|
||||
else if ((uni_str[0]&0xf800) == 0) {\
|
||||
str[0] = 0xc0|(uni_str[0]>>6);\
|
||||
str[1] = 0x80|(*uni_str++&0x3f);\
|
||||
str += 2;\
|
||||
} else if ((uni_str[0]&0xfc00) != 0xd800) {\
|
||||
str[0] = 0xe0|(uni_str[0]>>12);\
|
||||
str[1] = 0x80|((uni_str[0]>>6)&0x3f);\
|
||||
str[2] = 0x80|(*uni_str++&0x3f);\
|
||||
str += 3;\
|
||||
} else {\
|
||||
int val;\
|
||||
val = ((uni_str[0]-0xd7c0)<<10) | (uni_str[1]&0x3ff);\
|
||||
str[0] = 0xf0 | (val>>18);\
|
||||
str[1] = 0x80 | ((val>>12)&0x3f);\
|
||||
str[2] = 0x80 | ((val>>6)&0x3f);\
|
||||
str[3] = 0x80 | (val&0x3f);\
|
||||
uni_str += 2; str += 4;\
|
||||
}\
|
||||
}
|
||||
|
||||
// Another Uber Macro
|
||||
#define utf8_to_u_hostendian(str, uni_str, err_flag) \
|
||||
{\
|
||||
err_flag = 0;\
|
||||
if ((str[0]&0x80) == 0)\
|
||||
*uni_str++ = *str++;\
|
||||
else if ((str[1] & 0xC0) != 0x80) {\
|
||||
*uni_str++ = 0xfffd;\
|
||||
str+=1;\
|
||||
} else if ((str[0]&0x20) == 0) {\
|
||||
*uni_str++ = ((str[0]&31)<<6) | (str[1]&63);\
|
||||
str+=2;\
|
||||
} else if ((str[2] & 0xC0) != 0x80) {\
|
||||
*uni_str++ = 0xfffd;\
|
||||
str+=2;\
|
||||
} else if ((str[0]&0x10) == 0) {\
|
||||
*uni_str++ = ((str[0]&15)<<12) | ((str[1]&63)<<6) | (str[2]&63);\
|
||||
str+=3;\
|
||||
} else if ((str[3] & 0xC0) != 0x80) {\
|
||||
*uni_str++ = 0xfffd;\
|
||||
str+=3;\
|
||||
} else {\
|
||||
err_flag = 1;\
|
||||
}\
|
||||
}
|
||||
|
||||
// Count the number of bytes of a UTF-8 character
|
||||
#define utf8_char_len(c) ((((int32)0xE5000000 >> ((c >> 3) & 0x1E)) & 3) + 1)
|
||||
|
||||
// converts LENDIAN unicode to utf8
|
||||
static status_t
|
||||
_lendian_unicode_to_utf8(
|
||||
const char *src,
|
||||
int32 *srcLen,
|
||||
char *dst,
|
||||
uint32 *dstLen)
|
||||
{
|
||||
int32 srcLimit = *srcLen;
|
||||
int32 dstLimit = *dstLen;
|
||||
int32 srcCount = 0;
|
||||
int32 dstCount = 0;
|
||||
|
||||
for (srcCount = 0; srcCount < srcLimit; srcCount += 2) {
|
||||
uint16 *UNICODE = (uint16 *)&src[srcCount];
|
||||
if (*UNICODE == 0)
|
||||
break;
|
||||
uchar utf8[4];
|
||||
uchar *UTF8 = utf8;
|
||||
int32 utf8Len;
|
||||
int32 j;
|
||||
|
||||
u_lendian_to_utf8(UTF8, UNICODE);
|
||||
|
||||
utf8Len = UTF8 - utf8;
|
||||
if ((dstCount + utf8Len) > dstLimit)
|
||||
break;
|
||||
|
||||
for (j = 0; j < utf8Len; j++)
|
||||
dst[dstCount + j] = utf8[j];
|
||||
dstCount += utf8Len;
|
||||
}
|
||||
|
||||
*srcLen = srcCount;
|
||||
*dstLen = dstCount;
|
||||
dst[dstCount] = '\0';
|
||||
|
||||
return ((dstCount > 0) ? B_NO_ERROR : B_ERROR);
|
||||
}
|
||||
|
||||
// utf8 to LENDIAN unicode
|
||||
static status_t
|
||||
_utf8_to_lendian_unicode(
|
||||
const char *src,
|
||||
int32 *srcLen,
|
||||
char *dst,
|
||||
uint32 *dstLen)
|
||||
{
|
||||
int32 srcLimit = *srcLen;
|
||||
int32 dstLimit = *dstLen - 1;
|
||||
int32 srcCount = 0;
|
||||
int32 dstCount = 0;
|
||||
|
||||
while ((srcCount < srcLimit) && (dstCount < dstLimit)) {
|
||||
uint16 unicode;
|
||||
uint16 *UNICODE = &unicode;
|
||||
uchar *UTF8 = (uchar *)src + srcCount;
|
||||
int err_flag;
|
||||
|
||||
if ((srcCount + utf8_char_len(src[srcCount])) > srcLimit)
|
||||
break;
|
||||
|
||||
utf8_to_u_hostendian(UTF8, UNICODE, err_flag);
|
||||
if(err_flag == 1)
|
||||
return EINVAL;
|
||||
|
||||
unicode = B_HOST_TO_LENDIAN_INT16(unicode);
|
||||
dst[dstCount++] = unicode & 0xFF;
|
||||
dst[dstCount++] = unicode >> 8;
|
||||
|
||||
srcCount += UTF8 - ((uchar *)(src + srcCount));
|
||||
}
|
||||
|
||||
*srcLen = srcCount;
|
||||
*dstLen = dstCount;
|
||||
|
||||
return ((dstCount > 0) ? B_NO_ERROR : B_ERROR);
|
||||
}
|
||||
|
||||
|
||||
// takes a unicode name of unilen uchar's and converts to a utf8 name of at
|
||||
// most utf8len uint8's
|
||||
status_t unicode_to_utf8(const uchar *uni, uint32 unilen, uint8 *utf8,
|
||||
uint32 *utf8len)
|
||||
{
|
||||
uint32 origlen = unilen;
|
||||
status_t result = _lendian_unicode_to_utf8((char *)uni,
|
||||
(int32 *)&unilen, (char *)utf8, utf8len);
|
||||
|
||||
/*if (unilen < origlen) {
|
||||
panic("Name is too long (%lx < %lx)\n", unilen, origlen);
|
||||
return B_ERROR;
|
||||
}*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t utf8_to_unicode(const char *utf8, uchar *uni, uint32 *unilen)
|
||||
{
|
||||
uint32 origlen = strlen(utf8) + 1;
|
||||
uint32 utf8len = origlen;
|
||||
|
||||
status_t result = _utf8_to_lendian_unicode(utf8,
|
||||
(int32 *)&utf8len, (char *)uni, unilen);
|
||||
|
||||
/*if (origlen < utf8len) {
|
||||
panic("Name is too long (%lx < %lx)\n", *unilen, origlen);
|
||||
return B_ERROR;
|
||||
}*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright 1999-2001, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
#ifndef _ENCODINGS_H_
|
||||
#define _ENCODINGS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
status_t unicode_to_utf8(const uchar *uni, uint32 unilen, uint8 *utf8,
|
||||
uint32 *utf8len);
|
||||
status_t utf8_to_unicode(const char *utf8, uchar *uni, uint32 *unilen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _ENCODINGS_H_
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2011, Jérôme Duval, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef EXFAT_H
|
||||
#define EXFAT_H
|
||||
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <ByteOrder.h>
|
||||
#include <fs_interface.h>
|
||||
#include <KernelExport.h>
|
||||
|
||||
|
||||
typedef uint64 fileblock_t; // file block number
|
||||
typedef uint64 fsblock_t; // filesystem block number
|
||||
|
||||
typedef uint32 cluster_t;
|
||||
|
||||
#define EXFAT_SUPER_BLOCK_OFFSET 0x0
|
||||
|
||||
|
||||
struct exfat_super_block {
|
||||
uint8 jump_boot[3];
|
||||
char filesystem[8];
|
||||
uint8 reserved[53];
|
||||
uint64 first_block;
|
||||
uint64 num_blocks;
|
||||
uint32 first_fat_block;
|
||||
uint32 fat_length;
|
||||
uint32 first_data_block;
|
||||
uint32 cluster_count;
|
||||
uint32 root_dir_cluster;
|
||||
uint32 serial_number;
|
||||
uint8 version_minor;
|
||||
uint8 version_major;
|
||||
uint16 flags;
|
||||
uint8 block_shift;
|
||||
uint8 blocks_per_cluster_shift;
|
||||
uint8 fat_count;
|
||||
uint8 drive_select;
|
||||
uint8 used_percent;
|
||||
uint8 reserved2[7];
|
||||
uint8 boot_code[390];
|
||||
uint16 signature;
|
||||
|
||||
bool IsValid();
|
||||
// implemented in Volume.cpp
|
||||
uint64 FirstBlock() const { return B_LENDIAN_TO_HOST_INT64(first_block); }
|
||||
uint64 NumBlocks() const { return B_LENDIAN_TO_HOST_INT32(num_blocks); }
|
||||
uint32 FirstFatBlock() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(first_fat_block); }
|
||||
uint32 FatLength() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(fat_length); }
|
||||
uint32 FirstDataBlock() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(first_data_block); }
|
||||
uint32 ClusterCount() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(cluster_count); }
|
||||
uint32 RootDirCluster() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(root_dir_cluster); }
|
||||
uint32 SerialNumber() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(serial_number); }
|
||||
uint8 VersionMinor() const { return version_minor; }
|
||||
uint8 VersionMajor() const { return version_major; }
|
||||
uint16 Flags() const { return B_LENDIAN_TO_HOST_INT16(flags); }
|
||||
uint8 BlockShift() const { return block_shift; }
|
||||
uint8 BlocksPerClusterShift() const { return blocks_per_cluster_shift; }
|
||||
uint8 FatCount() const { return fat_count; }
|
||||
uint8 DriveSelect() const { return drive_select; }
|
||||
uint8 UsedPercent() const { return used_percent; }
|
||||
} _PACKED;
|
||||
|
||||
|
||||
#define EXFAT_SUPER_BLOCK_MAGIC "EXFAT "
|
||||
|
||||
#define EXFAT_ENTRY_TYPE_BITMAP 0x81
|
||||
#define EXFAT_ENTRY_TYPE_UPPERCASE 0x82
|
||||
#define EXFAT_ENTRY_TYPE_LABEL 0x83
|
||||
#define EXFAT_ENTRY_TYPE_FILE 0x85
|
||||
#define EXFAT_ENTRY_TYPE_FILEINFO 0xc0
|
||||
#define EXFAT_ENTRY_TYPE_FILENAME 0xc1
|
||||
#define EXFAT_CLUSTER_END 0xffffffff
|
||||
#define EXFAT_ENTRY_ATTRIB_SUBDIR 0x10
|
||||
|
||||
#define EXFAT_ENTRY_FLAG_CONTIGUOUS 0x3
|
||||
|
||||
#define EXFAT_FILENAME_MAX_LENGTH 512
|
||||
|
||||
struct exfat_entry {
|
||||
uint8 type;
|
||||
union {
|
||||
struct {
|
||||
uint8 length;
|
||||
char name[30];
|
||||
} _PACKED name_label;
|
||||
struct {
|
||||
uint8 reserved[3];
|
||||
uint32 checksum;
|
||||
uint8 reserved2[12];
|
||||
uint32 start_cluster;
|
||||
uint64 size;
|
||||
} _PACKED bitmap_uppercase;
|
||||
struct {
|
||||
uint8 chunkCount;
|
||||
uint16 checksum;
|
||||
uint16 attribs;
|
||||
uint16 reserved;
|
||||
uint16 creation_time;
|
||||
uint16 creation_date;
|
||||
uint16 modification_time;
|
||||
uint16 modification_date;
|
||||
uint16 access_time;
|
||||
uint16 access_date;
|
||||
uint8 creation_time_low;
|
||||
uint8 modification_time_low;
|
||||
uint8 reserved2[10];
|
||||
uint16 ModificationTime() const
|
||||
{ return B_LENDIAN_TO_HOST_INT16(modification_time); }
|
||||
uint16 ModificationDate() const
|
||||
{ return B_LENDIAN_TO_HOST_INT16(modification_date); }
|
||||
uint16 AccessTime() const
|
||||
{ return B_LENDIAN_TO_HOST_INT16(access_time); }
|
||||
uint16 AccessDate() const
|
||||
{ return B_LENDIAN_TO_HOST_INT16(access_date); }
|
||||
uint16 CreationTime() const
|
||||
{ return B_LENDIAN_TO_HOST_INT16(creation_time); }
|
||||
uint16 CreationDate() const
|
||||
{ return B_LENDIAN_TO_HOST_INT16(creation_date); }
|
||||
uint16 Attribs() const
|
||||
{ return B_LENDIAN_TO_HOST_INT16(attribs); }
|
||||
void SetAttribs(uint16 newAttribs)
|
||||
{ attribs = B_HOST_TO_LENDIAN_INT16(newAttribs); }
|
||||
} _PACKED file;
|
||||
struct {
|
||||
uint8 flag;
|
||||
uint8 reserved;
|
||||
uint8 name_length;
|
||||
uint16 name_hash;
|
||||
uint8 reserved2[2];
|
||||
uint64 size1;
|
||||
uint8 reserved3[4];
|
||||
uint32 start_cluster;
|
||||
uint64 size2;
|
||||
uint32 StartCluster() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(start_cluster); }
|
||||
void SetStartCluster(uint32 startCluster)
|
||||
{ start_cluster = B_HOST_TO_LENDIAN_INT32(startCluster); }
|
||||
bool IsContiguous() const
|
||||
{ return (flag & EXFAT_ENTRY_FLAG_CONTIGUOUS) != 0; }
|
||||
void SetFlag(uint8 newFlag)
|
||||
{ flag = newFlag; }
|
||||
uint64 Size() const
|
||||
{ return B_LENDIAN_TO_HOST_INT64(size1); }
|
||||
} _PACKED file_info;
|
||||
};
|
||||
} _PACKED;
|
||||
|
||||
|
||||
struct file_cookie {
|
||||
bigtime_t last_notification;
|
||||
off_t last_size;
|
||||
int open_mode;
|
||||
};
|
||||
|
||||
#define EXFAT_OPEN_MODE_USER_MASK 0x7fffffff
|
||||
|
||||
extern fs_volume_ops gExfatVolumeOps;
|
||||
extern fs_vnode_ops gExfatVnodeOps;
|
||||
|
||||
#endif // EXFAT_H
|
||||
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
* Copyright 2011, Jérôme Duval, [email protected].
|
||||
* Copyright 2008, Axel Dörfler, [email protected].
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <dirent.h>
|
||||
#include <util/kernel_cpp.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <fs_cache.h>
|
||||
#include <fs_info.h>
|
||||
#include <io_requests.h>
|
||||
#include <NodeMonitor.h>
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "DirectoryIterator.h"
|
||||
#include "exfat.h"
|
||||
#include "Inode.h"
|
||||
#include "Utility.h"
|
||||
|
||||
|
||||
//#define TRACE_EXFAT
|
||||
#ifdef TRACE_EXFAT
|
||||
# define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x)
|
||||
#else
|
||||
# define TRACE(x...) ;
|
||||
#endif
|
||||
#define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x)
|
||||
|
||||
|
||||
#define EXFAT_IO_SIZE 65536
|
||||
|
||||
|
||||
struct identify_cookie {
|
||||
exfat_super_block super_block;
|
||||
};
|
||||
|
||||
|
||||
//! exfat_io() callback hook
|
||||
static status_t
|
||||
iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
|
||||
size_t size, struct file_io_vec* vecs, size_t* _count)
|
||||
{
|
||||
Inode* inode = (Inode*)cookie;
|
||||
|
||||
return file_map_translate(inode->Map(), offset, size, vecs, _count,
|
||||
inode->GetVolume()->BlockSize());
|
||||
}
|
||||
|
||||
|
||||
//! exfat_io() callback hook
|
||||
static status_t
|
||||
iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
|
||||
bool partialTransfer, size_t bytesTransferred)
|
||||
{
|
||||
Inode* inode = (Inode*)cookie;
|
||||
rw_lock_read_unlock(inode->Lock());
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Scanning
|
||||
|
||||
|
||||
static float
|
||||
exfat_identify_partition(int fd, partition_data *partition, void **_cookie)
|
||||
{
|
||||
exfat_super_block superBlock;
|
||||
status_t status = Volume::Identify(fd, &superBlock);
|
||||
if (status != B_OK)
|
||||
return -1;
|
||||
|
||||
identify_cookie *cookie = new identify_cookie;
|
||||
memcpy(&cookie->super_block, &superBlock, sizeof(exfat_super_block));
|
||||
|
||||
*_cookie = cookie;
|
||||
return 0.8f;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_scan_partition(int fd, partition_data *partition, void *_cookie)
|
||||
{
|
||||
identify_cookie *cookie = (identify_cookie *)_cookie;
|
||||
|
||||
partition->status = B_PARTITION_VALID;
|
||||
partition->flags |= B_PARTITION_FILE_SYSTEM;
|
||||
partition->content_size = cookie->super_block.NumBlocks()
|
||||
<< cookie->super_block.BlockShift();
|
||||
partition->block_size = 1 << cookie->super_block.BlockShift();
|
||||
// TODO volume name isn't in the superblock
|
||||
partition->content_name = strdup(cookie->super_block.filesystem);
|
||||
if (partition->content_name == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
exfat_free_identify_partition_cookie(partition_data* partition, void* _cookie)
|
||||
{
|
||||
delete (identify_cookie*)_cookie;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_mount(fs_volume* _volume, const char* device, uint32 flags,
|
||||
const char* args, ino_t* _rootID)
|
||||
{
|
||||
Volume* volume = new(std::nothrow) Volume(_volume);
|
||||
if (volume == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
// TODO: this is a bit hacky: we can't use publish_vnode() to publish
|
||||
// the root node, or else its file cache cannot be created (we could
|
||||
// create it later, though). Therefore we're using get_vnode() in Mount(),
|
||||
// but that requires us to export our volume data before calling it.
|
||||
_volume->private_volume = volume;
|
||||
_volume->ops = &gExfatVolumeOps;
|
||||
|
||||
status_t status = volume->Mount(device, flags);
|
||||
if (status != B_OK) {
|
||||
ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
|
||||
delete volume;
|
||||
return status;
|
||||
}
|
||||
|
||||
*_rootID = volume->RootNode()->ID();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_unmount(fs_volume *_volume)
|
||||
{
|
||||
Volume* volume = (Volume *)_volume->private_volume;
|
||||
|
||||
status_t status = volume->Unmount();
|
||||
delete volume;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_read_fs_info(fs_volume* _volume, struct fs_info* info)
|
||||
{
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
|
||||
// File system flags
|
||||
info->flags = B_FS_IS_PERSISTENT
|
||||
| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
|
||||
info->io_size = EXFAT_IO_SIZE;
|
||||
info->block_size = volume->BlockSize();
|
||||
info->total_blocks = volume->SuperBlock().NumBlocks();
|
||||
info->free_blocks = 0; //volume->NumFreeBlocks();
|
||||
|
||||
// Volume name
|
||||
strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
|
||||
|
||||
// File system name
|
||||
strlcpy(info->fsh_name, "exfat", sizeof(info->fsh_name));
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
|
||||
uint32* _flags, bool reenter)
|
||||
{
|
||||
TRACE("get_vnode %lu\n", id);
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
|
||||
Inode* inode = new(std::nothrow) Inode(volume, id);
|
||||
if (inode == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t status = inode->InitCheck();
|
||||
if (status != B_OK)
|
||||
delete inode;
|
||||
|
||||
if (status == B_OK) {
|
||||
_node->private_node = inode;
|
||||
_node->ops = &gExfatVnodeOps;
|
||||
*_type = inode->Mode();
|
||||
*_flags = 0;
|
||||
} else
|
||||
ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
|
||||
{
|
||||
delete (Inode*)_node->private_node;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
exfat_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
|
||||
off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
|
||||
{
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
|
||||
if (inode->FileCache() == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
rw_lock_read_lock(inode->Lock());
|
||||
|
||||
uint32 vecIndex = 0;
|
||||
size_t vecOffset = 0;
|
||||
size_t bytesLeft = *_numBytes;
|
||||
status_t status;
|
||||
|
||||
while (true) {
|
||||
file_io_vec fileVecs[8];
|
||||
uint32 fileVecCount = 8;
|
||||
|
||||
status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
|
||||
&fileVecCount, 0);
|
||||
if (status != B_OK && status != B_BUFFER_OVERFLOW)
|
||||
break;
|
||||
|
||||
bool bufferOverflow = status == B_BUFFER_OVERFLOW;
|
||||
|
||||
size_t bytes = bytesLeft;
|
||||
status = read_file_io_vec_pages(volume->Device(), fileVecs,
|
||||
fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
|
||||
if (status != B_OK || !bufferOverflow)
|
||||
break;
|
||||
|
||||
pos += bytes;
|
||||
bytesLeft -= bytes;
|
||||
}
|
||||
|
||||
rw_lock_read_unlock(inode->Lock());
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, io_request* request)
|
||||
{
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
|
||||
#ifndef EXFAT_SHELL
|
||||
if (io_request_is_write(request) && volume->IsReadOnly()) {
|
||||
notify_io_request(request, B_READ_ONLY_DEVICE);
|
||||
return B_READ_ONLY_DEVICE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (inode->FileCache() == NULL) {
|
||||
#ifndef EXFAT_SHELL
|
||||
notify_io_request(request, B_BAD_VALUE);
|
||||
#endif
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
// We lock the node here and will unlock it in the "finished" hook.
|
||||
rw_lock_read_lock(inode->Lock());
|
||||
|
||||
return do_iterative_fd_io(volume->Device(), request,
|
||||
iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
|
||||
size_t size, struct file_io_vec* vecs, size_t* _count)
|
||||
{
|
||||
TRACE("exfat_get_file_map()\n");
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
size_t index = 0, max = *_count;
|
||||
|
||||
while (true) {
|
||||
off_t blockOffset;
|
||||
off_t blockLength;
|
||||
status_t status = inode->FindBlock(offset, blockOffset, &blockLength);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
if (index > 0 && (vecs[index - 1].offset
|
||||
== blockOffset - vecs[index - 1].length)) {
|
||||
vecs[index - 1].length += blockLength;
|
||||
} else {
|
||||
if (index >= max) {
|
||||
// we're out of file_io_vecs; let's bail out
|
||||
*_count = index;
|
||||
return B_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
vecs[index].offset = blockOffset;
|
||||
vecs[index].length = blockLength;
|
||||
index++;
|
||||
}
|
||||
|
||||
offset += blockLength;
|
||||
size -= blockLength;
|
||||
|
||||
if (size <= vecs[index - 1].length || offset >= inode->Size()) {
|
||||
// We're done!
|
||||
*_count = index;
|
||||
TRACE("exfat_get_file_map for inode %lld\n", inode->ID());
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// can never get here
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
|
||||
ino_t* _vnodeID)
|
||||
{
|
||||
TRACE("exfat_lookup: name address: %p (%s)\n", name, name);
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
Inode* directory = (Inode*)_directory->private_node;
|
||||
|
||||
// check access permissions
|
||||
status_t status = directory->CheckPermissions(X_OK);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
status = DirectoryIterator(directory).Lookup(name, strlen(name), _vnodeID);
|
||||
if (status != B_OK) {
|
||||
ERROR("exfat_lookup: name %s (%s)\n", name, strerror(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
TRACE("exfat_lookup: ID %d\n", *_vnodeID);
|
||||
|
||||
return get_vnode(volume->FSVolume(), *_vnodeID, NULL);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
|
||||
void* buffer, size_t bufferLength)
|
||||
{
|
||||
TRACE("ioctl: %lu\n", cmd);
|
||||
|
||||
/*Volume* volume = (Volume*)_volume->private_volume;*/
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
|
||||
{
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
|
||||
stat->st_dev = inode->GetVolume()->ID();
|
||||
stat->st_ino = inode->ID();
|
||||
stat->st_nlink = 1;
|
||||
stat->st_blksize = EXFAT_IO_SIZE;
|
||||
|
||||
stat->st_uid = inode->UserID();
|
||||
stat->st_gid = inode->GroupID();
|
||||
stat->st_mode = inode->Mode();
|
||||
stat->st_type = 0;
|
||||
|
||||
inode->GetAccessTime(stat->st_atim);
|
||||
inode->GetModificationTime(stat->st_mtim);
|
||||
inode->GetChangeTime(stat->st_ctim);
|
||||
inode->GetCreationTime(stat->st_crtim);
|
||||
|
||||
stat->st_size = inode->Size();
|
||||
stat->st_blocks = (inode->Size() + 511) / 512;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode,
|
||||
void** _cookie)
|
||||
{
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
|
||||
// opening a directory read-only is allowed, although you can't read
|
||||
// any data from it.
|
||||
if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
|
||||
return B_IS_A_DIRECTORY;
|
||||
|
||||
status_t status = inode->CheckPermissions(open_mode_to_access(openMode)
|
||||
| (openMode & O_TRUNC ? W_OK : 0));
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// Prepare the cookie
|
||||
file_cookie* cookie = new(std::nothrow) file_cookie;
|
||||
if (cookie == NULL)
|
||||
return B_NO_MEMORY;
|
||||
ObjectDeleter<file_cookie> cookieDeleter(cookie);
|
||||
|
||||
cookie->open_mode = openMode & EXFAT_OPEN_MODE_USER_MASK;
|
||||
cookie->last_size = inode->Size();
|
||||
cookie->last_notification = system_time();
|
||||
|
||||
if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) {
|
||||
// Disable the file cache, if requested?
|
||||
status = file_cache_disable(inode->FileCache());
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
cookieDeleter.Detach();
|
||||
*_cookie = cookie;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
|
||||
void* buffer, size_t* _length)
|
||||
{
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
|
||||
if (!inode->IsFile()) {
|
||||
*_length = 0;
|
||||
return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
|
||||
}
|
||||
|
||||
return inode->ReadAt(pos, (uint8*)buffer, _length);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
|
||||
{
|
||||
file_cookie* cookie = (file_cookie*)_cookie;
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
|
||||
if (inode->Size() != cookie->last_size)
|
||||
notify_stat_changed(volume->ID(), inode->ID(), B_STAT_SIZE);
|
||||
|
||||
delete cookie;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
|
||||
{
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
return inode->CheckPermissions(accessMode);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
|
||||
size_t *_bufferSize)
|
||||
{
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
return inode->ReadAt(0, (uint8*)buffer, _bufferSize);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Directory functions
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie)
|
||||
{
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
status_t status = inode->CheckPermissions(R_OK);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
if (!inode->IsDirectory())
|
||||
return B_NOT_A_DIRECTORY;
|
||||
|
||||
DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
|
||||
if (iterator == NULL || iterator->InitCheck() != B_OK) {
|
||||
delete iterator;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
*_cookie = iterator;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
|
||||
struct dirent *dirent, size_t bufferSize, uint32 *_num)
|
||||
{
|
||||
TRACE("exfat_read_dir\n");
|
||||
DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
|
||||
|
||||
size_t length = bufferSize;
|
||||
ino_t id;
|
||||
status_t status = iterator->GetNext(dirent->d_name, &length, &id);
|
||||
if (status == B_ENTRY_NOT_FOUND) {
|
||||
*_num = 0;
|
||||
return B_OK;
|
||||
} else if (status != B_OK)
|
||||
return status;
|
||||
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
dirent->d_dev = volume->ID();
|
||||
dirent->d_ino = id;
|
||||
dirent->d_reclen = sizeof(struct dirent) + length;
|
||||
*_num = 1;
|
||||
|
||||
TRACE("exfat_read_dir end\n");
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
|
||||
{
|
||||
DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
|
||||
|
||||
return iterator->Rewind();
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
exfat_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
|
||||
{
|
||||
delete (DirectoryIterator*)_cookie;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
fs_volume_ops gExfatVolumeOps = {
|
||||
&exfat_unmount,
|
||||
&exfat_read_fs_info,
|
||||
NULL, // write_fs_info()
|
||||
NULL, // fs_sync,
|
||||
&exfat_get_vnode,
|
||||
};
|
||||
|
||||
|
||||
fs_vnode_ops gExfatVnodeOps = {
|
||||
/* vnode operations */
|
||||
&exfat_lookup,
|
||||
NULL,
|
||||
&exfat_put_vnode,
|
||||
NULL, // exfat_remove_vnode,
|
||||
|
||||
/* VM file access */
|
||||
&exfat_can_page,
|
||||
&exfat_read_pages,
|
||||
NULL, // exfat_write_pages,
|
||||
|
||||
NULL, // io()
|
||||
NULL, // cancel_io()
|
||||
|
||||
&exfat_get_file_map,
|
||||
|
||||
&exfat_ioctl,
|
||||
NULL,
|
||||
NULL, // fs_select
|
||||
NULL, // fs_deselect
|
||||
NULL, // fs_fsync,
|
||||
|
||||
&exfat_read_link,
|
||||
NULL, // fs_create_symlink,
|
||||
|
||||
NULL, // fs_link,
|
||||
NULL, // fs_unlink,
|
||||
NULL, // fs_rename,
|
||||
|
||||
&exfat_access,
|
||||
&exfat_read_stat,
|
||||
NULL, // fs_write_stat,
|
||||
NULL, // fs_preallocate
|
||||
|
||||
/* file operations */
|
||||
NULL, // fs_create,
|
||||
&exfat_open,
|
||||
&exfat_close,
|
||||
&exfat_free_cookie,
|
||||
&exfat_read,
|
||||
NULL, // fs_write,
|
||||
|
||||
/* directory operations */
|
||||
NULL, // fs_create_dir,
|
||||
NULL, // fs_remove_dir,
|
||||
&exfat_open_dir,
|
||||
&exfat_close_dir,
|
||||
&exfat_free_dir_cookie,
|
||||
&exfat_read_dir,
|
||||
&exfat_rewind_dir,
|
||||
|
||||
/* attribute directory operations */
|
||||
NULL, // fs_open_attr_dir,
|
||||
NULL, // fs_close_attr_dir,
|
||||
NULL, // fs_free_attr_dir_cookie,
|
||||
NULL, // fs_read_attr_dir,
|
||||
NULL, // fs_rewind_attr_dir,
|
||||
|
||||
/* attribute operations */
|
||||
NULL, // fs_create_attr,
|
||||
NULL, // fs_open_attr,
|
||||
NULL, // fs_close_attr,
|
||||
NULL, // fs_free_attr_cookie,
|
||||
NULL, // fs_read_attr,
|
||||
NULL, // fs_write_attr,
|
||||
NULL, // fs_read_attr_stat,
|
||||
NULL, // fs_write_attr_stat,
|
||||
NULL, // fs_rename_attr,
|
||||
NULL, // fs_remove_attr,
|
||||
};
|
||||
|
||||
|
||||
static file_system_module_info sExfatFileSystem = {
|
||||
{
|
||||
"file_systems/exfat" B_CURRENT_FS_API_VERSION,
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
|
||||
"exfat", // short_name
|
||||
"ExFAT File System", // pretty_name
|
||||
0, // DDM flags
|
||||
|
||||
// scanning
|
||||
exfat_identify_partition,
|
||||
exfat_scan_partition,
|
||||
exfat_free_identify_partition_cookie,
|
||||
NULL, // free_partition_content_cookie()
|
||||
|
||||
&exfat_mount,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
module_info *modules[] = {
|
||||
(module_info *)&sExfatFileSystem,
|
||||
NULL,
|
||||
};
|
||||
Reference in New Issue
Block a user