1*d5c9a868SElliott Hughes /* Copyright 1996-2006,2008,2009 Alain Knaff.
2*d5c9a868SElliott Hughes * This file is part of mtools.
3*d5c9a868SElliott Hughes *
4*d5c9a868SElliott Hughes * Mtools is free software: you can redistribute it and/or modify
5*d5c9a868SElliott Hughes * it under the terms of the GNU General Public License as published by
6*d5c9a868SElliott Hughes * the Free Software Foundation, either version 3 of the License, or
7*d5c9a868SElliott Hughes * (at your option) any later version.
8*d5c9a868SElliott Hughes *
9*d5c9a868SElliott Hughes * Mtools is distributed in the hope that it will be useful,
10*d5c9a868SElliott Hughes * but WITHOUT ANY WARRANTY; without even the implied warranty of
11*d5c9a868SElliott Hughes * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12*d5c9a868SElliott Hughes * GNU General Public License for more details.
13*d5c9a868SElliott Hughes *
14*d5c9a868SElliott Hughes * You should have received a copy of the GNU General Public License
15*d5c9a868SElliott Hughes * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
16*d5c9a868SElliott Hughes */
17*d5c9a868SElliott Hughes
18*d5c9a868SElliott Hughes
19*d5c9a868SElliott Hughes #include "sysincludes.h"
20*d5c9a868SElliott Hughes #include "msdos.h"
21*d5c9a868SElliott Hughes #include "stream.h"
22*d5c9a868SElliott Hughes #include "mtools.h"
23*d5c9a868SElliott Hughes #include "fsP.h"
24*d5c9a868SElliott Hughes #include "file_name.h"
25*d5c9a868SElliott Hughes
26*d5c9a868SElliott Hughes #ifdef HAVE_LONG_LONG
27*d5c9a868SElliott Hughes typedef long long fatBitMask;
28*d5c9a868SElliott Hughes #else
29*d5c9a868SElliott Hughes typedef long fatBitMask;
30*d5c9a868SElliott Hughes #endif
31*d5c9a868SElliott Hughes
32*d5c9a868SElliott Hughes typedef struct FatMap_t {
33*d5c9a868SElliott Hughes unsigned char *data;
34*d5c9a868SElliott Hughes fatBitMask dirty;
35*d5c9a868SElliott Hughes fatBitMask valid;
36*d5c9a868SElliott Hughes } FatMap_t;
37*d5c9a868SElliott Hughes
38*d5c9a868SElliott Hughes #define SECT_PER_ENTRY (sizeof(fatBitMask)*8)
39*d5c9a868SElliott Hughes #define ONE ((fatBitMask) 1)
40*d5c9a868SElliott Hughes
readSector(Fs_t * This,char * buf,unsigned int off,size_t size)41*d5c9a868SElliott Hughes static __inline__ ssize_t readSector(Fs_t *This, char *buf, unsigned int off,
42*d5c9a868SElliott Hughes size_t size)
43*d5c9a868SElliott Hughes {
44*d5c9a868SElliott Hughes return PREADS(This->head.Next, buf, sectorsToBytes(This, off),
45*d5c9a868SElliott Hughes size << This->sectorShift);
46*d5c9a868SElliott Hughes }
47*d5c9a868SElliott Hughes
48*d5c9a868SElliott Hughes
forceReadSector(Fs_t * This,char * buf,unsigned int off,size_t size)49*d5c9a868SElliott Hughes static __inline__ ssize_t forceReadSector(Fs_t *This, char *buf,
50*d5c9a868SElliott Hughes unsigned int off, size_t size)
51*d5c9a868SElliott Hughes {
52*d5c9a868SElliott Hughes return force_pread(This->head.Next, buf, sectorsToBytes(This, off),
53*d5c9a868SElliott Hughes size << This->sectorShift);
54*d5c9a868SElliott Hughes }
55*d5c9a868SElliott Hughes
56*d5c9a868SElliott Hughes
forceWriteSector(Fs_t * This,char * buf,unsigned int off,size_t size)57*d5c9a868SElliott Hughes static __inline__ ssize_t forceWriteSector(Fs_t *This, char *buf, unsigned int off,
58*d5c9a868SElliott Hughes size_t size)
59*d5c9a868SElliott Hughes {
60*d5c9a868SElliott Hughes return force_pwrite(This->head.Next, buf, sectorsToBytes(This, off),
61*d5c9a868SElliott Hughes size << This->sectorShift);
62*d5c9a868SElliott Hughes }
63*d5c9a868SElliott Hughes
64*d5c9a868SElliott Hughes
GetFatMap(Fs_t * Stream)65*d5c9a868SElliott Hughes static FatMap_t *GetFatMap(Fs_t *Stream)
66*d5c9a868SElliott Hughes {
67*d5c9a868SElliott Hughes size_t nr_entries;
68*d5c9a868SElliott Hughes size_t i;
69*d5c9a868SElliott Hughes FatMap_t *map;
70*d5c9a868SElliott Hughes
71*d5c9a868SElliott Hughes Stream->fat_error = 0;
72*d5c9a868SElliott Hughes nr_entries = (Stream->fat_len + SECT_PER_ENTRY - 1) / SECT_PER_ENTRY;
73*d5c9a868SElliott Hughes map = NewArray(nr_entries, FatMap_t);
74*d5c9a868SElliott Hughes if(!map)
75*d5c9a868SElliott Hughes return 0;
76*d5c9a868SElliott Hughes
77*d5c9a868SElliott Hughes for(i=0; i< nr_entries; i++) {
78*d5c9a868SElliott Hughes map[i].data = 0;
79*d5c9a868SElliott Hughes map[i].valid = 0;
80*d5c9a868SElliott Hughes map[i].dirty = 0;
81*d5c9a868SElliott Hughes }
82*d5c9a868SElliott Hughes
83*d5c9a868SElliott Hughes return map;
84*d5c9a868SElliott Hughes }
85*d5c9a868SElliott Hughes
locate(Fs_t * Stream,uint32_t offset,uint32_t * slot,uint32_t * bit)86*d5c9a868SElliott Hughes static __inline__ int locate(Fs_t *Stream, uint32_t offset,
87*d5c9a868SElliott Hughes uint32_t *slot, uint32_t *bit)
88*d5c9a868SElliott Hughes {
89*d5c9a868SElliott Hughes if(offset >= Stream->fat_len)
90*d5c9a868SElliott Hughes return -1;
91*d5c9a868SElliott Hughes *slot = offset / SECT_PER_ENTRY;
92*d5c9a868SElliott Hughes *bit = offset % SECT_PER_ENTRY;
93*d5c9a868SElliott Hughes return 0;
94*d5c9a868SElliott Hughes }
95*d5c9a868SElliott Hughes
fatReadSector(Fs_t * This,unsigned int sector,unsigned int slot,unsigned int bit,unsigned int dupe,fatBitMask bitmap)96*d5c9a868SElliott Hughes static __inline__ ssize_t fatReadSector(Fs_t *This,
97*d5c9a868SElliott Hughes unsigned int sector,
98*d5c9a868SElliott Hughes unsigned int slot,
99*d5c9a868SElliott Hughes unsigned int bit, unsigned int dupe,
100*d5c9a868SElliott Hughes fatBitMask bitmap)
101*d5c9a868SElliott Hughes {
102*d5c9a868SElliott Hughes unsigned int fat_start;
103*d5c9a868SElliott Hughes ssize_t ret;
104*d5c9a868SElliott Hughes unsigned int nr_sectors;
105*d5c9a868SElliott Hughes
106*d5c9a868SElliott Hughes dupe = (dupe + This->primaryFat) % This->num_fat;
107*d5c9a868SElliott Hughes fat_start = This->fat_start + This->fat_len * dupe;
108*d5c9a868SElliott Hughes
109*d5c9a868SElliott Hughes if(bitmap == 0) {
110*d5c9a868SElliott Hughes nr_sectors = SECT_PER_ENTRY - bit%SECT_PER_ENTRY;
111*d5c9a868SElliott Hughes } else {
112*d5c9a868SElliott Hughes nr_sectors = 1;
113*d5c9a868SElliott Hughes }
114*d5c9a868SElliott Hughes
115*d5c9a868SElliott Hughes /* first, read as much as the buffer can give us */
116*d5c9a868SElliott Hughes ret = readSector(This,
117*d5c9a868SElliott Hughes (char *)(This->FatMap[slot].data+(bit<<This->sectorShift)),
118*d5c9a868SElliott Hughes fat_start+sector,
119*d5c9a868SElliott Hughes nr_sectors);
120*d5c9a868SElliott Hughes if(ret < 0)
121*d5c9a868SElliott Hughes return 0;
122*d5c9a868SElliott Hughes
123*d5c9a868SElliott Hughes if((size_t) ret < This->sector_size) {
124*d5c9a868SElliott Hughes /* if we got less than one sector's worth, insist to get at
125*d5c9a868SElliott Hughes * least one sector */
126*d5c9a868SElliott Hughes ret = forceReadSector(This,
127*d5c9a868SElliott Hughes (char *) (This->FatMap[slot].data +
128*d5c9a868SElliott Hughes (bit << This->sectorShift)),
129*d5c9a868SElliott Hughes fat_start+sector, 1);
130*d5c9a868SElliott Hughes if(ret < (int) This->sector_size)
131*d5c9a868SElliott Hughes return 0;
132*d5c9a868SElliott Hughes return 1;
133*d5c9a868SElliott Hughes }
134*d5c9a868SElliott Hughes
135*d5c9a868SElliott Hughes return ret >> This->sectorShift;
136*d5c9a868SElliott Hughes }
137*d5c9a868SElliott Hughes
138*d5c9a868SElliott Hughes
fatWriteSector(Fs_t * This,unsigned int sector,unsigned int slot,unsigned int bit,unsigned int dupe)139*d5c9a868SElliott Hughes static ssize_t fatWriteSector(Fs_t *This,
140*d5c9a868SElliott Hughes unsigned int sector,
141*d5c9a868SElliott Hughes unsigned int slot,
142*d5c9a868SElliott Hughes unsigned int bit,
143*d5c9a868SElliott Hughes unsigned int dupe)
144*d5c9a868SElliott Hughes {
145*d5c9a868SElliott Hughes unsigned int fat_start;
146*d5c9a868SElliott Hughes
147*d5c9a868SElliott Hughes dupe = (dupe + This->primaryFat) % This->num_fat;
148*d5c9a868SElliott Hughes if(dupe && !This->writeAllFats)
149*d5c9a868SElliott Hughes return This->sector_size;
150*d5c9a868SElliott Hughes
151*d5c9a868SElliott Hughes fat_start = This->fat_start + This->fat_len * dupe;
152*d5c9a868SElliott Hughes
153*d5c9a868SElliott Hughes return forceWriteSector(This,
154*d5c9a868SElliott Hughes (char *)
155*d5c9a868SElliott Hughes (This->FatMap[slot].data + bit * This->sector_size),
156*d5c9a868SElliott Hughes fat_start+sector, 1);
157*d5c9a868SElliott Hughes }
158*d5c9a868SElliott Hughes
loadSector(Fs_t * This,unsigned int sector,fatAccessMode_t mode,int recurs)159*d5c9a868SElliott Hughes static unsigned char *loadSector(Fs_t *This,
160*d5c9a868SElliott Hughes unsigned int sector, fatAccessMode_t mode,
161*d5c9a868SElliott Hughes int recurs)
162*d5c9a868SElliott Hughes {
163*d5c9a868SElliott Hughes uint32_t slot, bit;
164*d5c9a868SElliott Hughes ssize_t ret;
165*d5c9a868SElliott Hughes
166*d5c9a868SElliott Hughes if(locate(This,sector, &slot, &bit) < 0)
167*d5c9a868SElliott Hughes return 0;
168*d5c9a868SElliott Hughes #if 0
169*d5c9a868SElliott Hughes if (((This->fat_len + SECT_PER_ENTRY - 1) / SECT_PER_ENTRY) <= slot) {
170*d5c9a868SElliott Hughes fprintf(stderr,"This should not happen\n");
171*d5c9a868SElliott Hughes fprintf(stderr, "fat_len = %d\n", This->fat_len);
172*d5c9a868SElliott Hughes fprintf(stderr, "SECT_PER_ENTRY=%d\n", (int)SECT_PER_ENTRY);
173*d5c9a868SElliott Hughes fprintf(stderr, "sector = %d slot = %d bit=%d\n",
174*d5c9a868SElliott Hughes sector, slot, bit);
175*d5c9a868SElliott Hughes fprintf(stderr, "left = %d",(int)
176*d5c9a868SElliott Hughes ((This->fat_len+SECT_PER_ENTRY-1) / SECT_PER_ENTRY));
177*d5c9a868SElliott Hughes return 0;
178*d5c9a868SElliott Hughes }
179*d5c9a868SElliott Hughes #endif
180*d5c9a868SElliott Hughes if(!This->FatMap[slot].data) {
181*d5c9a868SElliott Hughes /* allocate the storage space */
182*d5c9a868SElliott Hughes This->FatMap[slot].data =
183*d5c9a868SElliott Hughes malloc(This->sector_size * SECT_PER_ENTRY);
184*d5c9a868SElliott Hughes if(!This->FatMap[slot].data)
185*d5c9a868SElliott Hughes return 0;
186*d5c9a868SElliott Hughes memset(This->FatMap[slot].data, 0xee,
187*d5c9a868SElliott Hughes This->sector_size * SECT_PER_ENTRY);
188*d5c9a868SElliott Hughes }
189*d5c9a868SElliott Hughes
190*d5c9a868SElliott Hughes if(! (This->FatMap[slot].valid & (ONE << bit))) {
191*d5c9a868SElliott Hughes unsigned int i;
192*d5c9a868SElliott Hughes ret = -1;
193*d5c9a868SElliott Hughes for(i=0; i< This->num_fat; i++) {
194*d5c9a868SElliott Hughes /* read the sector */
195*d5c9a868SElliott Hughes ret = fatReadSector(This, sector, slot, bit, i,
196*d5c9a868SElliott Hughes This->FatMap[slot].valid);
197*d5c9a868SElliott Hughes
198*d5c9a868SElliott Hughes if(ret == 0) {
199*d5c9a868SElliott Hughes fprintf(stderr,
200*d5c9a868SElliott Hughes "Error reading fat number %d\n", i);
201*d5c9a868SElliott Hughes continue;
202*d5c9a868SElliott Hughes }
203*d5c9a868SElliott Hughes if(This->FatMap[slot].valid)
204*d5c9a868SElliott Hughes /* Set recurs if there have already been
205*d5c9a868SElliott Hughes * sectors loaded in this bitmap long
206*d5c9a868SElliott Hughes */
207*d5c9a868SElliott Hughes recurs = 1;
208*d5c9a868SElliott Hughes break;
209*d5c9a868SElliott Hughes }
210*d5c9a868SElliott Hughes
211*d5c9a868SElliott Hughes /* all copies bad. Return error */
212*d5c9a868SElliott Hughes if(ret == 0)
213*d5c9a868SElliott Hughes return 0;
214*d5c9a868SElliott Hughes
215*d5c9a868SElliott Hughes for(i=0; (int) i < ret; i++)
216*d5c9a868SElliott Hughes This->FatMap[slot].valid |= ONE << (bit + i);
217*d5c9a868SElliott Hughes
218*d5c9a868SElliott Hughes if(!recurs && ret == 1)
219*d5c9a868SElliott Hughes /* do some prefetching, if we happened to only
220*d5c9a868SElliott Hughes * get one sector */
221*d5c9a868SElliott Hughes loadSector(This, sector+1, mode, 1);
222*d5c9a868SElliott Hughes if(!recurs && batchmode)
223*d5c9a868SElliott Hughes for(i=0; i < 1024; i++)
224*d5c9a868SElliott Hughes loadSector(This, sector+i, mode, 1);
225*d5c9a868SElliott Hughes }
226*d5c9a868SElliott Hughes
227*d5c9a868SElliott Hughes if(mode == FAT_ACCESS_WRITE) {
228*d5c9a868SElliott Hughes This->FatMap[slot].dirty |= ONE << bit;
229*d5c9a868SElliott Hughes This->fat_dirty = 1;
230*d5c9a868SElliott Hughes }
231*d5c9a868SElliott Hughes return This->FatMap[slot].data + (bit << This->sectorShift);
232*d5c9a868SElliott Hughes }
233*d5c9a868SElliott Hughes
234*d5c9a868SElliott Hughes
getAddress(Fs_t * Stream,unsigned int num,fatAccessMode_t mode)235*d5c9a868SElliott Hughes static unsigned char *getAddress(Fs_t *Stream,
236*d5c9a868SElliott Hughes unsigned int num, fatAccessMode_t mode)
237*d5c9a868SElliott Hughes {
238*d5c9a868SElliott Hughes unsigned char *ret;
239*d5c9a868SElliott Hughes unsigned int sector;
240*d5c9a868SElliott Hughes unsigned int offset;
241*d5c9a868SElliott Hughes
242*d5c9a868SElliott Hughes sector = num >> Stream->sectorShift;
243*d5c9a868SElliott Hughes ret = 0;
244*d5c9a868SElliott Hughes if(sector == Stream->lastFatSectorNr &&
245*d5c9a868SElliott Hughes Stream->lastFatAccessMode >= mode)
246*d5c9a868SElliott Hughes ret = Stream->lastFatSectorData;
247*d5c9a868SElliott Hughes if(!ret) {
248*d5c9a868SElliott Hughes ret = loadSector(Stream, sector, mode, 0);
249*d5c9a868SElliott Hughes if(!ret)
250*d5c9a868SElliott Hughes return 0;
251*d5c9a868SElliott Hughes Stream->lastFatSectorNr = sector;
252*d5c9a868SElliott Hughes Stream->lastFatSectorData = ret;
253*d5c9a868SElliott Hughes Stream->lastFatAccessMode = mode;
254*d5c9a868SElliott Hughes }
255*d5c9a868SElliott Hughes offset = num & Stream->sectorMask;
256*d5c9a868SElliott Hughes return ret+offset;
257*d5c9a868SElliott Hughes }
258*d5c9a868SElliott Hughes
259*d5c9a868SElliott Hughes
readByte(Fs_t * Stream,unsigned int start)260*d5c9a868SElliott Hughes static int readByte(Fs_t *Stream, unsigned int start)
261*d5c9a868SElliott Hughes {
262*d5c9a868SElliott Hughes unsigned char *address;
263*d5c9a868SElliott Hughes
264*d5c9a868SElliott Hughes address = getAddress(Stream, start, FAT_ACCESS_READ);
265*d5c9a868SElliott Hughes if(!address)
266*d5c9a868SElliott Hughes return -1;
267*d5c9a868SElliott Hughes return *address;
268*d5c9a868SElliott Hughes }
269*d5c9a868SElliott Hughes
270*d5c9a868SElliott Hughes
271*d5c9a868SElliott Hughes /*
272*d5c9a868SElliott Hughes * Fat 12 encoding:
273*d5c9a868SElliott Hughes * | byte n | byte n+1 | byte n+2 |
274*d5c9a868SElliott Hughes * |7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0|
275*d5c9a868SElliott Hughes * | | | | | | | | | | | | | | | | | | | | | | | | |
276*d5c9a868SElliott Hughes * | n+0.0 | n+0.5 | n+1.0 | n+1.5 | n+2.0 | n+2.5 |
277*d5c9a868SElliott Hughes * \_____ \____ \______/________/_____ /
278*d5c9a868SElliott Hughes * ____\______\________/ _____/ ____\_/
279*d5c9a868SElliott Hughes * / \ \ / / \
280*d5c9a868SElliott Hughes * | n+1.5 | n+0.0 | n+0.5 | n+2.0 | n+2.5 | n+1.0 |
281*d5c9a868SElliott Hughes * | FAT entry k | FAT entry k+1 |
282*d5c9a868SElliott Hughes */
283*d5c9a868SElliott Hughes
284*d5c9a868SElliott Hughes /*
285*d5c9a868SElliott Hughes * Get and decode a FAT (file allocation table) entry. Returns the cluster
286*d5c9a868SElliott Hughes * number on success or 1 on failure.
287*d5c9a868SElliott Hughes */
288*d5c9a868SElliott Hughes
fat12_decode(Fs_t * Stream,unsigned int num)289*d5c9a868SElliott Hughes static unsigned int fat12_decode(Fs_t *Stream, unsigned int num)
290*d5c9a868SElliott Hughes {
291*d5c9a868SElliott Hughes unsigned int start = num * 3 / 2;
292*d5c9a868SElliott Hughes int byte0 = readByte(Stream, start);
293*d5c9a868SElliott Hughes int byte1 = readByte(Stream, start+1);
294*d5c9a868SElliott Hughes
295*d5c9a868SElliott Hughes if (num < 2 || byte0 < 0 || byte1 < 0 || num > Stream->num_clus+1) {
296*d5c9a868SElliott Hughes fprintf(stderr,"[1] Bad address %d\n", num);
297*d5c9a868SElliott Hughes return 1;
298*d5c9a868SElliott Hughes }
299*d5c9a868SElliott Hughes
300*d5c9a868SElliott Hughes if (num & 1)
301*d5c9a868SElliott Hughes return ((uint32_t)byte1 << 4) | (((uint32_t)byte0 & 0xf0)>>4);
302*d5c9a868SElliott Hughes else
303*d5c9a868SElliott Hughes return (((uint32_t)byte1 & 0xf) << 8) | (uint32_t)byte0;
304*d5c9a868SElliott Hughes }
305*d5c9a868SElliott Hughes
306*d5c9a868SElliott Hughes
307*d5c9a868SElliott Hughes /*
308*d5c9a868SElliott Hughes * Puts a code into the FAT table. Is the opposite of fat_decode(). No
309*d5c9a868SElliott Hughes * sanity checking is done on the code. Returns a 1 on error.
310*d5c9a868SElliott Hughes */
fat12_encode(Fs_t * Stream,unsigned int num,unsigned int code)311*d5c9a868SElliott Hughes static void fat12_encode(Fs_t *Stream, unsigned int num, unsigned int code)
312*d5c9a868SElliott Hughes {
313*d5c9a868SElliott Hughes unsigned int start = num * 3 / 2;
314*d5c9a868SElliott Hughes unsigned char *address0 = getAddress(Stream, start, FAT_ACCESS_WRITE);
315*d5c9a868SElliott Hughes unsigned char *address1 = getAddress(Stream, start+1, FAT_ACCESS_WRITE);
316*d5c9a868SElliott Hughes
317*d5c9a868SElliott Hughes if (num & 1) {
318*d5c9a868SElliott Hughes /* (odd) not on byte boundary */
319*d5c9a868SElliott Hughes *address0 = (*address0 & 0x0f) | ((code << 4) & 0xf0);
320*d5c9a868SElliott Hughes *address1 = (code >> 4) & 0xff;
321*d5c9a868SElliott Hughes } else {
322*d5c9a868SElliott Hughes /* (even) on byte boundary */
323*d5c9a868SElliott Hughes *address0 = code & 0xff;
324*d5c9a868SElliott Hughes *address1 = (*address1 & 0xf0) | ((code >> 8) & 0x0f);
325*d5c9a868SElliott Hughes }
326*d5c9a868SElliott Hughes }
327*d5c9a868SElliott Hughes
328*d5c9a868SElliott Hughes
329*d5c9a868SElliott Hughes /*
330*d5c9a868SElliott Hughes * Fat 16 encoding:
331*d5c9a868SElliott Hughes * | byte n | byte n+1 |
332*d5c9a868SElliott Hughes * |7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0|
333*d5c9a868SElliott Hughes * | | | | | | | | | | | | | | | | |
334*d5c9a868SElliott Hughes * | FAT entry k |
335*d5c9a868SElliott Hughes */
336*d5c9a868SElliott Hughes
fat16_decode(Fs_t * Stream,unsigned int num)337*d5c9a868SElliott Hughes static unsigned int fat16_decode(Fs_t *Stream, unsigned int num)
338*d5c9a868SElliott Hughes {
339*d5c9a868SElliott Hughes unsigned char *address = getAddress(Stream, num << 1, FAT_ACCESS_READ);
340*d5c9a868SElliott Hughes if(!address)
341*d5c9a868SElliott Hughes return 1;
342*d5c9a868SElliott Hughes return _WORD(address);
343*d5c9a868SElliott Hughes }
344*d5c9a868SElliott Hughes
fat16_encode(Fs_t * Stream,unsigned int num,unsigned int code)345*d5c9a868SElliott Hughes static void fat16_encode(Fs_t *Stream, unsigned int num, unsigned int code)
346*d5c9a868SElliott Hughes {
347*d5c9a868SElliott Hughes if(code > UINT16_MAX) {
348*d5c9a868SElliott Hughes fprintf(stderr, "FAT16 code %x too big\n", code);
349*d5c9a868SElliott Hughes exit(1);
350*d5c9a868SElliott Hughes }
351*d5c9a868SElliott Hughes unsigned char *address = getAddress(Stream, num << 1, FAT_ACCESS_WRITE);
352*d5c9a868SElliott Hughes set_word(address, (uint16_t) code);
353*d5c9a868SElliott Hughes }
354*d5c9a868SElliott Hughes
355*d5c9a868SElliott Hughes
356*d5c9a868SElliott Hughes #pragma GCC diagnostic push
357*d5c9a868SElliott Hughes #pragma GCC diagnostic ignored "-Wcast-align"
358*d5c9a868SElliott Hughes /* Ignore alignment warnings about casting to type with higher
359*d5c9a868SElliott Hughes * alignment requirement. Requirement is met, as initial pointer is an
360*d5c9a868SElliott Hughes * even offset into a buffer allocated by malloc, which according to
361*d5c9a868SElliott Hughes * manpage is "suitably aligned for any built-in type */
fast_fat16_decode(Fs_t * Stream,unsigned int num)362*d5c9a868SElliott Hughes static unsigned int fast_fat16_decode(Fs_t *Stream, unsigned int num)
363*d5c9a868SElliott Hughes {
364*d5c9a868SElliott Hughes unsigned short *address =
365*d5c9a868SElliott Hughes (unsigned short *) getAddress(Stream, num << 1,
366*d5c9a868SElliott Hughes FAT_ACCESS_READ);
367*d5c9a868SElliott Hughes if(!address)
368*d5c9a868SElliott Hughes return 1;
369*d5c9a868SElliott Hughes return *address;
370*d5c9a868SElliott Hughes }
371*d5c9a868SElliott Hughes
fast_fat16_encode(Fs_t * Stream,unsigned int num,unsigned int code)372*d5c9a868SElliott Hughes static void fast_fat16_encode(Fs_t *Stream, unsigned int num, unsigned int code)
373*d5c9a868SElliott Hughes {
374*d5c9a868SElliott Hughes unsigned short *address =
375*d5c9a868SElliott Hughes (unsigned short *) getAddress(Stream, num << 1,
376*d5c9a868SElliott Hughes FAT_ACCESS_WRITE);
377*d5c9a868SElliott Hughes if(code > UINT16_MAX) {
378*d5c9a868SElliott Hughes fprintf(stderr, "FAT16 code %x too big\n", code);
379*d5c9a868SElliott Hughes exit(1);
380*d5c9a868SElliott Hughes }
381*d5c9a868SElliott Hughes *address = (uint16_t) code;
382*d5c9a868SElliott Hughes }
383*d5c9a868SElliott Hughes #pragma GCC diagnostic pop
384*d5c9a868SElliott Hughes
385*d5c9a868SElliott Hughes
386*d5c9a868SElliott Hughes
387*d5c9a868SElliott Hughes /*
388*d5c9a868SElliott Hughes * Fat 32 encoding
389*d5c9a868SElliott Hughes */
390*d5c9a868SElliott Hughes #define FAT32_HIGH 0xf0000000
391*d5c9a868SElliott Hughes #define FAT32_ADDR 0x0fffffff
392*d5c9a868SElliott Hughes
fat32_decode(Fs_t * Stream,unsigned int num)393*d5c9a868SElliott Hughes static unsigned int fat32_decode(Fs_t *Stream, unsigned int num)
394*d5c9a868SElliott Hughes {
395*d5c9a868SElliott Hughes unsigned char *address = getAddress(Stream, num << 2, FAT_ACCESS_READ);
396*d5c9a868SElliott Hughes if(!address)
397*d5c9a868SElliott Hughes return 1;
398*d5c9a868SElliott Hughes return _DWORD(address) & FAT32_ADDR;
399*d5c9a868SElliott Hughes }
400*d5c9a868SElliott Hughes
fat32_encode(Fs_t * Stream,unsigned int num,unsigned int code)401*d5c9a868SElliott Hughes static void fat32_encode(Fs_t *Stream, unsigned int num, unsigned int code)
402*d5c9a868SElliott Hughes {
403*d5c9a868SElliott Hughes unsigned char *address = getAddress(Stream, num << 2, FAT_ACCESS_WRITE);
404*d5c9a868SElliott Hughes set_dword(address,(code&FAT32_ADDR) | (_DWORD(address)&FAT32_HIGH));
405*d5c9a868SElliott Hughes }
406*d5c9a868SElliott Hughes
407*d5c9a868SElliott Hughes #pragma GCC diagnostic push
408*d5c9a868SElliott Hughes #pragma GCC diagnostic ignored "-Wcast-align"
fast_fat32_decode(Fs_t * Stream,unsigned int num)409*d5c9a868SElliott Hughes static unsigned int fast_fat32_decode(Fs_t *Stream, unsigned int num)
410*d5c9a868SElliott Hughes {
411*d5c9a868SElliott Hughes unsigned int *address =
412*d5c9a868SElliott Hughes (unsigned int *) getAddress(Stream, num << 2,
413*d5c9a868SElliott Hughes FAT_ACCESS_READ);
414*d5c9a868SElliott Hughes if(!address)
415*d5c9a868SElliott Hughes return 1;
416*d5c9a868SElliott Hughes return (*address) & FAT32_ADDR;
417*d5c9a868SElliott Hughes }
418*d5c9a868SElliott Hughes
fast_fat32_encode(Fs_t * Stream,unsigned int num,unsigned int code)419*d5c9a868SElliott Hughes static void fast_fat32_encode(Fs_t *Stream, unsigned int num, unsigned int code)
420*d5c9a868SElliott Hughes {
421*d5c9a868SElliott Hughes unsigned int *address =
422*d5c9a868SElliott Hughes (unsigned int *) getAddress(Stream, num << 2,
423*d5c9a868SElliott Hughes FAT_ACCESS_WRITE);
424*d5c9a868SElliott Hughes *address = (*address & FAT32_HIGH) | (code & FAT32_ADDR);
425*d5c9a868SElliott Hughes }
426*d5c9a868SElliott Hughes #pragma GCC diagnostic pop
427*d5c9a868SElliott Hughes
428*d5c9a868SElliott Hughes /*
429*d5c9a868SElliott Hughes * Write the FAT table to the disk. Up to now the FAT manipulation has
430*d5c9a868SElliott Hughes * been done in memory. All errors are fatal. (Might not be too smart
431*d5c9a868SElliott Hughes * to wait till the end of the program to write the table. Oh well...)
432*d5c9a868SElliott Hughes */
433*d5c9a868SElliott Hughes
fat_write(Fs_t * This)434*d5c9a868SElliott Hughes void fat_write(Fs_t *This)
435*d5c9a868SElliott Hughes {
436*d5c9a868SElliott Hughes unsigned int i, j, dups, bit, slot;
437*d5c9a868SElliott Hughes ssize_t ret;
438*d5c9a868SElliott Hughes
439*d5c9a868SElliott Hughes /*fprintf(stderr, "Fat write\n");*/
440*d5c9a868SElliott Hughes
441*d5c9a868SElliott Hughes if (!This->fat_dirty)
442*d5c9a868SElliott Hughes return;
443*d5c9a868SElliott Hughes
444*d5c9a868SElliott Hughes dups = This->num_fat;
445*d5c9a868SElliott Hughes if (This->fat_error)
446*d5c9a868SElliott Hughes dups = 1;
447*d5c9a868SElliott Hughes
448*d5c9a868SElliott Hughes
449*d5c9a868SElliott Hughes for(i=0; i<dups; i++){
450*d5c9a868SElliott Hughes j = 0;
451*d5c9a868SElliott Hughes for(slot=0;j<This->fat_len;slot++) {
452*d5c9a868SElliott Hughes if(!This->FatMap[slot].dirty) {
453*d5c9a868SElliott Hughes j += SECT_PER_ENTRY;
454*d5c9a868SElliott Hughes continue;
455*d5c9a868SElliott Hughes }
456*d5c9a868SElliott Hughes for(bit=0;
457*d5c9a868SElliott Hughes bit < SECT_PER_ENTRY && j<This->fat_len;
458*d5c9a868SElliott Hughes bit++,j++) {
459*d5c9a868SElliott Hughes if(!(This->FatMap[slot].dirty & (ONE << bit)))
460*d5c9a868SElliott Hughes continue;
461*d5c9a868SElliott Hughes ret = fatWriteSector(This,j,slot, bit, i);
462*d5c9a868SElliott Hughes if (ret < (int) This->sector_size){
463*d5c9a868SElliott Hughes if (ret < 0 ){
464*d5c9a868SElliott Hughes perror("error in fat_write");
465*d5c9a868SElliott Hughes exit(1);
466*d5c9a868SElliott Hughes } else {
467*d5c9a868SElliott Hughes fprintf(stderr,
468*d5c9a868SElliott Hughes "end of file in fat_write\n");
469*d5c9a868SElliott Hughes exit(1);
470*d5c9a868SElliott Hughes }
471*d5c9a868SElliott Hughes }
472*d5c9a868SElliott Hughes /* if last dupe, zero it out */
473*d5c9a868SElliott Hughes if(i==dups-1)
474*d5c9a868SElliott Hughes This->FatMap[slot].dirty &= ~(ONE<<bit);
475*d5c9a868SElliott Hughes }
476*d5c9a868SElliott Hughes }
477*d5c9a868SElliott Hughes }
478*d5c9a868SElliott Hughes /* write the info sector, if any */
479*d5c9a868SElliott Hughes if(This->infoSectorLoc && This->infoSectorLoc != MAX32) {
480*d5c9a868SElliott Hughes /* initialize info sector */
481*d5c9a868SElliott Hughes InfoSector_t *infoSector;
482*d5c9a868SElliott Hughes infoSector = (InfoSector_t *) safe_malloc(This->sector_size);
483*d5c9a868SElliott Hughes if(forceReadSector(This, (char *)infoSector,
484*d5c9a868SElliott Hughes This->infoSectorLoc, 1) !=
485*d5c9a868SElliott Hughes (signed int) This->sector_size) {
486*d5c9a868SElliott Hughes fprintf(stderr,"Trouble reading the info sector\n");
487*d5c9a868SElliott Hughes memset(infoSector->filler1, 0, sizeof(infoSector->filler1));
488*d5c9a868SElliott Hughes memset(infoSector->filler2, 0, sizeof(infoSector->filler2));
489*d5c9a868SElliott Hughes }
490*d5c9a868SElliott Hughes set_dword(infoSector->signature1, INFOSECT_SIGNATURE1);
491*d5c9a868SElliott Hughes set_dword(infoSector->signature2, INFOSECT_SIGNATURE2);
492*d5c9a868SElliott Hughes set_dword(infoSector->pos, This->last);
493*d5c9a868SElliott Hughes set_dword(infoSector->count, This->freeSpace);
494*d5c9a868SElliott Hughes set_word(infoSector->signature3, 0xaa55);
495*d5c9a868SElliott Hughes if(forceWriteSector(This, (char *)infoSector, This->infoSectorLoc, 1) !=
496*d5c9a868SElliott Hughes (signed int) This->sector_size)
497*d5c9a868SElliott Hughes fprintf(stderr,"Trouble writing the info sector\n");
498*d5c9a868SElliott Hughes free(infoSector);
499*d5c9a868SElliott Hughes }
500*d5c9a868SElliott Hughes This->fat_dirty = 0;
501*d5c9a868SElliott Hughes This->lastFatAccessMode = FAT_ACCESS_READ;
502*d5c9a868SElliott Hughes }
503*d5c9a868SElliott Hughes
504*d5c9a868SElliott Hughes
505*d5c9a868SElliott Hughes
506*d5c9a868SElliott Hughes /*
507*d5c9a868SElliott Hughes * Zero-Fat
508*d5c9a868SElliott Hughes * Used by mformat.
509*d5c9a868SElliott Hughes */
zero_fat(Fs_t * Stream,uint8_t media_descriptor)510*d5c9a868SElliott Hughes int zero_fat(Fs_t *Stream, uint8_t media_descriptor)
511*d5c9a868SElliott Hughes {
512*d5c9a868SElliott Hughes unsigned int i, j;
513*d5c9a868SElliott Hughes unsigned int fat_start;
514*d5c9a868SElliott Hughes unsigned char *buf;
515*d5c9a868SElliott Hughes
516*d5c9a868SElliott Hughes buf = malloc(Stream->sector_size);
517*d5c9a868SElliott Hughes if(!buf) {
518*d5c9a868SElliott Hughes perror("alloc fat sector buffer");
519*d5c9a868SElliott Hughes return -1;
520*d5c9a868SElliott Hughes }
521*d5c9a868SElliott Hughes for(i=0; i< Stream->num_fat; i++) {
522*d5c9a868SElliott Hughes fat_start = Stream->fat_start + i*Stream->fat_len;
523*d5c9a868SElliott Hughes for(j = 0; j < Stream->fat_len; j++) {
524*d5c9a868SElliott Hughes if(j <= 1)
525*d5c9a868SElliott Hughes memset(buf, 0, Stream->sector_size);
526*d5c9a868SElliott Hughes if(!j) {
527*d5c9a868SElliott Hughes buf[0] = media_descriptor;
528*d5c9a868SElliott Hughes buf[2] = buf[1] = 0xff;
529*d5c9a868SElliott Hughes if(Stream->fat_bits > 12)
530*d5c9a868SElliott Hughes buf[3] = 0xff;
531*d5c9a868SElliott Hughes if(Stream->fat_bits > 16) {
532*d5c9a868SElliott Hughes buf[4] = 0xff;
533*d5c9a868SElliott Hughes buf[5] = 0xff;
534*d5c9a868SElliott Hughes buf[6] = 0xff;
535*d5c9a868SElliott Hughes buf[7] = 0x0f;
536*d5c9a868SElliott Hughes }
537*d5c9a868SElliott Hughes }
538*d5c9a868SElliott Hughes
539*d5c9a868SElliott Hughes if(forceWriteSector(Stream, (char *)buf,
540*d5c9a868SElliott Hughes fat_start + j, 1) !=
541*d5c9a868SElliott Hughes (signed int) Stream->sector_size) {
542*d5c9a868SElliott Hughes fprintf(stderr,
543*d5c9a868SElliott Hughes "Trouble initializing a FAT sector\n");
544*d5c9a868SElliott Hughes free(buf);
545*d5c9a868SElliott Hughes return -1;
546*d5c9a868SElliott Hughes }
547*d5c9a868SElliott Hughes }
548*d5c9a868SElliott Hughes }
549*d5c9a868SElliott Hughes
550*d5c9a868SElliott Hughes free(buf);
551*d5c9a868SElliott Hughes Stream->FatMap = GetFatMap(Stream);
552*d5c9a868SElliott Hughes if (Stream->FatMap == NULL) {
553*d5c9a868SElliott Hughes perror("alloc fat map");
554*d5c9a868SElliott Hughes return -1;
555*d5c9a868SElliott Hughes }
556*d5c9a868SElliott Hughes return 0;
557*d5c9a868SElliott Hughes }
558*d5c9a868SElliott Hughes
559*d5c9a868SElliott Hughes
set_fat12(Fs_t * This)560*d5c9a868SElliott Hughes static void set_fat12(Fs_t *This)
561*d5c9a868SElliott Hughes {
562*d5c9a868SElliott Hughes This->fat_bits = 12;
563*d5c9a868SElliott Hughes This->end_fat = 0xfff;
564*d5c9a868SElliott Hughes This->last_fat = 0xff6;
565*d5c9a868SElliott Hughes This->fat_decode = fat12_decode;
566*d5c9a868SElliott Hughes This->fat_encode = fat12_encode;
567*d5c9a868SElliott Hughes }
568*d5c9a868SElliott Hughes
569*d5c9a868SElliott Hughes static uint16_t word_endian_test = 0x1234;
570*d5c9a868SElliott Hughes
set_fat16(Fs_t * This)571*d5c9a868SElliott Hughes static void set_fat16(Fs_t *This)
572*d5c9a868SElliott Hughes {
573*d5c9a868SElliott Hughes uint8_t *t = (uint8_t *) &word_endian_test;
574*d5c9a868SElliott Hughes This->fat_bits = 16;
575*d5c9a868SElliott Hughes This->end_fat = 0xffff;
576*d5c9a868SElliott Hughes This->last_fat = 0xfff6;
577*d5c9a868SElliott Hughes
578*d5c9a868SElliott Hughes if(t[0] == 0x34 && t[1] == 0x12) {
579*d5c9a868SElliott Hughes This->fat_decode = fast_fat16_decode;
580*d5c9a868SElliott Hughes This->fat_encode = fast_fat16_encode;
581*d5c9a868SElliott Hughes } else {
582*d5c9a868SElliott Hughes This->fat_decode = fat16_decode;
583*d5c9a868SElliott Hughes This->fat_encode = fat16_encode;
584*d5c9a868SElliott Hughes }
585*d5c9a868SElliott Hughes }
586*d5c9a868SElliott Hughes
587*d5c9a868SElliott Hughes static uint32_t dword_endian_test = 0x12345678;
588*d5c9a868SElliott Hughes
set_fat32(Fs_t * This)589*d5c9a868SElliott Hughes static void set_fat32(Fs_t *This)
590*d5c9a868SElliott Hughes {
591*d5c9a868SElliott Hughes uint8_t *t = (uint8_t *) &dword_endian_test;
592*d5c9a868SElliott Hughes This->fat_bits = 32;
593*d5c9a868SElliott Hughes This->end_fat = 0xfffffff;
594*d5c9a868SElliott Hughes This->last_fat = 0xffffff6;
595*d5c9a868SElliott Hughes
596*d5c9a868SElliott Hughes if(t[0] == 0x78 && t[1] == 0x56 && t[2] == 0x34 && t[3] == 0x12) {
597*d5c9a868SElliott Hughes This->fat_decode = fast_fat32_decode;
598*d5c9a868SElliott Hughes This->fat_encode = fast_fat32_encode;
599*d5c9a868SElliott Hughes } else {
600*d5c9a868SElliott Hughes This->fat_decode = fat32_decode;
601*d5c9a868SElliott Hughes This->fat_encode = fat32_encode;
602*d5c9a868SElliott Hughes }
603*d5c9a868SElliott Hughes }
604*d5c9a868SElliott Hughes
set_fat(Fs_t * This)605*d5c9a868SElliott Hughes void set_fat(Fs_t *This) {
606*d5c9a868SElliott Hughes if(This->num_clus < FAT12)
607*d5c9a868SElliott Hughes set_fat12(This);
608*d5c9a868SElliott Hughes else if(This->num_clus < FAT16)
609*d5c9a868SElliott Hughes set_fat16(This);
610*d5c9a868SElliott Hughes else
611*d5c9a868SElliott Hughes set_fat32(This);
612*d5c9a868SElliott Hughes }
613*d5c9a868SElliott Hughes
check_fat(Fs_t * This)614*d5c9a868SElliott Hughes static int check_fat(Fs_t *This)
615*d5c9a868SElliott Hughes {
616*d5c9a868SElliott Hughes /*
617*d5c9a868SElliott Hughes * This is only a sanity check. For disks with really big FATs,
618*d5c9a868SElliott Hughes * there is no point in checking the whole FAT.
619*d5c9a868SElliott Hughes */
620*d5c9a868SElliott Hughes
621*d5c9a868SElliott Hughes unsigned int i, f;
622*d5c9a868SElliott Hughes unsigned int tocheck;
623*d5c9a868SElliott Hughes if(mtools_skip_check)
624*d5c9a868SElliott Hughes return 0;
625*d5c9a868SElliott Hughes
626*d5c9a868SElliott Hughes /* too few sectors in the FAT */
627*d5c9a868SElliott Hughes if(This->fat_len < NEEDED_FAT_SIZE(This)) {
628*d5c9a868SElliott Hughes fprintf(stderr, "Too few sectors in FAT\n");
629*d5c9a868SElliott Hughes return -1;
630*d5c9a868SElliott Hughes }
631*d5c9a868SElliott Hughes /* we do not warn about too much sectors in FAT, which may
632*d5c9a868SElliott Hughes * happen when a partition has been shrunk using FIPS, or on
633*d5c9a868SElliott Hughes * other occurrences */
634*d5c9a868SElliott Hughes
635*d5c9a868SElliott Hughes tocheck = This->num_clus;
636*d5c9a868SElliott Hughes if (tocheck + 1 >= This->last_fat) {
637*d5c9a868SElliott Hughes fprintf(stderr, "Too many clusters in FAT\n");
638*d5c9a868SElliott Hughes return -1;
639*d5c9a868SElliott Hughes }
640*d5c9a868SElliott Hughes
641*d5c9a868SElliott Hughes if(tocheck > 4096)
642*d5c9a868SElliott Hughes tocheck = 4096;
643*d5c9a868SElliott Hughes
644*d5c9a868SElliott Hughes for ( i= 3 ; i < tocheck; i++){
645*d5c9a868SElliott Hughes f = This->fat_decode(This,i);
646*d5c9a868SElliott Hughes if (f == 1 || (f < This->last_fat && f > This->num_clus)){
647*d5c9a868SElliott Hughes fprintf(stderr,
648*d5c9a868SElliott Hughes "Cluster # at %d too big(%#x)\n", i,f);
649*d5c9a868SElliott Hughes fprintf(stderr,"Probably non MS-DOS disk\n");
650*d5c9a868SElliott Hughes return -1;
651*d5c9a868SElliott Hughes }
652*d5c9a868SElliott Hughes }
653*d5c9a868SElliott Hughes return 0;
654*d5c9a868SElliott Hughes }
655*d5c9a868SElliott Hughes
656*d5c9a868SElliott Hughes
657*d5c9a868SElliott Hughes /*
658*d5c9a868SElliott Hughes * Read the first sector of FAT table into memory. Crude error detection on
659*d5c9a868SElliott Hughes * wrong FAT encoding scheme.
660*d5c9a868SElliott Hughes */
check_media_type(Fs_t * This,union bootsector * boot)661*d5c9a868SElliott Hughes static int check_media_type(Fs_t *This, union bootsector *boot)
662*d5c9a868SElliott Hughes {
663*d5c9a868SElliott Hughes unsigned char *address;
664*d5c9a868SElliott Hughes
665*d5c9a868SElliott Hughes This->FatMap = GetFatMap(This);
666*d5c9a868SElliott Hughes if (This->FatMap == NULL) {
667*d5c9a868SElliott Hughes perror("alloc fat map");
668*d5c9a868SElliott Hughes return -1;
669*d5c9a868SElliott Hughes }
670*d5c9a868SElliott Hughes
671*d5c9a868SElliott Hughes address = getAddress(This, 0, FAT_ACCESS_READ);
672*d5c9a868SElliott Hughes if(!address) {
673*d5c9a868SElliott Hughes fprintf(stderr,
674*d5c9a868SElliott Hughes "Could not read first FAT sector\n");
675*d5c9a868SElliott Hughes return -1;
676*d5c9a868SElliott Hughes }
677*d5c9a868SElliott Hughes
678*d5c9a868SElliott Hughes if(mtools_skip_check)
679*d5c9a868SElliott Hughes return 0;
680*d5c9a868SElliott Hughes
681*d5c9a868SElliott Hughes if(!address[0] && !address[1] && !address[2])
682*d5c9a868SElliott Hughes /* Some Atari disks have zeroes where Dos has media descriptor
683*d5c9a868SElliott Hughes * and 0xff. Do not consider this as an error */
684*d5c9a868SElliott Hughes return 0;
685*d5c9a868SElliott Hughes
686*d5c9a868SElliott Hughes if((address[0] != boot->boot.descr && boot->boot.descr >= 0xf0 &&
687*d5c9a868SElliott Hughes ((address[0] != 0xf9 && address[0] != 0xf7)
688*d5c9a868SElliott Hughes || boot->boot.descr != 0xf0)) || address[0] < 0xf0) {
689*d5c9a868SElliott Hughes fprintf(stderr,
690*d5c9a868SElliott Hughes "Bad media types %02x/%02x, probably non-MSDOS disk\n",
691*d5c9a868SElliott Hughes address[0],
692*d5c9a868SElliott Hughes boot->boot.descr);
693*d5c9a868SElliott Hughes return -1;
694*d5c9a868SElliott Hughes }
695*d5c9a868SElliott Hughes
696*d5c9a868SElliott Hughes if(address[1] != 0xff || address[2] != 0xff){
697*d5c9a868SElliott Hughes fprintf(stderr,"Initial bytes of fat is not 0xff\n");
698*d5c9a868SElliott Hughes return -1;
699*d5c9a868SElliott Hughes }
700*d5c9a868SElliott Hughes
701*d5c9a868SElliott Hughes return 0;
702*d5c9a868SElliott Hughes }
703*d5c9a868SElliott Hughes
fat_32_read(Fs_t * This,union bootsector * boot)704*d5c9a868SElliott Hughes static int fat_32_read(Fs_t *This, union bootsector *boot)
705*d5c9a868SElliott Hughes {
706*d5c9a868SElliott Hughes size_t size;
707*d5c9a868SElliott Hughes
708*d5c9a868SElliott Hughes This->fat_len = DWORD(ext.fat32.bigFat);
709*d5c9a868SElliott Hughes This->writeAllFats = !(boot->boot.ext.fat32.extFlags[0] & 0x80);
710*d5c9a868SElliott Hughes This->primaryFat = boot->boot.ext.fat32.extFlags[0] & 0xf;
711*d5c9a868SElliott Hughes This->rootCluster = DWORD(ext.fat32.rootCluster);
712*d5c9a868SElliott Hughes This->clus_start = This->fat_start + This->num_fat * This->fat_len;
713*d5c9a868SElliott Hughes
714*d5c9a868SElliott Hughes /* read the info sector */
715*d5c9a868SElliott Hughes size = This->sector_size;
716*d5c9a868SElliott Hughes This->infoSectorLoc = WORD(ext.fat32.infoSector);
717*d5c9a868SElliott Hughes if(This->sector_size >= 512 &&
718*d5c9a868SElliott Hughes This->infoSectorLoc && This->infoSectorLoc != MAX32) {
719*d5c9a868SElliott Hughes InfoSector_t *infoSector;
720*d5c9a868SElliott Hughes infoSector = (InfoSector_t *) safe_malloc(size);
721*d5c9a868SElliott Hughes if(forceReadSector(This, (char *)infoSector,
722*d5c9a868SElliott Hughes This->infoSectorLoc, 1) ==
723*d5c9a868SElliott Hughes (signed int) This->sector_size &&
724*d5c9a868SElliott Hughes _DWORD(infoSector->signature1) == INFOSECT_SIGNATURE1 &&
725*d5c9a868SElliott Hughes _DWORD(infoSector->signature2) == INFOSECT_SIGNATURE2) {
726*d5c9a868SElliott Hughes This->freeSpace = _DWORD(infoSector->count);
727*d5c9a868SElliott Hughes This->last = _DWORD(infoSector->pos);
728*d5c9a868SElliott Hughes }
729*d5c9a868SElliott Hughes free(infoSector);
730*d5c9a868SElliott Hughes }
731*d5c9a868SElliott Hughes
732*d5c9a868SElliott Hughes return(check_media_type(This, boot) || check_fat(This));
733*d5c9a868SElliott Hughes }
734*d5c9a868SElliott Hughes
735*d5c9a868SElliott Hughes
old_fat_read(Fs_t * This,union bootsector * boot,int nodups)736*d5c9a868SElliott Hughes static int old_fat_read(Fs_t *This, union bootsector *boot, int nodups)
737*d5c9a868SElliott Hughes {
738*d5c9a868SElliott Hughes This->writeAllFats = 1;
739*d5c9a868SElliott Hughes This->primaryFat = 0;
740*d5c9a868SElliott Hughes This->dir_start = This->fat_start + This->num_fat * This->fat_len;
741*d5c9a868SElliott Hughes This->infoSectorLoc = MAX32;
742*d5c9a868SElliott Hughes
743*d5c9a868SElliott Hughes if(nodups)
744*d5c9a868SElliott Hughes This->num_fat = 1;
745*d5c9a868SElliott Hughes
746*d5c9a868SElliott Hughes if(check_media_type(This, boot))
747*d5c9a868SElliott Hughes return -1;
748*d5c9a868SElliott Hughes
749*d5c9a868SElliott Hughes if(This->num_clus >= FAT12)
750*d5c9a868SElliott Hughes /* third FAT byte must be 0xff */
751*d5c9a868SElliott Hughes if(!mtools_skip_check && readByte(This, 3) != 0xff)
752*d5c9a868SElliott Hughes return -1;
753*d5c9a868SElliott Hughes
754*d5c9a868SElliott Hughes return check_fat(This);
755*d5c9a868SElliott Hughes }
756*d5c9a868SElliott Hughes
757*d5c9a868SElliott Hughes /*
758*d5c9a868SElliott Hughes * Read the first sector of the FAT table into memory and initialize
759*d5c9a868SElliott Hughes * structures.
760*d5c9a868SElliott Hughes */
fat_read(Fs_t * This,union bootsector * boot,int nodups)761*d5c9a868SElliott Hughes int fat_read(Fs_t *This, union bootsector *boot, int nodups)
762*d5c9a868SElliott Hughes {
763*d5c9a868SElliott Hughes This->fat_error = 0;
764*d5c9a868SElliott Hughes This->fat_dirty = 0;
765*d5c9a868SElliott Hughes This->last = MAX32;
766*d5c9a868SElliott Hughes This->freeSpace = MAX32;
767*d5c9a868SElliott Hughes This->lastFatSectorNr = 0;
768*d5c9a868SElliott Hughes This->lastFatSectorData = 0;
769*d5c9a868SElliott Hughes
770*d5c9a868SElliott Hughes if(This->num_clus < FAT16)
771*d5c9a868SElliott Hughes return old_fat_read(This, boot, nodups);
772*d5c9a868SElliott Hughes else
773*d5c9a868SElliott Hughes return fat_32_read(This, boot);
774*d5c9a868SElliott Hughes }
775*d5c9a868SElliott Hughes
776*d5c9a868SElliott Hughes
fatDecode(Fs_t * This,unsigned int pos)777*d5c9a868SElliott Hughes unsigned int fatDecode(Fs_t *This, unsigned int pos)
778*d5c9a868SElliott Hughes {
779*d5c9a868SElliott Hughes unsigned int ret;
780*d5c9a868SElliott Hughes
781*d5c9a868SElliott Hughes ret = This->fat_decode(This, pos);
782*d5c9a868SElliott Hughes if(ret && (ret < 2 || ret > This->num_clus+1) && ret < This->last_fat) {
783*d5c9a868SElliott Hughes fprintf(stderr, "Bad FAT entry %d at %d\n", ret, pos);
784*d5c9a868SElliott Hughes This->fat_error++;
785*d5c9a868SElliott Hughes }
786*d5c9a868SElliott Hughes return ret;
787*d5c9a868SElliott Hughes }
788*d5c9a868SElliott Hughes
789*d5c9a868SElliott Hughes /* append a new cluster */
fatAppend(Fs_t * This,unsigned int pos,unsigned int newpos)790*d5c9a868SElliott Hughes void fatAppend(Fs_t *This, unsigned int pos, unsigned int newpos)
791*d5c9a868SElliott Hughes {
792*d5c9a868SElliott Hughes This->fat_encode(This, pos, newpos);
793*d5c9a868SElliott Hughes This->fat_encode(This, newpos, This->end_fat);
794*d5c9a868SElliott Hughes if(This->freeSpace != MAX32)
795*d5c9a868SElliott Hughes This->freeSpace--;
796*d5c9a868SElliott Hughes }
797*d5c9a868SElliott Hughes
798*d5c9a868SElliott Hughes /* de-allocates the given cluster */
fatDeallocate(Fs_t * This,unsigned int pos)799*d5c9a868SElliott Hughes void fatDeallocate(Fs_t *This, unsigned int pos)
800*d5c9a868SElliott Hughes {
801*d5c9a868SElliott Hughes This->fat_encode(This, pos, 0);
802*d5c9a868SElliott Hughes if(This->freeSpace != MAX32)
803*d5c9a868SElliott Hughes This->freeSpace++;
804*d5c9a868SElliott Hughes }
805*d5c9a868SElliott Hughes
806*d5c9a868SElliott Hughes /* allocate a new cluster */
fatAllocate(Fs_t * This,unsigned int pos,unsigned int value)807*d5c9a868SElliott Hughes void fatAllocate(Fs_t *This, unsigned int pos, unsigned int value)
808*d5c9a868SElliott Hughes {
809*d5c9a868SElliott Hughes This->fat_encode(This, pos, value);
810*d5c9a868SElliott Hughes if(This->freeSpace != MAX32)
811*d5c9a868SElliott Hughes This->freeSpace--;
812*d5c9a868SElliott Hughes }
813*d5c9a868SElliott Hughes
fatEncode(Fs_t * This,unsigned int pos,unsigned int value)814*d5c9a868SElliott Hughes void fatEncode(Fs_t *This, unsigned int pos, unsigned int value)
815*d5c9a868SElliott Hughes {
816*d5c9a868SElliott Hughes unsigned int oldvalue = This->fat_decode(This, pos);
817*d5c9a868SElliott Hughes This->fat_encode(This, pos, value);
818*d5c9a868SElliott Hughes if(This->freeSpace != MAX32) {
819*d5c9a868SElliott Hughes if(oldvalue)
820*d5c9a868SElliott Hughes This->freeSpace++;
821*d5c9a868SElliott Hughes if(value)
822*d5c9a868SElliott Hughes This->freeSpace--;
823*d5c9a868SElliott Hughes }
824*d5c9a868SElliott Hughes }
825*d5c9a868SElliott Hughes
get_next_free_cluster(Fs_t * This,unsigned int last)826*d5c9a868SElliott Hughes unsigned int get_next_free_cluster(Fs_t *This, unsigned int last)
827*d5c9a868SElliott Hughes {
828*d5c9a868SElliott Hughes unsigned int i;
829*d5c9a868SElliott Hughes
830*d5c9a868SElliott Hughes if(This->last != MAX32)
831*d5c9a868SElliott Hughes last = This->last;
832*d5c9a868SElliott Hughes
833*d5c9a868SElliott Hughes if (last < 2 ||
834*d5c9a868SElliott Hughes last >= This->num_clus+1)
835*d5c9a868SElliott Hughes last = 1;
836*d5c9a868SElliott Hughes
837*d5c9a868SElliott Hughes for (i=last+1; i< This->num_clus+2; i++) {
838*d5c9a868SElliott Hughes unsigned int r = fatDecode(This, i);
839*d5c9a868SElliott Hughes if(r == 1)
840*d5c9a868SElliott Hughes goto exit_0;
841*d5c9a868SElliott Hughes if (!r) {
842*d5c9a868SElliott Hughes This->last = i;
843*d5c9a868SElliott Hughes return i;
844*d5c9a868SElliott Hughes }
845*d5c9a868SElliott Hughes }
846*d5c9a868SElliott Hughes
847*d5c9a868SElliott Hughes for(i=2; i < last+1; i++) {
848*d5c9a868SElliott Hughes unsigned int r = fatDecode(This, i);
849*d5c9a868SElliott Hughes if(r == 1)
850*d5c9a868SElliott Hughes goto exit_0;
851*d5c9a868SElliott Hughes if (!r) {
852*d5c9a868SElliott Hughes This->last = i;
853*d5c9a868SElliott Hughes return i;
854*d5c9a868SElliott Hughes }
855*d5c9a868SElliott Hughes }
856*d5c9a868SElliott Hughes
857*d5c9a868SElliott Hughes
858*d5c9a868SElliott Hughes fprintf(stderr,"No free cluster %d %d\n", This->preallocatedClusters,
859*d5c9a868SElliott Hughes This->last);
860*d5c9a868SElliott Hughes return 1;
861*d5c9a868SElliott Hughes exit_0:
862*d5c9a868SElliott Hughes fprintf(stderr, "FAT error\n");
863*d5c9a868SElliott Hughes return 1;
864*d5c9a868SElliott Hughes }
865*d5c9a868SElliott Hughes
getSerialized(Fs_t * Fs)866*d5c9a868SElliott Hughes bool getSerialized(Fs_t *Fs) {
867*d5c9a868SElliott Hughes return Fs->serialized;
868*d5c9a868SElliott Hughes }
869*d5c9a868SElliott Hughes
getSerialNumber(Fs_t * Fs)870*d5c9a868SElliott Hughes unsigned long getSerialNumber(Fs_t *Fs) {
871*d5c9a868SElliott Hughes return Fs->serial_number;
872*d5c9a868SElliott Hughes }
873*d5c9a868SElliott Hughes
getClusterBytes(Fs_t * Fs)874*d5c9a868SElliott Hughes uint32_t getClusterBytes(Fs_t *Fs) {
875*d5c9a868SElliott Hughes return Fs->cluster_size * Fs->sector_size;
876*d5c9a868SElliott Hughes }
877*d5c9a868SElliott Hughes
fat_error(Stream_t * Dir)878*d5c9a868SElliott Hughes int fat_error(Stream_t *Dir)
879*d5c9a868SElliott Hughes {
880*d5c9a868SElliott Hughes Stream_t *Stream = GetFs(Dir);
881*d5c9a868SElliott Hughes DeclareThis(Fs_t);
882*d5c9a868SElliott Hughes
883*d5c9a868SElliott Hughes if(This->fat_error)
884*d5c9a868SElliott Hughes fprintf(stderr,"Fat error detected\n");
885*d5c9a868SElliott Hughes
886*d5c9a868SElliott Hughes return This->fat_error;
887*d5c9a868SElliott Hughes }
888*d5c9a868SElliott Hughes
fat32RootCluster(Stream_t * Dir)889*d5c9a868SElliott Hughes uint32_t fat32RootCluster(Stream_t *Dir)
890*d5c9a868SElliott Hughes {
891*d5c9a868SElliott Hughes Stream_t *Stream = GetFs(Dir);
892*d5c9a868SElliott Hughes DeclareThis(Fs_t);
893*d5c9a868SElliott Hughes
894*d5c9a868SElliott Hughes if(This->fat_bits == 32)
895*d5c9a868SElliott Hughes return This->rootCluster;
896*d5c9a868SElliott Hughes else
897*d5c9a868SElliott Hughes return 0;
898*d5c9a868SElliott Hughes }
899*d5c9a868SElliott Hughes
900*d5c9a868SElliott Hughes
901*d5c9a868SElliott Hughes /*
902*d5c9a868SElliott Hughes * Get the amount of free space on the diskette
903*d5c9a868SElliott Hughes */
getfree(Stream_t * Dir)904*d5c9a868SElliott Hughes mt_off_t getfree(Stream_t *Dir)
905*d5c9a868SElliott Hughes {
906*d5c9a868SElliott Hughes Stream_t *Stream = GetFs(Dir);
907*d5c9a868SElliott Hughes DeclareThis(Fs_t);
908*d5c9a868SElliott Hughes
909*d5c9a868SElliott Hughes if(This->freeSpace == MAX32 || This->freeSpace == 0) {
910*d5c9a868SElliott Hughes register unsigned int i;
911*d5c9a868SElliott Hughes uint32_t total;
912*d5c9a868SElliott Hughes
913*d5c9a868SElliott Hughes total = 0L;
914*d5c9a868SElliott Hughes for (i = 2; i < This->num_clus + 2; i++) {
915*d5c9a868SElliott Hughes unsigned int r = fatDecode(This,i);
916*d5c9a868SElliott Hughes if(r == 1) {
917*d5c9a868SElliott Hughes return -1;
918*d5c9a868SElliott Hughes }
919*d5c9a868SElliott Hughes if (!r)
920*d5c9a868SElliott Hughes total++;
921*d5c9a868SElliott Hughes }
922*d5c9a868SElliott Hughes This->freeSpace = total;
923*d5c9a868SElliott Hughes }
924*d5c9a868SElliott Hughes return sectorsToBytes(This,
925*d5c9a868SElliott Hughes This->freeSpace * This->cluster_size);
926*d5c9a868SElliott Hughes }
927*d5c9a868SElliott Hughes
928*d5c9a868SElliott Hughes
929*d5c9a868SElliott Hughes /*
930*d5c9a868SElliott Hughes * Ensure that there is a minimum of total sectors free
931*d5c9a868SElliott Hughes */
getfreeMinClusters(Stream_t * Dir,uint32_t size)932*d5c9a868SElliott Hughes int getfreeMinClusters(Stream_t *Dir, uint32_t size)
933*d5c9a868SElliott Hughes {
934*d5c9a868SElliott Hughes Stream_t *Stream = GetFs(Dir);
935*d5c9a868SElliott Hughes DeclareThis(Fs_t);
936*d5c9a868SElliott Hughes register unsigned int i, last;
937*d5c9a868SElliott Hughes size_t total;
938*d5c9a868SElliott Hughes
939*d5c9a868SElliott Hughes if(batchmode && This->freeSpace == MAX32)
940*d5c9a868SElliott Hughes getfree(Stream);
941*d5c9a868SElliott Hughes
942*d5c9a868SElliott Hughes if(This->freeSpace != MAX32) {
943*d5c9a868SElliott Hughes if(This->freeSpace >= size)
944*d5c9a868SElliott Hughes return 1;
945*d5c9a868SElliott Hughes else {
946*d5c9a868SElliott Hughes fprintf(stderr, "Disk full\n");
947*d5c9a868SElliott Hughes got_signal = 1;
948*d5c9a868SElliott Hughes return 0;
949*d5c9a868SElliott Hughes }
950*d5c9a868SElliott Hughes }
951*d5c9a868SElliott Hughes
952*d5c9a868SElliott Hughes total = 0L;
953*d5c9a868SElliott Hughes
954*d5c9a868SElliott Hughes /* we start at the same place where we'll start later to actually
955*d5c9a868SElliott Hughes * allocate the sectors. That way, the same sectors of the FAT, which
956*d5c9a868SElliott Hughes * are already loaded during getfreeMin will be able to be reused
957*d5c9a868SElliott Hughes * during get_next_free_cluster */
958*d5c9a868SElliott Hughes last = This->last;
959*d5c9a868SElliott Hughes
960*d5c9a868SElliott Hughes if ( last < 2 || last >= This->num_clus + 2)
961*d5c9a868SElliott Hughes last = 1;
962*d5c9a868SElliott Hughes for (i=last+1; i< This->num_clus+2; i++){
963*d5c9a868SElliott Hughes unsigned int r = fatDecode(This, i);
964*d5c9a868SElliott Hughes if(r == 1) {
965*d5c9a868SElliott Hughes goto exit_0;
966*d5c9a868SElliott Hughes }
967*d5c9a868SElliott Hughes if (!r)
968*d5c9a868SElliott Hughes total++;
969*d5c9a868SElliott Hughes if(total >= size)
970*d5c9a868SElliott Hughes return 1;
971*d5c9a868SElliott Hughes }
972*d5c9a868SElliott Hughes for(i=2; i < last+1; i++){
973*d5c9a868SElliott Hughes unsigned int r = fatDecode(This, i);
974*d5c9a868SElliott Hughes if(r == 1) {
975*d5c9a868SElliott Hughes goto exit_0;
976*d5c9a868SElliott Hughes }
977*d5c9a868SElliott Hughes if (!r)
978*d5c9a868SElliott Hughes total++;
979*d5c9a868SElliott Hughes if(total >= size)
980*d5c9a868SElliott Hughes return 1;
981*d5c9a868SElliott Hughes }
982*d5c9a868SElliott Hughes fprintf(stderr, "Disk full\n");
983*d5c9a868SElliott Hughes got_signal = 1;
984*d5c9a868SElliott Hughes return 0;
985*d5c9a868SElliott Hughes exit_0:
986*d5c9a868SElliott Hughes fprintf(stderr, "FAT error\n");
987*d5c9a868SElliott Hughes return 0;
988*d5c9a868SElliott Hughes }
989*d5c9a868SElliott Hughes
990*d5c9a868SElliott Hughes
getfreeMinBytes(Stream_t * Dir,mt_off_t size)991*d5c9a868SElliott Hughes int getfreeMinBytes(Stream_t *Dir, mt_off_t size)
992*d5c9a868SElliott Hughes {
993*d5c9a868SElliott Hughes Stream_t *Stream = GetFs(Dir);
994*d5c9a868SElliott Hughes DeclareThis(Fs_t);
995*d5c9a868SElliott Hughes mt_off_t size2;
996*d5c9a868SElliott Hughes
997*d5c9a868SElliott Hughes size2 = size / (This->sector_size * This->cluster_size);
998*d5c9a868SElliott Hughes if(size % (This->sector_size * This->cluster_size))
999*d5c9a868SElliott Hughes size2++;
1000*d5c9a868SElliott Hughes if((smt_off_t)size2 > UINT32_MAX) {
1001*d5c9a868SElliott Hughes fprintf(stderr, "Requested size too big\n");
1002*d5c9a868SElliott Hughes exit(1);
1003*d5c9a868SElliott Hughes }
1004*d5c9a868SElliott Hughes return getfreeMinClusters(Dir, (uint32_t) size2);
1005*d5c9a868SElliott Hughes }
1006*d5c9a868SElliott Hughes
1007*d5c9a868SElliott Hughes
getStart(Stream_t * Dir,struct directory * dir)1008*d5c9a868SElliott Hughes uint32_t getStart(Stream_t *Dir, struct directory *dir)
1009*d5c9a868SElliott Hughes {
1010*d5c9a868SElliott Hughes Stream_t *Stream = GetFs(Dir);
1011*d5c9a868SElliott Hughes uint32_t first;
1012*d5c9a868SElliott Hughes
1013*d5c9a868SElliott Hughes first = START(dir);
1014*d5c9a868SElliott Hughes if(fat32RootCluster(Stream)) {
1015*d5c9a868SElliott Hughes first |= (uint32_t) STARTHI(dir) << 16;
1016*d5c9a868SElliott Hughes }
1017*d5c9a868SElliott Hughes return first;
1018*d5c9a868SElliott Hughes }
1019*d5c9a868SElliott Hughes
fs_free(Stream_t * Stream)1020*d5c9a868SElliott Hughes int fs_free(Stream_t *Stream)
1021*d5c9a868SElliott Hughes {
1022*d5c9a868SElliott Hughes DeclareThis(Fs_t);
1023*d5c9a868SElliott Hughes
1024*d5c9a868SElliott Hughes if(This->FatMap) {
1025*d5c9a868SElliott Hughes int i, nr_entries;
1026*d5c9a868SElliott Hughes nr_entries = (This->fat_len + SECT_PER_ENTRY - 1) /
1027*d5c9a868SElliott Hughes SECT_PER_ENTRY;
1028*d5c9a868SElliott Hughes for(i=0; i< nr_entries; i++)
1029*d5c9a868SElliott Hughes if(This->FatMap[i].data)
1030*d5c9a868SElliott Hughes free(This->FatMap[i].data);
1031*d5c9a868SElliott Hughes free(This->FatMap);
1032*d5c9a868SElliott Hughes }
1033*d5c9a868SElliott Hughes if(This->cp)
1034*d5c9a868SElliott Hughes cp_close(This->cp);
1035*d5c9a868SElliott Hughes return 0;
1036*d5c9a868SElliott Hughes }
1037