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"
16 #define MAX_READ_WRITE_LENGTH (0x7ffff000)
18 #define ZERO_BUF_SIZE (1024*1024)
20 static void* gpvZeroBuf
= NULL
;
24 raw_readwrite_get_cluster_from_offset( vnode_t psVnode
, uint64_t uWantedOffset
, uint64_t* puStartCluster
, uint64_t* puInClusterOffset
, uint64_t* puContigousClustersInBytes
)
26 uint32_t uSectorsInCluster
= HFSTOVCB(psVnode
->sFSParams
.vnfs_mp
->psHfsmount
)->blockSize
/ HFSTOVCB(psVnode
->sFSParams
.vnfs_mp
->psHfsmount
)->hfs_physical_block_size
;
28 uint64_t uStartSector
= 0;
29 size_t uAvailableBytes
= 0;
31 int iErr
= MapFileBlockC( HFSTOVCB(psVnode
->sFSParams
.vnfs_mp
->psHfsmount
), VTOF(psVnode
), MAX_READ_WRITE_LENGTH
, uWantedOffset
, (daddr64_t
*)&uStartSector
, &uAvailableBytes
);
38 *puStartCluster
= uStartSector
/ uSectorsInCluster
;
41 if (puInClusterOffset
) {
42 *puInClusterOffset
= (uStartSector
% uSectorsInCluster
) * HFSTOVCB(psVnode
->sFSParams
.vnfs_mp
->psHfsmount
)->hfs_physical_block_size
;
45 if (puContigousClustersInBytes
) {
46 *puContigousClustersInBytes
= uAvailableBytes
;
51 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
) {
53 int iFD
= VNODE_TO_IFD(psMountVnode
);
54 uint64_t uWantedOffset
= uBlockN
* uClusterSize
;
56 if (puReadStartCluster
)
57 *puReadStartCluster
= uBlockN
;
59 hfs_assert( uBufLen
>= uClusterSize
);
61 ssize_t iReadBytes
= pread(iFD
, pvBuf
, uBufLen
, uWantedOffset
);
62 if ( iReadBytes
!= (ssize_t
)uBufLen
)
64 iErr
= ( (iReadBytes
< 0) ? errno
: EIO
);
65 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read_mount failed [%d]\n", iErr
);
69 *puActuallyRead
= iReadBytes
;
74 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
) {
76 int iFD
= VNODE_TO_IFD(psMountVnode
);
77 uint64_t uWantedOffset
= uBlockN
* uClusterSize
;
78 ssize_t uActuallyWritten
= 0;
80 if (puWriteStartCluster
)
81 *puWriteStartCluster
= uBlockN
;
83 hfs_assert( uBufLen
>= uClusterSize
);
85 uActuallyWritten
= pwrite(iFD
, pvBuf
, (size_t)uBufLen
, uWantedOffset
);
86 if ( uActuallyWritten
!= (ssize_t
)uBufLen
) {
87 iErr
= ( (uActuallyWritten
< 0) ? errno
: EIO
);
88 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write_mount failed [%d]\n", iErr
);
91 if (piActuallyWritten
)
92 *piActuallyWritten
= uActuallyWritten
;
98 raw_readwrite_read( vnode_t psVnode
, uint64_t uOffset
, void* pvBuf
, uint64_t uLength
, size_t *piActuallyRead
, uint64_t* puReadStartCluster
)
101 uint64_t uClusterSize
= psVnode
->sFSParams
.vnfs_mp
->psHfsmount
->blockSize
;
102 uint64_t uFileSize
= ((struct filefork
*)VTOF(psVnode
))->ff_data
.cf_blocks
* uClusterSize
;
103 uint64_t uActuallyRead
= 0;
104 bool bFirstLoop
= true;
107 while ( *piActuallyRead
< uLength
)
109 uint64_t uCurrentCluster
= 0;
110 uint64_t uInClusterOffset
= 0;
111 uint64_t uContigousClustersInBytes
= 0;
113 // Look for the location to read
114 iErr
= raw_readwrite_get_cluster_from_offset( psVnode
, uOffset
, &uCurrentCluster
, &uInClusterOffset
, &uContigousClustersInBytes
);
117 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read: raw_readwrite_get_cluster_from_offset failed [%d]\n", iErr
);
124 if (puReadStartCluster
)
125 *puReadStartCluster
= uCurrentCluster
;
128 // Stop reading if we've reached the end of the file
129 if ( (uContigousClustersInBytes
== 0) || (uOffset
>= uFileSize
) )
134 uint64_t uBytesToRead
= MIN(uFileSize
- uOffset
, uLength
- *piActuallyRead
);
137 iErr
= raw_readwrite_read_internal( psVnode
, uCurrentCluster
, uContigousClustersInBytes
, uOffset
, uBytesToRead
, pvBuf
, &uActuallyRead
);
140 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read_internal: raw_readwrite_read_internal failed [%d]\n", iErr
);
144 // Update the amount of bytes alreay read
145 *piActuallyRead
+= uActuallyRead
;
146 // Update file offset
147 uOffset
+= uActuallyRead
;
148 // Update buffer offset
149 pvBuf
= (uint8_t*)pvBuf
+ uActuallyRead
;
156 raw_readwrite_read_internal( vnode_t psVnode
, uint64_t uCluster
, uint64_t uContigousClustersInBytes
,
157 uint64_t uOffset
, uint64_t uBytesToRead
, void* pvBuf
, uint64_t *piActuallyRead
)
160 int iFD
= VNODE_TO_IFD(psVnode
);
161 struct hfsmount
*hfsmp
= VTOHFS(psVnode
);
162 uint64_t uClusterSize
= hfsmp
->blockSize
;
163 uint64_t uSectorSize
= hfsmp
->hfs_logical_block_size
;
164 uint64_t uBytesToCopy
= 0;
166 // Calculate offset - offset by sector and need to add the offset by sector
167 uint64_t uReadOffset
= FSOPS_GetOffsetFromClusterNum( psVnode
, uCluster
) + ( ROUND_DOWN(uOffset
, uSectorSize
) % uClusterSize
);
169 // If offset not align to sector size, need to read only 1 sector and memcpy its end
170 if ( (uOffset
% uSectorSize
) != 0 )
172 void* pvBuffer
= hfs_malloc(uSectorSize
);
173 if (pvBuffer
== NULL
)
178 uint64_t uInSectorOffset
= uOffset
% uSectorSize
;
179 uBytesToCopy
= MIN(uSectorSize
- uInSectorOffset
, uBytesToRead
);
181 // Read the content of the file
182 ssize_t iReadBytes
= pread( iFD
, pvBuffer
, uSectorSize
, uReadOffset
);
183 if ( iReadBytes
!= (ssize_t
)uSectorSize
)
185 iErr
= ((iReadBytes
< 0) ? errno
: EIO
);
186 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read: pread failed to read wanted length\n" );
190 memcpy( (uint8_t *)pvBuf
, (uint8_t *)pvBuffer
+uInSectorOffset
, uBytesToCopy
);
193 // If uBytesToRead < uClusterSize, need to read 1 sector and memcpy the begining
194 else if (uBytesToRead
< uSectorSize
)
196 void* pvBuffer
= hfs_malloc(uSectorSize
);
197 if (pvBuffer
== NULL
)
202 uBytesToCopy
= uBytesToRead
;
204 // Read the content of the file
205 ssize_t iReadBytes
= pread( iFD
, pvBuffer
, uSectorSize
, uReadOffset
);
206 if ( iReadBytes
!= (ssize_t
)uSectorSize
)
208 iErr
= ((iReadBytes
< 0) ? errno
: EIO
);
209 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read: pread failed to read wanted length\n" );
214 memcpy((uint8_t *)pvBuf
, pvBuffer
, uBytesToCopy
);
217 // Can read buffer size chunk
220 uint64_t uAvailSectors
= uContigousClustersInBytes
/ uSectorSize
;
221 uint64_t uRemainingSectors
= uBytesToRead
/ uSectorSize
;
223 uBytesToCopy
= MIN(uAvailSectors
, uRemainingSectors
) * uSectorSize
;
224 uBytesToCopy
= MIN( uBytesToCopy
, MAX_READ_WRITE_LENGTH
);
226 assert( (uBytesToCopy
% uSectorSize
) == 0 );
227 assert( (uReadOffset
% uSectorSize
) == 0 );
229 ssize_t iReadBytes
= pread( iFD
,(uint8_t *)pvBuf
, (size_t)uBytesToCopy
, uReadOffset
) ;
230 if ( iReadBytes
!= (ssize_t
)uBytesToCopy
)
232 iErr
= ((iReadBytes
< 0) ? errno
: EIO
);
233 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read: pread failed to read wanted length\n" );
238 // Update the amount of bytes alreay read
239 *piActuallyRead
= uBytesToCopy
;
245 raw_readwrite_write( vnode_t psVnode
, uint64_t uOffset
, void* pvBuf
, uint64_t uLength
, uint64_t *piActuallyWritten
)
248 uint64_t uClusterSize
= psVnode
->sFSParams
.vnfs_mp
->psHfsmount
->blockSize
;
249 uint64_t uFileSize
= ((struct filefork
*)VTOF(psVnode
))->ff_data
.cf_blocks
* uClusterSize
;
250 uint64_t uActuallyWritten
= 0;
252 *piActuallyWritten
= 0;
254 // Fill the buffer until the buffer is full or till the end of the file
255 while ( *piActuallyWritten
< uLength
)
257 uint64_t uCurrentCluster
= 0;
258 uint64_t uInClusterOffset
= 0;
259 uint64_t uContigousClustersInBytes
= 0;
261 iErr
= raw_readwrite_get_cluster_from_offset(psVnode
, uOffset
, &uCurrentCluster
, &uInClusterOffset
, &uContigousClustersInBytes
);
264 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: raw_readwrite_get_cluster_from_offset failed [%d]\n", iErr
);
268 // Stop writing if we've reached the end of the file
269 if ( (uContigousClustersInBytes
== 0) || (uOffset
>= uFileSize
) )
274 /* Calculate how many bytes are still missing to add to the device
275 * If offset near end of file need to set only (uFileSize - uOffset)
276 * else need to write as much as left (uLength - uAcctuallyRead)
278 uint64_t uBytesToWrite
= MIN(uFileSize
- uOffset
, uLength
- *piActuallyWritten
);
281 iErr
= raw_readwrite_write_internal( psVnode
, uCurrentCluster
, uContigousClustersInBytes
, uOffset
, uBytesToWrite
, pvBuf
, &uActuallyWritten
);
284 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_read_internal: raw_readwrite_read_internal failed [%d]\n", iErr
);
288 // Update the amount of bytes alreay written
289 *piActuallyWritten
+= uActuallyWritten
;
290 // Update file offset
291 uOffset
+= uActuallyWritten
;
292 // Update buffer offset
293 pvBuf
= (uint8_t*)pvBuf
+ uActuallyWritten
;
300 raw_readwrite_write_internal( vnode_t psVnode
, uint64_t uCluster
, uint64_t uContigousClustersInBytes
,
301 uint64_t uOffset
, uint64_t uBytesToWrite
, void* pvBuf
, uint64_t *piActuallyWritten
)
304 int iFD
= VNODE_TO_IFD(psVnode
);
305 struct hfsmount
*hfsmp
= VTOHFS(psVnode
);
306 uint64_t uClusterSize
= hfsmp
->blockSize
;
307 uint64_t uSectorSize
= hfsmp
->hfs_logical_block_size
;
308 uint64_t uBytesToCopy
= 0;
310 // Calculate offset - offset by sector and need to add the offset by sector
311 uint64_t uWriteOffset
= FSOPS_GetOffsetFromClusterNum( psVnode
, uCluster
) + ( ROUND_DOWN(uOffset
, uSectorSize
) % uClusterSize
);
313 // If offset not align to sector size, need to read the existing data
314 // memcpy it's beginning and write back to the device
315 if ( (uOffset
% uSectorSize
) != 0 )
317 void* pvBuffer
= hfs_malloc(uSectorSize
);
318 if (pvBuffer
== NULL
)
323 uint64_t uInSectorOffset
= uOffset
% uSectorSize
;
324 uBytesToCopy
= MIN( uBytesToWrite
, uSectorSize
- uInSectorOffset
);
326 // Read the content of the existing file
327 ssize_t iReadBytes
= pread(iFD
, pvBuffer
, uSectorSize
, uWriteOffset
);
328 if ( iReadBytes
!= (ssize_t
)uSectorSize
)
330 iErr
= (iReadBytes
< 0) ? errno
: EIO
;
331 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: pread failed to read wanted length\n" );
336 // memcpy the data from the given buffer
337 memcpy((uint8_t *)pvBuffer
+uInSectorOffset
, pvBuf
, uBytesToCopy
);
339 // Write the data into the device
340 ssize_t iWriteBytes
= pwrite(iFD
, pvBuffer
, uSectorSize
, uWriteOffset
);
341 if ( iWriteBytes
!= (ssize_t
)uSectorSize
)
343 iErr
= (iWriteBytes
< 0) ? errno
: EIO
;
344 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: pwrite failed to write wanted length\n" );
351 // If uBytesToWrite < uSectorSize, need to R/M/W 1 sector.
352 else if ( uBytesToWrite
< uSectorSize
)
354 void* pvBuffer
= hfs_malloc(uSectorSize
);
355 if (pvBuffer
== NULL
)
360 uBytesToCopy
= uBytesToWrite
;
362 // Read the content of the existing file
363 ssize_t iReadBytes
= pread(iFD
, pvBuffer
, uSectorSize
, uWriteOffset
);
364 if ( iReadBytes
!= (ssize_t
)uSectorSize
)
366 iErr
= (iReadBytes
< 0) ? errno
: EIO
;
367 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: pread failed to read wanted length\n" );
372 // memcpy the last data
373 memcpy(pvBuffer
, (uint8_t *)pvBuf
, uBytesToCopy
);
375 // Write the content to the file
376 ssize_t iWriteBytes
= pwrite(iFD
, pvBuffer
, uSectorSize
, uWriteOffset
);
377 if ( iWriteBytes
!= (ssize_t
)uSectorSize
)
379 iErr
= (iWriteBytes
< 0) ? errno
: EIO
;
380 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: pwrite failed to write wanted length\n" );
387 // Can write buffer size chunk
390 uint64_t uAvailSectors
= uContigousClustersInBytes
/ uSectorSize
;
391 uint64_t uRemainingSectors
= uBytesToWrite
/ uSectorSize
;
393 uBytesToCopy
= MIN(uAvailSectors
, uRemainingSectors
) * uSectorSize
;
394 uBytesToCopy
= MIN( uBytesToCopy
, MAX_READ_WRITE_LENGTH
);
396 assert( (uBytesToCopy
% uSectorSize
) == 0 );
397 assert( (uWriteOffset
% uSectorSize
) == 0 );
399 ssize_t iWriteBytes
= pwrite(iFD
, (uint8_t *)pvBuf
, uBytesToCopy
, uWriteOffset
) ;
400 if ( iWriteBytes
!= (ssize_t
) uBytesToCopy
)
402 iErr
= (iWriteBytes
< 0) ? errno
: EIO
;
403 LFHFS_LOG( LEVEL_ERROR
, "raw_readwrite_write: pwrite failed to write wanted length\n" );
408 // Update the amount of bytes alreay written
409 *piActuallyWritten
= uBytesToCopy
;
415 raw_readwrite_zero_fill_init()
422 gpvZeroBuf
= hfs_malloc( ZERO_BUF_SIZE
);
423 if ( gpvZeroBuf
== NULL
)
428 memset( gpvZeroBuf
, 0, ZERO_BUF_SIZE
);
434 raw_readwrite_zero_fill_de_init()
438 hfs_free( gpvZeroBuf
);
445 raw_readwrite_zero_fill_fill( hfsmount_t
* psMount
, uint64_t uBlock
, uint32_t uContigBlocks
)
448 int64_t lWriteSize
= 0;
449 uint64_t uCurWriteOffset
= 0;
450 uint64_t uCurWriteLen
= 0;
451 uint64_t uDataWriten
= 0;
453 if ( gpvZeroBuf
== NULL
)
459 uint64_t uLength
= uContigBlocks
*psMount
->blockSize
;
460 uint64_t uOffset
= psMount
->hfsPlusIOPosOffset
+ uBlock
*psMount
->blockSize
;
462 while ( uDataWriten
< uLength
)
464 uCurWriteOffset
= uOffset
+uDataWriten
;
465 uCurWriteLen
= MIN( (uLength
- uDataWriten
), ZERO_BUF_SIZE
);
467 lWriteSize
= pwrite( psMount
->hfs_devvp
->psFSRecord
->iFD
, gpvZeroBuf
, uCurWriteLen
, uCurWriteOffset
);
468 if ( lWriteSize
!= (int64_t)uCurWriteLen
)
474 uDataWriten
+= uCurWriteLen
;
482 raw_readwrite_zero_fill_last_block_suffix( vnode_t psVnode
)
486 int iFD
= (VPTOFSRECORD(psVnode
))->iFD
;
487 struct filefork
*fp
= VTOF(psVnode
);
488 uint32_t uBlockSize
= HFSTOVCB(psVnode
->sFSParams
.vnfs_mp
->psHfsmount
)->blockSize
;
489 uint32_t uBytesToKeep
= fp
->ff_size
% uBlockSize
;
490 uint64_t uBlockN
= 0;
491 uint64_t uContigousClustersInBytes
;
492 uint64_t uInClusterOffset
;
494 uint8_t* puClusterData
= NULL
;
496 iErr
= raw_readwrite_get_cluster_from_offset(psVnode
, fp
->ff_size
- uBytesToKeep
, &uBlockN
, &uInClusterOffset
, &uContigousClustersInBytes
);
502 // Allocate buffer for cluster.
503 puClusterData
= hfs_malloc( uBlockSize
);
504 if ( puClusterData
== NULL
)
510 // Read the last cluster.
511 size_t uBytesRead
= pread( iFD
, puClusterData
, uBlockSize
, FSOPS_GetOffsetFromClusterNum( psVnode
, uBlockN
) );
512 if ( uBytesRead
!= uBlockSize
)
518 memset( puClusterData
+uBytesToKeep
, 0, uBlockSize
-uBytesToKeep
);
520 // Write the last cluster.
521 size_t uBytesWrite
= pwrite( iFD
, puClusterData
, uBlockSize
, FSOPS_GetOffsetFromClusterNum( psVnode
, uBlockN
) );
522 if ( uBytesWrite
!= uBlockSize
)
530 hfs_free( puClusterData
);