2 * Copyright 2008, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
6 * François Revol <revol@free.fr>
19 #include <boot/FileMapDisk.h>
21 #include "CachedBlock.h"
22 #include "Directory.h"
26 //#define TRACE(x) dprintf x
27 #define TRACE(x) do {} while (0)
30 using namespace FATFS
;
33 Stream::Stream(Volume
&volume
, uint32 chain
, off_t size
, const char *name
)
38 fClusterMapCacheLast(0),
41 TRACE(("FATFS::Stream::(, %d, %Ld, %s)\n", chain
, size
, name
));
42 fName
[FATFS_NAME_LENGTH
] = '\0';
43 strlcpy(fName
, name
, FATFS_NAME_LENGTH
+1);
44 fClusterCount
= (fSize
+ fVolume
.ClusterSize() - 1) / fVolume
.ClusterSize();
45 if (size
== UINT32_MAX
)
46 fClusterCount
= 10; // ?
47 for (int i
= 0; i
< CLUSTER_MAP_CACHE_SIZE
; i
++) {
48 fClusterMapCache
[i
].block
= -1;
49 fClusterMapCache
[i
].cluster
= fVolume
.InvalidClusterID();
56 TRACE(("FATFS::Stream::~()\n"));
63 if (fSize
&& !fVolume
.IsValidCluster(fFirstCluster
))
70 Stream::GetName(char *nameBuffer
, size_t bufferSize
) const
72 return strlcpy(nameBuffer
, fName
, bufferSize
);
77 Stream::GetFileMap(struct file_map_run
*runs
, int32
*count
)
80 uint32 cluster
= fFirstCluster
;
81 uint32 next
= fVolume
.InvalidClusterID();
84 for (i
= 0; i
< *count
; i
++) {
85 runs
[i
].offset
= offset
;
86 runs
[i
].block
= fVolume
.ToBlock(cluster
);
87 runs
[i
].len
= fVolume
.ClusterSize();
89 next
= fVolume
.NextCluster(cluster
);
90 if (next
!= cluster
+ 1)
92 runs
[i
].len
+= fVolume
.ClusterSize();
94 if (!fVolume
.IsValidCluster(next
))
97 offset
+= runs
[i
].len
;
101 if (i
== *count
&& fVolume
.IsValidCluster(next
))
110 Stream::_FindCluster(off_t pos
, uint32
& _cluster
)
112 //TRACE(("FATFS::Stream::%s(%Ld,,)\n", __FUNCTION__, pos));
113 uint32 index
= (uint32
)(pos
/ fVolume
.ClusterSize());
114 if (pos
> fSize
|| index
>= fClusterCount
)
120 for (i
= 0; i
< CLUSTER_MAP_CACHE_SIZE
; i
++) {
121 if (fClusterMapCache
[i
].block
== index
) {
122 cluster
= fClusterMapCache
[i
].cluster
;
129 uint32 count
= (fSize
+ fVolume
.ClusterSize() - 1) / fVolume
.ClusterSize();
130 cluster
= fFirstCluster
;
131 if (fSize
== UINT32_MAX
) // it's a directory, try a large enough value
133 for (i
= 0; i
< index
&& fVolume
.IsValidCluster(cluster
); i
++) {
134 if (fVolume
.IsLastCluster(cluster
))
136 //TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, cluster));
137 cluster
= fVolume
.NextCluster(cluster
);
138 //TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, cluster));
142 cluster
= fVolume
.NextCluster(cluster
, index
);
145 if (!fVolume
.IsValidCluster(cluster
))
146 return B_ENTRY_NOT_FOUND
;
148 fClusterMapCache
[fClusterMapCacheLast
].block
= index
;
149 fClusterMapCache
[fClusterMapCacheLast
].cluster
= cluster
;
150 fClusterMapCacheLast
++;
151 fClusterMapCacheLast
%= CLUSTER_MAP_CACHE_SIZE
;
159 Stream::_FindOrCreateCluster(off_t pos
, uint32
& _cluster
, bool& _added
)
161 status_t error
= _FindCluster(pos
, _cluster
);
167 // iterate through the cluster list
168 uint32 index
= (uint32
)(pos
/ fVolume
.ClusterSize());
169 uint32 cluster
= fFirstCluster
;
170 uint32 clusterCount
= 0;
172 uint32 nextCluster
= cluster
;
173 while (clusterCount
<= index
) {
174 if (!fVolume
.IsValidCluster(nextCluster
)
175 || fVolume
.IsLastCluster(nextCluster
)) {
179 cluster
= nextCluster
;
181 nextCluster
= fVolume
.NextCluster(nextCluster
);
185 if (clusterCount
> index
) {
186 // the cluster existed after all
192 while (clusterCount
<= index
) {
194 error
= fVolume
.AllocateCluster(cluster
, newCluster
);
198 if (clusterCount
== 0)
199 fFirstCluster
= newCluster
;
201 // TODO: We should support to zero out the new cluster. Maybe make this
202 // and optional parameter of WriteAt().
204 cluster
= newCluster
;
215 Stream::FindBlock(off_t pos
, off_t
&block
, off_t
&offset
)
218 status_t error
= _FindCluster(pos
, cluster
);
222 // convert to position
223 offset
= fVolume
.ClusterToOffset(cluster
);
224 offset
+= (pos
%= fVolume
.ClusterSize());
226 // convert to block + offset
227 block
= fVolume
.ToBlock(offset
);
228 offset
%= fVolume
.BlockSize();
235 Stream::ReadAt(off_t pos
, void *_buffer
, size_t *_length
, off_t
*diskOffset
)
237 TRACE(("FATFS::Stream::%s(%Ld, )\n", __FUNCTION__
, pos
));
239 uint8
* buffer
= (uint8
*)_buffer
;
241 // set/check boundaries for pos/length
250 // lazily build the cluster list
252 status_t status
= BuildClusterList();
258 size_t length
= *_length
;
260 if (pos
+ (off_t
)length
> fSize
)
261 length
= fSize
- pos
;
263 off_t num
; // block number
265 if (FindBlock(pos
, num
, offset
) < B_OK
) {
270 if (diskOffset
!= NULL
)
271 *diskOffset
= fVolume
.BlockToOffset(num
) + offset
;
273 uint32 bytesRead
= 0;
274 uint32 blockSize
= fVolume
.BlockSize();
277 // the first block_run we read could not be aligned to the block_size boundary
278 // (read partial block at the beginning)
280 // pos % block_size == (pos - offset) % block_size, offset % block_size == 0
281 if (pos
% blockSize
!= 0) {
282 CachedBlock
cached(fVolume
, num
);
283 if ((block
= cached
.Block()) == NULL
) {
288 bytesRead
= blockSize
- (pos
% blockSize
);
289 if (length
< bytesRead
)
292 memcpy(buffer
, block
+ (pos
% blockSize
), bytesRead
);
297 *_length
= bytesRead
;
301 if (FindBlock(pos
, num
, offset
) < B_OK
) {
302 *_length
= bytesRead
;
307 // the first block_run is already filled in at this point
308 // read the following complete blocks using cached_read(),
309 // the last partial block is read using the generic Cache class
311 bool partial
= false;
314 // offset is the offset to the current pos in the block_run
316 if (length
< blockSize
) {
317 CachedBlock
cached(fVolume
, num
);
318 if ((block
= cached
.Block()) == NULL
) {
319 *_length
= bytesRead
;
322 memcpy(buffer
+ bytesRead
, block
, length
);
328 if (read_pos(fVolume
.Device(), fVolume
.BlockToOffset(num
),
329 buffer
+ bytesRead
, fVolume
.BlockSize()) < B_OK
) {
330 *_length
= bytesRead
;
334 int32 bytes
= fVolume
.BlockSize();
342 if (FindBlock(pos
, num
, offset
) < B_OK
) {
343 *_length
= bytesRead
;
348 *_length
= bytesRead
;
354 Stream::WriteAt(off_t pos
, const void* _buffer
, size_t* _length
,
360 const uint8
* buffer
= (const uint8
*)_buffer
;
361 size_t length
= *_length
;
362 size_t totalWritten
= 0;
363 status_t error
= B_OK
;
365 CachedBlock
cachedBlock(fVolume
);
371 error
= _FindOrCreateCluster(pos
, cluster
, added
);
375 // convert to position
376 off_t inClusterOffset
= pos
% fVolume
.ClusterSize();
377 off_t offset
= fVolume
.ClusterToOffset(cluster
) + inClusterOffset
;
379 if (diskOffset
!= NULL
) {
380 *diskOffset
= offset
;
384 // convert to block + offset
385 off_t block
= fVolume
.ToBlock(offset
);
386 size_t inBlockOffset
= offset
% fVolume
.BlockSize();
389 size_t toWrite
= std::min((size_t)fVolume
.BlockSize() - inBlockOffset
,
391 if (toWrite
== (size_t)fVolume
.BlockSize()) {
392 // write the whole block
393 ssize_t written
= write_pos(fVolume
.Device(),
394 fVolume
.BlockToOffset(block
), buffer
, fVolume
.BlockSize());
399 if (written
!= fVolume
.BlockSize()) {
404 // write a partial block -- need to read it from disk first
405 error
= cachedBlock
.SetTo(block
, CachedBlock::READ
);
409 memcpy(cachedBlock
.Block() + inBlockOffset
, buffer
, toWrite
);
411 error
= cachedBlock
.Flush();
416 totalWritten
+= toWrite
;
425 *_length
= totalWritten
;
426 return totalWritten
> 0 ? B_OK
: error
;
431 Stream::BuildClusterList()
434 TRACE(("FATFS::Stream::%s()\n", __FUNCTION__
));
435 uint32 count
= (fSize
+ fVolume
.ClusterSize() - 1) / fVolume
.ClusterSize();
436 uint32 c
= fFirstCluster
;
439 if (fSize
== UINT32_MAX
) // it's a directory, try a large enough value
441 //fClusters = (uint32 *)malloc(count * sizeof(uint32));
442 for (i
= 0; i
< count
&& fVolume
.IsValidCluster(c
); i
++) {
443 if (fVolume
.IsLastCluster(c
))
445 TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__
, i
, c
));
447 c
= fVolume
.NextCluster(c
);
448 TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__
, c
));
449 // XXX: try to realloc() for dirs maybe ?
452 TRACE(("FATFS::Stream::%s: %d clusters in chain\n", __FUNCTION__
, i
));