xref: /haiku/src/system/boot/loader/file_systems/fat/Stream.cpp (revision 4f2fd49bdc6078128b1391191e4edac647044c3d)
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 	uint32 cluster;
112 	if (pos >= fSize)
113 		return B_BAD_VALUE;
114 	if (index >= fClusterCount)
115 		return B_BAD_VALUE;
116 
117 	bool found = false;
118 	int i;
119 	for (i = 0; i < CLUSTER_MAP_CACHE_SIZE; i++) {
120 		if (fClusterMapCache[i].block == index) {
121 			cluster = fClusterMapCache[i].cluster;
122 			found = true;
123 			break;
124 		}
125 	}
126 	if (!found) {
127 #if 1
128 		uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize();
129 		cluster = fFirstCluster;
130 		if (fSize == UINT32_MAX) // it's a directory, try a large enough value
131 			count = 10;
132 		for (i = 0; i < index && fVolume.IsValidCluster(cluster); i++) {
133 			if (fVolume.IsLastCluster(cluster))
134 				break;
135 			//TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, cluster));
136 			cluster = fVolume.NextCluster(cluster);
137 			//TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, cluster));
138 		}
139 #endif
140 #if 0
141 		cluster = fVolume.NextCluster(cluster, index);
142 #endif
143 	}
144 	if (!fVolume.IsValidCluster(cluster))
145 		return ENOENT;
146 	fClusterMapCache[fClusterMapCacheLast].block = index;
147 	fClusterMapCache[fClusterMapCacheLast].cluster = cluster;
148 	fClusterMapCacheLast++;
149 	fClusterMapCacheLast %= CLUSTER_MAP_CACHE_SIZE;
150 
151 	//cluster = fClusters[index];
152 	// convert to position
153 	offset = fVolume.ToOffset(cluster);
154 	offset += (pos %= fVolume.ClusterSize());
155 	// convert to block + offset
156 	block = fVolume.ToBlock(offset);
157 	offset %= fVolume.BlockSize();
158 	//TRACE(("FATFS::Stream::FindBlock: %Ld:%Ld\n", block, offset));
159 	return B_OK;
160 }
161 
162 
163 status_t
164 Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
165 {
166 	TRACE(("FATFS::Stream::%s(%Ld, )\n", __FUNCTION__, pos));
167 	status_t err;
168 	// set/check boundaries for pos/length
169 
170 	if (pos < 0)
171 		return B_BAD_VALUE;
172 	if (pos >= fSize) {
173 		*_length = 0;
174 		return B_NO_ERROR;
175 	}
176 
177 #if 0
178 	// lazily build the cluster list
179 	if (!fClusters) {
180 		err = BuildClusterList();
181 		if (err < B_OK)
182 			return err;
183 	}
184 #endif
185 
186 	size_t length = *_length;
187 
188 	if (pos + length > fSize)
189 		length = fSize - pos;
190 
191 	off_t num; // block number
192 	off_t offset;
193 	if (FindBlock(pos, num, offset) < B_OK) {
194 		*_length = 0;
195 		return B_BAD_VALUE;
196 	}
197 
198 	uint32 bytesRead = 0;
199 	uint32 blockSize = fVolume.BlockSize();
200 	uint32 blockShift = fVolume.BlockShift();
201 	uint8 *block;
202 
203 	// the first block_run we read could not be aligned to the block_size boundary
204 	// (read partial block at the beginning)
205 
206 	// pos % block_size == (pos - offset) % block_size, offset % block_size == 0
207 	if (pos % blockSize != 0) {
208 		CachedBlock cached(fVolume, num);
209 		if ((block = cached.Block()) == NULL) {
210 			*_length = 0;
211 			return B_BAD_VALUE;
212 		}
213 
214 		bytesRead = blockSize - (pos % blockSize);
215 		if (length < bytesRead)
216 			bytesRead = length;
217 
218 		memcpy(buffer, block + (pos % blockSize), bytesRead);
219 		pos += bytesRead;
220 
221 		length -= bytesRead;
222 		if (length == 0) {
223 			*_length = bytesRead;
224 			return B_OK;
225 		}
226 
227 		if (FindBlock(pos, num, offset) < B_OK) {
228 			*_length = bytesRead;
229 			return B_BAD_VALUE;
230 		}
231 	}
232 
233 	// the first block_run is already filled in at this point
234 	// read the following complete blocks using cached_read(),
235 	// the last partial block is read using the generic Cache class
236 
237 	bool partial = false;
238 
239 	while (length > 0) {
240 		// offset is the offset to the current pos in the block_run
241 
242 		if (length < blockSize) {
243 			CachedBlock cached(fVolume, num);
244 			if ((block = cached.Block()) == NULL) {
245 				*_length = bytesRead;
246 				return B_BAD_VALUE;
247 			}
248 			memcpy(buffer + bytesRead, block, length);
249 			bytesRead += length;
250 			partial = true;
251 			break;
252 		}
253 
254 		if (read_pos(fVolume.Device(), fVolume.ToOffset(num),
255 			buffer + bytesRead, fVolume.BlockSize()) < B_OK) {
256 			*_length = bytesRead;
257 			return B_BAD_VALUE;
258 		}
259 
260 		int32 bytes = fVolume.BlockSize();
261 		length -= bytes;
262 		bytesRead += bytes;
263 		if (length == 0)
264 			break;
265 
266 		pos += bytes;
267 
268 		if (FindBlock(pos, num, offset) < B_OK) {
269 			*_length = bytesRead;
270 			return B_BAD_VALUE;
271 		}
272 	}
273 
274 	*_length = bytesRead;
275 	return B_NO_ERROR;
276 }
277 
278 status_t
279 Stream::BuildClusterList()
280 {
281 #if 0
282 	TRACE(("FATFS::Stream::%s()\n", __FUNCTION__));
283 	uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize();
284 	uint32 c = fFirstCluster;
285 	int i;
286 
287 	if (fSize == UINT32_MAX) // it's a directory, try a large enough value
288 		count = 10;
289 	//fClusters = (uint32 *)malloc(count * sizeof(uint32));
290 	for (i = 0; i < count && fVolume.IsValidCluster(c); i++) {
291 		if (fVolume.IsLastCluster(c))
292 			break;
293 		TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, c));
294 		fClusters[i] = c;
295 		c = fVolume.NextCluster(c);
296 		TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, c));
297 		// XXX: try to realloc() for dirs maybe ?
298 	}
299 	fClusterCount = i;
300 	TRACE(("FATFS::Stream::%s: %d clusters in chain\n", __FUNCTION__, i));
301 #endif
302 	return B_OK;
303 }
304