lf_hfs_raw_read_write.c [plain text]
#include "lf_hfs_raw_read_write.h"
#include "lf_hfs_utils.h"
#include "lf_hfs_fsops_handler.h"
#include "lf_hfs_file_mgr_internal.h"
#include "lf_hfs_file_extent_mapping.h"
#include "lf_hfs_vfsutils.h"
#include <UserFS/UserVFS.h>
#define MAX_READ_WRITE_LENGTH (0x7ffff000)
#define ZERO_BUF_SIZE (1024*1024)
static void* gpvZeroBuf = NULL;
int
raw_readwrite_get_cluster_from_offset( vnode_t psVnode, uint64_t uWantedOffset, uint64_t* puStartCluster, uint64_t* puInClusterOffset, uint64_t* puContigousClustersInBytes )
{
uint32_t uSectorsInCluster = HFSTOVCB(psVnode->sFSParams.vnfs_mp->psHfsmount)->blockSize / HFSTOVCB(psVnode->sFSParams.vnfs_mp->psHfsmount)->hfs_physical_block_size;
uint64_t uStartSector = 0;
size_t uAvailableBytes = 0;
int iErr = MapFileBlockC( HFSTOVCB(psVnode->sFSParams.vnfs_mp->psHfsmount), VTOF(psVnode), MAX_READ_WRITE_LENGTH, uWantedOffset, (daddr64_t*)&uStartSector, &uAvailableBytes );
if ( iErr != 0 )
{
return iErr;
}
if (puStartCluster) {
*puStartCluster = uStartSector / uSectorsInCluster;
}
if (puInClusterOffset) {
*puInClusterOffset = (uStartSector % uSectorsInCluster) * HFSTOVCB(psVnode->sFSParams.vnfs_mp->psHfsmount)->hfs_physical_block_size;
}
if (puContigousClustersInBytes) {
*puContigousClustersInBytes = uAvailableBytes;
}
return iErr;
}
errno_t raw_readwrite_read_mount( vnode_t psMountVnode, uint64_t uBlockN, uint64_t uClusterSize, void* pvBuf, uint64_t uBufLen, uint64_t *puActuallyRead, uint64_t* puReadStartCluster ) {
int iErr = 0;
int iFD = VNODE_TO_IFD(psMountVnode);
uint64_t uWantedOffset = uBlockN * uClusterSize;
if (puReadStartCluster)
*puReadStartCluster = uBlockN;
hfs_assert( uBufLen >= uClusterSize );
ssize_t iReadBytes = pread(iFD, pvBuf, uBufLen, uWantedOffset);
if ( iReadBytes != (ssize_t)uBufLen )
{
iErr = ( (iReadBytes < 0) ? errno : EIO );
HFSLogLevel_e eLogLevel = (VNODE_TO_UNMOUNT_HINT(psMountVnode)==UVFSUnmountHintForce)?LEVEL_DEBUG:LEVEL_ERROR;
LFHFS_LOG( eLogLevel, "raw_readwrite_read_mount failed [%d]\n", iErr );
}
if (puActuallyRead)
*puActuallyRead = iReadBytes;
return iErr;
}
errno_t raw_readwrite_write_mount( vnode_t psMountVnode, uint64_t uBlockN, uint64_t uClusterSize, void* pvBuf, uint64_t uBufLen, uint64_t *piActuallyWritten, uint64_t* puWriteStartCluster ) {
int iErr = 0;
int iFD = VNODE_TO_IFD(psMountVnode);
uint64_t uWantedOffset = uBlockN * uClusterSize;
ssize_t uActuallyWritten = 0;
if (puWriteStartCluster)
*puWriteStartCluster = uBlockN;
hfs_assert( uBufLen >= uClusterSize );
uActuallyWritten = pwrite(iFD, pvBuf, (size_t)uBufLen, uWantedOffset);
if ( uActuallyWritten != (ssize_t)uBufLen ) {
iErr = ( (uActuallyWritten < 0) ? errno : EIO );
HFSLogLevel_e eLogLevel = (VNODE_TO_UNMOUNT_HINT(psMountVnode)==UVFSUnmountHintForce)?LEVEL_DEBUG:LEVEL_ERROR;
LFHFS_LOG( eLogLevel, "raw_readwrite_write_mount failed [%d]\n", iErr );
}
if (piActuallyWritten)
*piActuallyWritten = uActuallyWritten;
return iErr;
}
errno_t
raw_readwrite_read( vnode_t psVnode, uint64_t uOffset, void* pvBuf, uint64_t uLength, size_t *piActuallyRead, uint64_t* puReadStartCluster )
{
errno_t iErr = 0;
uint64_t uClusterSize = psVnode->sFSParams.vnfs_mp->psHfsmount->blockSize;
uint64_t uFileSize = ((struct filefork *)VTOF(psVnode))->ff_data.cf_blocks * uClusterSize;
uint64_t uActuallyRead = 0;
bool bFirstLoop = true;
*piActuallyRead = 0;
while ( *piActuallyRead < uLength )
{
uint64_t uCurrentCluster = 0;
uint64_t uInClusterOffset = 0;
uint64_t uContigousClustersInBytes = 0;
iErr = raw_readwrite_get_cluster_from_offset( psVnode, uOffset, &uCurrentCluster, &uInClusterOffset, &uContigousClustersInBytes );
if ( iErr != 0 )
{
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_read: raw_readwrite_get_cluster_from_offset failed [%d]\n", iErr );
return iErr;
}
if ( bFirstLoop )
{
bFirstLoop = false;
if (puReadStartCluster)
*puReadStartCluster = uCurrentCluster;
}
if ( (uContigousClustersInBytes == 0) || (uOffset >= uFileSize) )
{
break;
}
uint64_t uBytesToRead = MIN(uFileSize - uOffset, uLength - *piActuallyRead);
iErr = raw_readwrite_read_internal( psVnode, uCurrentCluster, uContigousClustersInBytes, uOffset, uBytesToRead, pvBuf, &uActuallyRead );
if ( iErr != 0 )
{
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_read_internal: raw_readwrite_read_internal failed [%d]\n", iErr );
return iErr;
}
*piActuallyRead += uActuallyRead;
uOffset += uActuallyRead;
pvBuf = (uint8_t*)pvBuf + uActuallyRead;
}
return iErr;
}
errno_t
raw_readwrite_read_internal( vnode_t psVnode, uint64_t uCluster, uint64_t uContigousClustersInBytes,
uint64_t uOffset, uint64_t uBytesToRead, void* pvBuf, uint64_t *piActuallyRead )
{
errno_t iErr = 0;
int iFD = VNODE_TO_IFD(psVnode);
struct hfsmount *hfsmp = VTOHFS(psVnode);
uint64_t uClusterSize = hfsmp->blockSize;
uint64_t uSectorSize = hfsmp->hfs_logical_block_size;
uint64_t uBytesToCopy = 0;
uint64_t uReadOffset = FSOPS_GetOffsetFromClusterNum( psVnode, uCluster ) + ( ROUND_DOWN(uOffset, uSectorSize) % uClusterSize );
if ( (uOffset % uSectorSize) != 0 )
{
void* pvBuffer = hfs_malloc(uSectorSize);
if (pvBuffer == NULL)
{
return ENOMEM;
}
uint64_t uInSectorOffset = uOffset % uSectorSize;
uBytesToCopy = MIN(uSectorSize - uInSectorOffset, uBytesToRead);
ssize_t iReadBytes = pread( iFD, pvBuffer, uSectorSize, uReadOffset );
if ( iReadBytes != (ssize_t)uSectorSize )
{
iErr = ((iReadBytes < 0) ? errno : EIO);
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_read: pread failed to read wanted length\n" );
hfs_free(pvBuffer);
return iErr;
}
memcpy( (uint8_t *)pvBuf, (uint8_t *)pvBuffer+uInSectorOffset, uBytesToCopy );
hfs_free(pvBuffer);
}
else if (uBytesToRead < uSectorSize)
{
void* pvBuffer = hfs_malloc(uSectorSize);
if (pvBuffer == NULL)
{
return ENOMEM;
}
uBytesToCopy = uBytesToRead;
ssize_t iReadBytes = pread( iFD, pvBuffer, uSectorSize, uReadOffset );
if ( iReadBytes != (ssize_t)uSectorSize )
{
iErr = ((iReadBytes < 0) ? errno : EIO);
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_read: pread failed to read wanted length\n" );
hfs_free(pvBuffer);
return iErr;
}
memcpy((uint8_t *)pvBuf, pvBuffer, uBytesToCopy);
hfs_free(pvBuffer);
}
else
{
uint64_t uAvailSectors = uContigousClustersInBytes / uSectorSize;
uint64_t uRemainingSectors = uBytesToRead / uSectorSize;
uBytesToCopy = MIN(uAvailSectors, uRemainingSectors) * uSectorSize;
uBytesToCopy = MIN( uBytesToCopy, MAX_READ_WRITE_LENGTH );
assert( (uBytesToCopy % uSectorSize) == 0 );
assert( (uReadOffset % uSectorSize) == 0 );
ssize_t iReadBytes = pread( iFD,(uint8_t *)pvBuf, (size_t)uBytesToCopy, uReadOffset ) ;
if ( iReadBytes != (ssize_t)uBytesToCopy )
{
iErr = ((iReadBytes < 0) ? errno : EIO);
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_read: pread failed to read wanted length\n" );
return iErr;
}
}
*piActuallyRead = uBytesToCopy;
return iErr;
}
errno_t
raw_readwrite_write( vnode_t psVnode, uint64_t uOffset, void* pvBuf, uint64_t uLength, uint64_t *piActuallyWritten )
{
errno_t iErr = 0;
uint64_t uClusterSize = psVnode->sFSParams.vnfs_mp->psHfsmount->blockSize;
uint64_t uFileSize = ((struct filefork *)VTOF(psVnode))->ff_data.cf_blocks * uClusterSize;
uint64_t uActuallyWritten = 0;
*piActuallyWritten = 0;
while ( *piActuallyWritten < uLength )
{
uint64_t uCurrentCluster = 0;
uint64_t uInClusterOffset = 0;
uint64_t uContigousClustersInBytes = 0;
iErr = raw_readwrite_get_cluster_from_offset(psVnode, uOffset, &uCurrentCluster, &uInClusterOffset, &uContigousClustersInBytes );
if ( iErr != 0 )
{
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_write: raw_readwrite_get_cluster_from_offset failed [%d]\n", iErr );
return iErr;
}
if ( (uContigousClustersInBytes == 0) || (uOffset >= uFileSize) )
{
break;
}
uint64_t uBytesToWrite = MIN(uFileSize - uOffset, uLength - *piActuallyWritten);
iErr = raw_readwrite_write_internal( psVnode, uCurrentCluster, uContigousClustersInBytes, uOffset, uBytesToWrite, pvBuf, &uActuallyWritten );
if ( iErr != 0 )
{
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_read_internal: raw_readwrite_read_internal failed [%d]\n", iErr );
return iErr;
}
*piActuallyWritten += uActuallyWritten;
uOffset += uActuallyWritten;
pvBuf = (uint8_t*)pvBuf + uActuallyWritten;
}
return iErr;
}
errno_t
raw_readwrite_write_internal( vnode_t psVnode, uint64_t uCluster, uint64_t uContigousClustersInBytes,
uint64_t uOffset, uint64_t uBytesToWrite, void* pvBuf, uint64_t *piActuallyWritten )
{
errno_t iErr = 0;
int iFD = VNODE_TO_IFD(psVnode);
struct hfsmount *hfsmp = VTOHFS(psVnode);
uint64_t uClusterSize = hfsmp->blockSize;
uint64_t uSectorSize = hfsmp->hfs_logical_block_size;
uint64_t uBytesToCopy = 0;
uint64_t uWriteOffset = FSOPS_GetOffsetFromClusterNum( psVnode, uCluster ) + ( ROUND_DOWN(uOffset, uSectorSize) % uClusterSize );
if ( (uOffset % uSectorSize) != 0 )
{
void* pvBuffer = hfs_malloc(uSectorSize);
if (pvBuffer == NULL)
{
return ENOMEM;
}
uint64_t uInSectorOffset = uOffset % uSectorSize;
uBytesToCopy = MIN( uBytesToWrite, uSectorSize - uInSectorOffset );
ssize_t iReadBytes = pread(iFD, pvBuffer, uSectorSize, uWriteOffset);
if ( iReadBytes != (ssize_t)uSectorSize )
{
iErr = (iReadBytes < 0) ? errno : EIO;
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_write: pread failed to read wanted length\n" );
hfs_free(pvBuffer);
return iErr;
}
memcpy((uint8_t *)pvBuffer+uInSectorOffset, pvBuf, uBytesToCopy);
ssize_t iWriteBytes = pwrite(iFD, pvBuffer, uSectorSize, uWriteOffset);
if ( iWriteBytes != (ssize_t)uSectorSize )
{
iErr = (iWriteBytes < 0) ? errno : EIO;
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_write: pwrite failed to write wanted length\n" );
hfs_free(pvBuffer);
return iErr;
}
hfs_free(pvBuffer);
}
else if ( uBytesToWrite < uSectorSize )
{
void* pvBuffer = hfs_malloc(uSectorSize);
if (pvBuffer == NULL)
{
return ENOMEM;
}
uBytesToCopy = uBytesToWrite;
ssize_t iReadBytes = pread(iFD, pvBuffer, uSectorSize, uWriteOffset);
if ( iReadBytes != (ssize_t)uSectorSize )
{
iErr = (iReadBytes < 0) ? errno : EIO;
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_write: pread failed to read wanted length\n" );
hfs_free(pvBuffer);
return iErr;
}
memcpy(pvBuffer, (uint8_t *)pvBuf, uBytesToCopy);
ssize_t iWriteBytes = pwrite(iFD, pvBuffer, uSectorSize, uWriteOffset);
if ( iWriteBytes != (ssize_t)uSectorSize)
{
iErr = (iWriteBytes < 0) ? errno : EIO;
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_write: pwrite failed to write wanted length\n" );
hfs_free(pvBuffer);
return iErr;
}
hfs_free(pvBuffer);
}
else
{
uint64_t uAvailSectors = uContigousClustersInBytes / uSectorSize;
uint64_t uRemainingSectors = uBytesToWrite / uSectorSize;
uBytesToCopy = MIN(uAvailSectors, uRemainingSectors) * uSectorSize;
uBytesToCopy = MIN( uBytesToCopy, MAX_READ_WRITE_LENGTH );
assert( (uBytesToCopy % uSectorSize) == 0 );
assert( (uWriteOffset % uSectorSize) == 0 );
ssize_t iWriteBytes = pwrite(iFD, (uint8_t *)pvBuf, uBytesToCopy, uWriteOffset) ;
if ( iWriteBytes != (ssize_t) uBytesToCopy)
{
iErr = (iWriteBytes < 0) ? errno : EIO;
LFHFS_LOG( LEVEL_ERROR, "raw_readwrite_write: pwrite failed to write wanted length\n" );
return iErr;
}
}
*piActuallyWritten = uBytesToCopy;
return iErr;
}
int
raw_readwrite_zero_fill_init()
{
if ( gpvZeroBuf )
{
return 0;
}
gpvZeroBuf = hfs_malloc( ZERO_BUF_SIZE );
if ( gpvZeroBuf == NULL )
{
return ENOMEM;
}
memset( gpvZeroBuf, 0, ZERO_BUF_SIZE );
return 0;
}
void
raw_readwrite_zero_fill_de_init()
{
if ( gpvZeroBuf )
{
hfs_free( gpvZeroBuf );
}
gpvZeroBuf = NULL;
}
int
raw_readwrite_zero_fill_fill( hfsmount_t* psMount, uint64_t uBlock, uint32_t uContigBlocks )
{
int iErr = 0;
int64_t lWriteSize = 0;
uint64_t uCurWriteOffset = 0;
uint64_t uCurWriteLen = 0;
uint64_t uDataWriten = 0;
if ( gpvZeroBuf == NULL )
{
iErr = EINVAL;
goto exit;
}
uint64_t uLength = uContigBlocks*psMount->blockSize;
uint64_t uOffset = psMount->hfsPlusIOPosOffset + uBlock*psMount->blockSize;
while ( uDataWriten < uLength )
{
uCurWriteOffset = uOffset+uDataWriten;
uCurWriteLen = MIN( (uLength - uDataWriten), ZERO_BUF_SIZE );
lWriteSize = pwrite( psMount->hfs_devvp->psFSRecord->iFD, gpvZeroBuf, uCurWriteLen, uCurWriteOffset );
if ( lWriteSize != (int64_t)uCurWriteLen )
{
iErr = errno;
goto exit;
}
uDataWriten += uCurWriteLen;
}
exit:
return iErr;
}
errno_t
raw_readwrite_zero_fill_last_block_suffix( vnode_t psVnode )
{
int iErr = 0;
int iFD = (VPTOFSRECORD(psVnode))->iFD;
struct filefork *fp = VTOF(psVnode);
uint32_t uBlockSize = HFSTOVCB(psVnode->sFSParams.vnfs_mp->psHfsmount)->blockSize;
uint32_t uBytesToKeep = fp->ff_size % uBlockSize;
uint64_t uBlockN = 0;
uint64_t uContigousClustersInBytes;
uint64_t uInClusterOffset;
uint8_t* puClusterData = NULL;
iErr = raw_readwrite_get_cluster_from_offset(psVnode, fp->ff_size - uBytesToKeep, &uBlockN, &uInClusterOffset, &uContigousClustersInBytes);
if ( iErr != 0 )
{
goto exit;
}
puClusterData = hfs_malloc( uBlockSize );
if ( puClusterData == NULL )
{
iErr = ENOMEM;
goto exit;
}
size_t uBytesRead = pread( iFD, puClusterData, uBlockSize, FSOPS_GetOffsetFromClusterNum( psVnode, uBlockN ) );
if ( uBytesRead != uBlockSize )
{
iErr = errno;
goto exit;
}
memset( puClusterData+uBytesToKeep, 0, uBlockSize-uBytesToKeep );
size_t uBytesWrite = pwrite( iFD, puClusterData, uBlockSize, FSOPS_GetOffsetFromClusterNum( psVnode, uBlockN ) );
if ( uBytesWrite != uBlockSize )
{
iErr = errno;
goto exit;
}
exit:
if ( puClusterData )
hfs_free( puClusterData );
return iErr;
}