xref: /haiku/src/system/boot/loader/file_systems/fat/Stream.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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, %lld, %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(%lld,,)\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 		cluster = fFirstCluster;
130 		for (i = 0; i < index && fVolume.IsValidCluster(cluster); i++) {
131 			if (fVolume.IsLastCluster(cluster))
132 				break;
133 			//TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, cluster));
134 			cluster = fVolume.NextCluster(cluster);
135 			//TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, cluster));
136 		}
137 #endif
138 #if 0
139 		cluster = fVolume.NextCluster(cluster, index);
140 #endif
141 	}
142 	if (!fVolume.IsValidCluster(cluster))
143 		return B_ENTRY_NOT_FOUND;
144 
145 	fClusterMapCache[fClusterMapCacheLast].block = index;
146 	fClusterMapCache[fClusterMapCacheLast].cluster = cluster;
147 	fClusterMapCacheLast++;
148 	fClusterMapCacheLast %= CLUSTER_MAP_CACHE_SIZE;
149 
150 	_cluster = cluster;
151 	return B_OK;
152 }
153 
154 
155 status_t
156 Stream::_FindOrCreateCluster(off_t pos, uint32& _cluster, bool& _added)
157 {
158 	status_t error = _FindCluster(pos, _cluster);
159 	if (error == B_OK) {
160 		_added = false;
161 		return B_OK;
162 	}
163 
164 	// iterate through the cluster list
165 	uint32 index = (uint32)(pos / fVolume.ClusterSize());
166 	uint32 cluster = fFirstCluster;
167 	uint32 clusterCount = 0;
168 	if (cluster != 0) {
169 		uint32 nextCluster = cluster;
170 		while (clusterCount <= index) {
171 			if (!fVolume.IsValidCluster(nextCluster)
172 					|| fVolume.IsLastCluster(nextCluster)) {
173 				break;
174 			}
175 
176 			cluster = nextCluster;
177 			clusterCount++;
178 			nextCluster = fVolume.NextCluster(nextCluster);
179 		}
180 	}
181 
182 	if (clusterCount > index) {
183 		// the cluster existed after all
184 		_cluster = cluster;
185 		_added = false;
186 		return B_OK;
187 	}
188 
189 	while (clusterCount <= index) {
190 		uint32 newCluster;
191 		error = fVolume.AllocateCluster(cluster, newCluster);
192 		if (error != B_OK)
193 			return error;
194 
195 		if (clusterCount == 0)
196 			fFirstCluster = newCluster;
197 
198 		// TODO: We should support to zero out the new cluster. Maybe make this
199 		// and optional parameter of WriteAt().
200 
201 		cluster = newCluster;
202 		clusterCount++;
203 	}
204 
205 	_cluster = cluster;
206 	_added = true;
207 	return B_OK;
208 }
209 
210 
211 status_t
212 Stream::FindBlock(off_t pos, off_t &block, off_t &offset)
213 {
214 	uint32 cluster;
215 	status_t error = _FindCluster(pos, cluster);
216 	if (error != B_OK)
217 		return error;
218 
219 	// convert to position
220 	offset = fVolume.ClusterToOffset(cluster);
221 	offset += (pos %= fVolume.ClusterSize());
222 
223 	// convert to block + offset
224 	block = fVolume.ToBlock(offset);
225 	offset %= fVolume.BlockSize();
226 
227 	return B_OK;
228 }
229 
230 
231 status_t
232 Stream::ReadAt(off_t pos, void *_buffer, size_t *_length, off_t *diskOffset)
233 {
234 	TRACE(("FATFS::Stream::%s(%lld, )\n", __FUNCTION__, pos));
235 
236 	uint8* buffer = (uint8*)_buffer;
237 
238 	// set/check boundaries for pos/length
239 	if (pos < 0)
240 		return B_BAD_VALUE;
241 	if (pos >= fSize) {
242 		*_length = 0;
243 		return B_OK;
244 	}
245 
246 #if 0
247 	// lazily build the cluster list
248 	if (!fClusters) {
249 		status_t status = BuildClusterList();
250 		if (status != B_OK)
251 			return status;
252 	}
253 #endif
254 
255 	size_t length = *_length;
256 
257 	if (pos + (off_t)length > fSize)
258 		length = fSize - pos;
259 
260 	off_t num; // block number
261 	off_t offset;
262 	if (FindBlock(pos, num, offset) < B_OK) {
263 		*_length = 0;
264 		return B_BAD_VALUE;
265 	}
266 
267 	if (diskOffset != NULL)
268 		*diskOffset = fVolume.BlockToOffset(num) + offset;
269 
270 	uint32 bytesRead = 0;
271 	uint32 blockSize = fVolume.BlockSize();
272 	uint8 *block;
273 
274 	// the first block_run we read could not be aligned to the block_size boundary
275 	// (read partial block at the beginning)
276 
277 	// pos % block_size == (pos - offset) % block_size, offset % block_size == 0
278 	if (pos % blockSize != 0) {
279 		CachedBlock cached(fVolume, num);
280 		if ((block = cached.Block()) == NULL) {
281 			*_length = 0;
282 			return B_BAD_VALUE;
283 		}
284 
285 		bytesRead = blockSize - (pos % blockSize);
286 		if (length < bytesRead)
287 			bytesRead = length;
288 
289 		memcpy(buffer, block + (pos % blockSize), bytesRead);
290 		pos += bytesRead;
291 
292 		length -= bytesRead;
293 		if (length == 0) {
294 			*_length = bytesRead;
295 			return B_OK;
296 		}
297 
298 		if (FindBlock(pos, num, offset) < B_OK) {
299 			*_length = bytesRead;
300 			return B_BAD_VALUE;
301 		}
302 	}
303 
304 	// the first block_run is already filled in at this point
305 	// read the following complete blocks using cached_read(),
306 	// the last partial block is read using the generic Cache class
307 
308 	while (length > 0) {
309 		// offset is the offset to the current pos in the block_run
310 
311 		if (length < blockSize) {
312 			CachedBlock cached(fVolume, num);
313 			if ((block = cached.Block()) == NULL) {
314 				*_length = bytesRead;
315 				return B_BAD_VALUE;
316 			}
317 			memcpy(buffer + bytesRead, block, length);
318 			bytesRead += length;
319 			break;
320 		}
321 
322 		if (read_pos(fVolume.Device(), fVolume.BlockToOffset(num),
323 			buffer + bytesRead, fVolume.BlockSize()) < B_OK) {
324 			*_length = bytesRead;
325 			return B_BAD_VALUE;
326 		}
327 
328 		int32 bytes = fVolume.BlockSize();
329 		length -= bytes;
330 		bytesRead += bytes;
331 		if (length == 0)
332 			break;
333 
334 		pos += bytes;
335 
336 		if (FindBlock(pos, num, offset) < B_OK) {
337 			*_length = bytesRead;
338 			return B_BAD_VALUE;
339 		}
340 	}
341 
342 	*_length = bytesRead;
343 	return B_OK;
344 }
345 
346 
347 status_t
348 Stream::WriteAt(off_t pos, const void* _buffer, size_t* _length,
349 	off_t* diskOffset)
350 {
351 	if (pos < 0)
352 		return B_BAD_VALUE;
353 
354 	const uint8* buffer = (const uint8*)_buffer;
355 	size_t length = *_length;
356 	size_t totalWritten = 0;
357 	status_t error = B_OK;
358 
359 	CachedBlock cachedBlock(fVolume);
360 
361 	while (length > 0) {
362 		// get the cluster
363 		uint32 cluster;
364 		bool added;
365 		error = _FindOrCreateCluster(pos, cluster, added);
366 		if (error != B_OK)
367 			break;
368 
369 		// convert to position
370 		off_t inClusterOffset = pos % fVolume.ClusterSize();
371 		off_t offset = fVolume.ClusterToOffset(cluster) + inClusterOffset;
372 
373 		if (diskOffset != NULL) {
374 			*diskOffset = offset;
375 			diskOffset = NULL;
376 		}
377 
378 		// convert to block + offset
379 		off_t block = fVolume.ToBlock(offset);
380 		size_t inBlockOffset = offset % fVolume.BlockSize();
381 
382 		// write
383 		size_t toWrite = std::min((size_t)fVolume.BlockSize() - inBlockOffset,
384 			length);
385 		if (toWrite == (size_t)fVolume.BlockSize()) {
386 			// write the whole block
387 			ssize_t written = write_pos(fVolume.Device(),
388 				fVolume.BlockToOffset(block), buffer, fVolume.BlockSize());
389 			if (written < 0) {
390 				error = written;
391 				break;
392 			}
393 			if (written != fVolume.BlockSize()) {
394 				error = B_ERROR;
395 				break;
396 			}
397 		} else {
398 			// write a partial block -- need to read it from disk first
399 			error = cachedBlock.SetTo(block, CachedBlock::READ);
400 			if (error != B_OK)
401 				break;
402 
403 			memcpy(cachedBlock.Block() + inBlockOffset, buffer, toWrite);
404 
405 			error = cachedBlock.Flush();
406 			if (error != B_OK)
407 				break;
408 		}
409 
410 		totalWritten += toWrite;
411 		pos += toWrite;
412 		buffer += toWrite;
413 		length -= toWrite;
414 
415 		if (pos > fSize)
416 			fSize = pos;
417 	}
418 
419 	*_length = totalWritten;
420 	return totalWritten > 0 ? B_OK : error;
421 }
422 
423 
424 status_t
425 Stream::BuildClusterList()
426 {
427 #if 0
428 	TRACE(("FATFS::Stream::%s()\n", __FUNCTION__));
429 	uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize();
430 	uint32 c = fFirstCluster;
431 	int i;
432 
433 	if (fSize == UINT32_MAX) // it's a directory, try a large enough value
434 		count = 10;
435 	//fClusters = (uint32 *)malloc(count * sizeof(uint32));
436 	for (i = 0; i < count && fVolume.IsValidCluster(c); i++) {
437 		if (fVolume.IsLastCluster(c))
438 			break;
439 		TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, c));
440 		fClusters[i] = c;
441 		c = fVolume.NextCluster(c);
442 		TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, c));
443 		// XXX: try to realloc() for dirs maybe ?
444 	}
445 	fClusterCount = i;
446 	TRACE(("FATFS::Stream::%s: %d clusters in chain\n", __FUNCTION__, i));
447 #endif
448 	return B_OK;
449 }
450