blob: 559d00b2b9c0e42872648bc2fc045e97e8773fe2 [file] [log] [blame]
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
Björn Stenbergd9eb5c72002-03-28 15:09:10 +000019#include <stdio.h>
20#include <string.h>
Björn Stenbergd9eb5c72002-03-28 15:09:10 +000021#include <stdlib.h>
22#include <ctype.h>
Björn Stenberg3ad66b92002-05-29 11:56:42 +000023#include <stdbool.h>
Björn Stenbergd9eb5c72002-03-28 15:09:10 +000024#include "fat.h"
Björn Stenberg0f532a02002-04-22 12:33:04 +000025#include "ata.h"
Björn Stenberg697dd702002-05-02 14:05:51 +000026#include "debug.h"
Björn Stenbergb7b48fe2002-10-20 22:50:58 +000027#include "panic.h"
Linus Nielsen Feltzing8083e7a2002-05-09 22:57:54 +000028#include "system.h"
Björn Stenbergb5184d72002-11-22 23:51:46 +000029#include "timefuncs.h"
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +000030#include "kernel.h"
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +000031#include "rbunicode.h"
Björn Stenbergd9eb5c72002-03-28 15:09:10 +000032
Björn Stenberg1dff4b62002-04-26 16:44:58 +000033#define BYTES2INT16(array,pos) \
34 (array[pos] | (array[pos+1] << 8 ))
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +000035#define BYTES2INT32(array,pos) \
36 ((long)array[pos] | ((long)array[pos+1] << 8 ) | \
37 ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
Björn Stenberg1dff4b62002-04-26 16:44:58 +000038
Björn Stenberg5661b232002-04-27 01:25:22 +000039#define FATTYPE_FAT12 0
40#define FATTYPE_FAT16 1
41#define FATTYPE_FAT32 2
42
43/* BPB offsets; generic */
44#define BS_JMPBOOT 0
45#define BS_OEMNAME 3
46#define BPB_BYTSPERSEC 11
47#define BPB_SECPERCLUS 13
48#define BPB_RSVDSECCNT 14
49#define BPB_NUMFATS 16
50#define BPB_ROOTENTCNT 17
51#define BPB_TOTSEC16 19
52#define BPB_MEDIA 21
53#define BPB_FATSZ16 22
54#define BPB_SECPERTRK 24
55#define BPB_NUMHEADS 26
56#define BPB_HIDDSEC 28
57#define BPB_TOTSEC32 32
58
59/* fat12/16 */
60#define BS_DRVNUM 36
61#define BS_RESERVED1 37
62#define BS_BOOTSIG 38
63#define BS_VOLID 39
64#define BS_VOLLAB 43
65#define BS_FILSYSTYPE 54
66
67/* fat32 */
68#define BPB_FATSZ32 36
69#define BPB_EXTFLAGS 40
70#define BPB_FSVER 42
71#define BPB_ROOTCLUS 44
72#define BPB_FSINFO 48
73#define BPB_BKBOOTSEC 50
74#define BS_32_DRVNUM 64
75#define BS_32_BOOTSIG 66
76#define BS_32_VOLID 67
77#define BS_32_VOLLAB 71
78#define BS_32_FILSYSTYPE 82
79
80#define BPB_LAST_WORD 510
81
82
83/* attributes */
84#define FAT_ATTR_LONG_NAME (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \
85 FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID)
86#define FAT_ATTR_LONG_NAME_MASK (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \
87 FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID | \
88 FAT_ATTR_DIRECTORY | FAT_ATTR_ARCHIVE )
Jens Arnolda07c7352005-04-22 22:26:13 +000089
90/* NTRES flags */
91#define FAT_NTRES_LC_NAME 0x08
92#define FAT_NTRES_LC_EXT 0x10
Björn Stenberg5661b232002-04-27 01:25:22 +000093
94#define FATDIR_NAME 0
95#define FATDIR_ATTR 11
96#define FATDIR_NTRES 12
97#define FATDIR_CRTTIMETENTH 13
98#define FATDIR_CRTTIME 14
99#define FATDIR_CRTDATE 16
100#define FATDIR_LSTACCDATE 18
101#define FATDIR_FSTCLUSHI 20
102#define FATDIR_WRTTIME 22
103#define FATDIR_WRTDATE 24
104#define FATDIR_FSTCLUSLO 26
105#define FATDIR_FILESIZE 28
106
Björn Stenberg7aabb1a2002-11-15 16:41:02 +0000107#define FATLONG_ORDER 0
Björn Stenberg7aabb1a2002-11-15 16:41:02 +0000108#define FATLONG_TYPE 12
109#define FATLONG_CHKSUM 13
110
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000111#define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4)
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000112#define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2)
Björn Stenberg1f214f22002-11-13 23:16:32 +0000113#define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / DIR_ENTRY_SIZE)
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000114#define DIR_ENTRY_SIZE 32
Björn Stenberg7aabb1a2002-11-15 16:41:02 +0000115#define NAME_BYTES_PER_ENTRY 13
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000116#define FAT_BAD_MARK 0x0ffffff7
117#define FAT_EOF_MARK 0x0ffffff8
118
Björn Stenberg5661b232002-04-27 01:25:22 +0000119struct fsinfo {
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000120 unsigned long freecount; /* last known free cluster count */
121 unsigned long nextfree; /* first cluster to start looking for free
Björn Stenbergc442a682002-11-15 11:20:33 +0000122 clusters, or 0xffffffff for no hint */
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000123};
Björn Stenberg5661b232002-04-27 01:25:22 +0000124/* fsinfo offsets */
125#define FSINFO_FREECOUNT 488
126#define FSINFO_NEXTFREE 492
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000127
Björn Stenberg827cee32002-05-30 19:47:56 +0000128struct bpb
129{
Jörg Hohensohnfb0739b2004-10-14 07:52:08 +0000130 int bpb_bytspersec; /* Bytes per sector, typically 512 */
Björn Stenbergc442a682002-11-15 11:20:33 +0000131 unsigned int bpb_secperclus; /* Sectors per cluster */
Björn Stenberg827cee32002-05-30 19:47:56 +0000132 int bpb_rsvdseccnt; /* Number of reserved sectors */
133 int bpb_numfats; /* Number of FAT structures, typically 2 */
Björn Stenberg827cee32002-05-30 19:47:56 +0000134 int bpb_totsec16; /* Number of sectors on the volume (old 16-bit) */
135 int bpb_media; /* Media type (typically 0xf0 or 0xf8) */
136 int bpb_fatsz16; /* Number of used sectors per FAT structure */
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000137 unsigned long bpb_totsec32; /* Number of sectors on the volume
Björn Stenberg827cee32002-05-30 19:47:56 +0000138 (new 32-bit) */
Jean-Philippe Bernardy34d2a712005-01-24 14:26:24 +0000139 unsigned int last_word; /* 0xAA55 */
Björn Stenberg827cee32002-05-30 19:47:56 +0000140
Björn Stenberg827cee32002-05-30 19:47:56 +0000141 /**** FAT32 specific *****/
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000142 long bpb_fatsz32;
143 long bpb_rootclus;
144 long bpb_fsinfo;
Björn Stenberg827cee32002-05-30 19:47:56 +0000145
146 /* variables for internal use */
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000147 unsigned long fatsize;
148 unsigned long totalsectors;
149 unsigned long rootdirsector;
150 unsigned long firstdatasector;
151 unsigned long startsector;
152 unsigned long dataclusters;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000153 struct fsinfo fsinfo;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000154#ifdef HAVE_FAT16SUPPORT
Jörg Hohensohnfb0739b2004-10-14 07:52:08 +0000155 int bpb_rootentcnt; /* Number of dir entries in the root */
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000156 /* internals for FAT16 support */
157 bool is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */
Jens Arnold316ae182005-01-03 07:59:49 +0000158 unsigned int rootdiroffset; /* sector offset of root dir relative to start
159 * of first pseudo cluster */
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000160#endif /* #ifdef HAVE_FAT16SUPPORT */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000161#ifdef HAVE_MULTIVOLUME
162 int drive; /* on which physical device is this located */
163 bool mounted; /* flag if this volume is mounted */
164#endif
Björn Stenberg827cee32002-05-30 19:47:56 +0000165};
166
Jörg Hohensohnda848572004-12-28 22:16:07 +0000167static struct bpb fat_bpbs[NUM_VOLUMES]; /* mounted partition info */
Björn Stenberg827cee32002-05-30 19:47:56 +0000168
Jörg Hohensohnda848572004-12-28 22:16:07 +0000169static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb));
Jens Arnoldd851d982005-01-05 00:42:24 +0000170static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb));
Jörg Hohensohnda848572004-12-28 22:16:07 +0000171static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb));
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000172static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,) long secnum, bool dirty);
Jens Arnolde4e1f2c2005-04-23 13:15:25 +0000173static void create_dos_name(const unsigned char *name, unsigned char *newname);
Jens Arnolddbf7f512005-02-16 20:45:56 +0000174static void randomize_dos_name(unsigned char *name);
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000175static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,) unsigned long start);
176static int transfer(IF_MV2(struct bpb* fat_bpb,) unsigned long start, long count, char* buf, bool write );
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000177
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000178#define FAT_CACHE_SIZE 0x20
179#define FAT_CACHE_MASK (FAT_CACHE_SIZE-1)
180
181struct fat_cache_entry
182{
Jean-Philippe Bernardy907ba582005-02-25 09:11:29 +0000183 long secnum;
Björn Stenberg3ad66b92002-05-29 11:56:42 +0000184 bool inuse;
185 bool dirty;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000186#ifdef HAVE_MULTIVOLUME
187 struct bpb* fat_vol ; /* shared cache for all volumes */
188#endif
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000189};
190
Björn Stenberg3ad66b92002-05-29 11:56:42 +0000191static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE];
192static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE];
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000193static struct mutex cache_mutex;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000194
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000195static long cluster2sec(IF_MV2(struct bpb* fat_bpb,) long cluster)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000196{
Jörg Hohensohnda848572004-12-28 22:16:07 +0000197#ifndef HAVE_MULTIVOLUME
198 struct bpb* fat_bpb = &fat_bpbs[0];
199#endif
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000200#ifdef HAVE_FAT16SUPPORT
201 /* negative clusters (FAT16 root dir) don't get the 2 offset */
202 int zerocluster = cluster < 0 ? 0 : 2;
203#else
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000204 const long zerocluster = 2;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000205#endif
Jens Arnold2580cdf2005-02-03 22:58:50 +0000206
207 if (cluster > (long)(fat_bpb->dataclusters + 1))
Jens Arnold316ae182005-01-03 07:59:49 +0000208 {
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000209 DEBUGF( "cluster2sec() - Bad cluster number (%ld)\n", cluster);
Jens Arnold316ae182005-01-03 07:59:49 +0000210 return -1;
211 }
212
213 return (cluster - zerocluster) * fat_bpb->bpb_secperclus
214 + fat_bpb->firstdatasector;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000215}
216
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000217long fat_startsector(IF_MV_NONVOID(int volume))
Björn Stenberg113fffa2002-08-13 13:21:55 +0000218{
Jörg Hohensohnda848572004-12-28 22:16:07 +0000219#ifndef HAVE_MULTIVOLUME
220 const int volume = 0;
221#endif
222 struct bpb* fat_bpb = &fat_bpbs[volume];
223 return fat_bpb->startsector;
Björn Stenberg113fffa2002-08-13 13:21:55 +0000224}
225
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000226void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free)
Björn Stenberg6fb512a2002-11-12 11:32:26 +0000227{
Jörg Hohensohnda848572004-12-28 22:16:07 +0000228#ifndef HAVE_MULTIVOLUME
229 const int volume = 0;
230#endif
231 struct bpb* fat_bpb = &fat_bpbs[volume];
Björn Stenberg6fb512a2002-11-12 11:32:26 +0000232 if (size)
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000233 *size = fat_bpb->dataclusters * fat_bpb->bpb_secperclus / 2;
Björn Stenberg6fb512a2002-11-12 11:32:26 +0000234 if (free)
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000235 *free = fat_bpb->fsinfo.freecount * fat_bpb->bpb_secperclus / 2;
Björn Stenberg6fb512a2002-11-12 11:32:26 +0000236}
237
Jörg Hohensohnda848572004-12-28 22:16:07 +0000238void fat_init(void)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000239{
Björn Stenbergc442a682002-11-15 11:20:33 +0000240 unsigned int i;
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000241
242 mutex_init(&cache_mutex);
243
Jörg Hohensohnda848572004-12-28 22:16:07 +0000244 /* mark the FAT cache as unused */
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000245 for(i = 0;i < FAT_CACHE_SIZE;i++)
246 {
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000247 fat_cache[i].secnum = 8; /* We use a "safe" sector just in case */
Björn Stenberg3ad66b92002-05-29 11:56:42 +0000248 fat_cache[i].inuse = false;
249 fat_cache[i].dirty = false;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000250#ifdef HAVE_MULTIVOLUME
251 fat_cache[i].fat_vol = NULL;
252#endif
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000253 }
Jörg Hohensohnda848572004-12-28 22:16:07 +0000254#ifdef HAVE_MULTIVOLUME
255 /* mark the possible volumes as not mounted */
256 for (i=0; i<NUM_VOLUMES;i++)
257 {
258 fat_bpbs[i].mounted = false;
259 }
260#endif
261}
262
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000263int fat_mount(IF_MV2(int volume,) IF_MV2(int drive,) long startsector)
Jörg Hohensohnda848572004-12-28 22:16:07 +0000264{
265#ifndef HAVE_MULTIVOLUME
266 const int volume = 0;
267#endif
268 struct bpb* fat_bpb = &fat_bpbs[volume];
269 unsigned char buf[SECTOR_SIZE];
270 int rc;
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000271 long datasec;
Jens Arnold316ae182005-01-03 07:59:49 +0000272#ifdef HAVE_FAT16SUPPORT
273 int rootdirsectors;
274#endif
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000275
276 /* Read the sector */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000277 rc = ata_read_sectors(IF_MV2(drive,) startsector,1,buf);
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000278 if(rc)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000279 {
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000280 DEBUGF( "fat_mount() - Couldn't read BPB (error code %d)\n", rc);
281 return rc * 10 - 1;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000282 }
283
Jörg Hohensohnda848572004-12-28 22:16:07 +0000284 memset(fat_bpb, 0, sizeof(struct bpb));
Jens Arnold316ae182005-01-03 07:59:49 +0000285 fat_bpb->startsector = startsector;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000286#ifdef HAVE_MULTIVOLUME
287 fat_bpb->drive = drive;
288#endif
289
290 fat_bpb->bpb_bytspersec = BYTES2INT16(buf,BPB_BYTSPERSEC);
291 fat_bpb->bpb_secperclus = buf[BPB_SECPERCLUS];
292 fat_bpb->bpb_rsvdseccnt = BYTES2INT16(buf,BPB_RSVDSECCNT);
293 fat_bpb->bpb_numfats = buf[BPB_NUMFATS];
294 fat_bpb->bpb_totsec16 = BYTES2INT16(buf,BPB_TOTSEC16);
295 fat_bpb->bpb_media = buf[BPB_MEDIA];
296 fat_bpb->bpb_fatsz16 = BYTES2INT16(buf,BPB_FATSZ16);
297 fat_bpb->bpb_fatsz32 = BYTES2INT32(buf,BPB_FATSZ32);
298 fat_bpb->bpb_totsec32 = BYTES2INT32(buf,BPB_TOTSEC32);
299 fat_bpb->last_word = BYTES2INT16(buf,BPB_LAST_WORD);
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000300
Björn Stenberg5661b232002-04-27 01:25:22 +0000301 /* calculate a few commonly used values */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000302 if (fat_bpb->bpb_fatsz16 != 0)
303 fat_bpb->fatsize = fat_bpb->bpb_fatsz16;
Björn Stenberg5661b232002-04-27 01:25:22 +0000304 else
Jörg Hohensohnda848572004-12-28 22:16:07 +0000305 fat_bpb->fatsize = fat_bpb->bpb_fatsz32;
Björn Stenberg5661b232002-04-27 01:25:22 +0000306
Jörg Hohensohnda848572004-12-28 22:16:07 +0000307 if (fat_bpb->bpb_totsec16 != 0)
308 fat_bpb->totalsectors = fat_bpb->bpb_totsec16;
Björn Stenberg5661b232002-04-27 01:25:22 +0000309 else
Jörg Hohensohnda848572004-12-28 22:16:07 +0000310 fat_bpb->totalsectors = fat_bpb->bpb_totsec32;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000311
312#ifdef HAVE_FAT16SUPPORT
Jörg Hohensohnda848572004-12-28 22:16:07 +0000313 fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT);
Jens Arnold316ae182005-01-03 07:59:49 +0000314 rootdirsectors = ((fat_bpb->bpb_rootentcnt * 32)
Jörg Hohensohnda848572004-12-28 22:16:07 +0000315 + (fat_bpb->bpb_bytspersec - 1)) / fat_bpb->bpb_bytspersec;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000316#endif /* #ifdef HAVE_FAT16SUPPORT */
317
Jörg Hohensohnda848572004-12-28 22:16:07 +0000318 fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000319#ifdef HAVE_FAT16SUPPORT
Jens Arnold316ae182005-01-03 07:59:49 +0000320 + rootdirsectors
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000321#endif
Jörg Hohensohnda848572004-12-28 22:16:07 +0000322 + fat_bpb->bpb_numfats * fat_bpb->fatsize;
Björn Stenberg5661b232002-04-27 01:25:22 +0000323
324 /* Determine FAT type */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000325 datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector;
326 fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus;
Björn Stenberg5661b232002-04-27 01:25:22 +0000327
328#ifdef TEST_FAT
329 /*
330 we are sometimes testing with "illegally small" fat32 images,
331 so we don't use the proper fat32 test case for test code
332 */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000333 if ( fat_bpb->bpb_fatsz16 )
Björn Stenberg5661b232002-04-27 01:25:22 +0000334#else
Jörg Hohensohnda848572004-12-28 22:16:07 +0000335 if ( fat_bpb->dataclusters < 65525 )
Björn Stenberg5661b232002-04-27 01:25:22 +0000336#endif
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000337 { /* FAT16 */
338#ifdef HAVE_FAT16SUPPORT
Jörg Hohensohnda848572004-12-28 22:16:07 +0000339 fat_bpb->is_fat16 = true;
340 if (fat_bpb->dataclusters < 4085)
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000341 { /* FAT12 */
342 DEBUGF("This is FAT12. Go away!\n");
343 return -2;
344 }
345#else /* #ifdef HAVE_FAT16SUPPORT */
Björn Stenberg697dd702002-05-02 14:05:51 +0000346 DEBUGF("This is not FAT32. Go away!\n");
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000347 return -2;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000348#endif /* #ifndef HAVE_FAT16SUPPORT */
Björn Stenberg5661b232002-04-27 01:25:22 +0000349 }
350
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000351#ifdef HAVE_FAT16SUPPORT
Jörg Hohensohnda848572004-12-28 22:16:07 +0000352 if (fat_bpb->is_fat16)
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000353 { /* FAT16 specific part of BPB */
354 int dirclusters;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000355 fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt
356 + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16;
Jens Arnold316ae182005-01-03 07:59:49 +0000357 dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1)
358 / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000359 /* I assign negative pseudo cluster numbers for the root directory,
360 their range is counted upward until -1. */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000361 fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data */
Jens Arnold316ae182005-01-03 07:59:49 +0000362 fat_bpb->rootdiroffset = dirclusters * fat_bpb->bpb_secperclus
363 - rootdirsectors;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000364 }
365 else
366#endif /* #ifdef HAVE_FAT16SUPPORT */
367 { /* FAT32 specific part of BPB */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000368 fat_bpb->bpb_rootclus = BYTES2INT32(buf,BPB_ROOTCLUS);
369 fat_bpb->bpb_fsinfo = BYTES2INT16(buf,BPB_FSINFO);
370 fat_bpb->rootdirsector = cluster2sec(IF_MV2(fat_bpb,) fat_bpb->bpb_rootclus);
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000371 }
372
Jörg Hohensohnda848572004-12-28 22:16:07 +0000373 rc = bpb_is_sane(IF_MV(fat_bpb));
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000374 if (rc < 0)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000375 {
Björn Stenberg697dd702002-05-02 14:05:51 +0000376 DEBUGF( "fat_mount() - BPB is not sane\n");
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000377 return rc * 10 - 3;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000378 }
379
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000380#ifdef HAVE_FAT16SUPPORT
Jörg Hohensohnda848572004-12-28 22:16:07 +0000381 if (fat_bpb->is_fat16)
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000382 {
Jörg Hohensohnda848572004-12-28 22:16:07 +0000383 fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc below */
384 fat_bpb->fsinfo.nextfree = 0xffffffff;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000385 }
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000386 else
387#endif /* #ifdef HAVE_FAT16SUPPORT */
388 {
389 /* Read the fsinfo sector */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000390 rc = ata_read_sectors(IF_MV2(drive,)
391 startsector + fat_bpb->bpb_fsinfo, 1, buf);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000392 if (rc < 0)
393 {
394 DEBUGF( "fat_mount() - Couldn't read FSInfo (error code %d)\n", rc);
395 return rc * 10 - 4;
396 }
Jörg Hohensohnda848572004-12-28 22:16:07 +0000397 fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT);
398 fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000399 }
Björn Stenbergc442a682002-11-15 11:20:33 +0000400
401 /* calculate freecount if unset */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000402 if ( fat_bpb->fsinfo.freecount == 0xffffffff )
Björn Stenbergc442a682002-11-15 11:20:33 +0000403 {
Jörg Hohensohnda848572004-12-28 22:16:07 +0000404 fat_recalc_free(IF_MV(volume));
Björn Stenbergc442a682002-11-15 11:20:33 +0000405 }
406
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000407 LDEBUGF("Freecount: %ld\n",fat_bpb->fsinfo.freecount);
408 LDEBUGF("Nextfree: 0x%lx\n",fat_bpb->fsinfo.nextfree);
409 LDEBUGF("Cluster count: 0x%lx\n",fat_bpb->dataclusters);
Jörg Hohensohnda848572004-12-28 22:16:07 +0000410 LDEBUGF("Sectors per cluster: %d\n",fat_bpb->bpb_secperclus);
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000411 LDEBUGF("FAT sectors: 0x%lx\n",fat_bpb->fatsize);
Jörg Hohensohnda848572004-12-28 22:16:07 +0000412
413#ifdef HAVE_MULTIVOLUME
414 fat_bpb->mounted = true;
415#endif
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000416
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000417 return 0;
418}
419
Jörg Hohensohndc7534b2005-01-28 21:32:16 +0000420#ifdef HAVE_HOTSWAP
Jörg Hohensohn74146872005-01-05 00:09:04 +0000421int fat_unmount(int volume, bool flush)
422{
Jens Arnoldd851d982005-01-05 00:42:24 +0000423 int rc;
Jörg Hohensohn74146872005-01-05 00:09:04 +0000424 struct bpb* fat_bpb = &fat_bpbs[volume];
Jens Arnoldd851d982005-01-05 00:42:24 +0000425
Jörg Hohensohn74146872005-01-05 00:09:04 +0000426 if(flush)
427 {
Jens Arnoldd851d982005-01-05 00:42:24 +0000428 rc = flush_fat(fat_bpb); /* the clean way, while still alive */
Jörg Hohensohn74146872005-01-05 00:09:04 +0000429 }
430 else
431 { /* volume is not accessible any more, e.g. MMC removed */
432 int i;
433 mutex_lock(&cache_mutex);
434 for(i = 0;i < FAT_CACHE_SIZE;i++)
435 {
436 struct fat_cache_entry *fce = &fat_cache[i];
437 if(fce->inuse && fce->fat_vol == fat_bpb)
438 {
439 fce->inuse = false; /* discard all from that volume */
440 fce->dirty = false;
441 }
442 }
443 mutex_unlock(&cache_mutex);
Jens Arnoldd851d982005-01-05 00:42:24 +0000444 rc = 0;
Jörg Hohensohn74146872005-01-05 00:09:04 +0000445 }
446 fat_bpb->mounted = false;
Jens Arnoldd851d982005-01-05 00:42:24 +0000447 return rc;
Jörg Hohensohn74146872005-01-05 00:09:04 +0000448}
Jörg Hohensohndc7534b2005-01-28 21:32:16 +0000449#endif /* #ifdef HAVE_HOTSWAP */
Jörg Hohensohn74146872005-01-05 00:09:04 +0000450
Jörg Hohensohnda848572004-12-28 22:16:07 +0000451void fat_recalc_free(IF_MV_NONVOID(int volume))
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000452{
Jörg Hohensohnda848572004-12-28 22:16:07 +0000453#ifndef HAVE_MULTIVOLUME
454 const int volume = 0;
455#endif
456 struct bpb* fat_bpb = &fat_bpbs[volume];
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000457 long free = 0;
458 unsigned long i;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000459#ifdef HAVE_FAT16SUPPORT
Jörg Hohensohnda848572004-12-28 22:16:07 +0000460 if (fat_bpb->is_fat16)
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000461 {
Jörg Hohensohnda848572004-12-28 22:16:07 +0000462 for (i = 0; i<fat_bpb->fatsize; i++) {
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000463 unsigned int j;
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000464 unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000465 for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) {
466 unsigned int c = i * CLUSTERS_PER_FAT16_SECTOR + j;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000467 if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000468 break;
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000469
Dave Chapman9e19c952005-10-06 19:27:43 +0000470 if (letoh16(fat[j]) == 0x0000) {
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000471 free++;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000472 if ( fat_bpb->fsinfo.nextfree == 0xffffffff )
473 fat_bpb->fsinfo.nextfree = c;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000474 }
475 }
476 }
477 }
478 else
479#endif /* #ifdef HAVE_FAT16SUPPORT */
480 {
Jörg Hohensohnda848572004-12-28 22:16:07 +0000481 for (i = 0; i<fat_bpb->fatsize; i++) {
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000482 unsigned int j;
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000483 unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000484 for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) {
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000485 unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000486 if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000487 break;
488
Dave Chapman9e19c952005-10-06 19:27:43 +0000489 if (!(letoh32(fat[j]) & 0x0fffffff)) {
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000490 free++;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000491 if ( fat_bpb->fsinfo.nextfree == 0xffffffff )
492 fat_bpb->fsinfo.nextfree = c;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000493 }
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000494 }
495 }
496 }
Jörg Hohensohnda848572004-12-28 22:16:07 +0000497 fat_bpb->fsinfo.freecount = free;
498 update_fsinfo(IF_MV(fat_bpb));
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000499}
500
Jörg Hohensohnda848572004-12-28 22:16:07 +0000501static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb))
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000502{
Jörg Hohensohnda848572004-12-28 22:16:07 +0000503#ifndef HAVE_MULTIVOLUME
504 struct bpb* fat_bpb = &fat_bpbs[0];
505#endif
506 if(fat_bpb->bpb_bytspersec != 512)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000507 {
Linus Nielsen Feltzing3fd7fe92002-05-08 08:26:38 +0000508 DEBUGF( "bpb_is_sane() - Error: sector size is not 512 (%d)\n",
Jörg Hohensohnda848572004-12-28 22:16:07 +0000509 fat_bpb->bpb_bytspersec);
Björn Stenberge8bcc012002-04-27 19:37:41 +0000510 return -1;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000511 }
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000512 if((long)fat_bpb->bpb_secperclus * (long)fat_bpb->bpb_bytspersec > 128L*1024L)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000513 {
Björn Stenberg08356fb2002-10-31 19:05:25 +0000514 DEBUGF( "bpb_is_sane() - Error: cluster size is larger than 128K "
Linus Nielsen Feltzing3fd7fe92002-05-08 08:26:38 +0000515 "(%d * %d = %d)\n",
Jörg Hohensohnda848572004-12-28 22:16:07 +0000516 fat_bpb->bpb_bytspersec, fat_bpb->bpb_secperclus,
517 fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus);
Björn Stenberg08356fb2002-10-31 19:05:25 +0000518 return -2;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000519 }
Jörg Hohensohnda848572004-12-28 22:16:07 +0000520 if(fat_bpb->bpb_numfats != 2)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000521 {
Linus Nielsen Feltzing3fd7fe92002-05-08 08:26:38 +0000522 DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)\n",
Jörg Hohensohnda848572004-12-28 22:16:07 +0000523 fat_bpb->bpb_numfats);
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000524 }
Jörg Hohensohnda848572004-12-28 22:16:07 +0000525 if(fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000526 {
Björn Stenberg697dd702002-05-02 14:05:51 +0000527 DEBUGF( "bpb_is_sane() - Warning: Non-standard "
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000528 "media type (0x%02x)\n",
Jörg Hohensohnda848572004-12-28 22:16:07 +0000529 fat_bpb->bpb_media);
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000530 }
Jörg Hohensohnda848572004-12-28 22:16:07 +0000531 if(fat_bpb->last_word != 0xaa55)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000532 {
Björn Stenberg697dd702002-05-02 14:05:51 +0000533 DEBUGF( "bpb_is_sane() - Error: Last word is not "
Jörg Hohensohnda848572004-12-28 22:16:07 +0000534 "0xaa55 (0x%04x)\n", fat_bpb->last_word);
Björn Stenberg08356fb2002-10-31 19:05:25 +0000535 return -3;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000536 }
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000537
Jörg Hohensohnda848572004-12-28 22:16:07 +0000538 if (fat_bpb->fsinfo.freecount >
539 (fat_bpb->totalsectors - fat_bpb->firstdatasector)/
540 fat_bpb->bpb_secperclus)
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000541 {
542 DEBUGF( "bpb_is_sane() - Error: FSInfo.Freecount > disk size "
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000543 "(0x%04lx)\n", fat_bpb->fsinfo.freecount);
Björn Stenberg08356fb2002-10-31 19:05:25 +0000544 return -4;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000545 }
546
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000547 return 0;
548}
549
Jörg Hohensohnda848572004-12-28 22:16:07 +0000550static void flush_fat_sector(struct fat_cache_entry *fce,
551 unsigned char *sectorbuf)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000552{
Jörg Hohensohnda848572004-12-28 22:16:07 +0000553 int rc;
Jean-Philippe Bernardy907ba582005-02-25 09:11:29 +0000554 long secnum;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000555
556 /* With multivolume, use only the FAT info from the cached sector! */
557#ifdef HAVE_MULTIVOLUME
558 secnum = fce->secnum + fce->fat_vol->startsector;
559#else
560 secnum = fce->secnum + fat_bpbs[0].startsector;
561#endif
562
563 /* Write to the first FAT */
564 rc = ata_write_sectors(IF_MV2(fce->fat_vol->drive,)
565 secnum, 1,
566 sectorbuf);
567 if(rc < 0)
568 {
Jean-Philippe Bernardyfc194452005-02-25 18:50:16 +0000569 panicf("flush_fat_sector() - Could not write sector %ld"
Jörg Hohensohnda848572004-12-28 22:16:07 +0000570 " (error %d)\n",
571 secnum, rc);
572 }
573#ifdef HAVE_MULTIVOLUME
574 if(fce->fat_vol->bpb_numfats > 1)
575#else
576 if(fat_bpbs[0].bpb_numfats > 1)
577#endif
578 {
579 /* Write to the second FAT */
580#ifdef HAVE_MULTIVOLUME
581 secnum += fce->fat_vol->fatsize;
582#else
583 secnum += fat_bpbs[0].fatsize;
584#endif
585 rc = ata_write_sectors(IF_MV2(fce->fat_vol->drive,)
586 secnum, 1, sectorbuf);
587 if(rc < 0)
588 {
Jean-Philippe Bernardyfc194452005-02-25 18:50:16 +0000589 panicf("flush_fat_sector() - Could not write sector %ld"
Jörg Hohensohnda848572004-12-28 22:16:07 +0000590 " (error %d)\n",
591 secnum, rc);
592 }
593 }
594 fce->dirty = false;
595}
596
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000597/* Note: The returned pointer is only safely valid until the next
598 task switch! (Any subsequent ata read/write may yield.) */
599static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,)
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000600 long fatsector, bool dirty)
Jörg Hohensohnda848572004-12-28 22:16:07 +0000601{
602#ifndef HAVE_MULTIVOLUME
603 struct bpb* fat_bpb = &fat_bpbs[0];
604#endif
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000605 long secnum = fatsector + fat_bpb->bpb_rsvdseccnt;
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000606 int cache_index = secnum & FAT_CACHE_MASK;
Linus Nielsen Feltzing4382c682002-11-09 09:23:43 +0000607 struct fat_cache_entry *fce = &fat_cache[cache_index];
608 unsigned char *sectorbuf = &fat_cache_sectors[cache_index][0];
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000609 int rc;
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000610
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000611 mutex_lock(&cache_mutex); /* make changes atomic */
612
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000613 /* Delete the cache entry if it isn't the sector we want */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000614 if(fce->inuse && (fce->secnum != secnum
615#ifdef HAVE_MULTIVOLUME
616 || fce->fat_vol != fat_bpb
617#endif
618 ))
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000619 {
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000620 /* Write back if it is dirty */
Linus Nielsen Feltzing4382c682002-11-09 09:23:43 +0000621 if(fce->dirty)
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000622 {
Jörg Hohensohnda848572004-12-28 22:16:07 +0000623 flush_fat_sector(fce, sectorbuf);
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000624 }
Linus Nielsen Feltzing4382c682002-11-09 09:23:43 +0000625 fce->inuse = false;
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000626 }
Linus Nielsen Feltzing4382c682002-11-09 09:23:43 +0000627
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000628 /* Load the sector if it is not cached */
Linus Nielsen Feltzing4382c682002-11-09 09:23:43 +0000629 if(!fce->inuse)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000630 {
Jens Arnold316ae182005-01-03 07:59:49 +0000631 rc = ata_read_sectors(IF_MV2(fat_bpb->drive,)
Jörg Hohensohnda848572004-12-28 22:16:07 +0000632 secnum + fat_bpb->startsector,1,
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000633 sectorbuf);
634 if(rc < 0)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000635 {
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000636 DEBUGF( "cache_fat_sector() - Could not read sector %ld"
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000637 " (error %d)\n", secnum, rc);
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000638 mutex_unlock(&cache_mutex);
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000639 return NULL;
640 }
Linus Nielsen Feltzing4382c682002-11-09 09:23:43 +0000641 fce->inuse = true;
642 fce->secnum = secnum;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000643#ifdef HAVE_MULTIVOLUME
644 fce->fat_vol = fat_bpb;
645#endif
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000646 }
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000647 if (dirty)
648 fce->dirty = true; /* dirt remains, sticky until flushed */
649 mutex_unlock(&cache_mutex);
Linus Nielsen Feltzing4382c682002-11-09 09:23:43 +0000650 return sectorbuf;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000651}
652
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000653static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,) unsigned long startcluster)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000654{
Jörg Hohensohnda848572004-12-28 22:16:07 +0000655#ifndef HAVE_MULTIVOLUME
656 struct bpb* fat_bpb = &fat_bpbs[0];
657#endif
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000658 unsigned long sector;
659 unsigned long offset;
660 unsigned long i;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000661
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000662#ifdef HAVE_FAT16SUPPORT
Jörg Hohensohnda848572004-12-28 22:16:07 +0000663 if (fat_bpb->is_fat16)
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000664 {
665 sector = startcluster / CLUSTERS_PER_FAT16_SECTOR;
666 offset = startcluster % CLUSTERS_PER_FAT16_SECTOR;
Linus Nielsen Feltzing41249b72003-07-16 22:28:24 +0000667
Jörg Hohensohnda848572004-12-28 22:16:07 +0000668 for (i = 0; i<fat_bpb->fatsize; i++) {
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000669 unsigned int j;
Jörg Hohensohnda848572004-12-28 22:16:07 +0000670 unsigned int nr = (i + sector) % fat_bpb->fatsize;
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000671 unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000672 if ( !fat )
673 break;
674 for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) {
675 int k = (j + offset) % CLUSTERS_PER_FAT16_SECTOR;
Dave Chapman9e19c952005-10-06 19:27:43 +0000676 if (letoh16(fat[k]) == 0x0000) {
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000677 unsigned int c = nr * CLUSTERS_PER_FAT16_SECTOR + k;
678 /* Ignore the reserved clusters 0 & 1, and also
679 cluster numbers out of bounds */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000680 if ( c < 2 || c > fat_bpb->dataclusters+1 )
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000681 continue;
682 LDEBUGF("find_free_cluster(%x) == %x\n",startcluster,c);
Jörg Hohensohnda848572004-12-28 22:16:07 +0000683 fat_bpb->fsinfo.nextfree = c;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000684 return c;
685 }
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000686 }
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000687 offset = 0;
Björn Stenberg08356fb2002-10-31 19:05:25 +0000688 }
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000689 }
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000690 else
691#endif /* #ifdef HAVE_FAT16SUPPORT */
692 {
693 sector = startcluster / CLUSTERS_PER_FAT_SECTOR;
694 offset = startcluster % CLUSTERS_PER_FAT_SECTOR;
695
Jörg Hohensohnda848572004-12-28 22:16:07 +0000696 for (i = 0; i<fat_bpb->fatsize; i++) {
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000697 unsigned int j;
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000698 unsigned long nr = (i + sector) % fat_bpb->fatsize;
699 unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000700 if ( !fat )
701 break;
702 for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) {
703 int k = (j + offset) % CLUSTERS_PER_FAT_SECTOR;
Dave Chapman9e19c952005-10-06 19:27:43 +0000704 if (!(letoh32(fat[k]) & 0x0fffffff)) {
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000705 unsigned long c = nr * CLUSTERS_PER_FAT_SECTOR + k;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000706 /* Ignore the reserved clusters 0 & 1, and also
707 cluster numbers out of bounds */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000708 if ( c < 2 || c > fat_bpb->dataclusters+1 )
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000709 continue;
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000710 LDEBUGF("find_free_cluster(%lx) == %lx\n",startcluster,c);
Jörg Hohensohnda848572004-12-28 22:16:07 +0000711 fat_bpb->fsinfo.nextfree = c;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000712 return c;
713 }
714 }
715 offset = 0;
716 }
717 }
718
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000719 LDEBUGF("find_free_cluster(%lx) == 0\n",startcluster);
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000720 return 0; /* 0 is an illegal cluster number */
721}
722
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000723static int update_fat_entry(IF_MV2(struct bpb* fat_bpb,)
724 unsigned long entry,
725 unsigned long val)
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000726{
Jörg Hohensohnda848572004-12-28 22:16:07 +0000727#ifndef HAVE_MULTIVOLUME
728 struct bpb* fat_bpb = &fat_bpbs[0];
729#endif
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000730#ifdef HAVE_FAT16SUPPORT
Jörg Hohensohnda848572004-12-28 22:16:07 +0000731 if (fat_bpb->is_fat16)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000732 {
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000733 int sector = entry / CLUSTERS_PER_FAT16_SECTOR;
734 int offset = entry % CLUSTERS_PER_FAT16_SECTOR;
735 unsigned short* sec;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000736
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000737 val &= 0xFFFF;
Björn Stenberg5661b232002-04-27 01:25:22 +0000738
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000739 LDEBUGF("update_fat_entry(%x,%x)\n",entry,val);
Björn Stenberg307f5d82002-11-01 15:26:06 +0000740
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000741 if (entry==val)
Jean-Philippe Bernardy06ded522005-02-28 10:52:28 +0000742 panicf("Creating FAT loop: %lx,%lx\n",entry,val);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000743
744 if ( entry < 2 )
Jean-Philippe Bernardy06ded522005-02-28 10:52:28 +0000745 panicf("Updating reserved FAT entry %ld.\n",entry);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000746
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000747 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000748 if (!sec)
749 {
Jens Arnold316ae182005-01-03 07:59:49 +0000750 DEBUGF( "update_fat_entry() - Could not cache sector %d\n", sector);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000751 return -1;
752 }
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000753
754 if ( val ) {
Dave Chapman207c9502005-10-06 21:23:59 +0000755 if (letoh16(sec[offset]) == 0x0000 && fat_bpb->fsinfo.freecount > 0)
Jörg Hohensohnda848572004-12-28 22:16:07 +0000756 fat_bpb->fsinfo.freecount--;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000757 }
758 else {
Dave Chapman207c9502005-10-06 21:23:59 +0000759 if (letoh16(sec[offset]))
Jörg Hohensohnda848572004-12-28 22:16:07 +0000760 fat_bpb->fsinfo.freecount++;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000761 }
762
Jörg Hohensohnda848572004-12-28 22:16:07 +0000763 LDEBUGF("update_fat_entry: %d free clusters\n", fat_bpb->fsinfo.freecount);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000764
Dave Chapman9e19c952005-10-06 19:27:43 +0000765 sec[offset] = htole16(val);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000766 }
767 else
768#endif /* #ifdef HAVE_FAT16SUPPORT */
769 {
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000770 long sector = entry / CLUSTERS_PER_FAT_SECTOR;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000771 int offset = entry % CLUSTERS_PER_FAT_SECTOR;
Jean-Philippe Bernardyfc194452005-02-25 18:50:16 +0000772 unsigned long* sec;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000773
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000774 LDEBUGF("update_fat_entry(%lx,%lx)\n",entry,val);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000775
776 if (entry==val)
Jean-Philippe Bernardyfc194452005-02-25 18:50:16 +0000777 panicf("Creating FAT loop: %lx,%lx\n",entry,val);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000778
779 if ( entry < 2 )
Jean-Philippe Bernardyfc194452005-02-25 18:50:16 +0000780 panicf("Updating reserved FAT entry %ld.\n",entry);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000781
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000782 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000783 if (!sec)
784 {
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000785 DEBUGF( "update_fat_entry() - Could not cache sector %ld\n", sector);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000786 return -1;
787 }
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000788
789 if ( val ) {
Dave Chapman207c9502005-10-06 21:23:59 +0000790 if (!(letoh32(sec[offset]) & 0x0fffffff) &&
Jörg Hohensohnda848572004-12-28 22:16:07 +0000791 fat_bpb->fsinfo.freecount > 0)
792 fat_bpb->fsinfo.freecount--;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000793 }
794 else {
Dave Chapman207c9502005-10-06 21:23:59 +0000795 if (letoh32(sec[offset]) & 0x0fffffff)
Jörg Hohensohnda848572004-12-28 22:16:07 +0000796 fat_bpb->fsinfo.freecount++;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000797 }
798
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000799 LDEBUGF("update_fat_entry: %ld free clusters\n", fat_bpb->fsinfo.freecount);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000800
801 /* don't change top 4 bits */
Dave Chapman9e19c952005-10-06 19:27:43 +0000802 sec[offset] &= htole32(0xf0000000);
803 sec[offset] |= htole32(val & 0x0fffffff);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000804 }
Björn Stenberg1dff4b62002-04-26 16:44:58 +0000805
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000806 return 0;
807}
808
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000809static long read_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000810{
Jörg Hohensohn19934a12004-12-28 22:35:10 +0000811#ifdef HAVE_FAT16SUPPORT
Jörg Hohensohnda848572004-12-28 22:16:07 +0000812#ifndef HAVE_MULTIVOLUME
813 struct bpb* fat_bpb = &fat_bpbs[0];
814#endif
Jörg Hohensohnda848572004-12-28 22:16:07 +0000815 if (fat_bpb->is_fat16)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000816 {
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000817 int sector = entry / CLUSTERS_PER_FAT16_SECTOR;
818 int offset = entry % CLUSTERS_PER_FAT16_SECTOR;
819 unsigned short* sec;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000820
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000821 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000822 if (!sec)
823 {
824 DEBUGF( "read_fat_entry() - Could not cache sector %d\n", sector);
825 return -1;
826 }
827
Dave Chapman9e19c952005-10-06 19:27:43 +0000828 return letoh16(sec[offset]);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000829 }
830 else
831#endif /* #ifdef HAVE_FAT16SUPPORT */
832 {
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000833 long sector = entry / CLUSTERS_PER_FAT_SECTOR;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000834 int offset = entry % CLUSTERS_PER_FAT_SECTOR;
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000835 unsigned long* sec;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000836
Jörg Hohensohn50dba1f2005-01-04 23:01:25 +0000837 sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000838 if (!sec)
839 {
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000840 DEBUGF( "read_fat_entry() - Could not cache sector %ld\n", sector);
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000841 return -1;
842 }
843
Dave Chapman9e19c952005-10-06 19:27:43 +0000844 return letoh32(sec[offset]) & 0x0fffffff;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000845 }
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000846}
847
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000848static long get_next_cluster(IF_MV2(struct bpb* fat_bpb,) long cluster)
Björn Stenberg5661b232002-04-27 01:25:22 +0000849{
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +0000850 long next_cluster;
851 long eof_mark = FAT_EOF_MARK;
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000852
853#ifdef HAVE_FAT16SUPPORT
Jörg Hohensohn19934a12004-12-28 22:35:10 +0000854#ifndef HAVE_MULTIVOLUME
855 struct bpb* fat_bpb = &fat_bpbs[0];
856#endif
Jörg Hohensohnda848572004-12-28 22:16:07 +0000857 if (fat_bpb->is_fat16)
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000858 {
859 eof_mark &= 0xFFFF; /* only 16 bit */
860 if (cluster < 0) /* FAT16 root dir */
861 return cluster + 1; /* don't use the FAT */
862 }
863#endif
Jörg Hohensohnda848572004-12-28 22:16:07 +0000864 next_cluster = read_fat_entry(IF_MV2(fat_bpb,) cluster);
Linus Nielsen Feltzing8083e7a2002-05-09 22:57:54 +0000865
Björn Stenberg5661b232002-04-27 01:25:22 +0000866 /* is this last cluster in chain? */
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000867 if ( next_cluster >= eof_mark )
Björn Stenberg5661b232002-04-27 01:25:22 +0000868 return 0;
869 else
870 return next_cluster;
871}
872
Jörg Hohensohnda848572004-12-28 22:16:07 +0000873static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb))
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000874{
Jörg Hohensohnda848572004-12-28 22:16:07 +0000875#ifndef HAVE_MULTIVOLUME
876 struct bpb* fat_bpb = &fat_bpbs[0];
877#endif
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000878 unsigned char fsinfo[SECTOR_SIZE];
Jean-Philippe Bernardy907ba582005-02-25 09:11:29 +0000879 unsigned long* intptr;
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000880 int rc;
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000881
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000882#ifdef HAVE_FAT16SUPPORT
Jörg Hohensohnda848572004-12-28 22:16:07 +0000883 if (fat_bpb->is_fat16)
Jörg Hohensohn7f7afe42004-09-23 21:43:43 +0000884 return 0; /* FAT16 has no FsInfo */
885#endif /* #ifdef HAVE_FAT16SUPPORT */
886
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000887 /* update fsinfo */
Jörg Hohensohnda848572004-12-28 22:16:07 +0000888 rc = ata_read_sectors(IF_MV2(fat_bpb->drive,)
889 fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1,fsinfo);
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000890 if (rc < 0)
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000891 {
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000892 DEBUGF( "flush_fat() - Couldn't read FSInfo (error code %d)\n", rc);
893 return rc * 10 - 1;
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000894 }
Jean-Philippe Bernardy907ba582005-02-25 09:11:29 +0000895 intptr = (long*)&(fsinfo[FSINFO_FREECOUNT]);
Dave Chapman9e19c952005-10-06 19:27:43 +0000896 *intptr = htole32(fat_bpb->fsinfo.freecount);
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000897
Jean-Philippe Bernardy907ba582005-02-25 09:11:29 +0000898 intptr = (long*)&(fsinfo[FSINFO_NEXTFREE]);
Dave Chapman9e19c952005-10-06 19:27:43 +0000899 *intptr = htole32(fat_bpb->fsinfo.nextfree);
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000900
Jörg Hohensohnda848572004-12-28 22:16:07 +0000901 rc = ata_write_sectors(IF_MV2(fat_bpb->drive,)
902 fat_bpb->startsector + fat_bpb->bpb_fsinfo,1,fsinfo);
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000903 if (rc < 0)
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000904 {
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000905 DEBUGF( "flush_fat() - Couldn't write FSInfo (error code %d)\n", rc);
906 return rc * 10 - 2;
Björn Stenbergb17fe5a2002-12-09 15:39:32 +0000907 }
908
909 return 0;
910}
911
Jörg Hohensohnda848572004-12-28 22:16:07 +0000912static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb))
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000913{
914 int i;
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000915 int rc;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000916 unsigned char *sec;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000917 LDEBUGF("flush_fat()\n");
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000918
Jörg Hohensohn74146872005-01-05 00:09:04 +0000919 mutex_lock(&cache_mutex);
Linus Nielsen Feltzing114fce02002-05-09 21:27:49 +0000920 for(i = 0;i < FAT_CACHE_SIZE;i++)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000921 {
Jörg Hohensohnda848572004-12-28 22:16:07 +0000922 struct fat_cache_entry *fce = &fat_cache[i];
Jörg Hohensohn74146872005-01-05 00:09:04 +0000923 if(fce->inuse
924#ifdef HAVE_MULTIVOLUME
925 && fce->fat_vol == fat_bpb
926#endif
927 && fce->dirty)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000928 {
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000929 sec = fat_cache_sectors[i];
Jörg Hohensohnda848572004-12-28 22:16:07 +0000930 flush_fat_sector(fce, sec);
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000931 }
932 }
Jörg Hohensohn74146872005-01-05 00:09:04 +0000933 mutex_unlock(&cache_mutex);
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000934
Jörg Hohensohnda848572004-12-28 22:16:07 +0000935 rc = update_fsinfo(IF_MV(fat_bpb));
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +0000936 if (rc < 0)
937 return rc * 10 - 3;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +0000938
Björn Stenbergd9eb5c72002-03-28 15:09:10 +0000939 return 0;
940}
941
Björn Stenbergb5184d72002-11-22 23:51:46 +0000942static void fat_time(unsigned short* date,
943 unsigned short* time,
944 unsigned short* tenth )
945{
Dave Chapman8c800cf2005-12-04 15:23:47 +0000946#ifdef CONFIG_RTC
Björn Stenbergb5184d72002-11-22 23:51:46 +0000947 struct tm* tm = get_time();
948
949 if (date)
950 *date = ((tm->tm_year - 80) << 9) |
951 ((tm->tm_mon + 1) << 5) |
952 tm->tm_mday;
953
954 if (time)
955 *time = (tm->tm_hour << 11) |
956 (tm->tm_min << 5) |
957 (tm->tm_sec >> 1);
958
959 if (tenth)
960 *tenth = (tm->tm_sec & 1) * 100;
Linus Nielsen Feltzingd9e8bfe2003-11-03 00:59:04 +0000961#else
962 /* non-RTC version returns an increment from the supplied time, or a
963 * fixed standard time/date if no time given as input */
964 bool next_day = false;
965
966 if (time)
967 {
968 if (0 == *time)
969 {
970 /* set to 00:15:00 */
971 *time = (15 << 5);
972 }
973 else
974 {
975 unsigned short mins = (*time >> 5) & 0x003F;
976 unsigned short hours = (*time >> 11) & 0x001F;
977 if ((mins += 10) >= 60)
978 {
979 mins = 0;
980 hours++;
981 }
982 if ((++hours) >= 24)
983 {
984 hours = hours - 24;
985 next_day = true;
986 }
987 *time = (hours << 11) | (mins << 5);
988 }
989 }
990
991 if (date)
992 {
993 if (0 == *date)
994 {
Jens Arnoldb2964ca2005-09-06 18:03:50 +0000995/* Macros to convert a 2-digit string to a decimal constant.
996 (YEAR), MONTH and DAY are set by the date command, which outputs
997 DAY as 00..31 and MONTH as 01..12. The leading zero would lead to
998 misinterpretation as an octal constant. */
999#define S100(x) 1 ## x
1000#define C2DIG2DEC(x) (S100(x)-100)
1001 /* set to build date */
1002 *date = ((YEAR - 1980) << 9) | (C2DIG2DEC(MONTH) << 5)
1003 | C2DIG2DEC(DAY);
Linus Nielsen Feltzingd9e8bfe2003-11-03 00:59:04 +00001004 }
1005 else
1006 {
1007 unsigned short day = *date & 0x001F;
1008 unsigned short month = (*date >> 5) & 0x000F;
1009 unsigned short year = (*date >> 9) & 0x007F;
1010 if (next_day)
1011 {
1012 /* do a very simple day increment - never go above 28 days */
1013 if (++day > 28)
1014 {
1015 day = 1;
1016 if (++month > 12)
1017 {
1018 month = 1;
1019 year++;
1020 }
1021 }
1022 *date = (year << 9) | (month << 5) | day;
1023 }
1024 }
1025 }
1026 if (tenth)
1027 *tenth = 0;
Dave Chapman8c800cf2005-12-04 15:23:47 +00001028#endif /* CONFIG_RTC */
Björn Stenbergb5184d72002-11-22 23:51:46 +00001029}
1030
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001031static int write_long_name(struct fat_file* file,
1032 unsigned int firstentry,
1033 unsigned int numentries,
Jens Arnold0ceaa5e2004-08-17 01:45:48 +00001034 const unsigned char* name,
1035 const unsigned char* shortname,
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001036 bool is_directory)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001037{
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001038 unsigned char buf[SECTOR_SIZE];
1039 unsigned char* entry;
1040 unsigned int idx = firstentry % DIR_ENTRIES_PER_SECTOR;
1041 unsigned int sector = firstentry / DIR_ENTRIES_PER_SECTOR;
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001042 unsigned char chksum = 0;
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001043 unsigned int i, j=0;
1044 unsigned int nameidx=0, namelen = utf8length(name);
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001045 int rc;
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001046 unsigned short name_utf16[namelen + 1];
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001047
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001048 LDEBUGF("write_long_name(file:%lx, first:%d, num:%d, name:%s)\n",
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001049 file->firstcluster, firstentry, numentries, name);
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001050
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001051 rc = fat_seek(file, sector);
1052 if (rc<0)
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001053 return rc * 10 - 1;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001054
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001055 rc = fat_readwrite(file, 1, buf, false);
1056 if (rc<1)
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001057 return rc * 10 - 2;
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001058
1059 /* calculate shortname checksum */
1060 for (i=11; i>0; i--)
1061 chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++];
1062
1063 /* calc position of last name segment */
Björn Stenbergeee2c012002-11-18 11:58:43 +00001064 if ( namelen > NAME_BYTES_PER_ENTRY )
1065 for (nameidx=0;
1066 nameidx < (namelen - NAME_BYTES_PER_ENTRY);
1067 nameidx += NAME_BYTES_PER_ENTRY);
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001068
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001069 /* we need to convert the name first */
1070 /* since it is written in reverse order */
1071 for (i = 0; i <= namelen; i++)
1072 name = utf8decode(name, &name_utf16[i]);
1073
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001074 for (i=0; i < numentries; i++) {
1075 /* new sector? */
1076 if ( idx >= DIR_ENTRIES_PER_SECTOR ) {
1077 /* update current sector */
1078 rc = fat_seek(file, sector);
1079 if (rc<0)
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001080 return rc * 10 - 3;
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001081
1082 rc = fat_readwrite(file, 1, buf, true);
1083 if (rc<1)
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001084 return rc * 10 - 4;
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001085
Björn Stenbergeee2c012002-11-18 11:58:43 +00001086 /* read next sector */
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001087 rc = fat_readwrite(file, 1, buf, false);
Björn Stenbergeee2c012002-11-18 11:58:43 +00001088 if (rc<0) {
1089 LDEBUGF("Failed writing new sector: %d\n",rc);
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001090 return rc * 10 - 5;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001091 }
1092 if (rc==0)
1093 /* end of dir */
1094 memset(buf, 0, sizeof buf);
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001095
1096 sector++;
1097 idx = 0;
1098 }
1099
1100 entry = buf + idx * DIR_ENTRY_SIZE;
1101
1102 /* verify this entry is free */
1103 if (entry[0] && entry[0] != 0xe5 )
1104 panicf("Dir entry %d in sector %x is not free! "
1105 "%02x %02x %02x %02x",
1106 idx, sector,
1107 entry[0], entry[1], entry[2], entry[3]);
1108
1109 memset(entry, 0, DIR_ENTRY_SIZE);
1110 if ( i+1 < numentries ) {
1111 /* longname entry */
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001112 unsigned int k, l = nameidx;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001113
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001114 entry[FATLONG_ORDER] = numentries-i-1;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001115 if (i==0) {
1116 /* mark this as last long entry */
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001117 entry[FATLONG_ORDER] |= 0x40;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001118
1119 /* pad name with 0xffff */
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001120 for (k=1; k<11; k++) entry[k] = 0xff;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001121 for (k=14; k<26; k++) entry[k] = 0xff;
1122 for (k=28; k<32; k++) entry[k] = 0xff;
1123 };
1124 /* set name */
1125 for (k=0; k<5 && l <= namelen; k++) {
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001126 entry[k*2 + 1] = (unsigned char)(name_utf16[l] & 0xff);
1127 entry[k*2 + 2] = (unsigned char)(name_utf16[l++] >> 8);
Björn Stenbergeee2c012002-11-18 11:58:43 +00001128 }
1129 for (k=0; k<6 && l <= namelen; k++) {
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001130 entry[k*2 + 14] = (unsigned char)(name_utf16[l] & 0xff);
1131 entry[k*2 + 15] = (unsigned char)(name_utf16[l++] >> 8);
Björn Stenbergeee2c012002-11-18 11:58:43 +00001132 }
1133 for (k=0; k<2 && l <= namelen; k++) {
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001134 entry[k*2 + 28] = (unsigned char)(name_utf16[l] & 0xff);
1135 entry[k*2 + 29] = (unsigned char)(name_utf16[l++] >> 8);
Björn Stenbergeee2c012002-11-18 11:58:43 +00001136 }
1137
1138 entry[FATDIR_ATTR] = FAT_ATTR_LONG_NAME;
1139 entry[FATDIR_FSTCLUSLO] = 0;
1140 entry[FATLONG_TYPE] = 0;
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001141 entry[FATLONG_CHKSUM] = chksum;
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001142 LDEBUGF("Longname entry %d: %s\n", idx, name+nameidx);
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001143 }
1144 else {
1145 /* shortname entry */
Björn Stenbergb5184d72002-11-22 23:51:46 +00001146 unsigned short date=0, time=0, tenth=0;
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001147 LDEBUGF("Shortname entry: %s\n", shortname);
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001148 strncpy(entry + FATDIR_NAME, shortname, 11);
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001149 entry[FATDIR_ATTR] = is_directory?FAT_ATTR_DIRECTORY:0;
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001150 entry[FATDIR_NTRES] = 0;
Björn Stenbergb5184d72002-11-22 23:51:46 +00001151
Björn Stenbergb5184d72002-11-22 23:51:46 +00001152 fat_time(&date, &time, &tenth);
Björn Stenbergb5184d72002-11-22 23:51:46 +00001153 entry[FATDIR_CRTTIMETENTH] = tenth;
Dave Chapman9e19c952005-10-06 19:27:43 +00001154 *(unsigned short*)(entry + FATDIR_CRTTIME) = htole16(time);
1155 *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time);
1156 *(unsigned short*)(entry + FATDIR_CRTDATE) = htole16(date);
1157 *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date);
1158 *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date);
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001159 }
1160 idx++;
1161 nameidx -= NAME_BYTES_PER_ENTRY;
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001162 }
1163
1164 /* update last sector */
1165 rc = fat_seek(file, sector);
1166 if (rc<0)
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001167 return rc * 10 - 6;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001168
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001169 rc = fat_readwrite(file, 1, buf, true);
1170 if (rc<1)
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001171 return rc * 10 - 7;
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001172
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001173 return 0;
1174}
1175
Jonas Häggqvist98143f52005-09-07 01:35:15 +00001176static int fat_checkname(const unsigned char* newname)
1177{
1178 /* More sanity checks are probably needed */
1179 if ( newname[strlen(newname) - 1] == '.' ) {
1180 return -1;
1181 }
1182 return 0;
1183}
1184
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001185static int add_dir_entry(struct fat_dir* dir,
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001186 struct fat_file* file,
Jens Arnold0ceaa5e2004-08-17 01:45:48 +00001187 const char* name,
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001188 bool is_directory,
1189 bool dotdir)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001190{
Jörg Hohensohnda848572004-12-28 22:16:07 +00001191#ifdef HAVE_MULTIVOLUME
1192 struct bpb* fat_bpb = &fat_bpbs[dir->file.volume];
1193#else
1194 struct bpb* fat_bpb = &fat_bpbs[0];
1195#endif
Björn Stenberge8bcc012002-04-27 19:37:41 +00001196 unsigned char buf[SECTOR_SIZE];
Jens Arnolda07c7352005-04-22 22:26:13 +00001197 unsigned char shortname[12];
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001198 int rc;
Björn Stenbergc442a682002-11-15 11:20:33 +00001199 unsigned int sector;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001200 bool done = false;
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001201 int entries_needed, entries_found = 0;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001202 int firstentry;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001203
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001204 LDEBUGF( "add_dir_entry(%s,%lx)\n",
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001205 name, file->firstcluster);
1206
Jonas Häggqvist98143f52005-09-07 01:35:15 +00001207 /* Don't check dotdirs name for validity */
1208 if (dotdir == false) {
1209 rc = fat_checkname(name);
1210 if (rc < 0) {
1211 /* filename is invalid */
1212 return rc * 10 - 1;
1213 }
1214 }
1215
Jörg Hohensohnda848572004-12-28 22:16:07 +00001216#ifdef HAVE_MULTIVOLUME
1217 file->volume = dir->file.volume; /* inherit the volume, to make sure */
1218#endif
1219
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001220 /* The "." and ".." directory entries must not be long names */
1221 if(dotdir) {
1222 int i;
Jens Arnolda07c7352005-04-22 22:26:13 +00001223 strncpy(shortname, name, 12);
1224 for(i = strlen(shortname); i < 12; i++)
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001225 shortname[i] = ' ';
1226
1227 entries_needed = 1;
1228 } else {
Jens Arnolde4e1f2c2005-04-23 13:15:25 +00001229 create_dos_name(name, shortname);
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001230
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001231 /* one dir entry needed for every 13 bytes of filename,
1232 plus one entry for the short name */
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001233 entries_needed = (utf8length(name) + (NAME_BYTES_PER_ENTRY-1))
Jens Arnolda07c7352005-04-22 22:26:13 +00001234 / NAME_BYTES_PER_ENTRY + 1;
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001235 }
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001236
Jens Arnolda07c7352005-04-22 22:26:13 +00001237 restart:
1238 firstentry = -1;
1239
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001240 rc = fat_seek(&dir->file, 0);
1241 if (rc < 0)
Jonas Häggqvist98143f52005-09-07 01:35:15 +00001242 return rc * 10 - 2;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001243
Jens Arnolda07c7352005-04-22 22:26:13 +00001244 /* step 1: search for free entries and check for duplicate shortname */
1245 for (sector = 0; !done; sector++)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001246 {
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001247 unsigned int i;
1248
Jens Arnolda07c7352005-04-22 22:26:13 +00001249 rc = fat_readwrite(&dir->file, 1, buf, false);
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001250 if (rc < 0) {
Björn Stenberg1f214f22002-11-13 23:16:32 +00001251 DEBUGF( "add_dir_entry() - Couldn't read dir"
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001252 " (error code %d)\n", rc);
Jonas Häggqvist98143f52005-09-07 01:35:15 +00001253 return rc * 10 - 3;
Jens Arnolda07c7352005-04-22 22:26:13 +00001254 }
1255
1256 if (rc == 0) { /* current end of dir reached */
1257 LDEBUGF("End of dir on cluster boundary\n");
1258 break;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001259 }
1260
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001261 /* look for free slots */
Jens Arnolda07c7352005-04-22 22:26:13 +00001262 for (i = 0; i < DIR_ENTRIES_PER_SECTOR; i++)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001263 {
Jens Arnolda07c7352005-04-22 22:26:13 +00001264 switch (buf[i * DIR_ENTRY_SIZE]) {
1265 case 0:
1266 entries_found += DIR_ENTRIES_PER_SECTOR - i;
1267 LDEBUGF("Found end of dir %d\n",
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001268 sector * DIR_ENTRIES_PER_SECTOR + i);
Jens Arnolda07c7352005-04-22 22:26:13 +00001269 i = DIR_ENTRIES_PER_SECTOR - 1;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001270 done = true;
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001271 break;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001272
Jens Arnolda07c7352005-04-22 22:26:13 +00001273 case 0xe5:
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001274 entries_found++;
1275 LDEBUGF("Found free entry %d (%d/%d)\n",
1276 sector * DIR_ENTRIES_PER_SECTOR + i,
1277 entries_found, entries_needed);
1278 break;
1279
Jens Arnolda07c7352005-04-22 22:26:13 +00001280 default:
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001281 entries_found = 0;
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001282
Björn Stenbergeee2c012002-11-18 11:58:43 +00001283 /* check that our intended shortname doesn't already exist */
1284 if (!strncmp(shortname, buf + i * DIR_ENTRY_SIZE, 12)) {
Jens Arnolda07c7352005-04-22 22:26:13 +00001285 /* shortname exists already, make a new one */
Jens Arnolddbf7f512005-02-16 20:45:56 +00001286 randomize_dos_name(shortname);
Björn Stenbergeee2c012002-11-18 11:58:43 +00001287 LDEBUGF("Duplicate shortname, changing to %s\n",
1288 shortname);
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001289
Björn Stenbergeee2c012002-11-18 11:58:43 +00001290 /* name has changed, we need to restart search */
1291 goto restart;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001292 }
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001293 break;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001294 }
Jens Arnolda07c7352005-04-22 22:26:13 +00001295 if (firstentry < 0 && (entries_found >= entries_needed))
1296 firstentry = sector * DIR_ENTRIES_PER_SECTOR + i + 1
1297 - entries_found;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001298 }
1299 }
1300
Jens Arnolda07c7352005-04-22 22:26:13 +00001301 /* step 2: extend the dir if necessary */
1302 if (firstentry < 0)
1303 {
Jens Arnolda07c7352005-04-22 22:26:13 +00001304 LDEBUGF("Adding new sector(s) to dir\n");
1305 rc = fat_seek(&dir->file, sector);
1306 if (rc < 0)
1307 return rc * 10 - 4;
1308 memset(buf, 0, sizeof buf);
1309
1310 /* we must clear whole clusters */
1311 for (; (entries_found < entries_needed) ||
1312 (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); sector++)
1313 {
1314 if (sector >= (65536/DIR_ENTRIES_PER_SECTOR))
1315 return -5; /* dir too large -- FAT specification */
1316
1317 rc = fat_readwrite(&dir->file, 1, buf, true);
1318 if (rc < 1) /* No more room or something went wrong */
1319 return rc * 10 - 6;
1320
1321 entries_found += DIR_ENTRIES_PER_SECTOR;
1322 }
1323
1324 firstentry = sector * DIR_ENTRIES_PER_SECTOR - entries_found;
1325 }
1326
1327 /* step 3: add entry */
Björn Stenbergeee2c012002-11-18 11:58:43 +00001328 sector = firstentry / DIR_ENTRIES_PER_SECTOR;
1329 LDEBUGF("Adding longname to entry %d in sector %d\n",
1330 firstentry, sector);
Jens Arnolda07c7352005-04-22 22:26:13 +00001331
1332 rc = write_long_name(&dir->file, firstentry,
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001333 entries_needed, name, shortname, is_directory);
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001334 if (rc < 0)
Jens Arnolda07c7352005-04-22 22:26:13 +00001335 return rc * 10 - 7;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001336
1337 /* remember where the shortname dir entry is located */
1338 file->direntry = firstentry + entries_needed - 1;
1339 file->direntries = entries_needed;
1340 file->dircluster = dir->file.firstcluster;
1341 LDEBUGF("Added new dir entry %d, using %d slots.\n",
1342 file->direntry, file->direntries);
1343
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001344 return 0;
1345}
1346
Björn Stenberg5661b232002-04-27 01:25:22 +00001347unsigned char char2dos(unsigned char c)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001348{
1349 switch(c)
1350 {
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001351 case 0x22:
1352 case 0x2a:
1353 case 0x2b:
1354 case 0x2c:
1355 case 0x2e:
1356 case 0x3a:
1357 case 0x3b:
1358 case 0x3c:
1359 case 0x3d:
1360 case 0x3e:
1361 case 0x3f:
1362 case 0x5b:
1363 case 0x5c:
1364 case 0x5d:
1365 case 0x7c:
Jens Arnolda07c7352005-04-22 22:26:13 +00001366 /* Illegal char, replace */
Jens Arnoldf213afa2005-02-16 21:56:20 +00001367 c = '_';
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001368 break;
1369
1370 default:
Jens Arnoldf213afa2005-02-16 21:56:20 +00001371 if(c <= 0x20)
Jens Arnolda07c7352005-04-22 22:26:13 +00001372 c = 0; /* Illegal char, remove */
1373 else
1374 c = toupper(c);
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001375 break;
1376 }
1377 return c;
1378}
1379
Jens Arnolde4e1f2c2005-04-23 13:15:25 +00001380static void create_dos_name(const unsigned char *name, unsigned char *newname)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001381{
Jens Arnolddbf7f512005-02-16 20:45:56 +00001382 int i;
1383 unsigned char *ext;
1384
1385 /* Find extension part */
1386 ext = strrchr(name, '.');
1387 if (ext == name) /* handle .dotnames */
1388 ext = NULL;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001389
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001390 /* Name part */
Jens Arnolddbf7f512005-02-16 20:45:56 +00001391 for (i = 0; *name && (!ext || name < ext) && (i < 8); name++)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001392 {
Jens Arnolddbf7f512005-02-16 20:45:56 +00001393 unsigned char c = char2dos(*name);
Björn Stenbergeee2c012002-11-18 11:58:43 +00001394 if (c)
Jens Arnolda07c7352005-04-22 22:26:13 +00001395 newname[i++] = c;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001396 }
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001397
Jens Arnolddbf7f512005-02-16 20:45:56 +00001398 /* Pad both name and extension */
1399 while (i < 11)
1400 newname[i++] = ' ';
Björn Stenberg7aabb1a2002-11-15 16:41:02 +00001401
Jens Arnolda07c7352005-04-22 22:26:13 +00001402 if (newname[0] == 0xe5) /* Special kanji character */
1403 newname[0] = 0x05;
1404
Jens Arnolddbf7f512005-02-16 20:45:56 +00001405 if (ext)
1406 { /* Extension part */
1407 ext++;
1408 for (i = 8; *ext && (i < 11); ext++)
1409 {
1410 unsigned char c = char2dos(*ext);
1411 if (c)
Jens Arnolda07c7352005-04-22 22:26:13 +00001412 newname[i++] = c;
Jens Arnolddbf7f512005-02-16 20:45:56 +00001413 }
1414 }
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001415}
1416
Jens Arnolddbf7f512005-02-16 20:45:56 +00001417static void randomize_dos_name(unsigned char *name)
1418{
1419 int i;
1420 unsigned char buf[5];
1421
Jens Arnold6333f792005-02-16 22:21:45 +00001422 snprintf(buf, sizeof buf, "%04X", (unsigned)rand() & 0xffff);
Jens Arnolddbf7f512005-02-16 20:45:56 +00001423
1424 for (i = 0; (i < 4) && (name[i] != ' '); i++);
1425 /* account for possible shortname length < 4 */
1426 memcpy(&name[i], buf, 4);
1427}
1428
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001429static int update_short_entry( struct fat_file* file, long size, int attr )
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001430{
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001431 unsigned char buf[SECTOR_SIZE];
Björn Stenbergeee2c012002-11-18 11:58:43 +00001432 int sector = file->direntry / DIR_ENTRIES_PER_SECTOR;
1433 unsigned char* entry =
1434 buf + DIR_ENTRY_SIZE * (file->direntry % DIR_ENTRIES_PER_SECTOR);
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001435 unsigned long* sizeptr;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001436 unsigned short* clusptr;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001437 struct fat_file dir;
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001438 int rc;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001439
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001440 LDEBUGF("update_file_size(cluster:%lx entry:%d size:%ld)\n",
Björn Stenbergeee2c012002-11-18 11:58:43 +00001441 file->firstcluster, file->direntry, size);
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001442
Björn Stenbergeee2c012002-11-18 11:58:43 +00001443 /* create a temporary file handle for the dir holding this file */
Jörg Hohensohnda848572004-12-28 22:16:07 +00001444 rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL);
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001445 if (rc < 0)
1446 return rc * 10 - 1;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001447
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001448 rc = fat_seek( &dir, sector );
1449 if (rc<0)
1450 return rc * 10 - 2;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001451
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001452 rc = fat_readwrite(&dir, 1, buf, false);
1453 if (rc < 1)
1454 return rc * 10 - 3;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001455
1456 if (!entry[0] || entry[0] == 0xe5)
1457 panicf("Updating size on empty dir entry %d\n", file->direntry);
1458
Björn Stenberg184fd552003-01-27 09:32:17 +00001459 entry[FATDIR_ATTR] = attr & 0xFF;
1460
Björn Stenbergeee2c012002-11-18 11:58:43 +00001461 clusptr = (short*)(entry + FATDIR_FSTCLUSHI);
Dave Chapman9e19c952005-10-06 19:27:43 +00001462 *clusptr = htole16(file->firstcluster >> 16);
Björn Stenbergeee2c012002-11-18 11:58:43 +00001463
1464 clusptr = (short*)(entry + FATDIR_FSTCLUSLO);
Dave Chapman9e19c952005-10-06 19:27:43 +00001465 *clusptr = htole16(file->firstcluster & 0xffff);
Björn Stenbergeee2c012002-11-18 11:58:43 +00001466
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001467 sizeptr = (long*)(entry + FATDIR_FILESIZE);
Dave Chapman9e19c952005-10-06 19:27:43 +00001468 *sizeptr = htole32(size);
Björn Stenbergeee2c012002-11-18 11:58:43 +00001469
Björn Stenbergac88b9e2002-11-22 23:55:08 +00001470 {
Dave Chapman8c800cf2005-12-04 15:23:47 +00001471#ifdef CONFIG_RTC
Linus Nielsen Feltzingd9e8bfe2003-11-03 00:59:04 +00001472 unsigned short time = 0;
1473 unsigned short date = 0;
1474#else
1475 /* get old time to increment from */
Dave Chapman9e19c952005-10-06 19:27:43 +00001476 unsigned short time = htole16(*(unsigned short*)(entry + FATDIR_WRTTIME));
1477 unsigned short date = htole16(*(unsigned short*)(entry + FATDIR_WRTDATE));
Linus Nielsen Feltzingd9e8bfe2003-11-03 00:59:04 +00001478#endif
Björn Stenbergac88b9e2002-11-22 23:55:08 +00001479 fat_time(&date, &time, NULL);
Dave Chapman9e19c952005-10-06 19:27:43 +00001480 *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time);
1481 *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date);
1482 *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date);
Björn Stenbergac88b9e2002-11-22 23:55:08 +00001483 }
Björn Stenbergb5184d72002-11-22 23:51:46 +00001484
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001485 rc = fat_seek( &dir, sector );
1486 if (rc < 0)
1487 return rc * 10 - 4;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001488
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001489 rc = fat_readwrite(&dir, 1, buf, true);
1490 if (rc < 1)
1491 return rc * 10 - 5;
Björn Stenberg1f214f22002-11-13 23:16:32 +00001492
1493 return 0;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001494}
1495
Jens Arnold0ceaa5e2004-08-17 01:45:48 +00001496static int parse_direntry(struct fat_direntry *de, const unsigned char *buf)
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001497{
Björn Stenberg802740c2002-05-07 18:56:32 +00001498 int i=0,j=0;
Jens Arnolda07c7352005-04-22 22:26:13 +00001499 unsigned char c;
1500 bool lowercase;
1501
Björn Stenberg1dff4b62002-04-26 16:44:58 +00001502 memset(de, 0, sizeof(struct fat_direntry));
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001503 de->attr = buf[FATDIR_ATTR];
1504 de->crttimetenth = buf[FATDIR_CRTTIMETENTH];
Björn Stenberg1dff4b62002-04-26 16:44:58 +00001505 de->crtdate = BYTES2INT16(buf,FATDIR_CRTDATE);
1506 de->crttime = BYTES2INT16(buf,FATDIR_CRTTIME);
1507 de->wrtdate = BYTES2INT16(buf,FATDIR_WRTDATE);
1508 de->wrttime = BYTES2INT16(buf,FATDIR_WRTTIME);
1509 de->filesize = BYTES2INT32(buf,FATDIR_FILESIZE);
Jean-Philippe Bernardya83214d2005-02-27 20:44:26 +00001510 de->firstcluster = ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSLO)) |
1511 ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSHI) << 16);
1512 /* The double cast is to prevent a sign-extension to be done on CalmRISC16.
1513 (the result of the shift is always considered signed) */
Jens Arnolda07c7352005-04-22 22:26:13 +00001514
Björn Stenberg802740c2002-05-07 18:56:32 +00001515 /* fix the name */
Jens Arnolda07c7352005-04-22 22:26:13 +00001516 lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_NAME);
1517 c = buf[FATDIR_NAME];
1518 if (c == 0x05) /* special kanji char */
1519 c = 0xe5;
1520 i = 0;
1521 while (c != ' ') {
1522 de->name[j++] = lowercase ? tolower(c) : c;
1523 if (++i >= 8)
1524 break;
1525 c = buf[FATDIR_NAME+i];
1526 }
1527 if (buf[FATDIR_NAME+8] != ' ') {
1528 lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_EXT);
Björn Stenberg802740c2002-05-07 18:56:32 +00001529 de->name[j++] = '.';
Jens Arnolda07c7352005-04-22 22:26:13 +00001530 for (i = 8; (i < 11) && ((c = buf[FATDIR_NAME+i]) != ' '); i++)
1531 de->name[j++] = lowercase ? tolower(c) : c;
Björn Stenberg802740c2002-05-07 18:56:32 +00001532 }
Björn Stenberg1dff4b62002-04-26 16:44:58 +00001533 return 1;
Björn Stenbergd9eb5c72002-03-28 15:09:10 +00001534}
1535
Jens Arnold316ae182005-01-03 07:59:49 +00001536int fat_open(IF_MV2(int volume,)
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001537 long startcluster,
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001538 struct fat_file *file,
Jens Arnold0ceaa5e2004-08-17 01:45:48 +00001539 const struct fat_dir* dir)
Björn Stenberge8bcc012002-04-27 19:37:41 +00001540{
Björn Stenberg924164e2002-05-03 15:35:51 +00001541 file->firstcluster = startcluster;
Björn Stenberg46ddacf2002-10-22 15:06:08 +00001542 file->lastcluster = startcluster;
Björn Stenberga5e77d82002-10-31 16:09:28 +00001543 file->lastsector = 0;
Hardeep Sidhufc9b28d2003-06-03 18:04:22 +00001544 file->clusternum = 0;
Björn Stenberg924164e2002-05-03 15:35:51 +00001545 file->sectornum = 0;
Björn Stenberg1f214f22002-11-13 23:16:32 +00001546 file->eof = false;
Jörg Hohensohnda848572004-12-28 22:16:07 +00001547#ifdef HAVE_MULTIVOLUME
1548 file->volume = volume;
1549 /* fixme: remove error check when done */
1550 if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted)
1551 {
1552 LDEBUGF("fat_open() illegal volume %d\n", volume);
1553 return -1;
1554 }
1555#endif
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001556
1557 /* remember where the file's dir entry is located */
Björn Stenberg1f214f22002-11-13 23:16:32 +00001558 if ( dir ) {
Björn Stenberg1f214f22002-11-13 23:16:32 +00001559 file->direntry = dir->entry - 1;
Björn Stenbergeee2c012002-11-18 11:58:43 +00001560 file->direntries = dir->entrycount;
1561 file->dircluster = dir->file.firstcluster;
Björn Stenberg1f214f22002-11-13 23:16:32 +00001562 }
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001563 LDEBUGF("fat_open(%lx), entry %d\n",startcluster,file->direntry);
Björn Stenberge8bcc012002-04-27 19:37:41 +00001564 return 0;
1565}
1566
Jens Arnold0ceaa5e2004-08-17 01:45:48 +00001567int fat_create_file(const char* name,
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001568 struct fat_file* file,
1569 struct fat_dir* dir)
1570{
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001571 int rc;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001572
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001573 LDEBUGF("fat_create_file(\"%s\",%lx,%lx)\n",name,(long)file,(long)dir);
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001574 rc = add_dir_entry(dir, file, name, false, false);
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001575 if (!rc) {
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001576 file->firstcluster = 0;
Björn Stenberg46ddacf2002-10-22 15:06:08 +00001577 file->lastcluster = 0;
1578 file->lastsector = 0;
Hardeep Sidhufc9b28d2003-06-03 18:04:22 +00001579 file->clusternum = 0;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001580 file->sectornum = 0;
Björn Stenberg1f214f22002-11-13 23:16:32 +00001581 file->eof = false;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001582 }
1583
Linus Nielsen Feltzing96f51992003-02-26 03:02:49 +00001584 return rc;
Björn Stenbergb7b48fe2002-10-20 22:50:58 +00001585}
1586
Jens Arnold0ceaa5e2004-08-17 01:45:48 +00001587int fat_create_dir(const char* name,
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001588 struct fat_dir* newdir,
1589 struct fat_dir* dir)
1590{
Jörg Hohensohnda848572004-12-28 22:16:07 +00001591#ifdef HAVE_MULTIVOLUME
1592 struct bpb* fat_bpb = &fat_bpbs[dir->file.volume];
1593#else
1594 struct bpb* fat_bpb = &fat_bpbs[0];
1595#endif
Linus Nielsen Feltzing7c753862004-01-15 19:05:49 +00001596 unsigned char buf[SECTOR_SIZE];
1597 int i;
Jean-Philippe Bernardy907ba582005-02-25 09:11:29 +00001598 long sector;
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001599 int rc;
1600 struct fat_file dummyfile;
1601
Jean-Philippe Bernardy36b8e132005-01-23 23:20:40 +00001602 LDEBUGF("fat_create_dir(\"%s\",%lx,%lx)\n",name,(long)newdir,(long)dir);
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001603
Linus Nielsen Feltzingda115da2004-01-26 10:14:46 +00001604 memset(newdir, 0, sizeof(struct fat_dir));
1605 memset(&dummyfile, 0, sizeof(struct fat_file));
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001606
1607 /* First, add the entry in the parent directory */
1608 rc = add_dir_entry(dir, &newdir->file, name, true, false);
1609 if (rc < 0)
1610 return rc * 10 - 1;
1611
Linus Nielsen Feltzing7c753862004-01-15 19:05:49 +00001612 /* Allocate a new cluster for the directory */
Jörg Hohensohnda848572004-12-28 22:16:07 +00001613 newdir->file.firstcluster = find_free_cluster(IF_MV2(fat_bpb,) fat_bpb->fsinfo.nextfree);
Linus Nielsen Feltzing7c753862004-01-15 19:05:49 +00001614 if(newdir->file.firstcluster == 0)
1615 return -1;
1616
Jörg Hohensohnda848572004-12-28 22:16:07 +00001617 update_fat_entry(IF_MV2(fat_bpb,) newdir->file.firstcluster, FAT_EOF_MARK);
Linus Nielsen Feltzing7c753862004-01-15 19:05:49 +00001618
1619 /* Clear the entire cluster */
1620 memset(buf, 0, sizeof buf);
Jörg Hohensohnda848572004-12-28 22:16:07 +00001621 sector = cluster2sec(IF_MV2(fat_bpb,) newdir->file.firstcluster);
1622 for(i = 0;i < (int)fat_bpb->bpb_secperclus;i++) {
1623 rc = transfer(IF_MV2(fat_bpb,) sector + i, 1, buf, true );
Linus Nielsen Feltzing7c753862004-01-15 19:05:49 +00001624 if (rc < 0)
1625 return rc * 10 - 2;
1626 }
Linus Nielsen Feltzing54353e02004-01-15 15:56:19 +00001627
Linus Nielsen Feltzing7c753862004-01-15 19:05:49 +00001628 /* Then add the "." entry */
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001629 rc = add_dir_entry(newdir, &dummyfile, ".", true, true);
1630 if (rc < 0)
Linus Nielsen Feltzing7c753862004-01-15 19:05:49 +00001631 return rc * 10 - 3;
Linus Nielsen Feltzing60b1c4b2004-01-15 14:30:59 +00001632 dummyfile.firstcluster = newdir->file.firstcluster;