* Added file_cache_is_enabled() function.

* Added file_map_set_mode() function that you can use to keep a whole file
  cached. This is needed for the swap file support: FILE_MAP_CACHE_ALL will
  not only precache all file_io_vecs when called, but it will also cause all
  file_map_translate() calls to fail that would require further caching (ie.
  if the file size had changed).
* Updated the fs_shell file map code to the latest one (with several bug fixes).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26785 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2008-08-04 08:28:17 +00:00
parent b555dcbd39
commit 3c1a3047a4
7 changed files with 474 additions and 215 deletions
+8
View File
@@ -19,6 +19,12 @@ enum {
TRANSACTION_IDLE = 0x08 TRANSACTION_IDLE = 0x08
}; };
/* file map modes */
enum {
FILE_MAP_CACHE_ON_DEMAND = 0x01, /* default mode */
FILE_MAP_CACHE_ALL = 0x02
};
typedef void (*transaction_notification_hook)(int32 id, int32 event, typedef void (*transaction_notification_hook)(int32 id, int32 event,
void *data); void *data);
@@ -74,6 +80,7 @@ extern void block_cache_put(void *_cache, off_t blockNumber);
extern void *file_cache_create(dev_t mountID, ino_t vnodeID, off_t size); extern void *file_cache_create(dev_t mountID, ino_t vnodeID, off_t size);
extern void file_cache_delete(void *_cacheRef); extern void file_cache_delete(void *_cacheRef);
extern void file_cache_enable(void *_cacheRef); extern void file_cache_enable(void *_cacheRef);
extern bool file_cache_is_enabled(void *_cacheRef);
extern status_t file_cache_disable(void *_cacheRef); extern status_t file_cache_disable(void *_cacheRef);
extern status_t file_cache_set_size(void *_cacheRef, off_t size); extern status_t file_cache_set_size(void *_cacheRef, off_t size);
extern status_t file_cache_sync(void *_cache); extern status_t file_cache_sync(void *_cache);
@@ -88,6 +95,7 @@ extern void *file_map_create(dev_t mountID, ino_t vnodeID, off_t size);
extern void file_map_delete(void *_map); extern void file_map_delete(void *_map);
extern void file_map_set_size(void *_map, off_t size); extern void file_map_set_size(void *_map, off_t size);
extern void file_map_invalidate(void *_map, off_t offset, off_t size); extern void file_map_invalidate(void *_map, off_t offset, off_t size);
extern status_t file_map_set_mode(void *_map, uint32 mode);
extern status_t file_map_translate(void *_map, off_t offset, size_t size, extern status_t file_map_translate(void *_map, off_t offset, size_t size,
struct file_io_vec *vecs, size_t *_count); struct file_io_vec *vecs, size_t *_count);
@@ -794,6 +794,9 @@
#define TRANSACTION_ENDED FSSH_TRANSACTION_ENDED #define TRANSACTION_ENDED FSSH_TRANSACTION_ENDED
#define TRANSACTION_IDLE FSSH_TRANSACTION_IDLE #define TRANSACTION_IDLE FSSH_TRANSACTION_IDLE
#define FILE_MAP_CACHE_ON_DEMAND FSSH_FILE_MAP_CACHE_ON_DEMAND
#define FILE_MAP_CACHE_ALL FSSH_FILE_MAP_CACHE_ALL
#define transaction_notification_hook fssh_transaction_notification_hook #define transaction_notification_hook fssh_transaction_notification_hook
/* transactions */ /* transactions */
+8
View File
@@ -18,6 +18,12 @@ enum {
FSSH_TRANSACTION_IDLE = 0x08 FSSH_TRANSACTION_IDLE = 0x08
}; };
/* file map modes */
enum {
FSSH_FILE_MAP_CACHE_ON_DEMAND = 0x01, /* default mode */
FSSH_FILE_MAP_CACHE_ALL = 0x02
};
typedef void (*fssh_transaction_notification_hook)(int32_t id, int32_t event, typedef void (*fssh_transaction_notification_hook)(int32_t id, int32_t event,
void *data); void *data);
@@ -90,6 +96,7 @@ extern void * fssh_file_cache_create(fssh_mount_id mountID,
extern void fssh_file_cache_delete(void *_cacheRef); extern void fssh_file_cache_delete(void *_cacheRef);
extern void fssh_file_cache_enable(void *_cacheRef); extern void fssh_file_cache_enable(void *_cacheRef);
extern fssh_status_t fssh_file_cache_disable(void *_cacheRef); extern fssh_status_t fssh_file_cache_disable(void *_cacheRef);
extern bool fssh_file_cache_is_enabled(void *_cacheRef);
extern fssh_status_t fssh_file_cache_set_size(void *_cacheRef, extern fssh_status_t fssh_file_cache_set_size(void *_cacheRef,
fssh_off_t size); fssh_off_t size);
extern fssh_status_t fssh_file_cache_sync(void *_cache); extern fssh_status_t fssh_file_cache_sync(void *_cache);
@@ -108,6 +115,7 @@ extern void fssh_file_map_delete(void *_map);
extern void fssh_file_map_set_size(void *_map, fssh_off_t size); extern void fssh_file_map_set_size(void *_map, fssh_off_t size);
extern void fssh_file_map_invalidate(void *_map, fssh_off_t offset, extern void fssh_file_map_invalidate(void *_map, fssh_off_t offset,
fssh_off_t size); fssh_off_t size);
extern fssh_status_t fssh_file_map_set_mode(void *_map, uint32_t mode);
extern fssh_status_t fssh_file_map_translate(void *_map, fssh_off_t offset, extern fssh_status_t fssh_file_map_translate(void *_map, fssh_off_t offset,
fssh_size_t size, struct fssh_file_io_vec *vecs, fssh_size_t size, struct fssh_file_io_vec *vecs,
fssh_size_t *_count); fssh_size_t *_count);
+10
View File
@@ -979,6 +979,16 @@ file_cache_disable(void *_cacheRef)
} }
extern "C" bool
file_cache_is_enabled(void *_cacheRef)
{
file_cache_ref *ref = (file_cache_ref*)_cacheRef;
AutoLocker<VMCache> _(ref->cache);
return ref->disabled_count == 0;
}
extern "C" status_t extern "C" status_t
file_cache_set_size(void *_cacheRef, off_t newSize) file_cache_set_size(void *_cacheRef, off_t newSize)
{ {
+78 -22
View File
@@ -71,11 +71,14 @@ public:
struct vnode* Vnode() const { return fVnode; } struct vnode* Vnode() const { return fVnode; }
off_t Size() const { return fSize; } off_t Size() const { return fSize; }
status_t SetMode(uint32 mode);
private: private:
file_extent* _FindExtent(off_t offset, uint32* _index); file_extent* _FindExtent(off_t offset, uint32* _index);
status_t _MakeSpace(size_t count); status_t _MakeSpace(size_t count);
status_t _Add(file_io_vec* vecs, size_t vecCount, status_t _Add(file_io_vec* vecs, size_t vecCount,
off_t& lastOffset); off_t& lastOffset);
status_t _Cache(off_t offset, off_t size);
void _InvalidateAfter(off_t offset); void _InvalidateAfter(off_t offset);
void _Free(); void _Free();
@@ -87,6 +90,7 @@ private:
size_t fCount; size_t fCount;
struct vnode* fVnode; struct vnode* fVnode;
off_t fSize; off_t fSize;
bool fCacheAll;
}; };
#ifdef DEBUG_FILE_MAP #ifdef DEBUG_FILE_MAP
@@ -98,11 +102,13 @@ static mutex sLock;
FileMap::FileMap(struct vnode* vnode, off_t size) FileMap::FileMap(struct vnode* vnode, off_t size)
:
fCount(0),
fVnode(vnode),
fSize(size),
fCacheAll(false)
{ {
mutex_init(&fLock, "file map"); mutex_init(&fLock, "file map");
fCount = 0;
fVnode = vnode;
fSize = size;
#ifdef DEBUG_FILE_MAP #ifdef DEBUG_FILE_MAP
MutexLocker _(sLock); MutexLocker _(sLock);
@@ -319,13 +325,69 @@ FileMap::_Free()
} }
status_t
FileMap::_Cache(off_t offset, off_t size)
{
file_extent* lastExtent = NULL;
if (fCount > 0)
lastExtent = ExtentAt(fCount - 1);
off_t mapEnd = 0;
if (lastExtent != NULL)
mapEnd = lastExtent->offset + lastExtent->disk.length;
off_t end = offset + size;
if (fCacheAll && mapEnd < end)
return B_ERROR;
status_t status = B_OK;
file_io_vec vecs[8];
const size_t kMaxVecs = 8;
while (status == B_OK && mapEnd < end) {
// We don't have the requested extents yet, retrieve them
size_t vecCount = kMaxVecs;
status = vfs_get_file_map(Vnode(), mapEnd, ~0UL, vecs, &vecCount);
if (status == B_OK || status == B_BUFFER_OVERFLOW)
status = _Add(vecs, vecCount, mapEnd);
}
return status;
}
status_t
FileMap::SetMode(uint32 mode)
{
if (mode != FILE_MAP_CACHE_ALL && mode != FILE_MAP_CACHE_ON_DEMAND)
return B_BAD_VALUE;
MutexLocker _(fLock);
if (mode == FILE_MAP_CACHE_ALL && fCacheAll
|| mode == FILE_MAP_CACHE_ON_DEMAND && !fCacheAll)
return B_OK;
if (mode == FILE_MAP_CACHE_ALL) {
status_t status = _Cache(0, fSize);
if (status != B_OK)
return status;
fCacheAll = true;
} else
fCacheAll = false;
return B_OK;
}
status_t status_t
FileMap::Translate(off_t offset, size_t size, file_io_vec* vecs, size_t* _count) FileMap::Translate(off_t offset, size_t size, file_io_vec* vecs, size_t* _count)
{ {
MutexLocker _(fLock); MutexLocker _(fLock);
size_t maxVecs = *_count; size_t maxVecs = *_count;
status_t status = B_OK;
if (offset >= Size()) { if (offset >= Size()) {
*_count = 0; *_count = 0;
@@ -337,24 +399,7 @@ FileMap::Translate(off_t offset, size_t size, file_io_vec* vecs, size_t* _count)
// First, we need to make sure that we have already cached all file // First, we need to make sure that we have already cached all file
// extents needed for this request. // extents needed for this request.
file_extent* lastExtent = NULL; status_t status = _Cache(offset, size);
if (fCount > 0)
lastExtent = ExtentAt(fCount - 1);
off_t mapOffset = 0;
if (lastExtent != NULL)
mapOffset = lastExtent->offset + lastExtent->disk.length;
off_t end = offset + size;
while (status == B_OK && mapOffset < end) {
// We don't have the requested extents yet, retrieve them
size_t vecCount = maxVecs;
status = vfs_get_file_map(Vnode(), mapOffset, ~0UL, vecs, &vecCount);
if (status == B_OK || status == B_BUFFER_OVERFLOW)
status = _Add(vecs, vecCount, mapOffset);
}
if (status != B_OK) if (status != B_OK)
return status; return status;
@@ -569,6 +614,17 @@ file_map_invalidate(void* _map, off_t offset, off_t size)
} }
extern "C" status_t
file_map_set_mode(void* _map, uint32 mode)
{
FileMap* map = (FileMap*)_map;
if (map == NULL)
return B_BAD_VALUE;
return map->SetMode(mode);
}
extern "C" status_t extern "C" status_t
file_map_translate(void* _map, off_t offset, size_t size, file_io_vec* vecs, file_map_translate(void* _map, off_t offset, size_t size, file_io_vec* vecs,
size_t* _count) size_t* _count)
+7
View File
@@ -296,6 +296,13 @@ fssh_file_cache_disable(void *_cacheRef)
} }
bool
fssh_file_cache_is_enabled(void *_cacheRef)
{
return true;
}
fssh_status_t fssh_status_t
fssh_file_cache_set_size(void *_cacheRef, fssh_off_t size) fssh_file_cache_set_size(void *_cacheRef, fssh_off_t size)
{ {
+360 -193
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2004-2007, Axel Dörfler, [email protected]. All rights reserved. * Copyright 2004-2008, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -8,6 +8,7 @@
#include <new> #include <new>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "fssh_kernel_export.h" #include "fssh_kernel_export.h"
#include "vfs.h" #include "vfs.h"
@@ -33,103 +34,201 @@ struct file_extent {
fssh_file_io_vec disk; fssh_file_io_vec disk;
}; };
struct file_map { struct file_extent_array {
file_map(fssh_off_t size); file_extent* array;
~file_map(); fssh_size_t max_count;
};
file_extent *operator[](uint32_t index); class FileMap {
file_extent *ExtentAt(uint32_t index); public:
fssh_status_t Add(fssh_file_io_vec *vecs, fssh_size_t vecCount, FileMap(void* vnode, fssh_off_t size);
fssh_off_t &lastOffset); ~FileMap();
void Free();
void Invalidate(fssh_off_t offset, fssh_off_t size);
void SetSize(fssh_off_t size);
fssh_status_t Translate(fssh_off_t offset, fssh_size_t size,
fssh_file_io_vec* vecs, fssh_size_t* _count);
file_extent* ExtentAt(uint32_t index);
fssh_size_t Count() const { return fCount; }
void* Vnode() const { return fVnode; }
fssh_off_t Size() const { return fSize; }
fssh_status_t SetMode(uint32_t mode);
private:
file_extent* _FindExtent(fssh_off_t offset, uint32_t* _index);
fssh_status_t _MakeSpace(fssh_size_t count);
fssh_status_t _Add(fssh_file_io_vec* vecs, fssh_size_t vecCount,
fssh_off_t& lastOffset);
fssh_status_t _Cache(fssh_off_t offset, fssh_off_t size);
void _InvalidateAfter(fssh_off_t offset);
void _Free();
union { union {
file_extent direct[CACHED_FILE_EXTENTS]; file_extent fDirect[CACHED_FILE_EXTENTS];
file_extent *array; file_extent_array fIndirect;
}; };
fssh_size_t count; fssh_mutex fLock;
void *vnode; fssh_size_t fCount;
fssh_off_t size; void* fVnode;
fssh_off_t fSize;
bool fCacheAll;
}; };
file_map::file_map(fssh_off_t _size) FileMap::FileMap(void* vnode, fssh_off_t size)
:
fCount(0),
fVnode(vnode),
fSize(size),
fCacheAll(false)
{ {
array = NULL; fssh_mutex_init(&fLock, "file map");
count = 0;
size = _size;
} }
file_map::~file_map() FileMap::~FileMap()
{ {
Free(); _Free();
fssh_mutex_destroy(&fLock);
} }
file_extent * file_extent*
file_map::operator[](uint32_t index) FileMap::ExtentAt(uint32_t index)
{ {
return ExtentAt(index); if (index >= fCount)
}
file_extent *
file_map::ExtentAt(uint32_t index)
{
if (index >= count)
return NULL; return NULL;
if (count > CACHED_FILE_EXTENTS) if (fCount > CACHED_FILE_EXTENTS)
return &array[index]; return &fIndirect.array[index];
return &direct[index]; return &fDirect[index];
}
file_extent*
FileMap::_FindExtent(fssh_off_t offset, uint32_t *_index)
{
int32_t left = 0;
int32_t right = fCount - 1;
while (left <= right) {
int32_t index = (left + right) / 2;
file_extent* extent = ExtentAt(index);
if (extent->offset > offset) {
// search in left part
right = index - 1;
} else if (extent->offset + extent->disk.length <= offset) {
// search in right part
left = index + 1;
} else {
// found extent
if (_index)
*_index = index;
return extent;
}
}
return NULL;
} }
fssh_status_t fssh_status_t
file_map::Add(fssh_file_io_vec *vecs, fssh_size_t vecCount, FileMap::_MakeSpace(fssh_size_t count)
fssh_off_t &lastOffset)
{ {
TRACE(("file_map::Add(vecCount = %u)\n", vecCount)); if (count <= CACHED_FILE_EXTENTS) {
fssh_off_t offset = 0;
if (vecCount <= CACHED_FILE_EXTENTS && count == 0) {
// just use the reserved area in the file_cache_ref structure // just use the reserved area in the file_cache_ref structure
if (fCount > CACHED_FILE_EXTENTS) {
// the new size is smaller than the minimal array size
file_extent *array = fIndirect.array;
memcpy(fDirect, array, sizeof(file_extent) * count);
free(array);
}
} else { } else {
// TODO: once we can invalidate only parts of the file map, // resize array if needed
// we might need to copy the previously cached file extends file_extent* oldArray = NULL;
// from the direct range fssh_size_t maxCount = CACHED_FILE_EXTENTS;
file_extent *newMap = (file_extent *)realloc(array, if (fCount > CACHED_FILE_EXTENTS) {
(count + vecCount) * sizeof(file_extent)); oldArray = fIndirect.array;
if (newMap == NULL) maxCount = fIndirect.max_count;
return FSSH_B_NO_MEMORY; }
array = newMap; if (count > maxCount) {
// allocate new array
while (maxCount < count) {
if (maxCount < 32768)
maxCount <<= 1;
else
maxCount += 32768;
}
if (count != 0) { file_extent* newArray = (file_extent *)realloc(oldArray,
file_extent *extent = ExtentAt(count - 1); maxCount * sizeof(file_extent));
offset = extent->offset + extent->disk.length; if (newArray == NULL)
return FSSH_B_NO_MEMORY;
if (fCount > 0 && fCount <= CACHED_FILE_EXTENTS)
memcpy(newArray, fDirect, sizeof(file_extent) * fCount);
fIndirect.array = newArray;
fIndirect.max_count = maxCount;
} }
} }
int32_t start = count; fCount = count;
count += vecCount; return FSSH_B_OK;
}
fssh_status_t
FileMap::_Add(fssh_file_io_vec* vecs, fssh_size_t vecCount,
fssh_off_t& lastOffset)
{
TRACE(("FileMap@%p::Add(vecCount = %ld)\n", this, vecCount));
uint32_t start = fCount;
fssh_off_t offset = 0;
fssh_status_t status = _MakeSpace(fCount + vecCount);
if (status != FSSH_B_OK)
return status;
file_extent* lastExtent = NULL;
if (start != 0) {
lastExtent = ExtentAt(start - 1);
offset = lastExtent->offset + lastExtent->disk.length;
}
for (uint32_t i = 0; i < vecCount; i++) { for (uint32_t i = 0; i < vecCount; i++) {
file_extent *extent = ExtentAt(start + i); if (lastExtent != NULL) {
if (lastExtent->disk.offset + lastExtent->disk.length
== vecs[i].offset) {
lastExtent->disk.length += vecs[i].length;
offset += vecs[i].length;
start--;
_MakeSpace(fCount - 1);
continue;
}
}
file_extent* extent = ExtentAt(start + i);
extent->offset = offset; extent->offset = offset;
extent->disk = vecs[i]; extent->disk = vecs[i];
offset += extent->disk.length; offset += extent->disk.length;
lastExtent = extent;
} }
#ifdef TRACE_FILE_MAP #ifdef TRACE_FILE_MAP
for (uint32_t i = 0; i < count; i++) { for (uint32 i = 0; i < fCount; i++) {
file_extent *extent = ExtentAt(i); file_extent* extent = ExtentAt(i);
fssh_dprintf("[%u] extend offset %Ld, disk offset %Ld, length %Ld\n", dprintf("[%ld] extent offset %Ld, disk offset %Ld, length %Ld\n",
i, extent->offset, extent->disk.offset, extent->disk.length); i, extent->offset, extent->disk.offset, extent->disk.length);
} }
#endif #endif
@@ -140,36 +239,184 @@ file_map::Add(fssh_file_io_vec *vecs, fssh_size_t vecCount,
void void
file_map::Free() FileMap::_InvalidateAfter(fssh_off_t offset)
{ {
if (count > CACHED_FILE_EXTENTS) uint32_t index;
free(array); file_extent* extent = _FindExtent(offset, &index);
if (extent != NULL) {
_MakeSpace(index + 1);
array = NULL; if (extent->offset + extent->disk.length > offset) {
count = 0; extent->disk.length = offset - extent->offset;
if (extent->disk.length == 0)
_MakeSpace(index);
}
}
} }
// #pragma mark - /*! Invalidates or removes the specified part of the file map.
*/
void
static file_extent * FileMap::Invalidate(fssh_off_t offset, fssh_off_t size)
find_file_extent(file_map &map, fssh_off_t offset, uint32_t *_index)
{ {
// TODO: do binary search MutexLocker _(fLock);
for (uint32_t index = 0; index < map.count; index++) { // TODO: honour size, we currently always remove everything after "offset"
file_extent *extent = map[index]; if (offset == 0) {
_Free();
if (extent->offset <= offset return;
&& extent->offset + extent->disk.length > offset) {
if (_index)
*_index = index;
return extent;
}
} }
return NULL; _InvalidateAfter(offset);
}
void
FileMap::SetSize(fssh_off_t size)
{
MutexLocker _(fLock);
if (size < fSize)
_InvalidateAfter(size);
fSize = size;
}
void
FileMap::_Free()
{
if (fCount > CACHED_FILE_EXTENTS)
free(fIndirect.array);
fCount = 0;
}
fssh_status_t
FileMap::_Cache(fssh_off_t offset, fssh_off_t size)
{
file_extent* lastExtent = NULL;
if (fCount > 0)
lastExtent = ExtentAt(fCount - 1);
fssh_off_t mapEnd = 0;
if (lastExtent != NULL)
mapEnd = lastExtent->offset + lastExtent->disk.length;
fssh_off_t end = offset + size;
if (fCacheAll && mapEnd < end)
return FSSH_B_ERROR;
fssh_status_t status = FSSH_B_OK;
fssh_file_io_vec vecs[8];
const fssh_size_t kMaxVecs = 8;
while (status == FSSH_B_OK && mapEnd < end) {
// We don't have the requested extents yet, retrieve them
size_t vecCount = kMaxVecs;
status = vfs_get_file_map(Vnode(), mapEnd, ~0UL, vecs, &vecCount);
if (status == FSSH_B_OK || status == FSSH_B_BUFFER_OVERFLOW)
status = _Add(vecs, vecCount, mapEnd);
}
return status;
}
fssh_status_t
FileMap::SetMode(uint32_t mode)
{
if (mode != FSSH_FILE_MAP_CACHE_ALL
&& mode != FSSH_FILE_MAP_CACHE_ON_DEMAND)
return FSSH_B_BAD_VALUE;
MutexLocker _(fLock);
if (mode == FSSH_FILE_MAP_CACHE_ALL && fCacheAll
|| mode == FSSH_FILE_MAP_CACHE_ON_DEMAND && !fCacheAll)
return FSSH_B_OK;
if (mode == FSSH_FILE_MAP_CACHE_ALL) {
fssh_status_t status = _Cache(0, fSize);
if (status != FSSH_B_OK)
return status;
fCacheAll = true;
} else
fCacheAll = false;
return FSSH_B_OK;
}
fssh_status_t
FileMap::Translate(fssh_off_t offset, fssh_size_t size, fssh_file_io_vec* vecs,
fssh_size_t* _count)
{
MutexLocker _(fLock);
fssh_size_t maxVecs = *_count;
if (offset >= Size()) {
*_count = 0;
return FSSH_B_OK;
}
if (offset + size > fSize)
size = fSize - offset;
// First, we need to make sure that we have already cached all file
// extents needed for this request.
fssh_status_t status = _Cache(offset, size);
if (status != FSSH_B_OK)
return status;
// We now have cached the map of this file as far as we need it, now
// we need to translate it for the requested access.
uint32_t index;
file_extent* fileExtent = _FindExtent(offset, &index);
offset -= fileExtent->offset;
vecs[0].offset = fileExtent->disk.offset + offset;
vecs[0].length = fileExtent->disk.length - offset;
if (vecs[0].length >= size) {
if (vecs[0].length > size)
vecs[0].length = size;
*_count = 1;
return FSSH_B_OK;
}
// copy the rest of the vecs
size -= vecs[0].length;
uint32_t vecIndex = 1;
while (true) {
fileExtent++;
vecs[vecIndex++] = fileExtent->disk;
if (size <= fileExtent->disk.length) {
if (size < fileExtent->disk.length)
vecs[vecIndex - 1].length = size;
break;
}
if (vecIndex >= maxVecs) {
*_count = vecIndex;
return FSSH_B_BUFFER_OVERFLOW;
}
size -= fileExtent->disk.length;
}
*_count = vecIndex;
return FSSH_B_OK;
} }
@@ -179,31 +426,27 @@ find_file_extent(file_map &map, fssh_off_t offset, uint32_t *_index)
// #pragma mark - public FS API // #pragma mark - public FS API
extern "C" void * extern "C" void*
fssh_file_map_create(fssh_mount_id mountID, fssh_vnode_id vnodeID, fssh_file_map_create(fssh_mount_id mountID, fssh_vnode_id vnodeID,
fssh_off_t size) fssh_off_t size)
{ {
TRACE(("file_map_create(mountID = %d, vnodeID = %Ld)\n", mountID, vnodeID)); TRACE(("file_map_create(mountID = %ld, vnodeID = %Ld, size = %Ld)\n",
mountID, vnodeID, size));
file_map *map = new file_map(size);
if (map == NULL)
return NULL;
// Get the vnode for the object // Get the vnode for the object
// (note, this does not grab a reference to the node) // (note, this does not grab a reference to the node)
if (vfs_lookup_vnode(mountID, vnodeID, &map->vnode) != FSSH_B_OK) { void* vnode;
delete map; if (vfs_lookup_vnode(mountID, vnodeID, &vnode) != FSSH_B_OK)
return NULL; return NULL;
}
return map; return new(std::nothrow) FileMap(vnode, size);
} }
extern "C" void extern "C" void
fssh_file_map_delete(void *_map) fssh_file_map_delete(void* _map)
{ {
file_map *map = (file_map *)_map; FileMap* map = (FileMap*)_map;
if (map == NULL) if (map == NULL)
return; return;
@@ -213,125 +456,49 @@ fssh_file_map_delete(void *_map)
extern "C" void extern "C" void
fssh_file_map_set_size(void *_map, fssh_off_t size) fssh_file_map_set_size(void* _map, fssh_off_t size)
{ {
if (_map == NULL) FileMap* map = (FileMap*)_map;
if (map == NULL)
return; return;
// TODO: honour offset/size parameters map->SetSize(size);
file_map *map = (file_map *)_map;
//if (size < map->size)
map->Free();
map->size = size;
} }
extern "C" void extern "C" void
fssh_file_map_invalidate(void *_map, fssh_off_t offset, fssh_off_t size) fssh_file_map_invalidate(void* _map, fssh_off_t offset, fssh_off_t size)
{ {
if (_map == NULL) FileMap* map = (FileMap*)_map;
if (map == NULL)
return; return;
// TODO: honour offset/size parameters map->Invalidate(offset, size);
file_map *map = (file_map *)_map;
map->Free();
} }
extern "C" fssh_status_t extern "C" fssh_status_t
fssh_file_map_translate(void *_map, fssh_off_t offset, fssh_size_t size, fssh_file_map_set_mode(void* _map, uint32_t mode)
fssh_file_io_vec *vecs, fssh_size_t *_count)
{ {
if (_map == NULL) FileMap* map = (FileMap*)_map;
if (map == NULL)
return FSSH_B_BAD_VALUE; return FSSH_B_BAD_VALUE;
file_map &map = *(file_map *)_map; return map->SetMode(mode);
fssh_size_t maxVecs = *_count; }
fssh_status_t status = FSSH_B_OK;
if (offset >= map.size) { extern "C" fssh_status_t
*_count = 0; fssh_file_map_translate(void* _map, fssh_off_t offset, fssh_size_t size,
return FSSH_B_OK; fssh_file_io_vec* vecs, fssh_size_t* _count)
} {
if (offset + size > map.size) TRACE(("file_map_translate(map %p, offset %Ld, size %ld)\n",
size = map.size - offset; _map, offset, size));
if (map.count == 0) { FileMap* map = (FileMap*)_map;
// we don't yet have the map of this file, so let's grab it if (map == NULL)
// (ordered by offset, so that we can do a binary search on them) return FSSH_B_BAD_VALUE;
fssh_size_t vecCount = maxVecs;
fssh_off_t mapOffset = 0; return map->Translate(offset, size, vecs, _count);
while (true) {
status = vfs_get_file_map(map.vnode, mapOffset, ~0UL, vecs,
&vecCount);
if (status < FSSH_B_OK && status != FSSH_B_BUFFER_OVERFLOW)
return status;
fssh_status_t addStatus = map.Add(vecs, vecCount, mapOffset);
if (addStatus != FSSH_B_OK) {
// only clobber the status in case of failure
status = addStatus;
}
if (status != FSSH_B_BUFFER_OVERFLOW)
break;
// when we are here, the map has been stored in the array, and
// the array size was still too small to cover the whole file
vecCount = maxVecs;
}
}
if (status != FSSH_B_OK) {
// We must invalidate the (part of the) map we already
// have, as we cannot know if it's complete or not
map.Free();
return status;
}
// We now have cached the map of this file, we now need to
// translate it for the requested access.
uint32_t index;
file_extent *fileExtent = find_file_extent(map, offset, &index);
if (fileExtent == NULL) {
// access outside file bounds? But that's not our problem
*_count = 0;
return FSSH_B_OK;
}
offset -= fileExtent->offset;
vecs[0].offset = fileExtent->disk.offset + offset;
vecs[0].length = fileExtent->disk.length - offset;
if (vecs[0].length >= size || index >= map.count - 1) {
*_count = 1;
return FSSH_B_OK;
}
// copy the rest of the vecs
size -= vecs[0].length;
for (index = 1; index < map.count;) {
fileExtent++;
vecs[index] = fileExtent->disk;
index++;
if (size <= fileExtent->disk.length)
break;
if (index >= maxVecs) {
*_count = index;
return FSSH_B_BUFFER_OVERFLOW;
}
size -= fileExtent->disk.length;
}
*_count = index;
return FSSH_B_OK;
} }