1*9558e6acSTreehugger Robot /*-
2*9558e6acSTreehugger Robot * SPDX-License-Identifier: BSD-2-Clause
3*9558e6acSTreehugger Robot *
4*9558e6acSTreehugger Robot * Copyright (c) 2019 Google LLC
5*9558e6acSTreehugger Robot * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
6*9558e6acSTreehugger Robot * Copyright (c) 1995 Martin Husemann
7*9558e6acSTreehugger Robot *
8*9558e6acSTreehugger Robot * Redistribution and use in source and binary forms, with or without
9*9558e6acSTreehugger Robot * modification, are permitted provided that the following conditions
10*9558e6acSTreehugger Robot * are met:
11*9558e6acSTreehugger Robot * 1. Redistributions of source code must retain the above copyright
12*9558e6acSTreehugger Robot * notice, this list of conditions and the following disclaimer.
13*9558e6acSTreehugger Robot * 2. Redistributions in binary form must reproduce the above copyright
14*9558e6acSTreehugger Robot * notice, this list of conditions and the following disclaimer in the
15*9558e6acSTreehugger Robot * documentation and/or other materials provided with the distribution.
16*9558e6acSTreehugger Robot *
17*9558e6acSTreehugger Robot * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
18*9558e6acSTreehugger Robot * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19*9558e6acSTreehugger Robot * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20*9558e6acSTreehugger Robot * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21*9558e6acSTreehugger Robot * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22*9558e6acSTreehugger Robot * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23*9558e6acSTreehugger Robot * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24*9558e6acSTreehugger Robot * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25*9558e6acSTreehugger Robot * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26*9558e6acSTreehugger Robot * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*9558e6acSTreehugger Robot */
28*9558e6acSTreehugger Robot
29*9558e6acSTreehugger Robot
30*9558e6acSTreehugger Robot #include <sys/cdefs.h>
31*9558e6acSTreehugger Robot #ifndef lint
32*9558e6acSTreehugger Robot __RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $");
33*9558e6acSTreehugger Robot static const char rcsid[] =
34*9558e6acSTreehugger Robot "$FreeBSD$";
35*9558e6acSTreehugger Robot #endif /* not lint */
36*9558e6acSTreehugger Robot
37*9558e6acSTreehugger Robot #include <sys/endian.h>
38*9558e6acSTreehugger Robot #include <sys/queue.h>
39*9558e6acSTreehugger Robot #include <sys/limits.h>
40*9558e6acSTreehugger Robot #include <sys/mman.h>
41*9558e6acSTreehugger Robot #include <sys/param.h>
42*9558e6acSTreehugger Robot
43*9558e6acSTreehugger Robot #include <assert.h>
44*9558e6acSTreehugger Robot #include <stdbool.h>
45*9558e6acSTreehugger Robot #include <stdlib.h>
46*9558e6acSTreehugger Robot #include <string.h>
47*9558e6acSTreehugger Robot #include <ctype.h>
48*9558e6acSTreehugger Robot #include <stdio.h>
49*9558e6acSTreehugger Robot #include <unistd.h>
50*9558e6acSTreehugger Robot
51*9558e6acSTreehugger Robot #include "ext.h"
52*9558e6acSTreehugger Robot #include "fsutil.h"
53*9558e6acSTreehugger Robot
54*9558e6acSTreehugger Robot static int _readfat(struct fat_descriptor *);
55*9558e6acSTreehugger Robot static inline struct bootblock* boot_of_(struct fat_descriptor *);
56*9558e6acSTreehugger Robot static inline int fd_of_(struct fat_descriptor *);
57*9558e6acSTreehugger Robot static inline bool valid_cl(struct fat_descriptor *, cl_t);
58*9558e6acSTreehugger Robot
59*9558e6acSTreehugger Robot
60*9558e6acSTreehugger Robot /*
61*9558e6acSTreehugger Robot * Head bitmap for FAT scanning.
62*9558e6acSTreehugger Robot *
63*9558e6acSTreehugger Robot * FAT32 have up to 2^28 = 256M entries, and FAT16/12 have much less.
64*9558e6acSTreehugger Robot * For each cluster, we use 1 bit to represent if it's a head cluster
65*9558e6acSTreehugger Robot * (the first cluster of a cluster chain).
66*9558e6acSTreehugger Robot *
67*9558e6acSTreehugger Robot * Head bitmap
68*9558e6acSTreehugger Robot * ===========
69*9558e6acSTreehugger Robot * Initially, we set all bits to 1. In readfat(), we traverse the
70*9558e6acSTreehugger Robot * whole FAT and mark each cluster identified as "next" cluster as
71*9558e6acSTreehugger Robot * 0. After the scan, we have a bitmap with 1's to indicate the
72*9558e6acSTreehugger Robot * corresponding cluster was a "head" cluster.
73*9558e6acSTreehugger Robot *
74*9558e6acSTreehugger Robot * We use head bitmap to identify lost chains: a head cluster that was
75*9558e6acSTreehugger Robot * not being claimed by any file or directories is the head cluster of
76*9558e6acSTreehugger Robot * a lost chain.
77*9558e6acSTreehugger Robot *
78*9558e6acSTreehugger Robot * Handle of lost chains
79*9558e6acSTreehugger Robot * =====================
80*9558e6acSTreehugger Robot * At the end of scanning, we can easily find all lost chain's heads
81*9558e6acSTreehugger Robot * by finding out the 1's in the head bitmap.
82*9558e6acSTreehugger Robot */
83*9558e6acSTreehugger Robot
84*9558e6acSTreehugger Robot typedef struct long_bitmap {
85*9558e6acSTreehugger Robot unsigned long *map;
86*9558e6acSTreehugger Robot size_t count; /* Total set bits in the map */
87*9558e6acSTreehugger Robot } long_bitmap_t;
88*9558e6acSTreehugger Robot
89*9558e6acSTreehugger Robot static inline void
bitmap_clear(long_bitmap_t * lbp,cl_t cl)90*9558e6acSTreehugger Robot bitmap_clear(long_bitmap_t *lbp, cl_t cl)
91*9558e6acSTreehugger Robot {
92*9558e6acSTreehugger Robot cl_t i = cl / LONG_BIT;
93*9558e6acSTreehugger Robot unsigned long clearmask = ~(1UL << (cl % LONG_BIT));
94*9558e6acSTreehugger Robot
95*9558e6acSTreehugger Robot assert((lbp->map[i] & ~clearmask) != 0);
96*9558e6acSTreehugger Robot lbp->map[i] &= clearmask;
97*9558e6acSTreehugger Robot lbp->count--;
98*9558e6acSTreehugger Robot }
99*9558e6acSTreehugger Robot
100*9558e6acSTreehugger Robot static inline bool
bitmap_get(long_bitmap_t * lbp,cl_t cl)101*9558e6acSTreehugger Robot bitmap_get(long_bitmap_t *lbp, cl_t cl)
102*9558e6acSTreehugger Robot {
103*9558e6acSTreehugger Robot cl_t i = cl / LONG_BIT;
104*9558e6acSTreehugger Robot unsigned long usedbit = 1UL << (cl % LONG_BIT);
105*9558e6acSTreehugger Robot
106*9558e6acSTreehugger Robot return ((lbp->map[i] & usedbit) == usedbit);
107*9558e6acSTreehugger Robot }
108*9558e6acSTreehugger Robot
109*9558e6acSTreehugger Robot static inline bool
bitmap_none_in_range(long_bitmap_t * lbp,cl_t cl)110*9558e6acSTreehugger Robot bitmap_none_in_range(long_bitmap_t *lbp, cl_t cl)
111*9558e6acSTreehugger Robot {
112*9558e6acSTreehugger Robot cl_t i = cl / LONG_BIT;
113*9558e6acSTreehugger Robot
114*9558e6acSTreehugger Robot return (lbp->map[i] == 0);
115*9558e6acSTreehugger Robot }
116*9558e6acSTreehugger Robot
117*9558e6acSTreehugger Robot static inline size_t
bitmap_count(long_bitmap_t * lbp)118*9558e6acSTreehugger Robot bitmap_count(long_bitmap_t *lbp)
119*9558e6acSTreehugger Robot {
120*9558e6acSTreehugger Robot return (lbp->count);
121*9558e6acSTreehugger Robot }
122*9558e6acSTreehugger Robot
123*9558e6acSTreehugger Robot static int
bitmap_ctor(long_bitmap_t * lbp,size_t bits,bool allone)124*9558e6acSTreehugger Robot bitmap_ctor(long_bitmap_t *lbp, size_t bits, bool allone)
125*9558e6acSTreehugger Robot {
126*9558e6acSTreehugger Robot size_t bitmap_size = roundup2(bits, LONG_BIT) / (LONG_BIT / 8);
127*9558e6acSTreehugger Robot
128*9558e6acSTreehugger Robot free(lbp->map);
129*9558e6acSTreehugger Robot lbp->map = calloc(1, bitmap_size);
130*9558e6acSTreehugger Robot if (lbp->map == NULL)
131*9558e6acSTreehugger Robot return FSFATAL;
132*9558e6acSTreehugger Robot
133*9558e6acSTreehugger Robot if (allone) {
134*9558e6acSTreehugger Robot memset(lbp->map, 0xff, bitmap_size);
135*9558e6acSTreehugger Robot lbp->count = bits;
136*9558e6acSTreehugger Robot } else {
137*9558e6acSTreehugger Robot lbp->count = 0;
138*9558e6acSTreehugger Robot }
139*9558e6acSTreehugger Robot return FSOK;
140*9558e6acSTreehugger Robot }
141*9558e6acSTreehugger Robot
142*9558e6acSTreehugger Robot static void
bitmap_dtor(long_bitmap_t * lbp)143*9558e6acSTreehugger Robot bitmap_dtor(long_bitmap_t *lbp)
144*9558e6acSTreehugger Robot {
145*9558e6acSTreehugger Robot free(lbp->map);
146*9558e6acSTreehugger Robot lbp->map = NULL;
147*9558e6acSTreehugger Robot }
148*9558e6acSTreehugger Robot
149*9558e6acSTreehugger Robot /*
150*9558e6acSTreehugger Robot * FAT32 can be as big as 256MiB (2^26 entries * 4 bytes), when we
151*9558e6acSTreehugger Robot * can not ask the kernel to manage the access, use a simple LRU
152*9558e6acSTreehugger Robot * cache with chunk size of 128 KiB to manage it.
153*9558e6acSTreehugger Robot */
154*9558e6acSTreehugger Robot struct fat32_cache_entry {
155*9558e6acSTreehugger Robot TAILQ_ENTRY(fat32_cache_entry) entries;
156*9558e6acSTreehugger Robot uint8_t *chunk; /* pointer to chunk */
157*9558e6acSTreehugger Robot off_t addr; /* offset */
158*9558e6acSTreehugger Robot bool dirty; /* dirty bit */
159*9558e6acSTreehugger Robot };
160*9558e6acSTreehugger Robot
161*9558e6acSTreehugger Robot static const size_t fat32_cache_chunk_size = 131072; /* MAXPHYS */
162*9558e6acSTreehugger Robot static const size_t fat32_cache_size = 4194304;
163*9558e6acSTreehugger Robot static const size_t fat32_cache_entries = 32; /* XXXgcc: cache_size / cache_chunk_size */
164*9558e6acSTreehugger Robot
165*9558e6acSTreehugger Robot /*
166*9558e6acSTreehugger Robot * FAT table descriptor, represents a FAT table that is already loaded
167*9558e6acSTreehugger Robot * into memory.
168*9558e6acSTreehugger Robot */
169*9558e6acSTreehugger Robot struct fat_descriptor {
170*9558e6acSTreehugger Robot struct bootblock *boot;
171*9558e6acSTreehugger Robot uint8_t *fatbuf;
172*9558e6acSTreehugger Robot cl_t (*get)(struct fat_descriptor *, cl_t);
173*9558e6acSTreehugger Robot int (*set)(struct fat_descriptor *, cl_t, cl_t);
174*9558e6acSTreehugger Robot long_bitmap_t headbitmap;
175*9558e6acSTreehugger Robot int fd;
176*9558e6acSTreehugger Robot bool is_mmapped;
177*9558e6acSTreehugger Robot bool use_cache;
178*9558e6acSTreehugger Robot size_t fatsize;
179*9558e6acSTreehugger Robot
180*9558e6acSTreehugger Robot size_t fat32_cached_chunks;
181*9558e6acSTreehugger Robot TAILQ_HEAD(cachehead, fat32_cache_entry) fat32_cache_head;
182*9558e6acSTreehugger Robot struct fat32_cache_entry *fat32_cache_allentries;
183*9558e6acSTreehugger Robot off_t fat32_offset;
184*9558e6acSTreehugger Robot off_t fat32_lastaddr;
185*9558e6acSTreehugger Robot };
186*9558e6acSTreehugger Robot
187*9558e6acSTreehugger Robot void
fat_clear_cl_head(struct fat_descriptor * fat,cl_t cl)188*9558e6acSTreehugger Robot fat_clear_cl_head(struct fat_descriptor *fat, cl_t cl)
189*9558e6acSTreehugger Robot {
190*9558e6acSTreehugger Robot bitmap_clear(&fat->headbitmap, cl);
191*9558e6acSTreehugger Robot }
192*9558e6acSTreehugger Robot
193*9558e6acSTreehugger Robot bool
fat_is_cl_head(struct fat_descriptor * fat,cl_t cl)194*9558e6acSTreehugger Robot fat_is_cl_head(struct fat_descriptor *fat, cl_t cl)
195*9558e6acSTreehugger Robot {
196*9558e6acSTreehugger Robot return (bitmap_get(&fat->headbitmap, cl));
197*9558e6acSTreehugger Robot }
198*9558e6acSTreehugger Robot
199*9558e6acSTreehugger Robot static inline bool
fat_is_cl_head_in_range(struct fat_descriptor * fat,cl_t cl)200*9558e6acSTreehugger Robot fat_is_cl_head_in_range(struct fat_descriptor *fat, cl_t cl)
201*9558e6acSTreehugger Robot {
202*9558e6acSTreehugger Robot return (!(bitmap_none_in_range(&fat->headbitmap, cl)));
203*9558e6acSTreehugger Robot }
204*9558e6acSTreehugger Robot
205*9558e6acSTreehugger Robot static size_t
fat_get_head_count(struct fat_descriptor * fat)206*9558e6acSTreehugger Robot fat_get_head_count(struct fat_descriptor *fat)
207*9558e6acSTreehugger Robot {
208*9558e6acSTreehugger Robot return (bitmap_count(&fat->headbitmap));
209*9558e6acSTreehugger Robot }
210*9558e6acSTreehugger Robot
211*9558e6acSTreehugger Robot /*
212*9558e6acSTreehugger Robot * FAT12 accessors.
213*9558e6acSTreehugger Robot *
214*9558e6acSTreehugger Robot * FAT12s are sufficiently small, expect it to always fit in the RAM.
215*9558e6acSTreehugger Robot */
216*9558e6acSTreehugger Robot static inline uint8_t *
fat_get_fat12_ptr(struct fat_descriptor * fat,cl_t cl)217*9558e6acSTreehugger Robot fat_get_fat12_ptr(struct fat_descriptor *fat, cl_t cl)
218*9558e6acSTreehugger Robot {
219*9558e6acSTreehugger Robot return (fat->fatbuf + ((cl + (cl >> 1))));
220*9558e6acSTreehugger Robot }
221*9558e6acSTreehugger Robot
222*9558e6acSTreehugger Robot static cl_t
fat_get_fat12_next(struct fat_descriptor * fat,cl_t cl)223*9558e6acSTreehugger Robot fat_get_fat12_next(struct fat_descriptor *fat, cl_t cl)
224*9558e6acSTreehugger Robot {
225*9558e6acSTreehugger Robot const uint8_t *p;
226*9558e6acSTreehugger Robot cl_t retval;
227*9558e6acSTreehugger Robot
228*9558e6acSTreehugger Robot p = fat_get_fat12_ptr(fat, cl);
229*9558e6acSTreehugger Robot retval = le16dec(p);
230*9558e6acSTreehugger Robot /* Odd cluster: lower 4 bits belongs to the subsequent cluster */
231*9558e6acSTreehugger Robot if ((cl & 1) == 1)
232*9558e6acSTreehugger Robot retval >>= 4;
233*9558e6acSTreehugger Robot retval &= CLUST12_MASK;
234*9558e6acSTreehugger Robot
235*9558e6acSTreehugger Robot if (retval >= (CLUST_BAD & CLUST12_MASK))
236*9558e6acSTreehugger Robot retval |= ~CLUST12_MASK;
237*9558e6acSTreehugger Robot
238*9558e6acSTreehugger Robot return (retval);
239*9558e6acSTreehugger Robot }
240*9558e6acSTreehugger Robot
241*9558e6acSTreehugger Robot static int
fat_set_fat12_next(struct fat_descriptor * fat,cl_t cl,cl_t nextcl)242*9558e6acSTreehugger Robot fat_set_fat12_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl)
243*9558e6acSTreehugger Robot {
244*9558e6acSTreehugger Robot uint8_t *p;
245*9558e6acSTreehugger Robot
246*9558e6acSTreehugger Robot /* Truncate 'nextcl' value, if needed */
247*9558e6acSTreehugger Robot nextcl &= CLUST12_MASK;
248*9558e6acSTreehugger Robot
249*9558e6acSTreehugger Robot p = fat_get_fat12_ptr(fat, cl);
250*9558e6acSTreehugger Robot
251*9558e6acSTreehugger Robot /*
252*9558e6acSTreehugger Robot * Read in the 4 bits from the subsequent (for even clusters)
253*9558e6acSTreehugger Robot * or the preceding (for odd clusters) cluster and combine
254*9558e6acSTreehugger Robot * it to the nextcl value for encoding
255*9558e6acSTreehugger Robot */
256*9558e6acSTreehugger Robot if ((cl & 1) == 0) {
257*9558e6acSTreehugger Robot nextcl |= ((p[1] & 0xf0) << 8);
258*9558e6acSTreehugger Robot } else {
259*9558e6acSTreehugger Robot nextcl <<= 4;
260*9558e6acSTreehugger Robot nextcl |= (p[0] & 0x0f);
261*9558e6acSTreehugger Robot }
262*9558e6acSTreehugger Robot
263*9558e6acSTreehugger Robot le16enc(p, (uint16_t)nextcl);
264*9558e6acSTreehugger Robot
265*9558e6acSTreehugger Robot return (0);
266*9558e6acSTreehugger Robot }
267*9558e6acSTreehugger Robot
268*9558e6acSTreehugger Robot /*
269*9558e6acSTreehugger Robot * FAT16 accessors.
270*9558e6acSTreehugger Robot *
271*9558e6acSTreehugger Robot * FAT16s are sufficiently small, expect it to always fit in the RAM.
272*9558e6acSTreehugger Robot */
273*9558e6acSTreehugger Robot static inline uint8_t *
fat_get_fat16_ptr(struct fat_descriptor * fat,cl_t cl)274*9558e6acSTreehugger Robot fat_get_fat16_ptr(struct fat_descriptor *fat, cl_t cl)
275*9558e6acSTreehugger Robot {
276*9558e6acSTreehugger Robot return (fat->fatbuf + (cl << 1));
277*9558e6acSTreehugger Robot }
278*9558e6acSTreehugger Robot
279*9558e6acSTreehugger Robot static cl_t
fat_get_fat16_next(struct fat_descriptor * fat,cl_t cl)280*9558e6acSTreehugger Robot fat_get_fat16_next(struct fat_descriptor *fat, cl_t cl)
281*9558e6acSTreehugger Robot {
282*9558e6acSTreehugger Robot const uint8_t *p;
283*9558e6acSTreehugger Robot cl_t retval;
284*9558e6acSTreehugger Robot
285*9558e6acSTreehugger Robot p = fat_get_fat16_ptr(fat, cl);
286*9558e6acSTreehugger Robot retval = le16dec(p) & CLUST16_MASK;
287*9558e6acSTreehugger Robot
288*9558e6acSTreehugger Robot if (retval >= (CLUST_BAD & CLUST16_MASK))
289*9558e6acSTreehugger Robot retval |= ~CLUST16_MASK;
290*9558e6acSTreehugger Robot
291*9558e6acSTreehugger Robot return (retval);
292*9558e6acSTreehugger Robot }
293*9558e6acSTreehugger Robot
294*9558e6acSTreehugger Robot static int
fat_set_fat16_next(struct fat_descriptor * fat,cl_t cl,cl_t nextcl)295*9558e6acSTreehugger Robot fat_set_fat16_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl)
296*9558e6acSTreehugger Robot {
297*9558e6acSTreehugger Robot uint8_t *p;
298*9558e6acSTreehugger Robot
299*9558e6acSTreehugger Robot /* Truncate 'nextcl' value, if needed */
300*9558e6acSTreehugger Robot nextcl &= CLUST16_MASK;
301*9558e6acSTreehugger Robot
302*9558e6acSTreehugger Robot p = fat_get_fat16_ptr(fat, cl);
303*9558e6acSTreehugger Robot
304*9558e6acSTreehugger Robot le16enc(p, (uint16_t)nextcl);
305*9558e6acSTreehugger Robot
306*9558e6acSTreehugger Robot return (0);
307*9558e6acSTreehugger Robot }
308*9558e6acSTreehugger Robot
309*9558e6acSTreehugger Robot /*
310*9558e6acSTreehugger Robot * FAT32 accessors.
311*9558e6acSTreehugger Robot */
312*9558e6acSTreehugger Robot static inline uint8_t *
fat_get_fat32_ptr(struct fat_descriptor * fat,cl_t cl)313*9558e6acSTreehugger Robot fat_get_fat32_ptr(struct fat_descriptor *fat, cl_t cl)
314*9558e6acSTreehugger Robot {
315*9558e6acSTreehugger Robot return (fat->fatbuf + (cl << 2));
316*9558e6acSTreehugger Robot }
317*9558e6acSTreehugger Robot
318*9558e6acSTreehugger Robot static cl_t
fat_get_fat32_next(struct fat_descriptor * fat,cl_t cl)319*9558e6acSTreehugger Robot fat_get_fat32_next(struct fat_descriptor *fat, cl_t cl)
320*9558e6acSTreehugger Robot {
321*9558e6acSTreehugger Robot const uint8_t *p;
322*9558e6acSTreehugger Robot cl_t retval;
323*9558e6acSTreehugger Robot
324*9558e6acSTreehugger Robot p = fat_get_fat32_ptr(fat, cl);
325*9558e6acSTreehugger Robot retval = le32dec(p) & CLUST32_MASK;
326*9558e6acSTreehugger Robot
327*9558e6acSTreehugger Robot if (retval >= (CLUST_BAD & CLUST32_MASK))
328*9558e6acSTreehugger Robot retval |= ~CLUST32_MASK;
329*9558e6acSTreehugger Robot
330*9558e6acSTreehugger Robot return (retval);
331*9558e6acSTreehugger Robot }
332*9558e6acSTreehugger Robot
333*9558e6acSTreehugger Robot static int
fat_set_fat32_next(struct fat_descriptor * fat,cl_t cl,cl_t nextcl)334*9558e6acSTreehugger Robot fat_set_fat32_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl)
335*9558e6acSTreehugger Robot {
336*9558e6acSTreehugger Robot uint8_t *p;
337*9558e6acSTreehugger Robot
338*9558e6acSTreehugger Robot /* Truncate 'nextcl' value, if needed */
339*9558e6acSTreehugger Robot nextcl &= CLUST32_MASK;
340*9558e6acSTreehugger Robot
341*9558e6acSTreehugger Robot p = fat_get_fat32_ptr(fat, cl);
342*9558e6acSTreehugger Robot
343*9558e6acSTreehugger Robot le32enc(p, (uint32_t)nextcl);
344*9558e6acSTreehugger Robot
345*9558e6acSTreehugger Robot return (0);
346*9558e6acSTreehugger Robot }
347*9558e6acSTreehugger Robot
348*9558e6acSTreehugger Robot static inline size_t
fat_get_iosize(struct fat_descriptor * fat,off_t address)349*9558e6acSTreehugger Robot fat_get_iosize(struct fat_descriptor *fat, off_t address)
350*9558e6acSTreehugger Robot {
351*9558e6acSTreehugger Robot
352*9558e6acSTreehugger Robot if (address == fat->fat32_lastaddr) {
353*9558e6acSTreehugger Robot return (fat->fatsize & ((off_t)fat32_cache_chunk_size - 1));
354*9558e6acSTreehugger Robot } else {
355*9558e6acSTreehugger Robot return (fat32_cache_chunk_size);
356*9558e6acSTreehugger Robot }
357*9558e6acSTreehugger Robot }
358*9558e6acSTreehugger Robot
359*9558e6acSTreehugger Robot static int
fat_flush_fat32_cache_entry(struct fat_descriptor * fat,struct fat32_cache_entry * entry)360*9558e6acSTreehugger Robot fat_flush_fat32_cache_entry(struct fat_descriptor *fat,
361*9558e6acSTreehugger Robot struct fat32_cache_entry *entry)
362*9558e6acSTreehugger Robot {
363*9558e6acSTreehugger Robot int fd;
364*9558e6acSTreehugger Robot off_t fat_addr;
365*9558e6acSTreehugger Robot size_t writesize;
366*9558e6acSTreehugger Robot
367*9558e6acSTreehugger Robot fd = fd_of_(fat);
368*9558e6acSTreehugger Robot
369*9558e6acSTreehugger Robot if (!entry->dirty)
370*9558e6acSTreehugger Robot return (FSOK);
371*9558e6acSTreehugger Robot
372*9558e6acSTreehugger Robot writesize = fat_get_iosize(fat, entry->addr);
373*9558e6acSTreehugger Robot
374*9558e6acSTreehugger Robot fat_addr = fat->fat32_offset + entry->addr;
375*9558e6acSTreehugger Robot if (lseek(fd, fat_addr, SEEK_SET) != fat_addr ||
376*9558e6acSTreehugger Robot (size_t)write(fd, entry->chunk, writesize) != writesize) {
377*9558e6acSTreehugger Robot pfatal("Unable to write FAT");
378*9558e6acSTreehugger Robot return (FSFATAL);
379*9558e6acSTreehugger Robot }
380*9558e6acSTreehugger Robot
381*9558e6acSTreehugger Robot entry->dirty = false;
382*9558e6acSTreehugger Robot return (FSOK);
383*9558e6acSTreehugger Robot }
384*9558e6acSTreehugger Robot
385*9558e6acSTreehugger Robot static struct fat32_cache_entry *
fat_get_fat32_cache_entry(struct fat_descriptor * fat,off_t addr,bool writing)386*9558e6acSTreehugger Robot fat_get_fat32_cache_entry(struct fat_descriptor *fat, off_t addr,
387*9558e6acSTreehugger Robot bool writing)
388*9558e6acSTreehugger Robot {
389*9558e6acSTreehugger Robot int fd;
390*9558e6acSTreehugger Robot struct fat32_cache_entry *entry, *first;
391*9558e6acSTreehugger Robot off_t fat_addr;
392*9558e6acSTreehugger Robot size_t rwsize;
393*9558e6acSTreehugger Robot
394*9558e6acSTreehugger Robot addr &= ~(fat32_cache_chunk_size - 1);
395*9558e6acSTreehugger Robot
396*9558e6acSTreehugger Robot first = TAILQ_FIRST(&fat->fat32_cache_head);
397*9558e6acSTreehugger Robot
398*9558e6acSTreehugger Robot /*
399*9558e6acSTreehugger Robot * Cache hit: if we already have the chunk, move it to list head
400*9558e6acSTreehugger Robot */
401*9558e6acSTreehugger Robot TAILQ_FOREACH(entry, &fat->fat32_cache_head, entries) {
402*9558e6acSTreehugger Robot if (entry->addr == addr) {
403*9558e6acSTreehugger Robot if (writing) {
404*9558e6acSTreehugger Robot entry->dirty = true;
405*9558e6acSTreehugger Robot }
406*9558e6acSTreehugger Robot if (entry != first) {
407*9558e6acSTreehugger Robot
408*9558e6acSTreehugger Robot TAILQ_REMOVE(&fat->fat32_cache_head, entry, entries);
409*9558e6acSTreehugger Robot TAILQ_INSERT_HEAD(&fat->fat32_cache_head, entry, entries);
410*9558e6acSTreehugger Robot }
411*9558e6acSTreehugger Robot return (entry);
412*9558e6acSTreehugger Robot }
413*9558e6acSTreehugger Robot }
414*9558e6acSTreehugger Robot
415*9558e6acSTreehugger Robot /*
416*9558e6acSTreehugger Robot * Cache miss: detach the chunk at tail of list, overwrite with
417*9558e6acSTreehugger Robot * the located chunk, and populate with data from disk.
418*9558e6acSTreehugger Robot */
419*9558e6acSTreehugger Robot entry = TAILQ_LAST(&fat->fat32_cache_head, cachehead);
420*9558e6acSTreehugger Robot TAILQ_REMOVE(&fat->fat32_cache_head, entry, entries);
421*9558e6acSTreehugger Robot if (fat_flush_fat32_cache_entry(fat, entry) != FSOK) {
422*9558e6acSTreehugger Robot return (NULL);
423*9558e6acSTreehugger Robot }
424*9558e6acSTreehugger Robot
425*9558e6acSTreehugger Robot rwsize = fat_get_iosize(fat, addr);
426*9558e6acSTreehugger Robot fat_addr = fat->fat32_offset + addr;
427*9558e6acSTreehugger Robot entry->addr = addr;
428*9558e6acSTreehugger Robot fd = fd_of_(fat);
429*9558e6acSTreehugger Robot if (lseek(fd, fat_addr, SEEK_SET) != fat_addr ||
430*9558e6acSTreehugger Robot (size_t)read(fd, entry->chunk, rwsize) != rwsize) {
431*9558e6acSTreehugger Robot pfatal("Unable to read FAT");
432*9558e6acSTreehugger Robot return (NULL);
433*9558e6acSTreehugger Robot }
434*9558e6acSTreehugger Robot if (writing) {
435*9558e6acSTreehugger Robot entry->dirty = true;
436*9558e6acSTreehugger Robot }
437*9558e6acSTreehugger Robot TAILQ_INSERT_HEAD(&fat->fat32_cache_head, entry, entries);
438*9558e6acSTreehugger Robot
439*9558e6acSTreehugger Robot return (entry);
440*9558e6acSTreehugger Robot }
441*9558e6acSTreehugger Robot
442*9558e6acSTreehugger Robot static inline uint8_t *
fat_get_fat32_cached_ptr(struct fat_descriptor * fat,cl_t cl,bool writing)443*9558e6acSTreehugger Robot fat_get_fat32_cached_ptr(struct fat_descriptor *fat, cl_t cl, bool writing)
444*9558e6acSTreehugger Robot {
445*9558e6acSTreehugger Robot off_t addr, off;
446*9558e6acSTreehugger Robot struct fat32_cache_entry *entry;
447*9558e6acSTreehugger Robot
448*9558e6acSTreehugger Robot addr = cl << 2;
449*9558e6acSTreehugger Robot entry = fat_get_fat32_cache_entry(fat, addr, writing);
450*9558e6acSTreehugger Robot
451*9558e6acSTreehugger Robot if (entry != NULL) {
452*9558e6acSTreehugger Robot off = addr & (fat32_cache_chunk_size - 1);
453*9558e6acSTreehugger Robot return (entry->chunk + off);
454*9558e6acSTreehugger Robot } else {
455*9558e6acSTreehugger Robot return (NULL);
456*9558e6acSTreehugger Robot }
457*9558e6acSTreehugger Robot }
458*9558e6acSTreehugger Robot
459*9558e6acSTreehugger Robot
460*9558e6acSTreehugger Robot static cl_t
fat_get_fat32_cached_next(struct fat_descriptor * fat,cl_t cl)461*9558e6acSTreehugger Robot fat_get_fat32_cached_next(struct fat_descriptor *fat, cl_t cl)
462*9558e6acSTreehugger Robot {
463*9558e6acSTreehugger Robot const uint8_t *p;
464*9558e6acSTreehugger Robot cl_t retval;
465*9558e6acSTreehugger Robot
466*9558e6acSTreehugger Robot p = fat_get_fat32_cached_ptr(fat, cl, false);
467*9558e6acSTreehugger Robot if (p != NULL) {
468*9558e6acSTreehugger Robot retval = le32dec(p) & CLUST32_MASK;
469*9558e6acSTreehugger Robot if (retval >= (CLUST_BAD & CLUST32_MASK))
470*9558e6acSTreehugger Robot retval |= ~CLUST32_MASK;
471*9558e6acSTreehugger Robot } else {
472*9558e6acSTreehugger Robot retval = CLUST_DEAD;
473*9558e6acSTreehugger Robot }
474*9558e6acSTreehugger Robot
475*9558e6acSTreehugger Robot return (retval);
476*9558e6acSTreehugger Robot }
477*9558e6acSTreehugger Robot
478*9558e6acSTreehugger Robot static int
fat_set_fat32_cached_next(struct fat_descriptor * fat,cl_t cl,cl_t nextcl)479*9558e6acSTreehugger Robot fat_set_fat32_cached_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl)
480*9558e6acSTreehugger Robot {
481*9558e6acSTreehugger Robot uint8_t *p;
482*9558e6acSTreehugger Robot
483*9558e6acSTreehugger Robot /* Truncate 'nextcl' value, if needed */
484*9558e6acSTreehugger Robot nextcl &= CLUST32_MASK;
485*9558e6acSTreehugger Robot
486*9558e6acSTreehugger Robot p = fat_get_fat32_cached_ptr(fat, cl, true);
487*9558e6acSTreehugger Robot if (p != NULL) {
488*9558e6acSTreehugger Robot le32enc(p, (uint32_t)nextcl);
489*9558e6acSTreehugger Robot return FSOK;
490*9558e6acSTreehugger Robot } else {
491*9558e6acSTreehugger Robot return FSFATAL;
492*9558e6acSTreehugger Robot }
493*9558e6acSTreehugger Robot }
494*9558e6acSTreehugger Robot
fat_get_cl_next(struct fat_descriptor * fat,cl_t cl)495*9558e6acSTreehugger Robot cl_t fat_get_cl_next(struct fat_descriptor *fat, cl_t cl)
496*9558e6acSTreehugger Robot {
497*9558e6acSTreehugger Robot
498*9558e6acSTreehugger Robot if (!valid_cl(fat, cl)) {
499*9558e6acSTreehugger Robot pfatal("Invalid cluster: %ud", cl);
500*9558e6acSTreehugger Robot return CLUST_DEAD;
501*9558e6acSTreehugger Robot }
502*9558e6acSTreehugger Robot
503*9558e6acSTreehugger Robot return (fat->get(fat, cl));
504*9558e6acSTreehugger Robot }
505*9558e6acSTreehugger Robot
fat_set_cl_next(struct fat_descriptor * fat,cl_t cl,cl_t nextcl)506*9558e6acSTreehugger Robot int fat_set_cl_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl)
507*9558e6acSTreehugger Robot {
508*9558e6acSTreehugger Robot
509*9558e6acSTreehugger Robot if (rdonly) {
510*9558e6acSTreehugger Robot pwarn(" (NO WRITE)\n");
511*9558e6acSTreehugger Robot return FSFATAL;
512*9558e6acSTreehugger Robot }
513*9558e6acSTreehugger Robot
514*9558e6acSTreehugger Robot if (!valid_cl(fat, cl)) {
515*9558e6acSTreehugger Robot pfatal("Invalid cluster: %ud", cl);
516*9558e6acSTreehugger Robot return FSFATAL;
517*9558e6acSTreehugger Robot }
518*9558e6acSTreehugger Robot
519*9558e6acSTreehugger Robot return (fat->set(fat, cl, nextcl));
520*9558e6acSTreehugger Robot }
521*9558e6acSTreehugger Robot
522*9558e6acSTreehugger Robot static inline struct bootblock*
boot_of_(struct fat_descriptor * fat)523*9558e6acSTreehugger Robot boot_of_(struct fat_descriptor *fat) {
524*9558e6acSTreehugger Robot
525*9558e6acSTreehugger Robot return (fat->boot);
526*9558e6acSTreehugger Robot }
527*9558e6acSTreehugger Robot
528*9558e6acSTreehugger Robot struct bootblock*
fat_get_boot(struct fat_descriptor * fat)529*9558e6acSTreehugger Robot fat_get_boot(struct fat_descriptor *fat) {
530*9558e6acSTreehugger Robot
531*9558e6acSTreehugger Robot return (boot_of_(fat));
532*9558e6acSTreehugger Robot }
533*9558e6acSTreehugger Robot
534*9558e6acSTreehugger Robot static inline int
fd_of_(struct fat_descriptor * fat)535*9558e6acSTreehugger Robot fd_of_(struct fat_descriptor *fat)
536*9558e6acSTreehugger Robot {
537*9558e6acSTreehugger Robot return (fat->fd);
538*9558e6acSTreehugger Robot }
539*9558e6acSTreehugger Robot
540*9558e6acSTreehugger Robot int
fat_get_fd(struct fat_descriptor * fat)541*9558e6acSTreehugger Robot fat_get_fd(struct fat_descriptor * fat)
542*9558e6acSTreehugger Robot {
543*9558e6acSTreehugger Robot return (fd_of_(fat));
544*9558e6acSTreehugger Robot }
545*9558e6acSTreehugger Robot
546*9558e6acSTreehugger Robot /*
547*9558e6acSTreehugger Robot * Whether a cl is in valid data range.
548*9558e6acSTreehugger Robot */
549*9558e6acSTreehugger Robot bool
fat_is_valid_cl(struct fat_descriptor * fat,cl_t cl)550*9558e6acSTreehugger Robot fat_is_valid_cl(struct fat_descriptor *fat, cl_t cl)
551*9558e6acSTreehugger Robot {
552*9558e6acSTreehugger Robot
553*9558e6acSTreehugger Robot return (valid_cl(fat, cl));
554*9558e6acSTreehugger Robot }
555*9558e6acSTreehugger Robot
556*9558e6acSTreehugger Robot static inline bool
valid_cl(struct fat_descriptor * fat,cl_t cl)557*9558e6acSTreehugger Robot valid_cl(struct fat_descriptor *fat, cl_t cl)
558*9558e6acSTreehugger Robot {
559*9558e6acSTreehugger Robot const struct bootblock *boot = boot_of_(fat);
560*9558e6acSTreehugger Robot
561*9558e6acSTreehugger Robot return (cl >= CLUST_FIRST && cl < boot->NumClusters);
562*9558e6acSTreehugger Robot }
563*9558e6acSTreehugger Robot
564*9558e6acSTreehugger Robot /*
565*9558e6acSTreehugger Robot * The first 2 FAT entries contain pseudo-cluster numbers with the following
566*9558e6acSTreehugger Robot * layout:
567*9558e6acSTreehugger Robot *
568*9558e6acSTreehugger Robot * 31...... ........ ........ .......0
569*9558e6acSTreehugger Robot * rrrr1111 11111111 11111111 mmmmmmmm FAT32 entry 0
570*9558e6acSTreehugger Robot * rrrrsh11 11111111 11111111 11111xxx FAT32 entry 1
571*9558e6acSTreehugger Robot *
572*9558e6acSTreehugger Robot * 11111111 mmmmmmmm FAT16 entry 0
573*9558e6acSTreehugger Robot * sh111111 11111xxx FAT16 entry 1
574*9558e6acSTreehugger Robot *
575*9558e6acSTreehugger Robot * r = reserved
576*9558e6acSTreehugger Robot * m = BPB media ID byte
577*9558e6acSTreehugger Robot * s = clean flag (1 = dismounted; 0 = still mounted)
578*9558e6acSTreehugger Robot * h = hard error flag (1 = ok; 0 = I/O error)
579*9558e6acSTreehugger Robot * x = any value ok
580*9558e6acSTreehugger Robot */
581*9558e6acSTreehugger Robot int
checkdirty(int fs,struct bootblock * boot)582*9558e6acSTreehugger Robot checkdirty(int fs, struct bootblock *boot)
583*9558e6acSTreehugger Robot {
584*9558e6acSTreehugger Robot off_t off;
585*9558e6acSTreehugger Robot u_char *buffer;
586*9558e6acSTreehugger Robot int ret = 0;
587*9558e6acSTreehugger Robot size_t len;
588*9558e6acSTreehugger Robot
589*9558e6acSTreehugger Robot if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
590*9558e6acSTreehugger Robot return 0;
591*9558e6acSTreehugger Robot
592*9558e6acSTreehugger Robot off = boot->bpbResSectors;
593*9558e6acSTreehugger Robot off *= boot->bpbBytesPerSec;
594*9558e6acSTreehugger Robot
595*9558e6acSTreehugger Robot buffer = malloc(len = boot->bpbBytesPerSec);
596*9558e6acSTreehugger Robot if (buffer == NULL) {
597*9558e6acSTreehugger Robot perr("No space for FAT sectors (%zu)", len);
598*9558e6acSTreehugger Robot return 1;
599*9558e6acSTreehugger Robot }
600*9558e6acSTreehugger Robot
601*9558e6acSTreehugger Robot if (lseek(fs, off, SEEK_SET) != off) {
602*9558e6acSTreehugger Robot perr("Unable to read FAT");
603*9558e6acSTreehugger Robot goto err;
604*9558e6acSTreehugger Robot }
605*9558e6acSTreehugger Robot
606*9558e6acSTreehugger Robot if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) !=
607*9558e6acSTreehugger Robot boot->bpbBytesPerSec) {
608*9558e6acSTreehugger Robot perr("Unable to read FAT");
609*9558e6acSTreehugger Robot goto err;
610*9558e6acSTreehugger Robot }
611*9558e6acSTreehugger Robot
612*9558e6acSTreehugger Robot /*
613*9558e6acSTreehugger Robot * If we don't understand the FAT, then the file system must be
614*9558e6acSTreehugger Robot * assumed to be unclean.
615*9558e6acSTreehugger Robot */
616*9558e6acSTreehugger Robot if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff)
617*9558e6acSTreehugger Robot goto err;
618*9558e6acSTreehugger Robot if (boot->ClustMask == CLUST16_MASK) {
619*9558e6acSTreehugger Robot if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f)
620*9558e6acSTreehugger Robot goto err;
621*9558e6acSTreehugger Robot } else {
622*9558e6acSTreehugger Robot if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f
623*9558e6acSTreehugger Robot || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff
624*9558e6acSTreehugger Robot || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03)
625*9558e6acSTreehugger Robot goto err;
626*9558e6acSTreehugger Robot }
627*9558e6acSTreehugger Robot
628*9558e6acSTreehugger Robot /*
629*9558e6acSTreehugger Robot * Now check the actual clean flag (and the no-error flag).
630*9558e6acSTreehugger Robot */
631*9558e6acSTreehugger Robot if (boot->ClustMask == CLUST16_MASK) {
632*9558e6acSTreehugger Robot if ((buffer[3] & 0xc0) == 0xc0)
633*9558e6acSTreehugger Robot ret = 1;
634*9558e6acSTreehugger Robot } else {
635*9558e6acSTreehugger Robot if ((buffer[7] & 0x0c) == 0x0c)
636*9558e6acSTreehugger Robot ret = 1;
637*9558e6acSTreehugger Robot }
638*9558e6acSTreehugger Robot
639*9558e6acSTreehugger Robot err:
640*9558e6acSTreehugger Robot free(buffer);
641*9558e6acSTreehugger Robot return ret;
642*9558e6acSTreehugger Robot }
643*9558e6acSTreehugger Robot
644*9558e6acSTreehugger Robot int
cleardirty(struct fat_descriptor * fat)645*9558e6acSTreehugger Robot cleardirty(struct fat_descriptor *fat)
646*9558e6acSTreehugger Robot {
647*9558e6acSTreehugger Robot int fd, ret = FSERROR;
648*9558e6acSTreehugger Robot struct bootblock *boot;
649*9558e6acSTreehugger Robot u_char *buffer;
650*9558e6acSTreehugger Robot size_t len;
651*9558e6acSTreehugger Robot off_t off;
652*9558e6acSTreehugger Robot
653*9558e6acSTreehugger Robot boot = boot_of_(fat);
654*9558e6acSTreehugger Robot fd = fd_of_(fat);
655*9558e6acSTreehugger Robot
656*9558e6acSTreehugger Robot if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
657*9558e6acSTreehugger Robot return 0;
658*9558e6acSTreehugger Robot
659*9558e6acSTreehugger Robot off = boot->bpbResSectors;
660*9558e6acSTreehugger Robot off *= boot->bpbBytesPerSec;
661*9558e6acSTreehugger Robot
662*9558e6acSTreehugger Robot buffer = malloc(len = boot->bpbBytesPerSec);
663*9558e6acSTreehugger Robot if (buffer == NULL) {
664*9558e6acSTreehugger Robot perr("No memory for FAT sectors (%zu)", len);
665*9558e6acSTreehugger Robot return 1;
666*9558e6acSTreehugger Robot }
667*9558e6acSTreehugger Robot
668*9558e6acSTreehugger Robot if ((size_t)pread(fd, buffer, len, off) != len) {
669*9558e6acSTreehugger Robot perr("Unable to read FAT");
670*9558e6acSTreehugger Robot goto err;
671*9558e6acSTreehugger Robot }
672*9558e6acSTreehugger Robot
673*9558e6acSTreehugger Robot if (boot->ClustMask == CLUST16_MASK) {
674*9558e6acSTreehugger Robot buffer[3] |= 0x80;
675*9558e6acSTreehugger Robot } else {
676*9558e6acSTreehugger Robot buffer[7] |= 0x08;
677*9558e6acSTreehugger Robot }
678*9558e6acSTreehugger Robot
679*9558e6acSTreehugger Robot if ((size_t)pwrite(fd, buffer, len, off) != len) {
680*9558e6acSTreehugger Robot perr("Unable to write FAT");
681*9558e6acSTreehugger Robot goto err;
682*9558e6acSTreehugger Robot }
683*9558e6acSTreehugger Robot
684*9558e6acSTreehugger Robot ret = FSOK;
685*9558e6acSTreehugger Robot
686*9558e6acSTreehugger Robot err:
687*9558e6acSTreehugger Robot free(buffer);
688*9558e6acSTreehugger Robot return ret;
689*9558e6acSTreehugger Robot }
690*9558e6acSTreehugger Robot
691*9558e6acSTreehugger Robot /*
692*9558e6acSTreehugger Robot * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
693*9558e6acSTreehugger Robot */
694*9558e6acSTreehugger Robot static int
_readfat(struct fat_descriptor * fat)695*9558e6acSTreehugger Robot _readfat(struct fat_descriptor *fat)
696*9558e6acSTreehugger Robot {
697*9558e6acSTreehugger Robot int fd;
698*9558e6acSTreehugger Robot size_t i;
699*9558e6acSTreehugger Robot off_t off;
700*9558e6acSTreehugger Robot size_t readsize;
701*9558e6acSTreehugger Robot struct bootblock *boot;
702*9558e6acSTreehugger Robot struct fat32_cache_entry *entry;
703*9558e6acSTreehugger Robot
704*9558e6acSTreehugger Robot boot = boot_of_(fat);
705*9558e6acSTreehugger Robot fd = fd_of_(fat);
706*9558e6acSTreehugger Robot fat->fatsize = boot->FATsecs * boot->bpbBytesPerSec;
707*9558e6acSTreehugger Robot
708*9558e6acSTreehugger Robot off = boot->bpbResSectors;
709*9558e6acSTreehugger Robot off *= boot->bpbBytesPerSec;
710*9558e6acSTreehugger Robot
711*9558e6acSTreehugger Robot fat->is_mmapped = false;
712*9558e6acSTreehugger Robot fat->use_cache = false;
713*9558e6acSTreehugger Robot
714*9558e6acSTreehugger Robot /* Attempt to mmap() first */
715*9558e6acSTreehugger Robot if (allow_mmap) {
716*9558e6acSTreehugger Robot fat->fatbuf = mmap(NULL, fat->fatsize,
717*9558e6acSTreehugger Robot PROT_READ | (rdonly ? 0 : PROT_WRITE),
718*9558e6acSTreehugger Robot MAP_SHARED, fd_of_(fat), off);
719*9558e6acSTreehugger Robot if (fat->fatbuf != MAP_FAILED) {
720*9558e6acSTreehugger Robot fat->is_mmapped = true;
721*9558e6acSTreehugger Robot return 1;
722*9558e6acSTreehugger Robot }
723*9558e6acSTreehugger Robot }
724*9558e6acSTreehugger Robot
725*9558e6acSTreehugger Robot /*
726*9558e6acSTreehugger Robot * Unfortunately, we were unable to mmap().
727*9558e6acSTreehugger Robot *
728*9558e6acSTreehugger Robot * Only use the cache manager when it's necessary, that is,
729*9558e6acSTreehugger Robot * when the FAT is sufficiently large; in that case, only
730*9558e6acSTreehugger Robot * read in the first 4 MiB of FAT into memory, and split the
731*9558e6acSTreehugger Robot * buffer into chunks and insert to the LRU queue to populate
732*9558e6acSTreehugger Robot * the cache with data.
733*9558e6acSTreehugger Robot */
734*9558e6acSTreehugger Robot if (boot->ClustMask == CLUST32_MASK &&
735*9558e6acSTreehugger Robot fat->fatsize >= fat32_cache_size) {
736*9558e6acSTreehugger Robot readsize = fat32_cache_size;
737*9558e6acSTreehugger Robot fat->use_cache = true;
738*9558e6acSTreehugger Robot
739*9558e6acSTreehugger Robot fat->fat32_offset = boot->bpbResSectors * boot->bpbBytesPerSec;
740*9558e6acSTreehugger Robot fat->fat32_lastaddr = fat->fatsize & ~(fat32_cache_chunk_size);
741*9558e6acSTreehugger Robot } else {
742*9558e6acSTreehugger Robot readsize = fat->fatsize;
743*9558e6acSTreehugger Robot }
744*9558e6acSTreehugger Robot fat->fatbuf = malloc(readsize);
745*9558e6acSTreehugger Robot if (fat->fatbuf == NULL) {
746*9558e6acSTreehugger Robot perr("No space for FAT (%zu)", readsize);
747*9558e6acSTreehugger Robot return 0;
748*9558e6acSTreehugger Robot }
749*9558e6acSTreehugger Robot
750*9558e6acSTreehugger Robot if (lseek(fd, off, SEEK_SET) != off) {
751*9558e6acSTreehugger Robot perr("Unable to read FAT");
752*9558e6acSTreehugger Robot goto err;
753*9558e6acSTreehugger Robot }
754*9558e6acSTreehugger Robot if ((size_t)read(fd, fat->fatbuf, readsize) != readsize) {
755*9558e6acSTreehugger Robot perr("Unable to read FAT");
756*9558e6acSTreehugger Robot goto err;
757*9558e6acSTreehugger Robot }
758*9558e6acSTreehugger Robot
759*9558e6acSTreehugger Robot /*
760*9558e6acSTreehugger Robot * When cache is used, split the buffer into chunks, and
761*9558e6acSTreehugger Robot * connect the buffer into the cache.
762*9558e6acSTreehugger Robot */
763*9558e6acSTreehugger Robot if (fat->use_cache) {
764*9558e6acSTreehugger Robot TAILQ_INIT(&fat->fat32_cache_head);
765*9558e6acSTreehugger Robot entry = calloc(fat32_cache_entries, sizeof(*entry));
766*9558e6acSTreehugger Robot if (entry == NULL) {
767*9558e6acSTreehugger Robot perr("No space for FAT cache (%zu of %zu)",
768*9558e6acSTreehugger Robot fat32_cache_entries, sizeof(entry));
769*9558e6acSTreehugger Robot goto err;
770*9558e6acSTreehugger Robot }
771*9558e6acSTreehugger Robot for (i = 0; i < fat32_cache_entries; i++) {
772*9558e6acSTreehugger Robot entry[i].addr = fat32_cache_chunk_size * i;
773*9558e6acSTreehugger Robot entry[i].chunk = &fat->fatbuf[entry[i].addr];
774*9558e6acSTreehugger Robot TAILQ_INSERT_TAIL(&fat->fat32_cache_head,
775*9558e6acSTreehugger Robot &entry[i], entries);
776*9558e6acSTreehugger Robot }
777*9558e6acSTreehugger Robot fat->fat32_cache_allentries = entry;
778*9558e6acSTreehugger Robot }
779*9558e6acSTreehugger Robot
780*9558e6acSTreehugger Robot return 1;
781*9558e6acSTreehugger Robot
782*9558e6acSTreehugger Robot err:
783*9558e6acSTreehugger Robot free(fat->fatbuf);
784*9558e6acSTreehugger Robot fat->fatbuf = NULL;
785*9558e6acSTreehugger Robot return 0;
786*9558e6acSTreehugger Robot }
787*9558e6acSTreehugger Robot
788*9558e6acSTreehugger Robot static void
releasefat(struct fat_descriptor * fat)789*9558e6acSTreehugger Robot releasefat(struct fat_descriptor *fat)
790*9558e6acSTreehugger Robot {
791*9558e6acSTreehugger Robot if (fat->is_mmapped) {
792*9558e6acSTreehugger Robot munmap(fat->fatbuf, fat->fatsize);
793*9558e6acSTreehugger Robot } else {
794*9558e6acSTreehugger Robot if (fat->use_cache) {
795*9558e6acSTreehugger Robot free(fat->fat32_cache_allentries);
796*9558e6acSTreehugger Robot fat->fat32_cache_allentries = NULL;
797*9558e6acSTreehugger Robot }
798*9558e6acSTreehugger Robot free(fat->fatbuf);
799*9558e6acSTreehugger Robot }
800*9558e6acSTreehugger Robot fat->fatbuf = NULL;
801*9558e6acSTreehugger Robot bitmap_dtor(&fat->headbitmap);
802*9558e6acSTreehugger Robot }
803*9558e6acSTreehugger Robot
804*9558e6acSTreehugger Robot /*
805*9558e6acSTreehugger Robot * Read or map a FAT and populate head bitmap
806*9558e6acSTreehugger Robot */
807*9558e6acSTreehugger Robot int
readfat(int fs,struct bootblock * boot,struct fat_descriptor ** fp)808*9558e6acSTreehugger Robot readfat(int fs, struct bootblock *boot, struct fat_descriptor **fp)
809*9558e6acSTreehugger Robot {
810*9558e6acSTreehugger Robot struct fat_descriptor *fat;
811*9558e6acSTreehugger Robot u_char *buffer, *p;
812*9558e6acSTreehugger Robot cl_t cl, nextcl;
813*9558e6acSTreehugger Robot int ret = FSOK;
814*9558e6acSTreehugger Robot
815*9558e6acSTreehugger Robot boot->NumFree = boot->NumBad = 0;
816*9558e6acSTreehugger Robot
817*9558e6acSTreehugger Robot fat = calloc(1, sizeof(struct fat_descriptor));
818*9558e6acSTreehugger Robot if (fat == NULL) {
819*9558e6acSTreehugger Robot perr("No space for FAT descriptor");
820*9558e6acSTreehugger Robot return FSFATAL;
821*9558e6acSTreehugger Robot }
822*9558e6acSTreehugger Robot
823*9558e6acSTreehugger Robot fat->fd = fs;
824*9558e6acSTreehugger Robot fat->boot = boot;
825*9558e6acSTreehugger Robot
826*9558e6acSTreehugger Robot if (!_readfat(fat)) {
827*9558e6acSTreehugger Robot free(fat);
828*9558e6acSTreehugger Robot return FSFATAL;
829*9558e6acSTreehugger Robot }
830*9558e6acSTreehugger Robot buffer = fat->fatbuf;
831*9558e6acSTreehugger Robot
832*9558e6acSTreehugger Robot /* Populate accessors */
833*9558e6acSTreehugger Robot switch(boot->ClustMask) {
834*9558e6acSTreehugger Robot case CLUST12_MASK:
835*9558e6acSTreehugger Robot fat->get = fat_get_fat12_next;
836*9558e6acSTreehugger Robot fat->set = fat_set_fat12_next;
837*9558e6acSTreehugger Robot break;
838*9558e6acSTreehugger Robot case CLUST16_MASK:
839*9558e6acSTreehugger Robot fat->get = fat_get_fat16_next;
840*9558e6acSTreehugger Robot fat->set = fat_set_fat16_next;
841*9558e6acSTreehugger Robot break;
842*9558e6acSTreehugger Robot case CLUST32_MASK:
843*9558e6acSTreehugger Robot if (fat->is_mmapped || !fat->use_cache) {
844*9558e6acSTreehugger Robot fat->get = fat_get_fat32_next;
845*9558e6acSTreehugger Robot fat->set = fat_set_fat32_next;
846*9558e6acSTreehugger Robot } else {
847*9558e6acSTreehugger Robot fat->get = fat_get_fat32_cached_next;
848*9558e6acSTreehugger Robot fat->set = fat_set_fat32_cached_next;
849*9558e6acSTreehugger Robot }
850*9558e6acSTreehugger Robot break;
851*9558e6acSTreehugger Robot default:
852*9558e6acSTreehugger Robot pfatal("Invalid ClustMask: %d", boot->ClustMask);
853*9558e6acSTreehugger Robot releasefat(fat);
854*9558e6acSTreehugger Robot free(fat);
855*9558e6acSTreehugger Robot return FSFATAL;
856*9558e6acSTreehugger Robot }
857*9558e6acSTreehugger Robot
858*9558e6acSTreehugger Robot if (bitmap_ctor(&fat->headbitmap, boot->NumClusters,
859*9558e6acSTreehugger Robot true) != FSOK) {
860*9558e6acSTreehugger Robot perr("No space for head bitmap for FAT clusters (%zu)",
861*9558e6acSTreehugger Robot (size_t)boot->NumClusters);
862*9558e6acSTreehugger Robot releasefat(fat);
863*9558e6acSTreehugger Robot free(fat);
864*9558e6acSTreehugger Robot return FSFATAL;
865*9558e6acSTreehugger Robot }
866*9558e6acSTreehugger Robot
867*9558e6acSTreehugger Robot if (buffer[0] != boot->bpbMedia
868*9558e6acSTreehugger Robot || buffer[1] != 0xff || buffer[2] != 0xff
869*9558e6acSTreehugger Robot || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
870*9558e6acSTreehugger Robot || (boot->ClustMask == CLUST32_MASK
871*9558e6acSTreehugger Robot && ((buffer[3]&0x0f) != 0x0f
872*9558e6acSTreehugger Robot || buffer[4] != 0xff || buffer[5] != 0xff
873*9558e6acSTreehugger Robot || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
874*9558e6acSTreehugger Robot
875*9558e6acSTreehugger Robot /* Windows 95 OSR2 (and possibly any later) changes
876*9558e6acSTreehugger Robot * the FAT signature to 0xXXffff7f for FAT16 and to
877*9558e6acSTreehugger Robot * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
878*9558e6acSTreehugger Robot * file system is dirty if it doesn't reboot cleanly.
879*9558e6acSTreehugger Robot * Check this special condition before errorring out.
880*9558e6acSTreehugger Robot */
881*9558e6acSTreehugger Robot if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff
882*9558e6acSTreehugger Robot && buffer[2] == 0xff
883*9558e6acSTreehugger Robot && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
884*9558e6acSTreehugger Robot || (boot->ClustMask == CLUST32_MASK
885*9558e6acSTreehugger Robot && buffer[3] == 0x0f && buffer[4] == 0xff
886*9558e6acSTreehugger Robot && buffer[5] == 0xff && buffer[6] == 0xff
887*9558e6acSTreehugger Robot && buffer[7] == 0x07)))
888*9558e6acSTreehugger Robot ret |= FSDIRTY;
889*9558e6acSTreehugger Robot else {
890*9558e6acSTreehugger Robot /* just some odd byte sequence in FAT */
891*9558e6acSTreehugger Robot
892*9558e6acSTreehugger Robot switch (boot->ClustMask) {
893*9558e6acSTreehugger Robot case CLUST32_MASK:
894*9558e6acSTreehugger Robot pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
895*9558e6acSTreehugger Robot "FAT starts with odd byte sequence",
896*9558e6acSTreehugger Robot buffer[0], buffer[1], buffer[2], buffer[3],
897*9558e6acSTreehugger Robot buffer[4], buffer[5], buffer[6], buffer[7]);
898*9558e6acSTreehugger Robot break;
899*9558e6acSTreehugger Robot case CLUST16_MASK:
900*9558e6acSTreehugger Robot pwarn("%s (%02x%02x%02x%02x)\n",
901*9558e6acSTreehugger Robot "FAT starts with odd byte sequence",
902*9558e6acSTreehugger Robot buffer[0], buffer[1], buffer[2], buffer[3]);
903*9558e6acSTreehugger Robot break;
904*9558e6acSTreehugger Robot default:
905*9558e6acSTreehugger Robot pwarn("%s (%02x%02x%02x)\n",
906*9558e6acSTreehugger Robot "FAT starts with odd byte sequence",
907*9558e6acSTreehugger Robot buffer[0], buffer[1], buffer[2]);
908*9558e6acSTreehugger Robot break;
909*9558e6acSTreehugger Robot }
910*9558e6acSTreehugger Robot
911*9558e6acSTreehugger Robot if (ask(1, "Correct")) {
912*9558e6acSTreehugger Robot ret |= FSFATMOD;
913*9558e6acSTreehugger Robot p = buffer;
914*9558e6acSTreehugger Robot
915*9558e6acSTreehugger Robot *p++ = (u_char)boot->bpbMedia;
916*9558e6acSTreehugger Robot *p++ = 0xff;
917*9558e6acSTreehugger Robot *p++ = 0xff;
918*9558e6acSTreehugger Robot switch (boot->ClustMask) {
919*9558e6acSTreehugger Robot case CLUST16_MASK:
920*9558e6acSTreehugger Robot *p++ = 0xff;
921*9558e6acSTreehugger Robot break;
922*9558e6acSTreehugger Robot case CLUST32_MASK:
923*9558e6acSTreehugger Robot *p++ = 0x0f;
924*9558e6acSTreehugger Robot *p++ = 0xff;
925*9558e6acSTreehugger Robot *p++ = 0xff;
926*9558e6acSTreehugger Robot *p++ = 0xff;
927*9558e6acSTreehugger Robot *p++ = 0x0f;
928*9558e6acSTreehugger Robot break;
929*9558e6acSTreehugger Robot default:
930*9558e6acSTreehugger Robot break;
931*9558e6acSTreehugger Robot }
932*9558e6acSTreehugger Robot }
933*9558e6acSTreehugger Robot }
934*9558e6acSTreehugger Robot }
935*9558e6acSTreehugger Robot
936*9558e6acSTreehugger Robot /*
937*9558e6acSTreehugger Robot * Traverse the FAT table and populate head map. Initially, we
938*9558e6acSTreehugger Robot * consider all clusters as possible head cluster (beginning of
939*9558e6acSTreehugger Robot * a file or directory), and traverse the whole allocation table
940*9558e6acSTreehugger Robot * by marking every non-head nodes as such (detailed below) and
941*9558e6acSTreehugger Robot * fix obvious issues while we walk.
942*9558e6acSTreehugger Robot *
943*9558e6acSTreehugger Robot * For each "next" cluster, the possible values are:
944*9558e6acSTreehugger Robot *
945*9558e6acSTreehugger Robot * a) CLUST_FREE or CLUST_BAD. The *current* cluster can't be a
946*9558e6acSTreehugger Robot * head node.
947*9558e6acSTreehugger Robot * b) An out-of-range value. The only fix would be to truncate at
948*9558e6acSTreehugger Robot * the cluster.
949*9558e6acSTreehugger Robot * c) A valid cluster. It means that cluster (nextcl) is not a
950*9558e6acSTreehugger Robot * head cluster. Note that during the scan, every cluster is
951*9558e6acSTreehugger Robot * expected to be seen for at most once, and when we saw them
952*9558e6acSTreehugger Robot * twice, it means a cross-linked chain which should be
953*9558e6acSTreehugger Robot * truncated at the current cluster.
954*9558e6acSTreehugger Robot *
955*9558e6acSTreehugger Robot * After scan, the remaining set bits indicates all possible
956*9558e6acSTreehugger Robot * head nodes, because they were never claimed by any other
957*9558e6acSTreehugger Robot * node as the next node, but we do not know if these chains
958*9558e6acSTreehugger Robot * would end with a valid EOF marker. We will check that in
959*9558e6acSTreehugger Robot * checkchain() at a later time when checking directories,
960*9558e6acSTreehugger Robot * where these head nodes would be marked as non-head.
961*9558e6acSTreehugger Robot *
962*9558e6acSTreehugger Robot * In the final pass, all head nodes should be cleared, and if
963*9558e6acSTreehugger Robot * there is still head nodes, these would be leaders of lost
964*9558e6acSTreehugger Robot * chain.
965*9558e6acSTreehugger Robot */
966*9558e6acSTreehugger Robot for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
967*9558e6acSTreehugger Robot nextcl = fat_get_cl_next(fat, cl);
968*9558e6acSTreehugger Robot
969*9558e6acSTreehugger Robot /* Check if the next cluster number is valid */
970*9558e6acSTreehugger Robot if (nextcl == CLUST_FREE) {
971*9558e6acSTreehugger Robot /* Save a hint for next free cluster */
972*9558e6acSTreehugger Robot if (boot->FSNext == 0) {
973*9558e6acSTreehugger Robot boot->FSNext = cl;
974*9558e6acSTreehugger Robot }
975*9558e6acSTreehugger Robot if (fat_is_cl_head(fat, cl)) {
976*9558e6acSTreehugger Robot fat_clear_cl_head(fat, cl);
977*9558e6acSTreehugger Robot }
978*9558e6acSTreehugger Robot boot->NumFree++;
979*9558e6acSTreehugger Robot } else if (nextcl == CLUST_BAD) {
980*9558e6acSTreehugger Robot if (fat_is_cl_head(fat, cl)) {
981*9558e6acSTreehugger Robot fat_clear_cl_head(fat, cl);
982*9558e6acSTreehugger Robot }
983*9558e6acSTreehugger Robot boot->NumBad++;
984*9558e6acSTreehugger Robot } else if (!valid_cl(fat, nextcl) && nextcl < CLUST_RSRVD) {
985*9558e6acSTreehugger Robot pwarn("Cluster %u continues with out of range "
986*9558e6acSTreehugger Robot "cluster number %u\n",
987*9558e6acSTreehugger Robot cl,
988*9558e6acSTreehugger Robot nextcl & boot->ClustMask);
989*9558e6acSTreehugger Robot if (ask(0, "Truncate")) {
990*9558e6acSTreehugger Robot ret |= fat_set_cl_next(fat, cl, CLUST_EOF);
991*9558e6acSTreehugger Robot ret |= FSFATMOD;
992*9558e6acSTreehugger Robot }
993*9558e6acSTreehugger Robot } else if (valid_cl(fat, nextcl)) {
994*9558e6acSTreehugger Robot if (fat_is_cl_head(fat, nextcl)) {
995*9558e6acSTreehugger Robot fat_clear_cl_head(fat, nextcl);
996*9558e6acSTreehugger Robot } else {
997*9558e6acSTreehugger Robot pwarn("Cluster %u crossed another chain at %u\n",
998*9558e6acSTreehugger Robot cl, nextcl);
999*9558e6acSTreehugger Robot if (ask(0, "Truncate")) {
1000*9558e6acSTreehugger Robot ret |= fat_set_cl_next(fat, cl, CLUST_EOF);
1001*9558e6acSTreehugger Robot ret |= FSFATMOD;
1002*9558e6acSTreehugger Robot }
1003*9558e6acSTreehugger Robot }
1004*9558e6acSTreehugger Robot }
1005*9558e6acSTreehugger Robot
1006*9558e6acSTreehugger Robot }
1007*9558e6acSTreehugger Robot
1008*9558e6acSTreehugger Robot if (ret & FSFATAL) {
1009*9558e6acSTreehugger Robot releasefat(fat);
1010*9558e6acSTreehugger Robot free(fat);
1011*9558e6acSTreehugger Robot *fp = NULL;
1012*9558e6acSTreehugger Robot } else
1013*9558e6acSTreehugger Robot *fp = fat;
1014*9558e6acSTreehugger Robot return ret;
1015*9558e6acSTreehugger Robot }
1016*9558e6acSTreehugger Robot
1017*9558e6acSTreehugger Robot /*
1018*9558e6acSTreehugger Robot * Get type of reserved cluster
1019*9558e6acSTreehugger Robot */
1020*9558e6acSTreehugger Robot const char *
rsrvdcltype(cl_t cl)1021*9558e6acSTreehugger Robot rsrvdcltype(cl_t cl)
1022*9558e6acSTreehugger Robot {
1023*9558e6acSTreehugger Robot if (cl == CLUST_FREE)
1024*9558e6acSTreehugger Robot return "free";
1025*9558e6acSTreehugger Robot if (cl < CLUST_BAD)
1026*9558e6acSTreehugger Robot return "reserved";
1027*9558e6acSTreehugger Robot if (cl > CLUST_BAD)
1028*9558e6acSTreehugger Robot return "as EOF";
1029*9558e6acSTreehugger Robot return "bad";
1030*9558e6acSTreehugger Robot }
1031*9558e6acSTreehugger Robot
1032*9558e6acSTreehugger Robot /*
1033*9558e6acSTreehugger Robot * Examine a cluster chain for errors and count its size.
1034*9558e6acSTreehugger Robot */
1035*9558e6acSTreehugger Robot int
checkchain(struct fat_descriptor * fat,cl_t head,size_t * chainsize)1036*9558e6acSTreehugger Robot checkchain(struct fat_descriptor *fat, cl_t head, size_t *chainsize)
1037*9558e6acSTreehugger Robot {
1038*9558e6acSTreehugger Robot cl_t prev_cl, current_cl, next_cl;
1039*9558e6acSTreehugger Robot const char *op;
1040*9558e6acSTreehugger Robot
1041*9558e6acSTreehugger Robot /*
1042*9558e6acSTreehugger Robot * We expect that the caller to give us a real, unvisited 'head'
1043*9558e6acSTreehugger Robot * cluster, and it must be a valid cluster. While scanning the
1044*9558e6acSTreehugger Robot * FAT table, we already excluded all clusters that was claimed
1045*9558e6acSTreehugger Robot * as a "next" cluster. Assert all the three conditions.
1046*9558e6acSTreehugger Robot */
1047*9558e6acSTreehugger Robot assert(valid_cl(fat, head));
1048*9558e6acSTreehugger Robot assert(fat_is_cl_head(fat, head));
1049*9558e6acSTreehugger Robot
1050*9558e6acSTreehugger Robot /*
1051*9558e6acSTreehugger Robot * Immediately mark the 'head' cluster that we are about to visit.
1052*9558e6acSTreehugger Robot */
1053*9558e6acSTreehugger Robot fat_clear_cl_head(fat, head);
1054*9558e6acSTreehugger Robot
1055*9558e6acSTreehugger Robot /*
1056*9558e6acSTreehugger Robot * The allocation of a non-zero sized file or directory is
1057*9558e6acSTreehugger Robot * represented as a singly linked list, and the tail node
1058*9558e6acSTreehugger Robot * would be the EOF marker (>=CLUST_EOFS).
1059*9558e6acSTreehugger Robot *
1060*9558e6acSTreehugger Robot * With a valid head node at hand, we expect all subsequent
1061*9558e6acSTreehugger Robot * cluster to be either a not yet seen and valid cluster (we
1062*9558e6acSTreehugger Robot * would continue counting), or the EOF marker (we conclude
1063*9558e6acSTreehugger Robot * the scan of this chain).
1064*9558e6acSTreehugger Robot *
1065*9558e6acSTreehugger Robot * For all other cases, the chain is invalid, and the only
1066*9558e6acSTreehugger Robot * viable fix would be to truncate at the current node (mark
1067*9558e6acSTreehugger Robot * it as EOF) when the next node violates that.
1068*9558e6acSTreehugger Robot */
1069*9558e6acSTreehugger Robot *chainsize = 0;
1070*9558e6acSTreehugger Robot prev_cl = current_cl = head;
1071*9558e6acSTreehugger Robot for (next_cl = fat_get_cl_next(fat, current_cl);
1072*9558e6acSTreehugger Robot valid_cl(fat, next_cl);
1073*9558e6acSTreehugger Robot prev_cl = current_cl, current_cl = next_cl, next_cl = fat_get_cl_next(fat, current_cl))
1074*9558e6acSTreehugger Robot (*chainsize)++;
1075*9558e6acSTreehugger Robot
1076*9558e6acSTreehugger Robot /* A natural end */
1077*9558e6acSTreehugger Robot if (next_cl >= CLUST_EOFS) {
1078*9558e6acSTreehugger Robot (*chainsize)++;
1079*9558e6acSTreehugger Robot return FSOK;
1080*9558e6acSTreehugger Robot }
1081*9558e6acSTreehugger Robot
1082*9558e6acSTreehugger Robot /*
1083*9558e6acSTreehugger Robot * The chain ended with an out-of-range cluster number.
1084*9558e6acSTreehugger Robot *
1085*9558e6acSTreehugger Robot * If the current node is e.g. CLUST_FREE, CLUST_BAD, etc.,
1086*9558e6acSTreehugger Robot * it should not be present in a chain and we has to truncate
1087*9558e6acSTreehugger Robot * at the previous node.
1088*9558e6acSTreehugger Robot *
1089*9558e6acSTreehugger Robot * If the current cluster points to an invalid cluster, the
1090*9558e6acSTreehugger Robot * current cluster might have useful data and we truncate at
1091*9558e6acSTreehugger Robot * the current cluster instead.
1092*9558e6acSTreehugger Robot */
1093*9558e6acSTreehugger Robot if (next_cl == CLUST_FREE || next_cl >= CLUST_RSRVD) {
1094*9558e6acSTreehugger Robot pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
1095*9558e6acSTreehugger Robot head, rsrvdcltype(next_cl));
1096*9558e6acSTreehugger Robot current_cl = prev_cl;
1097*9558e6acSTreehugger Robot } else {
1098*9558e6acSTreehugger Robot pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
1099*9558e6acSTreehugger Robot head,
1100*9558e6acSTreehugger Robot next_cl & boot_of_(fat)->ClustMask);
1101*9558e6acSTreehugger Robot (*chainsize)++;
1102*9558e6acSTreehugger Robot }
1103*9558e6acSTreehugger Robot
1104*9558e6acSTreehugger Robot if (*chainsize > 0) {
1105*9558e6acSTreehugger Robot op = "Truncate";
1106*9558e6acSTreehugger Robot next_cl = CLUST_EOF;
1107*9558e6acSTreehugger Robot } else {
1108*9558e6acSTreehugger Robot op = "Clear";
1109*9558e6acSTreehugger Robot next_cl = CLUST_FREE;
1110*9558e6acSTreehugger Robot }
1111*9558e6acSTreehugger Robot if (ask(0, "%s", op)) {
1112*9558e6acSTreehugger Robot return (fat_set_cl_next(fat, current_cl, next_cl) | FSFATMOD);
1113*9558e6acSTreehugger Robot } else {
1114*9558e6acSTreehugger Robot return (FSERROR);
1115*9558e6acSTreehugger Robot }
1116*9558e6acSTreehugger Robot }
1117*9558e6acSTreehugger Robot
1118*9558e6acSTreehugger Robot /*
1119*9558e6acSTreehugger Robot * Clear cluster chain from head.
1120*9558e6acSTreehugger Robot */
1121*9558e6acSTreehugger Robot void
clearchain(struct fat_descriptor * fat,cl_t head)1122*9558e6acSTreehugger Robot clearchain(struct fat_descriptor *fat, cl_t head)
1123*9558e6acSTreehugger Robot {
1124*9558e6acSTreehugger Robot cl_t current_cl, next_cl;
1125*9558e6acSTreehugger Robot struct bootblock *boot = boot_of_(fat);
1126*9558e6acSTreehugger Robot
1127*9558e6acSTreehugger Robot current_cl = head;
1128*9558e6acSTreehugger Robot
1129*9558e6acSTreehugger Robot while (valid_cl(fat, current_cl)) {
1130*9558e6acSTreehugger Robot next_cl = fat_get_cl_next(fat, current_cl);
1131*9558e6acSTreehugger Robot (void)fat_set_cl_next(fat, current_cl, CLUST_FREE);
1132*9558e6acSTreehugger Robot boot->NumFree++;
1133*9558e6acSTreehugger Robot current_cl = next_cl;
1134*9558e6acSTreehugger Robot }
1135*9558e6acSTreehugger Robot
1136*9558e6acSTreehugger Robot }
1137*9558e6acSTreehugger Robot
1138*9558e6acSTreehugger Robot /*
1139*9558e6acSTreehugger Robot * Overwrite the n-th FAT with FAT0
1140*9558e6acSTreehugger Robot */
1141*9558e6acSTreehugger Robot static int
copyfat(struct fat_descriptor * fat,int n)1142*9558e6acSTreehugger Robot copyfat(struct fat_descriptor *fat, int n)
1143*9558e6acSTreehugger Robot {
1144*9558e6acSTreehugger Robot size_t rwsize, tailsize, blobs, i;
1145*9558e6acSTreehugger Robot off_t dst_off, src_off;
1146*9558e6acSTreehugger Robot struct bootblock *boot;
1147*9558e6acSTreehugger Robot int ret, fd;
1148*9558e6acSTreehugger Robot
1149*9558e6acSTreehugger Robot ret = FSOK;
1150*9558e6acSTreehugger Robot fd = fd_of_(fat);
1151*9558e6acSTreehugger Robot boot = boot_of_(fat);
1152*9558e6acSTreehugger Robot
1153*9558e6acSTreehugger Robot blobs = howmany(fat->fatsize, fat32_cache_size);
1154*9558e6acSTreehugger Robot tailsize = fat->fatsize % fat32_cache_size;
1155*9558e6acSTreehugger Robot if (tailsize == 0) {
1156*9558e6acSTreehugger Robot tailsize = fat32_cache_size;
1157*9558e6acSTreehugger Robot }
1158*9558e6acSTreehugger Robot rwsize = fat32_cache_size;
1159*9558e6acSTreehugger Robot
1160*9558e6acSTreehugger Robot src_off = fat->fat32_offset;
1161*9558e6acSTreehugger Robot dst_off = boot->bpbResSectors + n * boot->FATsecs;
1162*9558e6acSTreehugger Robot dst_off *= boot->bpbBytesPerSec;
1163*9558e6acSTreehugger Robot
1164*9558e6acSTreehugger Robot for (i = 0; i < blobs;
1165*9558e6acSTreehugger Robot i++, src_off += fat32_cache_size, dst_off += fat32_cache_size) {
1166*9558e6acSTreehugger Robot if (i == blobs - 1) {
1167*9558e6acSTreehugger Robot rwsize = tailsize;
1168*9558e6acSTreehugger Robot }
1169*9558e6acSTreehugger Robot if ((lseek(fd, src_off, SEEK_SET) != src_off ||
1170*9558e6acSTreehugger Robot (size_t)read(fd, fat->fatbuf, rwsize) != rwsize) &&
1171*9558e6acSTreehugger Robot ret == FSOK) {
1172*9558e6acSTreehugger Robot perr("Unable to read FAT0");
1173*9558e6acSTreehugger Robot ret = FSFATAL;
1174*9558e6acSTreehugger Robot continue;
1175*9558e6acSTreehugger Robot }
1176*9558e6acSTreehugger Robot if ((lseek(fd, dst_off, SEEK_SET) != dst_off ||
1177*9558e6acSTreehugger Robot (size_t)write(fd, fat->fatbuf, rwsize) != rwsize) &&
1178*9558e6acSTreehugger Robot ret == FSOK) {
1179*9558e6acSTreehugger Robot perr("Unable to write FAT %d", n);
1180*9558e6acSTreehugger Robot ret = FSERROR;
1181*9558e6acSTreehugger Robot }
1182*9558e6acSTreehugger Robot }
1183*9558e6acSTreehugger Robot return (ret);
1184*9558e6acSTreehugger Robot }
1185*9558e6acSTreehugger Robot
1186*9558e6acSTreehugger Robot /*
1187*9558e6acSTreehugger Robot * Write out FAT
1188*9558e6acSTreehugger Robot */
1189*9558e6acSTreehugger Robot int
writefat(struct fat_descriptor * fat)1190*9558e6acSTreehugger Robot writefat(struct fat_descriptor *fat)
1191*9558e6acSTreehugger Robot {
1192*9558e6acSTreehugger Robot u_int i;
1193*9558e6acSTreehugger Robot size_t writesz;
1194*9558e6acSTreehugger Robot off_t dst_base;
1195*9558e6acSTreehugger Robot int ret = FSOK, fd;
1196*9558e6acSTreehugger Robot struct bootblock *boot;
1197*9558e6acSTreehugger Robot struct fat32_cache_entry *entry;
1198*9558e6acSTreehugger Robot
1199*9558e6acSTreehugger Robot boot = boot_of_(fat);
1200*9558e6acSTreehugger Robot fd = fd_of_(fat);
1201*9558e6acSTreehugger Robot
1202*9558e6acSTreehugger Robot if (fat->use_cache) {
1203*9558e6acSTreehugger Robot /*
1204*9558e6acSTreehugger Robot * Attempt to flush all in-flight cache, and bail out
1205*9558e6acSTreehugger Robot * if we encountered an error (but only emit error
1206*9558e6acSTreehugger Robot * message once). Stop proceeding with copyfat()
1207*9558e6acSTreehugger Robot * if any flush failed.
1208*9558e6acSTreehugger Robot */
1209*9558e6acSTreehugger Robot TAILQ_FOREACH(entry, &fat->fat32_cache_head, entries) {
1210*9558e6acSTreehugger Robot if (fat_flush_fat32_cache_entry(fat, entry) != FSOK) {
1211*9558e6acSTreehugger Robot if (ret == FSOK) {
1212*9558e6acSTreehugger Robot perr("Unable to write FAT");
1213*9558e6acSTreehugger Robot ret = FSFATAL;
1214*9558e6acSTreehugger Robot }
1215*9558e6acSTreehugger Robot }
1216*9558e6acSTreehugger Robot }
1217*9558e6acSTreehugger Robot if (ret != FSOK)
1218*9558e6acSTreehugger Robot return (ret);
1219*9558e6acSTreehugger Robot
1220*9558e6acSTreehugger Robot /* Update backup copies of FAT, error is not fatal */
1221*9558e6acSTreehugger Robot for (i = 1; i < boot->bpbFATs; i++) {
1222*9558e6acSTreehugger Robot if (copyfat(fat, i) != FSOK)
1223*9558e6acSTreehugger Robot ret = FSERROR;
1224*9558e6acSTreehugger Robot }
1225*9558e6acSTreehugger Robot } else {
1226*9558e6acSTreehugger Robot writesz = fat->fatsize;
1227*9558e6acSTreehugger Robot
1228*9558e6acSTreehugger Robot for (i = fat->is_mmapped ? 1 : 0; i < boot->bpbFATs; i++) {
1229*9558e6acSTreehugger Robot dst_base = boot->bpbResSectors + i * boot->FATsecs;
1230*9558e6acSTreehugger Robot dst_base *= boot->bpbBytesPerSec;
1231*9558e6acSTreehugger Robot if ((lseek(fd, dst_base, SEEK_SET) != dst_base ||
1232*9558e6acSTreehugger Robot (size_t)write(fd, fat->fatbuf, writesz) != writesz) &&
1233*9558e6acSTreehugger Robot ret == FSOK) {
1234*9558e6acSTreehugger Robot perr("Unable to write FAT %d", i);
1235*9558e6acSTreehugger Robot ret = ((i == 0) ? FSFATAL : FSERROR);
1236*9558e6acSTreehugger Robot }
1237*9558e6acSTreehugger Robot }
1238*9558e6acSTreehugger Robot }
1239*9558e6acSTreehugger Robot
1240*9558e6acSTreehugger Robot return ret;
1241*9558e6acSTreehugger Robot }
1242*9558e6acSTreehugger Robot
1243*9558e6acSTreehugger Robot /*
1244*9558e6acSTreehugger Robot * Check a complete in-memory FAT for lost cluster chains
1245*9558e6acSTreehugger Robot */
1246*9558e6acSTreehugger Robot int
checklost(struct fat_descriptor * fat)1247*9558e6acSTreehugger Robot checklost(struct fat_descriptor *fat)
1248*9558e6acSTreehugger Robot {
1249*9558e6acSTreehugger Robot cl_t head;
1250*9558e6acSTreehugger Robot int mod = FSOK;
1251*9558e6acSTreehugger Robot int dosfs, ret;
1252*9558e6acSTreehugger Robot size_t chains, chainlength;
1253*9558e6acSTreehugger Robot struct bootblock *boot;
1254*9558e6acSTreehugger Robot
1255*9558e6acSTreehugger Robot dosfs = fd_of_(fat);
1256*9558e6acSTreehugger Robot boot = boot_of_(fat);
1257*9558e6acSTreehugger Robot
1258*9558e6acSTreehugger Robot /*
1259*9558e6acSTreehugger Robot * At this point, we have already traversed all directories.
1260*9558e6acSTreehugger Robot * All remaining chain heads in the bitmap are heads of lost
1261*9558e6acSTreehugger Robot * chains.
1262*9558e6acSTreehugger Robot */
1263*9558e6acSTreehugger Robot chains = fat_get_head_count(fat);
1264*9558e6acSTreehugger Robot for (head = CLUST_FIRST;
1265*9558e6acSTreehugger Robot chains > 0 && head < boot->NumClusters;
1266*9558e6acSTreehugger Robot ) {
1267*9558e6acSTreehugger Robot /*
1268*9558e6acSTreehugger Robot * We expect the bitmap to be very sparse, so skip if
1269*9558e6acSTreehugger Robot * the range is full of 0's
1270*9558e6acSTreehugger Robot */
1271*9558e6acSTreehugger Robot if (head % LONG_BIT == 0 &&
1272*9558e6acSTreehugger Robot !fat_is_cl_head_in_range(fat, head)) {
1273*9558e6acSTreehugger Robot head += LONG_BIT;
1274*9558e6acSTreehugger Robot continue;
1275*9558e6acSTreehugger Robot }
1276*9558e6acSTreehugger Robot if (fat_is_cl_head(fat, head)) {
1277*9558e6acSTreehugger Robot ret = checkchain(fat, head, &chainlength);
1278*9558e6acSTreehugger Robot if (ret != FSERROR && chainlength > 0) {
1279*9558e6acSTreehugger Robot pwarn("Lost cluster chain at cluster %u\n"
1280*9558e6acSTreehugger Robot "%zd Cluster(s) lost\n",
1281*9558e6acSTreehugger Robot head, chainlength);
1282*9558e6acSTreehugger Robot mod |= ret = reconnect(fat, head,
1283*9558e6acSTreehugger Robot chainlength);
1284*9558e6acSTreehugger Robot }
1285*9558e6acSTreehugger Robot if (mod & FSFATAL)
1286*9558e6acSTreehugger Robot break;
1287*9558e6acSTreehugger Robot if (ret == FSERROR && ask(0, "Clear")) {
1288*9558e6acSTreehugger Robot clearchain(fat, head);
1289*9558e6acSTreehugger Robot mod |= FSFATMOD;
1290*9558e6acSTreehugger Robot }
1291*9558e6acSTreehugger Robot chains--;
1292*9558e6acSTreehugger Robot }
1293*9558e6acSTreehugger Robot head++;
1294*9558e6acSTreehugger Robot }
1295*9558e6acSTreehugger Robot
1296*9558e6acSTreehugger Robot finishlf();
1297*9558e6acSTreehugger Robot
1298*9558e6acSTreehugger Robot if (boot->bpbFSInfo) {
1299*9558e6acSTreehugger Robot ret = 0;
1300*9558e6acSTreehugger Robot if (boot->FSFree != 0xffffffffU &&
1301*9558e6acSTreehugger Robot boot->FSFree != boot->NumFree) {
1302*9558e6acSTreehugger Robot pwarn("Free space in FSInfo block (%u) not correct (%u)\n",
1303*9558e6acSTreehugger Robot boot->FSFree, boot->NumFree);
1304*9558e6acSTreehugger Robot if (ask(1, "Fix")) {
1305*9558e6acSTreehugger Robot boot->FSFree = boot->NumFree;
1306*9558e6acSTreehugger Robot ret = 1;
1307*9558e6acSTreehugger Robot }
1308*9558e6acSTreehugger Robot }
1309*9558e6acSTreehugger Robot if (boot->FSNext != 0xffffffffU &&
1310*9558e6acSTreehugger Robot (boot->FSNext >= boot->NumClusters ||
1311*9558e6acSTreehugger Robot (boot->NumFree && fat_get_cl_next(fat, boot->FSNext) != CLUST_FREE))) {
1312*9558e6acSTreehugger Robot pwarn("Next free cluster in FSInfo block (%u) %s\n",
1313*9558e6acSTreehugger Robot boot->FSNext,
1314*9558e6acSTreehugger Robot (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free");
1315*9558e6acSTreehugger Robot if (ask(1, "Fix"))
1316*9558e6acSTreehugger Robot for (head = CLUST_FIRST; head < boot->NumClusters; head++)
1317*9558e6acSTreehugger Robot if (fat_get_cl_next(fat, head) == CLUST_FREE) {
1318*9558e6acSTreehugger Robot boot->FSNext = head;
1319*9558e6acSTreehugger Robot ret = 1;
1320*9558e6acSTreehugger Robot break;
1321*9558e6acSTreehugger Robot }
1322*9558e6acSTreehugger Robot }
1323*9558e6acSTreehugger Robot if (ret)
1324*9558e6acSTreehugger Robot mod |= writefsinfo(dosfs, boot);
1325*9558e6acSTreehugger Robot }
1326*9558e6acSTreehugger Robot
1327*9558e6acSTreehugger Robot return mod;
1328*9558e6acSTreehugger Robot }
1329