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