Project

General

Profile

Statistics
| Branch: | Revision:

root / prex-0.9.0 / usr / server / fs / fatfs / fatfs_fat.c @ 03e9c04a

History | View | Annotate | Download (8.19 KB)

1
/*
2
 * Copyright (c) 2005-2008, Kohsuke Ohtani
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 * 3. Neither the name of the author nor the names of any co-contributors
14
 *    may be used to endorse or promote products derived from this software
15
 *    without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29

    
30
#include <sys/prex.h>
31
#include <sys/buf.h>
32

    
33
#include <ctype.h>
34
#include <errno.h>
35
#include <string.h>
36
#include <stdlib.h>
37

    
38
#include "fatfs.h"
39

    
40
/*
41
 * Read the FAT entry for specified cluster.
42
 */
43
static int
44
read_fat_entry(struct fatfsmount *fmp, u_long cl)
45
{
46
        u_long sec;
47
        char *buf = fmp->fat_buf;
48
        int error, border = 0;
49
        struct buf *bp;
50

    
51
        /* Get the sector number in FAT entry. */
52
        if (FAT16(fmp))
53
                sec = (cl * 2) / SEC_SIZE;
54
        else {
55
                sec = (cl * 3 / 2) / SEC_SIZE;
56
                /*
57
                 * Check if the entry data is placed at the
58
                 * end of sector. If so, we have to read one
59
                 * more sector to get complete FAT12 entry.
60
                 */
61
                if ((cl * 3 / 2) % SEC_SIZE == SEC_SIZE - 1)
62
                        border = 1;
63
        }
64
        sec += fmp->fat_start;
65

    
66
        /* Read first sector. */
67
        if ((error = bread(fmp->dev, sec, &bp)) != 0)
68
                return error;
69
        memcpy(buf, bp->b_data, SEC_SIZE);
70
        brelse(bp);
71

    
72
        if (!FAT12(fmp) || border == 0)
73
                return 0;
74

    
75
        /* Read second sector for the border entry of FAT12. */
76
        if ((error = bread(fmp->dev, sec + 1, &bp)) != 0)
77
                return error;
78
        memcpy(buf + SEC_SIZE, bp->b_data, SEC_SIZE);
79
        brelse(bp);
80
        return 0;
81
}
82

    
83
/*
84
 * Write fat entry from buffer.
85
 */
86
static int
87
write_fat_entry(struct fatfsmount *fmp, u_long cl)
88
{
89
        u_long sec;
90
        char *buf = fmp->fat_buf;
91
        int error, border = 0;
92
        struct buf *bp;
93

    
94
        /* Get the sector number in FAT entry. */
95
        if (FAT16(fmp))
96
                sec = (cl * 2) / SEC_SIZE;
97
        else {
98
                sec = (cl * 3 / 2) / SEC_SIZE;
99
                /* Check if border entry for FAT12 */
100
                if ((cl * 3 / 2) % SEC_SIZE == SEC_SIZE - 1)
101
                        border = 1;
102
        }
103
        sec += fmp->fat_start;
104

    
105
        /* Write first sector. */
106
        bp = getblk(fmp->dev, sec);
107
        memcpy(bp->b_data, buf, SEC_SIZE);
108
        if ((error = bwrite(bp)) != 0)
109
                return error;
110

    
111
        if (!FAT12(fmp) || border == 0)
112
                return 0;
113

    
114
        /* Write second sector for the border entry of FAT12. */
115
        bp = getblk(fmp->dev, sec + 1);
116
        memcpy(bp->b_data, buf + SEC_SIZE, SEC_SIZE);
117
        error = bwrite(bp);
118
        return error;
119
}
120

    
121
/*
122
 * Get next cluster number of FAT chain.
123
 * @fmp: fat mount data
124
 * @cl: previous cluster#
125
 * @next: next cluster# to return
126
 */
127
int
128
fat_next_cluster(struct fatfsmount *fmp, u_long cl, u_long *next)
129
{
130
        u_int offset;
131
        uint16_t val;
132
        int error;
133

    
134
        /* Read FAT entry */
135
        error = read_fat_entry(fmp, cl);
136
        if (error)
137
                return error;
138

    
139
        /* Get offset in buffer. */
140
        if (FAT16(fmp))
141
                offset = (cl * 2) % SEC_SIZE;
142
        else
143
                offset = (cl * 3 / 2) % SEC_SIZE;
144

    
145
        /* Pick up cluster# */
146
        val = *((uint16_t *)(fmp->fat_buf + offset));
147

    
148
        /* Adjust data for FAT12 entry */
149
        if (FAT12(fmp)) {
150
                if (cl & 1)
151
                        val >>= 4;
152
                else
153
                        val &= 0xfff;
154
        }
155
        *next = (u_long)val;
156
        DPRINTF(("fat_next_cluster: %d => %d\n", cl, *next));
157
        return 0;
158
}
159

    
160
/*
161
 * Set next cluster number in FAT chain.
162
 * @fmp: fat mount data
163
 * @cl: previous cluster#
164
 * @next: cluster# to set (can be eof)
165
 */
166
int
167
fat_set_cluster(struct fatfsmount *fmp, u_long cl, u_long next)
168
{
169
        u_int offset;
170
        char *buf = fmp->fat_buf;
171
        int error;
172
        uint16_t val, tmp;
173

    
174
        /* Read FAT entry */
175
        error = read_fat_entry(fmp, cl);
176
        if (error)
177
                return error;
178

    
179
        /* Get offset in buffer. */
180
        if (FAT16(fmp))
181
                offset = (cl * 2) % SEC_SIZE;
182
        else
183
                offset = (cl * 3 / 2) % SEC_SIZE;
184

    
185
        /* Modify FAT entry for target cluster. */
186
        val = (uint16_t)(next & fmp->fat_mask);
187
        if (FAT12(fmp)) {
188
                tmp = *((uint16_t *)(buf + offset));
189
                if (cl & 1) {
190
                        val <<= 4;
191
                        val |= (tmp & 0xf);
192
                } else {
193
                        tmp &= 0xf000;
194
                        val |= tmp;
195
                }
196
        }
197
        *((uint16_t *)(buf + offset)) = val;
198

    
199
        /* Write FAT entry */
200
        error = write_fat_entry(fmp, cl);
201
        return error;
202
}
203

    
204
/*
205
 * Allocate free cluster in FAT chain.
206
 *
207
 * @fmp: fat mount data
208
 * @scan_start: cluster# to scan first. If 0, use the previous used value.
209
 * @free: allocated cluster# to return
210
 */
211
int
212
fat_alloc_cluster(struct fatfsmount *fmp, u_long scan_start, u_long *free)
213
{
214
        u_long cl, next;
215
        int error;
216

    
217
        if (scan_start == 0)
218
                scan_start = fmp->free_scan;
219

    
220
        DPRINTF(("fat_alloc_cluster: start=%d\n", scan_start));
221

    
222
        cl = scan_start + 1;
223
        while (cl != scan_start) {
224
                error = fat_next_cluster(fmp, cl, &next);
225
                if (error)
226
                        return error;
227
                if (next == CL_FREE) {        /* free ? */
228
                        DPRINTF(("fat_alloc_cluster: free cluster=%d\n", cl));
229
                        *free = cl;
230
                        return 0;
231
                }
232
                if (++cl >= fmp->last_cluster)
233
                        cl = CL_FIRST;
234
        }
235
        return ENOSPC;                /* no space */
236
}
237

    
238
/*
239
 * Deallocate needless cluster.
240
 * @fmp: fat mount data
241
 * @start: first cluster# of FAT chain
242
 */
243
int
244
fat_free_clusters(struct fatfsmount *fmp, u_long start)
245
{
246
        int error;
247
        u_long cl, next;
248

    
249
        cl = start;
250
        if (cl < CL_FIRST)
251
                return EINVAL;
252

    
253
        while (!IS_EOFCL(fmp, cl)) {
254
                error = fat_next_cluster(fmp, cl, &next);
255
                if (error)
256
                        return error;
257
                error = fat_set_cluster(fmp, cl, CL_FREE);
258
                if (error)
259
                        return error;
260
                cl = next;
261
        }
262
        /* Clear eof */
263
        error = fat_set_cluster(fmp, cl, CL_FREE);
264
        if (error)
265
                return error;
266
        return 0;
267
}
268

    
269
/*
270
 * Get the cluster# for the specific file offset.
271
 *
272
 * @fmp: fat mount data
273
 * @start: start cluster# of file.
274
 * @offset: file offset
275
 * @cl: cluster# to return
276
 */
277
int
278
fat_seek_cluster(struct fatfsmount *fmp, u_long start, u_long offset,
279
                 u_long *cl)
280
{
281
        int error, i;
282
        u_long c, target;
283

    
284
        if (start > fmp->last_cluster)
285
                return EIO;
286

    
287
        c = start;
288
        target = offset / fmp->cluster_size;
289
        for (i = 0; i < target; i++) {
290
                error = fat_next_cluster(fmp, c, &c);
291
                if (error)
292
                        return error;
293
                if (IS_EOFCL(fmp, c))
294
                        return EIO;
295
        }
296
        *cl = c;
297
        return 0;
298
}
299

    
300
/*
301
 * Expand file size.
302
 *
303
 * @fmp: fat mount data
304
 * @cl: cluster# of target file.
305
 * @size: new size of file in bytes.
306
 */
307
int
308
fat_expand_file(struct fatfsmount *fmp, u_long cl, int size)
309
{
310
        int i, cl_len, alloc, error;
311
        u_long next;
312

    
313
        alloc = 0;
314
        cl_len = size / fmp->cluster_size + 1;
315

    
316
        for (i = 0; i < cl_len; i++) {
317
                error = fat_next_cluster(fmp, cl, &next);
318
                if (error)
319
                        return error;
320
                if (alloc || next >= fmp->fat_eof) {
321
                        error = fat_alloc_cluster(fmp, cl, &next);
322
                        if (error)
323
                                return error;
324
                        alloc = 1;
325
                }
326
                if (alloc) {
327
                        error = fat_set_cluster(fmp, cl, next);
328
                        if (error)
329
                                return error;
330
                }
331
                cl = next;
332
        }
333
        if (alloc)
334
                fat_set_cluster(fmp, cl, fmp->fat_eof);        /* add eof */
335
        DPRINTF(("fat_expand_file: new size=%d\n", size));
336
        return 0;
337
}
338

    
339
/*
340
 * Expand directory size.
341
 *
342
 * @fmp: fat mount data
343
 * @cl: cluster# of target directory
344
 * @new_cl: cluster# for new directory to return
345
 *
346
 * Note: The root directory can not be expanded.
347
 */
348
int
349
fat_expand_dir(struct fatfsmount *fmp, u_long cl, u_long *new_cl)
350
{
351
        int error;
352
        u_long next;
353

    
354
        /* Find last cluster number of FAT chain. */
355
        while (!IS_EOFCL(fmp, cl)) {
356
                error = fat_next_cluster(fmp, cl, &next);
357
                if (error)
358
                        return error;
359
                cl = next;
360
        }
361

    
362
        error = fat_alloc_cluster(fmp, cl, &next);
363
        if (error)
364
                return error;
365

    
366
        error = fat_set_cluster(fmp, cl, next);
367
        if (error)
368
                return error;
369

    
370
        error = fat_set_cluster(fmp, next, fmp->fat_eof);
371
        if (error)
372
                return error;
373

    
374
        *new_cl = next;
375
        return 0;
376
}