/*
* Copyright 2012, Jérôme Duval, korli@users.berlios.de.
* Copyright 2010, Michael Lotz, mmlr@mlotz.ch.
* Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net.
* Distributed under the terms of the MIT License.
*/
#include "Icb.h"
#include "time.h"
#include "AllocationDescriptorList.h"
#include "Utils.h"
#include "Volume.h"
#include <file_cache.h>
status_t
DirectoryIterator::GetNextEntry(char *name, uint32 *length, ino_t *id)
{
if (!id || !name || !length)
return B_BAD_VALUE;
TRACE(("DirectoryIterator::GetNextEntry: name = %p, length = %" B_PRIu32
", id = %p, position = %" B_PRIdOFF ", parent length = %" B_PRIu64
"\n", name, *length, id, fPosition, Parent()->Length()));
status_t status = B_OK;
if (fAtBeginning) {
TRACE(("DirectoryIterator::GetNextEntry: .\n"));
sprintf(name, ".");
*length = 2;
*id = Parent()->Id();
fAtBeginning = false;
} else {
if (uint64(fPosition) >= Parent()->Length()) {
TRACE(("DirectoryIterator::GetNextEntry: end of dir\n"));
return B_ENTRY_NOT_FOUND;
}
uint8 data[kMaxFileIdSize];
file_id_descriptor *entry = (file_id_descriptor *)data;
uint32 block = 0;
off_t offset = fPosition;
size_t entryLength = kMaxFileIdSize;
// First read in the static portion of the file id descriptor,
// then, based on the information therein, read in the variable
// length tail portion as well.
status = Parent()->Read(offset, entry, &entryLength, &block);
if (!status && entryLength >= sizeof(file_id_descriptor)
&& entry->tag().init_check(block) == B_OK) {
PDUMP(entry);
offset += entry->total_length();
if (entry->is_parent()) {
TRACE(("DirectoryIterator::GetNextEntry: ..\n"));
sprintf(name, "..");
*length = 3;
} else {
UdfString string(entry->id(), entry->id_length());
TRACE(("DirectoryIterator::GetNextEntry: UfdString id == `%s', "
"length = %" B_PRIu32 "\n", string.Utf8(),
string.Utf8Length()));
DUMP(entry->icb());
sprintf(name, "%s", string.Utf8());
*length = string.Utf8Length();
}
*id = to_vnode_id(entry->icb());
}
if (!status)
fPosition = offset;
}
return status;
}
/* \brief Rewinds the iterator to point to the first entry in the directory. */
void
DirectoryIterator::Rewind()
{
fAtBeginning = true;
fPosition = 0;
}
// #pragma mark - Private methods
DirectoryIterator::DirectoryIterator(Icb *parent)
:
fAtBeginning(true),
fParent(parent),
fPosition(0)
{
}
Icb::Icb(Volume *volume, long_address address)
:
fVolume(volume),
fData(volume),
fInitStatus(B_NO_INIT),
fId(to_vnode_id(address)),
fPartition(address.partition()),
fFileEntry(&fData),
fExtendedEntry(&fData),
fFileCache(NULL),
fFileMap(NULL)
{
TRACE(("Icb::Icb: volume = %p, address(block = %" B_PRIu32 ", partition = "
"%d, length = %" B_PRIu32 ")\n", volume, address.block(),
address.partition(), address.length()));
if (volume == NULL) {
fInitStatus = B_BAD_VALUE;
return;
}
off_t block;
status_t status = fVolume->MapBlock(address, &block);
if (!status) {
icb_header *header = (icb_header *)fData.SetTo(block);
if (header->tag().id() == TAGID_FILE_ENTRY) {
file_icb_entry *entry = (file_icb_entry *)header;
PDUMP(entry);
(void)entry; // warning death
} else if (header->tag().id() == TAGID_EXTENDED_FILE_ENTRY) {
extended_file_icb_entry *entry = (extended_file_icb_entry *)header;
PDUMP(entry);
(void)entry; // warning death
} else {
PDUMP(header);
}
status = header->tag().init_check(address.block());
}
if (IsFile()) {
fFileCache = file_cache_create(fVolume->ID(), fId, Length());
fFileMap = file_map_create(fVolume->ID(), fId, Length());
}
fInitStatus = status;
TRACE(("Icb::Icb: status = 0x%" B_PRIx32 ", `%s'\n", status,
strerror(status)));
}
Icb::~Icb()
{
if (fFileCache != NULL) {
file_cache_delete(fFileCache);
file_map_delete(fFileMap);
}
}
status_t
Icb::GetDirectoryIterator(DirectoryIterator **iterator)
{
status_t error = iterator ? B_OK : B_BAD_VALUE;
if (!error) {
*iterator = new(std::nothrow) DirectoryIterator(this);
if (*iterator)
fIteratorList.Add(*iterator);
else
error = B_NO_MEMORY;
}
return error;
}
status_t
Icb::InitCheck()
{
return fInitStatus;
}
void
Icb::GetAccessTime(struct timespec ×pec) const
{
timestamp ts;
if (_Tag().id() == TAGID_EXTENDED_FILE_ENTRY)
ts = _ExtendedEntry()->access_date_and_time();
else
ts = _FileEntry()->access_date_and_time();
if (decode_time(ts, timespec) != B_OK) {
decode_time(
fVolume->PrimaryVolumeDescriptor()->recording_date_and_time(),
timespec);
}
}
void
Icb::GetModificationTime(struct timespec ×pec) const
{
timestamp ts;
if (_Tag().id() == TAGID_EXTENDED_FILE_ENTRY)
ts = _ExtendedEntry()->modification_date_and_time();
else
ts = _FileEntry()->modification_date_and_time();
if (decode_time(ts, timespec) != B_OK) {
decode_time(
fVolume->PrimaryVolumeDescriptor()->recording_date_and_time(),
timespec);
}
}
status_t
Icb::FindBlock(uint32 logicalBlock, off_t &block, bool &recorded)
{
off_t pos = logicalBlock << fVolume->BlockShift();
if (uint64(pos) >= Length()) {
block = -1;
return B_ERROR;
}
DEBUG_INIT_ETC("Icb", ("pos: %" B_PRIdOFF, pos));
status_t status = B_OK;
long_address extent;
bool isEmpty = false;
recorded = false;
switch (_IcbTag().descriptor_flags()) {
case ICB_DESCRIPTOR_TYPE_SHORT:
{
TRACE(("Icb::FindBlock: descriptor type -> short\n"));
AllocationDescriptorList<ShortDescriptorAccessor> list(this,
ShortDescriptorAccessor(fPartition));
status = list.FindExtent(pos, &extent, &isEmpty);
if (status != B_OK) {
TRACE_ERROR(("Icb::FindBlock: error finding extent for offset "
"%" B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos, status,
strerror(status)));
}
break;
}
case ICB_DESCRIPTOR_TYPE_LONG:
{
TRACE(("Icb::FindBlock: descriptor type -> long\n"));
AllocationDescriptorList<LongDescriptorAccessor> list(this);
status = list.FindExtent(pos, &extent, &isEmpty);
if (status != B_OK) {
TRACE_ERROR(("Icb::FindBlock: error finding extent for offset "
"%" B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos,
status, strerror(status)));
}
break;
}
case ICB_DESCRIPTOR_TYPE_EXTENDED:
{
TRACE(("Icb::FindBlock: descriptor type -> extended\n"));
// AllocationDescriptorList<ExtendedDescriptorAccessor> list(this, ExtendedDescriptorAccessor(0));
// RETURN(_Read(list, pos, buffer, length, block));
RETURN(B_ERROR);
break;
}
case ICB_DESCRIPTOR_TYPE_EMBEDDED:
{
TRACE(("Icb::FindBlock: descriptor type: embedded\n"));
RETURN(B_ERROR);
break;
}
default:
TRACE(("Icb::FindBlock: invalid icb descriptor flags! (flags = %d)\n",
_IcbTag().descriptor_flags()));
RETURN(B_BAD_VALUE);
break;
}
if (status == B_OK) {
block = extent.block();
recorded = extent.type() == EXTENT_TYPE_RECORDED;
TRACE(("Icb::FindBlock: block %" B_PRIdOFF "\n", block));
}
return status;
}
status_t
Icb::Read(off_t pos, void *buffer, size_t *length, uint32 *block)
{
TRACE(("Icb::Read: pos = %" B_PRIdOFF ", buffer = %p, length = (%p)->%ld\n",
pos, buffer, length, (length ? *length : 0)));
DEBUG_INIT_ETC("Icb", ("pos: %lld, length: %ld", pos, *length));
if (fFileCache != NULL)
return file_cache_read(fFileCache, NULL, pos, buffer, length);
if (!buffer || !length || pos < 0)
return B_BAD_VALUE;
if (uint64(pos) >= Length()) {
*length = 0;
return B_OK;
}
switch (_IcbTag().descriptor_flags()) {
case ICB_DESCRIPTOR_TYPE_SHORT:
{
TRACE(("Icb::Read: descriptor type -> short\n"));
AllocationDescriptorList<ShortDescriptorAccessor> list(this,
ShortDescriptorAccessor(fPartition));
RETURN(_Read(list, pos, buffer, length, block));
break;
}
case ICB_DESCRIPTOR_TYPE_LONG:
{
TRACE(("Icb::Read: descriptor type -> long\n"));
AllocationDescriptorList<LongDescriptorAccessor> list(this);
RETURN(_Read(list, pos, buffer, length, block));
break;
}
case ICB_DESCRIPTOR_TYPE_EXTENDED:
{
TRACE(("Icb::Read: descriptor type -> extended\n"));
// AllocationDescriptorList<ExtendedDescriptorAccessor> list(this, ExtendedDescriptorAccessor(0));
// RETURN(_Read(list, pos, buffer, length, block));
RETURN(B_ERROR);
break;
}
case ICB_DESCRIPTOR_TYPE_EMBEDDED:
{
TRACE(("Icb::Read: descriptor type: embedded\n"));
RETURN(B_ERROR);
break;
}
default:
TRACE(("Icb::Read: invalid icb descriptor flags! (flags = %d)\n",
_IcbTag().descriptor_flags()));
RETURN(B_BAD_VALUE);
break;
}
}
/*! \brief Does the dirty work of reading using the given DescriptorList object
to access the allocation descriptors properly.
*/
template <class DescriptorList>
status_t
Icb::_Read(DescriptorList &list, off_t pos, void *_buffer, size_t *length, uint32 *block)
{
TRACE(("Icb::_Read(): list = %p, pos = %" B_PRIdOFF ", buffer = %p, "
"length = %ld\n", &list, pos, _buffer, (length ? *length : 0)));
uint64 bytesLeftInFile = uint64(pos) > Length() ? 0 : Length() - pos;
size_t bytesLeft = (*length >= bytesLeftInFile) ? bytesLeftInFile : *length;
size_t bytesRead = 0;
Volume *volume = GetVolume();
status_t status = B_OK;
uint8 *buffer = (uint8 *)_buffer;
bool isFirstBlock = true;
while (bytesLeft > 0) {
TRACE(("Icb::_Read(): pos: %" B_PRIdOFF ", bytesLeft: %ld\n", pos,
bytesLeft));
long_address extent;
bool isEmpty = false;
status = list.FindExtent(pos, &extent, &isEmpty);
if (status != B_OK) {
TRACE_ERROR(("Icb::_Read: error finding extent for offset %"
B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos, status,
strerror(status)));
break;
}
TRACE(("Icb::_Read(): found extent for offset %" B_PRIdOFF ": (block: "
"%" B_PRIu32 ", partition: %d, length: %" B_PRIu32 ", type: %d)\n",
pos, extent.block(), extent.partition(), extent.length(),
extent.type()));
switch (extent.type()) {
case EXTENT_TYPE_RECORDED:
isEmpty = false;
break;
case EXTENT_TYPE_ALLOCATED:
case EXTENT_TYPE_UNALLOCATED:
isEmpty = true;
break;
default:
TRACE_ERROR(("Icb::_Read(): Invalid extent type found: %d\n",
extent.type()));
status = B_ERROR;
break;
}
if (status != B_OK)
break;
// Note the unmapped first block of the total read in
// the block output parameter if provided
if (isFirstBlock) {
isFirstBlock = false;
if (block)
*block = extent.block();
}
off_t blockOffset
= pos - off_t((pos >> volume->BlockShift()) << volume->BlockShift());
size_t readLength = volume->BlockSize() - blockOffset;
if (bytesLeft < readLength)
readLength = bytesLeft;
if (extent.length() < readLength)
readLength = extent.length();
TRACE(("Icb::_Read: reading block. offset = %" B_PRIdOFF
", length: %ld\n", blockOffset, readLength));
if (isEmpty) {
TRACE(("Icb::_Read: reading %ld empty bytes as zeros\n",
readLength));
memset(buffer, 0, readLength);
} else {
off_t diskBlock;
status = volume->MapBlock(extent, &diskBlock);
if (status != B_OK) {
TRACE_ERROR(("Icb::_Read: could not map extent\n"));
break;
}
TRACE(("Icb::_Read: %ld bytes from disk block %" B_PRIdOFF " using"
" block_cache_get_etc()\n", readLength, diskBlock));
uint8 *data = (uint8*)block_cache_get_etc(volume->BlockCache(),
diskBlock, 0, readLength);
if (data == NULL)
break;
memcpy(buffer, data + blockOffset, readLength);
block_cache_put(volume->BlockCache(), diskBlock);
}
bytesLeft -= readLength;
bytesRead += readLength;
pos += readLength;
buffer += readLength;
}
*length = bytesRead;
return status;
}
status_t
Icb::GetFileMap(off_t offset, size_t size, file_io_vec *vecs, size_t *count)
{
switch (_IcbTag().descriptor_flags()) {
case ICB_DESCRIPTOR_TYPE_SHORT:
{
AllocationDescriptorList<ShortDescriptorAccessor> list(this,
ShortDescriptorAccessor(0));
return _GetFileMap(list, offset, size, vecs, count);
}
case ICB_DESCRIPTOR_TYPE_LONG:
{
AllocationDescriptorList<LongDescriptorAccessor> list(this);
return _GetFileMap(list, offset, size, vecs, count);
}
case ICB_DESCRIPTOR_TYPE_EXTENDED:
case ICB_DESCRIPTOR_TYPE_EMBEDDED:
default:
{
// TODO: implement?
return B_UNSUPPORTED;
}
}
}
template<class DescriptorList>
status_t
Icb::_GetFileMap(DescriptorList &list, off_t offset, size_t size,
struct file_io_vec *vecs, size_t *count)
{
size_t index = 0;
size_t max = *count;
while (true) {
long_address extent;
bool isEmpty = false;
status_t status = list.FindExtent(offset, &extent, &isEmpty);
if (status != B_OK)
return status;
switch (extent.type()) {
case EXTENT_TYPE_RECORDED:
isEmpty = false;
break;
case EXTENT_TYPE_ALLOCATED:
case EXTENT_TYPE_UNALLOCATED:
isEmpty = true;
break;
default:
return B_ERROR;
}
if (isEmpty)
vecs[index].offset = -1;
else {
off_t diskBlock;
fVolume->MapBlock(extent, &diskBlock);
vecs[index].offset = diskBlock << fVolume->BlockShift();
}
off_t length = extent.length();
vecs[index].length = length;
offset += length;
size -= length;
index++;
if (index >= max || (off_t)size <= vecs[index - 1].length
|| offset >= (off_t)Length()) {
*count = index;
return index >= max ? B_BUFFER_OVERFLOW : B_OK;
}
}
// can never get here
return B_ERROR;
}
status_t
Icb::Find(const char *filename, ino_t *id)
{
TRACE(("Icb::Find: filename = `%s', id = %p\n", filename, id));
if (!filename || !id)
return B_BAD_VALUE;
DirectoryIterator *i;
status_t status = GetDirectoryIterator(&i);
if (status != B_OK)
return status;
ino_t entryId;
uint32 length = B_FILE_NAME_LENGTH;
char name[B_FILE_NAME_LENGTH];
bool foundIt = false;
while (i->GetNextEntry(name, &length, &entryId) == B_OK) {
if (strcmp(filename, name) == 0) {
foundIt = true;
break;
}
// reset overwritten length
length = B_FILE_NAME_LENGTH;
}
if (foundIt)
*id = entryId;
else
status = B_ENTRY_NOT_FOUND;
return status;
}
↑ V629 Consider inspecting the 'logicalBlock << fVolume->BlockShift()' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.