xref: /haiku/src/system/boot/loader/file_systems/fat/Stream.cpp (revision 83b1a68c52ba3e0e8796282759f694b7fdddf06d)
1 /*
2  * Copyright 2008, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		François Revol <revol@free.fr>
7  */
8 
9 
10 #include "Stream.h"
11 
12 #include <stdint.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 
17 #include <algorithm>
18 
19 #include <boot/FileMapDisk.h>
20 
21 #include "CachedBlock.h"
22 #include "Directory.h"
23 #include "File.h"
24 
25 
26 //#define TRACE(x) dprintf x
27 #define TRACE(x) do {} while (0)
28 
29 
30 using namespace FATFS;
31 
32 
33 Stream::Stream(Volume &volume, uint32 chain, off_t size, const char *name)
34 	:
35 	fVolume(volume),
36 	fFirstCluster(chain),
37 	//fClusters(NULL),
38 	fClusterMapCacheLast(0),
39 	fSize(size)
40 {
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();
50 	}
51 }
52 
53 
54 Stream::~Stream()
55 {
56 	TRACE(("FATFS::Stream::~()\n"));
57 }
58 
59 
60 status_t
61 Stream::InitCheck()
62 {
63 	if (fSize && !fVolume.IsValidCluster(fFirstCluster))
64 		return B_BAD_VALUE;
65 	return B_OK;
66 }
67 
68 
69 status_t
70 Stream::GetName(char *nameBuffer, size_t bufferSize) const
71 {
72 	return strlcpy(nameBuffer, fName, bufferSize);
73 }
74 
75 
76 status_t
77 Stream::GetFileMap(struct file_map_run *runs, int32 *count)
78 {
79 	int32 i;
80 	uint32 cluster = fFirstCluster;
81 	uint32 next = fVolume.InvalidClusterID();
82 	off_t offset = 0LL;
83 
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;
98 	}
99 
100 	// too big
101 	if (i == *count && fVolume.IsValidCluster(next))
102 		return B_ERROR;
103 
104 	*count = i;
105 	return B_OK;
106 }
107 
108 
109 status_t
110 Stream::_FindCluster(off_t pos, uint32& _cluster)
111 {
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;
116 
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;
125 		}
126 	}
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));
139 		}
140 #endif
141 #if 0
142 		cluster = fVolume.NextCluster(cluster, index);
143 #endif
144 	}
145 	if (!fVolume.IsValidCluster(cluster))
146 		return B_ENTRY_NOT_FOUND;
147 
148 	fClusterMapCache[fClusterMapCacheLast].block = index;
149 	fClusterMapCache[fClusterMapCacheLast].cluster = cluster;
150 	fClusterMapCacheLast++;
151 	fClusterMapCacheLast %= CLUSTER_MAP_CACHE_SIZE;
152 
153 	_cluster = cluster;
154 	return B_OK;
155 }
156 
157 
158 status_t
159 Stream::_FindOrCreateCluster(off_t pos, uint32& _cluster, bool& _added)
160 {
161 	status_t error = _FindCluster(pos, _cluster);
162 	if (error == B_OK) {
163 		_added = false;
164 		return B_OK;
165 	}
166 
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;
177 			}
178 
179 			cluster = nextCluster;
180 			clusterCount++;
181 			nextCluster = fVolume.NextCluster(nextCluster);
182 		}
183 	}
184 
185 	if (clusterCount > index) {
186 		// the cluster existed after all
187 		_cluster = cluster;
188 		_added = false;
189 		return B_OK;
190 	}
191 
192 	while (clusterCount <= index) {
193 		uint32 newCluster;
194 		error = fVolume.AllocateCluster(cluster, newCluster);
195 		if (error != B_OK)
196 			return error;
197 
198 		if (clusterCount == 0)
199 			fFirstCluster = newCluster;
200 
201 		// TODO: We should support to zero out the new cluster. Maybe make this
202 		// and optional parameter of WriteAt().
203 
204 		cluster = newCluster;
205 		clusterCount++;
206 	}
207 
208 	_cluster = cluster;
209 	_added = true;
210 	return B_OK;
211 }
212 
213 
214 status_t
215 Stream::FindBlock(off_t pos, off_t &block, off_t &offset)
216 {
217 	uint32 cluster;
218 	status_t error = _FindCluster(pos, cluster);
219 	if (error != B_OK)
220 		return error;
221 
222 	// convert to position
223 	offset = fVolume.ClusterToOffset(cluster);
224 	offset += (pos %= fVolume.ClusterSize());
225 
226 	// convert to block + offset
227 	block = fVolume.ToBlock(offset);
228 	offset %= fVolume.BlockSize();
229 
230 	return B_OK;
231 }
232 
233 
234 status_t
235 Stream::ReadAt(off_t pos, void *_buffer, size_t *_length, off_t *diskOffset)
236 {
237 	TRACE(("FATFS::Stream::%s(%Ld, )\n", __FUNCTION__, pos));
238 
239 	uint8* buffer = (uint8*)_buffer;
240 
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;
247 	}
248 
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;
255 	}
256 #endif
257 
258 	size_t length = *_length;
259 
260 	if (pos + length > fSize)
261 		length = fSize - pos;
262 
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;
268 	}
269 
270 	if (diskOffset != NULL)
271 		*diskOffset = fVolume.BlockToOffset(num) + offset;
272 
273 	uint32 bytesRead = 0;
274 	uint32 blockSize = fVolume.BlockSize();
275 	uint8 *block;
276 
277 	// the first block_run we read could not be aligned to the block_size boundary
278 	// (read partial block at the beginning)
279 
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;
286 		}
287 
288 		bytesRead = blockSize - (pos % blockSize);
289 		if (length < bytesRead)
290 			bytesRead = length;
291 
292 		memcpy(buffer, block + (pos % blockSize), bytesRead);
293 		pos += bytesRead;
294 
295 		length -= bytesRead;
296 		if (length == 0) {
297 			*_length = bytesRead;
298 			return B_OK;
299 		}
300 
301 		if (FindBlock(pos, num, offset) < B_OK) {
302 			*_length = bytesRead;
303 			return B_BAD_VALUE;
304 		}
305 	}
306 
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
310 
311 	bool partial = false;
312 
313 	while (length > 0) {
314 		// offset is the offset to the current pos in the block_run
315 
316 		if (length < blockSize) {
317 			CachedBlock cached(fVolume, num);
318 			if ((block = cached.Block()) == NULL) {
319 				*_length = bytesRead;
320 				return B_BAD_VALUE;
321 			}
322 			memcpy(buffer + bytesRead, block, length);
323 			bytesRead += length;
324 			partial = true;
325 			break;
326 		}
327 
328 		if (read_pos(fVolume.Device(), fVolume.BlockToOffset(num),
329 			buffer + bytesRead, fVolume.BlockSize()) < B_OK) {
330 			*_length = bytesRead;
331 			return B_BAD_VALUE;
332 		}
333 
334 		int32 bytes = fVolume.BlockSize();
335 		length -= bytes;
336 		bytesRead += bytes;
337 		if (length == 0)
338 			break;
339 
340 		pos += bytes;
341 
342 		if (FindBlock(pos, num, offset) < B_OK) {
343 			*_length = bytesRead;
344 			return B_BAD_VALUE;
345 		}
346 	}
347 
348 	*_length = bytesRead;
349 	return B_OK;
350 }
351 
352 
353 status_t
354 Stream::WriteAt(off_t pos, const void* _buffer, size_t* _length,
355 	off_t* diskOffset)
356 {
357 	if (pos < 0)
358 		return B_BAD_VALUE;
359 
360 	const uint8* buffer = (const uint8*)_buffer;
361 	size_t length = *_length;
362 	size_t totalWritten = 0;
363 	status_t error = B_OK;
364 
365 	CachedBlock cachedBlock(fVolume);
366 
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;
374 
375 		// convert to position
376 		off_t inClusterOffset = pos % fVolume.ClusterSize();
377 		off_t offset = fVolume.ClusterToOffset(cluster) + inClusterOffset;
378 
379 		if (diskOffset != NULL) {
380 			*diskOffset = offset;
381 			diskOffset = NULL;
382 		}
383 
384 		// convert to block + offset
385 		off_t block = fVolume.ToBlock(offset);
386 		size_t inBlockOffset = offset % fVolume.BlockSize();
387 
388 		// write
389 		size_t toWrite = std::min(fVolume.BlockSize() - inBlockOffset, length);
390 		if (toWrite == (size_t)fVolume.BlockSize()) {
391 			// write the whole block
392 			ssize_t written = write_pos(fVolume.Device(),
393 				fVolume.BlockToOffset(block), buffer, fVolume.BlockSize());
394 			if (written < 0) {
395 				error = written;
396 				break;
397 			}
398 			if (written != fVolume.BlockSize()) {
399 				error = B_ERROR;
400 				break;
401 			}
402 		} else {
403 			// write a partial block -- need to read it from disk first
404 			error = cachedBlock.SetTo(block, CachedBlock::READ);
405 			if (error != B_OK)
406 				break;
407 
408 			memcpy(cachedBlock.Block() + inBlockOffset, buffer, toWrite);
409 
410 			error = cachedBlock.Flush();
411 			if (error != B_OK)
412 				break;
413 		}
414 
415 		totalWritten += toWrite;
416 		pos += toWrite;
417 		buffer += toWrite;
418 		length -= toWrite;
419 
420 		if (pos > fSize)
421 			fSize = pos;
422 	}
423 
424 	*_length = totalWritten;
425 	return totalWritten > 0 ? B_OK : error;
426 }
427 
428 
429 status_t
430 Stream::BuildClusterList()
431 {
432 #if 0
433 	TRACE(("FATFS::Stream::%s()\n", __FUNCTION__));
434 	uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize();
435 	uint32 c = fFirstCluster;
436 	int i;
437 
438 	if (fSize == UINT32_MAX) // it's a directory, try a large enough value
439 		count = 10;
440 	//fClusters = (uint32 *)malloc(count * sizeof(uint32));
441 	for (i = 0; i < count && fVolume.IsValidCluster(c); i++) {
442 		if (fVolume.IsLastCluster(c))
443 			break;
444 		TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, c));
445 		fClusters[i] = c;
446 		c = fVolume.NextCluster(c);
447 		TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, c));
448 		// XXX: try to realloc() for dirs maybe ?
449 	}
450 	fClusterCount = i;
451 	TRACE(("FATFS::Stream::%s: %d clusters in chain\n", __FUNCTION__, i));
452 #endif
453 	return B_OK;
454 }
455