xref: /freebsd-src/sbin/fsck_msdosfs/fat.c (revision eba230afba4932f02a1ca44efc797cf7499a5cb0)
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