xref: /haiku/src/system/boot/loader/file_systems/fat/Stream.cpp (revision 893988af824e65e49e55f517b157db8386e8002b)
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 #include "CachedBlock.h"
12 #include "Directory.h"
13 #include "File.h"
14 
15 #include <util/kernel_cpp.h>
16 #include <boot/FileMapDisk.h>
17 
18 #include <stdint.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <string.h>
22 
23 //#define TRACE(x) dprintf x
24 #define TRACE(x) do {} while (0)
25 
26 
27 using namespace FATFS;
28 
29 
30 Stream::Stream(Volume &volume, uint32 chain, off_t size, const char *name)
31 	:
32 	fVolume(volume),
33 	fFirstCluster(chain),
34 	//fClusters(NULL),
35 	fClusterMapCacheLast(0),
36 	fSize(size)
37 {
38 	TRACE(("FATFS::Stream::(, %d, %Ld, %s)\n", chain, size, name));
39 	fName[FATFS_NAME_LENGTH] = '\0';
40 	strlcpy(fName, name, FATFS_NAME_LENGTH+1);
41 	fClusterCount = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize();
42 	if (size == UINT32_MAX)
43 		fClusterCount = 10; // ?
44 	for (int i = 0; i < CLUSTER_MAP_CACHE_SIZE; i++) {
45 		fClusterMapCache[i].block = -1;
46 		fClusterMapCache[i].cluster = fVolume.InvalidClusterID();
47 	}
48 }
49 
50 
51 Stream::~Stream()
52 {
53 	TRACE(("FATFS::Stream::~()\n"));
54 }
55 
56 
57 status_t
58 Stream::InitCheck()
59 {
60 	if (fSize && !fVolume.IsValidCluster(fFirstCluster))
61 		return B_BAD_VALUE;
62 	return B_OK;
63 }
64 
65 
66 status_t
67 Stream::GetName(char *nameBuffer, size_t bufferSize) const
68 {
69 	return strlcpy(nameBuffer, fName, bufferSize);
70 }
71 
72 
73 status_t
74 Stream::GetFileMap(struct file_map_run *runs, int32 *count)
75 {
76 	int32 i;
77 	uint32 cluster = fFirstCluster;
78 	uint32 next = fVolume.InvalidClusterID();
79 	off_t offset = 0LL;
80 
81 	for (i = 0; i < *count; i++) {
82 		runs[i].offset = offset;
83 		runs[i].block = fVolume.ToBlock(cluster);
84 		runs[i].len = fVolume.ClusterSize();
85 		do {
86 			next = fVolume.NextCluster(cluster);
87 			if (next != cluster + 1)
88 				break;
89 			runs[i].len += fVolume.ClusterSize();
90 		} while (true);
91 		if (!fVolume.IsValidCluster(next))
92 			break;
93 		cluster = next;
94 		offset += runs[i].len;
95 	}
96 
97 	// too big
98 	if (i == *count && fVolume.IsValidCluster(next))
99 		return B_ERROR;
100 
101 	*count = i;
102 	return B_OK;
103 }
104 
105 
106 status_t
107 Stream::FindBlock(off_t pos, off_t &block, off_t &offset)
108 {
109 	//TRACE(("FATFS::Stream::%s(%Ld,,)\n", __FUNCTION__, pos));
110 	uint32 index = (uint32)(pos / fVolume.ClusterSize());
111 	if (pos >= fSize || index >= fClusterCount)
112 		return B_BAD_VALUE;
113 
114 	uint32 cluster = 0;
115 	bool found = false;
116 	uint32 i;
117 	for (i = 0; i < CLUSTER_MAP_CACHE_SIZE; i++) {
118 		if (fClusterMapCache[i].block == index) {
119 			cluster = fClusterMapCache[i].cluster;
120 			found = true;
121 			break;
122 		}
123 	}
124 	if (!found) {
125 #if 1
126 		uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize();
127 		cluster = fFirstCluster;
128 		if (fSize == UINT32_MAX) // it's a directory, try a large enough value
129 			count = 10;
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 = fClusters[index];
151 	// convert to position
152 	offset = fVolume.ToOffset(cluster);
153 	offset += (pos %= fVolume.ClusterSize());
154 	// convert to block + offset
155 	block = fVolume.ToBlock(offset);
156 	offset %= fVolume.BlockSize();
157 	//TRACE(("FATFS::Stream::FindBlock: %Ld:%Ld\n", block, offset));
158 	return B_OK;
159 }
160 
161 
162 status_t
163 Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
164 {
165 	TRACE(("FATFS::Stream::%s(%Ld, )\n", __FUNCTION__, pos));
166 
167 	// set/check boundaries for pos/length
168 	if (pos < 0)
169 		return B_BAD_VALUE;
170 	if (pos >= fSize) {
171 		*_length = 0;
172 		return B_OK;
173 	}
174 
175 #if 0
176 	// lazily build the cluster list
177 	if (!fClusters) {
178 		status_t status = BuildClusterList();
179 		if (status != B_OK)
180 			return status;
181 	}
182 #endif
183 
184 	size_t length = *_length;
185 
186 	if (pos + length > fSize)
187 		length = fSize - pos;
188 
189 	off_t num; // block number
190 	off_t offset;
191 	if (FindBlock(pos, num, offset) < B_OK) {
192 		*_length = 0;
193 		return B_BAD_VALUE;
194 	}
195 
196 	uint32 bytesRead = 0;
197 	uint32 blockSize = fVolume.BlockSize();
198 	uint8 *block;
199 
200 	// the first block_run we read could not be aligned to the block_size boundary
201 	// (read partial block at the beginning)
202 
203 	// pos % block_size == (pos - offset) % block_size, offset % block_size == 0
204 	if (pos % blockSize != 0) {
205 		CachedBlock cached(fVolume, num);
206 		if ((block = cached.Block()) == NULL) {
207 			*_length = 0;
208 			return B_BAD_VALUE;
209 		}
210 
211 		bytesRead = blockSize - (pos % blockSize);
212 		if (length < bytesRead)
213 			bytesRead = length;
214 
215 		memcpy(buffer, block + (pos % blockSize), bytesRead);
216 		pos += bytesRead;
217 
218 		length -= bytesRead;
219 		if (length == 0) {
220 			*_length = bytesRead;
221 			return B_OK;
222 		}
223 
224 		if (FindBlock(pos, num, offset) < B_OK) {
225 			*_length = bytesRead;
226 			return B_BAD_VALUE;
227 		}
228 	}
229 
230 	// the first block_run is already filled in at this point
231 	// read the following complete blocks using cached_read(),
232 	// the last partial block is read using the generic Cache class
233 
234 	bool partial = false;
235 
236 	while (length > 0) {
237 		// offset is the offset to the current pos in the block_run
238 
239 		if (length < blockSize) {
240 			CachedBlock cached(fVolume, num);
241 			if ((block = cached.Block()) == NULL) {
242 				*_length = bytesRead;
243 				return B_BAD_VALUE;
244 			}
245 			memcpy(buffer + bytesRead, block, length);
246 			bytesRead += length;
247 			partial = true;
248 			break;
249 		}
250 
251 		if (read_pos(fVolume.Device(), fVolume.ToOffset(num),
252 			buffer + bytesRead, fVolume.BlockSize()) < B_OK) {
253 			*_length = bytesRead;
254 			return B_BAD_VALUE;
255 		}
256 
257 		int32 bytes = fVolume.BlockSize();
258 		length -= bytes;
259 		bytesRead += bytes;
260 		if (length == 0)
261 			break;
262 
263 		pos += bytes;
264 
265 		if (FindBlock(pos, num, offset) < B_OK) {
266 			*_length = bytesRead;
267 			return B_BAD_VALUE;
268 		}
269 	}
270 
271 	*_length = bytesRead;
272 	return B_OK;
273 }
274 
275 
276 status_t
277 Stream::BuildClusterList()
278 {
279 #if 0
280 	TRACE(("FATFS::Stream::%s()\n", __FUNCTION__));
281 	uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize();
282 	uint32 c = fFirstCluster;
283 	int i;
284 
285 	if (fSize == UINT32_MAX) // it's a directory, try a large enough value
286 		count = 10;
287 	//fClusters = (uint32 *)malloc(count * sizeof(uint32));
288 	for (i = 0; i < count && fVolume.IsValidCluster(c); i++) {
289 		if (fVolume.IsLastCluster(c))
290 			break;
291 		TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, c));
292 		fClusters[i] = c;
293 		c = fVolume.NextCluster(c);
294 		TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, c));
295 		// XXX: try to realloc() for dirs maybe ?
296 	}
297 	fClusterCount = i;
298 	TRACE(("FATFS::Stream::%s: %d clusters in chain\n", __FUNCTION__, i));
299 #endif
300 	return B_OK;
301 }
302