1 /* Copyright © 2017-2018 Apple Inc. All rights reserved.
3 * lf_hfs_raw_read_write.c
6 * Created by Or Haimovich on 19/3/18.
9 #include "lf_hfs_raw_read_write.h"
10 #include "lf_hfs_utils.h"
11 #include "lf_hfs_fsops_handler.h"
12 #include "lf_hfs_file_mgr_internal.h"
13 #include "lf_hfs_file_extent_mapping.h"
14 #include "lf_hfs_vfsutils.h"
15 #include <UserFS/UserVFS.h>
17 #define MAX_READ_WRITE_LENGTH (0x7ffff000)
19 #define ZERO_BUF_SIZE (1024*1024)
21 static void* gpvZeroBuf
= NULL
;
25 raw_readwrite_get_cluster_from_offset( vnode_t psVnode
, uint64_t uWantedOffset
, uint64_t* puStartCluster
, uint64_t* puInClusterOffset
, uint64_t* puContigousClustersInBytes
)
27 uint32_t uSectorsInCluster
= HFSTOVCB(psVnode
->sFSParams
.vnfs_mp
->psHfsmount
)->blockSize
/ HFSTOVCB(psVnode
->sFSParams
.vnfs_mp
->psHfsmount
)->hfs_physical_block_size
;
29 uint64_t uStartSector
= 0;
30 size_t uAvailableBytes
= 0;
32 int iErr
= MapFileBlockC( HFSTOVCB(psVnode
->sFSParams
.vnfs_mp
->psHfsmount
), VTOF(psVnode
), MAX_READ_WRITE_LENGTH
, uWantedOffset
, (daddr64_t
*)&uStartSector
, &uAvailableBytes
);
39 *puStartCluster
= uStartSector
/ uSectorsInCluster
;
42 if (puInClusterOffset
) {
43 *puInClusterOffset
= (uStartSector
% uSectorsInCluster
) * HFSTOVCB(psVnode
->sFSParams
.vnfs_mp
->psHfsmount
)->hfs_physical_block_size
;
46 if (puContigousClustersInBytes
) {
47 *puContigousClustersInBytes
= uAvailableBytes
;
52 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
) {
54 int iFD
= VNODE_TO_IFD(psMountVnode
);
55 uint64_t uWantedOffset
= uBlockN
* uClusterSize
;
57 if (puReadStartCluster
)
58 *puReadStartCluster
= uBlockN
;
60 hfs_assert( uBufLen
>= uClusterSize
);
62 ssize_t iReadBytes
= pread(iFD
, pvBuf
, uBufLen
, uWantedOffset
);
63 if ( iReadBytes
!= (ssize_t
)uBufLen
)
65 iErr
= ( (iReadBytes
< 0) ? errno
: EIO
);
66 HFSLogLevel_e eLogLevel
= (VNODE_TO_UNMOUNT_HINT(psMountVnode
)==UVFSUnmountHintForce
)?LEVEL_DEBUG
:LEVEL_ERROR
;
67 LFHFS_LOG( eLogLevel
, "raw_readwrite_read_mount failed [%d]\n", iErr
);
71 *puActuallyRead
= iReadBytes
;
76 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
) {
78 int iFD
= VNODE_TO_IFD(psMountVnode
);
79 uint64_t uWantedOffset
= uBlockN
* uClusterSize
;
80 ssize_t uActuallyWritten
= 0;
82 if (puWriteStartCluster
)
83 *puWriteStartCluster
= uBlockN
;
85 hfs_assert( uBufLen
>= uClusterSize
);
87 uActuallyWritten
= pwrite(iFD
, pvBuf
, (size_t)uBufLen
, uWantedOffset
);
88 if ( uActuallyWritten
!= (ssize_t
)uBufLen
) {
89 iErr
= ( (uActuallyWritten
< 0) ? errno
: EIO
);
90 HFSLogLevel_e eLogLevel
= (VNODE_TO_UNMOUNT_HINT(psMountVnode
)==UVFSUnmountHintForce
)?LEVEL_DEBUG
:LEVEL_ERROR
;
91 LFHFS_LOG( eLogLevel
, "raw_readwrite_write_mount failed [%d]\n", iErr
);
94 if (piActuallyWritten
)
95 *piActuallyWritten
= uActuallyWritten
;
101 raw_readwrite_read( vnode_t psVnode
, uint64_t uOffset
, void* pvBuf
, uint64_t uLength
, size_t *piActuallyRead
, uint64_t* puReadStartCluster
)
104 uint64_t uClusterSize
= psVnode
->sFSParams
.vnfs_mp
->psHfsmount
->blockSize
;
105 uint64_t uFileSize
= ((struct filefork
*)VTOF(psVnode
))->ff_data
.cf_blocks
* uClusterSize
;
106 uint64_t uActuallyRead
= 0;
107 bool bFirstLoop
= true;
110 while ( *piActuallyRead
< uLength
)
112 uint64_t uCurrentCluster
= 0;
113 uint64_t uInClusterOffset
= 0;
114 uint64_t uContigousClustersInBytes
= 0;
116 // Look for the location to read
117 iErr
= raw_readwrite_get_cluster_from_offset( psVnode
, uOffset
, &uCurrentCluster
, &uInClusterOffset
, &uContigousClustersInBytes
);
120 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read: raw_readwrite_get_cluster_from_offset failed [%d]\n", iErr
);
127 if (puReadStartCluster
)
128 *puReadStartCluster
= uCurrentCluster
;
131 // Stop reading if we've reached the end of the file
132 if ( (uContigousClustersInBytes
== 0) || (uOffset
>= uFileSize
) )
137 uint64_t uBytesToRead
= MIN(uFileSize
- uOffset
, uLength
- *piActuallyRead
);
140 iErr
= raw_readwrite_read_internal( psVnode
, uCurrentCluster
, uContigousClustersInBytes
, uOffset
, uBytesToRead
, pvBuf
, &uActuallyRead
);
143 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read_internal: raw_readwrite_read_internal failed [%d]\n", iErr
);
147 // Update the amount of bytes alreay read
148 *piActuallyRead
+= uActuallyRead
;
149 // Update file offset
150 uOffset
+= uActuallyRead
;
151 // Update buffer offset
152 pvBuf
= (uint8_t*)pvBuf
+ uActuallyRead
;
159 raw_readwrite_read_internal( vnode_t psVnode
, uint64_t uCluster
, uint64_t uContigousClustersInBytes
,
160 uint64_t uOffset
, uint64_t uBytesToRead
, void* pvBuf
, uint64_t *piActuallyRead
)
163 int iFD
= VNODE_TO_IFD(psVnode
);
164 struct hfsmount
*hfsmp
= VTOHFS(psVnode
);
165 uint64_t uClusterSize
= hfsmp
->blockSize
;
166 uint64_t uSectorSize
= hfsmp
->hfs_logical_block_size
;
167 uint64_t uBytesToCopy
= 0;
169 // Calculate offset - offset by sector and need to add the offset by sector
170 uint64_t uReadOffset
= FSOPS_GetOffsetFromClusterNum( psVnode
, uCluster
) + ( ROUND_DOWN(uOffset
, uSectorSize
) % uClusterSize
);
172 // If offset not align to sector size, need to read only 1 sector and memcpy its end
173 if ( (uOffset
% uSectorSize
) != 0 )
175 void* pvBuffer
= hfs_malloc(uSectorSize
);
176 if (pvBuffer
== NULL
)
181 uint64_t uInSectorOffset
= uOffset
% uSectorSize
;
182 uBytesToCopy
= MIN(uSectorSize
- uInSectorOffset
, uBytesToRead
);
184 // Read the content of the file
185 ssize_t iReadBytes
= pread( iFD
, pvBuffer
, uSectorSize
, uReadOffset
);
186 if ( iReadBytes
!= (ssize_t
)uSectorSize
)
188 iErr
= ((iReadBytes
< 0) ? errno
: EIO
);
189 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read: pread failed to read wanted length\n" );
193 memcpy( (uint8_t *)pvBuf
, (uint8_t *)pvBuffer
+uInSectorOffset
, uBytesToCopy
);
196 // If uBytesToRead < uClusterSize, need to read 1 sector and memcpy the begining
197 else if (uBytesToRead
< uSectorSize
)
199 void* pvBuffer
= hfs_malloc(uSectorSize
);
200 if (pvBuffer
== NULL
)
205 uBytesToCopy
= uBytesToRead
;
207 // Read the content of the file
208 ssize_t iReadBytes
= pread( iFD
, pvBuffer
, uSectorSize
, uReadOffset
);
209 if ( iReadBytes
!= (ssize_t
)uSectorSize
)
211 iErr
= ((iReadBytes
< 0) ? errno
: EIO
);
212 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read: pread failed to read wanted length\n" );
217 memcpy((uint8_t *)pvBuf
, pvBuffer
, uBytesToCopy
);
220 // Can read buffer size chunk
223 uint64_t uAvailSectors
= uContigousClustersInBytes
/ uSectorSize
;
224 uint64_t uRemainingSectors
= uBytesToRead
/ uSectorSize
;
226 uBytesToCopy
= MIN(uAvailSectors
, uRemainingSectors
) * uSectorSize
;
227 uBytesToCopy
= MIN( uBytesToCopy
, MAX_READ_WRITE_LENGTH
);
229 assert( (uBytesToCopy
% uSectorSize
) == 0 );
230 assert( (uReadOffset
% uSectorSize
) == 0 );
232 ssize_t iReadBytes
= pread( iFD
,(uint8_t *)pvBuf
, (size_t)uBytesToCopy
, uReadOffset
) ;
233 if ( iReadBytes
!= (ssize_t
)uBytesToCopy
)
235 iErr
= ((iReadBytes
< 0) ? errno
: EIO
);
236 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read: pread failed to read wanted length\n" );
241 // Update the amount of bytes alreay read
242 *piActuallyRead
= uBytesToCopy
;
248 raw_readwrite_write( vnode_t psVnode
, uint64_t uOffset
, void* pvBuf
, uint64_t uLength
, uint64_t *piActuallyWritten
)
251 uint64_t uClusterSize
= psVnode
->sFSParams
.vnfs_mp
->psHfsmount
->blockSize
;
252 uint64_t uFileSize
= ((struct filefork
*)VTOF(psVnode
))->ff_data
.cf_blocks
* uClusterSize
;
253 uint64_t uActuallyWritten
= 0;
255 *piActuallyWritten
= 0;
257 // Fill the buffer until the buffer is full or till the end of the file
258 while ( *piActuallyWritten
< uLength
)
260 uint64_t uCurrentCluster
= 0;
261 uint64_t uInClusterOffset
= 0;
262 uint64_t uContigousClustersInBytes
= 0;
264 iErr
= raw_readwrite_get_cluster_from_offset(psVnode
, uOffset
, &uCurrentCluster
, &uInClusterOffset
, &uContigousClustersInBytes
);
267 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: raw_readwrite_get_cluster_from_offset failed [%d]\n", iErr
);
271 // Stop writing if we've reached the end of the file
272 if ( (uContigousClustersInBytes
== 0) || (uOffset
>= uFileSize
) )
277 /* Calculate how many bytes are still missing to add to the device
278 * If offset near end of file need to set only (uFileSize - uOffset)
279 * else need to write as much as left (uLength - uAcctuallyRead)
281 uint64_t uBytesToWrite
= MIN(uFileSize
- uOffset
, uLength
- *piActuallyWritten
);
284 iErr
= raw_readwrite_write_internal( psVnode
, uCurrentCluster
, uContigousClustersInBytes
, uOffset
, uBytesToWrite
, pvBuf
, &uActuallyWritten
);
287 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read_internal: raw_readwrite_read_internal failed [%d]\n", iErr
);
291 // Update the amount of bytes alreay written
292 *piActuallyWritten
+= uActuallyWritten
;
293 // Update file offset
294 uOffset
+= uActuallyWritten
;
295 // Update buffer offset
296 pvBuf
= (uint8_t*)pvBuf
+ uActuallyWritten
;
303 raw_readwrite_write_internal( vnode_t psVnode
, uint64_t uCluster
, uint64_t uContigousClustersInBytes
,
304 uint64_t uOffset
, uint64_t uBytesToWrite
, void* pvBuf
, uint64_t *piActuallyWritten
)
307 int iFD
= VNODE_TO_IFD(psVnode
);
308 struct hfsmount
*hfsmp
= VTOHFS(psVnode
);
309 uint64_t uClusterSize
= hfsmp
->blockSize
;
310 uint64_t uSectorSize
= hfsmp
->hfs_logical_block_size
;
311 uint64_t uBytesToCopy
= 0;
313 // Calculate offset - offset by sector and need to add the offset by sector
314 uint64_t uWriteOffset
= FSOPS_GetOffsetFromClusterNum( psVnode
, uCluster
) + ( ROUND_DOWN(uOffset
, uSectorSize
) % uClusterSize
);
316 // If offset not align to sector size, need to read the existing data
317 // memcpy it's beginning and write back to the device
318 if ( (uOffset
% uSectorSize
) != 0 )
320 void* pvBuffer
= hfs_malloc(uSectorSize
);
321 if (pvBuffer
== NULL
)
326 uint64_t uInSectorOffset
= uOffset
% uSectorSize
;
327 uBytesToCopy
= MIN( uBytesToWrite
, uSectorSize
- uInSectorOffset
);
329 // Read the content of the existing file
330 ssize_t iReadBytes
= pread(iFD
, pvBuffer
, uSectorSize
, uWriteOffset
);
331 if ( iReadBytes
!= (ssize_t
)uSectorSize
)
333 iErr
= (iReadBytes
< 0) ? errno
: EIO
;
334 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: pread failed to read wanted length\n" );
339 // memcpy the data from the given buffer
340 memcpy((uint8_t *)pvBuffer
+uInSectorOffset
, pvBuf
, uBytesToCopy
);
342 // Write the data into the device
343 ssize_t iWriteBytes
= pwrite(iFD
, pvBuffer
, uSectorSize
, uWriteOffset
);
344 if ( iWriteBytes
!= (ssize_t
)uSectorSize
)
346 iErr
= (iWriteBytes
< 0) ? errno
: EIO
;
347 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: pwrite failed to write wanted length\n" );
354 // If uBytesToWrite < uSectorSize, need to R/M/W 1 sector.
355 else if ( uBytesToWrite
< uSectorSize
)
357 void* pvBuffer
= hfs_malloc(uSectorSize
);
358 if (pvBuffer
== NULL
)
363 uBytesToCopy
= uBytesToWrite
;
365 // Read the content of the existing file
366 ssize_t iReadBytes
= pread(iFD
, pvBuffer
, uSectorSize
, uWriteOffset
);
367 if ( iReadBytes
!= (ssize_t
)uSectorSize
)
369 iErr
= (iReadBytes
< 0) ? errno
: EIO
;
370 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: pread failed to read wanted length\n" );
375 // memcpy the last data
376 memcpy(pvBuffer
, (uint8_t *)pvBuf
, uBytesToCopy
);
378 // Write the content to the file
379 ssize_t iWriteBytes
= pwrite(iFD
, pvBuffer
, uSectorSize
, uWriteOffset
);
380 if ( iWriteBytes
!= (ssize_t
)uSectorSize
)
382 iErr
= (iWriteBytes
< 0) ? errno
: EIO
;
383 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: pwrite failed to write wanted length\n" );
390 // Can write buffer size chunk
393 uint64_t uAvailSectors
= uContigousClustersInBytes
/ uSectorSize
;
394 uint64_t uRemainingSectors
= uBytesToWrite
/ uSectorSize
;
396 uBytesToCopy
= MIN(uAvailSectors
, uRemainingSectors
) * uSectorSize
;
397 uBytesToCopy
= MIN( uBytesToCopy
, MAX_READ_WRITE_LENGTH
);
399 assert( (uBytesToCopy
% uSectorSize
) == 0 );
400 assert( (uWriteOffset
% uSectorSize
) == 0 );
402 ssize_t iWriteBytes
= pwrite(iFD
, (uint8_t *)pvBuf
, uBytesToCopy
, uWriteOffset
) ;
403 if ( iWriteBytes
!= (ssize_t
) uBytesToCopy
)
405 iErr
= (iWriteBytes
< 0) ? errno
: EIO
;
406 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: pwrite failed to write wanted length\n" );
411 // Update the amount of bytes alreay written
412 *piActuallyWritten
= uBytesToCopy
;
418 raw_readwrite_zero_fill_init()
425 gpvZeroBuf
= hfs_malloc( ZERO_BUF_SIZE
);
426 if ( gpvZeroBuf
== NULL
)
431 memset( gpvZeroBuf
, 0, ZERO_BUF_SIZE
);
437 raw_readwrite_zero_fill_de_init()
441 hfs_free( gpvZeroBuf
);
448 raw_readwrite_zero_fill_fill( hfsmount_t
* psMount
, uint64_t uBlock
, uint32_t uContigBlocks
)
451 int64_t lWriteSize
= 0;
452 uint64_t uCurWriteOffset
= 0;
453 uint64_t uCurWriteLen
= 0;
454 uint64_t uDataWriten
= 0;
456 if ( gpvZeroBuf
== NULL
)
462 uint64_t uLength
= uContigBlocks
*psMount
->blockSize
;
463 uint64_t uOffset
= psMount
->hfsPlusIOPosOffset
+ uBlock
*psMount
->blockSize
;
465 while ( uDataWriten
< uLength
)
467 uCurWriteOffset
= uOffset
+uDataWriten
;
468 uCurWriteLen
= MIN( (uLength
- uDataWriten
), ZERO_BUF_SIZE
);
470 lWriteSize
= pwrite( psMount
->hfs_devvp
->psFSRecord
->iFD
, gpvZeroBuf
, uCurWriteLen
, uCurWriteOffset
);
471 if ( lWriteSize
!= (int64_t)uCurWriteLen
)
477 uDataWriten
+= uCurWriteLen
;
485 raw_readwrite_zero_fill_last_block_suffix( vnode_t psVnode
)
489 int iFD
= (VPTOFSRECORD(psVnode
))->iFD
;
490 struct filefork
*fp
= VTOF(psVnode
);
491 uint32_t uBlockSize
= HFSTOVCB(psVnode
->sFSParams
.vnfs_mp
->psHfsmount
)->blockSize
;
492 uint32_t uBytesToKeep
= fp
->ff_size
% uBlockSize
;
493 uint64_t uBlockN
= 0;
494 uint64_t uContigousClustersInBytes
;
495 uint64_t uInClusterOffset
;
497 uint8_t* puClusterData
= NULL
;
499 iErr
= raw_readwrite_get_cluster_from_offset(psVnode
, fp
->ff_size
- uBytesToKeep
, &uBlockN
, &uInClusterOffset
, &uContigousClustersInBytes
);
505 // Allocate buffer for cluster.
506 puClusterData
= hfs_malloc( uBlockSize
);
507 if ( puClusterData
== NULL
)
513 // Read the last cluster.
514 size_t uBytesRead
= pread( iFD
, puClusterData
, uBlockSize
, FSOPS_GetOffsetFromClusterNum( psVnode
, uBlockN
) );
515 if ( uBytesRead
!= uBlockSize
)
521 memset( puClusterData
+uBytesToKeep
, 0, uBlockSize
-uBytesToKeep
);
523 // Write the last cluster.
524 size_t uBytesWrite
= pwrite( iFD
, puClusterData
, uBlockSize
, FSOPS_GetOffsetFromClusterNum( psVnode
, uBlockN
) );
525 if ( uBytesWrite
!= uBlockSize
)
533 hfs_free( puClusterData
);