1/*
2	Copyright 1999-2001, Be Incorporated.   All Rights Reserved.
3	This file may be used under the terms of the Be Sample Code License.
4*/
5
6
7#include "iter.h"
8
9#include "system_dependencies.h"
10
11#include "dosfs.h"
12#include "fat.h"
13#include "util.h"
14
15
16#define DPRINTF(a,b) if (debug_iter > (a)) dprintf b
17
18
19static int
20_validate_cs_(nspace *vol, uint32 cluster, uint32 sector)
21{
22	if (sector < 0) return -1;
23
24	if ((vol->fat_bits != 32) && IS_FIXED_ROOT(cluster)) { // fat12 or fat16 root
25		if (sector >= vol->root_sectors)
26			return -1;
27		return 0;
28	}
29
30	if (sector >= vol->sectors_per_cluster) return -1;
31
32	if (!IS_DATA_CLUSTER(cluster)) return -1;
33
34	return 0;
35}
36
37
38off_t
39csi_to_block(struct csi *csi)
40{
41	// presumes the caller has already called _validate_cs_ on the argument
42	ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
43	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
44		return EINVAL;
45
46	if (IS_FIXED_ROOT(csi->cluster))
47		return csi->vol->root_start + csi->sector;
48
49	return csi->vol->data_start +
50		(off_t)(csi->cluster - 2)* csi->vol->sectors_per_cluster +
51		csi->sector;
52}
53
54
55int
56init_csi(nspace *vol, uint32 cluster, uint32 sector, struct csi *csi)
57{
58	int ret;
59	if ((ret = _validate_cs_(vol,cluster,sector)) != 0)
60		return ret;
61
62	csi->vol = vol; csi->cluster = cluster; csi->sector = sector;
63
64	return 0;
65}
66
67
68int
69iter_csi(struct csi *csi, int sectors)
70{
71	if (csi->sector == 0xffff) // check if already at end of chain
72		return -1;
73
74	if (sectors < 0)
75		return EINVAL;
76
77	if (sectors == 0)
78		return 0;
79
80	if (IS_FIXED_ROOT(csi->cluster)) {
81		csi->sector += sectors;
82		if (csi->sector < csi->vol->root_sectors)
83			return 0;
84	} else {
85		csi->sector += sectors;
86		if (csi->sector < csi->vol->sectors_per_cluster)
87			return 0;
88		csi->cluster = get_nth_fat_entry(csi->vol, csi->cluster,
89			csi->sector / csi->vol->sectors_per_cluster);
90
91		if ((int32)csi->cluster < 0) {
92			csi->sector = 0xffff;
93			return csi->cluster;
94		}
95
96		if (vIS_DATA_CLUSTER(csi->vol,csi->cluster)) {
97			csi->sector %= csi->vol->sectors_per_cluster;
98			return 0;
99		}
100	}
101
102	csi->sector = 0xffff;
103
104	return -1;
105}
106
107
108uint8 *
109csi_get_block(struct csi *csi)
110{
111	status_t status;
112	const void* block;
113
114	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
115		return NULL;
116
117	status = block_cache_get_etc(csi->vol->fBlockCache,
118		csi_to_block(csi), 1, csi->vol->bytes_per_sector, &block);
119	if (status != B_OK)
120		return NULL;
121	return (uint8 *)block;
122}
123
124
125status_t
126csi_release_block(struct csi *csi)
127{
128	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
129		return EINVAL;
130
131	block_cache_put(csi->vol->fBlockCache, csi_to_block(csi));
132	return B_OK;
133}
134
135
136status_t
137csi_make_writable(struct csi *csi)
138{
139	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
140		return EINVAL;
141
142	return block_cache_make_writable(csi->vol->fBlockCache, csi_to_block(csi),
143		-1);
144}
145
146
147/* XXX: not the most efficient implementation, but it gets the job done */
148status_t
149csi_read_blocks(struct csi *csi, uint8 *buffer, ssize_t len)
150{
151	struct csi old_csi;
152	uint32 sectors;
153	off_t block;
154	status_t err;
155	uint8 *buf = buffer;
156	int32 i;
157
158	ASSERT(len > 0 && (size_t)len >= csi->vol->bytes_per_sector);
159
160	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
161		return EINVAL;
162
163	sectors = 1;
164	block = csi_to_block(csi);
165
166	while (1) {
167		old_csi = *csi;
168		err = iter_csi(csi, 1);
169		if ((size_t)len < (sectors + 1) * csi->vol->bytes_per_sector)
170			break;
171		if ((err < B_OK) || (block + sectors != csi_to_block(csi)))
172			break;
173		sectors++;
174	}
175
176	for (i = block; i < block + sectors; i++) {
177		char *blockData = (char *)block_cache_get(csi->vol->fBlockCache, i);
178		memcpy(buf, blockData, csi->vol->bytes_per_sector);
179		buf += csi->vol->bytes_per_sector;
180		block_cache_put(csi->vol->fBlockCache, i);
181	}
182
183	*csi = old_csi;
184
185	return sectors * csi->vol->bytes_per_sector;
186}
187
188
189status_t
190csi_write_blocks(struct csi *csi, uint8 *buffer, ssize_t len)
191{
192	struct csi old_csi;
193	uint32 sectors;
194	off_t block;
195	status_t err;
196	uint8 *buf = buffer;
197	int32 i;
198
199	ASSERT(len > 0 && (size_t)len >= csi->vol->bytes_per_sector);
200
201	ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
202	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
203		return EINVAL;
204
205	sectors = 1;
206	block = csi_to_block(csi);
207
208	while (1) {
209		old_csi = *csi;
210		err = iter_csi(csi, 1);
211		if ((size_t)len < (sectors + 1) * csi->vol->bytes_per_sector)
212			break;
213		if ((err < B_OK) || (block + sectors != csi_to_block(csi)))
214			break;
215		sectors++;
216	}
217
218	for (i = block; i < block + sectors; i++) {
219		char *blockData;
220		status_t status = block_cache_get_writable_etc(csi->vol->fBlockCache,
221			i, 0, 1, -1, (void**)&blockData);
222		if (status != B_OK)
223			return status;
224
225		memcpy(blockData, buf, csi->vol->bytes_per_sector);
226		buf += csi->vol->bytes_per_sector;
227		block_cache_put(csi->vol->fBlockCache, i);
228	}
229
230	/* return the last state of the iterator because that's what dosfs_write
231	 * expects. this lets it meaningfully cache the state even when it's
232	 * writing to the end of the file. */
233	*csi = old_csi;
234
235	return sectors * csi->vol->bytes_per_sector;
236}
237
238
239status_t
240csi_write_block(struct csi *csi, uint8 *buffer)
241{
242	status_t status;
243	off_t block;
244	char *blockData;
245
246	block = csi_to_block(csi);
247
248	ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
249	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
250		return EINVAL;
251
252	status = block_cache_get_writable_etc(csi->vol->fBlockCache, block, 0, 1,
253		-1, (void**)&blockData);
254	if (status != B_OK)
255		return status;
256
257	memcpy(blockData, buffer, csi->vol->bytes_per_sector);
258	block_cache_put(csi->vol->fBlockCache, block);
259
260	return B_OK;
261}
262
263
264//	#pragma mark -
265
266
267static void
268_diri_release_current_block_(struct diri *diri)
269{
270	ASSERT(diri->current_block);
271	if (diri->current_block == NULL)
272		return;
273	csi_release_block(&(diri->csi));
274	diri->current_block = NULL;
275}
276
277
278uint8 *
279diri_init(nspace *vol, uint32 cluster, uint32 index, struct diri *diri)
280{
281	if (diri->current_block != NULL)
282		_diri_release_current_block_(diri);
283
284	if (cluster >= vol->total_clusters + 2)
285		return NULL;
286
287	if (init_csi(vol,cluster,0,&(diri->csi)) != 0)
288		return NULL;
289
290	diri->starting_cluster = cluster;
291	diri->current_index = index;
292	if (index >= vol->bytes_per_sector / 0x20
293		&& iter_csi(&(diri->csi), diri->current_index
294				/ (vol->bytes_per_sector / 0x20)) != 0)
295		return NULL;
296
297	// get current sector
298	diri->current_block = csi_get_block(&diri->csi);
299
300	if (diri->current_block == NULL)
301		return NULL;
302
303	// now the diri is valid
304	return diri->current_block
305		+ (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
306}
307
308
309diri::diri()
310{
311	csi.vol = NULL;
312	csi.cluster = 0;
313	csi.sector = 0;
314
315	current_block = NULL;
316}
317
318
319diri::~diri()
320{
321	if (current_block != NULL)
322		_diri_release_current_block_(this);
323}
324
325
326uint8 *
327diri_current_entry(struct diri *diri)
328{
329	if (diri->current_block == NULL)
330		return NULL;
331
332	return diri->current_block
333		+ (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
334}
335
336
337uint8 *
338diri_next_entry(struct diri *diri)
339{
340	if (diri->current_block == NULL)
341		return NULL;
342
343	if ((++diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20)) == 0) {
344		_diri_release_current_block_(diri);
345		if (iter_csi(&(diri->csi), 1) != 0)
346			return NULL;
347		diri->current_block = csi_get_block(&(diri->csi));
348		if (diri->current_block == NULL)
349			return NULL;
350	}
351
352	return diri->current_block
353		+ (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
354}
355
356
357uint8 *
358diri_rewind(struct diri *diri)
359{
360	if (diri->current_index > (diri->csi.vol->bytes_per_sector / 0x20 - 1)) {
361		if (diri->current_block)
362			_diri_release_current_block_(diri);
363		if (init_csi(diri->csi.vol, diri->starting_cluster, 0, &(diri->csi)) != 0)
364			return NULL;
365		diri->current_block = csi_get_block(&diri->csi);
366	}
367	diri->current_index = 0;
368	return diri->current_block;
369}
370
371
372void
373diri_make_writable(struct diri *diri)
374{
375	csi_make_writable(&diri->csi);
376}
377