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#include <KernelExport.h>
6
7#include <fsproto.h>
8#include <lock.h>
9#include <cache.h>
10
11#include "iter.h"
12#include "dosfs.h"
13#include "fat.h"
14#include "util.h"
15
16#define DPRINTF(a,b) if (debug_iter > (a)) dprintf b
17
18CHECK_MAGIC(diri,struct diri,DIRI_MAGIC)
19
20static int _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
37static off_t _csi_to_block_(struct csi *csi)
38{
39	// presumes the caller has already called _validate_cs_ on the argument
40	ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
41	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
42		return EINVAL;
43
44	if (IS_FIXED_ROOT(csi->cluster))
45		return csi->vol->root_start + csi->sector;
46
47	return csi->vol->data_start +
48		(off_t)(csi->cluster - 2)* csi->vol->sectors_per_cluster +
49		csi->sector;
50}
51
52int init_csi(nspace *vol, uint32 cluster, uint32 sector, struct csi *csi)
53{
54	int ret;
55	if ((ret = _validate_cs_(vol,cluster,sector)) != 0)
56		return ret;
57
58	csi->vol = vol; csi->cluster = cluster; csi->sector = sector;
59
60	return 0;
61}
62
63int iter_csi(struct csi *csi, int sectors)
64{
65	if (csi->sector == 0xffff) // check if already at end of chain
66		return -1;
67
68	if (sectors < 0)
69		return EINVAL;
70
71	if (sectors == 0)
72		return 0;
73
74	if (IS_FIXED_ROOT(csi->cluster)) {
75		csi->sector += sectors;
76		if (csi->sector < csi->vol->root_sectors)
77			return 0;
78	} else {
79		csi->sector += sectors;
80		if (csi->sector < csi->vol->sectors_per_cluster)
81			return 0;
82		csi->cluster = get_nth_fat_entry(csi->vol, csi->cluster, csi->sector / csi->vol->sectors_per_cluster);
83
84		if ((int32)csi->cluster < 0) {
85			csi->sector = 0xffff;
86			return csi->cluster;
87		}
88
89		if (vIS_DATA_CLUSTER(csi->vol,csi->cluster)) {
90			csi->sector %= csi->vol->sectors_per_cluster;
91			return 0;
92		}
93	}
94
95	csi->sector = 0xffff;
96
97	return -1;
98}
99
100uint8 *csi_get_block(struct csi *csi)
101{
102	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
103		return NULL;
104
105	return get_block(csi->vol->fd, _csi_to_block_(csi), csi->vol->bytes_per_sector);
106}
107
108status_t csi_release_block(struct csi *csi)
109{
110	status_t err;
111
112	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
113		return EINVAL;
114
115	err = release_block(csi->vol->fd, _csi_to_block_(csi));
116	ASSERT(err == B_OK);
117	return err;
118}
119
120status_t csi_mark_block_dirty(struct csi *csi)
121{
122	status_t err;
123
124	ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
125	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
126		return EINVAL;
127
128	err = mark_blocks_dirty(csi->vol->fd, _csi_to_block_(csi), 1);
129	ASSERT(err == B_OK);
130	return err;
131}
132
133/* XXX: not the most efficient implementation, but it gets the job done */
134status_t csi_read_blocks(struct csi *csi, uint8 *buffer, ssize_t len)
135{
136	struct csi old_csi;
137	uint32 sectors;
138	off_t block;
139	status_t err;
140
141	ASSERT(len >= csi->vol->bytes_per_sector);
142
143	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
144		return EINVAL;
145
146	sectors = 1;
147	block = _csi_to_block_(csi);
148
149	while (1) {
150		old_csi = *csi;
151		err = iter_csi(csi, 1);
152		if (len < (sectors + 1) * csi->vol->bytes_per_sector)
153			break;
154		if ((err < B_OK) || (block + sectors != _csi_to_block_(csi)))
155			break;
156		sectors++;
157	}
158
159	err = cached_read(csi->vol->fd, block, buffer, sectors, csi->vol->bytes_per_sector);
160	if (err < B_OK)
161		return err;
162
163	*csi = old_csi;
164
165	return sectors * csi->vol->bytes_per_sector;
166}
167
168status_t csi_write_blocks(struct csi *csi, uint8 *buffer, ssize_t len)
169{
170	struct csi old_csi;
171	uint32 sectors;
172	off_t block;
173	status_t err;
174
175	ASSERT(len >= csi->vol->bytes_per_sector);
176
177	ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
178	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
179		return EINVAL;
180
181	sectors = 1;
182	block = _csi_to_block_(csi);
183
184	while (1) {
185		old_csi = *csi;
186		err = iter_csi(csi, 1);
187		if (len < (sectors + 1) * csi->vol->bytes_per_sector)
188			break;
189		if ((err < B_OK) || (block + sectors != _csi_to_block_(csi)))
190			break;
191		sectors++;
192	}
193
194	err = cached_write(csi->vol->fd, block, buffer, sectors, csi->vol->bytes_per_sector);
195	if (err < B_OK)
196		return err;
197
198	/* return the last state of the iterator because that's what dosfs_write
199	 * expects. this lets it meaningfully cache the state even when it's
200	 * writing to the end of the file. */
201	*csi = old_csi;
202
203	return sectors * csi->vol->bytes_per_sector;
204}
205
206status_t csi_write_block(struct csi *csi, uint8 *buffer)
207{
208	ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
209	if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
210		return EINVAL;
211
212	return cached_write(csi->vol->fd, _csi_to_block_(csi), buffer, 1, csi->vol->bytes_per_sector);
213}
214
215static void _diri_release_current_block_(struct diri *diri)
216{
217	ASSERT(diri->current_block);
218	if (diri->current_block == NULL)
219		return;
220	csi_release_block(&(diri->csi));
221	diri->current_block = NULL;
222}
223
224uint8 *diri_init(nspace *vol, uint32 cluster, uint32 index, struct diri *diri)
225{
226	diri->magic = DIRI_MAGIC;
227	diri->current_block = NULL;
228
229	if (cluster >= vol->total_clusters + 2)
230		return NULL;
231
232	if (init_csi(vol,cluster,0,&(diri->csi)) != 0)
233		return NULL;
234
235	diri->starting_cluster = cluster;
236	diri->current_index = index;
237	if (index >= vol->bytes_per_sector / 0x20) {
238		if (iter_csi(&(diri->csi), diri->current_index / (vol->bytes_per_sector / 0x20)) != 0)
239			return NULL;
240	}
241
242	// get current sector
243	diri->current_block = csi_get_block(&(diri->csi));
244
245	if (diri->current_block == NULL)
246		return NULL;
247
248	return diri->current_block + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
249}
250
251int diri_free(struct diri *diri)
252{
253	if (check_diri_magic(diri, "diri_free")) return EINVAL;
254
255	diri->magic = ~DIRI_MAGIC; // trash magic number
256
257	if (diri->current_block)
258		_diri_release_current_block_(diri);
259
260	return 0;
261}
262
263uint8 *diri_current_entry(struct diri *diri)
264{
265	if (check_diri_magic(diri, "diri_current_entry")) return NULL;
266
267	if (diri->current_block == NULL)
268		return NULL;
269
270	return diri->current_block + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
271}
272
273uint8 *diri_next_entry(struct diri *diri)
274{
275	if (check_diri_magic(diri, "diri_next_entry")) return NULL;
276
277	if (diri->current_block == NULL)
278		return NULL;
279
280	if ((++diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20)) == 0) {
281		_diri_release_current_block_(diri);
282		if (iter_csi(&(diri->csi), 1) != 0)
283			return NULL;
284		diri->current_block = csi_get_block(&(diri->csi));
285		if (diri->current_block == NULL)
286			return NULL;
287	}
288
289	return diri->current_block + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20;
290}
291
292uint8 *diri_rewind(struct diri *diri)
293{
294	if (check_diri_magic(diri, "diri_rewind")) return NULL;
295
296	if (diri->current_index > (diri->csi.vol->bytes_per_sector / 0x20 - 1)) {
297		if (diri->current_block)
298			_diri_release_current_block_(diri);
299		if (init_csi(diri->csi.vol, diri->starting_cluster, 0, &(diri->csi)) != 0)
300			return NULL;
301		diri->current_block = csi_get_block(&(diri->csi));
302	}
303	diri->current_index = 0;
304	return diri->current_block;
305}
306
307void diri_mark_dirty(struct diri *diri)
308{
309	csi_mark_block_dirty(&(diri->csi));
310}
311