* 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:
Jérôme Duval
2011-02-09 20:08:48 +00:00
parent 294da6bff9
commit e74e90aef9
16 changed files with 2815 additions and 0 deletions
+1
View File
@@ -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, &currentLength, _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 &timespec) 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 &timespec) const
{ GetModificationTime(timespec); }
void GetModificationTime(struct timespec &timespec) const
{ _GetTimespec(fFileEntry.file.ModificationDate(),
fFileEntry.file.ModificationTime(), timespec); }
void GetCreationTime(struct timespec &timespec) const
{ _GetTimespec(fFileEntry.file.CreationDate(),
fFileEntry.file.CreationTime(), timespec); }
void GetAccessTime(struct timespec &timespec) 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 &timespec) 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,
};