headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / boot / loader / file_systems / fat / Stream.cpp
blob07874cfd55afd06f1c063b53ef18ccd3507a4003
1 /*
2 * Copyright 2008, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * François Revol <revol@free.fr>
7 */
10 #include "Stream.h"
12 #include <stdint.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
17 #include <algorithm>
19 #include <boot/FileMapDisk.h>
21 #include "CachedBlock.h"
22 #include "Directory.h"
23 #include "File.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)
35 fVolume(volume),
36 fFirstCluster(chain),
37 //fClusters(NULL),
38 fClusterMapCacheLast(0),
39 fSize(size)
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();
54 Stream::~Stream()
56 TRACE(("FATFS::Stream::~()\n"));
60 status_t
61 Stream::InitCheck()
63 if (fSize && !fVolume.IsValidCluster(fFirstCluster))
64 return B_BAD_VALUE;
65 return B_OK;
69 status_t
70 Stream::GetName(char *nameBuffer, size_t bufferSize) const
72 return strlcpy(nameBuffer, fName, bufferSize);
76 status_t
77 Stream::GetFileMap(struct file_map_run *runs, int32 *count)
79 int32 i;
80 uint32 cluster = fFirstCluster;
81 uint32 next = fVolume.InvalidClusterID();
82 off_t offset = 0LL;
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();
88 do {
89 next = fVolume.NextCluster(cluster);
90 if (next != cluster + 1)
91 break;
92 runs[i].len += fVolume.ClusterSize();
93 } while (true);
94 if (!fVolume.IsValidCluster(next))
95 break;
96 cluster = next;
97 offset += runs[i].len;
100 // too big
101 if (i == *count && fVolume.IsValidCluster(next))
102 return B_ERROR;
104 *count = i;
105 return B_OK;
109 status_t
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)
115 return B_BAD_VALUE;
117 uint32 cluster = 0;
118 bool found = false;
119 uint32 i;
120 for (i = 0; i < CLUSTER_MAP_CACHE_SIZE; i++) {
121 if (fClusterMapCache[i].block == index) {
122 cluster = fClusterMapCache[i].cluster;
123 found = true;
124 break;
127 if (!found) {
128 #if 1
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
132 count = 10;
133 for (i = 0; i < index && fVolume.IsValidCluster(cluster); i++) {
134 if (fVolume.IsLastCluster(cluster))
135 break;
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));
140 #endif
141 #if 0
142 cluster = fVolume.NextCluster(cluster, index);
143 #endif
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;
153 _cluster = cluster;
154 return B_OK;
158 status_t
159 Stream::_FindOrCreateCluster(off_t pos, uint32& _cluster, bool& _added)
161 status_t error = _FindCluster(pos, _cluster);
162 if (error == B_OK) {
163 _added = false;
164 return B_OK;
167 // iterate through the cluster list
168 uint32 index = (uint32)(pos / fVolume.ClusterSize());
169 uint32 cluster = fFirstCluster;
170 uint32 clusterCount = 0;
171 if (cluster != 0) {
172 uint32 nextCluster = cluster;
173 while (clusterCount <= index) {
174 if (!fVolume.IsValidCluster(nextCluster)
175 || fVolume.IsLastCluster(nextCluster)) {
176 break;
179 cluster = nextCluster;
180 clusterCount++;
181 nextCluster = fVolume.NextCluster(nextCluster);
185 if (clusterCount > index) {
186 // the cluster existed after all
187 _cluster = cluster;
188 _added = false;
189 return B_OK;
192 while (clusterCount <= index) {
193 uint32 newCluster;
194 error = fVolume.AllocateCluster(cluster, newCluster);
195 if (error != B_OK)
196 return error;
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;
205 clusterCount++;
208 _cluster = cluster;
209 _added = true;
210 return B_OK;
214 status_t
215 Stream::FindBlock(off_t pos, off_t &block, off_t &offset)
217 uint32 cluster;
218 status_t error = _FindCluster(pos, cluster);
219 if (error != B_OK)
220 return error;
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();
230 return B_OK;
234 status_t
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
242 if (pos < 0)
243 return B_BAD_VALUE;
244 if (pos >= fSize) {
245 *_length = 0;
246 return B_OK;
249 #if 0
250 // lazily build the cluster list
251 if (!fClusters) {
252 status_t status = BuildClusterList();
253 if (status != B_OK)
254 return status;
256 #endif
258 size_t length = *_length;
260 if (pos + (off_t)length > fSize)
261 length = fSize - pos;
263 off_t num; // block number
264 off_t offset;
265 if (FindBlock(pos, num, offset) < B_OK) {
266 *_length = 0;
267 return B_BAD_VALUE;
270 if (diskOffset != NULL)
271 *diskOffset = fVolume.BlockToOffset(num) + offset;
273 uint32 bytesRead = 0;
274 uint32 blockSize = fVolume.BlockSize();
275 uint8 *block;
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) {
284 *_length = 0;
285 return B_BAD_VALUE;
288 bytesRead = blockSize - (pos % blockSize);
289 if (length < bytesRead)
290 bytesRead = length;
292 memcpy(buffer, block + (pos % blockSize), bytesRead);
293 pos += bytesRead;
295 length -= bytesRead;
296 if (length == 0) {
297 *_length = bytesRead;
298 return B_OK;
301 if (FindBlock(pos, num, offset) < B_OK) {
302 *_length = bytesRead;
303 return B_BAD_VALUE;
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;
313 while (length > 0) {
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;
320 return B_BAD_VALUE;
322 memcpy(buffer + bytesRead, block, length);
323 bytesRead += length;
324 partial = true;
325 break;
328 if (read_pos(fVolume.Device(), fVolume.BlockToOffset(num),
329 buffer + bytesRead, fVolume.BlockSize()) < B_OK) {
330 *_length = bytesRead;
331 return B_BAD_VALUE;
334 int32 bytes = fVolume.BlockSize();
335 length -= bytes;
336 bytesRead += bytes;
337 if (length == 0)
338 break;
340 pos += bytes;
342 if (FindBlock(pos, num, offset) < B_OK) {
343 *_length = bytesRead;
344 return B_BAD_VALUE;
348 *_length = bytesRead;
349 return B_OK;
353 status_t
354 Stream::WriteAt(off_t pos, const void* _buffer, size_t* _length,
355 off_t* diskOffset)
357 if (pos < 0)
358 return B_BAD_VALUE;
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);
367 while (length > 0) {
368 // get the cluster
369 uint32 cluster;
370 bool added;
371 error = _FindOrCreateCluster(pos, cluster, added);
372 if (error != B_OK)
373 break;
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;
381 diskOffset = NULL;
384 // convert to block + offset
385 off_t block = fVolume.ToBlock(offset);
386 size_t inBlockOffset = offset % fVolume.BlockSize();
388 // write
389 size_t toWrite = std::min((size_t)fVolume.BlockSize() - inBlockOffset,
390 length);
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());
395 if (written < 0) {
396 error = written;
397 break;
399 if (written != fVolume.BlockSize()) {
400 error = B_ERROR;
401 break;
403 } else {
404 // write a partial block -- need to read it from disk first
405 error = cachedBlock.SetTo(block, CachedBlock::READ);
406 if (error != B_OK)
407 break;
409 memcpy(cachedBlock.Block() + inBlockOffset, buffer, toWrite);
411 error = cachedBlock.Flush();
412 if (error != B_OK)
413 break;
416 totalWritten += toWrite;
417 pos += toWrite;
418 buffer += toWrite;
419 length -= toWrite;
421 if (pos > fSize)
422 fSize = pos;
425 *_length = totalWritten;
426 return totalWritten > 0 ? B_OK : error;
430 status_t
431 Stream::BuildClusterList()
433 #if 0
434 TRACE(("FATFS::Stream::%s()\n", __FUNCTION__));
435 uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize();
436 uint32 c = fFirstCluster;
437 int i;
439 if (fSize == UINT32_MAX) // it's a directory, try a large enough value
440 count = 10;
441 //fClusters = (uint32 *)malloc(count * sizeof(uint32));
442 for (i = 0; i < count && fVolume.IsValidCluster(c); i++) {
443 if (fVolume.IsLastCluster(c))
444 break;
445 TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, c));
446 fClusters[i] = c;
447 c = fVolume.NextCluster(c);
448 TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, c));
449 // XXX: try to realloc() for dirs maybe ?
451 fClusterCount = i;
452 TRACE(("FATFS::Stream::%s: %d clusters in chain\n", __FUNCTION__, i));
453 #endif
454 return B_OK;