Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 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 Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 19 | #include <stdio.h> |
| 20 | #include <string.h> |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 21 | #include <stdlib.h> |
| 22 | #include <ctype.h> |
Björn Stenberg | 3ad66b9 | 2002-05-29 11:56:42 +0000 | [diff] [blame] | 23 | #include <stdbool.h> |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 24 | #include "fat.h" |
Björn Stenberg | 0f532a0 | 2002-04-22 12:33:04 +0000 | [diff] [blame] | 25 | #include "ata.h" |
Björn Stenberg | 697dd70 | 2002-05-02 14:05:51 +0000 | [diff] [blame] | 26 | #include "debug.h" |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 27 | #include "panic.h" |
Linus Nielsen Feltzing | 8083e7a | 2002-05-09 22:57:54 +0000 | [diff] [blame] | 28 | #include "system.h" |
Björn Stenberg | b5184d7 | 2002-11-22 23:51:46 +0000 | [diff] [blame] | 29 | #include "timefuncs.h" |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 30 | #include "kernel.h" |
Marcoen Hirschberg | b0fee17 | 2005-12-06 13:27:15 +0000 | [diff] [blame^] | 31 | #include "rbunicode.h" |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 32 | |
Björn Stenberg | 1dff4b6 | 2002-04-26 16:44:58 +0000 | [diff] [blame] | 33 | #define BYTES2INT16(array,pos) \ |
| 34 | (array[pos] | (array[pos+1] << 8 )) |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 35 | #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 Stenberg | 1dff4b6 | 2002-04-26 16:44:58 +0000 | [diff] [blame] | 38 | |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 39 | #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 Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 89 | |
| 90 | /* NTRES flags */ |
| 91 | #define FAT_NTRES_LC_NAME 0x08 |
| 92 | #define FAT_NTRES_LC_EXT 0x10 |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 93 | |
| 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 Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 107 | #define FATLONG_ORDER 0 |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 108 | #define FATLONG_TYPE 12 |
| 109 | #define FATLONG_CHKSUM 13 |
| 110 | |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 111 | #define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4) |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 112 | #define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2) |
Björn Stenberg | 1f214f2 | 2002-11-13 23:16:32 +0000 | [diff] [blame] | 113 | #define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / DIR_ENTRY_SIZE) |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 114 | #define DIR_ENTRY_SIZE 32 |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 115 | #define NAME_BYTES_PER_ENTRY 13 |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 116 | #define FAT_BAD_MARK 0x0ffffff7 |
| 117 | #define FAT_EOF_MARK 0x0ffffff8 |
| 118 | |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 119 | struct fsinfo { |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 120 | unsigned long freecount; /* last known free cluster count */ |
| 121 | unsigned long nextfree; /* first cluster to start looking for free |
Björn Stenberg | c442a68 | 2002-11-15 11:20:33 +0000 | [diff] [blame] | 122 | clusters, or 0xffffffff for no hint */ |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 123 | }; |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 124 | /* fsinfo offsets */ |
| 125 | #define FSINFO_FREECOUNT 488 |
| 126 | #define FSINFO_NEXTFREE 492 |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 127 | |
Björn Stenberg | 827cee3 | 2002-05-30 19:47:56 +0000 | [diff] [blame] | 128 | struct bpb |
| 129 | { |
Jörg Hohensohn | fb0739b | 2004-10-14 07:52:08 +0000 | [diff] [blame] | 130 | int bpb_bytspersec; /* Bytes per sector, typically 512 */ |
Björn Stenberg | c442a68 | 2002-11-15 11:20:33 +0000 | [diff] [blame] | 131 | unsigned int bpb_secperclus; /* Sectors per cluster */ |
Björn Stenberg | 827cee3 | 2002-05-30 19:47:56 +0000 | [diff] [blame] | 132 | int bpb_rsvdseccnt; /* Number of reserved sectors */ |
| 133 | int bpb_numfats; /* Number of FAT structures, typically 2 */ |
Björn Stenberg | 827cee3 | 2002-05-30 19:47:56 +0000 | [diff] [blame] | 134 | 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 Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 137 | unsigned long bpb_totsec32; /* Number of sectors on the volume |
Björn Stenberg | 827cee3 | 2002-05-30 19:47:56 +0000 | [diff] [blame] | 138 | (new 32-bit) */ |
Jean-Philippe Bernardy | 34d2a71 | 2005-01-24 14:26:24 +0000 | [diff] [blame] | 139 | unsigned int last_word; /* 0xAA55 */ |
Björn Stenberg | 827cee3 | 2002-05-30 19:47:56 +0000 | [diff] [blame] | 140 | |
Björn Stenberg | 827cee3 | 2002-05-30 19:47:56 +0000 | [diff] [blame] | 141 | /**** FAT32 specific *****/ |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 142 | long bpb_fatsz32; |
| 143 | long bpb_rootclus; |
| 144 | long bpb_fsinfo; |
Björn Stenberg | 827cee3 | 2002-05-30 19:47:56 +0000 | [diff] [blame] | 145 | |
| 146 | /* variables for internal use */ |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 147 | 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 Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 153 | struct fsinfo fsinfo; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 154 | #ifdef HAVE_FAT16SUPPORT |
Jörg Hohensohn | fb0739b | 2004-10-14 07:52:08 +0000 | [diff] [blame] | 155 | int bpb_rootentcnt; /* Number of dir entries in the root */ |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 156 | /* internals for FAT16 support */ |
| 157 | bool is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */ |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 158 | unsigned int rootdiroffset; /* sector offset of root dir relative to start |
| 159 | * of first pseudo cluster */ |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 160 | #endif /* #ifdef HAVE_FAT16SUPPORT */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 161 | #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 Stenberg | 827cee3 | 2002-05-30 19:47:56 +0000 | [diff] [blame] | 165 | }; |
| 166 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 167 | static struct bpb fat_bpbs[NUM_VOLUMES]; /* mounted partition info */ |
Björn Stenberg | 827cee3 | 2002-05-30 19:47:56 +0000 | [diff] [blame] | 168 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 169 | static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)); |
Jens Arnold | d851d98 | 2005-01-05 00:42:24 +0000 | [diff] [blame] | 170 | static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb)); |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 171 | static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)); |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 172 | static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,) long secnum, bool dirty); |
Jens Arnold | e4e1f2c | 2005-04-23 13:15:25 +0000 | [diff] [blame] | 173 | static void create_dos_name(const unsigned char *name, unsigned char *newname); |
Jens Arnold | dbf7f51 | 2005-02-16 20:45:56 +0000 | [diff] [blame] | 174 | static void randomize_dos_name(unsigned char *name); |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 175 | static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,) unsigned long start); |
| 176 | static int transfer(IF_MV2(struct bpb* fat_bpb,) unsigned long start, long count, char* buf, bool write ); |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 177 | |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 178 | #define FAT_CACHE_SIZE 0x20 |
| 179 | #define FAT_CACHE_MASK (FAT_CACHE_SIZE-1) |
| 180 | |
| 181 | struct fat_cache_entry |
| 182 | { |
Jean-Philippe Bernardy | 907ba58 | 2005-02-25 09:11:29 +0000 | [diff] [blame] | 183 | long secnum; |
Björn Stenberg | 3ad66b9 | 2002-05-29 11:56:42 +0000 | [diff] [blame] | 184 | bool inuse; |
| 185 | bool dirty; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 186 | #ifdef HAVE_MULTIVOLUME |
| 187 | struct bpb* fat_vol ; /* shared cache for all volumes */ |
| 188 | #endif |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 189 | }; |
| 190 | |
Björn Stenberg | 3ad66b9 | 2002-05-29 11:56:42 +0000 | [diff] [blame] | 191 | static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE]; |
| 192 | static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE]; |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 193 | static struct mutex cache_mutex; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 194 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 195 | static long cluster2sec(IF_MV2(struct bpb* fat_bpb,) long cluster) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 196 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 197 | #ifndef HAVE_MULTIVOLUME |
| 198 | struct bpb* fat_bpb = &fat_bpbs[0]; |
| 199 | #endif |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 200 | #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 Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 204 | const long zerocluster = 2; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 205 | #endif |
Jens Arnold | 2580cdf | 2005-02-03 22:58:50 +0000 | [diff] [blame] | 206 | |
| 207 | if (cluster > (long)(fat_bpb->dataclusters + 1)) |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 208 | { |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 209 | DEBUGF( "cluster2sec() - Bad cluster number (%ld)\n", cluster); |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 210 | return -1; |
| 211 | } |
| 212 | |
| 213 | return (cluster - zerocluster) * fat_bpb->bpb_secperclus |
| 214 | + fat_bpb->firstdatasector; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 215 | } |
| 216 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 217 | long fat_startsector(IF_MV_NONVOID(int volume)) |
Björn Stenberg | 113fffa | 2002-08-13 13:21:55 +0000 | [diff] [blame] | 218 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 219 | #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 Stenberg | 113fffa | 2002-08-13 13:21:55 +0000 | [diff] [blame] | 224 | } |
| 225 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 226 | void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free) |
Björn Stenberg | 6fb512a | 2002-11-12 11:32:26 +0000 | [diff] [blame] | 227 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 228 | #ifndef HAVE_MULTIVOLUME |
| 229 | const int volume = 0; |
| 230 | #endif |
| 231 | struct bpb* fat_bpb = &fat_bpbs[volume]; |
Björn Stenberg | 6fb512a | 2002-11-12 11:32:26 +0000 | [diff] [blame] | 232 | if (size) |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 233 | *size = fat_bpb->dataclusters * fat_bpb->bpb_secperclus / 2; |
Björn Stenberg | 6fb512a | 2002-11-12 11:32:26 +0000 | [diff] [blame] | 234 | if (free) |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 235 | *free = fat_bpb->fsinfo.freecount * fat_bpb->bpb_secperclus / 2; |
Björn Stenberg | 6fb512a | 2002-11-12 11:32:26 +0000 | [diff] [blame] | 236 | } |
| 237 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 238 | void fat_init(void) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 239 | { |
Björn Stenberg | c442a68 | 2002-11-15 11:20:33 +0000 | [diff] [blame] | 240 | unsigned int i; |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 241 | |
| 242 | mutex_init(&cache_mutex); |
| 243 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 244 | /* mark the FAT cache as unused */ |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 245 | for(i = 0;i < FAT_CACHE_SIZE;i++) |
| 246 | { |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 247 | fat_cache[i].secnum = 8; /* We use a "safe" sector just in case */ |
Björn Stenberg | 3ad66b9 | 2002-05-29 11:56:42 +0000 | [diff] [blame] | 248 | fat_cache[i].inuse = false; |
| 249 | fat_cache[i].dirty = false; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 250 | #ifdef HAVE_MULTIVOLUME |
| 251 | fat_cache[i].fat_vol = NULL; |
| 252 | #endif |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 253 | } |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 254 | #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 Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 263 | int fat_mount(IF_MV2(int volume,) IF_MV2(int drive,) long startsector) |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 264 | { |
| 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 Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 271 | long datasec; |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 272 | #ifdef HAVE_FAT16SUPPORT |
| 273 | int rootdirsectors; |
| 274 | #endif |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 275 | |
| 276 | /* Read the sector */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 277 | rc = ata_read_sectors(IF_MV2(drive,) startsector,1,buf); |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 278 | if(rc) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 279 | { |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 280 | DEBUGF( "fat_mount() - Couldn't read BPB (error code %d)\n", rc); |
| 281 | return rc * 10 - 1; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 282 | } |
| 283 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 284 | memset(fat_bpb, 0, sizeof(struct bpb)); |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 285 | fat_bpb->startsector = startsector; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 286 | #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 Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 300 | |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 301 | /* calculate a few commonly used values */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 302 | if (fat_bpb->bpb_fatsz16 != 0) |
| 303 | fat_bpb->fatsize = fat_bpb->bpb_fatsz16; |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 304 | else |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 305 | fat_bpb->fatsize = fat_bpb->bpb_fatsz32; |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 306 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 307 | if (fat_bpb->bpb_totsec16 != 0) |
| 308 | fat_bpb->totalsectors = fat_bpb->bpb_totsec16; |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 309 | else |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 310 | fat_bpb->totalsectors = fat_bpb->bpb_totsec32; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 311 | |
| 312 | #ifdef HAVE_FAT16SUPPORT |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 313 | fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT); |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 314 | rootdirsectors = ((fat_bpb->bpb_rootentcnt * 32) |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 315 | + (fat_bpb->bpb_bytspersec - 1)) / fat_bpb->bpb_bytspersec; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 316 | #endif /* #ifdef HAVE_FAT16SUPPORT */ |
| 317 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 318 | fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 319 | #ifdef HAVE_FAT16SUPPORT |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 320 | + rootdirsectors |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 321 | #endif |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 322 | + fat_bpb->bpb_numfats * fat_bpb->fatsize; |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 323 | |
| 324 | /* Determine FAT type */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 325 | datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector; |
| 326 | fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus; |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 327 | |
| 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 Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 333 | if ( fat_bpb->bpb_fatsz16 ) |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 334 | #else |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 335 | if ( fat_bpb->dataclusters < 65525 ) |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 336 | #endif |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 337 | { /* FAT16 */ |
| 338 | #ifdef HAVE_FAT16SUPPORT |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 339 | fat_bpb->is_fat16 = true; |
| 340 | if (fat_bpb->dataclusters < 4085) |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 341 | { /* FAT12 */ |
| 342 | DEBUGF("This is FAT12. Go away!\n"); |
| 343 | return -2; |
| 344 | } |
| 345 | #else /* #ifdef HAVE_FAT16SUPPORT */ |
Björn Stenberg | 697dd70 | 2002-05-02 14:05:51 +0000 | [diff] [blame] | 346 | DEBUGF("This is not FAT32. Go away!\n"); |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 347 | return -2; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 348 | #endif /* #ifndef HAVE_FAT16SUPPORT */ |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 349 | } |
| 350 | |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 351 | #ifdef HAVE_FAT16SUPPORT |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 352 | if (fat_bpb->is_fat16) |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 353 | { /* FAT16 specific part of BPB */ |
| 354 | int dirclusters; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 355 | fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt |
| 356 | + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16; |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 357 | dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1) |
| 358 | / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */ |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 359 | /* I assign negative pseudo cluster numbers for the root directory, |
| 360 | their range is counted upward until -1. */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 361 | fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data */ |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 362 | fat_bpb->rootdiroffset = dirclusters * fat_bpb->bpb_secperclus |
| 363 | - rootdirsectors; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 364 | } |
| 365 | else |
| 366 | #endif /* #ifdef HAVE_FAT16SUPPORT */ |
| 367 | { /* FAT32 specific part of BPB */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 368 | 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 Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 371 | } |
| 372 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 373 | rc = bpb_is_sane(IF_MV(fat_bpb)); |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 374 | if (rc < 0) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 375 | { |
Björn Stenberg | 697dd70 | 2002-05-02 14:05:51 +0000 | [diff] [blame] | 376 | DEBUGF( "fat_mount() - BPB is not sane\n"); |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 377 | return rc * 10 - 3; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 378 | } |
| 379 | |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 380 | #ifdef HAVE_FAT16SUPPORT |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 381 | if (fat_bpb->is_fat16) |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 382 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 383 | fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc below */ |
| 384 | fat_bpb->fsinfo.nextfree = 0xffffffff; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 385 | } |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 386 | else |
| 387 | #endif /* #ifdef HAVE_FAT16SUPPORT */ |
| 388 | { |
| 389 | /* Read the fsinfo sector */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 390 | rc = ata_read_sectors(IF_MV2(drive,) |
| 391 | startsector + fat_bpb->bpb_fsinfo, 1, buf); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 392 | 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 Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 397 | fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT); |
| 398 | fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 399 | } |
Björn Stenberg | c442a68 | 2002-11-15 11:20:33 +0000 | [diff] [blame] | 400 | |
| 401 | /* calculate freecount if unset */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 402 | if ( fat_bpb->fsinfo.freecount == 0xffffffff ) |
Björn Stenberg | c442a68 | 2002-11-15 11:20:33 +0000 | [diff] [blame] | 403 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 404 | fat_recalc_free(IF_MV(volume)); |
Björn Stenberg | c442a68 | 2002-11-15 11:20:33 +0000 | [diff] [blame] | 405 | } |
| 406 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 407 | 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 Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 410 | LDEBUGF("Sectors per cluster: %d\n",fat_bpb->bpb_secperclus); |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 411 | LDEBUGF("FAT sectors: 0x%lx\n",fat_bpb->fatsize); |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 412 | |
| 413 | #ifdef HAVE_MULTIVOLUME |
| 414 | fat_bpb->mounted = true; |
| 415 | #endif |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 416 | |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 417 | return 0; |
| 418 | } |
| 419 | |
Jörg Hohensohn | dc7534b | 2005-01-28 21:32:16 +0000 | [diff] [blame] | 420 | #ifdef HAVE_HOTSWAP |
Jörg Hohensohn | 7414687 | 2005-01-05 00:09:04 +0000 | [diff] [blame] | 421 | int fat_unmount(int volume, bool flush) |
| 422 | { |
Jens Arnold | d851d98 | 2005-01-05 00:42:24 +0000 | [diff] [blame] | 423 | int rc; |
Jörg Hohensohn | 7414687 | 2005-01-05 00:09:04 +0000 | [diff] [blame] | 424 | struct bpb* fat_bpb = &fat_bpbs[volume]; |
Jens Arnold | d851d98 | 2005-01-05 00:42:24 +0000 | [diff] [blame] | 425 | |
Jörg Hohensohn | 7414687 | 2005-01-05 00:09:04 +0000 | [diff] [blame] | 426 | if(flush) |
| 427 | { |
Jens Arnold | d851d98 | 2005-01-05 00:42:24 +0000 | [diff] [blame] | 428 | rc = flush_fat(fat_bpb); /* the clean way, while still alive */ |
Jörg Hohensohn | 7414687 | 2005-01-05 00:09:04 +0000 | [diff] [blame] | 429 | } |
| 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 Arnold | d851d98 | 2005-01-05 00:42:24 +0000 | [diff] [blame] | 444 | rc = 0; |
Jörg Hohensohn | 7414687 | 2005-01-05 00:09:04 +0000 | [diff] [blame] | 445 | } |
| 446 | fat_bpb->mounted = false; |
Jens Arnold | d851d98 | 2005-01-05 00:42:24 +0000 | [diff] [blame] | 447 | return rc; |
Jörg Hohensohn | 7414687 | 2005-01-05 00:09:04 +0000 | [diff] [blame] | 448 | } |
Jörg Hohensohn | dc7534b | 2005-01-28 21:32:16 +0000 | [diff] [blame] | 449 | #endif /* #ifdef HAVE_HOTSWAP */ |
Jörg Hohensohn | 7414687 | 2005-01-05 00:09:04 +0000 | [diff] [blame] | 450 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 451 | void fat_recalc_free(IF_MV_NONVOID(int volume)) |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 452 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 453 | #ifndef HAVE_MULTIVOLUME |
| 454 | const int volume = 0; |
| 455 | #endif |
| 456 | struct bpb* fat_bpb = &fat_bpbs[volume]; |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 457 | long free = 0; |
| 458 | unsigned long i; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 459 | #ifdef HAVE_FAT16SUPPORT |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 460 | if (fat_bpb->is_fat16) |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 461 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 462 | for (i = 0; i<fat_bpb->fatsize; i++) { |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 463 | unsigned int j; |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 464 | unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 465 | for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { |
| 466 | unsigned int c = i * CLUSTERS_PER_FAT16_SECTOR + j; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 467 | if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 468 | break; |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 469 | |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 470 | if (letoh16(fat[j]) == 0x0000) { |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 471 | free++; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 472 | if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) |
| 473 | fat_bpb->fsinfo.nextfree = c; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 474 | } |
| 475 | } |
| 476 | } |
| 477 | } |
| 478 | else |
| 479 | #endif /* #ifdef HAVE_FAT16SUPPORT */ |
| 480 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 481 | for (i = 0; i<fat_bpb->fatsize; i++) { |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 482 | unsigned int j; |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 483 | unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 484 | for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 485 | unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 486 | if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 487 | break; |
| 488 | |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 489 | if (!(letoh32(fat[j]) & 0x0fffffff)) { |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 490 | free++; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 491 | if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) |
| 492 | fat_bpb->fsinfo.nextfree = c; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 493 | } |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 494 | } |
| 495 | } |
| 496 | } |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 497 | fat_bpb->fsinfo.freecount = free; |
| 498 | update_fsinfo(IF_MV(fat_bpb)); |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 499 | } |
| 500 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 501 | static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 502 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 503 | #ifndef HAVE_MULTIVOLUME |
| 504 | struct bpb* fat_bpb = &fat_bpbs[0]; |
| 505 | #endif |
| 506 | if(fat_bpb->bpb_bytspersec != 512) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 507 | { |
Linus Nielsen Feltzing | 3fd7fe9 | 2002-05-08 08:26:38 +0000 | [diff] [blame] | 508 | DEBUGF( "bpb_is_sane() - Error: sector size is not 512 (%d)\n", |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 509 | fat_bpb->bpb_bytspersec); |
Björn Stenberg | e8bcc01 | 2002-04-27 19:37:41 +0000 | [diff] [blame] | 510 | return -1; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 511 | } |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 512 | if((long)fat_bpb->bpb_secperclus * (long)fat_bpb->bpb_bytspersec > 128L*1024L) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 513 | { |
Björn Stenberg | 08356fb | 2002-10-31 19:05:25 +0000 | [diff] [blame] | 514 | DEBUGF( "bpb_is_sane() - Error: cluster size is larger than 128K " |
Linus Nielsen Feltzing | 3fd7fe9 | 2002-05-08 08:26:38 +0000 | [diff] [blame] | 515 | "(%d * %d = %d)\n", |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 516 | fat_bpb->bpb_bytspersec, fat_bpb->bpb_secperclus, |
| 517 | fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus); |
Björn Stenberg | 08356fb | 2002-10-31 19:05:25 +0000 | [diff] [blame] | 518 | return -2; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 519 | } |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 520 | if(fat_bpb->bpb_numfats != 2) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 521 | { |
Linus Nielsen Feltzing | 3fd7fe9 | 2002-05-08 08:26:38 +0000 | [diff] [blame] | 522 | DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)\n", |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 523 | fat_bpb->bpb_numfats); |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 524 | } |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 525 | if(fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 526 | { |
Björn Stenberg | 697dd70 | 2002-05-02 14:05:51 +0000 | [diff] [blame] | 527 | DEBUGF( "bpb_is_sane() - Warning: Non-standard " |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 528 | "media type (0x%02x)\n", |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 529 | fat_bpb->bpb_media); |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 530 | } |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 531 | if(fat_bpb->last_word != 0xaa55) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 532 | { |
Björn Stenberg | 697dd70 | 2002-05-02 14:05:51 +0000 | [diff] [blame] | 533 | DEBUGF( "bpb_is_sane() - Error: Last word is not " |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 534 | "0xaa55 (0x%04x)\n", fat_bpb->last_word); |
Björn Stenberg | 08356fb | 2002-10-31 19:05:25 +0000 | [diff] [blame] | 535 | return -3; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 536 | } |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 537 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 538 | if (fat_bpb->fsinfo.freecount > |
| 539 | (fat_bpb->totalsectors - fat_bpb->firstdatasector)/ |
| 540 | fat_bpb->bpb_secperclus) |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 541 | { |
| 542 | DEBUGF( "bpb_is_sane() - Error: FSInfo.Freecount > disk size " |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 543 | "(0x%04lx)\n", fat_bpb->fsinfo.freecount); |
Björn Stenberg | 08356fb | 2002-10-31 19:05:25 +0000 | [diff] [blame] | 544 | return -4; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 545 | } |
| 546 | |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 547 | return 0; |
| 548 | } |
| 549 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 550 | static void flush_fat_sector(struct fat_cache_entry *fce, |
| 551 | unsigned char *sectorbuf) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 552 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 553 | int rc; |
Jean-Philippe Bernardy | 907ba58 | 2005-02-25 09:11:29 +0000 | [diff] [blame] | 554 | long secnum; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 555 | |
| 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 Bernardy | fc19445 | 2005-02-25 18:50:16 +0000 | [diff] [blame] | 569 | panicf("flush_fat_sector() - Could not write sector %ld" |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 570 | " (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 Bernardy | fc19445 | 2005-02-25 18:50:16 +0000 | [diff] [blame] | 589 | panicf("flush_fat_sector() - Could not write sector %ld" |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 590 | " (error %d)\n", |
| 591 | secnum, rc); |
| 592 | } |
| 593 | } |
| 594 | fce->dirty = false; |
| 595 | } |
| 596 | |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 597 | /* Note: The returned pointer is only safely valid until the next |
| 598 | task switch! (Any subsequent ata read/write may yield.) */ |
| 599 | static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,) |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 600 | long fatsector, bool dirty) |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 601 | { |
| 602 | #ifndef HAVE_MULTIVOLUME |
| 603 | struct bpb* fat_bpb = &fat_bpbs[0]; |
| 604 | #endif |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 605 | long secnum = fatsector + fat_bpb->bpb_rsvdseccnt; |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 606 | int cache_index = secnum & FAT_CACHE_MASK; |
Linus Nielsen Feltzing | 4382c68 | 2002-11-09 09:23:43 +0000 | [diff] [blame] | 607 | struct fat_cache_entry *fce = &fat_cache[cache_index]; |
| 608 | unsigned char *sectorbuf = &fat_cache_sectors[cache_index][0]; |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 609 | int rc; |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 610 | |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 611 | mutex_lock(&cache_mutex); /* make changes atomic */ |
| 612 | |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 613 | /* Delete the cache entry if it isn't the sector we want */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 614 | if(fce->inuse && (fce->secnum != secnum |
| 615 | #ifdef HAVE_MULTIVOLUME |
| 616 | || fce->fat_vol != fat_bpb |
| 617 | #endif |
| 618 | )) |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 619 | { |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 620 | /* Write back if it is dirty */ |
Linus Nielsen Feltzing | 4382c68 | 2002-11-09 09:23:43 +0000 | [diff] [blame] | 621 | if(fce->dirty) |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 622 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 623 | flush_fat_sector(fce, sectorbuf); |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 624 | } |
Linus Nielsen Feltzing | 4382c68 | 2002-11-09 09:23:43 +0000 | [diff] [blame] | 625 | fce->inuse = false; |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 626 | } |
Linus Nielsen Feltzing | 4382c68 | 2002-11-09 09:23:43 +0000 | [diff] [blame] | 627 | |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 628 | /* Load the sector if it is not cached */ |
Linus Nielsen Feltzing | 4382c68 | 2002-11-09 09:23:43 +0000 | [diff] [blame] | 629 | if(!fce->inuse) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 630 | { |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 631 | rc = ata_read_sectors(IF_MV2(fat_bpb->drive,) |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 632 | secnum + fat_bpb->startsector,1, |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 633 | sectorbuf); |
| 634 | if(rc < 0) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 635 | { |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 636 | DEBUGF( "cache_fat_sector() - Could not read sector %ld" |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 637 | " (error %d)\n", secnum, rc); |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 638 | mutex_unlock(&cache_mutex); |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 639 | return NULL; |
| 640 | } |
Linus Nielsen Feltzing | 4382c68 | 2002-11-09 09:23:43 +0000 | [diff] [blame] | 641 | fce->inuse = true; |
| 642 | fce->secnum = secnum; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 643 | #ifdef HAVE_MULTIVOLUME |
| 644 | fce->fat_vol = fat_bpb; |
| 645 | #endif |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 646 | } |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 647 | if (dirty) |
| 648 | fce->dirty = true; /* dirt remains, sticky until flushed */ |
| 649 | mutex_unlock(&cache_mutex); |
Linus Nielsen Feltzing | 4382c68 | 2002-11-09 09:23:43 +0000 | [diff] [blame] | 650 | return sectorbuf; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 651 | } |
| 652 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 653 | static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,) unsigned long startcluster) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 654 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 655 | #ifndef HAVE_MULTIVOLUME |
| 656 | struct bpb* fat_bpb = &fat_bpbs[0]; |
| 657 | #endif |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 658 | unsigned long sector; |
| 659 | unsigned long offset; |
| 660 | unsigned long i; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 661 | |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 662 | #ifdef HAVE_FAT16SUPPORT |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 663 | if (fat_bpb->is_fat16) |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 664 | { |
| 665 | sector = startcluster / CLUSTERS_PER_FAT16_SECTOR; |
| 666 | offset = startcluster % CLUSTERS_PER_FAT16_SECTOR; |
Linus Nielsen Feltzing | 41249b7 | 2003-07-16 22:28:24 +0000 | [diff] [blame] | 667 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 668 | for (i = 0; i<fat_bpb->fatsize; i++) { |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 669 | unsigned int j; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 670 | unsigned int nr = (i + sector) % fat_bpb->fatsize; |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 671 | unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 672 | if ( !fat ) |
| 673 | break; |
| 674 | for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { |
| 675 | int k = (j + offset) % CLUSTERS_PER_FAT16_SECTOR; |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 676 | if (letoh16(fat[k]) == 0x0000) { |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 677 | 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 Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 680 | if ( c < 2 || c > fat_bpb->dataclusters+1 ) |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 681 | continue; |
| 682 | LDEBUGF("find_free_cluster(%x) == %x\n",startcluster,c); |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 683 | fat_bpb->fsinfo.nextfree = c; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 684 | return c; |
| 685 | } |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 686 | } |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 687 | offset = 0; |
Björn Stenberg | 08356fb | 2002-10-31 19:05:25 +0000 | [diff] [blame] | 688 | } |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 689 | } |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 690 | else |
| 691 | #endif /* #ifdef HAVE_FAT16SUPPORT */ |
| 692 | { |
| 693 | sector = startcluster / CLUSTERS_PER_FAT_SECTOR; |
| 694 | offset = startcluster % CLUSTERS_PER_FAT_SECTOR; |
| 695 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 696 | for (i = 0; i<fat_bpb->fatsize; i++) { |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 697 | unsigned int j; |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 698 | unsigned long nr = (i + sector) % fat_bpb->fatsize; |
| 699 | unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 700 | if ( !fat ) |
| 701 | break; |
| 702 | for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { |
| 703 | int k = (j + offset) % CLUSTERS_PER_FAT_SECTOR; |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 704 | if (!(letoh32(fat[k]) & 0x0fffffff)) { |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 705 | unsigned long c = nr * CLUSTERS_PER_FAT_SECTOR + k; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 706 | /* Ignore the reserved clusters 0 & 1, and also |
| 707 | cluster numbers out of bounds */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 708 | if ( c < 2 || c > fat_bpb->dataclusters+1 ) |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 709 | continue; |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 710 | LDEBUGF("find_free_cluster(%lx) == %lx\n",startcluster,c); |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 711 | fat_bpb->fsinfo.nextfree = c; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 712 | return c; |
| 713 | } |
| 714 | } |
| 715 | offset = 0; |
| 716 | } |
| 717 | } |
| 718 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 719 | LDEBUGF("find_free_cluster(%lx) == 0\n",startcluster); |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 720 | return 0; /* 0 is an illegal cluster number */ |
| 721 | } |
| 722 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 723 | static int update_fat_entry(IF_MV2(struct bpb* fat_bpb,) |
| 724 | unsigned long entry, |
| 725 | unsigned long val) |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 726 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 727 | #ifndef HAVE_MULTIVOLUME |
| 728 | struct bpb* fat_bpb = &fat_bpbs[0]; |
| 729 | #endif |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 730 | #ifdef HAVE_FAT16SUPPORT |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 731 | if (fat_bpb->is_fat16) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 732 | { |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 733 | int sector = entry / CLUSTERS_PER_FAT16_SECTOR; |
| 734 | int offset = entry % CLUSTERS_PER_FAT16_SECTOR; |
| 735 | unsigned short* sec; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 736 | |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 737 | val &= 0xFFFF; |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 738 | |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 739 | LDEBUGF("update_fat_entry(%x,%x)\n",entry,val); |
Björn Stenberg | 307f5d8 | 2002-11-01 15:26:06 +0000 | [diff] [blame] | 740 | |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 741 | if (entry==val) |
Jean-Philippe Bernardy | 06ded52 | 2005-02-28 10:52:28 +0000 | [diff] [blame] | 742 | panicf("Creating FAT loop: %lx,%lx\n",entry,val); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 743 | |
| 744 | if ( entry < 2 ) |
Jean-Philippe Bernardy | 06ded52 | 2005-02-28 10:52:28 +0000 | [diff] [blame] | 745 | panicf("Updating reserved FAT entry %ld.\n",entry); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 746 | |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 747 | sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 748 | if (!sec) |
| 749 | { |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 750 | DEBUGF( "update_fat_entry() - Could not cache sector %d\n", sector); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 751 | return -1; |
| 752 | } |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 753 | |
| 754 | if ( val ) { |
Dave Chapman | 207c950 | 2005-10-06 21:23:59 +0000 | [diff] [blame] | 755 | if (letoh16(sec[offset]) == 0x0000 && fat_bpb->fsinfo.freecount > 0) |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 756 | fat_bpb->fsinfo.freecount--; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 757 | } |
| 758 | else { |
Dave Chapman | 207c950 | 2005-10-06 21:23:59 +0000 | [diff] [blame] | 759 | if (letoh16(sec[offset])) |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 760 | fat_bpb->fsinfo.freecount++; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 761 | } |
| 762 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 763 | LDEBUGF("update_fat_entry: %d free clusters\n", fat_bpb->fsinfo.freecount); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 764 | |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 765 | sec[offset] = htole16(val); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 766 | } |
| 767 | else |
| 768 | #endif /* #ifdef HAVE_FAT16SUPPORT */ |
| 769 | { |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 770 | long sector = entry / CLUSTERS_PER_FAT_SECTOR; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 771 | int offset = entry % CLUSTERS_PER_FAT_SECTOR; |
Jean-Philippe Bernardy | fc19445 | 2005-02-25 18:50:16 +0000 | [diff] [blame] | 772 | unsigned long* sec; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 773 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 774 | LDEBUGF("update_fat_entry(%lx,%lx)\n",entry,val); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 775 | |
| 776 | if (entry==val) |
Jean-Philippe Bernardy | fc19445 | 2005-02-25 18:50:16 +0000 | [diff] [blame] | 777 | panicf("Creating FAT loop: %lx,%lx\n",entry,val); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 778 | |
| 779 | if ( entry < 2 ) |
Jean-Philippe Bernardy | fc19445 | 2005-02-25 18:50:16 +0000 | [diff] [blame] | 780 | panicf("Updating reserved FAT entry %ld.\n",entry); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 781 | |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 782 | sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 783 | if (!sec) |
| 784 | { |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 785 | DEBUGF( "update_fat_entry() - Could not cache sector %ld\n", sector); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 786 | return -1; |
| 787 | } |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 788 | |
| 789 | if ( val ) { |
Dave Chapman | 207c950 | 2005-10-06 21:23:59 +0000 | [diff] [blame] | 790 | if (!(letoh32(sec[offset]) & 0x0fffffff) && |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 791 | fat_bpb->fsinfo.freecount > 0) |
| 792 | fat_bpb->fsinfo.freecount--; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 793 | } |
| 794 | else { |
Dave Chapman | 207c950 | 2005-10-06 21:23:59 +0000 | [diff] [blame] | 795 | if (letoh32(sec[offset]) & 0x0fffffff) |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 796 | fat_bpb->fsinfo.freecount++; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 797 | } |
| 798 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 799 | LDEBUGF("update_fat_entry: %ld free clusters\n", fat_bpb->fsinfo.freecount); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 800 | |
| 801 | /* don't change top 4 bits */ |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 802 | sec[offset] &= htole32(0xf0000000); |
| 803 | sec[offset] |= htole32(val & 0x0fffffff); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 804 | } |
Björn Stenberg | 1dff4b6 | 2002-04-26 16:44:58 +0000 | [diff] [blame] | 805 | |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 806 | return 0; |
| 807 | } |
| 808 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 809 | static long read_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 810 | { |
Jörg Hohensohn | 19934a1 | 2004-12-28 22:35:10 +0000 | [diff] [blame] | 811 | #ifdef HAVE_FAT16SUPPORT |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 812 | #ifndef HAVE_MULTIVOLUME |
| 813 | struct bpb* fat_bpb = &fat_bpbs[0]; |
| 814 | #endif |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 815 | if (fat_bpb->is_fat16) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 816 | { |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 817 | int sector = entry / CLUSTERS_PER_FAT16_SECTOR; |
| 818 | int offset = entry % CLUSTERS_PER_FAT16_SECTOR; |
| 819 | unsigned short* sec; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 820 | |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 821 | sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 822 | if (!sec) |
| 823 | { |
| 824 | DEBUGF( "read_fat_entry() - Could not cache sector %d\n", sector); |
| 825 | return -1; |
| 826 | } |
| 827 | |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 828 | return letoh16(sec[offset]); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 829 | } |
| 830 | else |
| 831 | #endif /* #ifdef HAVE_FAT16SUPPORT */ |
| 832 | { |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 833 | long sector = entry / CLUSTERS_PER_FAT_SECTOR; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 834 | int offset = entry % CLUSTERS_PER_FAT_SECTOR; |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 835 | unsigned long* sec; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 836 | |
Jörg Hohensohn | 50dba1f | 2005-01-04 23:01:25 +0000 | [diff] [blame] | 837 | sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 838 | if (!sec) |
| 839 | { |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 840 | DEBUGF( "read_fat_entry() - Could not cache sector %ld\n", sector); |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 841 | return -1; |
| 842 | } |
| 843 | |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 844 | return letoh32(sec[offset]) & 0x0fffffff; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 845 | } |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 846 | } |
| 847 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 848 | static long get_next_cluster(IF_MV2(struct bpb* fat_bpb,) long cluster) |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 849 | { |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 850 | long next_cluster; |
| 851 | long eof_mark = FAT_EOF_MARK; |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 852 | |
| 853 | #ifdef HAVE_FAT16SUPPORT |
Jörg Hohensohn | 19934a1 | 2004-12-28 22:35:10 +0000 | [diff] [blame] | 854 | #ifndef HAVE_MULTIVOLUME |
| 855 | struct bpb* fat_bpb = &fat_bpbs[0]; |
| 856 | #endif |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 857 | if (fat_bpb->is_fat16) |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 858 | { |
| 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 Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 864 | next_cluster = read_fat_entry(IF_MV2(fat_bpb,) cluster); |
Linus Nielsen Feltzing | 8083e7a | 2002-05-09 22:57:54 +0000 | [diff] [blame] | 865 | |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 866 | /* is this last cluster in chain? */ |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 867 | if ( next_cluster >= eof_mark ) |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 868 | return 0; |
| 869 | else |
| 870 | return next_cluster; |
| 871 | } |
| 872 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 873 | static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)) |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 874 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 875 | #ifndef HAVE_MULTIVOLUME |
| 876 | struct bpb* fat_bpb = &fat_bpbs[0]; |
| 877 | #endif |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 878 | unsigned char fsinfo[SECTOR_SIZE]; |
Jean-Philippe Bernardy | 907ba58 | 2005-02-25 09:11:29 +0000 | [diff] [blame] | 879 | unsigned long* intptr; |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 880 | int rc; |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 881 | |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 882 | #ifdef HAVE_FAT16SUPPORT |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 883 | if (fat_bpb->is_fat16) |
Jörg Hohensohn | 7f7afe4 | 2004-09-23 21:43:43 +0000 | [diff] [blame] | 884 | return 0; /* FAT16 has no FsInfo */ |
| 885 | #endif /* #ifdef HAVE_FAT16SUPPORT */ |
| 886 | |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 887 | /* update fsinfo */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 888 | rc = ata_read_sectors(IF_MV2(fat_bpb->drive,) |
| 889 | fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1,fsinfo); |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 890 | if (rc < 0) |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 891 | { |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 892 | DEBUGF( "flush_fat() - Couldn't read FSInfo (error code %d)\n", rc); |
| 893 | return rc * 10 - 1; |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 894 | } |
Jean-Philippe Bernardy | 907ba58 | 2005-02-25 09:11:29 +0000 | [diff] [blame] | 895 | intptr = (long*)&(fsinfo[FSINFO_FREECOUNT]); |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 896 | *intptr = htole32(fat_bpb->fsinfo.freecount); |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 897 | |
Jean-Philippe Bernardy | 907ba58 | 2005-02-25 09:11:29 +0000 | [diff] [blame] | 898 | intptr = (long*)&(fsinfo[FSINFO_NEXTFREE]); |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 899 | *intptr = htole32(fat_bpb->fsinfo.nextfree); |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 900 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 901 | rc = ata_write_sectors(IF_MV2(fat_bpb->drive,) |
| 902 | fat_bpb->startsector + fat_bpb->bpb_fsinfo,1,fsinfo); |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 903 | if (rc < 0) |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 904 | { |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 905 | DEBUGF( "flush_fat() - Couldn't write FSInfo (error code %d)\n", rc); |
| 906 | return rc * 10 - 2; |
Björn Stenberg | b17fe5a | 2002-12-09 15:39:32 +0000 | [diff] [blame] | 907 | } |
| 908 | |
| 909 | return 0; |
| 910 | } |
| 911 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 912 | static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb)) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 913 | { |
| 914 | int i; |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 915 | int rc; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 916 | unsigned char *sec; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 917 | LDEBUGF("flush_fat()\n"); |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 918 | |
Jörg Hohensohn | 7414687 | 2005-01-05 00:09:04 +0000 | [diff] [blame] | 919 | mutex_lock(&cache_mutex); |
Linus Nielsen Feltzing | 114fce0 | 2002-05-09 21:27:49 +0000 | [diff] [blame] | 920 | for(i = 0;i < FAT_CACHE_SIZE;i++) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 921 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 922 | struct fat_cache_entry *fce = &fat_cache[i]; |
Jörg Hohensohn | 7414687 | 2005-01-05 00:09:04 +0000 | [diff] [blame] | 923 | if(fce->inuse |
| 924 | #ifdef HAVE_MULTIVOLUME |
| 925 | && fce->fat_vol == fat_bpb |
| 926 | #endif |
| 927 | && fce->dirty) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 928 | { |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 929 | sec = fat_cache_sectors[i]; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 930 | flush_fat_sector(fce, sec); |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 931 | } |
| 932 | } |
Jörg Hohensohn | 7414687 | 2005-01-05 00:09:04 +0000 | [diff] [blame] | 933 | mutex_unlock(&cache_mutex); |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 934 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 935 | rc = update_fsinfo(IF_MV(fat_bpb)); |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 936 | if (rc < 0) |
| 937 | return rc * 10 - 3; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 938 | |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 939 | return 0; |
| 940 | } |
| 941 | |
Björn Stenberg | b5184d7 | 2002-11-22 23:51:46 +0000 | [diff] [blame] | 942 | static void fat_time(unsigned short* date, |
| 943 | unsigned short* time, |
| 944 | unsigned short* tenth ) |
| 945 | { |
Dave Chapman | 8c800cf | 2005-12-04 15:23:47 +0000 | [diff] [blame] | 946 | #ifdef CONFIG_RTC |
Björn Stenberg | b5184d7 | 2002-11-22 23:51:46 +0000 | [diff] [blame] | 947 | 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 Feltzing | d9e8bfe | 2003-11-03 00:59:04 +0000 | [diff] [blame] | 961 | #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 Arnold | b2964ca | 2005-09-06 18:03:50 +0000 | [diff] [blame] | 995 | /* 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 Feltzing | d9e8bfe | 2003-11-03 00:59:04 +0000 | [diff] [blame] | 1004 | } |
| 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 Chapman | 8c800cf | 2005-12-04 15:23:47 +0000 | [diff] [blame] | 1028 | #endif /* CONFIG_RTC */ |
Björn Stenberg | b5184d7 | 2002-11-22 23:51:46 +0000 | [diff] [blame] | 1029 | } |
| 1030 | |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1031 | static int write_long_name(struct fat_file* file, |
| 1032 | unsigned int firstentry, |
| 1033 | unsigned int numentries, |
Jens Arnold | 0ceaa5e | 2004-08-17 01:45:48 +0000 | [diff] [blame] | 1034 | const unsigned char* name, |
| 1035 | const unsigned char* shortname, |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1036 | bool is_directory) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1037 | { |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1038 | 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 Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1042 | unsigned char chksum = 0; |
Marcoen Hirschberg | b0fee17 | 2005-12-06 13:27:15 +0000 | [diff] [blame^] | 1043 | unsigned int i, j=0; |
| 1044 | unsigned int nameidx=0, namelen = utf8length(name); |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1045 | int rc; |
Marcoen Hirschberg | b0fee17 | 2005-12-06 13:27:15 +0000 | [diff] [blame^] | 1046 | unsigned short name_utf16[namelen + 1]; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1047 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1048 | LDEBUGF("write_long_name(file:%lx, first:%d, num:%d, name:%s)\n", |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1049 | file->firstcluster, firstentry, numentries, name); |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1050 | |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1051 | rc = fat_seek(file, sector); |
| 1052 | if (rc<0) |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1053 | return rc * 10 - 1; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1054 | |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1055 | rc = fat_readwrite(file, 1, buf, false); |
| 1056 | if (rc<1) |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1057 | return rc * 10 - 2; |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1058 | |
| 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 Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1064 | 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 Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1068 | |
Marcoen Hirschberg | b0fee17 | 2005-12-06 13:27:15 +0000 | [diff] [blame^] | 1069 | /* 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 Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1074 | 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 Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1080 | return rc * 10 - 3; |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1081 | |
| 1082 | rc = fat_readwrite(file, 1, buf, true); |
| 1083 | if (rc<1) |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1084 | return rc * 10 - 4; |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1085 | |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1086 | /* read next sector */ |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1087 | rc = fat_readwrite(file, 1, buf, false); |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1088 | if (rc<0) { |
| 1089 | LDEBUGF("Failed writing new sector: %d\n",rc); |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1090 | return rc * 10 - 5; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1091 | } |
| 1092 | if (rc==0) |
| 1093 | /* end of dir */ |
| 1094 | memset(buf, 0, sizeof buf); |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1095 | |
| 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 Hirschberg | b0fee17 | 2005-12-06 13:27:15 +0000 | [diff] [blame^] | 1112 | unsigned int k, l = nameidx; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1113 | |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1114 | entry[FATLONG_ORDER] = numentries-i-1; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1115 | if (i==0) { |
| 1116 | /* mark this as last long entry */ |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1117 | entry[FATLONG_ORDER] |= 0x40; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1118 | |
| 1119 | /* pad name with 0xffff */ |
Marcoen Hirschberg | b0fee17 | 2005-12-06 13:27:15 +0000 | [diff] [blame^] | 1120 | for (k=1; k<11; k++) entry[k] = 0xff; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1121 | 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 Hirschberg | b0fee17 | 2005-12-06 13:27:15 +0000 | [diff] [blame^] | 1126 | entry[k*2 + 1] = (unsigned char)(name_utf16[l] & 0xff); |
| 1127 | entry[k*2 + 2] = (unsigned char)(name_utf16[l++] >> 8); |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1128 | } |
| 1129 | for (k=0; k<6 && l <= namelen; k++) { |
Marcoen Hirschberg | b0fee17 | 2005-12-06 13:27:15 +0000 | [diff] [blame^] | 1130 | entry[k*2 + 14] = (unsigned char)(name_utf16[l] & 0xff); |
| 1131 | entry[k*2 + 15] = (unsigned char)(name_utf16[l++] >> 8); |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1132 | } |
| 1133 | for (k=0; k<2 && l <= namelen; k++) { |
Marcoen Hirschberg | b0fee17 | 2005-12-06 13:27:15 +0000 | [diff] [blame^] | 1134 | entry[k*2 + 28] = (unsigned char)(name_utf16[l] & 0xff); |
| 1135 | entry[k*2 + 29] = (unsigned char)(name_utf16[l++] >> 8); |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1136 | } |
| 1137 | |
| 1138 | entry[FATDIR_ATTR] = FAT_ATTR_LONG_NAME; |
| 1139 | entry[FATDIR_FSTCLUSLO] = 0; |
| 1140 | entry[FATLONG_TYPE] = 0; |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1141 | entry[FATLONG_CHKSUM] = chksum; |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1142 | LDEBUGF("Longname entry %d: %s\n", idx, name+nameidx); |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1143 | } |
| 1144 | else { |
| 1145 | /* shortname entry */ |
Björn Stenberg | b5184d7 | 2002-11-22 23:51:46 +0000 | [diff] [blame] | 1146 | unsigned short date=0, time=0, tenth=0; |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1147 | LDEBUGF("Shortname entry: %s\n", shortname); |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1148 | strncpy(entry + FATDIR_NAME, shortname, 11); |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1149 | entry[FATDIR_ATTR] = is_directory?FAT_ATTR_DIRECTORY:0; |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1150 | entry[FATDIR_NTRES] = 0; |
Björn Stenberg | b5184d7 | 2002-11-22 23:51:46 +0000 | [diff] [blame] | 1151 | |
Björn Stenberg | b5184d7 | 2002-11-22 23:51:46 +0000 | [diff] [blame] | 1152 | fat_time(&date, &time, &tenth); |
Björn Stenberg | b5184d7 | 2002-11-22 23:51:46 +0000 | [diff] [blame] | 1153 | entry[FATDIR_CRTTIMETENTH] = tenth; |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 1154 | *(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 Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1159 | } |
| 1160 | idx++; |
| 1161 | nameidx -= NAME_BYTES_PER_ENTRY; |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1162 | } |
| 1163 | |
| 1164 | /* update last sector */ |
| 1165 | rc = fat_seek(file, sector); |
| 1166 | if (rc<0) |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1167 | return rc * 10 - 6; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1168 | |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1169 | rc = fat_readwrite(file, 1, buf, true); |
| 1170 | if (rc<1) |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1171 | return rc * 10 - 7; |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1172 | |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1173 | return 0; |
| 1174 | } |
| 1175 | |
Jonas Häggqvist | 98143f5 | 2005-09-07 01:35:15 +0000 | [diff] [blame] | 1176 | static 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 Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1185 | static int add_dir_entry(struct fat_dir* dir, |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1186 | struct fat_file* file, |
Jens Arnold | 0ceaa5e | 2004-08-17 01:45:48 +0000 | [diff] [blame] | 1187 | const char* name, |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1188 | bool is_directory, |
| 1189 | bool dotdir) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1190 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 1191 | #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 Stenberg | e8bcc01 | 2002-04-27 19:37:41 +0000 | [diff] [blame] | 1196 | unsigned char buf[SECTOR_SIZE]; |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1197 | unsigned char shortname[12]; |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1198 | int rc; |
Björn Stenberg | c442a68 | 2002-11-15 11:20:33 +0000 | [diff] [blame] | 1199 | unsigned int sector; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1200 | bool done = false; |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1201 | int entries_needed, entries_found = 0; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1202 | int firstentry; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1203 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1204 | LDEBUGF( "add_dir_entry(%s,%lx)\n", |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1205 | name, file->firstcluster); |
| 1206 | |
Jonas Häggqvist | 98143f5 | 2005-09-07 01:35:15 +0000 | [diff] [blame] | 1207 | /* 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 Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 1216 | #ifdef HAVE_MULTIVOLUME |
| 1217 | file->volume = dir->file.volume; /* inherit the volume, to make sure */ |
| 1218 | #endif |
| 1219 | |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1220 | /* The "." and ".." directory entries must not be long names */ |
| 1221 | if(dotdir) { |
| 1222 | int i; |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1223 | strncpy(shortname, name, 12); |
| 1224 | for(i = strlen(shortname); i < 12; i++) |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1225 | shortname[i] = ' '; |
| 1226 | |
| 1227 | entries_needed = 1; |
| 1228 | } else { |
Jens Arnold | e4e1f2c | 2005-04-23 13:15:25 +0000 | [diff] [blame] | 1229 | create_dos_name(name, shortname); |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1230 | |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1231 | /* one dir entry needed for every 13 bytes of filename, |
| 1232 | plus one entry for the short name */ |
Marcoen Hirschberg | b0fee17 | 2005-12-06 13:27:15 +0000 | [diff] [blame^] | 1233 | entries_needed = (utf8length(name) + (NAME_BYTES_PER_ENTRY-1)) |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1234 | / NAME_BYTES_PER_ENTRY + 1; |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1235 | } |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1236 | |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1237 | restart: |
| 1238 | firstentry = -1; |
| 1239 | |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1240 | rc = fat_seek(&dir->file, 0); |
| 1241 | if (rc < 0) |
Jonas Häggqvist | 98143f5 | 2005-09-07 01:35:15 +0000 | [diff] [blame] | 1242 | return rc * 10 - 2; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1243 | |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1244 | /* step 1: search for free entries and check for duplicate shortname */ |
| 1245 | for (sector = 0; !done; sector++) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1246 | { |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1247 | unsigned int i; |
| 1248 | |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1249 | rc = fat_readwrite(&dir->file, 1, buf, false); |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1250 | if (rc < 0) { |
Björn Stenberg | 1f214f2 | 2002-11-13 23:16:32 +0000 | [diff] [blame] | 1251 | DEBUGF( "add_dir_entry() - Couldn't read dir" |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1252 | " (error code %d)\n", rc); |
Jonas Häggqvist | 98143f5 | 2005-09-07 01:35:15 +0000 | [diff] [blame] | 1253 | return rc * 10 - 3; |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1254 | } |
| 1255 | |
| 1256 | if (rc == 0) { /* current end of dir reached */ |
| 1257 | LDEBUGF("End of dir on cluster boundary\n"); |
| 1258 | break; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1259 | } |
| 1260 | |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1261 | /* look for free slots */ |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1262 | for (i = 0; i < DIR_ENTRIES_PER_SECTOR; i++) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1263 | { |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1264 | 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 Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1268 | sector * DIR_ENTRIES_PER_SECTOR + i); |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1269 | i = DIR_ENTRIES_PER_SECTOR - 1; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1270 | done = true; |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1271 | break; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1272 | |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1273 | case 0xe5: |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1274 | 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 Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1280 | default: |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1281 | entries_found = 0; |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1282 | |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1283 | /* check that our intended shortname doesn't already exist */ |
| 1284 | if (!strncmp(shortname, buf + i * DIR_ENTRY_SIZE, 12)) { |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1285 | /* shortname exists already, make a new one */ |
Jens Arnold | dbf7f51 | 2005-02-16 20:45:56 +0000 | [diff] [blame] | 1286 | randomize_dos_name(shortname); |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1287 | LDEBUGF("Duplicate shortname, changing to %s\n", |
| 1288 | shortname); |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1289 | |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1290 | /* name has changed, we need to restart search */ |
| 1291 | goto restart; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1292 | } |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1293 | break; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1294 | } |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1295 | if (firstentry < 0 && (entries_found >= entries_needed)) |
| 1296 | firstentry = sector * DIR_ENTRIES_PER_SECTOR + i + 1 |
| 1297 | - entries_found; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1298 | } |
| 1299 | } |
| 1300 | |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1301 | /* step 2: extend the dir if necessary */ |
| 1302 | if (firstentry < 0) |
| 1303 | { |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1304 | 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 Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1328 | sector = firstentry / DIR_ENTRIES_PER_SECTOR; |
| 1329 | LDEBUGF("Adding longname to entry %d in sector %d\n", |
| 1330 | firstentry, sector); |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1331 | |
| 1332 | rc = write_long_name(&dir->file, firstentry, |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1333 | entries_needed, name, shortname, is_directory); |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1334 | if (rc < 0) |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1335 | return rc * 10 - 7; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1336 | |
| 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 Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1344 | return 0; |
| 1345 | } |
| 1346 | |
Björn Stenberg | 5661b23 | 2002-04-27 01:25:22 +0000 | [diff] [blame] | 1347 | unsigned char char2dos(unsigned char c) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1348 | { |
| 1349 | switch(c) |
| 1350 | { |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1351 | 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 Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1366 | /* Illegal char, replace */ |
Jens Arnold | f213afa | 2005-02-16 21:56:20 +0000 | [diff] [blame] | 1367 | c = '_'; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1368 | break; |
| 1369 | |
| 1370 | default: |
Jens Arnold | f213afa | 2005-02-16 21:56:20 +0000 | [diff] [blame] | 1371 | if(c <= 0x20) |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1372 | c = 0; /* Illegal char, remove */ |
| 1373 | else |
| 1374 | c = toupper(c); |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1375 | break; |
| 1376 | } |
| 1377 | return c; |
| 1378 | } |
| 1379 | |
Jens Arnold | e4e1f2c | 2005-04-23 13:15:25 +0000 | [diff] [blame] | 1380 | static void create_dos_name(const unsigned char *name, unsigned char *newname) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1381 | { |
Jens Arnold | dbf7f51 | 2005-02-16 20:45:56 +0000 | [diff] [blame] | 1382 | 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 Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1389 | |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1390 | /* Name part */ |
Jens Arnold | dbf7f51 | 2005-02-16 20:45:56 +0000 | [diff] [blame] | 1391 | for (i = 0; *name && (!ext || name < ext) && (i < 8); name++) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1392 | { |
Jens Arnold | dbf7f51 | 2005-02-16 20:45:56 +0000 | [diff] [blame] | 1393 | unsigned char c = char2dos(*name); |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1394 | if (c) |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1395 | newname[i++] = c; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1396 | } |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1397 | |
Jens Arnold | dbf7f51 | 2005-02-16 20:45:56 +0000 | [diff] [blame] | 1398 | /* Pad both name and extension */ |
| 1399 | while (i < 11) |
| 1400 | newname[i++] = ' '; |
Björn Stenberg | 7aabb1a | 2002-11-15 16:41:02 +0000 | [diff] [blame] | 1401 | |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1402 | if (newname[0] == 0xe5) /* Special kanji character */ |
| 1403 | newname[0] = 0x05; |
| 1404 | |
Jens Arnold | dbf7f51 | 2005-02-16 20:45:56 +0000 | [diff] [blame] | 1405 | 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 Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1412 | newname[i++] = c; |
Jens Arnold | dbf7f51 | 2005-02-16 20:45:56 +0000 | [diff] [blame] | 1413 | } |
| 1414 | } |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1415 | } |
| 1416 | |
Jens Arnold | dbf7f51 | 2005-02-16 20:45:56 +0000 | [diff] [blame] | 1417 | static void randomize_dos_name(unsigned char *name) |
| 1418 | { |
| 1419 | int i; |
| 1420 | unsigned char buf[5]; |
| 1421 | |
Jens Arnold | 6333f79 | 2005-02-16 22:21:45 +0000 | [diff] [blame] | 1422 | snprintf(buf, sizeof buf, "%04X", (unsigned)rand() & 0xffff); |
Jens Arnold | dbf7f51 | 2005-02-16 20:45:56 +0000 | [diff] [blame] | 1423 | |
| 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 Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1429 | static int update_short_entry( struct fat_file* file, long size, int attr ) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1430 | { |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1431 | unsigned char buf[SECTOR_SIZE]; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1432 | 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 Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1435 | unsigned long* sizeptr; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1436 | unsigned short* clusptr; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1437 | struct fat_file dir; |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1438 | int rc; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1439 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1440 | LDEBUGF("update_file_size(cluster:%lx entry:%d size:%ld)\n", |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1441 | file->firstcluster, file->direntry, size); |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1442 | |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1443 | /* create a temporary file handle for the dir holding this file */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 1444 | rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL); |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1445 | if (rc < 0) |
| 1446 | return rc * 10 - 1; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1447 | |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1448 | rc = fat_seek( &dir, sector ); |
| 1449 | if (rc<0) |
| 1450 | return rc * 10 - 2; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1451 | |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1452 | rc = fat_readwrite(&dir, 1, buf, false); |
| 1453 | if (rc < 1) |
| 1454 | return rc * 10 - 3; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1455 | |
| 1456 | if (!entry[0] || entry[0] == 0xe5) |
| 1457 | panicf("Updating size on empty dir entry %d\n", file->direntry); |
| 1458 | |
Björn Stenberg | 184fd55 | 2003-01-27 09:32:17 +0000 | [diff] [blame] | 1459 | entry[FATDIR_ATTR] = attr & 0xFF; |
| 1460 | |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1461 | clusptr = (short*)(entry + FATDIR_FSTCLUSHI); |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 1462 | *clusptr = htole16(file->firstcluster >> 16); |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1463 | |
| 1464 | clusptr = (short*)(entry + FATDIR_FSTCLUSLO); |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 1465 | *clusptr = htole16(file->firstcluster & 0xffff); |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1466 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1467 | sizeptr = (long*)(entry + FATDIR_FILESIZE); |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 1468 | *sizeptr = htole32(size); |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1469 | |
Björn Stenberg | ac88b9e | 2002-11-22 23:55:08 +0000 | [diff] [blame] | 1470 | { |
Dave Chapman | 8c800cf | 2005-12-04 15:23:47 +0000 | [diff] [blame] | 1471 | #ifdef CONFIG_RTC |
Linus Nielsen Feltzing | d9e8bfe | 2003-11-03 00:59:04 +0000 | [diff] [blame] | 1472 | unsigned short time = 0; |
| 1473 | unsigned short date = 0; |
| 1474 | #else |
| 1475 | /* get old time to increment from */ |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 1476 | unsigned short time = htole16(*(unsigned short*)(entry + FATDIR_WRTTIME)); |
| 1477 | unsigned short date = htole16(*(unsigned short*)(entry + FATDIR_WRTDATE)); |
Linus Nielsen Feltzing | d9e8bfe | 2003-11-03 00:59:04 +0000 | [diff] [blame] | 1478 | #endif |
Björn Stenberg | ac88b9e | 2002-11-22 23:55:08 +0000 | [diff] [blame] | 1479 | fat_time(&date, &time, NULL); |
Dave Chapman | 9e19c95 | 2005-10-06 19:27:43 +0000 | [diff] [blame] | 1480 | *(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 Stenberg | ac88b9e | 2002-11-22 23:55:08 +0000 | [diff] [blame] | 1483 | } |
Björn Stenberg | b5184d7 | 2002-11-22 23:51:46 +0000 | [diff] [blame] | 1484 | |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1485 | rc = fat_seek( &dir, sector ); |
| 1486 | if (rc < 0) |
| 1487 | return rc * 10 - 4; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1488 | |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1489 | rc = fat_readwrite(&dir, 1, buf, true); |
| 1490 | if (rc < 1) |
| 1491 | return rc * 10 - 5; |
Björn Stenberg | 1f214f2 | 2002-11-13 23:16:32 +0000 | [diff] [blame] | 1492 | |
| 1493 | return 0; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1494 | } |
| 1495 | |
Jens Arnold | 0ceaa5e | 2004-08-17 01:45:48 +0000 | [diff] [blame] | 1496 | static int parse_direntry(struct fat_direntry *de, const unsigned char *buf) |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1497 | { |
Björn Stenberg | 802740c | 2002-05-07 18:56:32 +0000 | [diff] [blame] | 1498 | int i=0,j=0; |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1499 | unsigned char c; |
| 1500 | bool lowercase; |
| 1501 | |
Björn Stenberg | 1dff4b6 | 2002-04-26 16:44:58 +0000 | [diff] [blame] | 1502 | memset(de, 0, sizeof(struct fat_direntry)); |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1503 | de->attr = buf[FATDIR_ATTR]; |
| 1504 | de->crttimetenth = buf[FATDIR_CRTTIMETENTH]; |
Björn Stenberg | 1dff4b6 | 2002-04-26 16:44:58 +0000 | [diff] [blame] | 1505 | 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 Bernardy | a83214d | 2005-02-27 20:44:26 +0000 | [diff] [blame] | 1510 | 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 Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1514 | |
Björn Stenberg | 802740c | 2002-05-07 18:56:32 +0000 | [diff] [blame] | 1515 | /* fix the name */ |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1516 | 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 Stenberg | 802740c | 2002-05-07 18:56:32 +0000 | [diff] [blame] | 1529 | de->name[j++] = '.'; |
Jens Arnold | a07c735 | 2005-04-22 22:26:13 +0000 | [diff] [blame] | 1530 | for (i = 8; (i < 11) && ((c = buf[FATDIR_NAME+i]) != ' '); i++) |
| 1531 | de->name[j++] = lowercase ? tolower(c) : c; |
Björn Stenberg | 802740c | 2002-05-07 18:56:32 +0000 | [diff] [blame] | 1532 | } |
Björn Stenberg | 1dff4b6 | 2002-04-26 16:44:58 +0000 | [diff] [blame] | 1533 | return 1; |
Björn Stenberg | d9eb5c7 | 2002-03-28 15:09:10 +0000 | [diff] [blame] | 1534 | } |
| 1535 | |
Jens Arnold | 316ae18 | 2005-01-03 07:59:49 +0000 | [diff] [blame] | 1536 | int fat_open(IF_MV2(int volume,) |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1537 | long startcluster, |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1538 | struct fat_file *file, |
Jens Arnold | 0ceaa5e | 2004-08-17 01:45:48 +0000 | [diff] [blame] | 1539 | const struct fat_dir* dir) |
Björn Stenberg | e8bcc01 | 2002-04-27 19:37:41 +0000 | [diff] [blame] | 1540 | { |
Björn Stenberg | 924164e | 2002-05-03 15:35:51 +0000 | [diff] [blame] | 1541 | file->firstcluster = startcluster; |
Björn Stenberg | 46ddacf | 2002-10-22 15:06:08 +0000 | [diff] [blame] | 1542 | file->lastcluster = startcluster; |
Björn Stenberg | a5e77d8 | 2002-10-31 16:09:28 +0000 | [diff] [blame] | 1543 | file->lastsector = 0; |
Hardeep Sidhu | fc9b28d | 2003-06-03 18:04:22 +0000 | [diff] [blame] | 1544 | file->clusternum = 0; |
Björn Stenberg | 924164e | 2002-05-03 15:35:51 +0000 | [diff] [blame] | 1545 | file->sectornum = 0; |
Björn Stenberg | 1f214f2 | 2002-11-13 23:16:32 +0000 | [diff] [blame] | 1546 | file->eof = false; |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 1547 | #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 Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1556 | |
| 1557 | /* remember where the file's dir entry is located */ |
Björn Stenberg | 1f214f2 | 2002-11-13 23:16:32 +0000 | [diff] [blame] | 1558 | if ( dir ) { |
Björn Stenberg | 1f214f2 | 2002-11-13 23:16:32 +0000 | [diff] [blame] | 1559 | file->direntry = dir->entry - 1; |
Björn Stenberg | eee2c01 | 2002-11-18 11:58:43 +0000 | [diff] [blame] | 1560 | file->direntries = dir->entrycount; |
| 1561 | file->dircluster = dir->file.firstcluster; |
Björn Stenberg | 1f214f2 | 2002-11-13 23:16:32 +0000 | [diff] [blame] | 1562 | } |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1563 | LDEBUGF("fat_open(%lx), entry %d\n",startcluster,file->direntry); |
Björn Stenberg | e8bcc01 | 2002-04-27 19:37:41 +0000 | [diff] [blame] | 1564 | return 0; |
| 1565 | } |
| 1566 | |
Jens Arnold | 0ceaa5e | 2004-08-17 01:45:48 +0000 | [diff] [blame] | 1567 | int fat_create_file(const char* name, |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1568 | struct fat_file* file, |
| 1569 | struct fat_dir* dir) |
| 1570 | { |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1571 | int rc; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1572 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1573 | LDEBUGF("fat_create_file(\"%s\",%lx,%lx)\n",name,(long)file,(long)dir); |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1574 | rc = add_dir_entry(dir, file, name, false, false); |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1575 | if (!rc) { |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1576 | file->firstcluster = 0; |
Björn Stenberg | 46ddacf | 2002-10-22 15:06:08 +0000 | [diff] [blame] | 1577 | file->lastcluster = 0; |
| 1578 | file->lastsector = 0; |
Hardeep Sidhu | fc9b28d | 2003-06-03 18:04:22 +0000 | [diff] [blame] | 1579 | file->clusternum = 0; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1580 | file->sectornum = 0; |
Björn Stenberg | 1f214f2 | 2002-11-13 23:16:32 +0000 | [diff] [blame] | 1581 | file->eof = false; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1582 | } |
| 1583 | |
Linus Nielsen Feltzing | 96f5199 | 2003-02-26 03:02:49 +0000 | [diff] [blame] | 1584 | return rc; |
Björn Stenberg | b7b48fe | 2002-10-20 22:50:58 +0000 | [diff] [blame] | 1585 | } |
| 1586 | |
Jens Arnold | 0ceaa5e | 2004-08-17 01:45:48 +0000 | [diff] [blame] | 1587 | int fat_create_dir(const char* name, |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1588 | struct fat_dir* newdir, |
| 1589 | struct fat_dir* dir) |
| 1590 | { |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 1591 | #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 Feltzing | 7c75386 | 2004-01-15 19:05:49 +0000 | [diff] [blame] | 1596 | unsigned char buf[SECTOR_SIZE]; |
| 1597 | int i; |
Jean-Philippe Bernardy | 907ba58 | 2005-02-25 09:11:29 +0000 | [diff] [blame] | 1598 | long sector; |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1599 | int rc; |
| 1600 | struct fat_file dummyfile; |
| 1601 | |
Jean-Philippe Bernardy | 36b8e13 | 2005-01-23 23:20:40 +0000 | [diff] [blame] | 1602 | LDEBUGF("fat_create_dir(\"%s\",%lx,%lx)\n",name,(long)newdir,(long)dir); |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1603 | |
Linus Nielsen Feltzing | da115da | 2004-01-26 10:14:46 +0000 | [diff] [blame] | 1604 | memset(newdir, 0, sizeof(struct fat_dir)); |
| 1605 | memset(&dummyfile, 0, sizeof(struct fat_file)); |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1606 | |
| 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 Feltzing | 7c75386 | 2004-01-15 19:05:49 +0000 | [diff] [blame] | 1612 | /* Allocate a new cluster for the directory */ |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 1613 | newdir->file.firstcluster = find_free_cluster(IF_MV2(fat_bpb,) fat_bpb->fsinfo.nextfree); |
Linus Nielsen Feltzing | 7c75386 | 2004-01-15 19:05:49 +0000 | [diff] [blame] | 1614 | if(newdir->file.firstcluster == 0) |
| 1615 | return -1; |
| 1616 | |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 1617 | update_fat_entry(IF_MV2(fat_bpb,) newdir->file.firstcluster, FAT_EOF_MARK); |
Linus Nielsen Feltzing | 7c75386 | 2004-01-15 19:05:49 +0000 | [diff] [blame] | 1618 | |
| 1619 | /* Clear the entire cluster */ |
| 1620 | memset(buf, 0, sizeof buf); |
Jörg Hohensohn | da84857 | 2004-12-28 22:16:07 +0000 | [diff] [blame] | 1621 | 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 Feltzing | 7c75386 | 2004-01-15 19:05:49 +0000 | [diff] [blame] | 1624 | if (rc < 0) |
| 1625 | return rc * 10 - 2; |
| 1626 | } |
Linus Nielsen Feltzing | 54353e0 | 2004-01-15 15:56:19 +0000 | [diff] [blame] | 1627 | |
Linus Nielsen Feltzing | 7c75386 | 2004-01-15 19:05:49 +0000 | [diff] [blame] | 1628 | /* Then add the "." entry */ |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1629 | rc = add_dir_entry(newdir, &dummyfile, ".", true, true); |
| 1630 | if (rc < 0) |
Linus Nielsen Feltzing | 7c75386 | 2004-01-15 19:05:49 +0000 | [diff] [blame] | 1631 | return rc * 10 - 3; |
Linus Nielsen Feltzing | 60b1c4b | 2004-01-15 14:30:59 +0000 | [diff] [blame] | 1632 | dummyfile.firstcluster = newdir->file.firstcluster; |
| |