11de7b4b8SPedro F. Giffuni /*-
21de7b4b8SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni *
49708ba9fSXin LI * Copyright (c) 2019 Google LLC
50121b42aSDavid E. O'Brien * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
60121b42aSDavid E. O'Brien * Copyright (c) 1995 Martin Husemann
70121b42aSDavid E. O'Brien *
80121b42aSDavid E. O'Brien * Redistribution and use in source and binary forms, with or without
90121b42aSDavid E. O'Brien * modification, are permitted provided that the following conditions
100121b42aSDavid E. O'Brien * are met:
110121b42aSDavid E. O'Brien * 1. Redistributions of source code must retain the above copyright
120121b42aSDavid E. O'Brien * notice, this list of conditions and the following disclaimer.
130121b42aSDavid E. O'Brien * 2. Redistributions in binary form must reproduce the above copyright
140121b42aSDavid E. O'Brien * notice, this list of conditions and the following disclaimer in the
150121b42aSDavid E. O'Brien * documentation and/or other materials provided with the distribution.
160121b42aSDavid E. O'Brien *
170121b42aSDavid E. O'Brien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
180121b42aSDavid E. O'Brien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
190121b42aSDavid E. O'Brien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
200121b42aSDavid E. O'Brien * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
210121b42aSDavid E. O'Brien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
220121b42aSDavid E. O'Brien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
230121b42aSDavid E. O'Brien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
240121b42aSDavid E. O'Brien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
250121b42aSDavid E. O'Brien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
260121b42aSDavid E. O'Brien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
270121b42aSDavid E. O'Brien */
280121b42aSDavid E. O'Brien
290121b42aSDavid E. O'Brien
300121b42aSDavid E. O'Brien #include <sys/cdefs.h>
310121b42aSDavid E. O'Brien #ifndef lint
326cf357bcSUlrich Spörlein __RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $");
330121b42aSDavid E. O'Brien #endif /* not lint */
340121b42aSDavid E. O'Brien
359708ba9fSXin LI #include <sys/endian.h>
369708ba9fSXin LI #include <sys/queue.h>
379708ba9fSXin LI #include <sys/limits.h>
389708ba9fSXin LI #include <sys/mman.h>
399708ba9fSXin LI #include <sys/param.h>
409708ba9fSXin LI
419708ba9fSXin LI #include <assert.h>
429708ba9fSXin LI #include <stdbool.h>
430121b42aSDavid E. O'Brien #include <stdlib.h>
440121b42aSDavid E. O'Brien #include <string.h>
450121b42aSDavid E. O'Brien #include <ctype.h>
460121b42aSDavid E. O'Brien #include <stdio.h>
470121b42aSDavid E. O'Brien #include <unistd.h>
480121b42aSDavid E. O'Brien
490121b42aSDavid E. O'Brien #include "ext.h"
500121b42aSDavid E. O'Brien #include "fsutil.h"
510121b42aSDavid E. O'Brien
529708ba9fSXin LI static int _readfat(struct fat_descriptor *);
539708ba9fSXin LI static inline struct bootblock* boot_of_(struct fat_descriptor *);
549708ba9fSXin LI static inline int fd_of_(struct fat_descriptor *);
559708ba9fSXin LI static inline bool valid_cl(struct fat_descriptor *, cl_t);
560121b42aSDavid E. O'Brien
579708ba9fSXin LI
589708ba9fSXin LI /*
599708ba9fSXin LI * Head bitmap for FAT scanning.
609708ba9fSXin LI *
619708ba9fSXin LI * FAT32 have up to 2^28 = 256M entries, and FAT16/12 have much less.
629708ba9fSXin LI * For each cluster, we use 1 bit to represent if it's a head cluster
639708ba9fSXin LI * (the first cluster of a cluster chain).
649708ba9fSXin LI *
659708ba9fSXin LI * Head bitmap
669708ba9fSXin LI * ===========
679708ba9fSXin LI * Initially, we set all bits to 1. In readfat(), we traverse the
689708ba9fSXin LI * whole FAT and mark each cluster identified as "next" cluster as
699708ba9fSXin LI * 0. After the scan, we have a bitmap with 1's to indicate the
709708ba9fSXin LI * corresponding cluster was a "head" cluster.
719708ba9fSXin LI *
729708ba9fSXin LI * We use head bitmap to identify lost chains: a head cluster that was
739708ba9fSXin LI * not being claimed by any file or directories is the head cluster of
749708ba9fSXin LI * a lost chain.
759708ba9fSXin LI *
769708ba9fSXin LI * Handle of lost chains
779708ba9fSXin LI * =====================
789708ba9fSXin LI * At the end of scanning, we can easily find all lost chain's heads
799708ba9fSXin LI * by finding out the 1's in the head bitmap.
809708ba9fSXin LI */
819708ba9fSXin LI
829708ba9fSXin LI typedef struct long_bitmap {
839708ba9fSXin LI unsigned long *map;
849708ba9fSXin LI size_t count; /* Total set bits in the map */
859708ba9fSXin LI } long_bitmap_t;
869708ba9fSXin LI
879708ba9fSXin LI static inline void
bitmap_clear(long_bitmap_t * lbp,cl_t cl)889708ba9fSXin LI bitmap_clear(long_bitmap_t *lbp, cl_t cl)
899708ba9fSXin LI {
909708ba9fSXin LI cl_t i = cl / LONG_BIT;
919708ba9fSXin LI unsigned long clearmask = ~(1UL << (cl % LONG_BIT));
929708ba9fSXin LI
939708ba9fSXin LI assert((lbp->map[i] & ~clearmask) != 0);
949708ba9fSXin LI lbp->map[i] &= clearmask;
959708ba9fSXin LI lbp->count--;
969708ba9fSXin LI }
979708ba9fSXin LI
989708ba9fSXin LI static inline bool
bitmap_get(long_bitmap_t * lbp,cl_t cl)999708ba9fSXin LI bitmap_get(long_bitmap_t *lbp, cl_t cl)
1009708ba9fSXin LI {
1019708ba9fSXin LI cl_t i = cl / LONG_BIT;
1029708ba9fSXin LI unsigned long usedbit = 1UL << (cl % LONG_BIT);
1039708ba9fSXin LI
1049708ba9fSXin LI return ((lbp->map[i] & usedbit) == usedbit);
1059708ba9fSXin LI }
1069708ba9fSXin LI
1079708ba9fSXin LI static inline bool
bitmap_none_in_range(long_bitmap_t * lbp,cl_t cl)1089708ba9fSXin LI bitmap_none_in_range(long_bitmap_t *lbp, cl_t cl)
1099708ba9fSXin LI {
1109708ba9fSXin LI cl_t i = cl / LONG_BIT;
1119708ba9fSXin LI
1129708ba9fSXin LI return (lbp->map[i] == 0);
1139708ba9fSXin LI }
1149708ba9fSXin LI
1159708ba9fSXin LI static inline size_t
bitmap_count(long_bitmap_t * lbp)1169708ba9fSXin LI bitmap_count(long_bitmap_t *lbp)
1179708ba9fSXin LI {
1189708ba9fSXin LI return (lbp->count);
1199708ba9fSXin LI }
1209708ba9fSXin LI
1219708ba9fSXin LI static int
bitmap_ctor(long_bitmap_t * lbp,size_t bits,bool allone)1229708ba9fSXin LI bitmap_ctor(long_bitmap_t *lbp, size_t bits, bool allone)
1239708ba9fSXin LI {
1249708ba9fSXin LI size_t bitmap_size = roundup2(bits, LONG_BIT) / (LONG_BIT / 8);
1259708ba9fSXin LI
1269708ba9fSXin LI free(lbp->map);
1279708ba9fSXin LI lbp->map = calloc(1, bitmap_size);
1289708ba9fSXin LI if (lbp->map == NULL)
1299708ba9fSXin LI return FSFATAL;
1309708ba9fSXin LI
1319708ba9fSXin LI if (allone) {
1329708ba9fSXin LI memset(lbp->map, 0xff, bitmap_size);
1339708ba9fSXin LI lbp->count = bits;
1349708ba9fSXin LI } else {
1359708ba9fSXin LI lbp->count = 0;
1369708ba9fSXin LI }
1379708ba9fSXin LI return FSOK;
1389708ba9fSXin LI }
1399708ba9fSXin LI
1409708ba9fSXin LI static void
bitmap_dtor(long_bitmap_t * lbp)1419708ba9fSXin LI bitmap_dtor(long_bitmap_t *lbp)
1429708ba9fSXin LI {
1439708ba9fSXin LI free(lbp->map);
1449708ba9fSXin LI lbp->map = NULL;
1459708ba9fSXin LI }
1469708ba9fSXin LI
1479708ba9fSXin LI /*
1489708ba9fSXin LI * FAT32 can be as big as 256MiB (2^26 entries * 4 bytes), when we
1499708ba9fSXin LI * can not ask the kernel to manage the access, use a simple LRU
1509708ba9fSXin LI * cache with chunk size of 128 KiB to manage it.
1519708ba9fSXin LI */
1529708ba9fSXin LI struct fat32_cache_entry {
1539708ba9fSXin LI TAILQ_ENTRY(fat32_cache_entry) entries;
1549708ba9fSXin LI uint8_t *chunk; /* pointer to chunk */
1559708ba9fSXin LI off_t addr; /* offset */
1569708ba9fSXin LI bool dirty; /* dirty bit */
1579708ba9fSXin LI };
1589708ba9fSXin LI
1599708ba9fSXin LI static const size_t fat32_cache_chunk_size = 131072; /* MAXPHYS */
1609708ba9fSXin LI static const size_t fat32_cache_size = 4194304;
1619708ba9fSXin LI static const size_t fat32_cache_entries = 32; /* XXXgcc: cache_size / cache_chunk_size */
1629708ba9fSXin LI
1639708ba9fSXin LI /*
1649708ba9fSXin LI * FAT table descriptor, represents a FAT table that is already loaded
1659708ba9fSXin LI * into memory.
1669708ba9fSXin LI */
1679708ba9fSXin LI struct fat_descriptor {
1689708ba9fSXin LI struct bootblock *boot;
1699708ba9fSXin LI uint8_t *fatbuf;
1709708ba9fSXin LI cl_t (*get)(struct fat_descriptor *, cl_t);
1719708ba9fSXin LI int (*set)(struct fat_descriptor *, cl_t, cl_t);
1729708ba9fSXin LI long_bitmap_t headbitmap;
1739708ba9fSXin LI int fd;
1749708ba9fSXin LI bool is_mmapped;
1759708ba9fSXin LI bool use_cache;
1769708ba9fSXin LI size_t fatsize;
1779708ba9fSXin LI
1789708ba9fSXin LI size_t fat32_cached_chunks;
1799708ba9fSXin LI TAILQ_HEAD(cachehead, fat32_cache_entry) fat32_cache_head;
1809708ba9fSXin LI struct fat32_cache_entry *fat32_cache_allentries;
1819708ba9fSXin LI off_t fat32_offset;
1829708ba9fSXin LI off_t fat32_lastaddr;
1839708ba9fSXin LI };
1849708ba9fSXin LI
1859708ba9fSXin LI void
fat_clear_cl_head(struct fat_descriptor * fat,cl_t cl)1869708ba9fSXin LI fat_clear_cl_head(struct fat_descriptor *fat, cl_t cl)
1879708ba9fSXin LI {
1889708ba9fSXin LI bitmap_clear(&fat->headbitmap, cl);
1899708ba9fSXin LI }
1909708ba9fSXin LI
1919708ba9fSXin LI bool
fat_is_cl_head(struct fat_descriptor * fat,cl_t cl)1929708ba9fSXin LI fat_is_cl_head(struct fat_descriptor *fat, cl_t cl)
1939708ba9fSXin LI {
1949708ba9fSXin LI return (bitmap_get(&fat->headbitmap, cl));
1959708ba9fSXin LI }
1969708ba9fSXin LI
1979708ba9fSXin LI static inline bool
fat_is_cl_head_in_range(struct fat_descriptor * fat,cl_t cl)1989708ba9fSXin LI fat_is_cl_head_in_range(struct fat_descriptor *fat, cl_t cl)
1999708ba9fSXin LI {
2009708ba9fSXin LI return (!(bitmap_none_in_range(&fat->headbitmap, cl)));
2019708ba9fSXin LI }
2029708ba9fSXin LI
2039708ba9fSXin LI static size_t
fat_get_head_count(struct fat_descriptor * fat)2049708ba9fSXin LI fat_get_head_count(struct fat_descriptor *fat)
2059708ba9fSXin LI {
2069708ba9fSXin LI return (bitmap_count(&fat->headbitmap));
2079708ba9fSXin LI }
2089708ba9fSXin LI
2099708ba9fSXin LI /*
2109708ba9fSXin LI * FAT12 accessors.
2119708ba9fSXin LI *
2129708ba9fSXin LI * FAT12s are sufficiently small, expect it to always fit in the RAM.
2139708ba9fSXin LI */
2149708ba9fSXin LI static inline uint8_t *
fat_get_fat12_ptr(struct fat_descriptor * fat,cl_t cl)2159708ba9fSXin LI fat_get_fat12_ptr(struct fat_descriptor *fat, cl_t cl)
2169708ba9fSXin LI {
2179708ba9fSXin LI return (fat->fatbuf + ((cl + (cl >> 1))));
2189708ba9fSXin LI }
2199708ba9fSXin LI
2209708ba9fSXin LI static cl_t
fat_get_fat12_next(struct fat_descriptor * fat,cl_t cl)2219708ba9fSXin LI fat_get_fat12_next(struct fat_descriptor *fat, cl_t cl)
2229708ba9fSXin LI {
2239708ba9fSXin LI const uint8_t *p;
2249708ba9fSXin LI cl_t retval;
2259708ba9fSXin LI
2269708ba9fSXin LI p = fat_get_fat12_ptr(fat, cl);
2279708ba9fSXin LI retval = le16dec(p);
2289708ba9fSXin LI /* Odd cluster: lower 4 bits belongs to the subsequent cluster */
2299708ba9fSXin LI if ((cl & 1) == 1)
2309708ba9fSXin LI retval >>= 4;
2319708ba9fSXin LI retval &= CLUST12_MASK;
2329708ba9fSXin LI
2339708ba9fSXin LI if (retval >= (CLUST_BAD & CLUST12_MASK))
2349708ba9fSXin LI retval |= ~CLUST12_MASK;
2359708ba9fSXin LI
2369708ba9fSXin LI return (retval);
2379708ba9fSXin LI }
2389708ba9fSXin LI
2399708ba9fSXin LI static int
fat_set_fat12_next(struct fat_descriptor * fat,cl_t cl,cl_t nextcl)2409708ba9fSXin LI fat_set_fat12_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl)
2419708ba9fSXin LI {
2429708ba9fSXin LI uint8_t *p;
2439708ba9fSXin LI
2449708ba9fSXin LI /* Truncate 'nextcl' value, if needed */
2459708ba9fSXin LI nextcl &= CLUST12_MASK;
2469708ba9fSXin LI
2479708ba9fSXin LI p = fat_get_fat12_ptr(fat, cl);
2489708ba9fSXin LI
2499708ba9fSXin LI /*
2509708ba9fSXin LI * Read in the 4 bits from the subsequent (for even clusters)
2519708ba9fSXin LI * or the preceding (for odd clusters) cluster and combine
2529708ba9fSXin LI * it to the nextcl value for encoding
2539708ba9fSXin LI */
2549708ba9fSXin LI if ((cl & 1) == 0) {
2559708ba9fSXin LI nextcl |= ((p[1] & 0xf0) << 8);
2569708ba9fSXin LI } else {
2579708ba9fSXin LI nextcl <<= 4;
2589708ba9fSXin LI nextcl |= (p[0] & 0x0f);
2599708ba9fSXin LI }
2609708ba9fSXin LI
2619708ba9fSXin LI le16enc(p, (uint16_t)nextcl);
2629708ba9fSXin LI
2639708ba9fSXin LI return (0);
2649708ba9fSXin LI }
2659708ba9fSXin LI
2669708ba9fSXin LI /*
2679708ba9fSXin LI * FAT16 accessors.
2689708ba9fSXin LI *
2699708ba9fSXin LI * FAT16s are sufficiently small, expect it to always fit in the RAM.
2709708ba9fSXin LI */
2719708ba9fSXin LI static inline uint8_t *
fat_get_fat16_ptr(struct fat_descriptor * fat,cl_t cl)2729708ba9fSXin LI fat_get_fat16_ptr(struct fat_descriptor *fat, cl_t cl)
2739708ba9fSXin LI {
2749708ba9fSXin LI return (fat->fatbuf + (cl << 1));
2759708ba9fSXin LI }
2769708ba9fSXin LI
2779708ba9fSXin LI static cl_t
fat_get_fat16_next(struct fat_descriptor * fat,cl_t cl)2789708ba9fSXin LI fat_get_fat16_next(struct fat_descriptor *fat, cl_t cl)
2799708ba9fSXin LI {
2809708ba9fSXin LI const uint8_t *p;
2819708ba9fSXin LI cl_t retval;
2829708ba9fSXin LI
2839708ba9fSXin LI p = fat_get_fat16_ptr(fat, cl);
2849708ba9fSXin LI retval = le16dec(p) & CLUST16_MASK;
2859708ba9fSXin LI
2869708ba9fSXin LI if (retval >= (CLUST_BAD & CLUST16_MASK))
2879708ba9fSXin LI retval |= ~CLUST16_MASK;
2889708ba9fSXin LI
2899708ba9fSXin LI return (retval);
2909708ba9fSXin LI }
2919708ba9fSXin LI
2929708ba9fSXin LI static int
fat_set_fat16_next(struct fat_descriptor * fat,cl_t cl,cl_t nextcl)2939708ba9fSXin LI fat_set_fat16_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl)
2949708ba9fSXin LI {
2959708ba9fSXin LI uint8_t *p;
2969708ba9fSXin LI
2979708ba9fSXin LI /* Truncate 'nextcl' value, if needed */
2989708ba9fSXin LI nextcl &= CLUST16_MASK;
2999708ba9fSXin LI
3009708ba9fSXin LI p = fat_get_fat16_ptr(fat, cl);
3019708ba9fSXin LI
3029708ba9fSXin LI le16enc(p, (uint16_t)nextcl);
3039708ba9fSXin LI
3049708ba9fSXin LI return (0);
3059708ba9fSXin LI }
3069708ba9fSXin LI
3079708ba9fSXin LI /*
3089708ba9fSXin LI * FAT32 accessors.
3099708ba9fSXin LI */
3109708ba9fSXin LI static inline uint8_t *
fat_get_fat32_ptr(struct fat_descriptor * fat,cl_t cl)3119708ba9fSXin LI fat_get_fat32_ptr(struct fat_descriptor *fat, cl_t cl)
3129708ba9fSXin LI {
3139708ba9fSXin LI return (fat->fatbuf + (cl << 2));
3149708ba9fSXin LI }
3159708ba9fSXin LI
3169708ba9fSXin LI static cl_t
fat_get_fat32_next(struct fat_descriptor * fat,cl_t cl)3179708ba9fSXin LI fat_get_fat32_next(struct fat_descriptor *fat, cl_t cl)
3189708ba9fSXin LI {
3199708ba9fSXin LI const uint8_t *p;
3209708ba9fSXin LI cl_t retval;
3219708ba9fSXin LI
3229708ba9fSXin LI p = fat_get_fat32_ptr(fat, cl);
3239708ba9fSXin LI retval = le32dec(p) & CLUST32_MASK;
3249708ba9fSXin LI
3259708ba9fSXin LI if (retval >= (CLUST_BAD & CLUST32_MASK))
3269708ba9fSXin LI retval |= ~CLUST32_MASK;
3279708ba9fSXin LI
3289708ba9fSXin LI return (retval);
3299708ba9fSXin LI }
3309708ba9fSXin LI
3319708ba9fSXin LI static int
fat_set_fat32_next(struct fat_descriptor * fat,cl_t cl,cl_t nextcl)3329708ba9fSXin LI fat_set_fat32_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl)
3339708ba9fSXin LI {
3349708ba9fSXin LI uint8_t *p;
3359708ba9fSXin LI
3369708ba9fSXin LI /* Truncate 'nextcl' value, if needed */
3379708ba9fSXin LI nextcl &= CLUST32_MASK;
3389708ba9fSXin LI
3399708ba9fSXin LI p = fat_get_fat32_ptr(fat, cl);
3409708ba9fSXin LI
3419708ba9fSXin LI le32enc(p, (uint32_t)nextcl);
3429708ba9fSXin LI
3439708ba9fSXin LI return (0);
3449708ba9fSXin LI }
3459708ba9fSXin LI
3469708ba9fSXin LI static inline size_t
fat_get_iosize(struct fat_descriptor * fat,off_t address)3479708ba9fSXin LI fat_get_iosize(struct fat_descriptor *fat, off_t address)
3489708ba9fSXin LI {
3499708ba9fSXin LI
3509708ba9fSXin LI if (address == fat->fat32_lastaddr) {
3519708ba9fSXin LI return (fat->fatsize & ((off_t)fat32_cache_chunk_size - 1));
3529708ba9fSXin LI } else {
3539708ba9fSXin LI return (fat32_cache_chunk_size);
3549708ba9fSXin LI }
3559708ba9fSXin LI }
3569708ba9fSXin LI
3579708ba9fSXin LI static int
fat_flush_fat32_cache_entry(struct fat_descriptor * fat,struct fat32_cache_entry * entry)3589708ba9fSXin LI fat_flush_fat32_cache_entry(struct fat_descriptor *fat,
3599708ba9fSXin LI struct fat32_cache_entry *entry)
3609708ba9fSXin LI {
3619708ba9fSXin LI int fd;
3629708ba9fSXin LI off_t fat_addr;
3639708ba9fSXin LI size_t writesize;
3649708ba9fSXin LI
3659708ba9fSXin LI fd = fd_of_(fat);
3669708ba9fSXin LI
3679708ba9fSXin LI if (!entry->dirty)
3689708ba9fSXin LI return (FSOK);
3699708ba9fSXin LI
3709708ba9fSXin LI writesize = fat_get_iosize(fat, entry->addr);
3719708ba9fSXin LI
3729708ba9fSXin LI fat_addr = fat->fat32_offset + entry->addr;
3739708ba9fSXin LI if (lseek(fd, fat_addr, SEEK_SET) != fat_addr ||
3749708ba9fSXin LI (size_t)write(fd, entry->chunk, writesize) != writesize) {
3759708ba9fSXin LI pfatal("Unable to write FAT");
3769708ba9fSXin LI return (FSFATAL);
3779708ba9fSXin LI }
3789708ba9fSXin LI
3799708ba9fSXin LI entry->dirty = false;
3809708ba9fSXin LI return (FSOK);
3819708ba9fSXin LI }
3829708ba9fSXin LI
3839708ba9fSXin LI static struct fat32_cache_entry *
fat_get_fat32_cache_entry(struct fat_descriptor * fat,off_t addr,bool writing)3849708ba9fSXin LI fat_get_fat32_cache_entry(struct fat_descriptor *fat, off_t addr,
3859708ba9fSXin LI bool writing)
3869708ba9fSXin LI {
3879708ba9fSXin LI int fd;
3889708ba9fSXin LI struct fat32_cache_entry *entry, *first;
3899708ba9fSXin LI off_t fat_addr;
3909708ba9fSXin LI size_t rwsize;
3919708ba9fSXin LI
3929708ba9fSXin LI addr &= ~(fat32_cache_chunk_size - 1);
3939708ba9fSXin LI
3949708ba9fSXin LI first = TAILQ_FIRST(&fat->fat32_cache_head);
3959708ba9fSXin LI
3969708ba9fSXin LI /*
3979708ba9fSXin LI * Cache hit: if we already have the chunk, move it to list head
3989708ba9fSXin LI */
3999708ba9fSXin LI TAILQ_FOREACH(entry, &fat->fat32_cache_head, entries) {
4009708ba9fSXin LI if (entry->addr == addr) {
4019708ba9fSXin LI if (writing) {
4029708ba9fSXin LI entry->dirty = true;
4039708ba9fSXin LI }
4049708ba9fSXin LI if (entry != first) {
4059708ba9fSXin LI
4069708ba9fSXin LI TAILQ_REMOVE(&fat->fat32_cache_head, entry, entries);
4079708ba9fSXin LI TAILQ_INSERT_HEAD(&fat->fat32_cache_head, entry, entries);
4089708ba9fSXin LI }
4099708ba9fSXin LI return (entry);
4109708ba9fSXin LI }
4119708ba9fSXin LI }
4129708ba9fSXin LI
4139708ba9fSXin LI /*
4149708ba9fSXin LI * Cache miss: detach the chunk at tail of list, overwrite with
4159708ba9fSXin LI * the located chunk, and populate with data from disk.
4169708ba9fSXin LI */
4179708ba9fSXin LI entry = TAILQ_LAST(&fat->fat32_cache_head, cachehead);
4189708ba9fSXin LI TAILQ_REMOVE(&fat->fat32_cache_head, entry, entries);
4199708ba9fSXin LI if (fat_flush_fat32_cache_entry(fat, entry) != FSOK) {
4209708ba9fSXin LI return (NULL);
4219708ba9fSXin LI }
4229708ba9fSXin LI
4239708ba9fSXin LI rwsize = fat_get_iosize(fat, addr);
4249708ba9fSXin LI fat_addr = fat->fat32_offset + addr;
4259708ba9fSXin LI entry->addr = addr;
4269708ba9fSXin LI fd = fd_of_(fat);
4279708ba9fSXin LI if (lseek(fd, fat_addr, SEEK_SET) != fat_addr ||
4289708ba9fSXin LI (size_t)read(fd, entry->chunk, rwsize) != rwsize) {
4299708ba9fSXin LI pfatal("Unable to read FAT");
4309708ba9fSXin LI return (NULL);
4319708ba9fSXin LI }
4329708ba9fSXin LI if (writing) {
4339708ba9fSXin LI entry->dirty = true;
4349708ba9fSXin LI }
4359708ba9fSXin LI TAILQ_INSERT_HEAD(&fat->fat32_cache_head, entry, entries);
4369708ba9fSXin LI
4379708ba9fSXin LI return (entry);
4389708ba9fSXin LI }
4399708ba9fSXin LI
4409708ba9fSXin LI static inline uint8_t *
fat_get_fat32_cached_ptr(struct fat_descriptor * fat,cl_t cl,bool writing)4419708ba9fSXin LI fat_get_fat32_cached_ptr(struct fat_descriptor *fat, cl_t cl, bool writing)
4429708ba9fSXin LI {
4439708ba9fSXin LI off_t addr, off;
4449708ba9fSXin LI struct fat32_cache_entry *entry;
4459708ba9fSXin LI
4469708ba9fSXin LI addr = cl << 2;
4479708ba9fSXin LI entry = fat_get_fat32_cache_entry(fat, addr, writing);
4489708ba9fSXin LI
4499708ba9fSXin LI if (entry != NULL) {
4509708ba9fSXin LI off = addr & (fat32_cache_chunk_size - 1);
4519708ba9fSXin LI return (entry->chunk + off);
4529708ba9fSXin LI } else {
4539708ba9fSXin LI return (NULL);
4549708ba9fSXin LI }
4559708ba9fSXin LI }
4569708ba9fSXin LI
4579708ba9fSXin LI
4589708ba9fSXin LI static cl_t
fat_get_fat32_cached_next(struct fat_descriptor * fat,cl_t cl)4599708ba9fSXin LI fat_get_fat32_cached_next(struct fat_descriptor *fat, cl_t cl)
4609708ba9fSXin LI {
4619708ba9fSXin LI const uint8_t *p;
4629708ba9fSXin LI cl_t retval;
4639708ba9fSXin LI
4649708ba9fSXin LI p = fat_get_fat32_cached_ptr(fat, cl, false);
4659708ba9fSXin LI if (p != NULL) {
4669708ba9fSXin LI retval = le32dec(p) & CLUST32_MASK;
4679708ba9fSXin LI if (retval >= (CLUST_BAD & CLUST32_MASK))
4689708ba9fSXin LI retval |= ~CLUST32_MASK;
4699708ba9fSXin LI } else {
4709708ba9fSXin LI retval = CLUST_DEAD;
4719708ba9fSXin LI }
4729708ba9fSXin LI
4739708ba9fSXin LI return (retval);
4749708ba9fSXin LI }
4759708ba9fSXin LI
4769708ba9fSXin LI static int
fat_set_fat32_cached_next(struct fat_descriptor * fat,cl_t cl,cl_t nextcl)4779708ba9fSXin LI fat_set_fat32_cached_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl)
4789708ba9fSXin LI {
4799708ba9fSXin LI uint8_t *p;
4809708ba9fSXin LI
4819708ba9fSXin LI /* Truncate 'nextcl' value, if needed */
4829708ba9fSXin LI nextcl &= CLUST32_MASK;
4839708ba9fSXin LI
4849708ba9fSXin LI p = fat_get_fat32_cached_ptr(fat, cl, true);
4859708ba9fSXin LI if (p != NULL) {
4869708ba9fSXin LI le32enc(p, (uint32_t)nextcl);
4879708ba9fSXin LI return FSOK;
4889708ba9fSXin LI } else {
4899708ba9fSXin LI return FSFATAL;
4909708ba9fSXin LI }
4919708ba9fSXin LI }
4929708ba9fSXin LI
fat_get_cl_next(struct fat_descriptor * fat,cl_t cl)4939708ba9fSXin LI cl_t fat_get_cl_next(struct fat_descriptor *fat, cl_t cl)
4949708ba9fSXin LI {
4959708ba9fSXin LI
4969708ba9fSXin LI if (!valid_cl(fat, cl)) {
4979708ba9fSXin LI pfatal("Invalid cluster: %ud", cl);
4989708ba9fSXin LI return CLUST_DEAD;
4999708ba9fSXin LI }
5009708ba9fSXin LI
5019708ba9fSXin LI return (fat->get(fat, cl));
5029708ba9fSXin LI }
5039708ba9fSXin LI
fat_set_cl_next(struct fat_descriptor * fat,cl_t cl,cl_t nextcl)5049708ba9fSXin LI int fat_set_cl_next(struct fat_descriptor *fat, cl_t cl, cl_t nextcl)
5059708ba9fSXin LI {
5069708ba9fSXin LI
5079708ba9fSXin LI if (rdonly) {
5089708ba9fSXin LI pwarn(" (NO WRITE)\n");
5099708ba9fSXin LI return FSFATAL;
5109708ba9fSXin LI }
5119708ba9fSXin LI
5129708ba9fSXin LI if (!valid_cl(fat, cl)) {
5139708ba9fSXin LI pfatal("Invalid cluster: %ud", cl);
5149708ba9fSXin LI return FSFATAL;
5159708ba9fSXin LI }
5169708ba9fSXin LI
5179708ba9fSXin LI return (fat->set(fat, cl, nextcl));
5189708ba9fSXin LI }
5199708ba9fSXin LI
5209708ba9fSXin LI static inline struct bootblock*
boot_of_(struct fat_descriptor * fat)5219708ba9fSXin LI boot_of_(struct fat_descriptor *fat) {
5229708ba9fSXin LI
5239708ba9fSXin LI return (fat->boot);
5249708ba9fSXin LI }
5259708ba9fSXin LI
5269708ba9fSXin LI struct bootblock*
fat_get_boot(struct fat_descriptor * fat)5279708ba9fSXin LI fat_get_boot(struct fat_descriptor *fat) {
5289708ba9fSXin LI
5299708ba9fSXin LI return (boot_of_(fat));
5309708ba9fSXin LI }
5319708ba9fSXin LI
5329708ba9fSXin LI static inline int
fd_of_(struct fat_descriptor * fat)5339708ba9fSXin LI fd_of_(struct fat_descriptor *fat)
5349708ba9fSXin LI {
5359708ba9fSXin LI return (fat->fd);
5369708ba9fSXin LI }
5379708ba9fSXin LI
5389708ba9fSXin LI int
fat_get_fd(struct fat_descriptor * fat)5399708ba9fSXin LI fat_get_fd(struct fat_descriptor * fat)
5409708ba9fSXin LI {
5419708ba9fSXin LI return (fd_of_(fat));
5429708ba9fSXin LI }
5439708ba9fSXin LI
5449708ba9fSXin LI /*
5459708ba9fSXin LI * Whether a cl is in valid data range.
5469708ba9fSXin LI */
5479708ba9fSXin LI bool
fat_is_valid_cl(struct fat_descriptor * fat,cl_t cl)5489708ba9fSXin LI fat_is_valid_cl(struct fat_descriptor *fat, cl_t cl)
5499708ba9fSXin LI {
5509708ba9fSXin LI
5519708ba9fSXin LI return (valid_cl(fat, cl));
5529708ba9fSXin LI }
5539708ba9fSXin LI
5549708ba9fSXin LI static inline bool
valid_cl(struct fat_descriptor * fat,cl_t cl)5559708ba9fSXin LI valid_cl(struct fat_descriptor *fat, cl_t cl)
5569708ba9fSXin LI {
5579708ba9fSXin LI const struct bootblock *boot = boot_of_(fat);
5589708ba9fSXin LI
5599708ba9fSXin LI return (cl >= CLUST_FIRST && cl < boot->NumClusters);
5609708ba9fSXin LI }
5619708ba9fSXin LI
5629708ba9fSXin LI /*
5638e9c564aSBruce Evans * The first 2 FAT entries contain pseudo-cluster numbers with the following
5648e9c564aSBruce Evans * layout:
5658e9c564aSBruce Evans *
5668e9c564aSBruce Evans * 31...... ........ ........ .......0
5678e9c564aSBruce Evans * rrrr1111 11111111 11111111 mmmmmmmm FAT32 entry 0
5688e9c564aSBruce Evans * rrrrsh11 11111111 11111111 11111xxx FAT32 entry 1
5698e9c564aSBruce Evans *
5708e9c564aSBruce Evans * 11111111 mmmmmmmm FAT16 entry 0
5718e9c564aSBruce Evans * sh111111 11111xxx FAT16 entry 1
5728e9c564aSBruce Evans *
5738e9c564aSBruce Evans * r = reserved
5748e9c564aSBruce Evans * m = BPB media ID byte
5758e9c564aSBruce Evans * s = clean flag (1 = dismounted; 0 = still mounted)
5768e9c564aSBruce Evans * h = hard error flag (1 = ok; 0 = I/O error)
5778e9c564aSBruce Evans * x = any value ok
5788e9c564aSBruce Evans */
579a270f31eSBruce Evans int
checkdirty(int fs,struct bootblock * boot)580a270f31eSBruce Evans checkdirty(int fs, struct bootblock *boot)
581a270f31eSBruce Evans {
582a270f31eSBruce Evans off_t off;
583a270f31eSBruce Evans u_char *buffer;
584a270f31eSBruce Evans int ret = 0;
5856cf357bcSUlrich Spörlein size_t len;
586a270f31eSBruce Evans
587910da6c6SBruce Evans if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
588a270f31eSBruce Evans return 0;
589a270f31eSBruce Evans
59075fb5353SKonstantin Belousov off = boot->bpbResSectors;
59175fb5353SKonstantin Belousov off *= boot->bpbBytesPerSec;
592a270f31eSBruce Evans
5936cf357bcSUlrich Spörlein buffer = malloc(len = boot->bpbBytesPerSec);
594a270f31eSBruce Evans if (buffer == NULL) {
5956cf357bcSUlrich Spörlein perr("No space for FAT sectors (%zu)", len);
596a270f31eSBruce Evans return 1;
597a270f31eSBruce Evans }
598a270f31eSBruce Evans
599a270f31eSBruce Evans if (lseek(fs, off, SEEK_SET) != off) {
6006cf357bcSUlrich Spörlein perr("Unable to read FAT");
601a270f31eSBruce Evans goto err;
602a270f31eSBruce Evans }
603a270f31eSBruce Evans
60448790afaSBrian Somers if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) !=
60548790afaSBrian Somers boot->bpbBytesPerSec) {
6066cf357bcSUlrich Spörlein perr("Unable to read FAT");
607a270f31eSBruce Evans goto err;
608a270f31eSBruce Evans }
609a270f31eSBruce Evans
610910da6c6SBruce Evans /*
611910da6c6SBruce Evans * If we don't understand the FAT, then the file system must be
612910da6c6SBruce Evans * assumed to be unclean.
613910da6c6SBruce Evans */
61475fb5353SKonstantin Belousov if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff)
615910da6c6SBruce Evans goto err;
616910da6c6SBruce Evans if (boot->ClustMask == CLUST16_MASK) {
617910da6c6SBruce Evans if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f)
618910da6c6SBruce Evans goto err;
619910da6c6SBruce Evans } else {
620910da6c6SBruce Evans if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f
621910da6c6SBruce Evans || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff
622910da6c6SBruce Evans || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03)
623910da6c6SBruce Evans goto err;
624910da6c6SBruce Evans }
625910da6c6SBruce Evans
626910da6c6SBruce Evans /*
627910da6c6SBruce Evans * Now check the actual clean flag (and the no-error flag).
628910da6c6SBruce Evans */
629910da6c6SBruce Evans if (boot->ClustMask == CLUST16_MASK) {
630910da6c6SBruce Evans if ((buffer[3] & 0xc0) == 0xc0)
631a270f31eSBruce Evans ret = 1;
632910da6c6SBruce Evans } else {
633910da6c6SBruce Evans if ((buffer[7] & 0x0c) == 0x0c)
634910da6c6SBruce Evans ret = 1;
635910da6c6SBruce Evans }
636a270f31eSBruce Evans
637a270f31eSBruce Evans err:
638a270f31eSBruce Evans free(buffer);
639a270f31eSBruce Evans return ret;
640a270f31eSBruce Evans }
641a270f31eSBruce Evans
642*401475f5SXin LI int
cleardirty(struct fat_descriptor * fat)643*401475f5SXin LI cleardirty(struct fat_descriptor *fat)
644*401475f5SXin LI {
645*401475f5SXin LI int fd, ret = FSERROR;
646*401475f5SXin LI struct bootblock *boot;
647*401475f5SXin LI u_char *buffer;
648*401475f5SXin LI size_t len;
649*401475f5SXin LI off_t off;
650*401475f5SXin LI
651*401475f5SXin LI boot = boot_of_(fat);
652*401475f5SXin LI fd = fd_of_(fat);
653*401475f5SXin LI
654*401475f5SXin LI if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
655*401475f5SXin LI return 0;
656*401475f5SXin LI
657*401475f5SXin LI off = boot->bpbResSectors;
658*401475f5SXin LI off *= boot->bpbBytesPerSec;
659*401475f5SXin LI
660*401475f5SXin LI buffer = malloc(len = boot->bpbBytesPerSec);
661*401475f5SXin LI if (buffer == NULL) {
662*401475f5SXin LI perr("No memory for FAT sectors (%zu)", len);
663*401475f5SXin LI return 1;
664*401475f5SXin LI }
665*401475f5SXin LI
666*401475f5SXin LI if ((size_t)pread(fd, buffer, len, off) != len) {
667*401475f5SXin LI perr("Unable to read FAT");
668*401475f5SXin LI goto err;
669*401475f5SXin LI }
670*401475f5SXin LI
671*401475f5SXin LI if (boot->ClustMask == CLUST16_MASK) {
672*401475f5SXin LI buffer[3] |= 0x80;
673*401475f5SXin LI } else {
674*401475f5SXin LI buffer[7] |= 0x08;
675*401475f5SXin LI }
676*401475f5SXin LI
677*401475f5SXin LI if ((size_t)pwrite(fd, buffer, len, off) != len) {
678*401475f5SXin LI perr("Unable to write FAT");
679*401475f5SXin LI goto err;
680*401475f5SXin LI }
681*401475f5SXin LI
682*401475f5SXin LI ret = FSOK;
683*401475f5SXin LI
684*401475f5SXin LI err:
685*401475f5SXin LI free(buffer);
686*401475f5SXin LI return ret;
687*401475f5SXin LI }
688*401475f5SXin LI
6890121b42aSDavid E. O'Brien /*
6900121b42aSDavid E. O'Brien * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
6910121b42aSDavid E. O'Brien */
6920121b42aSDavid E. O'Brien static int
_readfat(struct fat_descriptor * fat)6939708ba9fSXin LI _readfat(struct fat_descriptor *fat)
6940121b42aSDavid E. O'Brien {
6959708ba9fSXin LI int fd;
6969708ba9fSXin LI size_t i;
6970121b42aSDavid E. O'Brien off_t off;
6989708ba9fSXin LI size_t readsize;
6999708ba9fSXin LI struct bootblock *boot;
7009708ba9fSXin LI struct fat32_cache_entry *entry;
7010121b42aSDavid E. O'Brien
7029708ba9fSXin LI boot = boot_of_(fat);
7039708ba9fSXin LI fd = fd_of_(fat);
7049708ba9fSXin LI fat->fatsize = boot->FATsecs * boot->bpbBytesPerSec;
7059708ba9fSXin LI
7069708ba9fSXin LI off = boot->bpbResSectors;
7079708ba9fSXin LI off *= boot->bpbBytesPerSec;
7089708ba9fSXin LI
7099708ba9fSXin LI fat->is_mmapped = false;
7109708ba9fSXin LI fat->use_cache = false;
7119708ba9fSXin LI
7129708ba9fSXin LI /* Attempt to mmap() first */
7139708ba9fSXin LI if (allow_mmap) {
7149708ba9fSXin LI fat->fatbuf = mmap(NULL, fat->fatsize,
7159708ba9fSXin LI PROT_READ | (rdonly ? 0 : PROT_WRITE),
7169708ba9fSXin LI MAP_SHARED, fd_of_(fat), off);
7179708ba9fSXin LI if (fat->fatbuf != MAP_FAILED) {
7189708ba9fSXin LI fat->is_mmapped = true;
7199708ba9fSXin LI return 1;
7209708ba9fSXin LI }
7219708ba9fSXin LI }
7229708ba9fSXin LI
7239708ba9fSXin LI /*
7249708ba9fSXin LI * Unfortunately, we were unable to mmap().
7259708ba9fSXin LI *
7269708ba9fSXin LI * Only use the cache manager when it's necessary, that is,
7279708ba9fSXin LI * when the FAT is sufficiently large; in that case, only
7289708ba9fSXin LI * read in the first 4 MiB of FAT into memory, and split the
7299708ba9fSXin LI * buffer into chunks and insert to the LRU queue to populate
7309708ba9fSXin LI * the cache with data.
7319708ba9fSXin LI */
7329708ba9fSXin LI if (boot->ClustMask == CLUST32_MASK &&
7339708ba9fSXin LI fat->fatsize >= fat32_cache_size) {
7349708ba9fSXin LI readsize = fat32_cache_size;
7359708ba9fSXin LI fat->use_cache = true;
7369708ba9fSXin LI
7379708ba9fSXin LI fat->fat32_offset = boot->bpbResSectors * boot->bpbBytesPerSec;
7389708ba9fSXin LI fat->fat32_lastaddr = fat->fatsize & ~(fat32_cache_chunk_size);
7399708ba9fSXin LI } else {
7409708ba9fSXin LI readsize = fat->fatsize;
7419708ba9fSXin LI }
7429708ba9fSXin LI fat->fatbuf = malloc(readsize);
7439708ba9fSXin LI if (fat->fatbuf == NULL) {
7449708ba9fSXin LI perr("No space for FAT (%zu)", readsize);
7450121b42aSDavid E. O'Brien return 0;
7460121b42aSDavid E. O'Brien }
7470121b42aSDavid E. O'Brien
7489708ba9fSXin LI if (lseek(fd, off, SEEK_SET) != off) {
7499708ba9fSXin LI perr("Unable to read FAT");
7509708ba9fSXin LI goto err;
7519708ba9fSXin LI }
7529708ba9fSXin LI if ((size_t)read(fd, fat->fatbuf, readsize) != readsize) {
7536cf357bcSUlrich Spörlein perr("Unable to read FAT");
7540121b42aSDavid E. O'Brien goto err;
7550121b42aSDavid E. O'Brien }
7560121b42aSDavid E. O'Brien
7579708ba9fSXin LI /*
7589708ba9fSXin LI * When cache is used, split the buffer into chunks, and
7599708ba9fSXin LI * connect the buffer into the cache.
7609708ba9fSXin LI */
7619708ba9fSXin LI if (fat->use_cache) {
7629708ba9fSXin LI TAILQ_INIT(&fat->fat32_cache_head);
7639708ba9fSXin LI entry = calloc(fat32_cache_entries, sizeof(*entry));
7649708ba9fSXin LI if (entry == NULL) {
7659708ba9fSXin LI perr("No space for FAT cache (%zu of %zu)",
7669708ba9fSXin LI fat32_cache_entries, sizeof(entry));
7670121b42aSDavid E. O'Brien goto err;
7680121b42aSDavid E. O'Brien }
7699708ba9fSXin LI for (i = 0; i < fat32_cache_entries; i++) {
7709708ba9fSXin LI entry[i].addr = fat32_cache_chunk_size * i;
7719708ba9fSXin LI entry[i].chunk = &fat->fatbuf[entry[i].addr];
7729708ba9fSXin LI TAILQ_INSERT_TAIL(&fat->fat32_cache_head,
7739708ba9fSXin LI &entry[i], entries);
7749708ba9fSXin LI }
7759708ba9fSXin LI fat->fat32_cache_allentries = entry;
7769708ba9fSXin LI }
7770121b42aSDavid E. O'Brien
7780121b42aSDavid E. O'Brien return 1;
7790121b42aSDavid E. O'Brien
7800121b42aSDavid E. O'Brien err:
7819708ba9fSXin LI free(fat->fatbuf);
7829708ba9fSXin LI fat->fatbuf = NULL;
7830121b42aSDavid E. O'Brien return 0;
7840121b42aSDavid E. O'Brien }
7850121b42aSDavid E. O'Brien
7869708ba9fSXin LI static void
releasefat(struct fat_descriptor * fat)7879708ba9fSXin LI releasefat(struct fat_descriptor *fat)
7889708ba9fSXin LI {
7899708ba9fSXin LI if (fat->is_mmapped) {
7909708ba9fSXin LI munmap(fat->fatbuf, fat->fatsize);
7919708ba9fSXin LI } else {
7929708ba9fSXin LI if (fat->use_cache) {
7939708ba9fSXin LI free(fat->fat32_cache_allentries);
7949708ba9fSXin LI fat->fat32_cache_allentries = NULL;
7959708ba9fSXin LI }
7969708ba9fSXin LI free(fat->fatbuf);
7979708ba9fSXin LI }
7989708ba9fSXin LI fat->fatbuf = NULL;
7999708ba9fSXin LI bitmap_dtor(&fat->headbitmap);
8009708ba9fSXin LI }
8019708ba9fSXin LI
8020121b42aSDavid E. O'Brien /*
8039708ba9fSXin LI * Read or map a FAT and populate head bitmap
8040121b42aSDavid E. O'Brien */
8050121b42aSDavid E. O'Brien int
readfat(int fs,struct bootblock * boot,struct fat_descriptor ** fp)8069708ba9fSXin LI readfat(int fs, struct bootblock *boot, struct fat_descriptor **fp)
8070121b42aSDavid E. O'Brien {
8089708ba9fSXin LI struct fat_descriptor *fat;
8090121b42aSDavid E. O'Brien u_char *buffer, *p;
8109708ba9fSXin LI cl_t cl, nextcl;
8110121b42aSDavid E. O'Brien int ret = FSOK;
8120121b42aSDavid E. O'Brien
8130121b42aSDavid E. O'Brien boot->NumFree = boot->NumBad = 0;
8140121b42aSDavid E. O'Brien
8159708ba9fSXin LI fat = calloc(1, sizeof(struct fat_descriptor));
8160121b42aSDavid E. O'Brien if (fat == NULL) {
8179708ba9fSXin LI perr("No space for FAT descriptor");
8189708ba9fSXin LI return FSFATAL;
8199708ba9fSXin LI }
8209708ba9fSXin LI
8219708ba9fSXin LI fat->fd = fs;
8229708ba9fSXin LI fat->boot = boot;
8239708ba9fSXin LI
8249708ba9fSXin LI if (!_readfat(fat)) {
8259708ba9fSXin LI free(fat);
8269708ba9fSXin LI return FSFATAL;
8279708ba9fSXin LI }
8289708ba9fSXin LI buffer = fat->fatbuf;
8299708ba9fSXin LI
8309708ba9fSXin LI /* Populate accessors */
8319708ba9fSXin LI switch(boot->ClustMask) {
8329708ba9fSXin LI case CLUST12_MASK:
8339708ba9fSXin LI fat->get = fat_get_fat12_next;
8349708ba9fSXin LI fat->set = fat_set_fat12_next;
8359708ba9fSXin LI break;
8369708ba9fSXin LI case CLUST16_MASK:
8379708ba9fSXin LI fat->get = fat_get_fat16_next;
8389708ba9fSXin LI fat->set = fat_set_fat16_next;
8399708ba9fSXin LI break;
8409708ba9fSXin LI case CLUST32_MASK:
8419708ba9fSXin LI if (fat->is_mmapped || !fat->use_cache) {
8429708ba9fSXin LI fat->get = fat_get_fat32_next;
8439708ba9fSXin LI fat->set = fat_set_fat32_next;
8449708ba9fSXin LI } else {
8459708ba9fSXin LI fat->get = fat_get_fat32_cached_next;
8469708ba9fSXin LI fat->set = fat_set_fat32_cached_next;
8479708ba9fSXin LI }
8489708ba9fSXin LI break;
8499708ba9fSXin LI default:
8509708ba9fSXin LI pfatal("Invalid ClustMask: %d", boot->ClustMask);
8519708ba9fSXin LI releasefat(fat);
8529708ba9fSXin LI free(fat);
8539708ba9fSXin LI return FSFATAL;
8549708ba9fSXin LI }
8559708ba9fSXin LI
8569708ba9fSXin LI if (bitmap_ctor(&fat->headbitmap, boot->NumClusters,
8579708ba9fSXin LI true) != FSOK) {
8589708ba9fSXin LI perr("No space for head bitmap for FAT clusters (%zu)",
85955d26365SXin LI (size_t)boot->NumClusters);
8609708ba9fSXin LI releasefat(fat);
8619708ba9fSXin LI free(fat);
8620121b42aSDavid E. O'Brien return FSFATAL;
8630121b42aSDavid E. O'Brien }
8640121b42aSDavid E. O'Brien
86575fb5353SKonstantin Belousov if (buffer[0] != boot->bpbMedia
8660121b42aSDavid E. O'Brien || buffer[1] != 0xff || buffer[2] != 0xff
8670121b42aSDavid E. O'Brien || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
8680121b42aSDavid E. O'Brien || (boot->ClustMask == CLUST32_MASK
8690121b42aSDavid E. O'Brien && ((buffer[3]&0x0f) != 0x0f
8700121b42aSDavid E. O'Brien || buffer[4] != 0xff || buffer[5] != 0xff
8710121b42aSDavid E. O'Brien || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) {
8720121b42aSDavid E. O'Brien
8730121b42aSDavid E. O'Brien /* Windows 95 OSR2 (and possibly any later) changes
8740121b42aSDavid E. O'Brien * the FAT signature to 0xXXffff7f for FAT16 and to
8750121b42aSDavid E. O'Brien * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the
8760121b42aSDavid E. O'Brien * file system is dirty if it doesn't reboot cleanly.
8770121b42aSDavid E. O'Brien * Check this special condition before errorring out.
8780121b42aSDavid E. O'Brien */
87975fb5353SKonstantin Belousov if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff
8800121b42aSDavid E. O'Brien && buffer[2] == 0xff
8810121b42aSDavid E. O'Brien && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
8820121b42aSDavid E. O'Brien || (boot->ClustMask == CLUST32_MASK
8830121b42aSDavid E. O'Brien && buffer[3] == 0x0f && buffer[4] == 0xff
8840121b42aSDavid E. O'Brien && buffer[5] == 0xff && buffer[6] == 0xff
8850121b42aSDavid E. O'Brien && buffer[7] == 0x07)))
8860121b42aSDavid E. O'Brien ret |= FSDIRTY;
8870121b42aSDavid E. O'Brien else {
8880121b42aSDavid E. O'Brien /* just some odd byte sequence in FAT */
8890121b42aSDavid E. O'Brien
8900121b42aSDavid E. O'Brien switch (boot->ClustMask) {
8910121b42aSDavid E. O'Brien case CLUST32_MASK:
8920121b42aSDavid E. O'Brien pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
8930121b42aSDavid E. O'Brien "FAT starts with odd byte sequence",
8940121b42aSDavid E. O'Brien buffer[0], buffer[1], buffer[2], buffer[3],
8950121b42aSDavid E. O'Brien buffer[4], buffer[5], buffer[6], buffer[7]);
8960121b42aSDavid E. O'Brien break;
8970121b42aSDavid E. O'Brien case CLUST16_MASK:
8980121b42aSDavid E. O'Brien pwarn("%s (%02x%02x%02x%02x)\n",
8990121b42aSDavid E. O'Brien "FAT starts with odd byte sequence",
9000121b42aSDavid E. O'Brien buffer[0], buffer[1], buffer[2], buffer[3]);
9010121b42aSDavid E. O'Brien break;
9020121b42aSDavid E. O'Brien default:
9030121b42aSDavid E. O'Brien pwarn("%s (%02x%02x%02x)\n",
9040121b42aSDavid E. O'Brien "FAT starts with odd byte sequence",
9050121b42aSDavid E. O'Brien buffer[0], buffer[1], buffer[2]);
9060121b42aSDavid E. O'Brien break;
9070121b42aSDavid E. O'Brien }
9080121b42aSDavid E. O'Brien
9099708ba9fSXin LI if (ask(1, "Correct")) {
9109708ba9fSXin LI ret |= FSFATMOD;
9119708ba9fSXin LI p = buffer;
9120121b42aSDavid E. O'Brien
9139708ba9fSXin LI *p++ = (u_char)boot->bpbMedia;
9149708ba9fSXin LI *p++ = 0xff;
9159708ba9fSXin LI *p++ = 0xff;
9160121b42aSDavid E. O'Brien switch (boot->ClustMask) {
91773db93b8SXin LI case CLUST16_MASK:
9189708ba9fSXin LI *p++ = 0xff;
9199708ba9fSXin LI break;
9209708ba9fSXin LI case CLUST32_MASK:
9219708ba9fSXin LI *p++ = 0x0f;
9229708ba9fSXin LI *p++ = 0xff;
9239708ba9fSXin LI *p++ = 0xff;
9249708ba9fSXin LI *p++ = 0xff;
9259708ba9fSXin LI *p++ = 0x0f;
9260121b42aSDavid E. O'Brien break;
9270121b42aSDavid E. O'Brien default:
92873db93b8SXin LI break;
92973db93b8SXin LI }
9309708ba9fSXin LI }
9310121b42aSDavid E. O'Brien }
932b06cf1e4SXin LI }
9330121b42aSDavid E. O'Brien
9349708ba9fSXin LI /*
9359708ba9fSXin LI * Traverse the FAT table and populate head map. Initially, we
9369708ba9fSXin LI * consider all clusters as possible head cluster (beginning of
9379708ba9fSXin LI * a file or directory), and traverse the whole allocation table
9389708ba9fSXin LI * by marking every non-head nodes as such (detailed below) and
9399708ba9fSXin LI * fix obvious issues while we walk.
9409708ba9fSXin LI *
9419708ba9fSXin LI * For each "next" cluster, the possible values are:
9429708ba9fSXin LI *
9439708ba9fSXin LI * a) CLUST_FREE or CLUST_BAD. The *current* cluster can't be a
9449708ba9fSXin LI * head node.
9459708ba9fSXin LI * b) An out-of-range value. The only fix would be to truncate at
9469708ba9fSXin LI * the cluster.
9479708ba9fSXin LI * c) A valid cluster. It means that cluster (nextcl) is not a
9489708ba9fSXin LI * head cluster. Note that during the scan, every cluster is
9499708ba9fSXin LI * expected to be seen for at most once, and when we saw them
9509708ba9fSXin LI * twice, it means a cross-linked chain which should be
9519708ba9fSXin LI * truncated at the current cluster.
9529708ba9fSXin LI *
9539708ba9fSXin LI * After scan, the remaining set bits indicates all possible
9549708ba9fSXin LI * head nodes, because they were never claimed by any other
9559708ba9fSXin LI * node as the next node, but we do not know if these chains
9569708ba9fSXin LI * would end with a valid EOF marker. We will check that in
9579708ba9fSXin LI * checkchain() at a later time when checking directories,
9589708ba9fSXin LI * where these head nodes would be marked as non-head.
9599708ba9fSXin LI *
9609708ba9fSXin LI * In the final pass, all head nodes should be cleared, and if
9619708ba9fSXin LI * there is still head nodes, these would be leaders of lost
9629708ba9fSXin LI * chain.
9639708ba9fSXin LI */
9649708ba9fSXin LI for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
9659708ba9fSXin LI nextcl = fat_get_cl_next(fat, cl);
9669708ba9fSXin LI
9679708ba9fSXin LI /* Check if the next cluster number is valid */
9689708ba9fSXin LI if (nextcl == CLUST_FREE) {
9699708ba9fSXin LI /* Save a hint for next free cluster */
9709708ba9fSXin LI if (boot->FSNext == 0) {
9719708ba9fSXin LI boot->FSNext = cl;
9729708ba9fSXin LI }
9739708ba9fSXin LI if (fat_is_cl_head(fat, cl)) {
9749708ba9fSXin LI fat_clear_cl_head(fat, cl);
9759708ba9fSXin LI }
9769708ba9fSXin LI boot->NumFree++;
9779708ba9fSXin LI } else if (nextcl == CLUST_BAD) {
9789708ba9fSXin LI if (fat_is_cl_head(fat, cl)) {
9799708ba9fSXin LI fat_clear_cl_head(fat, cl);
9809708ba9fSXin LI }
9819708ba9fSXin LI boot->NumBad++;
982d14a599dSXin LI } else if (!valid_cl(fat, nextcl) && nextcl < CLUST_RSRVD) {
983d14a599dSXin LI pwarn("Cluster %u continues with out of range "
9849708ba9fSXin LI "cluster number %u\n",
985d14a599dSXin LI cl,
9869708ba9fSXin LI nextcl & boot->ClustMask);
9879708ba9fSXin LI if (ask(0, "Truncate")) {
9889708ba9fSXin LI ret |= fat_set_cl_next(fat, cl, CLUST_EOF);
9899708ba9fSXin LI ret |= FSFATMOD;
9909708ba9fSXin LI }
991d14a599dSXin LI } else if (valid_cl(fat, nextcl)) {
9929708ba9fSXin LI if (fat_is_cl_head(fat, nextcl)) {
9939708ba9fSXin LI fat_clear_cl_head(fat, nextcl);
9949708ba9fSXin LI } else {
9959708ba9fSXin LI pwarn("Cluster %u crossed another chain at %u\n",
9969708ba9fSXin LI cl, nextcl);
9979708ba9fSXin LI if (ask(0, "Truncate")) {
9989708ba9fSXin LI ret |= fat_set_cl_next(fat, cl, CLUST_EOF);
9999708ba9fSXin LI ret |= FSFATMOD;
10009708ba9fSXin LI }
10019708ba9fSXin LI }
10029708ba9fSXin LI }
10039708ba9fSXin LI
10049708ba9fSXin LI }
10059708ba9fSXin LI
10066069db97SKonstantin Belousov if (ret & FSFATAL) {
10079708ba9fSXin LI releasefat(fat);
10086069db97SKonstantin Belousov free(fat);
10096069db97SKonstantin Belousov *fp = NULL;
10106069db97SKonstantin Belousov } else
10110121b42aSDavid E. O'Brien *fp = fat;
10120121b42aSDavid E. O'Brien return ret;
10130121b42aSDavid E. O'Brien }
10140121b42aSDavid E. O'Brien
10150121b42aSDavid E. O'Brien /*
10160121b42aSDavid E. O'Brien * Get type of reserved cluster
10170121b42aSDavid E. O'Brien */
10183bbc4438SUlrich Spörlein const char *
rsrvdcltype(cl_t cl)1019b70cd7eeSWarner Losh rsrvdcltype(cl_t cl)
10200121b42aSDavid E. O'Brien {
10210121b42aSDavid E. O'Brien if (cl == CLUST_FREE)
10220121b42aSDavid E. O'Brien return "free";
10230121b42aSDavid E. O'Brien if (cl < CLUST_BAD)
10240121b42aSDavid E. O'Brien return "reserved";
10250121b42aSDavid E. O'Brien if (cl > CLUST_BAD)
10260121b42aSDavid E. O'Brien return "as EOF";
10270121b42aSDavid E. O'Brien return "bad";
10280121b42aSDavid E. O'Brien }
10290121b42aSDavid E. O'Brien
10300121b42aSDavid E. O'Brien /*
10319708ba9fSXin LI * Examine a cluster chain for errors and count its size.
1032b06cf1e4SXin LI */
1033b06cf1e4SXin LI int
checkchain(struct fat_descriptor * fat,cl_t head,size_t * chainsize)10349708ba9fSXin LI checkchain(struct fat_descriptor *fat, cl_t head, size_t *chainsize)
1035b06cf1e4SXin LI {
1036d14a599dSXin LI cl_t prev_cl, current_cl, next_cl;
1037d14a599dSXin LI const char *op;
1038b06cf1e4SXin LI
10399708ba9fSXin LI /*
10409708ba9fSXin LI * We expect that the caller to give us a real, unvisited 'head'
10419708ba9fSXin LI * cluster, and it must be a valid cluster. While scanning the
10429708ba9fSXin LI * FAT table, we already excluded all clusters that was claimed
10439708ba9fSXin LI * as a "next" cluster. Assert all the three conditions.
10449708ba9fSXin LI */
10459708ba9fSXin LI assert(valid_cl(fat, head));
10469708ba9fSXin LI assert(fat_is_cl_head(fat, head));
10479708ba9fSXin LI
10489708ba9fSXin LI /*
10499708ba9fSXin LI * Immediately mark the 'head' cluster that we are about to visit.
10509708ba9fSXin LI */
10519708ba9fSXin LI fat_clear_cl_head(fat, head);
10529708ba9fSXin LI
10539708ba9fSXin LI /*
10549708ba9fSXin LI * The allocation of a non-zero sized file or directory is
10559708ba9fSXin LI * represented as a singly linked list, and the tail node
10569708ba9fSXin LI * would be the EOF marker (>=CLUST_EOFS).
10579708ba9fSXin LI *
10589708ba9fSXin LI * With a valid head node at hand, we expect all subsequent
10599708ba9fSXin LI * cluster to be either a not yet seen and valid cluster (we
10609708ba9fSXin LI * would continue counting), or the EOF marker (we conclude
10619708ba9fSXin LI * the scan of this chain).
10629708ba9fSXin LI *
10639708ba9fSXin LI * For all other cases, the chain is invalid, and the only
10649708ba9fSXin LI * viable fix would be to truncate at the current node (mark
10659708ba9fSXin LI * it as EOF) when the next node violates that.
10669708ba9fSXin LI */
10679708ba9fSXin LI *chainsize = 0;
1068d14a599dSXin LI prev_cl = current_cl = head;
10699708ba9fSXin LI for (next_cl = fat_get_cl_next(fat, current_cl);
10709708ba9fSXin LI valid_cl(fat, next_cl);
1071d14a599dSXin LI prev_cl = current_cl, current_cl = next_cl, next_cl = fat_get_cl_next(fat, current_cl))
10729708ba9fSXin LI (*chainsize)++;
10739708ba9fSXin LI
10749708ba9fSXin LI /* A natural end */
10759708ba9fSXin LI if (next_cl >= CLUST_EOFS) {
10769708ba9fSXin LI (*chainsize)++;
10779708ba9fSXin LI return FSOK;
1078b06cf1e4SXin LI }
10799708ba9fSXin LI
1080d14a599dSXin LI /*
1081d14a599dSXin LI * The chain ended with an out-of-range cluster number.
1082d14a599dSXin LI *
1083d14a599dSXin LI * If the current node is e.g. CLUST_FREE, CLUST_BAD, etc.,
1084d14a599dSXin LI * it should not be present in a chain and we has to truncate
1085d14a599dSXin LI * at the previous node.
1086d14a599dSXin LI *
1087d14a599dSXin LI * If the current cluster points to an invalid cluster, the
1088d14a599dSXin LI * current cluster might have useful data and we truncate at
1089d14a599dSXin LI * the current cluster instead.
1090d14a599dSXin LI */
1091d14a599dSXin LI if (next_cl == CLUST_FREE || next_cl >= CLUST_RSRVD) {
1092d14a599dSXin LI pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
1093d14a599dSXin LI head, rsrvdcltype(next_cl));
1094d14a599dSXin LI current_cl = prev_cl;
1095d14a599dSXin LI } else {
1096d14a599dSXin LI pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
1097d14a599dSXin LI head,
10989708ba9fSXin LI next_cl & boot_of_(fat)->ClustMask);
1099d14a599dSXin LI (*chainsize)++;
1100d14a599dSXin LI }
1101d14a599dSXin LI
1102d14a599dSXin LI if (*chainsize > 0) {
1103d14a599dSXin LI op = "Truncate";
1104d14a599dSXin LI next_cl = CLUST_EOF;
1105d14a599dSXin LI } else {
1106d14a599dSXin LI op = "Clear";
1107d14a599dSXin LI next_cl = CLUST_FREE;
1108d14a599dSXin LI }
1109d14a599dSXin LI if (ask(0, "%s", op)) {
1110d14a599dSXin LI return (fat_set_cl_next(fat, current_cl, next_cl) | FSFATMOD);
1111d14a599dSXin LI } else {
1112d14a599dSXin LI return (FSERROR);
1113d14a599dSXin LI }
11149708ba9fSXin LI }
11159708ba9fSXin LI
11169708ba9fSXin LI /*
11179708ba9fSXin LI * Clear cluster chain from head.
11189708ba9fSXin LI */
11199708ba9fSXin LI void
clearchain(struct fat_descriptor * fat,cl_t head)11209708ba9fSXin LI clearchain(struct fat_descriptor *fat, cl_t head)
11219708ba9fSXin LI {
11229708ba9fSXin LI cl_t current_cl, next_cl;
11239708ba9fSXin LI struct bootblock *boot = boot_of_(fat);
11249708ba9fSXin LI
11259708ba9fSXin LI current_cl = head;
11269708ba9fSXin LI
11279708ba9fSXin LI while (valid_cl(fat, current_cl)) {
1128d14a599dSXin LI next_cl = fat_get_cl_next(fat, current_cl);
11299708ba9fSXin LI (void)fat_set_cl_next(fat, current_cl, CLUST_FREE);
11309708ba9fSXin LI boot->NumFree++;
11319708ba9fSXin LI current_cl = next_cl;
11329708ba9fSXin LI }
11339708ba9fSXin LI
11349708ba9fSXin LI }
11359708ba9fSXin LI
11369708ba9fSXin LI /*
11379708ba9fSXin LI * Overwrite the n-th FAT with FAT0
11389708ba9fSXin LI */
11399708ba9fSXin LI static int
copyfat(struct fat_descriptor * fat,int n)11409708ba9fSXin LI copyfat(struct fat_descriptor *fat, int n)
11419708ba9fSXin LI {
11429708ba9fSXin LI size_t rwsize, tailsize, blobs, i;
11439708ba9fSXin LI off_t dst_off, src_off;
11449708ba9fSXin LI struct bootblock *boot;
11459708ba9fSXin LI int ret, fd;
11469708ba9fSXin LI
11479708ba9fSXin LI ret = FSOK;
11489708ba9fSXin LI fd = fd_of_(fat);
11499708ba9fSXin LI boot = boot_of_(fat);
11509708ba9fSXin LI
11519708ba9fSXin LI blobs = howmany(fat->fatsize, fat32_cache_size);
11529708ba9fSXin LI tailsize = fat->fatsize % fat32_cache_size;
11539708ba9fSXin LI if (tailsize == 0) {
11549708ba9fSXin LI tailsize = fat32_cache_size;
11559708ba9fSXin LI }
11569708ba9fSXin LI rwsize = fat32_cache_size;
11579708ba9fSXin LI
11589708ba9fSXin LI src_off = fat->fat32_offset;
11599708ba9fSXin LI dst_off = boot->bpbResSectors + n * boot->FATsecs;
11609708ba9fSXin LI dst_off *= boot->bpbBytesPerSec;
11619708ba9fSXin LI
11629708ba9fSXin LI for (i = 0; i < blobs;
11639708ba9fSXin LI i++, src_off += fat32_cache_size, dst_off += fat32_cache_size) {
11649708ba9fSXin LI if (i == blobs - 1) {
11659708ba9fSXin LI rwsize = tailsize;
11669708ba9fSXin LI }
11679708ba9fSXin LI if ((lseek(fd, src_off, SEEK_SET) != src_off ||
11689708ba9fSXin LI (size_t)read(fd, fat->fatbuf, rwsize) != rwsize) &&
11699708ba9fSXin LI ret == FSOK) {
11709708ba9fSXin LI perr("Unable to read FAT0");
11719708ba9fSXin LI ret = FSFATAL;
11729708ba9fSXin LI continue;
11739708ba9fSXin LI }
11749708ba9fSXin LI if ((lseek(fd, dst_off, SEEK_SET) != dst_off ||
11759708ba9fSXin LI (size_t)write(fd, fat->fatbuf, rwsize) != rwsize) &&
11769708ba9fSXin LI ret == FSOK) {
11779708ba9fSXin LI perr("Unable to write FAT %d", n);
11789708ba9fSXin LI ret = FSERROR;
11799708ba9fSXin LI }
11809708ba9fSXin LI }
11819708ba9fSXin LI return (ret);
11829708ba9fSXin LI }
11839708ba9fSXin LI
11849708ba9fSXin LI /*
11859708ba9fSXin LI * Write out FAT
11869708ba9fSXin LI */
11879708ba9fSXin LI int
writefat(struct fat_descriptor * fat)11889708ba9fSXin LI writefat(struct fat_descriptor *fat)
11899708ba9fSXin LI {
11909708ba9fSXin LI u_int i;
11919708ba9fSXin LI size_t writesz;
11929708ba9fSXin LI off_t dst_base;
11939708ba9fSXin LI int ret = FSOK, fd;
11949708ba9fSXin LI struct bootblock *boot;
11959708ba9fSXin LI struct fat32_cache_entry *entry;
11969708ba9fSXin LI
11979708ba9fSXin LI boot = boot_of_(fat);
11989708ba9fSXin LI fd = fd_of_(fat);
11999708ba9fSXin LI
12009708ba9fSXin LI if (fat->use_cache) {
12019708ba9fSXin LI /*
12029708ba9fSXin LI * Attempt to flush all in-flight cache, and bail out
12039708ba9fSXin LI * if we encountered an error (but only emit error
12049708ba9fSXin LI * message once). Stop proceeding with copyfat()
12059708ba9fSXin LI * if any flush failed.
12069708ba9fSXin LI */
12079708ba9fSXin LI TAILQ_FOREACH(entry, &fat->fat32_cache_head, entries) {
12089708ba9fSXin LI if (fat_flush_fat32_cache_entry(fat, entry) != FSOK) {
12099708ba9fSXin LI if (ret == FSOK) {
12109708ba9fSXin LI perr("Unable to write FAT");
12119708ba9fSXin LI ret = FSFATAL;
12129708ba9fSXin LI }
12139708ba9fSXin LI }
12149708ba9fSXin LI }
12159708ba9fSXin LI if (ret != FSOK)
12169708ba9fSXin LI return (ret);
12179708ba9fSXin LI
12189708ba9fSXin LI /* Update backup copies of FAT, error is not fatal */
12199708ba9fSXin LI for (i = 1; i < boot->bpbFATs; i++) {
12209708ba9fSXin LI if (copyfat(fat, i) != FSOK)
12219708ba9fSXin LI ret = FSERROR;
12220121b42aSDavid E. O'Brien }
12230121b42aSDavid E. O'Brien } else {
12249708ba9fSXin LI writesz = fat->fatsize;
12250121b42aSDavid E. O'Brien
12269708ba9fSXin LI for (i = fat->is_mmapped ? 1 : 0; i < boot->bpbFATs; i++) {
12279708ba9fSXin LI dst_base = boot->bpbResSectors + i * boot->FATsecs;
12289708ba9fSXin LI dst_base *= boot->bpbBytesPerSec;
12299708ba9fSXin LI if ((lseek(fd, dst_base, SEEK_SET) != dst_base ||
12309708ba9fSXin LI (size_t)write(fd, fat->fatbuf, writesz) != writesz) &&
12319708ba9fSXin LI ret == FSOK) {
12329708ba9fSXin LI perr("Unable to write FAT %d", i);
12339708ba9fSXin LI ret = ((i == 0) ? FSFATAL : FSERROR);
12349708ba9fSXin LI }
12359708ba9fSXin LI }
12360121b42aSDavid E. O'Brien }
12370121b42aSDavid E. O'Brien
12380121b42aSDavid E. O'Brien return ret;
12390121b42aSDavid E. O'Brien }
12400121b42aSDavid E. O'Brien
12410121b42aSDavid E. O'Brien /*
12420121b42aSDavid E. O'Brien * Check a complete in-memory FAT for lost cluster chains
12430121b42aSDavid E. O'Brien */
12440121b42aSDavid E. O'Brien int
checklost(struct fat_descriptor * fat)12459708ba9fSXin LI checklost(struct fat_descriptor *fat)
12460121b42aSDavid E. O'Brien {
12470121b42aSDavid E. O'Brien cl_t head;
12480121b42aSDavid E. O'Brien int mod = FSOK;
12499708ba9fSXin LI int dosfs, ret;
12509708ba9fSXin LI size_t chains, chainlength;
12519708ba9fSXin LI struct bootblock *boot;
12520121b42aSDavid E. O'Brien
12539708ba9fSXin LI dosfs = fd_of_(fat);
12549708ba9fSXin LI boot = boot_of_(fat);
12559708ba9fSXin LI
12569708ba9fSXin LI /*
12579708ba9fSXin LI * At this point, we have already traversed all directories.
12589708ba9fSXin LI * All remaining chain heads in the bitmap are heads of lost
12599708ba9fSXin LI * chains.
12609708ba9fSXin LI */
12619708ba9fSXin LI chains = fat_get_head_count(fat);
12629708ba9fSXin LI for (head = CLUST_FIRST;
12639708ba9fSXin LI chains > 0 && head < boot->NumClusters;
12649708ba9fSXin LI ) {
12659708ba9fSXin LI /*
12669708ba9fSXin LI * We expect the bitmap to be very sparse, so skip if
12679708ba9fSXin LI * the range is full of 0's
12689708ba9fSXin LI */
12699708ba9fSXin LI if (head % LONG_BIT == 0 &&
12709708ba9fSXin LI !fat_is_cl_head_in_range(fat, head)) {
12719708ba9fSXin LI head += LONG_BIT;
12720121b42aSDavid E. O'Brien continue;
12739708ba9fSXin LI }
12749708ba9fSXin LI if (fat_is_cl_head(fat, head)) {
12759708ba9fSXin LI ret = checkchain(fat, head, &chainlength);
1276d14a599dSXin LI if (ret != FSERROR && chainlength > 0) {
12779708ba9fSXin LI pwarn("Lost cluster chain at cluster %u\n"
12789708ba9fSXin LI "%zd Cluster(s) lost\n",
12799708ba9fSXin LI head, chainlength);
12809708ba9fSXin LI mod |= ret = reconnect(fat, head,
12819708ba9fSXin LI chainlength);
12829708ba9fSXin LI }
12830121b42aSDavid E. O'Brien if (mod & FSFATAL)
12840121b42aSDavid E. O'Brien break;
12850121b42aSDavid E. O'Brien if (ret == FSERROR && ask(0, "Clear")) {
12869708ba9fSXin LI clearchain(fat, head);
12870121b42aSDavid E. O'Brien mod |= FSFATMOD;
12880121b42aSDavid E. O'Brien }
12899708ba9fSXin LI chains--;
12900121b42aSDavid E. O'Brien }
12919708ba9fSXin LI head++;
12929708ba9fSXin LI }
12939708ba9fSXin LI
12940121b42aSDavid E. O'Brien finishlf();
12950121b42aSDavid E. O'Brien
129675fb5353SKonstantin Belousov if (boot->bpbFSInfo) {
12970121b42aSDavid E. O'Brien ret = 0;
129821f0d838SPedro F. Giffuni if (boot->FSFree != 0xffffffffU &&
129921f0d838SPedro F. Giffuni boot->FSFree != boot->NumFree) {
130021f0d838SPedro F. Giffuni pwarn("Free space in FSInfo block (%u) not correct (%u)\n",
13010121b42aSDavid E. O'Brien boot->FSFree, boot->NumFree);
1302dc401d49SYaroslav Tykhiy if (ask(1, "Fix")) {
13030121b42aSDavid E. O'Brien boot->FSFree = boot->NumFree;
13040121b42aSDavid E. O'Brien ret = 1;
13050121b42aSDavid E. O'Brien }
13060121b42aSDavid E. O'Brien }
13072bf0ee64SXin LI if (boot->FSNext != 0xffffffffU &&
13082bf0ee64SXin LI (boot->FSNext >= boot->NumClusters ||
13099708ba9fSXin LI (boot->NumFree && fat_get_cl_next(fat, boot->FSNext) != CLUST_FREE))) {
13102bf0ee64SXin LI pwarn("Next free cluster in FSInfo block (%u) %s\n",
13112bf0ee64SXin LI boot->FSNext,
13122bf0ee64SXin LI (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free");
13139708ba9fSXin LI if (ask(1, "Fix"))
13142bf0ee64SXin LI for (head = CLUST_FIRST; head < boot->NumClusters; head++)
13159708ba9fSXin LI if (fat_get_cl_next(fat, head) == CLUST_FREE) {
13162bf0ee64SXin LI boot->FSNext = head;
13172bf0ee64SXin LI ret = 1;
13182bf0ee64SXin LI break;
13192bf0ee64SXin LI }
13202bf0ee64SXin LI }
13210121b42aSDavid E. O'Brien if (ret)
13220121b42aSDavid E. O'Brien mod |= writefsinfo(dosfs, boot);
13230121b42aSDavid E. O'Brien }
13240121b42aSDavid E. O'Brien
13250121b42aSDavid E. O'Brien return mod;
13260121b42aSDavid E. O'Brien }
1327