| Index: embios/trunk/target/ipodnano2g/ftl.c | 
| — | — | @@ -2565,19 +2565,19 @@ | 
| 2566 | 2566 | ftl_initialized = true; | 
| 2567 | 2567 | return 0; | 
| 2568 | 2568 | } | 
| 2569 |  | -        cprintf(CONSOLE_BOOT, "The FTL seems to be damaged. Forcing check.\n");
 | 
|  | 2569 | +        cputs(CONSOLE_BOOT, "The FTL seems to be damaged. Forcing check.\n"); | 
| 2570 | 2570 | if (ftl_repair() != 0) | 
| 2571 |  | -            cprintf(CONSOLE_BOOT, "FTL recovery failed. Use disk mode to recover.\n");
 | 
|  | 2571 | +            cputs(CONSOLE_BOOT, "FTL recovery failed. Use disk mode to recover.\n"); | 
| 2572 | 2572 | else | 
| 2573 | 2573 | { | 
| 2574 |  | -            cprintf(CONSOLE_BOOT, "FTL recovery finished. Trying to mount again...\n");
 | 
|  | 2574 | +            cputs(CONSOLE_BOOT, "FTL recovery finished. Trying to mount again...\n"); | 
| 2575 | 2575 | if (ftl_open() == 0) | 
| 2576 | 2576 | { | 
| 2577 |  | -                cprintf(CONSOLE_BOOT, "Mount succeeded.\n");
 | 
|  | 2577 | +                cputs(CONSOLE_BOOT, "Mount succeeded.\n"); | 
| 2578 | 2578 | ftl_initialized = true; | 
| 2579 | 2579 | return 0; | 
| 2580 | 2580 | } | 
| 2581 |  | -            cprintf(CONSOLE_BOOT, "Mounting FTL failed again, use disk mode to recover.\n");
 | 
|  | 2581 | +            cputs(CONSOLE_BOOT, "Mounting FTL failed again, use disk mode to recover.\n"); | 
| 2582 | 2582 | } | 
| 2583 | 2583 | } | 
| 2584 | 2584 |  | 
| Index: embios/trunk/fat.c | 
| — | — | @@ -1,2578 +1,2561 @@ | 
| 2 |  | -/*************************************************************************** | 
| 3 |  | - *             __________               __   ___. | 
| 4 |  | - *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___ | 
| 5 |  | - *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  / | 
| 6 |  | - *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  < | 
| 7 |  | - *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \ | 
| 8 |  | - *                     \/            \/     \/    \/            \/ | 
| 9 |  | - * $Id: fat.c 25459 2010-04-03 22:02:09Z gevaerts $ | 
| 10 |  | - * | 
| 11 |  | - * Copyright (C) 2002 by Linus Nielsen Feltzing | 
| 12 |  | - * | 
| 13 |  | - * This program is free software; you can redistribute it and/or | 
| 14 |  | - * modify it under the terms of the GNU General Public License | 
| 15 |  | - * as published by the Free Software Foundation; either version 2 | 
| 16 |  | - * of the License, or (at your option) any later version. | 
| 17 |  | - * | 
| 18 |  | - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | 
| 19 |  | - * KIND, either express or implied. | 
| 20 |  | - * | 
| 21 |  | - ****************************************************************************/ | 
| 22 |  | -#include "global.h" | 
| 23 |  | -#include "thread.h" | 
| 24 |  | -#include "libc/include/string.h" | 
| 25 |  | -#include "libc/include/stdio.h" | 
| 26 |  | -#include "fat.h" | 
| 27 |  | -#include "storage.h" | 
| 28 |  | -#include "debug.h" | 
| 29 |  | -#include "panic.h" | 
| 30 |  | -#include "libc/include/ctype.h" | 
| 31 |  | - | 
| 32 |  | -#define BYTES2INT16(array,pos) \ | 
| 33 |  | -          (array[pos] | (array[pos+1] << 8 )) | 
| 34 |  | -#define BYTES2INT32(array,pos) \ | 
| 35 |  | -    ((long)array[pos] | ((long)array[pos+1] << 8 ) | \ | 
| 36 |  | -    ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) | 
| 37 |  | - | 
| 38 |  | -#define FATTYPE_FAT12       0 | 
| 39 |  | -#define FATTYPE_FAT16       1 | 
| 40 |  | -#define FATTYPE_FAT32       2 | 
| 41 |  | - | 
| 42 |  | -/* BPB offsets; generic */ | 
| 43 |  | -#define BS_JMPBOOT          0 | 
| 44 |  | -#define BS_OEMNAME          3 | 
| 45 |  | -#define BPB_BYTSPERSEC      11 | 
| 46 |  | -#define BPB_SECPERCLUS      13 | 
| 47 |  | -#define BPB_RSVDSECCNT      14 | 
| 48 |  | -#define BPB_NUMFATS         16 | 
| 49 |  | -#define BPB_ROOTENTCNT      17 | 
| 50 |  | -#define BPB_TOTSEC16        19 | 
| 51 |  | -#define BPB_MEDIA           21 | 
| 52 |  | -#define BPB_FATSZ16         22 | 
| 53 |  | -#define BPB_SECPERTRK       24 | 
| 54 |  | -#define BPB_NUMHEADS        26 | 
| 55 |  | -#define BPB_HIDDSEC         28 | 
| 56 |  | -#define BPB_TOTSEC32        32 | 
| 57 |  | - | 
| 58 |  | -/* fat12/16 */ | 
| 59 |  | -#define BS_DRVNUM           36 | 
| 60 |  | -#define BS_RESERVED1        37 | 
| 61 |  | -#define BS_BOOTSIG          38 | 
| 62 |  | -#define BS_VOLID            39 | 
| 63 |  | -#define BS_VOLLAB           43 | 
| 64 |  | -#define BS_FILSYSTYPE       54 | 
| 65 |  | - | 
| 66 |  | -/* fat32 */ | 
| 67 |  | -#define BPB_FATSZ32         36 | 
| 68 |  | -#define BPB_EXTFLAGS        40 | 
| 69 |  | -#define BPB_FSVER           42 | 
| 70 |  | -#define BPB_ROOTCLUS        44 | 
| 71 |  | -#define BPB_FSINFO          48 | 
| 72 |  | -#define BPB_BKBOOTSEC       50 | 
| 73 |  | -#define BS_32_DRVNUM        64 | 
| 74 |  | -#define BS_32_BOOTSIG       66 | 
| 75 |  | -#define BS_32_VOLID         67 | 
| 76 |  | -#define BS_32_VOLLAB        71 | 
| 77 |  | -#define BS_32_FILSYSTYPE    82 | 
| 78 |  | - | 
| 79 |  | -#define BPB_LAST_WORD       510 | 
| 80 |  | - | 
| 81 |  | - | 
| 82 |  | -/* attributes */ | 
| 83 |  | -#define FAT_ATTR_LONG_NAME   (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \ | 
| 84 |  | -                              FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID) | 
| 85 |  | -#define FAT_ATTR_LONG_NAME_MASK (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \ | 
| 86 |  | -                                 FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID | \ | 
| 87 |  | -                                 FAT_ATTR_DIRECTORY | FAT_ATTR_ARCHIVE ) | 
| 88 |  | - | 
| 89 |  | -/* NTRES flags */ | 
| 90 |  | -#define FAT_NTRES_LC_NAME    0x08 | 
| 91 |  | -#define FAT_NTRES_LC_EXT     0x10 | 
| 92 |  | - | 
| 93 |  | -#define FATDIR_NAME          0 | 
| 94 |  | -#define FATDIR_ATTR          11 | 
| 95 |  | -#define FATDIR_NTRES         12 | 
| 96 |  | -#define FATDIR_CRTTIMETENTH  13 | 
| 97 |  | -#define FATDIR_CRTTIME       14 | 
| 98 |  | -#define FATDIR_CRTDATE       16 | 
| 99 |  | -#define FATDIR_LSTACCDATE    18 | 
| 100 |  | -#define FATDIR_FSTCLUSHI     20 | 
| 101 |  | -#define FATDIR_WRTTIME       22 | 
| 102 |  | -#define FATDIR_WRTDATE       24 | 
| 103 |  | -#define FATDIR_FSTCLUSLO     26 | 
| 104 |  | -#define FATDIR_FILESIZE      28 | 
| 105 |  | - | 
| 106 |  | -#define FATLONG_ORDER        0 | 
| 107 |  | -#define FATLONG_TYPE         12 | 
| 108 |  | -#define FATLONG_CHKSUM       13 | 
| 109 |  | -#define FATLONG_LAST_LONG_ENTRY 0x40 | 
| 110 |  | -#define FATLONG_NAME_BYTES_PER_ENTRY 26 | 
| 111 |  | -/* at most 20 LFN entries, keep coherent with fat_dir->longname size ! */ | 
| 112 |  | -#define FATLONG_MAX_ORDER    20 | 
| 113 |  | - | 
| 114 |  | -#define FATLONG_NAME_CHUNKS 3 | 
| 115 |  | -static unsigned char FATLONG_NAME_POS[FATLONG_NAME_CHUNKS] = {1, 14, 28}; | 
| 116 |  | -static unsigned char FATLONG_NAME_SIZE[FATLONG_NAME_CHUNKS] = {10, 12, 4}; | 
| 117 |  | - | 
| 118 |  | -#define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4) | 
| 119 |  | -#define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2) | 
| 120 |  | -#define DIR_ENTRIES_PER_SECTOR  (SECTOR_SIZE / DIR_ENTRY_SIZE) | 
| 121 |  | -#define DIR_ENTRY_SIZE       32 | 
| 122 |  | -#define NAME_BYTES_PER_ENTRY 13 | 
| 123 |  | -#define FAT_BAD_MARK         0x0ffffff7 | 
| 124 |  | -#define FAT_EOF_MARK         0x0ffffff8 | 
| 125 |  | -#define FAT_LONGNAME_PAD_BYTE 0xff | 
| 126 |  | -#define FAT_LONGNAME_PAD_UCS 0xffff | 
| 127 |  | - | 
| 128 |  | -struct fsinfo { | 
| 129 |  | -    unsigned long freecount; /* last known free cluster count */ | 
| 130 |  | -    unsigned long nextfree;  /* first cluster to start looking for free | 
| 131 |  | -                               clusters, or 0xffffffff for no hint */ | 
| 132 |  | -}; | 
| 133 |  | -/* fsinfo offsets */ | 
| 134 |  | -#define FSINFO_FREECOUNT 488 | 
| 135 |  | -#define FSINFO_NEXTFREE  492 | 
| 136 |  | - | 
| 137 |  | -/* Note: This struct doesn't hold the raw values after mounting if | 
| 138 |  | - * bpb_bytspersec isn't 512. All sector counts are normalized to 512 byte | 
| 139 |  | - * physical sectors. */ | 
| 140 |  | -struct bpb | 
| 141 |  | -{ | 
| 142 |  | -    int bpb_bytspersec;  /* Bytes per sector, typically 512 */ | 
| 143 |  | -    unsigned int bpb_secperclus;  /* Sectors per cluster */ | 
| 144 |  | -    int bpb_rsvdseccnt;  /* Number of reserved sectors */ | 
| 145 |  | -    int bpb_numfats;     /* Number of FAT structures, typically 2 */ | 
| 146 |  | -    int bpb_totsec16;    /* Number of sectors on the volume (old 16-bit) */ | 
| 147 |  | -    int bpb_media;       /* Media type (typically 0xf0 or 0xf8) */ | 
| 148 |  | -    int bpb_fatsz16;     /* Number of used sectors per FAT structure */ | 
| 149 |  | -    unsigned long bpb_totsec32;    /* Number of sectors on the volume | 
| 150 |  | -                                     (new 32-bit) */ | 
| 151 |  | -    unsigned int last_word;       /* 0xAA55 */ | 
| 152 |  | - | 
| 153 |  | -    /**** FAT32 specific *****/ | 
| 154 |  | -    long bpb_fatsz32; | 
| 155 |  | -    long bpb_rootclus; | 
| 156 |  | -    long bpb_fsinfo; | 
| 157 |  | - | 
| 158 |  | -    /* variables for internal use */ | 
| 159 |  | -    unsigned long fatsize; | 
| 160 |  | -    unsigned long totalsectors; | 
| 161 |  | -    unsigned long rootdirsector; | 
| 162 |  | -    unsigned long firstdatasector; | 
| 163 |  | -    unsigned long startsector; | 
| 164 |  | -    unsigned long dataclusters; | 
| 165 |  | -    struct fsinfo fsinfo; | 
| 166 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 167 |  | -    int bpb_rootentcnt;  /* Number of dir entries in the root */ | 
| 168 |  | -    /* internals for FAT16 support */ | 
| 169 |  | -    bool is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */ | 
| 170 |  | -    unsigned int rootdiroffset; /* sector offset of root dir relative to start | 
| 171 |  | -                                 * of first pseudo cluster */ | 
| 172 |  | -#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
| 173 |  | -#ifdef HAVE_MULTIVOLUME | 
| 174 |  | -#ifdef HAVE_MULTIDRIVE | 
| 175 |  | -    int drive; /* on which physical device is this located */ | 
| 176 |  | -#endif | 
| 177 |  | -    bool mounted; /* flag if this volume is mounted */ | 
| 178 |  | -#endif | 
| 179 |  | -}; | 
| 180 |  | - | 
| 181 |  | -static struct bpb fat_bpbs[NUM_VOLUMES]; /* mounted partition info */ | 
| 182 |  | -static bool initialized = false; | 
| 183 |  | - | 
| 184 |  | -static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)); | 
| 185 |  | -static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb)); | 
| 186 |  | -static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)); | 
| 187 |  | -static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,) | 
| 188 |  | -                              long secnum, bool dirty); | 
| 189 |  | -static void unlock_fat_sector(IF_MV2(struct bpb* fat_bpb,) | 
| 190 |  | -                              long secnum); | 
| 191 |  | -static void create_dos_name(const unsigned char *name, unsigned char *newname); | 
| 192 |  | -static void randomize_dos_name(unsigned char *name); | 
| 193 |  | -static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,) | 
| 194 |  | -                                       unsigned long start); | 
| 195 |  | -static int transfer(IF_MV2(struct bpb* fat_bpb,) unsigned long start, | 
| 196 |  | -                    long count, char* buf, bool write ); | 
| 197 |  | - | 
| 198 |  | -#define FAT_CACHE_SIZE 4 | 
| 199 |  | -#define FAT_CACHE_MASK (FAT_CACHE_SIZE-1) | 
| 200 |  | - | 
| 201 |  | -struct fat_cache_entry | 
| 202 |  | -{ | 
| 203 |  | -    long secnum; | 
| 204 |  | -    bool valid; | 
| 205 |  | -    int locked; | 
| 206 |  | -    bool dirty; | 
| 207 |  | -#ifdef HAVE_MULTIVOLUME | 
| 208 |  | -    struct bpb* fat_vol; /* shared cache for all volumes */ | 
| 209 |  | -#endif | 
| 210 |  | -}; | 
| 211 |  | - | 
| 212 |  | -static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE] CACHEALIGN_ATTR; | 
| 213 |  | -static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE]; | 
| 214 |  | -static struct mutex cache_mutex; | 
| 215 |  | -static struct mutex tempbuf_mutex; | 
| 216 |  | -static char fat_tempbuf[SECTOR_SIZE] CACHEALIGN_ATTR; | 
| 217 |  | -static bool tempbuf_locked; | 
| 218 |  | - | 
| 219 |  | -#if defined(HAVE_HOTSWAP) && !(CONFIG_STORAGE & STORAGE_MMC) /* A better condition ?? */ | 
| 220 |  | -void fat_lock(void) | 
| 221 |  | -{ | 
| 222 |  | -    mutex_lock(&cache_mutex, TIMEOUT_BLOCK); | 
| 223 |  | -} | 
| 224 |  | - | 
| 225 |  | -void fat_unlock(void) | 
| 226 |  | -{ | 
| 227 |  | -    mutex_unlock(&cache_mutex); | 
| 228 |  | -} | 
| 229 |  | -#endif | 
| 230 |  | - | 
| 231 |  | -static long cluster2sec(IF_MV2(struct bpb* fat_bpb,) long cluster) | 
| 232 |  | -{ | 
| 233 |  | -#ifndef HAVE_MULTIVOLUME | 
| 234 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 235 |  | -#endif | 
| 236 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 237 |  | -    /* negative clusters (FAT16 root dir) don't get the 2 offset */ | 
| 238 |  | -    int zerocluster = cluster < 0 ? 0 : 2; | 
| 239 |  | -#else | 
| 240 |  | -    const long zerocluster = 2; | 
| 241 |  | -#endif | 
| 242 |  | - | 
| 243 |  | -    if (cluster > (long)(fat_bpb->dataclusters + 1)) | 
| 244 |  | -    { | 
| 245 |  | -      DEBUGF( "cluster2sec() - Bad cluster number (%ld)", cluster); | 
| 246 |  | -        return -1; | 
| 247 |  | -    } | 
| 248 |  | - | 
| 249 |  | -    return (cluster - zerocluster) * fat_bpb->bpb_secperclus  | 
| 250 |  | -           + fat_bpb->firstdatasector; | 
| 251 |  | -} | 
| 252 |  | - | 
| 253 |  | -void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free) | 
| 254 |  | -{ | 
| 255 |  | -#ifndef HAVE_MULTIVOLUME | 
| 256 |  | -    const int volume = 0; | 
| 257 |  | -#endif | 
| 258 |  | -    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
| 259 |  | -    if (size) | 
| 260 |  | -      *size = fat_bpb->dataclusters * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024); | 
| 261 |  | -    if (free) | 
| 262 |  | -      *free = fat_bpb->fsinfo.freecount * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024); | 
| 263 |  | -} | 
| 264 |  | - | 
| 265 |  | -void fat_init(void) | 
| 266 |  | -{ | 
| 267 |  | -    unsigned int i; | 
| 268 |  | - | 
| 269 |  | -    if (!initialized) | 
| 270 |  | -    { | 
| 271 |  | -        initialized = true; | 
| 272 |  | -        mutex_init(&cache_mutex); | 
| 273 |  | -        mutex_init(&tempbuf_mutex); | 
| 274 |  | -        tempbuf_locked = false; | 
| 275 |  | -    } | 
| 276 |  | - | 
| 277 |  | -    /* mark the FAT cache as unused */ | 
| 278 |  | -    for(i = 0;i < FAT_CACHE_SIZE;i++) | 
| 279 |  | -    { | 
| 280 |  | -        fat_cache[i].secnum = -1; | 
| 281 |  | -        fat_cache[i].valid = false; | 
| 282 |  | -        fat_cache[i].locked = 0; | 
| 283 |  | -        fat_cache[i].dirty = false; | 
| 284 |  | -#ifdef HAVE_MULTIVOLUME | 
| 285 |  | -        fat_cache[i].fat_vol = NULL; | 
| 286 |  | -#endif | 
| 287 |  | -    } | 
| 288 |  | -#ifdef HAVE_MULTIVOLUME | 
| 289 |  | -    /* mark the possible volumes as not mounted */ | 
| 290 |  | -    for (i=0; i<NUM_VOLUMES;i++) | 
| 291 |  | -    { | 
| 292 |  | -        fat_bpbs[i].mounted = false; | 
| 293 |  | -    } | 
| 294 |  | -#endif | 
| 295 |  | -} | 
| 296 |  | - | 
| 297 |  | -int fat_mount(IF_MV2(int volume,) IF_MD2(int drive,) long startsector) | 
| 298 |  | -{ | 
| 299 |  | -#ifndef HAVE_MULTIVOLUME | 
| 300 |  | -    const int volume = 0; | 
| 301 |  | -#endif | 
| 302 |  | -    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
| 303 |  | -    int rc; | 
| 304 |  | -    int secmult; | 
| 305 |  | -    long datasec; | 
| 306 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 307 |  | -    int rootdirsectors; | 
| 308 |  | -#endif | 
| 309 |  | - | 
| 310 |  | -    /* Read the sector */ | 
| 311 |  | -    unsigned char* buf = fat_get_sector_buffer(); | 
| 312 |  | -    rc = storage_read_sectors(IF_MD2(drive,) startsector,1,buf); | 
| 313 |  | -    if(rc) | 
| 314 |  | -    { | 
| 315 |  | -        fat_release_sector_buffer(); | 
| 316 |  | -        DEBUGF( "fat_mount() - Couldn't read BPB (error code %d)", rc); | 
| 317 |  | -        return rc * 10 - 1; | 
| 318 |  | -    } | 
| 319 |  | - | 
| 320 |  | -    memset(fat_bpb, 0, sizeof(struct bpb)); | 
| 321 |  | -    fat_bpb->startsector    = startsector; | 
| 322 |  | -#ifdef HAVE_MULTIDRIVE | 
| 323 |  | -    fat_bpb->drive          = drive; | 
| 324 |  | -#endif | 
| 325 |  | - | 
| 326 |  | -    fat_bpb->bpb_bytspersec = BYTES2INT16(buf,BPB_BYTSPERSEC); | 
| 327 |  | -    secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE;  | 
| 328 |  | -    /* Sanity check is performed later */ | 
| 329 |  | - | 
| 330 |  | -    fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS]; | 
| 331 |  | -    fat_bpb->bpb_rsvdseccnt = secmult * BYTES2INT16(buf,BPB_RSVDSECCNT); | 
| 332 |  | -    fat_bpb->bpb_numfats    = buf[BPB_NUMFATS]; | 
| 333 |  | -    fat_bpb->bpb_media      = buf[BPB_MEDIA]; | 
| 334 |  | -    fat_bpb->bpb_fatsz16    = secmult * BYTES2INT16(buf,BPB_FATSZ16); | 
| 335 |  | -    fat_bpb->bpb_fatsz32    = secmult * BYTES2INT32(buf,BPB_FATSZ32); | 
| 336 |  | -    fat_bpb->bpb_totsec16   = secmult * BYTES2INT16(buf,BPB_TOTSEC16); | 
| 337 |  | -    fat_bpb->bpb_totsec32   = secmult * BYTES2INT32(buf,BPB_TOTSEC32); | 
| 338 |  | -    fat_bpb->last_word      = BYTES2INT16(buf,BPB_LAST_WORD); | 
| 339 |  | - | 
| 340 |  | -    /* calculate a few commonly used values */ | 
| 341 |  | -    if (fat_bpb->bpb_fatsz16 != 0) | 
| 342 |  | -        fat_bpb->fatsize = fat_bpb->bpb_fatsz16; | 
| 343 |  | -    else | 
| 344 |  | -        fat_bpb->fatsize = fat_bpb->bpb_fatsz32; | 
| 345 |  | - | 
| 346 |  | -    if (fat_bpb->bpb_totsec16 != 0) | 
| 347 |  | -        fat_bpb->totalsectors = fat_bpb->bpb_totsec16; | 
| 348 |  | -    else | 
| 349 |  | -        fat_bpb->totalsectors = fat_bpb->bpb_totsec32; | 
| 350 |  | - | 
| 351 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 352 |  | -    fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT); | 
| 353 |  | -    if (!fat_bpb->bpb_bytspersec) | 
| 354 |  | -    { | 
| 355 |  | -        fat_release_sector_buffer(); | 
| 356 |  | -        return -2; | 
| 357 |  | -    } | 
| 358 |  | -    rootdirsectors = secmult * ((fat_bpb->bpb_rootentcnt * DIR_ENTRY_SIZE | 
| 359 |  | -                     + fat_bpb->bpb_bytspersec - 1) / fat_bpb->bpb_bytspersec); | 
| 360 |  | -#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
| 361 |  | - | 
| 362 |  | -    fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt | 
| 363 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 364 |  | -        + rootdirsectors | 
| 365 |  | -#endif | 
| 366 |  | -        + fat_bpb->bpb_numfats * fat_bpb->fatsize; | 
| 367 |  | - | 
| 368 |  | -    /* Determine FAT type */ | 
| 369 |  | -    datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector; | 
| 370 |  | -    if (fat_bpb->bpb_secperclus) | 
| 371 |  | -        fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus; | 
| 372 |  | -    else | 
| 373 |  | -    { | 
| 374 |  | -        fat_release_sector_buffer(); | 
| 375 |  | -        return -2; | 
| 376 |  | -    } | 
| 377 |  | - | 
| 378 |  | -#ifdef TEST_FAT | 
| 379 |  | -    /* | 
| 380 |  | -      we are sometimes testing with "illegally small" fat32 images, | 
| 381 |  | -      so we don't use the proper fat32 test case for test code | 
| 382 |  | -    */ | 
| 383 |  | -    if ( fat_bpb->bpb_fatsz16 ) | 
| 384 |  | -#else | 
| 385 |  | -    if ( fat_bpb->dataclusters < 65525 ) | 
| 386 |  | -#endif | 
| 387 |  | -    { /* FAT16 */ | 
| 388 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 389 |  | -        fat_bpb->is_fat16 = true; | 
| 390 |  | -        if (fat_bpb->dataclusters < 4085) | 
| 391 |  | -        { /* FAT12 */ | 
| 392 |  | -            fat_release_sector_buffer(); | 
| 393 |  | -            DEBUGF("This is FAT12. Go away!"); | 
| 394 |  | -            return -2; | 
| 395 |  | -        } | 
| 396 |  | -#else /* #ifdef HAVE_FAT16SUPPORT */ | 
| 397 |  | -        fat_release_sector_buffer(); | 
| 398 |  | -        DEBUGF("This is not FAT32. Go away!"); | 
| 399 |  | -        return -2; | 
| 400 |  | -#endif /* #ifndef HAVE_FAT16SUPPORT */ | 
| 401 |  | -    } | 
| 402 |  | - | 
| 403 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 404 |  | -    if (fat_bpb->is_fat16) | 
| 405 |  | -    { /* FAT16 specific part of BPB */ | 
| 406 |  | -        int dirclusters;   | 
| 407 |  | -        fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt | 
| 408 |  | -            + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16; | 
| 409 |  | -        dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1) | 
| 410 |  | -            / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */ | 
| 411 |  | -        /* I assign negative pseudo cluster numbers for the root directory, | 
| 412 |  | -           their range is counted upward until -1. */ | 
| 413 |  | -        fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data*/ | 
| 414 |  | -        fat_bpb->rootdiroffset = dirclusters * fat_bpb->bpb_secperclus | 
| 415 |  | -            - rootdirsectors; | 
| 416 |  | -    } | 
| 417 |  | -    else | 
| 418 |  | -#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
| 419 |  | -    { /* FAT32 specific part of BPB */ | 
| 420 |  | -        fat_bpb->bpb_rootclus  = BYTES2INT32(buf,BPB_ROOTCLUS); | 
| 421 |  | -        fat_bpb->bpb_fsinfo    = secmult * BYTES2INT16(buf,BPB_FSINFO); | 
| 422 |  | -        fat_bpb->rootdirsector = cluster2sec(IF_MV2(fat_bpb,) | 
| 423 |  | -                                             fat_bpb->bpb_rootclus); | 
| 424 |  | -    } | 
| 425 |  | - | 
| 426 |  | -    rc = bpb_is_sane(IF_MV(fat_bpb)); | 
| 427 |  | -    if (rc < 0) | 
| 428 |  | -    { | 
| 429 |  | -        fat_release_sector_buffer(); | 
| 430 |  | -        DEBUGF( "fat_mount() - BPB is not sane"); | 
| 431 |  | -        return rc * 10 - 3; | 
| 432 |  | -    } | 
| 433 |  | - | 
| 434 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 435 |  | -    if (fat_bpb->is_fat16) | 
| 436 |  | -    { | 
| 437 |  | -        fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc below */ | 
| 438 |  | -        fat_bpb->fsinfo.nextfree = 0xffffffff; | 
| 439 |  | -    } | 
| 440 |  | -    else | 
| 441 |  | -#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
| 442 |  | -    { | 
| 443 |  | -        /* Read the fsinfo sector */ | 
| 444 |  | -        rc = storage_read_sectors(IF_MD2(drive,) | 
| 445 |  | -            startsector + fat_bpb->bpb_fsinfo, 1, buf); | 
| 446 |  | -        if (rc < 0) | 
| 447 |  | -        { | 
| 448 |  | -            fat_release_sector_buffer(); | 
| 449 |  | -            DEBUGF( "fat_mount() - Couldn't read FSInfo (error code %d)", rc); | 
| 450 |  | -            return rc * 10 - 4; | 
| 451 |  | -        } | 
| 452 |  | -        fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT); | 
| 453 |  | -        fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE); | 
| 454 |  | -    } | 
| 455 |  | -    fat_release_sector_buffer(); | 
| 456 |  | - | 
| 457 |  | -    /* calculate freecount if unset */ | 
| 458 |  | -    if ( fat_bpb->fsinfo.freecount == 0xffffffff ) | 
| 459 |  | -    { | 
| 460 |  | -        fat_recalc_free(IF_MV(volume)); | 
| 461 |  | -    } | 
| 462 |  | - | 
| 463 |  | -    DEBUGF("Freecount: %ld",fat_bpb->fsinfo.freecount); | 
| 464 |  | -    DEBUGF("Nextfree: 0x%lx",fat_bpb->fsinfo.nextfree); | 
| 465 |  | -    DEBUGF("Cluster count: 0x%lx",fat_bpb->dataclusters); | 
| 466 |  | -    DEBUGF("Sectors per cluster: %d",fat_bpb->bpb_secperclus); | 
| 467 |  | -    DEBUGF("FAT sectors: 0x%lx",fat_bpb->fatsize); | 
| 468 |  | - | 
| 469 |  | -#ifdef HAVE_MULTIVOLUME | 
| 470 |  | -    fat_bpb->mounted = true; | 
| 471 |  | -#endif | 
| 472 |  | - | 
| 473 |  | -    return 0; | 
| 474 |  | -} | 
| 475 |  | - | 
| 476 |  | -#ifdef HAVE_HOTSWAP | 
| 477 |  | -int fat_unmount(int volume, bool flush) | 
| 478 |  | -{ | 
| 479 |  | -    int rc; | 
| 480 |  | -#ifdef HAVE_MULTIVOLUME | 
| 481 |  | -    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
| 482 |  | -#else | 
| 483 |  | -    (void)volume; | 
| 484 |  | -#endif | 
| 485 |  | - | 
| 486 |  | -    if(flush) | 
| 487 |  | -    { | 
| 488 |  | -        rc = flush_fat(IF_MV(fat_bpb)); /* the clean way, while still alive */ | 
| 489 |  | -    } | 
| 490 |  | -    else | 
| 491 |  | -    {   /* volume is not accessible any more, e.g. MMC removed */ | 
| 492 |  | -        int i; | 
| 493 |  | -        mutex_lock(&cache_mutex, TIMEOUT_BLOCK); | 
| 494 |  | -        for(i = 0;i < FAT_CACHE_SIZE;i++) | 
| 495 |  | -        { | 
| 496 |  | -            struct fat_cache_entry *fce = &fat_cache[i]; | 
| 497 |  | -            if((fce->valid || fce->locked) | 
| 498 |  | -#ifdef HAVE_MULTIVOLUME | 
| 499 |  | -               && fce->fat_vol == fat_bpb | 
| 500 |  | -#endif | 
| 501 |  | -              ) | 
| 502 |  | -            { | 
| 503 |  | -                fce->valid = false; /* discard all from that volume */ | 
| 504 |  | -                fce->locked = 0; | 
| 505 |  | -                fce->dirty = false; | 
| 506 |  | -            } | 
| 507 |  | -        } | 
| 508 |  | -        mutex_unlock(&cache_mutex); | 
| 509 |  | -        rc = 0; | 
| 510 |  | -    } | 
| 511 |  | -#ifdef HAVE_MULTIVOLUME | 
| 512 |  | -    fat_bpb->mounted = false; | 
| 513 |  | -#endif | 
| 514 |  | -    return rc; | 
| 515 |  | -} | 
| 516 |  | -#endif /* #ifdef HAVE_HOTSWAP */ | 
| 517 |  | - | 
| 518 |  | -void fat_recalc_free(IF_MV_NONVOID(int volume)) | 
| 519 |  | -{ | 
| 520 |  | -#ifndef HAVE_MULTIVOLUME | 
| 521 |  | -    const int volume = 0; | 
| 522 |  | -#endif | 
| 523 |  | -    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
| 524 |  | -    long free = 0; | 
| 525 |  | -    unsigned long i; | 
| 526 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 527 |  | -    if (fat_bpb->is_fat16) | 
| 528 |  | -    { | 
| 529 |  | -        for (i = 0; i<fat_bpb->fatsize; i++) { | 
| 530 |  | -            unsigned int j; | 
| 531 |  | -            unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false); | 
| 532 |  | -            for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { | 
| 533 |  | -                unsigned int c = i * CLUSTERS_PER_FAT16_SECTOR + j; | 
| 534 |  | -                if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ | 
| 535 |  | -                    break; | 
| 536 |  | - | 
| 537 |  | -                if (letoh16(fat[j]) == 0x0000) { | 
| 538 |  | -                    free++; | 
| 539 |  | -                    if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) | 
| 540 |  | -                        fat_bpb->fsinfo.nextfree = c; | 
| 541 |  | -                } | 
| 542 |  | -            } | 
| 543 |  | -            unlock_fat_sector(IF_MV2(fat_bpb,) i); | 
| 544 |  | -        } | 
| 545 |  | -    } | 
| 546 |  | -    else | 
| 547 |  | -#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
| 548 |  | -    { | 
| 549 |  | -        for (i = 0; i<fat_bpb->fatsize; i++) { | 
| 550 |  | -            unsigned int j; | 
| 551 |  | -            unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false); | 
| 552 |  | -            for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { | 
| 553 |  | -                unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j; | 
| 554 |  | -                if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ | 
| 555 |  | -                    break; | 
| 556 |  | - | 
| 557 |  | -                if (!(letoh32(fat[j]) & 0x0fffffff)) { | 
| 558 |  | -                    free++; | 
| 559 |  | -                    if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) | 
| 560 |  | -                        fat_bpb->fsinfo.nextfree = c; | 
| 561 |  | -                } | 
| 562 |  | -            } | 
| 563 |  | -            unlock_fat_sector(IF_MV2(fat_bpb,) i); | 
| 564 |  | -        } | 
| 565 |  | -    } | 
| 566 |  | -    fat_bpb->fsinfo.freecount = free; | 
| 567 |  | -    update_fsinfo(IF_MV(fat_bpb)); | 
| 568 |  | -} | 
| 569 |  | - | 
| 570 |  | -static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)) | 
| 571 |  | -{ | 
| 572 |  | -#ifndef HAVE_MULTIVOLUME | 
| 573 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 574 |  | -#endif | 
| 575 |  | -    if(fat_bpb->bpb_bytspersec % SECTOR_SIZE) | 
| 576 |  | -    { | 
| 577 |  | -        DEBUGF( "bpb_is_sane() - Error: sector size is not sane (%d)", | 
| 578 |  | -                fat_bpb->bpb_bytspersec); | 
| 579 |  | -        return -1; | 
| 580 |  | -    } | 
| 581 |  | -    if((long)fat_bpb->bpb_secperclus * (long)fat_bpb->bpb_bytspersec | 
| 582 |  | -                                                                   > 128L*1024L) | 
| 583 |  | -    { | 
| 584 |  | -        DEBUGF( "bpb_is_sane() - Error: cluster size is larger than 128K " | 
| 585 |  | -                "(%d * %d = %d)", | 
| 586 |  | -                fat_bpb->bpb_bytspersec, fat_bpb->bpb_secperclus, | 
| 587 |  | -                fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus); | 
| 588 |  | -        return -2; | 
| 589 |  | -    } | 
| 590 |  | -    if(fat_bpb->bpb_numfats != 2) | 
| 591 |  | -    { | 
| 592 |  | -        DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)", | 
| 593 |  | -                fat_bpb->bpb_numfats); | 
| 594 |  | -    } | 
| 595 |  | -    if(fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8) | 
| 596 |  | -    { | 
| 597 |  | -        DEBUGF( "bpb_is_sane() - Warning: Non-standard " | 
| 598 |  | -                "media type (0x%02x)", | 
| 599 |  | -                fat_bpb->bpb_media); | 
| 600 |  | -    } | 
| 601 |  | -    if(fat_bpb->last_word != 0xaa55) | 
| 602 |  | -    { | 
| 603 |  | -        DEBUGF( "bpb_is_sane() - Error: Last word is not " | 
| 604 |  | -                "0xaa55 (0x%04x)", fat_bpb->last_word); | 
| 605 |  | -        return -3; | 
| 606 |  | -    } | 
| 607 |  | - | 
| 608 |  | -    if (fat_bpb->fsinfo.freecount > | 
| 609 |  | -        (fat_bpb->totalsectors - fat_bpb->firstdatasector)/ | 
| 610 |  | -        fat_bpb->bpb_secperclus) | 
| 611 |  | -    { | 
| 612 |  | -        DEBUGF( "bpb_is_sane() - Error: FSInfo.Freecount > disk size " | 
| 613 |  | -                 "(0x%04lx)", fat_bpb->fsinfo.freecount); | 
| 614 |  | -        return -4; | 
| 615 |  | -    } | 
| 616 |  | - | 
| 617 |  | -    return 0; | 
| 618 |  | -} | 
| 619 |  | - | 
| 620 |  | -static void flush_fat_sector(struct fat_cache_entry *fce, | 
| 621 |  | -                             unsigned char *sectorbuf) | 
| 622 |  | -{ | 
| 623 |  | -    int rc; | 
| 624 |  | -    long secnum; | 
| 625 |  | - | 
| 626 |  | -    /* With multivolume, use only the FAT info from the cached sector! */ | 
| 627 |  | -#ifdef HAVE_MULTIVOLUME | 
| 628 |  | -    secnum = fce->secnum + fce->fat_vol->startsector; | 
| 629 |  | -#else | 
| 630 |  | -    secnum = fce->secnum + fat_bpbs[0].startsector; | 
| 631 |  | -#endif | 
| 632 |  | - | 
| 633 |  | -    /* Write to the first FAT */ | 
| 634 |  | -    rc = storage_write_sectors(IF_MD2(fce->fat_vol->drive,) | 
| 635 |  | -                           secnum, 1, | 
| 636 |  | -                           sectorbuf); | 
| 637 |  | -    if(rc < 0) | 
| 638 |  | -    { | 
| 639 |  | -        panicf(PANIC_KILLUSERTHREADS, "flush_fat_sector() - Could not write sector %ld" | 
| 640 |  | -               " (error %d)", | 
| 641 |  | -               secnum, rc); | 
| 642 |  | -    } | 
| 643 |  | -#ifdef HAVE_MULTIVOLUME | 
| 644 |  | -    if(fce->fat_vol->bpb_numfats > 1) | 
| 645 |  | -#else | 
| 646 |  | -    if(fat_bpbs[0].bpb_numfats > 1) | 
| 647 |  | -#endif | 
| 648 |  | -    { | 
| 649 |  | -        /* Write to the second FAT */ | 
| 650 |  | -#ifdef HAVE_MULTIVOLUME | 
| 651 |  | -        secnum += fce->fat_vol->fatsize; | 
| 652 |  | -#else | 
| 653 |  | -        secnum += fat_bpbs[0].fatsize; | 
| 654 |  | -#endif | 
| 655 |  | -        rc = storage_write_sectors(IF_MD2(fce->fat_vol->drive,) | 
| 656 |  | -                               secnum, 1, sectorbuf); | 
| 657 |  | -        if(rc < 0) | 
| 658 |  | -        { | 
| 659 |  | -            panicf(PANIC_KILLUSERTHREADS, "flush_fat_sector() - Could not write sector %ld" | 
| 660 |  | -                   " (error %d)", | 
| 661 |  | -                   secnum, rc); | 
| 662 |  | -        } | 
| 663 |  | -    } | 
| 664 |  | -    fce->dirty = false; | 
| 665 |  | -} | 
| 666 |  | - | 
| 667 |  | -static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,) | 
| 668 |  | -                              long fatsector, bool dirty) | 
| 669 |  | -{ | 
| 670 |  | -#ifndef HAVE_MULTIVOLUME | 
| 671 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 672 |  | -#endif | 
| 673 |  | -    long secnum = fatsector + fat_bpb->bpb_rsvdseccnt; | 
| 674 |  | -    int cache_index = secnum & FAT_CACHE_MASK; | 
| 675 |  | -    struct fat_cache_entry *fce = &fat_cache[cache_index]; | 
| 676 |  | -    unsigned char *sectorbuf = &fat_cache_sectors[cache_index][0]; | 
| 677 |  | -    int rc; | 
| 678 |  | - | 
| 679 |  | -    mutex_lock(&cache_mutex, TIMEOUT_BLOCK); /* make changes atomic */ | 
| 680 |  | - | 
| 681 |  | -    /* Delete the cache entry if it isn't the sector we want */ | 
| 682 |  | -    if(fce->valid && (fce->secnum != secnum | 
| 683 |  | -#ifdef HAVE_MULTIVOLUME | 
| 684 |  | -        || fce->fat_vol != fat_bpb | 
| 685 |  | -#endif | 
| 686 |  | -    )) | 
| 687 |  | -    { | 
| 688 |  | -        while (fce->locked) sleep(1000); | 
| 689 |  | -        /* Write back if it is dirty */ | 
| 690 |  | -        if(fce->dirty) | 
| 691 |  | -        { | 
| 692 |  | -            flush_fat_sector(fce, sectorbuf); | 
| 693 |  | -        } | 
| 694 |  | -        fce->valid = false; | 
| 695 |  | -    } | 
| 696 |  | - | 
| 697 |  | -    /* Load the sector if it is not cached */ | 
| 698 |  | -    if(!fce->valid) | 
| 699 |  | -    { | 
| 700 |  | -        rc = storage_read_sectors(IF_MD2(fat_bpb->drive,) | 
| 701 |  | -                              secnum + fat_bpb->startsector,1, | 
| 702 |  | -                              sectorbuf); | 
| 703 |  | -        if(rc < 0) | 
| 704 |  | -        { | 
| 705 |  | -            DEBUGF( "cache_fat_sector() - Could not read sector %ld" | 
| 706 |  | -                    " (error %d)", secnum, rc); | 
| 707 |  | -            mutex_unlock(&cache_mutex); | 
| 708 |  | -            return NULL; | 
| 709 |  | -        } | 
| 710 |  | -        fce->valid = true; | 
| 711 |  | -        fce->secnum = secnum; | 
| 712 |  | -#ifdef HAVE_MULTIVOLUME | 
| 713 |  | -        fce->fat_vol = fat_bpb; | 
| 714 |  | -#endif | 
| 715 |  | -    } | 
| 716 |  | -    fce->locked++; | 
| 717 |  | -    if (dirty) | 
| 718 |  | -        fce->dirty = true; /* dirt remains, sticky until flushed */ | 
| 719 |  | -    mutex_unlock(&cache_mutex); | 
| 720 |  | -    return sectorbuf; | 
| 721 |  | -} | 
| 722 |  | - | 
| 723 |  | -static void unlock_fat_sector(IF_MV2(struct bpb* fat_bpb,) long fatsector) | 
| 724 |  | -{ | 
| 725 |  | -#ifndef HAVE_MULTIVOLUME | 
| 726 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 727 |  | -#endif | 
| 728 |  | -    long secnum = fatsector + fat_bpb->bpb_rsvdseccnt; | 
| 729 |  | -    int cache_index = secnum & FAT_CACHE_MASK; | 
| 730 |  | -    fat_cache[cache_index].locked--; | 
| 731 |  | -} | 
| 732 |  | - | 
| 733 |  | -void* fat_get_sector_buffer() | 
| 734 |  | -{ | 
| 735 |  | -    mutex_lock(&tempbuf_mutex, TIMEOUT_BLOCK); | 
| 736 |  | -    if (tempbuf_locked) | 
| 737 |  | -        panicf(PANIC_KILLUSERTHREADS, "FAT: Tried to lock temporary sector buffer twice!"); | 
| 738 |  | -    tempbuf_locked = true; | 
| 739 |  | -    return fat_tempbuf; | 
| 740 |  | -} | 
| 741 |  | - | 
| 742 |  | -void fat_release_sector_buffer() | 
| 743 |  | -{ | 
| 744 |  | -    tempbuf_locked = false; | 
| 745 |  | -    mutex_unlock(&tempbuf_mutex); | 
| 746 |  | -} | 
| 747 |  | - | 
| 748 |  | -static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,) | 
| 749 |  | -                                       unsigned long startcluster) | 
| 750 |  | -{ | 
| 751 |  | -#ifndef HAVE_MULTIVOLUME | 
| 752 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 753 |  | -#endif | 
| 754 |  | -    unsigned long sector; | 
| 755 |  | -    unsigned long offset; | 
| 756 |  | -    unsigned long i; | 
| 757 |  | - | 
| 758 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 759 |  | -    if (fat_bpb->is_fat16) | 
| 760 |  | -    { | 
| 761 |  | -        sector = startcluster / CLUSTERS_PER_FAT16_SECTOR; | 
| 762 |  | -        offset = startcluster % CLUSTERS_PER_FAT16_SECTOR; | 
| 763 |  | - | 
| 764 |  | -        for (i = 0; i<fat_bpb->fatsize; i++) { | 
| 765 |  | -            unsigned int j; | 
| 766 |  | -            unsigned int nr = (i + sector) % fat_bpb->fatsize; | 
| 767 |  | -            unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false); | 
| 768 |  | -            if ( !fat ) | 
| 769 |  | -                break; | 
| 770 |  | -            for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { | 
| 771 |  | -                int k = (j + offset) % CLUSTERS_PER_FAT16_SECTOR; | 
| 772 |  | -                if (letoh16(fat[k]) == 0x0000) { | 
| 773 |  | -                    unsigned int c = nr * CLUSTERS_PER_FAT16_SECTOR + k; | 
| 774 |  | -                     /* Ignore the reserved clusters 0 & 1, and also | 
| 775 |  | -                        cluster numbers out of bounds */ | 
| 776 |  | -                    if ( c < 2 || c > fat_bpb->dataclusters+1 ) | 
| 777 |  | -                        continue; | 
| 778 |  | -                    unlock_fat_sector(IF_MV2(fat_bpb,) nr); | 
| 779 |  | -                    DEBUGF("find_free_cluster(%x) == %x",startcluster,c); | 
| 780 |  | -                    fat_bpb->fsinfo.nextfree = c; | 
| 781 |  | -                    return c; | 
| 782 |  | -                } | 
| 783 |  | -            } | 
| 784 |  | -            unlock_fat_sector(IF_MV2(fat_bpb,) nr); | 
| 785 |  | -            offset = 0; | 
| 786 |  | -        } | 
| 787 |  | -    } | 
| 788 |  | -    else | 
| 789 |  | -#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
| 790 |  | -    { | 
| 791 |  | -        sector = startcluster / CLUSTERS_PER_FAT_SECTOR; | 
| 792 |  | -        offset = startcluster % CLUSTERS_PER_FAT_SECTOR; | 
| 793 |  | - | 
| 794 |  | -        for (i = 0; i<fat_bpb->fatsize; i++) { | 
| 795 |  | -            unsigned int j; | 
| 796 |  | -            unsigned long nr = (i + sector) % fat_bpb->fatsize; | 
| 797 |  | -            unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false); | 
| 798 |  | -            if ( !fat ) | 
| 799 |  | -                break; | 
| 800 |  | -            for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { | 
| 801 |  | -                int k = (j + offset) % CLUSTERS_PER_FAT_SECTOR; | 
| 802 |  | -                if (!(letoh32(fat[k]) & 0x0fffffff)) { | 
| 803 |  | -                    unsigned long c = nr * CLUSTERS_PER_FAT_SECTOR + k; | 
| 804 |  | -                     /* Ignore the reserved clusters 0 & 1, and also | 
| 805 |  | -                        cluster numbers out of bounds */ | 
| 806 |  | -                    if ( c < 2 || c > fat_bpb->dataclusters+1 ) | 
| 807 |  | -                        continue; | 
| 808 |  | -                    unlock_fat_sector(IF_MV2(fat_bpb,) nr); | 
| 809 |  | -                    DEBUGF("find_free_cluster(%lx) == %lx",startcluster,c); | 
| 810 |  | -                    fat_bpb->fsinfo.nextfree = c; | 
| 811 |  | -                    return c; | 
| 812 |  | -                } | 
| 813 |  | -            } | 
| 814 |  | -            unlock_fat_sector(IF_MV2(fat_bpb,) nr); | 
| 815 |  | -            offset = 0; | 
| 816 |  | -        } | 
| 817 |  | -    } | 
| 818 |  | - | 
| 819 |  | -    DEBUGF("find_free_cluster(%lx) == 0",startcluster); | 
| 820 |  | -    return 0; /* 0 is an illegal cluster number */ | 
| 821 |  | -} | 
| 822 |  | - | 
| 823 |  | -static int update_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry, | 
| 824 |  | -                            unsigned long val) | 
| 825 |  | -{ | 
| 826 |  | -#ifndef HAVE_MULTIVOLUME | 
| 827 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 828 |  | -#endif | 
| 829 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 830 |  | -    if (fat_bpb->is_fat16) | 
| 831 |  | -    { | 
| 832 |  | -        int sector = entry / CLUSTERS_PER_FAT16_SECTOR; | 
| 833 |  | -        int offset = entry % CLUSTERS_PER_FAT16_SECTOR; | 
| 834 |  | -        unsigned short* sec; | 
| 835 |  | - | 
| 836 |  | -        val &= 0xFFFF; | 
| 837 |  | - | 
| 838 |  | -        DEBUGF("update_fat_entry(%x,%x)",entry,val); | 
| 839 |  | - | 
| 840 |  | -        if (entry==val) | 
| 841 |  | -            panicf(PANIC_KILLUSERTHREADS, "Creating FAT loop: %lx,%lx",entry,val); | 
| 842 |  | - | 
| 843 |  | -        if ( entry < 2 ) | 
| 844 |  | -            panicf(PANIC_KILLUSERTHREADS, "Updating reserved FAT entry %ld.",entry); | 
| 845 |  | - | 
| 846 |  | -        sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true); | 
| 847 |  | -        if (!sec) | 
| 848 |  | -        { | 
| 849 |  | -            DEBUGF( "update_fat_entry() - Could not cache sector %d", sector); | 
| 850 |  | -            return -1; | 
| 851 |  | -        } | 
| 852 |  | - | 
| 853 |  | -        if ( val ) { | 
| 854 |  | -            if (letoh16(sec[offset]) == 0x0000 && fat_bpb->fsinfo.freecount > 0) | 
| 855 |  | -                fat_bpb->fsinfo.freecount--; | 
| 856 |  | -        } | 
| 857 |  | -        else { | 
| 858 |  | -            if (letoh16(sec[offset])) | 
| 859 |  | -                fat_bpb->fsinfo.freecount++; | 
| 860 |  | -        } | 
| 861 |  | - | 
| 862 |  | -        DEBUGF("update_fat_entry: %d free clusters", | 
| 863 |  | -                fat_bpb->fsinfo.freecount); | 
| 864 |  | - | 
| 865 |  | -        sec[offset] = htole16(val); | 
| 866 |  | -        unlock_fat_sector(IF_MV2(fat_bpb,) sector); | 
| 867 |  | -    } | 
| 868 |  | -    else | 
| 869 |  | -#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
| 870 |  | -    { | 
| 871 |  | -        long sector = entry / CLUSTERS_PER_FAT_SECTOR; | 
| 872 |  | -        int offset = entry % CLUSTERS_PER_FAT_SECTOR; | 
| 873 |  | -        unsigned long* sec; | 
| 874 |  | - | 
| 875 |  | -        DEBUGF("update_fat_entry(%lx,%lx)",entry,val); | 
| 876 |  | - | 
| 877 |  | -        if (entry==val) | 
| 878 |  | -            panicf(PANIC_KILLUSERTHREADS, "Creating FAT loop: %lx,%lx",entry,val); | 
| 879 |  | - | 
| 880 |  | -        if ( entry < 2 ) | 
| 881 |  | -            panicf(PANIC_KILLUSERTHREADS, "Updating reserved FAT entry %ld.",entry); | 
| 882 |  | - | 
| 883 |  | -        sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true); | 
| 884 |  | -        if (!sec) | 
| 885 |  | -        { | 
| 886 |  | -            DEBUGF("update_fat_entry() - Could not cache sector %ld", sector); | 
| 887 |  | -            return -1; | 
| 888 |  | -        } | 
| 889 |  | - | 
| 890 |  | -        if ( val ) { | 
| 891 |  | -            if (!(letoh32(sec[offset]) & 0x0fffffff) && | 
| 892 |  | -                fat_bpb->fsinfo.freecount > 0) | 
| 893 |  | -                fat_bpb->fsinfo.freecount--; | 
| 894 |  | -        } | 
| 895 |  | -        else { | 
| 896 |  | -            if (letoh32(sec[offset]) & 0x0fffffff) | 
| 897 |  | -                fat_bpb->fsinfo.freecount++; | 
| 898 |  | -        } | 
| 899 |  | - | 
| 900 |  | -        DEBUGF("update_fat_entry: %ld free clusters", | 
| 901 |  | -                fat_bpb->fsinfo.freecount); | 
| 902 |  | - | 
| 903 |  | -        /* don't change top 4 bits */ | 
| 904 |  | -        sec[offset] &= htole32(0xf0000000); | 
| 905 |  | -        sec[offset] |= htole32(val & 0x0fffffff); | 
| 906 |  | -        unlock_fat_sector(IF_MV2(fat_bpb,) sector); | 
| 907 |  | -    } | 
| 908 |  | - | 
| 909 |  | -    return 0; | 
| 910 |  | -} | 
| 911 |  | - | 
| 912 |  | -static long read_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry) | 
| 913 |  | -{ | 
| 914 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 915 |  | -#ifndef HAVE_MULTIVOLUME | 
| 916 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 917 |  | -#endif | 
| 918 |  | -    if (fat_bpb->is_fat16) | 
| 919 |  | -    { | 
| 920 |  | -        int sector = entry / CLUSTERS_PER_FAT16_SECTOR; | 
| 921 |  | -        int offset = entry % CLUSTERS_PER_FAT16_SECTOR; | 
| 922 |  | -        unsigned short* sec; | 
| 923 |  | - | 
| 924 |  | -        sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false); | 
| 925 |  | -        if (!sec) | 
| 926 |  | -        { | 
| 927 |  | -            DEBUGF( "read_fat_entry() - Could not cache sector %d", sector); | 
| 928 |  | -            return -1; | 
| 929 |  | -        } | 
| 930 |  | - | 
| 931 |  | -        long val = letoh16(sec[offset]); | 
| 932 |  | -        unlock_fat_sector(IF_MV2(fat_bpb,) sector); | 
| 933 |  | - | 
| 934 |  | -        return val; | 
| 935 |  | -    } | 
| 936 |  | -    else | 
| 937 |  | -#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
| 938 |  | -    { | 
| 939 |  | -        long sector = entry / CLUSTERS_PER_FAT_SECTOR; | 
| 940 |  | -        int offset = entry % CLUSTERS_PER_FAT_SECTOR; | 
| 941 |  | -        unsigned long* sec; | 
| 942 |  | - | 
| 943 |  | -        sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false); | 
| 944 |  | -        if (!sec) | 
| 945 |  | -        { | 
| 946 |  | -            DEBUGF( "read_fat_entry() - Could not cache sector %ld", sector); | 
| 947 |  | -            return -1; | 
| 948 |  | -        } | 
| 949 |  | - | 
| 950 |  | -        long val = letoh32(sec[offset]) & 0x0fffffff; | 
| 951 |  | -        unlock_fat_sector(IF_MV2(fat_bpb,) sector); | 
| 952 |  | - | 
| 953 |  | -        return val; | 
| 954 |  | -    } | 
| 955 |  | -} | 
| 956 |  | - | 
| 957 |  | -static long get_next_cluster(IF_MV2(struct bpb* fat_bpb,) long cluster) | 
| 958 |  | -{ | 
| 959 |  | -    long next_cluster; | 
| 960 |  | -    long eof_mark = FAT_EOF_MARK; | 
| 961 |  | - | 
| 962 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 963 |  | -#ifndef HAVE_MULTIVOLUME | 
| 964 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 965 |  | -#endif | 
| 966 |  | -    if (fat_bpb->is_fat16) | 
| 967 |  | -    { | 
| 968 |  | -        eof_mark &= 0xFFFF; /* only 16 bit */ | 
| 969 |  | -        if (cluster < 0) /* FAT16 root dir */ | 
| 970 |  | -            return cluster + 1; /* don't use the FAT */ | 
| 971 |  | -    } | 
| 972 |  | -#endif | 
| 973 |  | -    next_cluster = read_fat_entry(IF_MV2(fat_bpb,) cluster); | 
| 974 |  | - | 
| 975 |  | -    /* is this last cluster in chain? */ | 
| 976 |  | -    if ( next_cluster >= eof_mark ) | 
| 977 |  | -        return 0; | 
| 978 |  | -    else | 
| 979 |  | -        return next_cluster; | 
| 980 |  | -} | 
| 981 |  | - | 
| 982 |  | -static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)) | 
| 983 |  | -{ | 
| 984 |  | -#ifndef HAVE_MULTIVOLUME | 
| 985 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 986 |  | -#endif | 
| 987 |  | -    unsigned long* intptr; | 
| 988 |  | -    int rc; | 
| 989 |  | - | 
| 990 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 991 |  | -    if (fat_bpb->is_fat16) | 
| 992 |  | -        return 0; /* FAT16 has no FsInfo */ | 
| 993 |  | -#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
| 994 |  | - | 
| 995 |  | -    /* update fsinfo */ | 
| 996 |  | -    unsigned char* fsinfo = fat_get_sector_buffer(); | 
| 997 |  | -    rc = storage_read_sectors(IF_MD2(fat_bpb->drive,) | 
| 998 |  | -                          fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1,fsinfo); | 
| 999 |  | -    if (rc < 0) | 
| 1000 |  | -    { | 
| 1001 |  | -        fat_release_sector_buffer(); | 
| 1002 |  | -        DEBUGF( "update_fsinfo() - Couldn't read FSInfo (error code %d)", rc); | 
| 1003 |  | -        return rc * 10 - 1; | 
| 1004 |  | -    } | 
| 1005 |  | -    intptr = (long*)&(fsinfo[FSINFO_FREECOUNT]); | 
| 1006 |  | -    *intptr = htole32(fat_bpb->fsinfo.freecount); | 
| 1007 |  | - | 
| 1008 |  | -    intptr = (long*)&(fsinfo[FSINFO_NEXTFREE]); | 
| 1009 |  | -    *intptr = htole32(fat_bpb->fsinfo.nextfree); | 
| 1010 |  | - | 
| 1011 |  | -    rc = storage_write_sectors(IF_MD2(fat_bpb->drive,) | 
| 1012 |  | -                           fat_bpb->startsector + fat_bpb->bpb_fsinfo,1,fsinfo); | 
| 1013 |  | -    fat_release_sector_buffer(); | 
| 1014 |  | -    if (rc < 0) | 
| 1015 |  | -    { | 
| 1016 |  | -        DEBUGF( "update_fsinfo() - Couldn't write FSInfo (error code %d)", rc); | 
| 1017 |  | -        return rc * 10 - 2; | 
| 1018 |  | -    } | 
| 1019 |  | - | 
| 1020 |  | -    return 0; | 
| 1021 |  | -} | 
| 1022 |  | - | 
| 1023 |  | -static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb)) | 
| 1024 |  | -{ | 
| 1025 |  | -    int i; | 
| 1026 |  | -    int rc; | 
| 1027 |  | -    unsigned char *sec; | 
| 1028 |  | -    DEBUGF("flush_fat()"); | 
| 1029 |  | - | 
| 1030 |  | -    mutex_lock(&cache_mutex, TIMEOUT_BLOCK); | 
| 1031 |  | -    for(i = 0;i < FAT_CACHE_SIZE;i++) | 
| 1032 |  | -    { | 
| 1033 |  | -        struct fat_cache_entry *fce = &fat_cache[i]; | 
| 1034 |  | -        if(fce->valid  | 
| 1035 |  | -#ifdef HAVE_MULTIVOLUME | 
| 1036 |  | -            && fce->fat_vol == fat_bpb | 
| 1037 |  | -#endif | 
| 1038 |  | -            && fce->dirty) | 
| 1039 |  | -        { | 
| 1040 |  | -            sec = fat_cache_sectors[i]; | 
| 1041 |  | -            flush_fat_sector(fce, sec); | 
| 1042 |  | -        } | 
| 1043 |  | -    } | 
| 1044 |  | -    mutex_unlock(&cache_mutex); | 
| 1045 |  | - | 
| 1046 |  | -    rc = update_fsinfo(IF_MV(fat_bpb)); | 
| 1047 |  | -    if (rc < 0) | 
| 1048 |  | -        return rc * 10 - 3; | 
| 1049 |  | - | 
| 1050 |  | -    return 0; | 
| 1051 |  | -} | 
| 1052 |  | - | 
| 1053 |  | -static void fat_time(unsigned short* date, | 
| 1054 |  | -                     unsigned short* time, | 
| 1055 |  | -                     unsigned short* tenth ) | 
| 1056 |  | -{ | 
| 1057 |  | -#if CONFIG_RTC | 
| 1058 |  | -    struct tm* tm = get_time(); | 
| 1059 |  | - | 
| 1060 |  | -    if (date) | 
| 1061 |  | -        *date = ((tm->tm_year - 80) << 9) | | 
| 1062 |  | -            ((tm->tm_mon + 1) << 5) | | 
| 1063 |  | -            tm->tm_mday; | 
| 1064 |  | - | 
| 1065 |  | -    if (time) | 
| 1066 |  | -        *time = (tm->tm_hour << 11) | | 
| 1067 |  | -            (tm->tm_min << 5) | | 
| 1068 |  | -            (tm->tm_sec >> 1); | 
| 1069 |  | - | 
| 1070 |  | -    if (tenth) | 
| 1071 |  | -        *tenth = (tm->tm_sec & 1) * 100; | 
| 1072 |  | -#else | 
| 1073 |  | - | 
| 1074 |  | -    if (date) *date = (1 << 5) | 1; | 
| 1075 |  | -    if (time) *time = 0; | 
| 1076 |  | -    if (tenth) *tenth = 0; | 
| 1077 |  | - | 
| 1078 |  | -#endif /* CONFIG_RTC */ | 
| 1079 |  | -} | 
| 1080 |  | - | 
| 1081 |  | -static int write_long_name(struct fat_file* file, | 
| 1082 |  | -                           unsigned int firstentry, | 
| 1083 |  | -                           unsigned int numentries, | 
| 1084 |  | -                           const unsigned char* name, | 
| 1085 |  | -                           const unsigned char* shortname, | 
| 1086 |  | -                           bool is_directory) | 
| 1087 |  | -{ | 
| 1088 |  | -    unsigned char* entry; | 
| 1089 |  | -    unsigned int idx = firstentry % DIR_ENTRIES_PER_SECTOR; | 
| 1090 |  | -    unsigned int sector = firstentry / DIR_ENTRIES_PER_SECTOR; | 
| 1091 |  | -    unsigned char chksum = 0; | 
| 1092 |  | -    unsigned int i, j=0; | 
| 1093 |  | -    unsigned int nameidx=0, namelen = strlen(name); | 
| 1094 |  | -    int rc; | 
| 1095 |  | -    unsigned short name_utf16[namelen + 1]; | 
| 1096 |  | - | 
| 1097 |  | -    DEBUGF("write_long_name(file:%lx, first:%d, num:%d, name:%s)", | 
| 1098 |  | -            file->firstcluster, firstentry, numentries, name); | 
| 1099 |  | - | 
| 1100 |  | -    rc = fat_seek(file, sector); | 
| 1101 |  | -    if (rc<0) | 
| 1102 |  | -        return rc * 10 - 1; | 
| 1103 |  | - | 
| 1104 |  | -    unsigned char* buf = fat_get_sector_buffer(); | 
| 1105 |  | -    rc = fat_readwrite(file, 1, buf, false); | 
| 1106 |  | -    if (rc<1) | 
| 1107 |  | -    { | 
| 1108 |  | -        fat_release_sector_buffer(); | 
| 1109 |  | -        return rc * 10 - 2; | 
| 1110 |  | -    } | 
| 1111 |  | - | 
| 1112 |  | -    /* calculate shortname checksum */ | 
| 1113 |  | -    for (i=11; i>0; i--) | 
| 1114 |  | -        chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++]; | 
| 1115 |  | - | 
| 1116 |  | -    /* calc position of last name segment */ | 
| 1117 |  | -    if ( namelen > NAME_BYTES_PER_ENTRY ) | 
| 1118 |  | -        for (nameidx=0; | 
| 1119 |  | -             nameidx < (namelen - NAME_BYTES_PER_ENTRY); | 
| 1120 |  | -             nameidx += NAME_BYTES_PER_ENTRY); | 
| 1121 |  | - | 
| 1122 |  | -    /* we need to convert the name first    */ | 
| 1123 |  | -    /* since it is written in reverse order */ | 
| 1124 |  | -    for (i = 0; i <= namelen; i++) | 
| 1125 |  | -        name_utf16[i] = *(name++); | 
| 1126 |  | - | 
| 1127 |  | -    for (i=0; i < numentries; i++) { | 
| 1128 |  | -        /* new sector? */ | 
| 1129 |  | -        if ( idx >= DIR_ENTRIES_PER_SECTOR ) { | 
| 1130 |  | -            /* update current sector */ | 
| 1131 |  | -            rc = fat_seek(file, sector); | 
| 1132 |  | -            if (rc<0) | 
| 1133 |  | -            { | 
| 1134 |  | -                fat_release_sector_buffer(); | 
| 1135 |  | -                return rc * 10 - 3; | 
| 1136 |  | -            } | 
| 1137 |  | - | 
| 1138 |  | -            rc = fat_readwrite(file, 1, buf, true); | 
| 1139 |  | -            if (rc<1) | 
| 1140 |  | -            { | 
| 1141 |  | -                fat_release_sector_buffer(); | 
| 1142 |  | -                return rc * 10 - 4; | 
| 1143 |  | -            } | 
| 1144 |  | - | 
| 1145 |  | -            /* read next sector */ | 
| 1146 |  | -            rc = fat_readwrite(file, 1, buf, false); | 
| 1147 |  | -            if (rc<0) { | 
| 1148 |  | -                fat_release_sector_buffer(); | 
| 1149 |  | -                DEBUGF("Failed writing new sector: %d",rc); | 
| 1150 |  | -                return rc * 10 - 5; | 
| 1151 |  | -            } | 
| 1152 |  | -            if (rc==0) | 
| 1153 |  | -                /* end of dir */ | 
| 1154 |  | -                memset(buf, 0, SECTOR_SIZE); | 
| 1155 |  | - | 
| 1156 |  | -            sector++; | 
| 1157 |  | -            idx = 0; | 
| 1158 |  | -        } | 
| 1159 |  | - | 
| 1160 |  | -        entry = buf + idx * DIR_ENTRY_SIZE; | 
| 1161 |  | - | 
| 1162 |  | -        /* verify this entry is free */ | 
| 1163 |  | -        if (entry[0] && entry[0] != 0xe5 ) | 
| 1164 |  | -        { | 
| 1165 |  | -            fat_release_sector_buffer(); | 
| 1166 |  | -            panicf(PANIC_KILLUSERTHREADS, "Dir entry %d in sector %x is not free! " | 
| 1167 |  | -                   "%02x %02x %02x %02x", | 
| 1168 |  | -                   idx, sector, | 
| 1169 |  | -                   entry[0], entry[1], entry[2], entry[3]); | 
| 1170 |  | -        } | 
| 1171 |  | - | 
| 1172 |  | -        memset(entry, 0, DIR_ENTRY_SIZE); | 
| 1173 |  | -        if ( i+1 < numentries ) { | 
| 1174 |  | -            /* longname entry */ | 
| 1175 |  | -            unsigned int k, l = nameidx; | 
| 1176 |  | - | 
| 1177 |  | -            entry[FATLONG_ORDER] = numentries-i-1; | 
| 1178 |  | -            if (i==0) { | 
| 1179 |  | -                /* mark this as last long entry */ | 
| 1180 |  | -                entry[FATLONG_ORDER] |= FATLONG_LAST_LONG_ENTRY; | 
| 1181 |  | - | 
| 1182 |  | -                /* pad name with 0xffff  */ | 
| 1183 |  | -                for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | 
| 1184 |  | -                for (k=14; k<26; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | 
| 1185 |  | -                for (k=28; k<32; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | 
| 1186 |  | -            }; | 
| 1187 |  | -            /* set name */ | 
| 1188 |  | -            for (k=0; k<5 && l <= namelen; k++) { | 
| 1189 |  | -                entry[k*2 + 1] = (unsigned char)(name_utf16[l] & 0xff); | 
| 1190 |  | -                entry[k*2 + 2] = (unsigned char)(name_utf16[l++] >> 8); | 
| 1191 |  | -            } | 
| 1192 |  | -            for (k=0; k<6 && l <= namelen; k++) { | 
| 1193 |  | -                entry[k*2 + 14] = (unsigned char)(name_utf16[l] & 0xff); | 
| 1194 |  | -                entry[k*2 + 15] = (unsigned char)(name_utf16[l++] >> 8); | 
| 1195 |  | -            } | 
| 1196 |  | -            for (k=0; k<2 && l <= namelen; k++) { | 
| 1197 |  | -                entry[k*2 + 28] = (unsigned char)(name_utf16[l] & 0xff); | 
| 1198 |  | -                entry[k*2 + 29] = (unsigned char)(name_utf16[l++] >> 8); | 
| 1199 |  | -            } | 
| 1200 |  | - | 
| 1201 |  | -            entry[FATDIR_ATTR] = FAT_ATTR_LONG_NAME; | 
| 1202 |  | -            entry[FATDIR_FSTCLUSLO] = 0; | 
| 1203 |  | -            entry[FATLONG_TYPE] = 0; | 
| 1204 |  | -            entry[FATLONG_CHKSUM] = chksum; | 
| 1205 |  | -            DEBUGF("Longname entry %d (%d): %s", idx, nameidx, name+nameidx); | 
| 1206 |  | -        } | 
| 1207 |  | -        else { | 
| 1208 |  | -            /* shortname entry */ | 
| 1209 |  | -            unsigned short date=0, time=0, tenth=0; | 
| 1210 |  | -            DEBUGF("Shortname entry: %s", shortname); | 
| 1211 |  | -            memcpy(entry + FATDIR_NAME, shortname, 11); | 
| 1212 |  | -            entry[FATDIR_ATTR] = is_directory?FAT_ATTR_DIRECTORY:0; | 
| 1213 |  | -            entry[FATDIR_NTRES] = 0; | 
| 1214 |  | - | 
| 1215 |  | -            fat_time(&date, &time, &tenth); | 
| 1216 |  | -            entry[FATDIR_CRTTIMETENTH] = tenth; | 
| 1217 |  | -            *(unsigned short*)(entry + FATDIR_CRTTIME) = htole16(time); | 
| 1218 |  | -            *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time); | 
| 1219 |  | -            *(unsigned short*)(entry + FATDIR_CRTDATE) = htole16(date); | 
| 1220 |  | -            *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date); | 
| 1221 |  | -            *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date); | 
| 1222 |  | -        } | 
| 1223 |  | -        idx++; | 
| 1224 |  | -        nameidx -= NAME_BYTES_PER_ENTRY; | 
| 1225 |  | -    } | 
| 1226 |  | - | 
| 1227 |  | -    /* update last sector */ | 
| 1228 |  | -    rc = fat_seek(file, sector); | 
| 1229 |  | -    if (rc<0) | 
| 1230 |  | -    { | 
| 1231 |  | -        fat_release_sector_buffer(); | 
| 1232 |  | -        return rc * 10 - 6; | 
| 1233 |  | -    } | 
| 1234 |  | - | 
| 1235 |  | -    rc = fat_readwrite(file, 1, buf, true); | 
| 1236 |  | -    fat_release_sector_buffer(); | 
| 1237 |  | -    if (rc<1) | 
| 1238 |  | -        return rc * 10 - 7; | 
| 1239 |  | - | 
| 1240 |  | -    DEBUGF("write_long_name: success"); | 
| 1241 |  | -    return 0; | 
| 1242 |  | -} | 
| 1243 |  | - | 
| 1244 |  | -static int fat_checkname(const unsigned char* newname) | 
| 1245 |  | -{ | 
| 1246 |  | -    static const char invalid_chars[] = "\"*/:<>?\\|"; | 
| 1247 |  | -    int len = strlen(newname); | 
| 1248 |  | -    /* More sanity checks are probably needed */ | 
| 1249 |  | -    if (len > 255 || newname[len - 1] == '.') | 
| 1250 |  | -    { | 
| 1251 |  | -        return -1; | 
| 1252 |  | -    } | 
| 1253 |  | -    while (*newname) | 
| 1254 |  | -    { | 
| 1255 |  | -        if (*newname < ' ' || strchr(invalid_chars, *newname) != NULL) | 
| 1256 |  | -            return -1; | 
| 1257 |  | -        newname++; | 
| 1258 |  | -    } | 
| 1259 |  | -    /* check trailing space(s) */ | 
| 1260 |  | -    if(*(--newname) == ' ') | 
| 1261 |  | -        return -1; | 
| 1262 |  | - | 
| 1263 |  | -    return 0; | 
| 1264 |  | -} | 
| 1265 |  | - | 
| 1266 |  | -static int add_dir_entry(struct fat_dir* dir, | 
| 1267 |  | -                         struct fat_file* file, | 
| 1268 |  | -                         const char* name, | 
| 1269 |  | -                         bool is_directory, | 
| 1270 |  | -                         bool dotdir) | 
| 1271 |  | -{ | 
| 1272 |  | -#ifdef HAVE_MULTIVOLUME | 
| 1273 |  | -    struct bpb* fat_bpb = &fat_bpbs[dir->file.volume]; | 
| 1274 |  | -#else | 
| 1275 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 1276 |  | -#endif | 
| 1277 |  | -    unsigned char shortname[12]; | 
| 1278 |  | -    int rc; | 
| 1279 |  | -    unsigned int sector; | 
| 1280 |  | -    bool done = false; | 
| 1281 |  | -    int entries_needed, entries_found = 0; | 
| 1282 |  | -    int firstentry; | 
| 1283 |  | - | 
| 1284 |  | -    DEBUGF( "add_dir_entry(%s,%lx)", | 
| 1285 |  | -             name, file->firstcluster); | 
| 1286 |  | - | 
| 1287 |  | -    /* Don't check dotdirs name for validity */ | 
| 1288 |  | -    if (dotdir == false) { | 
| 1289 |  | -        rc = fat_checkname(name); | 
| 1290 |  | -        if (rc < 0) { | 
| 1291 |  | -            /* filename is invalid */ | 
| 1292 |  | -            return rc * 10 - 1; | 
| 1293 |  | -        } | 
| 1294 |  | -    } | 
| 1295 |  | - | 
| 1296 |  | -#ifdef HAVE_MULTIVOLUME | 
| 1297 |  | -    file->volume = dir->file.volume; /* inherit the volume, to make sure */ | 
| 1298 |  | -#endif | 
| 1299 |  | - | 
| 1300 |  | -    /* The "." and ".." directory entries must not be long names */ | 
| 1301 |  | -    if(dotdir) { | 
| 1302 |  | -        int i; | 
| 1303 |  | -        strlcpy(shortname, name, 12); | 
| 1304 |  | -        for(i = strlen(shortname); i < 12; i++) | 
| 1305 |  | -            shortname[i] = ' '; | 
| 1306 |  | - | 
| 1307 |  | -        entries_needed = 1; | 
| 1308 |  | -    } else { | 
| 1309 |  | -        create_dos_name(name, shortname); | 
| 1310 |  | - | 
| 1311 |  | -        /* one dir entry needed for every 13 bytes of filename, | 
| 1312 |  | -           plus one entry for the short name */ | 
| 1313 |  | -        entries_needed = (strlen(name) + (NAME_BYTES_PER_ENTRY-1)) | 
| 1314 |  | -                         / NAME_BYTES_PER_ENTRY + 1; | 
| 1315 |  | -    } | 
| 1316 |  | - | 
| 1317 |  | -    unsigned char* buf = fat_get_sector_buffer(); | 
| 1318 |  | -  restart: | 
| 1319 |  | -    firstentry = -1; | 
| 1320 |  | - | 
| 1321 |  | -    rc = fat_seek(&dir->file, 0); | 
| 1322 |  | -    if (rc < 0) | 
| 1323 |  | -    { | 
| 1324 |  | -        fat_release_sector_buffer(); | 
| 1325 |  | -        return rc * 10 - 2; | 
| 1326 |  | -    } | 
| 1327 |  | - | 
| 1328 |  | -    /* step 1: search for free entries and check for duplicate shortname */ | 
| 1329 |  | -    for (sector = 0; !done; sector++) | 
| 1330 |  | -    { | 
| 1331 |  | -        unsigned int i; | 
| 1332 |  | - | 
| 1333 |  | -        rc = fat_readwrite(&dir->file, 1, buf, false); | 
| 1334 |  | -        if (rc < 0) { | 
| 1335 |  | -            fat_release_sector_buffer(); | 
| 1336 |  | -            DEBUGF( "add_dir_entry() - Couldn't read dir" | 
| 1337 |  | -                    " (error code %d)", rc); | 
| 1338 |  | -            return rc * 10 - 3; | 
| 1339 |  | -        } | 
| 1340 |  | - | 
| 1341 |  | -        if (rc == 0) { /* current end of dir reached */ | 
| 1342 |  | -            DEBUGF("End of dir on cluster boundary"); | 
| 1343 |  | -            break; | 
| 1344 |  | -        } | 
| 1345 |  | - | 
| 1346 |  | -        /* look for free slots */ | 
| 1347 |  | -        for (i = 0; i < DIR_ENTRIES_PER_SECTOR; i++) | 
| 1348 |  | -        { | 
| 1349 |  | -            switch (buf[i * DIR_ENTRY_SIZE]) { | 
| 1350 |  | -              case 0: | 
| 1351 |  | -                entries_found += DIR_ENTRIES_PER_SECTOR - i; | 
| 1352 |  | -                DEBUGF("Found end of dir %d", | 
| 1353 |  | -                        sector * DIR_ENTRIES_PER_SECTOR + i); | 
| 1354 |  | -                i = DIR_ENTRIES_PER_SECTOR - 1; | 
| 1355 |  | -                done = true; | 
| 1356 |  | -                break; | 
| 1357 |  | - | 
| 1358 |  | -              case 0xe5: | 
| 1359 |  | -                entries_found++; | 
| 1360 |  | -                DEBUGF("Found free entry %d (%d/%d)", | 
| 1361 |  | -                        sector * DIR_ENTRIES_PER_SECTOR + i, | 
| 1362 |  | -                        entries_found, entries_needed); | 
| 1363 |  | -                break; | 
| 1364 |  | - | 
| 1365 |  | -              default: | 
| 1366 |  | -                entries_found = 0; | 
| 1367 |  | - | 
| 1368 |  | -                /* check that our intended shortname doesn't already exist */ | 
| 1369 |  | -                if (!strncmp(shortname, buf + i * DIR_ENTRY_SIZE, 11)) { | 
| 1370 |  | -                    /* shortname exists already, make a new one */ | 
| 1371 |  | -                    randomize_dos_name(shortname); | 
| 1372 |  | -                    DEBUGF("Duplicate shortname, changing to %s", | 
| 1373 |  | -                            shortname); | 
| 1374 |  | - | 
| 1375 |  | -                    /* name has changed, we need to restart search */ | 
| 1376 |  | -                    goto restart; | 
| 1377 |  | -                } | 
| 1378 |  | -                break; | 
| 1379 |  | -            } | 
| 1380 |  | -            if (firstentry < 0 && (entries_found >= entries_needed)) | 
| 1381 |  | -                firstentry = sector * DIR_ENTRIES_PER_SECTOR + i + 1 | 
| 1382 |  | -                             - entries_found; | 
| 1383 |  | -        } | 
| 1384 |  | -    } | 
| 1385 |  | - | 
| 1386 |  | -    /* step 2: extend the dir if necessary */ | 
| 1387 |  | -    if (firstentry < 0) | 
| 1388 |  | -    { | 
| 1389 |  | -        DEBUGF("Adding new sector(s) to dir"); | 
| 1390 |  | -        rc = fat_seek(&dir->file, sector); | 
| 1391 |  | -        if (rc < 0) | 
| 1392 |  | -        { | 
| 1393 |  | -            fat_release_sector_buffer(); | 
| 1394 |  | -            return rc * 10 - 4; | 
| 1395 |  | -        } | 
| 1396 |  | -        memset(buf, 0, SECTOR_SIZE); | 
| 1397 |  | - | 
| 1398 |  | -        /* we must clear whole clusters */ | 
| 1399 |  | -        for (; (entries_found < entries_needed) || | 
| 1400 |  | -               (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); sector++) | 
| 1401 |  | -        { | 
| 1402 |  | -            if (sector >= (65536/DIR_ENTRIES_PER_SECTOR)) | 
| 1403 |  | -            { | 
| 1404 |  | -                fat_release_sector_buffer(); | 
| 1405 |  | -                return -5; /* dir too large -- FAT specification */ | 
| 1406 |  | -            } | 
| 1407 |  | - | 
| 1408 |  | -            rc = fat_readwrite(&dir->file, 1, buf, true); | 
| 1409 |  | -            if (rc < 1)  /* No more room or something went wrong */ | 
| 1410 |  | -            { | 
| 1411 |  | -                fat_release_sector_buffer(); | 
| 1412 |  | -                return rc * 10 - 6; | 
| 1413 |  | -            } | 
| 1414 |  | - | 
| 1415 |  | -            entries_found += DIR_ENTRIES_PER_SECTOR; | 
| 1416 |  | -        } | 
| 1417 |  | - | 
| 1418 |  | -        firstentry = sector * DIR_ENTRIES_PER_SECTOR - entries_found; | 
| 1419 |  | -    } | 
| 1420 |  | -    fat_release_sector_buffer(); | 
| 1421 |  | - | 
| 1422 |  | -    /* step 3: add entry */ | 
| 1423 |  | -    sector = firstentry / DIR_ENTRIES_PER_SECTOR; | 
| 1424 |  | -    DEBUGF("Adding longname to entry %d in sector %d", | 
| 1425 |  | -            firstentry, sector); | 
| 1426 |  | - | 
| 1427 |  | -    rc = write_long_name(&dir->file, firstentry, | 
| 1428 |  | -                         entries_needed, name, shortname, is_directory); | 
| 1429 |  | -    if (rc < 0) | 
| 1430 |  | -        return rc * 10 - 7; | 
| 1431 |  | - | 
| 1432 |  | -    /* remember where the shortname dir entry is located */ | 
| 1433 |  | -    file->direntry = firstentry + entries_needed - 1; | 
| 1434 |  | -    file->direntries = entries_needed; | 
| 1435 |  | -    file->dircluster = dir->file.firstcluster; | 
| 1436 |  | -    DEBUGF("Added new dir entry %d, using %d slots.", | 
| 1437 |  | -            file->direntry, file->direntries); | 
| 1438 |  | - | 
| 1439 |  | -    return 0; | 
| 1440 |  | -} | 
| 1441 |  | - | 
| 1442 |  | -static unsigned char char2dos(unsigned char c, int* randomize) | 
| 1443 |  | -{ | 
| 1444 |  | -    static const char invalid_chars[] = "\"*+,./:;<=>?[\\]|"; | 
| 1445 |  | - | 
| 1446 |  | -    if (c <= 0x20) | 
| 1447 |  | -        c = 0;   /* Illegal char, remove */ | 
| 1448 |  | -    else if (strchr(invalid_chars, c) != NULL) | 
| 1449 |  | -    { | 
| 1450 |  | -        /* Illegal char, replace */ | 
| 1451 |  | -        c = '_'; | 
| 1452 |  | -        *randomize = 1; /* as per FAT spec */ | 
| 1453 |  | -    } | 
| 1454 |  | -    else | 
| 1455 |  | -        c = toupper(c); | 
| 1456 |  | - | 
| 1457 |  | -    return c; | 
| 1458 |  | -} | 
| 1459 |  | - | 
| 1460 |  | -static void create_dos_name(const unsigned char *name, unsigned char *newname) | 
| 1461 |  | -{ | 
| 1462 |  | -    int i; | 
| 1463 |  | -    unsigned char *ext; | 
| 1464 |  | -    int randomize = 0; | 
| 1465 |  | - | 
| 1466 |  | -    /* Find extension part */ | 
| 1467 |  | -    ext = strrchr(name, '.'); | 
| 1468 |  | -    if (ext == name)         /* handle .dotnames */ | 
| 1469 |  | -        ext = NULL; | 
| 1470 |  | - | 
| 1471 |  | -    /* needs to randomize? */ | 
| 1472 |  | -    if((ext && (strlen(ext) > 4)) || | 
| 1473 |  | -       ((ext ? (unsigned int)(ext-name) : strlen(name)) > 8) ) | 
| 1474 |  | -        randomize = 1; | 
| 1475 |  | - | 
| 1476 |  | -    /* Name part */ | 
| 1477 |  | -    for (i = 0; *name && (!ext || name < ext) && (i < 8); name++) | 
| 1478 |  | -    { | 
| 1479 |  | -        unsigned char c = char2dos(*name, &randomize); | 
| 1480 |  | -        if (c) | 
| 1481 |  | -            newname[i++] = c; | 
| 1482 |  | -    } | 
| 1483 |  | - | 
| 1484 |  | -    /* Pad both name and extension */ | 
| 1485 |  | -    while (i < 11) | 
| 1486 |  | -        newname[i++] = ' '; | 
| 1487 |  | - | 
| 1488 |  | -    if (newname[0] == 0xe5) /* Special kanji character */ | 
| 1489 |  | -        newname[0] = 0x05; | 
| 1490 |  | - | 
| 1491 |  | -    if (ext) | 
| 1492 |  | -    {   /* Extension part */ | 
| 1493 |  | -        ext++; | 
| 1494 |  | -        for (i = 8; *ext && (i < 11); ext++) | 
| 1495 |  | -        { | 
| 1496 |  | -            unsigned char c = char2dos(*ext, &randomize); | 
| 1497 |  | -            if (c) | 
| 1498 |  | -                newname[i++] = c; | 
| 1499 |  | -        } | 
| 1500 |  | -    } | 
| 1501 |  | - | 
| 1502 |  | -    if(randomize) | 
| 1503 |  | -        randomize_dos_name(newname); | 
| 1504 |  | -} | 
| 1505 |  | - | 
| 1506 |  | -static void randomize_dos_name(unsigned char *name) | 
| 1507 |  | -{ | 
| 1508 |  | -    unsigned char* tilde = NULL;    /* ~ location */ | 
| 1509 |  | -    unsigned char* lastpt = NULL;   /* last point of filename */ | 
| 1510 |  | -    unsigned char* nameptr = name;  /* working copy of name pointer */ | 
| 1511 |  | -    unsigned char num[9];           /* holds number as string */ | 
| 1512 |  | -    int i = 0; | 
| 1513 |  | -    int cnt = 1; | 
| 1514 |  | -    int numlen; | 
| 1515 |  | -    int offset; | 
| 1516 |  | - | 
| 1517 |  | -    while(i++ < 8) | 
| 1518 |  | -    { | 
| 1519 |  | -        /* hunt for ~ and where to put it */ | 
| 1520 |  | -        if((!tilde) && (*nameptr == '~')) | 
| 1521 |  | -            tilde = nameptr; | 
| 1522 |  | -        if((!lastpt) && ((*nameptr == ' ' || *nameptr == '~'))) | 
| 1523 |  | -            lastpt = nameptr; | 
| 1524 |  | -        nameptr++; | 
| 1525 |  | -    } | 
| 1526 |  | -    if(tilde) | 
| 1527 |  | -    { | 
| 1528 |  | -        /* extract current count and increment */ | 
| 1529 |  | -        memcpy(num,tilde+1,7-(unsigned int)(tilde-name)); | 
| 1530 |  | -        num[7-(unsigned int)(tilde-name)] = 0; | 
| 1531 |  | -        cnt = atoi(num) + 1; | 
| 1532 |  | -    } | 
| 1533 |  | -    cnt %= 10000000; /* protection */ | 
| 1534 |  | -    snprintf(num, 9, "~%d", cnt);   /* allow room for trailing zero */ | 
| 1535 |  | -    numlen = strlen(num);           /* required space */ | 
| 1536 |  | -    offset = (unsigned int)(lastpt ? lastpt - name : 8); /* prev startpoint */ | 
| 1537 |  | -    if(offset > (8-numlen)) offset = 8-numlen;  /* correct for new numlen */ | 
| 1538 |  | - | 
| 1539 |  | -    memcpy(&name[offset], num, numlen); | 
| 1540 |  | - | 
| 1541 |  | -    /* in special case of counter overflow: pad with spaces */ | 
| 1542 |  | -    for(offset = offset+numlen; offset < 8; offset++) | 
| 1543 |  | -        name[offset] = ' '; | 
| 1544 |  | -} | 
| 1545 |  | - | 
| 1546 |  | -static int update_short_entry( struct fat_file* file, long size, int attr ) | 
| 1547 |  | -{ | 
| 1548 |  | -    int sector = file->direntry / DIR_ENTRIES_PER_SECTOR; | 
| 1549 |  | -    unsigned long* sizeptr; | 
| 1550 |  | -    unsigned short* clusptr; | 
| 1551 |  | -    struct fat_file dir; | 
| 1552 |  | -    int rc; | 
| 1553 |  | - | 
| 1554 |  | -    DEBUGF("update_file_size(cluster:%lx entry:%d size:%ld)", | 
| 1555 |  | -            file->firstcluster, file->direntry, size); | 
| 1556 |  | - | 
| 1557 |  | -    /* create a temporary file handle for the dir holding this file */ | 
| 1558 |  | -    rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL); | 
| 1559 |  | -    if (rc < 0) | 
| 1560 |  | -        return rc * 10 - 1; | 
| 1561 |  | - | 
| 1562 |  | -    rc = fat_seek( &dir, sector ); | 
| 1563 |  | -    if (rc<0) | 
| 1564 |  | -        return rc * 10 - 2; | 
| 1565 |  | - | 
| 1566 |  | -    unsigned char* buf = fat_get_sector_buffer(); | 
| 1567 |  | -    unsigned char* entry = | 
| 1568 |  | -        buf + DIR_ENTRY_SIZE * (file->direntry % DIR_ENTRIES_PER_SECTOR); | 
| 1569 |  | -    rc = fat_readwrite(&dir, 1, buf, false); | 
| 1570 |  | -    if (rc < 1) | 
| 1571 |  | -    { | 
| 1572 |  | -        fat_release_sector_buffer(); | 
| 1573 |  | -        return rc * 10 - 3; | 
| 1574 |  | -    } | 
| 1575 |  | - | 
| 1576 |  | -    if (!entry[0] || entry[0] == 0xe5) | 
| 1577 |  | -    { | 
| 1578 |  | -        fat_release_sector_buffer(); | 
| 1579 |  | -        panicf(PANIC_KILLUSERTHREADS, "Updating size on empty dir entry %d", file->direntry); | 
| 1580 |  | -    } | 
| 1581 |  | - | 
| 1582 |  | -    entry[FATDIR_ATTR] = attr & 0xFF; | 
| 1583 |  | - | 
| 1584 |  | -    clusptr = (short*)(entry + FATDIR_FSTCLUSHI); | 
| 1585 |  | -    *clusptr = htole16(file->firstcluster >> 16); | 
| 1586 |  | - | 
| 1587 |  | -    clusptr = (short*)(entry + FATDIR_FSTCLUSLO); | 
| 1588 |  | -    *clusptr = htole16(file->firstcluster & 0xffff); | 
| 1589 |  | - | 
| 1590 |  | -    sizeptr = (long*)(entry + FATDIR_FILESIZE); | 
| 1591 |  | -    *sizeptr = htole32(size); | 
| 1592 |  | - | 
| 1593 |  | -    { | 
| 1594 |  | -#if CONFIG_RTC | 
| 1595 |  | -        unsigned short time = 0; | 
| 1596 |  | -        unsigned short date = 0; | 
| 1597 |  | -#else | 
| 1598 |  | -        /* get old time to increment from */ | 
| 1599 |  | -        unsigned short time = htole16(*(unsigned short*)(entry+FATDIR_WRTTIME)); | 
| 1600 |  | -        unsigned short date = htole16(*(unsigned short*)(entry+FATDIR_WRTDATE)); | 
| 1601 |  | -#endif | 
| 1602 |  | -        fat_time(&date, &time, NULL); | 
| 1603 |  | -        *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time); | 
| 1604 |  | -        *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date); | 
| 1605 |  | -        *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date); | 
| 1606 |  | -    } | 
| 1607 |  | - | 
| 1608 |  | -    rc = fat_seek( &dir, sector ); | 
| 1609 |  | -    if (rc < 0) | 
| 1610 |  | -    { | 
| 1611 |  | -        fat_release_sector_buffer(); | 
| 1612 |  | -        return rc * 10 - 4; | 
| 1613 |  | -    } | 
| 1614 |  | - | 
| 1615 |  | -    rc = fat_readwrite(&dir, 1, buf, true); | 
| 1616 |  | -    fat_release_sector_buffer(); | 
| 1617 |  | -    if (rc < 1) | 
| 1618 |  | -        return rc * 10 - 5; | 
| 1619 |  | - | 
| 1620 |  | -    return 0; | 
| 1621 |  | -} | 
| 1622 |  | - | 
| 1623 |  | -static int parse_direntry(struct fat_direntry *de, const unsigned char *buf) | 
| 1624 |  | -{ | 
| 1625 |  | -    int i=0,j=0; | 
| 1626 |  | -    unsigned char c; | 
| 1627 |  | -    bool lowercase; | 
| 1628 |  | - | 
| 1629 |  | -    memset(de, 0, sizeof(struct fat_direntry)); | 
| 1630 |  | -    de->attr = buf[FATDIR_ATTR]; | 
| 1631 |  | -    de->crttimetenth = buf[FATDIR_CRTTIMETENTH]; | 
| 1632 |  | -    de->crtdate = BYTES2INT16(buf,FATDIR_CRTDATE); | 
| 1633 |  | -    de->crttime = BYTES2INT16(buf,FATDIR_CRTTIME); | 
| 1634 |  | -    de->wrtdate = BYTES2INT16(buf,FATDIR_WRTDATE); | 
| 1635 |  | -    de->wrttime = BYTES2INT16(buf,FATDIR_WRTTIME); | 
| 1636 |  | -    de->filesize = BYTES2INT32(buf,FATDIR_FILESIZE); | 
| 1637 |  | -    de->firstcluster = ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSLO)) | | 
| 1638 |  | -        ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSHI) << 16); | 
| 1639 |  | -    /* The double cast is to prevent a sign-extension to be done on CalmRISC16. | 
| 1640 |  | -       (the result of the shift is always considered signed) */ | 
| 1641 |  | - | 
| 1642 |  | -    /* fix the name */ | 
| 1643 |  | -    lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_NAME); | 
| 1644 |  | -    c = buf[FATDIR_NAME]; | 
| 1645 |  | -    if (c == 0x05)  /* special kanji char */ | 
| 1646 |  | -        c = 0xe5; | 
| 1647 |  | -    i = 0; | 
| 1648 |  | -    while (c != ' ') { | 
| 1649 |  | -        de->name[j++] = lowercase ? tolower(c) : c; | 
| 1650 |  | -        if (++i >= 8) | 
| 1651 |  | -            break; | 
| 1652 |  | -        c = buf[FATDIR_NAME+i]; | 
| 1653 |  | -    } | 
| 1654 |  | -    if (buf[FATDIR_NAME+8] != ' ') { | 
| 1655 |  | -        lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_EXT); | 
| 1656 |  | -        de->name[j++] = '.'; | 
| 1657 |  | -        for (i = 8; (i < 11) && ((c = buf[FATDIR_NAME+i]) != ' '); i++) | 
| 1658 |  | -            de->name[j++] = lowercase ? tolower(c) : c; | 
| 1659 |  | -    } | 
| 1660 |  | -    return 1; | 
| 1661 |  | -} | 
| 1662 |  | - | 
| 1663 |  | -int fat_open(IF_MV2(int volume,) | 
| 1664 |  | -             long startcluster, | 
| 1665 |  | -             struct fat_file *file, | 
| 1666 |  | -             const struct fat_dir* dir) | 
| 1667 |  | -{ | 
| 1668 |  | -    /* Remember where the file's dir entry is located | 
| 1669 |  | -     * Do it before assigning other fields so that fat_open | 
| 1670 |  | -     * can be called with file == &dir->file (see fat_opendir) */ | 
| 1671 |  | -    if ( dir ) { | 
| 1672 |  | -        file->direntry = dir->entry - 1; | 
| 1673 |  | -        file->direntries = dir->entrycount; | 
| 1674 |  | -        file->dircluster = dir->file.firstcluster; | 
| 1675 |  | -    } | 
| 1676 |  | -     | 
| 1677 |  | -    file->firstcluster = startcluster; | 
| 1678 |  | -    file->lastcluster = startcluster; | 
| 1679 |  | -    file->lastsector = 0; | 
| 1680 |  | -    file->clusternum = 0; | 
| 1681 |  | -    file->sectornum = 0; | 
| 1682 |  | -    file->eof = false; | 
| 1683 |  | -#ifdef HAVE_MULTIVOLUME | 
| 1684 |  | -    file->volume = volume; | 
| 1685 |  | -    /* fixme: remove error check when done */ | 
| 1686 |  | -    if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted) | 
| 1687 |  | -    { | 
| 1688 |  | -        DEBUGF("fat_open() illegal volume %d", volume); | 
| 1689 |  | -        return -1; | 
| 1690 |  | -    } | 
| 1691 |  | -#endif | 
| 1692 |  | - | 
| 1693 |  | -    DEBUGF("fat_open(%lx), entry %d",startcluster,file->direntry); | 
| 1694 |  | -    return 0; | 
| 1695 |  | -} | 
| 1696 |  | - | 
| 1697 |  | -int fat_create_file(const char* name, | 
| 1698 |  | -                    struct fat_file* file, | 
| 1699 |  | -                    struct fat_dir* dir) | 
| 1700 |  | -{ | 
| 1701 |  | -    int rc; | 
| 1702 |  | - | 
| 1703 |  | -    DEBUGF("fat_create_file(\"%s\",%lx,%lx)",name,(long)file,(long)dir); | 
| 1704 |  | -    rc = add_dir_entry(dir, file, name, false, false); | 
| 1705 |  | -    if (!rc) { | 
| 1706 |  | -        file->firstcluster = 0; | 
| 1707 |  | -        file->lastcluster = 0; | 
| 1708 |  | -        file->lastsector = 0; | 
| 1709 |  | -        file->clusternum = 0; | 
| 1710 |  | -        file->sectornum = 0; | 
| 1711 |  | -        file->eof = false; | 
| 1712 |  | -    } | 
| 1713 |  | - | 
| 1714 |  | -    return rc; | 
| 1715 |  | -} | 
| 1716 |  | - | 
| 1717 |  | -int fat_create_dir(const char* name, | 
| 1718 |  | -                   struct fat_dir* newdir, | 
| 1719 |  | -                   struct fat_dir* dir) | 
| 1720 |  | -{ | 
| 1721 |  | -#ifdef HAVE_MULTIVOLUME | 
| 1722 |  | -    struct bpb* fat_bpb = &fat_bpbs[dir->file.volume]; | 
| 1723 |  | -#else | 
| 1724 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 1725 |  | -#endif | 
| 1726 |  | -    int i; | 
| 1727 |  | -    long sector; | 
| 1728 |  | -    int rc; | 
| 1729 |  | -    struct fat_file dummyfile; | 
| 1730 |  | - | 
| 1731 |  | -    DEBUGF("fat_create_dir(\"%s\",%lx,%lx)",name,(long)newdir,(long)dir); | 
| 1732 |  | - | 
| 1733 |  | -    memset(newdir, 0, sizeof(struct fat_dir)); | 
| 1734 |  | -    memset(&dummyfile, 0, sizeof(struct fat_file)); | 
| 1735 |  | - | 
| 1736 |  | -    /* First, add the entry in the parent directory */ | 
| 1737 |  | -    rc = add_dir_entry(dir, &newdir->file, name, true, false); | 
| 1738 |  | -    if (rc < 0) | 
| 1739 |  | -        return rc * 10 - 1; | 
| 1740 |  | - | 
| 1741 |  | -    /* Allocate a new cluster for the directory */ | 
| 1742 |  | -    newdir->file.firstcluster = find_free_cluster(IF_MV2(fat_bpb,) | 
| 1743 |  | -                                                  fat_bpb->fsinfo.nextfree); | 
| 1744 |  | -    if(newdir->file.firstcluster == 0) | 
| 1745 |  | -        return -1; | 
| 1746 |  | - | 
| 1747 |  | -    update_fat_entry(IF_MV2(fat_bpb,) newdir->file.firstcluster, FAT_EOF_MARK); | 
| 1748 |  | - | 
| 1749 |  | -    /* Clear the entire cluster */ | 
| 1750 |  | -    unsigned char* buf = fat_get_sector_buffer(); | 
| 1751 |  | -    memset(buf, 0, SECTOR_SIZE); | 
| 1752 |  | -    sector = cluster2sec(IF_MV2(fat_bpb,) newdir->file.firstcluster); | 
| 1753 |  | -    for(i = 0;i < (int)fat_bpb->bpb_secperclus;i++) { | 
| 1754 |  | -        rc = transfer(IF_MV2(fat_bpb,) sector + i, 1, buf, true ); | 
| 1755 |  | -        if (rc < 0) | 
| 1756 |  | -        { | 
| 1757 |  | -            fat_release_sector_buffer(); | 
| 1758 |  | -            return rc * 10 - 2; | 
| 1759 |  | -        } | 
| 1760 |  | -    } | 
| 1761 |  | -    fat_release_sector_buffer(); | 
| 1762 |  | - | 
| 1763 |  | -    /* Then add the "." entry */ | 
| 1764 |  | -    rc = add_dir_entry(newdir, &dummyfile, ".", true, true); | 
| 1765 |  | -    if (rc < 0) | 
| 1766 |  | -        return rc * 10 - 3; | 
| 1767 |  | -    dummyfile.firstcluster = newdir->file.firstcluster; | 
| 1768 |  | -    update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY); | 
| 1769 |  | - | 
| 1770 |  | -    /* and the ".." entry */ | 
| 1771 |  | -    rc = add_dir_entry(newdir, &dummyfile, "..", true, true); | 
| 1772 |  | -    if (rc < 0) | 
| 1773 |  | -        return rc * 10 - 4; | 
| 1774 |  | - | 
| 1775 |  | -    /* The root cluster is cluster 0 in the ".." entry */ | 
| 1776 |  | -    if(dir->file.firstcluster == fat_bpb->bpb_rootclus) | 
| 1777 |  | -        dummyfile.firstcluster = 0; | 
| 1778 |  | -    else | 
| 1779 |  | -        dummyfile.firstcluster = dir->file.firstcluster; | 
| 1780 |  | -    update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY); | 
| 1781 |  | - | 
| 1782 |  | -    /* Set the firstcluster field in the direntry */ | 
| 1783 |  | -    update_short_entry(&newdir->file, 0, FAT_ATTR_DIRECTORY); | 
| 1784 |  | - | 
| 1785 |  | -    rc = flush_fat(IF_MV(fat_bpb)); | 
| 1786 |  | -    if (rc < 0) | 
| 1787 |  | -        return rc * 10 - 5; | 
| 1788 |  | - | 
| 1789 |  | -    return rc; | 
| 1790 |  | -} | 
| 1791 |  | - | 
| 1792 |  | -int fat_truncate(const struct fat_file *file) | 
| 1793 |  | -{ | 
| 1794 |  | -    /* truncate trailing clusters */ | 
| 1795 |  | -    long next; | 
| 1796 |  | -    long last = file->lastcluster; | 
| 1797 |  | -#ifdef HAVE_MULTIVOLUME | 
| 1798 |  | -    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
| 1799 |  | -#endif | 
| 1800 |  | - | 
| 1801 |  | -    DEBUGF("fat_truncate(%lx, %lx)", file->firstcluster, last); | 
| 1802 |  | - | 
| 1803 |  | -    for ( last = get_next_cluster(IF_MV2(fat_bpb,) last); last; last = next ) { | 
| 1804 |  | -        next = get_next_cluster(IF_MV2(fat_bpb,) last); | 
| 1805 |  | -        update_fat_entry(IF_MV2(fat_bpb,) last,0); | 
| 1806 |  | -    } | 
| 1807 |  | -    if (file->lastcluster) | 
| 1808 |  | -        update_fat_entry(IF_MV2(fat_bpb,) file->lastcluster,FAT_EOF_MARK); | 
| 1809 |  | - | 
| 1810 |  | -    return 0; | 
| 1811 |  | -} | 
| 1812 |  | - | 
| 1813 |  | -int fat_closewrite(struct fat_file *file, long size, int attr) | 
| 1814 |  | -{ | 
| 1815 |  | -    int rc; | 
| 1816 |  | -#ifdef HAVE_MULTIVOLUME | 
| 1817 |  | -    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
| 1818 |  | -#endif | 
| 1819 |  | -    DEBUGF("fat_closewrite(size=%ld)",size); | 
| 1820 |  | - | 
| 1821 |  | -    if (!size) { | 
| 1822 |  | -        /* empty file */ | 
| 1823 |  | -        if ( file->firstcluster ) { | 
| 1824 |  | -            update_fat_entry(IF_MV2(fat_bpb,) file->firstcluster, 0); | 
| 1825 |  | -            file->firstcluster = 0; | 
| 1826 |  | -        } | 
| 1827 |  | -    } | 
| 1828 |  | - | 
| 1829 |  | -    if (file->dircluster) { | 
| 1830 |  | -        rc = update_short_entry(file, size, attr); | 
| 1831 |  | -        if (rc < 0) | 
| 1832 |  | -            return rc * 10 - 1; | 
| 1833 |  | -    } | 
| 1834 |  | - | 
| 1835 |  | -    flush_fat(IF_MV(fat_bpb)); | 
| 1836 |  | - | 
| 1837 |  | -#ifdef TEST_FAT | 
| 1838 |  | -    if ( file->firstcluster ) { | 
| 1839 |  | -        /* debug */ | 
| 1840 |  | -#ifdef HAVE_MULTIVOLUME | 
| 1841 |  | -        struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
| 1842 |  | -#else | 
| 1843 |  | -        struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 1844 |  | -#endif | 
| 1845 |  | -        long count = 0; | 
| 1846 |  | -        long len; | 
| 1847 |  | -        long next; | 
| 1848 |  | -        for ( next = file->firstcluster; next; | 
| 1849 |  | -              next = get_next_cluster(IF_MV2(fat_bpb,) next) ) { | 
| 1850 |  | -            DEBUGF("cluster %ld: %lx", count, next); | 
| 1851 |  | -            count++; | 
| 1852 |  | -        } | 
| 1853 |  | -        len = count * fat_bpb->bpb_secperclus * SECTOR_SIZE; | 
| 1854 |  | -        DEBUGF("File is %ld clusters (chainlen=%ld, size=%ld)", | 
| 1855 |  | -                count, len, size ); | 
| 1856 |  | -        if ( len > size + fat_bpb->bpb_secperclus * SECTOR_SIZE) | 
| 1857 |  | -            panicf(PANIC_KILLUSERTHREADS, "Cluster chain is too long"); | 
| 1858 |  | -        if ( len < size ) | 
| 1859 |  | -            panicf(PANIC_KILLUSERTHREADS, "Cluster chain is too short"); | 
| 1860 |  | -    } | 
| 1861 |  | -#endif | 
| 1862 |  | - | 
| 1863 |  | -    return 0; | 
| 1864 |  | -} | 
| 1865 |  | - | 
| 1866 |  | -static int free_direntries(struct fat_file* file) | 
| 1867 |  | -{ | 
| 1868 |  | -    struct fat_file dir; | 
| 1869 |  | -    int numentries = file->direntries; | 
| 1870 |  | -    unsigned int entry = file->direntry - numentries + 1; | 
| 1871 |  | -    unsigned int sector = entry / DIR_ENTRIES_PER_SECTOR; | 
| 1872 |  | -    int i; | 
| 1873 |  | -    int rc; | 
| 1874 |  | - | 
| 1875 |  | -    /* create a temporary file handle for the dir holding this file */ | 
| 1876 |  | -    rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL); | 
| 1877 |  | -    if (rc < 0) | 
| 1878 |  | -        return rc * 10 - 1; | 
| 1879 |  | - | 
| 1880 |  | -    rc = fat_seek( &dir, sector ); | 
| 1881 |  | -    if (rc < 0) | 
| 1882 |  | -        return rc * 10 - 2; | 
| 1883 |  | - | 
| 1884 |  | -    unsigned char* buf = fat_get_sector_buffer(); | 
| 1885 |  | -    rc = fat_readwrite(&dir, 1, buf, false); | 
| 1886 |  | -    if (rc < 1) | 
| 1887 |  | -    { | 
| 1888 |  | -        fat_release_sector_buffer(); | 
| 1889 |  | -        return rc * 10 - 3; | 
| 1890 |  | -    } | 
| 1891 |  | - | 
| 1892 |  | -    for (i=0; i < numentries; i++) { | 
| 1893 |  | -        DEBUGF("Clearing dir entry %d (%d/%d)", | 
| 1894 |  | -                entry, i+1, numentries); | 
| 1895 |  | -        buf[(entry % DIR_ENTRIES_PER_SECTOR) * DIR_ENTRY_SIZE] = 0xe5; | 
| 1896 |  | -        entry++; | 
| 1897 |  | - | 
| 1898 |  | -        if ( (entry % DIR_ENTRIES_PER_SECTOR) == 0 ) { | 
| 1899 |  | -            /* flush this sector */ | 
| 1900 |  | -            rc = fat_seek(&dir, sector); | 
| 1901 |  | -            if (rc < 0) | 
| 1902 |  | -            { | 
| 1903 |  | -                fat_release_sector_buffer(); | 
| 1904 |  | -                return rc * 10 - 4; | 
| 1905 |  | -            } | 
| 1906 |  | - | 
| 1907 |  | -            rc = fat_readwrite(&dir, 1, buf, true); | 
| 1908 |  | -            if (rc < 1) | 
| 1909 |  | -            { | 
| 1910 |  | -                fat_release_sector_buffer(); | 
| 1911 |  | -                return rc * 10 - 5; | 
| 1912 |  | -            } | 
| 1913 |  | - | 
| 1914 |  | -            if ( i+1 < numentries ) { | 
| 1915 |  | -                /* read next sector */ | 
| 1916 |  | -                rc = fat_readwrite(&dir, 1, buf, false); | 
| 1917 |  | -                if (rc < 1) | 
| 1918 |  | -                { | 
| 1919 |  | -                    fat_release_sector_buffer(); | 
| 1920 |  | -                    return rc * 10 - 6; | 
| 1921 |  | -                } | 
| 1922 |  | -            } | 
| 1923 |  | -            sector++; | 
| 1924 |  | -        } | 
| 1925 |  | -    } | 
| 1926 |  | - | 
| 1927 |  | -    if ( entry % DIR_ENTRIES_PER_SECTOR ) { | 
| 1928 |  | -        /* flush this sector */ | 
| 1929 |  | -        rc = fat_seek(&dir, sector); | 
| 1930 |  | -        if (rc < 0) | 
| 1931 |  | -        { | 
| 1932 |  | -            fat_release_sector_buffer(); | 
| 1933 |  | -            return rc * 10 - 7; | 
| 1934 |  | -        } | 
| 1935 |  | - | 
| 1936 |  | -        rc = fat_readwrite(&dir, 1, buf, true); | 
| 1937 |  | -        if (rc < 1) | 
| 1938 |  | -        { | 
| 1939 |  | -            fat_release_sector_buffer(); | 
| 1940 |  | -            return rc * 10 - 8; | 
| 1941 |  | -        } | 
| 1942 |  | -    } | 
| 1943 |  | - | 
| 1944 |  | -    fat_release_sector_buffer(); | 
| 1945 |  | -    return 0; | 
| 1946 |  | -} | 
| 1947 |  | - | 
| 1948 |  | -int fat_remove(struct fat_file* file) | 
| 1949 |  | -{ | 
| 1950 |  | -    long next, last = file->firstcluster; | 
| 1951 |  | -    int rc; | 
| 1952 |  | -#ifdef HAVE_MULTIVOLUME | 
| 1953 |  | -    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
| 1954 |  | -#endif | 
| 1955 |  | - | 
| 1956 |  | -    DEBUGF("fat_remove(%lx)",last); | 
| 1957 |  | - | 
| 1958 |  | -    while ( last ) { | 
| 1959 |  | -        next = get_next_cluster(IF_MV2(fat_bpb,) last); | 
| 1960 |  | -        update_fat_entry(IF_MV2(fat_bpb,) last,0); | 
| 1961 |  | -        last = next; | 
| 1962 |  | -    } | 
| 1963 |  | - | 
| 1964 |  | -    if ( file->dircluster ) { | 
| 1965 |  | -        rc = free_direntries(file); | 
| 1966 |  | -        if (rc < 0) | 
| 1967 |  | -            return rc * 10 - 1; | 
| 1968 |  | -    } | 
| 1969 |  | - | 
| 1970 |  | -    file->firstcluster = 0; | 
| 1971 |  | -    file->dircluster = 0; | 
| 1972 |  | - | 
| 1973 |  | -    rc = flush_fat(IF_MV(fat_bpb)); | 
| 1974 |  | -    if (rc < 0) | 
| 1975 |  | -        return rc * 10 - 2; | 
| 1976 |  | - | 
| 1977 |  | -    return 0; | 
| 1978 |  | -} | 
| 1979 |  | - | 
| 1980 |  | -int fat_rename(struct fat_file* file,  | 
| 1981 |  | -                struct fat_dir* dir,  | 
| 1982 |  | -                const unsigned char* newname, | 
| 1983 |  | -                long size, | 
| 1984 |  | -                int attr) | 
| 1985 |  | -{ | 
| 1986 |  | -    int rc; | 
| 1987 |  | -    struct fat_dir olddir; | 
| 1988 |  | -    struct fat_file newfile = *file; | 
| 1989 |  | -    unsigned char* entry = NULL; | 
| 1990 |  | -    unsigned short* clusptr = NULL; | 
| 1991 |  | -    unsigned int parentcluster; | 
| 1992 |  | -#ifdef HAVE_MULTIVOLUME | 
| 1993 |  | -    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
| 1994 |  | - | 
| 1995 |  | -    if (file->volume != dir->file.volume) { | 
| 1996 |  | -        DEBUGF("No rename across volumes!"); | 
| 1997 |  | -        return -1; | 
| 1998 |  | -    } | 
| 1999 |  | -#else | 
| 2000 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 2001 |  | -#endif | 
| 2002 |  | - | 
| 2003 |  | -    if ( !file->dircluster ) { | 
| 2004 |  | -        DEBUGF("File has no dir cluster!"); | 
| 2005 |  | -        return -2; | 
| 2006 |  | -    } | 
| 2007 |  | - | 
| 2008 |  | -    /* create a temporary file handle */ | 
| 2009 |  | -    rc = fat_opendir(IF_MV2(file->volume,) &olddir, file->dircluster, NULL); | 
| 2010 |  | -    if (rc < 0) | 
| 2011 |  | -        return rc * 10 - 1; | 
| 2012 |  | - | 
| 2013 |  | -    /* create new name */ | 
| 2014 |  | -    rc = add_dir_entry(dir, &newfile, newname, false, false); | 
| 2015 |  | -    if (rc < 0) | 
| 2016 |  | -        return rc * 10 - 2; | 
| 2017 |  | - | 
| 2018 |  | -    /* write size and cluster link */ | 
| 2019 |  | -    rc = update_short_entry(&newfile, size, attr); | 
| 2020 |  | -    if (rc < 0) | 
| 2021 |  | -        return rc * 10 - 3; | 
| 2022 |  | - | 
| 2023 |  | -    /* remove old name */ | 
| 2024 |  | -    rc = free_direntries(file); | 
| 2025 |  | -    if (rc < 0) | 
| 2026 |  | -        return rc * 10 - 4; | 
| 2027 |  | - | 
| 2028 |  | -    rc = flush_fat(IF_MV(fat_bpb)); | 
| 2029 |  | -    if (rc < 0) | 
| 2030 |  | -        return rc * 10 - 5; | 
| 2031 |  | - | 
| 2032 |  | -    /* if renaming a directory, update the .. entry to make sure | 
| 2033 |  | -       it points to its parent directory (we don't check if it was a move) */ | 
| 2034 |  | -    if(FAT_ATTR_DIRECTORY == attr) { | 
| 2035 |  | -        /* open the dir that was renamed, we re-use the olddir struct */ | 
| 2036 |  | -        rc = fat_opendir(IF_MV2(file->volume,) &olddir, newfile.firstcluster, | 
| 2037 |  | -                                                                          NULL); | 
| 2038 |  | -        if (rc < 0) | 
| 2039 |  | -            return rc * 10 - 6; | 
| 2040 |  | - | 
| 2041 |  | -        /* get the first sector of the dir */ | 
| 2042 |  | -        rc = fat_seek(&olddir.file, 0); | 
| 2043 |  | -        if (rc < 0) | 
| 2044 |  | -            return rc * 10 - 7; | 
| 2045 |  | - | 
| 2046 |  | -        unsigned char* buf = fat_get_sector_buffer(); | 
| 2047 |  | -        rc = fat_readwrite(&olddir.file, 1, buf, false); | 
| 2048 |  | -        if (rc < 0) | 
| 2049 |  | -        { | 
| 2050 |  | -            fat_release_sector_buffer(); | 
| 2051 |  | -            return rc * 10 - 8; | 
| 2052 |  | -        } | 
| 2053 |  | - | 
| 2054 |  | -        /* parent cluster is 0 if parent dir is the root - FAT spec (p.29) */ | 
| 2055 |  | -        if(dir->file.firstcluster == fat_bpb->bpb_rootclus) | 
| 2056 |  | -            parentcluster = 0; | 
| 2057 |  | -        else | 
| 2058 |  | -            parentcluster = dir->file.firstcluster; | 
| 2059 |  | - | 
| 2060 |  | -        entry = buf + DIR_ENTRY_SIZE; | 
| 2061 |  | -        if(strncmp("..         ", entry, 11)) | 
| 2062 |  | -        { | 
| 2063 |  | -            fat_release_sector_buffer(); | 
| 2064 |  | -            /* .. entry must be second entry according to FAT spec (p.29) */ | 
| 2065 |  | -            DEBUGF("Second dir entry is not double-dot!"); | 
| 2066 |  | -            return rc * 10 - 9; | 
| 2067 |  | -        } | 
| 2068 |  | -        clusptr = (short*)(entry + FATDIR_FSTCLUSHI); | 
| 2069 |  | -        *clusptr = htole16(parentcluster >> 16); | 
| 2070 |  | - | 
| 2071 |  | -        clusptr = (short*)(entry + FATDIR_FSTCLUSLO); | 
| 2072 |  | -        *clusptr = htole16(parentcluster & 0xffff); | 
| 2073 |  | - | 
| 2074 |  | -        /* write back this sector */ | 
| 2075 |  | -        rc = fat_seek(&olddir.file, 0); | 
| 2076 |  | -        if (rc < 0) | 
| 2077 |  | -        { | 
| 2078 |  | -            fat_release_sector_buffer(); | 
| 2079 |  | -            return rc * 10 - 7; | 
| 2080 |  | -        } | 
| 2081 |  | - | 
| 2082 |  | -        rc = fat_readwrite(&olddir.file, 1, buf, true); | 
| 2083 |  | -        fat_release_sector_buffer(); | 
| 2084 |  | -        if (rc < 1) | 
| 2085 |  | -            return rc * 10 - 8; | 
| 2086 |  | -    } | 
| 2087 |  | - | 
| 2088 |  | -    return 0; | 
| 2089 |  | -} | 
| 2090 |  | - | 
| 2091 |  | -static long next_write_cluster(struct fat_file* file, | 
| 2092 |  | -                              long oldcluster, | 
| 2093 |  | -                              long* newsector) | 
| 2094 |  | -{ | 
| 2095 |  | -#ifdef HAVE_MULTIVOLUME | 
| 2096 |  | -    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
| 2097 |  | -#else | 
| 2098 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 2099 |  | -#endif | 
| 2100 |  | -    long cluster = 0; | 
| 2101 |  | -    long sector; | 
| 2102 |  | - | 
| 2103 |  | -    DEBUGF("next_write_cluster(%lx,%lx)",file->firstcluster, oldcluster); | 
| 2104 |  | - | 
| 2105 |  | -    if (oldcluster) | 
| 2106 |  | -        cluster = get_next_cluster(IF_MV2(fat_bpb,) oldcluster); | 
| 2107 |  | - | 
| 2108 |  | -    if (!cluster) { | 
| 2109 |  | -        if (oldcluster > 0) | 
| 2110 |  | -            cluster = find_free_cluster(IF_MV2(fat_bpb,) oldcluster+1); | 
| 2111 |  | -        else if (oldcluster == 0) | 
| 2112 |  | -            cluster = find_free_cluster(IF_MV2(fat_bpb,) | 
| 2113 |  | -                                        fat_bpb->fsinfo.nextfree); | 
| 2114 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 2115 |  | -        else /* negative, pseudo-cluster of the root dir */ | 
| 2116 |  | -            return 0; /* impossible to append something to the root */ | 
| 2117 |  | -#endif | 
| 2118 |  | - | 
| 2119 |  | -        if (cluster) { | 
| 2120 |  | -            if (oldcluster) | 
| 2121 |  | -                update_fat_entry(IF_MV2(fat_bpb,) oldcluster, cluster); | 
| 2122 |  | -            else | 
| 2123 |  | -                file->firstcluster = cluster; | 
| 2124 |  | -            update_fat_entry(IF_MV2(fat_bpb,) cluster, FAT_EOF_MARK); | 
| 2125 |  | -        } | 
| 2126 |  | -        else { | 
| 2127 |  | -#ifdef TEST_FAT | 
| 2128 |  | -            if (fat_bpb->fsinfo.freecount>0) | 
| 2129 |  | -                panicf(PANIC_KILLUSERTHREADS, "There is free space, but find_free_cluster() " | 
| 2130 |  | -                       "didn't find it!"); | 
| 2131 |  | -#endif | 
| 2132 |  | -            DEBUGF("next_write_cluster(): Disk full!"); | 
| 2133 |  | -            return 0; | 
| 2134 |  | -        } | 
| 2135 |  | -    } | 
| 2136 |  | -    sector = cluster2sec(IF_MV2(fat_bpb,) cluster); | 
| 2137 |  | -    if (sector<0) | 
| 2138 |  | -        return 0; | 
| 2139 |  | - | 
| 2140 |  | -    *newsector = sector; | 
| 2141 |  | -    return cluster; | 
| 2142 |  | -} | 
| 2143 |  | - | 
| 2144 |  | -static int transfer(IF_MV2(struct bpb* fat_bpb,)  | 
| 2145 |  | -                    unsigned long start, long count, char* buf, bool write ) | 
| 2146 |  | -{ | 
| 2147 |  | -#ifndef HAVE_MULTIVOLUME | 
| 2148 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 2149 |  | -#endif | 
| 2150 |  | -    int rc; | 
| 2151 |  | - | 
| 2152 |  | -    DEBUGF("transfer(s=%lx, c=%lx, %s)", | 
| 2153 |  | -        start+ fat_bpb->startsector, count, write?"write":"read"); | 
| 2154 |  | -    if (write) { | 
| 2155 |  | -        unsigned long firstallowed; | 
| 2156 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 2157 |  | -        if (fat_bpb->is_fat16) | 
| 2158 |  | -            firstallowed = fat_bpb->rootdirsector; | 
| 2159 |  | -        else | 
| 2160 |  | -#endif | 
| 2161 |  | -            firstallowed = fat_bpb->firstdatasector; | 
| 2162 |  | - | 
| 2163 |  | -        if (start < firstallowed) | 
| 2164 |  | -            panicf(PANIC_KILLUSERTHREADS, "Write %ld before data", firstallowed - start); | 
| 2165 |  | -        if (start + count > fat_bpb->totalsectors) | 
| 2166 |  | -            panicf(PANIC_KILLUSERTHREADS, "Write %ld after data", | 
| 2167 |  | -                start + count - fat_bpb->totalsectors); | 
| 2168 |  | -        rc = storage_write_sectors(IF_MD2(fat_bpb->drive,) | 
| 2169 |  | -                               start + fat_bpb->startsector, count, buf); | 
| 2170 |  | -    } | 
| 2171 |  | -    else | 
| 2172 |  | -        rc = storage_read_sectors(IF_MD2(fat_bpb->drive,) | 
| 2173 |  | -                              start + fat_bpb->startsector, count, buf); | 
| 2174 |  | -    if (rc < 0) { | 
| 2175 |  | -        DEBUGF( "transfer() - Couldn't %s sector %lx" | 
| 2176 |  | -                " (error code %d)",  | 
| 2177 |  | -                write ? "write":"read", start, rc); | 
| 2178 |  | -        return rc; | 
| 2179 |  | -    } | 
| 2180 |  | -    return 0; | 
| 2181 |  | -} | 
| 2182 |  | - | 
| 2183 |  | - | 
| 2184 |  | -long fat_readwrite( struct fat_file *file, long sectorcount, | 
| 2185 |  | -                   void* buf, bool write ) | 
| 2186 |  | -{ | 
| 2187 |  | -#ifdef HAVE_MULTIVOLUME | 
| 2188 |  | -    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
| 2189 |  | -#else | 
| 2190 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 2191 |  | -#endif | 
| 2192 |  | -    long cluster = file->lastcluster; | 
| 2193 |  | -    long sector = file->lastsector; | 
| 2194 |  | -    long clusternum = file->clusternum; | 
| 2195 |  | -    long numsec = file->sectornum; | 
| 2196 |  | -    bool eof = file->eof; | 
| 2197 |  | -    long first=0, last=0; | 
| 2198 |  | -    long i; | 
| 2199 |  | -    int rc; | 
| 2200 |  | - | 
| 2201 |  | -    DEBUGF( "fat_readwrite(file:%lx,count:0x%lx,buf:%lx,%s)", | 
| 2202 |  | -             file->firstcluster,sectorcount,(long)buf,write?"write":"read"); | 
| 2203 |  | -    DEBUGF( "fat_readwrite: sec=%lx numsec=%ld eof=%d", | 
| 2204 |  | -             sector,numsec, eof?1:0); | 
| 2205 |  | - | 
| 2206 |  | -    if ( eof && !write) | 
| 2207 |  | -        return 0; | 
| 2208 |  | - | 
| 2209 |  | -    /* find sequential sectors and write them all at once */ | 
| 2210 |  | -    for (i=0; (i < sectorcount) && (sector > -1); i++ ) { | 
| 2211 |  | -        numsec++; | 
| 2212 |  | -        if ( numsec > (long)fat_bpb->bpb_secperclus || !cluster ) { | 
| 2213 |  | -            long oldcluster = cluster; | 
| 2214 |  | -            long oldsector = sector; | 
| 2215 |  | -            long oldnumsec = numsec; | 
| 2216 |  | -            if (write) | 
| 2217 |  | -                cluster = next_write_cluster(file, cluster, §or); | 
| 2218 |  | -            else { | 
| 2219 |  | -                cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster); | 
| 2220 |  | -                sector = cluster2sec(IF_MV2(fat_bpb,) cluster); | 
| 2221 |  | -            } | 
| 2222 |  | - | 
| 2223 |  | -            clusternum++; | 
| 2224 |  | -            numsec=1; | 
| 2225 |  | - | 
| 2226 |  | -            if (!cluster) { | 
| 2227 |  | -                eof = true; | 
| 2228 |  | -                if ( write ) { | 
| 2229 |  | -                    /* remember last cluster, in case | 
| 2230 |  | -                       we want to append to the file */ | 
| 2231 |  | -                    sector = oldsector; | 
| 2232 |  | -                    cluster = oldcluster; | 
| 2233 |  | -                    numsec = oldnumsec; | 
| 2234 |  | -                    clusternum--; | 
| 2235 |  | -                    i = -1; /* Error code */ | 
| 2236 |  | -                    break; | 
| 2237 |  | -                } | 
| 2238 |  | -            } | 
| 2239 |  | -            else | 
| 2240 |  | -                eof = false; | 
| 2241 |  | -        } | 
| 2242 |  | -        else { | 
| 2243 |  | -            if (sector) | 
| 2244 |  | -                sector++; | 
| 2245 |  | -            else { | 
| 2246 |  | -                /* look up first sector of file */ | 
| 2247 |  | -                sector = cluster2sec(IF_MV2(fat_bpb,) file->firstcluster); | 
| 2248 |  | -                numsec=1; | 
| 2249 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 2250 |  | -                if (file->firstcluster < 0) | 
| 2251 |  | -                {   /* FAT16 root dir */ | 
| 2252 |  | -                    sector += fat_bpb->rootdiroffset; | 
| 2253 |  | -                    numsec += fat_bpb->rootdiroffset; | 
| 2254 |  | -                } | 
| 2255 |  | -#endif | 
| 2256 |  | -            } | 
| 2257 |  | -        } | 
| 2258 |  | - | 
| 2259 |  | -        if (!first) | 
| 2260 |  | -            first = sector; | 
| 2261 |  | - | 
| 2262 |  | -        if ( ((sector != first) && (sector != last+1)) || /* not sequential */ | 
| 2263 |  | -             (last-first+1 == 256) ) { /* max 256 sectors per ata request */ | 
| 2264 |  | -            long count = last - first + 1; | 
| 2265 |  | -            rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write ); | 
| 2266 |  | -            if (rc < 0) | 
| 2267 |  | -                return rc * 10 - 1; | 
| 2268 |  | - | 
| 2269 |  | -            buf = (char *)buf + count * SECTOR_SIZE; | 
| 2270 |  | -            first = sector; | 
| 2271 |  | -        } | 
| 2272 |  | - | 
| 2273 |  | -        if ((i == sectorcount-1) && /* last sector requested */ | 
| 2274 |  | -            (!eof)) | 
| 2275 |  | -        { | 
| 2276 |  | -            long count = sector - first + 1; | 
| 2277 |  | -            rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write ); | 
| 2278 |  | -            if (rc < 0) | 
| 2279 |  | -                return rc * 10 - 2; | 
| 2280 |  | -        } | 
| 2281 |  | - | 
| 2282 |  | -        last = sector; | 
| 2283 |  | -    } | 
| 2284 |  | - | 
| 2285 |  | -    file->lastcluster = cluster; | 
| 2286 |  | -    file->lastsector = sector; | 
| 2287 |  | -    file->clusternum = clusternum; | 
| 2288 |  | -    file->sectornum = numsec; | 
| 2289 |  | -    file->eof = eof; | 
| 2290 |  | - | 
| 2291 |  | -    /* if eof, don't report last block as read/written */ | 
| 2292 |  | -    if (eof) | 
| 2293 |  | -        i--; | 
| 2294 |  | - | 
| 2295 |  | -    DEBUGF("Sectors written: %ld", i); | 
| 2296 |  | -    return i; | 
| 2297 |  | -} | 
| 2298 |  | - | 
| 2299 |  | -int fat_seek(struct fat_file *file, unsigned long seeksector ) | 
| 2300 |  | -{ | 
| 2301 |  | -#ifdef HAVE_MULTIVOLUME | 
| 2302 |  | -    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
| 2303 |  | -#else | 
| 2304 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 2305 |  | -#endif | 
| 2306 |  | -    long clusternum=0, numclusters=0, sectornum=0, sector=0; | 
| 2307 |  | -    long cluster = file->firstcluster; | 
| 2308 |  | -    long i; | 
| 2309 |  | - | 
| 2310 |  | -#ifdef HAVE_FAT16SUPPORT | 
| 2311 |  | -    if (cluster < 0) /* FAT16 root dir */ | 
| 2312 |  | -        seeksector += fat_bpb->rootdiroffset; | 
| 2313 |  | -#endif | 
| 2314 |  | - | 
| 2315 |  | -    file->eof = false; | 
| 2316 |  | -    if (seeksector) { | 
| 2317 |  | -        /* we need to find the sector BEFORE the requested, since | 
| 2318 |  | -           the file struct stores the last accessed sector */ | 
| 2319 |  | -        seeksector--; | 
| 2320 |  | -        numclusters = clusternum = seeksector / fat_bpb->bpb_secperclus; | 
| 2321 |  | -        sectornum = seeksector % fat_bpb->bpb_secperclus; | 
| 2322 |  | - | 
| 2323 |  | -        if (file->clusternum && clusternum >= file->clusternum) | 
| 2324 |  | -        { | 
| 2325 |  | -            cluster = file->lastcluster; | 
| 2326 |  | -            numclusters -= file->clusternum; | 
| 2327 |  | -        } | 
| 2328 |  | - | 
| 2329 |  | -        for (i=0; i<numclusters; i++) { | 
| 2330 |  | -            cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster); | 
| 2331 |  | -            if (!cluster) { | 
| 2332 |  | -                DEBUGF("Seeking beyond the end of the file! " | 
| 2333 |  | -                       "(sector %ld, cluster %ld)", seeksector, i); | 
| 2334 |  | -                return -1; | 
| 2335 |  | -            } | 
| 2336 |  | -        } | 
| 2337 |  | - | 
| 2338 |  | -        sector = cluster2sec(IF_MV2(fat_bpb,) cluster) + sectornum; | 
| 2339 |  | -    } | 
| 2340 |  | -    else { | 
| 2341 |  | -        sectornum = -1; | 
| 2342 |  | -    } | 
| 2343 |  | - | 
| 2344 |  | -    DEBUGF("fat_seek(%lx, %lx) == %lx, %lx, %lx", | 
| 2345 |  | -            file->firstcluster, seeksector, cluster, sector, sectornum); | 
| 2346 |  | - | 
| 2347 |  | -    file->lastcluster = cluster; | 
| 2348 |  | -    file->lastsector = sector; | 
| 2349 |  | -    file->clusternum = clusternum; | 
| 2350 |  | -    file->sectornum = sectornum + 1; | 
| 2351 |  | -    return 0; | 
| 2352 |  | -} | 
| 2353 |  | - | 
| 2354 |  | -int fat_opendir(IF_MV2(int volume,)  | 
| 2355 |  | -                struct fat_dir *dir, unsigned long startcluster, | 
| 2356 |  | -                const struct fat_dir *parent_dir) | 
| 2357 |  | -{ | 
| 2358 |  | -#ifdef HAVE_MULTIVOLUME | 
| 2359 |  | -    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
| 2360 |  | -    /* fixme: remove error check when done */ | 
| 2361 |  | -    if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted) | 
| 2362 |  | -    { | 
| 2363 |  | -        DEBUGF("fat_open() illegal volume %d", volume); | 
| 2364 |  | -        return -1; | 
| 2365 |  | -    } | 
| 2366 |  | -#else | 
| 2367 |  | -    struct bpb* fat_bpb = &fat_bpbs[0]; | 
| 2368 |  | -#endif | 
| 2369 |  | -    int rc; | 
| 2370 |  | - | 
| 2371 |  | -    if (startcluster == 0) | 
| 2372 |  | -        startcluster = fat_bpb->bpb_rootclus; | 
| 2373 |  | - | 
| 2374 |  | -    rc = fat_open(IF_MV2(volume,) startcluster, &dir->file, parent_dir); | 
| 2375 |  | -    if(rc) | 
| 2376 |  | -    { | 
| 2377 |  | -        DEBUGF( "fat_opendir() - Couldn't open dir" | 
| 2378 |  | -                " (error code %d)", rc); | 
| 2379 |  | -        return rc * 10 - 1; | 
| 2380 |  | -    } | 
| 2381 |  | -     | 
| 2382 |  | -    /* assign them after fat_open call so that fat_opendir can be called with the same | 
| 2383 |  | -     * fat_dir as parent and result */ | 
| 2384 |  | -    dir->entry = 0; | 
| 2385 |  | -    dir->sector = 0; | 
| 2386 |  | - | 
| 2387 |  | -    return 0; | 
| 2388 |  | -} | 
| 2389 |  | - | 
| 2390 |  | -int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) | 
| 2391 |  | -{ | 
| 2392 |  | -    bool done = false; | 
| 2393 |  | -    int i, j; | 
| 2394 |  | -    int rc; | 
| 2395 |  | -    int order; | 
| 2396 |  | -    unsigned char firstbyte; | 
| 2397 |  | -    /* Long file names are stored in special entries. Each entry holds | 
| 2398 |  | -       up to 13 characters. Names can be max 255 chars (not bytes!) long */ | 
| 2399 |  | -    /* The number of long entries in the long name can be retrieve from the first | 
| 2400 |  | -     * long entry because there are stored in reverse order and have an ordinal */ | 
| 2401 |  | -    int nb_longs = 0; | 
| 2402 |  | -    /* The long entries are expected to be in order, so remember the last ordinal */ | 
| 2403 |  | -    int last_long_ord = 0; | 
| 2404 |  | - | 
| 2405 |  | -    dir->entrycount = 0; | 
| 2406 |  | - | 
| 2407 |  | -    while(!done) | 
| 2408 |  | -    { | 
| 2409 |  | -        if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector ) | 
| 2410 |  | -        { | 
| 2411 |  | -            rc = fat_readwrite(&dir->file, 1, dir->sectorcache, false); | 
| 2412 |  | -            if (rc == 0) { | 
| 2413 |  | -                /* eof */ | 
| 2414 |  | -                entry->name[0] = 0; | 
| 2415 |  | -                break; | 
| 2416 |  | -            } | 
| 2417 |  | -            if (rc < 0) { | 
| 2418 |  | -                DEBUGF( "fat_getnext() - Couldn't read dir" | 
| 2419 |  | -                        " (error code %d)", rc); | 
| 2420 |  | -                return rc * 10 - 1; | 
| 2421 |  | -            } | 
| 2422 |  | -            dir->sector = dir->file.lastsector; | 
| 2423 |  | -        } | 
| 2424 |  | - | 
| 2425 |  | -        for (i = dir->entry % DIR_ENTRIES_PER_SECTOR; | 
| 2426 |  | -             i < DIR_ENTRIES_PER_SECTOR; i++) { | 
| 2427 |  | -            unsigned int entrypos = i * DIR_ENTRY_SIZE; | 
| 2428 |  | - | 
| 2429 |  | -            firstbyte = dir->sectorcache[entrypos]; | 
| 2430 |  | -            dir->entry++; | 
| 2431 |  | - | 
| 2432 |  | -            if (firstbyte == 0xe5) { | 
| 2433 |  | -                /* free entry */ | 
| 2434 |  | -                dir->entrycount = 0; | 
| 2435 |  | -                continue; | 
| 2436 |  | -            } | 
| 2437 |  | - | 
| 2438 |  | -            if (firstbyte == 0) { | 
| 2439 |  | -                /* last entry */ | 
| 2440 |  | -                entry->name[0] = 0; | 
| 2441 |  | -                dir->entrycount = 0; | 
| 2442 |  | -                return 0; | 
| 2443 |  | -            } | 
| 2444 |  | - | 
| 2445 |  | -            dir->entrycount++; | 
| 2446 |  | - | 
| 2447 |  | -            /* LFN entry? */ | 
| 2448 |  | -            if ( ( dir->sectorcache[entrypos + FATDIR_ATTR] & | 
| 2449 |  | -                   FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) { | 
| 2450 |  | -                /* extract ordinal */ | 
| 2451 |  | -                order = dir->sectorcache[entrypos + FATLONG_ORDER] & ~FATLONG_LAST_LONG_ENTRY; | 
| 2452 |  | -                /* is this entry the first long entry ? (first in order but containing last part) */ | 
| 2453 |  | -                if (dir->sectorcache[entrypos + FATLONG_ORDER] & FATLONG_LAST_LONG_ENTRY) { | 
| 2454 |  | -                    /* check that order is not too big ! (and non-zero) */ | 
| 2455 |  | -                    if(order <= 0 || order > FATLONG_MAX_ORDER) | 
| 2456 |  | -                        continue; /* ignore the whole LFN, will trigger lots of warnings */ | 
| 2457 |  | -                    nb_longs = order; | 
| 2458 |  | -                    last_long_ord = order; | 
| 2459 |  | -                } | 
| 2460 |  | -                else { | 
| 2461 |  | -                    /* check orphan entry */ | 
| 2462 |  | -                    if (nb_longs == 0) { | 
| 2463 |  | -                        DEBUGF("fat warning: orphan LFN entry"); | 
| 2464 |  | -                        /* ignore */ | 
| 2465 |  | -                        continue; | 
| 2466 |  | -                    } | 
| 2467 |  | -                     | 
| 2468 |  | -                    /* check order */ | 
| 2469 |  | -                    if (order != (last_long_ord - 1)) { | 
| 2470 |  | -                        DEBUGF("fat warning: wrong LFN ordinal"); | 
| 2471 |  | -                        /* ignore the whole LFN, will trigger lots of warnings */ | 
| 2472 |  | -                        nb_longs = 0; | 
| 2473 |  | -                    } | 
| 2474 |  | - | 
| 2475 |  | -                    last_long_ord = order; | 
| 2476 |  | -                } | 
| 2477 |  | - | 
| 2478 |  | -                /* copy part, reuse [order] for another purpose :) */ | 
| 2479 |  | -                order = (order - 1) * FATLONG_NAME_BYTES_PER_ENTRY; | 
| 2480 |  | -                for(j = 0; j < FATLONG_NAME_CHUNKS; j++) { | 
| 2481 |  | -                    memcpy(dir->longname + order, | 
| 2482 |  | -                            dir->sectorcache + entrypos + FATLONG_NAME_POS[j], | 
| 2483 |  | -                            FATLONG_NAME_SIZE[j]); | 
| 2484 |  | -                    order += FATLONG_NAME_SIZE[j]; | 
| 2485 |  | -                } | 
| 2486 |  | -            } | 
| 2487 |  | -            else { | 
| 2488 |  | -                if ( parse_direntry(entry, dir->sectorcache + entrypos) ) { | 
| 2489 |  | - | 
| 2490 |  | -                    /* don't return volume id entry */ | 
| 2491 |  | -                    if ( (entry->attr & | 
| 2492 |  | -                          (FAT_ATTR_VOLUME_ID|FAT_ATTR_DIRECTORY)) | 
| 2493 |  | -                         == FAT_ATTR_VOLUME_ID) | 
| 2494 |  | -                        continue; | 
| 2495 |  | - | 
| 2496 |  | -                    /* replace shortname with longname? */ | 
| 2497 |  | -                    /* check that the long name is complete */ | 
| 2498 |  | -                    if (nb_longs != 0 && last_long_ord == 1) { | 
| 2499 |  | -                        /* hold a copy of the shortname in case the long one is too long */ | 
| 2500 |  | -                        unsigned char shortname[13]; /* 8+3+dot+\0 */ | 
| 2501 |  | -                        int longname_utf8len = 0; | 
| 2502 |  | -                        /* One character at a time, add 1 for trailing \0, 4 is the maximum size | 
| 2503 |  | -                         * of a UTF8 encoded character in rockbox */ | 
| 2504 |  | -                        unsigned char longname_utf8segm[4 + 1]; | 
| 2505 |  | -                        unsigned short ucs; | 
| 2506 |  | -                        int segm_utf8len; | 
| 2507 |  | -                        /* Temporarily store short name */ | 
| 2508 |  | -                        strcpy(shortname, entry->name); | 
| 2509 |  | -                        entry->name[0] = 0; | 
| 2510 |  | - | 
| 2511 |  | -                        /* Convert the FAT name to a utf8-encoded one. | 
| 2512 |  | -                         * The name is not necessary NUL-terminated ! */ | 
| 2513 |  | -                        for (j = 0; j < nb_longs * FATLONG_NAME_BYTES_PER_ENTRY; j += 2) { | 
| 2514 |  | -                            ucs = dir->longname[j] | (dir->longname[j + 1] << 8); | 
| 2515 |  | -                            if(ucs == 0 || ucs == FAT_LONGNAME_PAD_UCS) | 
| 2516 |  | -                                break; | 
| 2517 |  | -                            /* utf8encode will return a pointer after the converted | 
| 2518 |  | -                             * string, subtract the pointer to the start to get the length of it */ | 
| 2519 |  | -                            segm_utf8len = 1; | 
| 2520 |  | - | 
| 2521 |  | -                            /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */ | 
| 2522 |  | -                            if (longname_utf8len + segm_utf8len >= FAT_FILENAME_BYTES) { | 
| 2523 |  | -                                /* force use of short name */ | 
| 2524 |  | -                                longname_utf8len = FAT_FILENAME_BYTES + 1; | 
| 2525 |  | -                                break; /* fallback later */ | 
| 2526 |  | -                            } | 
| 2527 |  | -                            else { | 
| 2528 |  | -                                if (ucs < 128) longname_utf8segm[0] = (unsigned char)ucs; | 
| 2529 |  | -                                else longname_utf8segm[0] = '?'; | 
| 2530 |  | -                                longname_utf8segm[segm_utf8len] = 0; | 
| 2531 |  | -                                strcat(entry->name + longname_utf8len, longname_utf8segm); | 
| 2532 |  | -                                longname_utf8len += segm_utf8len; | 
| 2533 |  | -                            } | 
| 2534 |  | -                        } | 
| 2535 |  | - | 
| 2536 |  | -                        /* Does the utf8-encoded name fit into the entry? */ | 
| 2537 |  | -                        /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */ | 
| 2538 |  | -                        if (longname_utf8len >= FAT_FILENAME_BYTES) { | 
| 2539 |  | -                            /* Take the short DOS name. Need to utf8-encode it | 
| 2540 |  | -                               since it may contain chars from the upper half of | 
| 2541 |  | -                               the OEM code page which wouldn't be a valid utf8. | 
| 2542 |  | -                               Beware: this file will be shown with strange | 
| 2543 |  | -                               glyphs in file browser since unicode 0x80 to 0x9F | 
| 2544 |  | -                               are control characters. */ | 
| 2545 |  | -                            DEBUGF("SN-DOS: %s", shortname); | 
| 2546 |  | -                            unsigned char *utf8; | 
| 2547 |  | -                            memcpy(entry->name, shortname, strlen(shortname)); | 
| 2548 |  | -                            *(entry->name + strlen(shortname)) = 0; | 
| 2549 |  | -                            DEBUGF("SN: %s", entry->name); | 
| 2550 |  | -                        } else { | 
| 2551 |  | -                            DEBUGF("LN: %s", entry->name); | 
| 2552 |  | -                            DEBUGF("LNLen: %d", longname_utf8len); | 
| 2553 |  | -                        } | 
| 2554 |  | -                    } | 
| 2555 |  | -                    done = true; | 
| 2556 |  | -                    i++; | 
| 2557 |  | -                    break; | 
| 2558 |  | -                } | 
| 2559 |  | -            } | 
| 2560 |  | -        } | 
| 2561 |  | -    } | 
| 2562 |  | -    return 0; | 
| 2563 |  | -} | 
| 2564 |  | - | 
| 2565 |  | -unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)) | 
| 2566 |  | -{ | 
| 2567 |  | -#ifndef HAVE_MULTIVOLUME | 
| 2568 |  | -    const int volume = 0; | 
| 2569 |  | -#endif | 
| 2570 |  | -    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
| 2571 |  | -    return fat_bpb->bpb_secperclus * SECTOR_SIZE; | 
| 2572 |  | -} | 
| 2573 |  | - | 
| 2574 |  | -#ifdef HAVE_MULTIVOLUME | 
| 2575 |  | -bool fat_ismounted(int volume) | 
| 2576 |  | -{ | 
| 2577 |  | -    return (volume<NUM_VOLUMES && fat_bpbs[volume].mounted); | 
| 2578 |  | -} | 
| 2579 |  | -#endif | 
|  | 2 | +/*************************************************************************** | 
|  | 3 | + *             __________               __   ___. | 
|  | 4 | + *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___ | 
|  | 5 | + *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  / | 
|  | 6 | + *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  < | 
|  | 7 | + *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \ | 
|  | 8 | + *                     \/            \/     \/    \/            \/ | 
|  | 9 | + * $Id: fat.c 25459 2010-04-03 22:02:09Z gevaerts $ | 
|  | 10 | + * | 
|  | 11 | + * Copyright (C) 2002 by Linus Nielsen Feltzing | 
|  | 12 | + * | 
|  | 13 | + * This program is free software; you can redistribute it and/or | 
|  | 14 | + * modify it under the terms of the GNU General Public License | 
|  | 15 | + * as published by the Free Software Foundation; either version 2 | 
|  | 16 | + * of the License, or (at your option) any later version. | 
|  | 17 | + * | 
|  | 18 | + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | 
|  | 19 | + * KIND, either express or implied. | 
|  | 20 | + * | 
|  | 21 | + ****************************************************************************/ | 
|  | 22 | +#include "global.h" | 
|  | 23 | +#include "thread.h" | 
|  | 24 | +#include "libc/include/string.h" | 
|  | 25 | +#include "libc/include/stdio.h" | 
|  | 26 | +#include "fat.h" | 
|  | 27 | +#include "storage.h" | 
|  | 28 | +#include "debug.h" | 
|  | 29 | +#include "panic.h" | 
|  | 30 | +#include "libc/include/ctype.h" | 
|  | 31 | + | 
|  | 32 | +#define BYTES2INT16(array,pos) \ | 
|  | 33 | +          (array[pos] | (array[pos+1] << 8 )) | 
|  | 34 | +#define BYTES2INT32(array,pos) \ | 
|  | 35 | +    ((long)array[pos] | ((long)array[pos+1] << 8 ) | \ | 
|  | 36 | +    ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) | 
|  | 37 | + | 
|  | 38 | +#define FATTYPE_FAT12       0 | 
|  | 39 | +#define FATTYPE_FAT16       1 | 
|  | 40 | +#define FATTYPE_FAT32       2 | 
|  | 41 | + | 
|  | 42 | +/* BPB offsets; generic */ | 
|  | 43 | +#define BS_JMPBOOT          0 | 
|  | 44 | +#define BS_OEMNAME          3 | 
|  | 45 | +#define BPB_BYTSPERSEC      11 | 
|  | 46 | +#define BPB_SECPERCLUS      13 | 
|  | 47 | +#define BPB_RSVDSECCNT      14 | 
|  | 48 | +#define BPB_NUMFATS         16 | 
|  | 49 | +#define BPB_ROOTENTCNT      17 | 
|  | 50 | +#define BPB_TOTSEC16        19 | 
|  | 51 | +#define BPB_MEDIA           21 | 
|  | 52 | +#define BPB_FATSZ16         22 | 
|  | 53 | +#define BPB_SECPERTRK       24 | 
|  | 54 | +#define BPB_NUMHEADS        26 | 
|  | 55 | +#define BPB_HIDDSEC         28 | 
|  | 56 | +#define BPB_TOTSEC32        32 | 
|  | 57 | + | 
|  | 58 | +/* fat12/16 */ | 
|  | 59 | +#define BS_DRVNUM           36 | 
|  | 60 | +#define BS_RESERVED1        37 | 
|  | 61 | +#define BS_BOOTSIG          38 | 
|  | 62 | +#define BS_VOLID            39 | 
|  | 63 | +#define BS_VOLLAB           43 | 
|  | 64 | +#define BS_FILSYSTYPE       54 | 
|  | 65 | + | 
|  | 66 | +/* fat32 */ | 
|  | 67 | +#define BPB_FATSZ32         36 | 
|  | 68 | +#define BPB_EXTFLAGS        40 | 
|  | 69 | +#define BPB_FSVER           42 | 
|  | 70 | +#define BPB_ROOTCLUS        44 | 
|  | 71 | +#define BPB_FSINFO          48 | 
|  | 72 | +#define BPB_BKBOOTSEC       50 | 
|  | 73 | +#define BS_32_DRVNUM        64 | 
|  | 74 | +#define BS_32_BOOTSIG       66 | 
|  | 75 | +#define BS_32_VOLID         67 | 
|  | 76 | +#define BS_32_VOLLAB        71 | 
|  | 77 | +#define BS_32_FILSYSTYPE    82 | 
|  | 78 | + | 
|  | 79 | +#define BPB_LAST_WORD       510 | 
|  | 80 | + | 
|  | 81 | + | 
|  | 82 | +/* attributes */ | 
|  | 83 | +#define FAT_ATTR_LONG_NAME   (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \ | 
|  | 84 | +                              FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID) | 
|  | 85 | +#define FAT_ATTR_LONG_NAME_MASK (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \ | 
|  | 86 | +                                 FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID | \ | 
|  | 87 | +                                 FAT_ATTR_DIRECTORY | FAT_ATTR_ARCHIVE ) | 
|  | 88 | + | 
|  | 89 | +/* NTRES flags */ | 
|  | 90 | +#define FAT_NTRES_LC_NAME    0x08 | 
|  | 91 | +#define FAT_NTRES_LC_EXT     0x10 | 
|  | 92 | + | 
|  | 93 | +#define FATDIR_NAME          0 | 
|  | 94 | +#define FATDIR_ATTR          11 | 
|  | 95 | +#define FATDIR_NTRES         12 | 
|  | 96 | +#define FATDIR_CRTTIMETENTH  13 | 
|  | 97 | +#define FATDIR_CRTTIME       14 | 
|  | 98 | +#define FATDIR_CRTDATE       16 | 
|  | 99 | +#define FATDIR_LSTACCDATE    18 | 
|  | 100 | +#define FATDIR_FSTCLUSHI     20 | 
|  | 101 | +#define FATDIR_WRTTIME       22 | 
|  | 102 | +#define FATDIR_WRTDATE       24 | 
|  | 103 | +#define FATDIR_FSTCLUSLO     26 | 
|  | 104 | +#define FATDIR_FILESIZE      28 | 
|  | 105 | + | 
|  | 106 | +#define FATLONG_ORDER        0 | 
|  | 107 | +#define FATLONG_TYPE         12 | 
|  | 108 | +#define FATLONG_CHKSUM       13 | 
|  | 109 | +#define FATLONG_LAST_LONG_ENTRY 0x40 | 
|  | 110 | +#define FATLONG_NAME_BYTES_PER_ENTRY 26 | 
|  | 111 | +/* at most 20 LFN entries, keep coherent with fat_dir->longname size ! */ | 
|  | 112 | +#define FATLONG_MAX_ORDER    20 | 
|  | 113 | + | 
|  | 114 | +#define FATLONG_NAME_CHUNKS 3 | 
|  | 115 | +static unsigned char FATLONG_NAME_POS[FATLONG_NAME_CHUNKS] = {1, 14, 28}; | 
|  | 116 | +static unsigned char FATLONG_NAME_SIZE[FATLONG_NAME_CHUNKS] = {10, 12, 4}; | 
|  | 117 | + | 
|  | 118 | +#define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4) | 
|  | 119 | +#define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2) | 
|  | 120 | +#define DIR_ENTRIES_PER_SECTOR  (SECTOR_SIZE / DIR_ENTRY_SIZE) | 
|  | 121 | +#define DIR_ENTRY_SIZE       32 | 
|  | 122 | +#define NAME_BYTES_PER_ENTRY 13 | 
|  | 123 | +#define FAT_BAD_MARK         0x0ffffff7 | 
|  | 124 | +#define FAT_EOF_MARK         0x0ffffff8 | 
|  | 125 | +#define FAT_LONGNAME_PAD_BYTE 0xff | 
|  | 126 | +#define FAT_LONGNAME_PAD_UCS 0xffff | 
|  | 127 | + | 
|  | 128 | +struct fsinfo { | 
|  | 129 | +    unsigned long freecount; /* last known free cluster count */ | 
|  | 130 | +    unsigned long nextfree;  /* first cluster to start looking for free | 
|  | 131 | +                               clusters, or 0xffffffff for no hint */ | 
|  | 132 | +}; | 
|  | 133 | +/* fsinfo offsets */ | 
|  | 134 | +#define FSINFO_FREECOUNT 488 | 
|  | 135 | +#define FSINFO_NEXTFREE  492 | 
|  | 136 | + | 
|  | 137 | +/* Note: This struct doesn't hold the raw values after mounting if | 
|  | 138 | + * bpb_bytspersec isn't 512. All sector counts are normalized to 512 byte | 
|  | 139 | + * physical sectors. */ | 
|  | 140 | +struct bpb | 
|  | 141 | +{ | 
|  | 142 | +    int bpb_bytspersec;  /* Bytes per sector, typically 512 */ | 
|  | 143 | +    unsigned int bpb_secperclus;  /* Sectors per cluster */ | 
|  | 144 | +    int bpb_rsvdseccnt;  /* Number of reserved sectors */ | 
|  | 145 | +    int bpb_numfats;     /* Number of FAT structures, typically 2 */ | 
|  | 146 | +    int bpb_totsec16;    /* Number of sectors on the volume (old 16-bit) */ | 
|  | 147 | +    int bpb_media;       /* Media type (typically 0xf0 or 0xf8) */ | 
|  | 148 | +    int bpb_fatsz16;     /* Number of used sectors per FAT structure */ | 
|  | 149 | +    unsigned long bpb_totsec32;    /* Number of sectors on the volume | 
|  | 150 | +                                     (new 32-bit) */ | 
|  | 151 | +    unsigned int last_word;       /* 0xAA55 */ | 
|  | 152 | + | 
|  | 153 | +    /**** FAT32 specific *****/ | 
|  | 154 | +    long bpb_fatsz32; | 
|  | 155 | +    long bpb_rootclus; | 
|  | 156 | +    long bpb_fsinfo; | 
|  | 157 | + | 
|  | 158 | +    /* variables for internal use */ | 
|  | 159 | +    unsigned long fatsize; | 
|  | 160 | +    unsigned long totalsectors; | 
|  | 161 | +    unsigned long rootdirsector; | 
|  | 162 | +    unsigned long firstdatasector; | 
|  | 163 | +    unsigned long startsector; | 
|  | 164 | +    unsigned long dataclusters; | 
|  | 165 | +    struct fsinfo fsinfo; | 
|  | 166 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 167 | +    int bpb_rootentcnt;  /* Number of dir entries in the root */ | 
|  | 168 | +    /* internals for FAT16 support */ | 
|  | 169 | +    bool is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */ | 
|  | 170 | +    unsigned int rootdiroffset; /* sector offset of root dir relative to start | 
|  | 171 | +                                 * of first pseudo cluster */ | 
|  | 172 | +#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
|  | 173 | +#ifdef HAVE_MULTIVOLUME | 
|  | 174 | +#ifdef HAVE_MULTIDRIVE | 
|  | 175 | +    int drive; /* on which physical device is this located */ | 
|  | 176 | +#endif | 
|  | 177 | +    bool mounted; /* flag if this volume is mounted */ | 
|  | 178 | +#endif | 
|  | 179 | +}; | 
|  | 180 | + | 
|  | 181 | +static struct bpb fat_bpbs[NUM_VOLUMES]; /* mounted partition info */ | 
|  | 182 | +static bool initialized = false; | 
|  | 183 | + | 
|  | 184 | +static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)); | 
|  | 185 | +static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb)); | 
|  | 186 | +static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)); | 
|  | 187 | +static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,) | 
|  | 188 | +                              long secnum, bool dirty); | 
|  | 189 | +static void unlock_fat_sector(IF_MV2(struct bpb* fat_bpb,) | 
|  | 190 | +                              long secnum); | 
|  | 191 | +static void create_dos_name(const unsigned char *name, unsigned char *newname); | 
|  | 192 | +static void randomize_dos_name(unsigned char *name); | 
|  | 193 | +static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,) | 
|  | 194 | +                                       unsigned long start); | 
|  | 195 | +static int transfer(IF_MV2(struct bpb* fat_bpb,) unsigned long start, | 
|  | 196 | +                    long count, char* buf, bool write ); | 
|  | 197 | + | 
|  | 198 | +#define FAT_CACHE_SIZE 4 | 
|  | 199 | +#define FAT_CACHE_MASK (FAT_CACHE_SIZE-1) | 
|  | 200 | + | 
|  | 201 | +struct fat_cache_entry | 
|  | 202 | +{ | 
|  | 203 | +    long secnum; | 
|  | 204 | +    bool valid; | 
|  | 205 | +    int locked; | 
|  | 206 | +    bool dirty; | 
|  | 207 | +#ifdef HAVE_MULTIVOLUME | 
|  | 208 | +    struct bpb* fat_vol; /* shared cache for all volumes */ | 
|  | 209 | +#endif | 
|  | 210 | +}; | 
|  | 211 | + | 
|  | 212 | +static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE] CACHEALIGN_ATTR; | 
|  | 213 | +static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE]; | 
|  | 214 | +static struct mutex cache_mutex; | 
|  | 215 | +static struct mutex tempbuf_mutex; | 
|  | 216 | +static char fat_tempbuf[SECTOR_SIZE] CACHEALIGN_ATTR; | 
|  | 217 | +static bool tempbuf_locked; | 
|  | 218 | + | 
|  | 219 | +#if defined(HAVE_HOTSWAP) && !(CONFIG_STORAGE & STORAGE_MMC) /* A better condition ?? */ | 
|  | 220 | +void fat_lock(void) | 
|  | 221 | +{ | 
|  | 222 | +    mutex_lock(&cache_mutex, TIMEOUT_BLOCK); | 
|  | 223 | +} | 
|  | 224 | + | 
|  | 225 | +void fat_unlock(void) | 
|  | 226 | +{ | 
|  | 227 | +    mutex_unlock(&cache_mutex); | 
|  | 228 | +} | 
|  | 229 | +#endif | 
|  | 230 | + | 
|  | 231 | +static long cluster2sec(IF_MV2(struct bpb* fat_bpb,) long cluster) | 
|  | 232 | +{ | 
|  | 233 | +#ifndef HAVE_MULTIVOLUME | 
|  | 234 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 235 | +#endif | 
|  | 236 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 237 | +    /* negative clusters (FAT16 root dir) don't get the 2 offset */ | 
|  | 238 | +    int zerocluster = cluster < 0 ? 0 : 2; | 
|  | 239 | +#else | 
|  | 240 | +    const long zerocluster = 2; | 
|  | 241 | +#endif | 
|  | 242 | + | 
|  | 243 | +    if (cluster > (long)(fat_bpb->dataclusters + 1)) | 
|  | 244 | +    { | 
|  | 245 | +      DEBUGF( "cluster2sec() - Bad cluster number (%ld)", cluster); | 
|  | 246 | +        return -1; | 
|  | 247 | +    } | 
|  | 248 | + | 
|  | 249 | +    return (cluster - zerocluster) * fat_bpb->bpb_secperclus | 
|  | 250 | +           + fat_bpb->firstdatasector; | 
|  | 251 | +} | 
|  | 252 | + | 
|  | 253 | +void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free) | 
|  | 254 | +{ | 
|  | 255 | +#ifndef HAVE_MULTIVOLUME | 
|  | 256 | +    const int volume = 0; | 
|  | 257 | +#endif | 
|  | 258 | +    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
|  | 259 | +    if (size) | 
|  | 260 | +      *size = fat_bpb->dataclusters * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024); | 
|  | 261 | +    if (free) | 
|  | 262 | +      *free = fat_bpb->fsinfo.freecount * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024); | 
|  | 263 | +} | 
|  | 264 | + | 
|  | 265 | +void fat_init(void) | 
|  | 266 | +{ | 
|  | 267 | +    unsigned int i; | 
|  | 268 | + | 
|  | 269 | +    if (!initialized) | 
|  | 270 | +    { | 
|  | 271 | +        initialized = true; | 
|  | 272 | +        mutex_init(&cache_mutex); | 
|  | 273 | +        mutex_init(&tempbuf_mutex); | 
|  | 274 | +        tempbuf_locked = false; | 
|  | 275 | +    } | 
|  | 276 | + | 
|  | 277 | +    /* mark the FAT cache as unused */ | 
|  | 278 | +    for(i = 0;i < FAT_CACHE_SIZE;i++) | 
|  | 279 | +    { | 
|  | 280 | +        fat_cache[i].secnum = -1; | 
|  | 281 | +        fat_cache[i].valid = false; | 
|  | 282 | +        fat_cache[i].locked = 0; | 
|  | 283 | +        fat_cache[i].dirty = false; | 
|  | 284 | +#ifdef HAVE_MULTIVOLUME | 
|  | 285 | +        fat_cache[i].fat_vol = NULL; | 
|  | 286 | +#endif | 
|  | 287 | +    } | 
|  | 288 | +#ifdef HAVE_MULTIVOLUME | 
|  | 289 | +    /* mark the possible volumes as not mounted */ | 
|  | 290 | +    for (i=0; i<NUM_VOLUMES;i++) | 
|  | 291 | +    { | 
|  | 292 | +        fat_bpbs[i].mounted = false; | 
|  | 293 | +    } | 
|  | 294 | +#endif | 
|  | 295 | +} | 
|  | 296 | + | 
|  | 297 | +int fat_mount(IF_MV2(int volume,) IF_MD2(int drive,) long startsector) | 
|  | 298 | +{ | 
|  | 299 | +#ifndef HAVE_MULTIVOLUME | 
|  | 300 | +    const int volume = 0; | 
|  | 301 | +#endif | 
|  | 302 | +    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
|  | 303 | +    int rc; | 
|  | 304 | +    int secmult; | 
|  | 305 | +    long datasec; | 
|  | 306 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 307 | +    int rootdirsectors; | 
|  | 308 | +#endif | 
|  | 309 | + | 
|  | 310 | +    /* Read the sector */ | 
|  | 311 | +    unsigned char* buf = fat_get_sector_buffer(); | 
|  | 312 | +    rc = storage_read_sectors(IF_MD2(drive,) startsector,1,buf); | 
|  | 313 | +    if(rc) | 
|  | 314 | +    { | 
|  | 315 | +        fat_release_sector_buffer(); | 
|  | 316 | +        DEBUGF( "fat_mount() - Couldn't read BPB (error code %d)", rc); | 
|  | 317 | +        return rc * 10 - 1; | 
|  | 318 | +    } | 
|  | 319 | + | 
|  | 320 | +    memset(fat_bpb, 0, sizeof(struct bpb)); | 
|  | 321 | +    fat_bpb->startsector    = startsector; | 
|  | 322 | +#ifdef HAVE_MULTIDRIVE | 
|  | 323 | +    fat_bpb->drive          = drive; | 
|  | 324 | +#endif | 
|  | 325 | + | 
|  | 326 | +    fat_bpb->bpb_bytspersec = BYTES2INT16(buf,BPB_BYTSPERSEC); | 
|  | 327 | +    secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE; | 
|  | 328 | +    /* Sanity check is performed later */ | 
|  | 329 | + | 
|  | 330 | +    fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS]; | 
|  | 331 | +    fat_bpb->bpb_rsvdseccnt = secmult * BYTES2INT16(buf,BPB_RSVDSECCNT); | 
|  | 332 | +    fat_bpb->bpb_numfats    = buf[BPB_NUMFATS]; | 
|  | 333 | +    fat_bpb->bpb_media      = buf[BPB_MEDIA]; | 
|  | 334 | +    fat_bpb->bpb_fatsz16    = secmult * BYTES2INT16(buf,BPB_FATSZ16); | 
|  | 335 | +    fat_bpb->bpb_fatsz32    = secmult * BYTES2INT32(buf,BPB_FATSZ32); | 
|  | 336 | +    fat_bpb->bpb_totsec16   = secmult * BYTES2INT16(buf,BPB_TOTSEC16); | 
|  | 337 | +    fat_bpb->bpb_totsec32   = secmult * BYTES2INT32(buf,BPB_TOTSEC32); | 
|  | 338 | +    fat_bpb->last_word      = BYTES2INT16(buf,BPB_LAST_WORD); | 
|  | 339 | + | 
|  | 340 | +    /* calculate a few commonly used values */ | 
|  | 341 | +    if (fat_bpb->bpb_fatsz16 != 0) | 
|  | 342 | +        fat_bpb->fatsize = fat_bpb->bpb_fatsz16; | 
|  | 343 | +    else | 
|  | 344 | +        fat_bpb->fatsize = fat_bpb->bpb_fatsz32; | 
|  | 345 | + | 
|  | 346 | +    if (fat_bpb->bpb_totsec16 != 0) | 
|  | 347 | +        fat_bpb->totalsectors = fat_bpb->bpb_totsec16; | 
|  | 348 | +    else | 
|  | 349 | +        fat_bpb->totalsectors = fat_bpb->bpb_totsec32; | 
|  | 350 | + | 
|  | 351 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 352 | +    fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT); | 
|  | 353 | +    if (!fat_bpb->bpb_bytspersec) | 
|  | 354 | +    { | 
|  | 355 | +        fat_release_sector_buffer(); | 
|  | 356 | +        return -2; | 
|  | 357 | +    } | 
|  | 358 | +    rootdirsectors = secmult * ((fat_bpb->bpb_rootentcnt * DIR_ENTRY_SIZE | 
|  | 359 | +                     + fat_bpb->bpb_bytspersec - 1) / fat_bpb->bpb_bytspersec); | 
|  | 360 | +#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
|  | 361 | + | 
|  | 362 | +    fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt | 
|  | 363 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 364 | +        + rootdirsectors | 
|  | 365 | +#endif | 
|  | 366 | +        + fat_bpb->bpb_numfats * fat_bpb->fatsize; | 
|  | 367 | + | 
|  | 368 | +    /* Determine FAT type */ | 
|  | 369 | +    datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector; | 
|  | 370 | +    if (fat_bpb->bpb_secperclus) | 
|  | 371 | +        fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus; | 
|  | 372 | +    else | 
|  | 373 | +    { | 
|  | 374 | +        fat_release_sector_buffer(); | 
|  | 375 | +        return -2; | 
|  | 376 | +    } | 
|  | 377 | + | 
|  | 378 | +#ifdef TEST_FAT | 
|  | 379 | +    /* | 
|  | 380 | +      we are sometimes testing with "illegally small" fat32 images, | 
|  | 381 | +      so we don't use the proper fat32 test case for test code | 
|  | 382 | +    */ | 
|  | 383 | +    if ( fat_bpb->bpb_fatsz16 ) | 
|  | 384 | +#else | 
|  | 385 | +    if ( fat_bpb->dataclusters < 65525 ) | 
|  | 386 | +#endif | 
|  | 387 | +    { /* FAT16 */ | 
|  | 388 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 389 | +        fat_bpb->is_fat16 = true; | 
|  | 390 | +        if (fat_bpb->dataclusters < 4085) | 
|  | 391 | +        { /* FAT12 */ | 
|  | 392 | +            fat_release_sector_buffer(); | 
|  | 393 | +            DEBUGF("This is FAT12. Go away!"); | 
|  | 394 | +            return -2; | 
|  | 395 | +        } | 
|  | 396 | +#else /* #ifdef HAVE_FAT16SUPPORT */ | 
|  | 397 | +        fat_release_sector_buffer(); | 
|  | 398 | +        DEBUGF("This is not FAT32. Go away!"); | 
|  | 399 | +        return -2; | 
|  | 400 | +#endif /* #ifndef HAVE_FAT16SUPPORT */ | 
|  | 401 | +    } | 
|  | 402 | + | 
|  | 403 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 404 | +    if (fat_bpb->is_fat16) | 
|  | 405 | +    { /* FAT16 specific part of BPB */ | 
|  | 406 | +        int dirclusters; | 
|  | 407 | +        fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt | 
|  | 408 | +            + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16; | 
|  | 409 | +        dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1) | 
|  | 410 | +            / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */ | 
|  | 411 | +        /* I assign negative pseudo cluster numbers for the root directory, | 
|  | 412 | +           their range is counted upward until -1. */ | 
|  | 413 | +        fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data*/ | 
|  | 414 | +        fat_bpb->rootdiroffset = dirclusters * fat_bpb->bpb_secperclus | 
|  | 415 | +            - rootdirsectors; | 
|  | 416 | +    } | 
|  | 417 | +    else | 
|  | 418 | +#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
|  | 419 | +    { /* FAT32 specific part of BPB */ | 
|  | 420 | +        fat_bpb->bpb_rootclus  = BYTES2INT32(buf,BPB_ROOTCLUS); | 
|  | 421 | +        fat_bpb->bpb_fsinfo    = secmult * BYTES2INT16(buf,BPB_FSINFO); | 
|  | 422 | +        fat_bpb->rootdirsector = cluster2sec(IF_MV2(fat_bpb,) | 
|  | 423 | +                                             fat_bpb->bpb_rootclus); | 
|  | 424 | +    } | 
|  | 425 | + | 
|  | 426 | +    rc = bpb_is_sane(IF_MV(fat_bpb)); | 
|  | 427 | +    if (rc < 0) | 
|  | 428 | +    { | 
|  | 429 | +        fat_release_sector_buffer(); | 
|  | 430 | +        DEBUGF( "fat_mount() - BPB is not sane"); | 
|  | 431 | +        return rc * 10 - 3; | 
|  | 432 | +    } | 
|  | 433 | + | 
|  | 434 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 435 | +    if (fat_bpb->is_fat16) | 
|  | 436 | +    { | 
|  | 437 | +        fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc below */ | 
|  | 438 | +        fat_bpb->fsinfo.nextfree = 0xffffffff; | 
|  | 439 | +    } | 
|  | 440 | +    else | 
|  | 441 | +#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
|  | 442 | +    { | 
|  | 443 | +        /* Read the fsinfo sector */ | 
|  | 444 | +        rc = storage_read_sectors(IF_MD2(drive,) | 
|  | 445 | +            startsector + fat_bpb->bpb_fsinfo, 1, buf); | 
|  | 446 | +        if (rc < 0) | 
|  | 447 | +        { | 
|  | 448 | +            fat_release_sector_buffer(); | 
|  | 449 | +            DEBUGF( "fat_mount() - Couldn't read FSInfo (error code %d)", rc); | 
|  | 450 | +            return rc * 10 - 4; | 
|  | 451 | +        } | 
|  | 452 | +        fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT); | 
|  | 453 | +        fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE); | 
|  | 454 | +    } | 
|  | 455 | +    fat_release_sector_buffer(); | 
|  | 456 | + | 
|  | 457 | +    /* calculate freecount if unset */ | 
|  | 458 | +    if ( fat_bpb->fsinfo.freecount == 0xffffffff ) | 
|  | 459 | +    { | 
|  | 460 | +        fat_recalc_free(IF_MV(volume)); | 
|  | 461 | +    } | 
|  | 462 | + | 
|  | 463 | +    DEBUGF("Freecount: %ld",fat_bpb->fsinfo.freecount); | 
|  | 464 | +    DEBUGF("Nextfree: 0x%lx",fat_bpb->fsinfo.nextfree); | 
|  | 465 | +    DEBUGF("Cluster count: 0x%lx",fat_bpb->dataclusters); | 
|  | 466 | +    DEBUGF("Sectors per cluster: %d",fat_bpb->bpb_secperclus); | 
|  | 467 | +    DEBUGF("FAT sectors: 0x%lx",fat_bpb->fatsize); | 
|  | 468 | + | 
|  | 469 | +#ifdef HAVE_MULTIVOLUME | 
|  | 470 | +    fat_bpb->mounted = true; | 
|  | 471 | +#endif | 
|  | 472 | + | 
|  | 473 | +    return 0; | 
|  | 474 | +} | 
|  | 475 | + | 
|  | 476 | +#ifdef HAVE_HOTSWAP | 
|  | 477 | +int fat_unmount(int volume, bool flush) | 
|  | 478 | +{ | 
|  | 479 | +    int rc; | 
|  | 480 | +#ifdef HAVE_MULTIVOLUME | 
|  | 481 | +    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
|  | 482 | +#else | 
|  | 483 | +    (void)volume; | 
|  | 484 | +#endif | 
|  | 485 | + | 
|  | 486 | +    if(flush) | 
|  | 487 | +    { | 
|  | 488 | +        rc = flush_fat(IF_MV(fat_bpb)); /* the clean way, while still alive */ | 
|  | 489 | +    } | 
|  | 490 | +    else | 
|  | 491 | +    {   /* volume is not accessible any more, e.g. MMC removed */ | 
|  | 492 | +        int i; | 
|  | 493 | +        mutex_lock(&cache_mutex, TIMEOUT_BLOCK); | 
|  | 494 | +        for(i = 0;i < FAT_CACHE_SIZE;i++) | 
|  | 495 | +        { | 
|  | 496 | +            struct fat_cache_entry *fce = &fat_cache[i]; | 
|  | 497 | +            if((fce->valid || fce->locked) | 
|  | 498 | +#ifdef HAVE_MULTIVOLUME | 
|  | 499 | +               && fce->fat_vol == fat_bpb | 
|  | 500 | +#endif | 
|  | 501 | +              ) | 
|  | 502 | +            { | 
|  | 503 | +                fce->valid = false; /* discard all from that volume */ | 
|  | 504 | +                fce->locked = 0; | 
|  | 505 | +                fce->dirty = false; | 
|  | 506 | +            } | 
|  | 507 | +        } | 
|  | 508 | +        mutex_unlock(&cache_mutex); | 
|  | 509 | +        rc = 0; | 
|  | 510 | +    } | 
|  | 511 | +#ifdef HAVE_MULTIVOLUME | 
|  | 512 | +    fat_bpb->mounted = false; | 
|  | 513 | +#endif | 
|  | 514 | +    return rc; | 
|  | 515 | +} | 
|  | 516 | +#endif /* #ifdef HAVE_HOTSWAP */ | 
|  | 517 | + | 
|  | 518 | +void fat_recalc_free(IF_MV_NONVOID(int volume)) | 
|  | 519 | +{ | 
|  | 520 | +#ifndef HAVE_MULTIVOLUME | 
|  | 521 | +    const int volume = 0; | 
|  | 522 | +#endif | 
|  | 523 | +    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
|  | 524 | +    long free = 0; | 
|  | 525 | +    unsigned long i; | 
|  | 526 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 527 | +    if (fat_bpb->is_fat16) | 
|  | 528 | +    { | 
|  | 529 | +        for (i = 0; i<fat_bpb->fatsize; i++) { | 
|  | 530 | +            unsigned int j; | 
|  | 531 | +            unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false); | 
|  | 532 | +            for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { | 
|  | 533 | +                unsigned int c = i * CLUSTERS_PER_FAT16_SECTOR + j; | 
|  | 534 | +                if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ | 
|  | 535 | +                    break; | 
|  | 536 | + | 
|  | 537 | +                if (letoh16(fat[j]) == 0x0000) { | 
|  | 538 | +                    free++; | 
|  | 539 | +                    if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) | 
|  | 540 | +                        fat_bpb->fsinfo.nextfree = c; | 
|  | 541 | +                } | 
|  | 542 | +            } | 
|  | 543 | +            unlock_fat_sector(IF_MV2(fat_bpb,) i); | 
|  | 544 | +        } | 
|  | 545 | +    } | 
|  | 546 | +    else | 
|  | 547 | +#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
|  | 548 | +    { | 
|  | 549 | +        for (i = 0; i<fat_bpb->fatsize; i++) { | 
|  | 550 | +            unsigned int j; | 
|  | 551 | +            unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false); | 
|  | 552 | +            for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { | 
|  | 553 | +                unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j; | 
|  | 554 | +                if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ | 
|  | 555 | +                    break; | 
|  | 556 | + | 
|  | 557 | +                if (!(letoh32(fat[j]) & 0x0fffffff)) { | 
|  | 558 | +                    free++; | 
|  | 559 | +                    if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) | 
|  | 560 | +                        fat_bpb->fsinfo.nextfree = c; | 
|  | 561 | +                } | 
|  | 562 | +            } | 
|  | 563 | +            unlock_fat_sector(IF_MV2(fat_bpb,) i); | 
|  | 564 | +        } | 
|  | 565 | +    } | 
|  | 566 | +    fat_bpb->fsinfo.freecount = free; | 
|  | 567 | +    update_fsinfo(IF_MV(fat_bpb)); | 
|  | 568 | +} | 
|  | 569 | + | 
|  | 570 | +static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)) | 
|  | 571 | +{ | 
|  | 572 | +#ifndef HAVE_MULTIVOLUME | 
|  | 573 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 574 | +#endif | 
|  | 575 | +    if(fat_bpb->bpb_bytspersec % SECTOR_SIZE) | 
|  | 576 | +    { | 
|  | 577 | +        DEBUGF( "bpb_is_sane() - Error: sector size is not sane (%d)", | 
|  | 578 | +                fat_bpb->bpb_bytspersec); | 
|  | 579 | +        return -1; | 
|  | 580 | +    } | 
|  | 581 | +    if((long)fat_bpb->bpb_secperclus * (long)fat_bpb->bpb_bytspersec | 
|  | 582 | +                                                                   > 128L*1024L) | 
|  | 583 | +    { | 
|  | 584 | +        DEBUGF( "bpb_is_sane() - Error: cluster size is larger than 128K " | 
|  | 585 | +                "(%d * %d = %d)", | 
|  | 586 | +                fat_bpb->bpb_bytspersec, fat_bpb->bpb_secperclus, | 
|  | 587 | +                fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus); | 
|  | 588 | +        return -2; | 
|  | 589 | +    } | 
|  | 590 | +    if(fat_bpb->bpb_numfats != 2) | 
|  | 591 | +    { | 
|  | 592 | +        DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)", | 
|  | 593 | +                fat_bpb->bpb_numfats); | 
|  | 594 | +    } | 
|  | 595 | +    if(fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8) | 
|  | 596 | +    { | 
|  | 597 | +        DEBUGF( "bpb_is_sane() - Warning: Non-standard " | 
|  | 598 | +                "media type (0x%02x)", | 
|  | 599 | +                fat_bpb->bpb_media); | 
|  | 600 | +    } | 
|  | 601 | +    if(fat_bpb->last_word != 0xaa55) | 
|  | 602 | +    { | 
|  | 603 | +        DEBUGF( "bpb_is_sane() - Error: Last word is not " | 
|  | 604 | +                "0xaa55 (0x%04x)", fat_bpb->last_word); | 
|  | 605 | +        return -3; | 
|  | 606 | +    } | 
|  | 607 | + | 
|  | 608 | +    if (fat_bpb->fsinfo.freecount > | 
|  | 609 | +        (fat_bpb->totalsectors - fat_bpb->firstdatasector)/ | 
|  | 610 | +        fat_bpb->bpb_secperclus) | 
|  | 611 | +    { | 
|  | 612 | +        DEBUGF( "bpb_is_sane() - Error: FSInfo.Freecount > disk size " | 
|  | 613 | +                 "(0x%04lx)", fat_bpb->fsinfo.freecount); | 
|  | 614 | +        return -4; | 
|  | 615 | +    } | 
|  | 616 | + | 
|  | 617 | +    return 0; | 
|  | 618 | +} | 
|  | 619 | + | 
|  | 620 | +static void flush_fat_sector(struct fat_cache_entry *fce, | 
|  | 621 | +                             unsigned char *sectorbuf) | 
|  | 622 | +{ | 
|  | 623 | +    int rc; | 
|  | 624 | +    long secnum; | 
|  | 625 | + | 
|  | 626 | +    /* With multivolume, use only the FAT info from the cached sector! */ | 
|  | 627 | +#ifdef HAVE_MULTIVOLUME | 
|  | 628 | +    secnum = fce->secnum + fce->fat_vol->startsector; | 
|  | 629 | +#else | 
|  | 630 | +    secnum = fce->secnum + fat_bpbs[0].startsector; | 
|  | 631 | +#endif | 
|  | 632 | + | 
|  | 633 | +    /* Write to the first FAT */ | 
|  | 634 | +    rc = storage_write_sectors(IF_MD2(fce->fat_vol->drive,) | 
|  | 635 | +                           secnum, 1, | 
|  | 636 | +                           sectorbuf); | 
|  | 637 | +    if(rc < 0) | 
|  | 638 | +    { | 
|  | 639 | +        panicf(PANIC_KILLUSERTHREADS, "flush_fat_sector() - Could not write sector %ld" | 
|  | 640 | +               " (error %d)", | 
|  | 641 | +               secnum, rc); | 
|  | 642 | +    } | 
|  | 643 | +#ifdef HAVE_MULTIVOLUME | 
|  | 644 | +    if(fce->fat_vol->bpb_numfats > 1) | 
|  | 645 | +#else | 
|  | 646 | +    if(fat_bpbs[0].bpb_numfats > 1) | 
|  | 647 | +#endif | 
|  | 648 | +    { | 
|  | 649 | +        /* Write to the second FAT */ | 
|  | 650 | +#ifdef HAVE_MULTIVOLUME | 
|  | 651 | +        secnum += fce->fat_vol->fatsize; | 
|  | 652 | +#else | 
|  | 653 | +        secnum += fat_bpbs[0].fatsize; | 
|  | 654 | +#endif | 
|  | 655 | +        rc = storage_write_sectors(IF_MD2(fce->fat_vol->drive,) | 
|  | 656 | +                               secnum, 1, sectorbuf); | 
|  | 657 | +        if(rc < 0) | 
|  | 658 | +        { | 
|  | 659 | +            panicf(PANIC_KILLUSERTHREADS, "flush_fat_sector() - Could not write sector %ld" | 
|  | 660 | +                   " (error %d)", | 
|  | 661 | +                   secnum, rc); | 
|  | 662 | +        } | 
|  | 663 | +    } | 
|  | 664 | +    fce->dirty = false; | 
|  | 665 | +} | 
|  | 666 | + | 
|  | 667 | +static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,) | 
|  | 668 | +                              long fatsector, bool dirty) | 
|  | 669 | +{ | 
|  | 670 | +#ifndef HAVE_MULTIVOLUME | 
|  | 671 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 672 | +#endif | 
|  | 673 | +    long secnum = fatsector + fat_bpb->bpb_rsvdseccnt; | 
|  | 674 | +    int cache_index = secnum & FAT_CACHE_MASK; | 
|  | 675 | +    struct fat_cache_entry *fce = &fat_cache[cache_index]; | 
|  | 676 | +    unsigned char *sectorbuf = &fat_cache_sectors[cache_index][0]; | 
|  | 677 | +    int rc; | 
|  | 678 | + | 
|  | 679 | +    mutex_lock(&cache_mutex, TIMEOUT_BLOCK); /* make changes atomic */ | 
|  | 680 | + | 
|  | 681 | +    /* Delete the cache entry if it isn't the sector we want */ | 
|  | 682 | +    if(fce->valid && (fce->secnum != secnum | 
|  | 683 | +#ifdef HAVE_MULTIVOLUME | 
|  | 684 | +        || fce->fat_vol != fat_bpb | 
|  | 685 | +#endif | 
|  | 686 | +    )) | 
|  | 687 | +    { | 
|  | 688 | +        while (fce->locked) sleep(1000); | 
|  | 689 | +        /* Write back if it is dirty */ | 
|  | 690 | +        if(fce->dirty) | 
|  | 691 | +        { | 
|  | 692 | +            flush_fat_sector(fce, sectorbuf); | 
|  | 693 | +        } | 
|  | 694 | +        fce->valid = false; | 
|  | 695 | +    } | 
|  | 696 | + | 
|  | 697 | +    /* Load the sector if it is not cached */ | 
|  | 698 | +    if(!fce->valid) | 
|  | 699 | +    { | 
|  | 700 | +        rc = storage_read_sectors(IF_MD2(fat_bpb->drive,) | 
|  | 701 | +                              secnum + fat_bpb->startsector,1, | 
|  | 702 | +                              sectorbuf); | 
|  | 703 | +        if(rc < 0) | 
|  | 704 | +        { | 
|  | 705 | +            DEBUGF( "cache_fat_sector() - Could not read sector %ld" | 
|  | 706 | +                    " (error %d)", secnum, rc); | 
|  | 707 | +            mutex_unlock(&cache_mutex); | 
|  | 708 | +            return NULL; | 
|  | 709 | +        } | 
|  | 710 | +        fce->valid = true; | 
|  | 711 | +        fce->secnum = secnum; | 
|  | 712 | +#ifdef HAVE_MULTIVOLUME | 
|  | 713 | +        fce->fat_vol = fat_bpb; | 
|  | 714 | +#endif | 
|  | 715 | +    } | 
|  | 716 | +    fce->locked++; | 
|  | 717 | +    if (dirty) | 
|  | 718 | +        fce->dirty = true; /* dirt remains, sticky until flushed */ | 
|  | 719 | +    mutex_unlock(&cache_mutex); | 
|  | 720 | +    return sectorbuf; | 
|  | 721 | +} | 
|  | 722 | + | 
|  | 723 | +static void unlock_fat_sector(IF_MV2(struct bpb* fat_bpb,) long fatsector) | 
|  | 724 | +{ | 
|  | 725 | +#ifndef HAVE_MULTIVOLUME | 
|  | 726 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 727 | +#endif | 
|  | 728 | +    long secnum = fatsector + fat_bpb->bpb_rsvdseccnt; | 
|  | 729 | +    int cache_index = secnum & FAT_CACHE_MASK; | 
|  | 730 | +    fat_cache[cache_index].locked--; | 
|  | 731 | +} | 
|  | 732 | + | 
|  | 733 | +void* fat_get_sector_buffer() | 
|  | 734 | +{ | 
|  | 735 | +    mutex_lock(&tempbuf_mutex, TIMEOUT_BLOCK); | 
|  | 736 | +    if (tempbuf_locked) | 
|  | 737 | +        panicf(PANIC_KILLUSERTHREADS, "FAT: Tried to lock temporary sector buffer twice!"); | 
|  | 738 | +    tempbuf_locked = true; | 
|  | 739 | +    return fat_tempbuf; | 
|  | 740 | +} | 
|  | 741 | + | 
|  | 742 | +void fat_release_sector_buffer() | 
|  | 743 | +{ | 
|  | 744 | +    tempbuf_locked = false; | 
|  | 745 | +    mutex_unlock(&tempbuf_mutex); | 
|  | 746 | +} | 
|  | 747 | + | 
|  | 748 | +static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,) | 
|  | 749 | +                                       unsigned long startcluster) | 
|  | 750 | +{ | 
|  | 751 | +#ifndef HAVE_MULTIVOLUME | 
|  | 752 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 753 | +#endif | 
|  | 754 | +    unsigned long sector; | 
|  | 755 | +    unsigned long offset; | 
|  | 756 | +    unsigned long i; | 
|  | 757 | + | 
|  | 758 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 759 | +    if (fat_bpb->is_fat16) | 
|  | 760 | +    { | 
|  | 761 | +        sector = startcluster / CLUSTERS_PER_FAT16_SECTOR; | 
|  | 762 | +        offset = startcluster % CLUSTERS_PER_FAT16_SECTOR; | 
|  | 763 | + | 
|  | 764 | +        for (i = 0; i<fat_bpb->fatsize; i++) { | 
|  | 765 | +            unsigned int j; | 
|  | 766 | +            unsigned int nr = (i + sector) % fat_bpb->fatsize; | 
|  | 767 | +            unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false); | 
|  | 768 | +            if ( !fat ) | 
|  | 769 | +                break; | 
|  | 770 | +            for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { | 
|  | 771 | +                int k = (j + offset) % CLUSTERS_PER_FAT16_SECTOR; | 
|  | 772 | +                if (letoh16(fat[k]) == 0x0000) { | 
|  | 773 | +                    unsigned int c = nr * CLUSTERS_PER_FAT16_SECTOR + k; | 
|  | 774 | +                     /* Ignore the reserved clusters 0 & 1, and also | 
|  | 775 | +                        cluster numbers out of bounds */ | 
|  | 776 | +                    if ( c < 2 || c > fat_bpb->dataclusters+1 ) | 
|  | 777 | +                        continue; | 
|  | 778 | +                    unlock_fat_sector(IF_MV2(fat_bpb,) nr); | 
|  | 779 | +                    DEBUGF("find_free_cluster(%x) == %x",startcluster,c); | 
|  | 780 | +                    fat_bpb->fsinfo.nextfree = c; | 
|  | 781 | +                    return c; | 
|  | 782 | +                } | 
|  | 783 | +            } | 
|  | 784 | +            unlock_fat_sector(IF_MV2(fat_bpb,) nr); | 
|  | 785 | +            offset = 0; | 
|  | 786 | +        } | 
|  | 787 | +    } | 
|  | 788 | +    else | 
|  | 789 | +#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
|  | 790 | +    { | 
|  | 791 | +        sector = startcluster / CLUSTERS_PER_FAT_SECTOR; | 
|  | 792 | +        offset = startcluster % CLUSTERS_PER_FAT_SECTOR; | 
|  | 793 | + | 
|  | 794 | +        for (i = 0; i<fat_bpb->fatsize; i++) { | 
|  | 795 | +            unsigned int j; | 
|  | 796 | +            unsigned long nr = (i + sector) % fat_bpb->fatsize; | 
|  | 797 | +            unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr, false); | 
|  | 798 | +            if ( !fat ) | 
|  | 799 | +                break; | 
|  | 800 | +            for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { | 
|  | 801 | +                int k = (j + offset) % CLUSTERS_PER_FAT_SECTOR; | 
|  | 802 | +                if (!(letoh32(fat[k]) & 0x0fffffff)) { | 
|  | 803 | +                    unsigned long c = nr * CLUSTERS_PER_FAT_SECTOR + k; | 
|  | 804 | +                     /* Ignore the reserved clusters 0 & 1, and also | 
|  | 805 | +                        cluster numbers out of bounds */ | 
|  | 806 | +                    if ( c < 2 || c > fat_bpb->dataclusters+1 ) | 
|  | 807 | +                        continue; | 
|  | 808 | +                    unlock_fat_sector(IF_MV2(fat_bpb,) nr); | 
|  | 809 | +                    DEBUGF("find_free_cluster(%lx) == %lx",startcluster,c); | 
|  | 810 | +                    fat_bpb->fsinfo.nextfree = c; | 
|  | 811 | +                    return c; | 
|  | 812 | +                } | 
|  | 813 | +            } | 
|  | 814 | +            unlock_fat_sector(IF_MV2(fat_bpb,) nr); | 
|  | 815 | +            offset = 0; | 
|  | 816 | +        } | 
|  | 817 | +    } | 
|  | 818 | + | 
|  | 819 | +    DEBUGF("find_free_cluster(%lx) == 0",startcluster); | 
|  | 820 | +    return 0; /* 0 is an illegal cluster number */ | 
|  | 821 | +} | 
|  | 822 | + | 
|  | 823 | +static int update_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry, | 
|  | 824 | +                            unsigned long val) | 
|  | 825 | +{ | 
|  | 826 | +#ifndef HAVE_MULTIVOLUME | 
|  | 827 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 828 | +#endif | 
|  | 829 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 830 | +    if (fat_bpb->is_fat16) | 
|  | 831 | +    { | 
|  | 832 | +        int sector = entry / CLUSTERS_PER_FAT16_SECTOR; | 
|  | 833 | +        int offset = entry % CLUSTERS_PER_FAT16_SECTOR; | 
|  | 834 | +        unsigned short* sec; | 
|  | 835 | + | 
|  | 836 | +        val &= 0xFFFF; | 
|  | 837 | + | 
|  | 838 | +        DEBUGF("update_fat_entry(%x,%x)",entry,val); | 
|  | 839 | + | 
|  | 840 | +        if (entry==val) | 
|  | 841 | +            panicf(PANIC_KILLUSERTHREADS, "Creating FAT loop: %lx,%lx",entry,val); | 
|  | 842 | + | 
|  | 843 | +        if ( entry < 2 ) | 
|  | 844 | +            panicf(PANIC_KILLUSERTHREADS, "Updating reserved FAT entry %ld.",entry); | 
|  | 845 | + | 
|  | 846 | +        sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true); | 
|  | 847 | +        if (!sec) | 
|  | 848 | +        { | 
|  | 849 | +            DEBUGF( "update_fat_entry() - Could not cache sector %d", sector); | 
|  | 850 | +            return -1; | 
|  | 851 | +        } | 
|  | 852 | + | 
|  | 853 | +        if ( val ) { | 
|  | 854 | +            if (letoh16(sec[offset]) == 0x0000 && fat_bpb->fsinfo.freecount > 0) | 
|  | 855 | +                fat_bpb->fsinfo.freecount--; | 
|  | 856 | +        } | 
|  | 857 | +        else { | 
|  | 858 | +            if (letoh16(sec[offset])) | 
|  | 859 | +                fat_bpb->fsinfo.freecount++; | 
|  | 860 | +        } | 
|  | 861 | + | 
|  | 862 | +        DEBUGF("update_fat_entry: %d free clusters", | 
|  | 863 | +                fat_bpb->fsinfo.freecount); | 
|  | 864 | + | 
|  | 865 | +        sec[offset] = htole16(val); | 
|  | 866 | +        unlock_fat_sector(IF_MV2(fat_bpb,) sector); | 
|  | 867 | +    } | 
|  | 868 | +    else | 
|  | 869 | +#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
|  | 870 | +    { | 
|  | 871 | +        long sector = entry / CLUSTERS_PER_FAT_SECTOR; | 
|  | 872 | +        int offset = entry % CLUSTERS_PER_FAT_SECTOR; | 
|  | 873 | +        unsigned long* sec; | 
|  | 874 | + | 
|  | 875 | +        DEBUGF("update_fat_entry(%lx,%lx)",entry,val); | 
|  | 876 | + | 
|  | 877 | +        if (entry==val) | 
|  | 878 | +            panicf(PANIC_KILLUSERTHREADS, "Creating FAT loop: %lx,%lx",entry,val); | 
|  | 879 | + | 
|  | 880 | +        if ( entry < 2 ) | 
|  | 881 | +            panicf(PANIC_KILLUSERTHREADS, "Updating reserved FAT entry %ld.",entry); | 
|  | 882 | + | 
|  | 883 | +        sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, true); | 
|  | 884 | +        if (!sec) | 
|  | 885 | +        { | 
|  | 886 | +            DEBUGF("update_fat_entry() - Could not cache sector %ld", sector); | 
|  | 887 | +            return -1; | 
|  | 888 | +        } | 
|  | 889 | + | 
|  | 890 | +        if ( val ) { | 
|  | 891 | +            if (!(letoh32(sec[offset]) & 0x0fffffff) && | 
|  | 892 | +                fat_bpb->fsinfo.freecount > 0) | 
|  | 893 | +                fat_bpb->fsinfo.freecount--; | 
|  | 894 | +        } | 
|  | 895 | +        else { | 
|  | 896 | +            if (letoh32(sec[offset]) & 0x0fffffff) | 
|  | 897 | +                fat_bpb->fsinfo.freecount++; | 
|  | 898 | +        } | 
|  | 899 | + | 
|  | 900 | +        DEBUGF("update_fat_entry: %ld free clusters", | 
|  | 901 | +                fat_bpb->fsinfo.freecount); | 
|  | 902 | + | 
|  | 903 | +        /* don't change top 4 bits */ | 
|  | 904 | +        sec[offset] &= htole32(0xf0000000); | 
|  | 905 | +        sec[offset] |= htole32(val & 0x0fffffff); | 
|  | 906 | +        unlock_fat_sector(IF_MV2(fat_bpb,) sector); | 
|  | 907 | +    } | 
|  | 908 | + | 
|  | 909 | +    return 0; | 
|  | 910 | +} | 
|  | 911 | + | 
|  | 912 | +static long read_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry) | 
|  | 913 | +{ | 
|  | 914 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 915 | +#ifndef HAVE_MULTIVOLUME | 
|  | 916 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 917 | +#endif | 
|  | 918 | +    if (fat_bpb->is_fat16) | 
|  | 919 | +    { | 
|  | 920 | +        int sector = entry / CLUSTERS_PER_FAT16_SECTOR; | 
|  | 921 | +        int offset = entry % CLUSTERS_PER_FAT16_SECTOR; | 
|  | 922 | +        unsigned short* sec; | 
|  | 923 | + | 
|  | 924 | +        sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false); | 
|  | 925 | +        if (!sec) | 
|  | 926 | +        { | 
|  | 927 | +            DEBUGF( "read_fat_entry() - Could not cache sector %d", sector); | 
|  | 928 | +            return -1; | 
|  | 929 | +        } | 
|  | 930 | + | 
|  | 931 | +        long val = letoh16(sec[offset]); | 
|  | 932 | +        unlock_fat_sector(IF_MV2(fat_bpb,) sector); | 
|  | 933 | + | 
|  | 934 | +        return val; | 
|  | 935 | +    } | 
|  | 936 | +    else | 
|  | 937 | +#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
|  | 938 | +    { | 
|  | 939 | +        long sector = entry / CLUSTERS_PER_FAT_SECTOR; | 
|  | 940 | +        int offset = entry % CLUSTERS_PER_FAT_SECTOR; | 
|  | 941 | +        unsigned long* sec; | 
|  | 942 | + | 
|  | 943 | +        sec = cache_fat_sector(IF_MV2(fat_bpb,) sector, false); | 
|  | 944 | +        if (!sec) | 
|  | 945 | +        { | 
|  | 946 | +            DEBUGF( "read_fat_entry() - Could not cache sector %ld", sector); | 
|  | 947 | +            return -1; | 
|  | 948 | +        } | 
|  | 949 | + | 
|  | 950 | +        long val = letoh32(sec[offset]) & 0x0fffffff; | 
|  | 951 | +        unlock_fat_sector(IF_MV2(fat_bpb,) sector); | 
|  | 952 | + | 
|  | 953 | +        return val; | 
|  | 954 | +    } | 
|  | 955 | +} | 
|  | 956 | + | 
|  | 957 | +static long get_next_cluster(IF_MV2(struct bpb* fat_bpb,) long cluster) | 
|  | 958 | +{ | 
|  | 959 | +    long next_cluster; | 
|  | 960 | +    long eof_mark = FAT_EOF_MARK; | 
|  | 961 | + | 
|  | 962 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 963 | +#ifndef HAVE_MULTIVOLUME | 
|  | 964 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 965 | +#endif | 
|  | 966 | +    if (fat_bpb->is_fat16) | 
|  | 967 | +    { | 
|  | 968 | +        eof_mark &= 0xFFFF; /* only 16 bit */ | 
|  | 969 | +        if (cluster < 0) /* FAT16 root dir */ | 
|  | 970 | +            return cluster + 1; /* don't use the FAT */ | 
|  | 971 | +    } | 
|  | 972 | +#endif | 
|  | 973 | +    next_cluster = read_fat_entry(IF_MV2(fat_bpb,) cluster); | 
|  | 974 | + | 
|  | 975 | +    /* is this last cluster in chain? */ | 
|  | 976 | +    if ( next_cluster >= eof_mark ) | 
|  | 977 | +        return 0; | 
|  | 978 | +    else | 
|  | 979 | +        return next_cluster; | 
|  | 980 | +} | 
|  | 981 | + | 
|  | 982 | +static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)) | 
|  | 983 | +{ | 
|  | 984 | +#ifndef HAVE_MULTIVOLUME | 
|  | 985 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 986 | +#endif | 
|  | 987 | +    unsigned long* intptr; | 
|  | 988 | +    int rc; | 
|  | 989 | + | 
|  | 990 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 991 | +    if (fat_bpb->is_fat16) | 
|  | 992 | +        return 0; /* FAT16 has no FsInfo */ | 
|  | 993 | +#endif /* #ifdef HAVE_FAT16SUPPORT */ | 
|  | 994 | + | 
|  | 995 | +    /* update fsinfo */ | 
|  | 996 | +    unsigned char* fsinfo = fat_get_sector_buffer(); | 
|  | 997 | +    rc = storage_read_sectors(IF_MD2(fat_bpb->drive,) | 
|  | 998 | +                          fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1,fsinfo); | 
|  | 999 | +    if (rc < 0) | 
|  | 1000 | +    { | 
|  | 1001 | +        fat_release_sector_buffer(); | 
|  | 1002 | +        DEBUGF( "update_fsinfo() - Couldn't read FSInfo (error code %d)", rc); | 
|  | 1003 | +        return rc * 10 - 1; | 
|  | 1004 | +    } | 
|  | 1005 | +    intptr = (long*)&(fsinfo[FSINFO_FREECOUNT]); | 
|  | 1006 | +    *intptr = htole32(fat_bpb->fsinfo.freecount); | 
|  | 1007 | + | 
|  | 1008 | +    intptr = (long*)&(fsinfo[FSINFO_NEXTFREE]); | 
|  | 1009 | +    *intptr = htole32(fat_bpb->fsinfo.nextfree); | 
|  | 1010 | + | 
|  | 1011 | +    rc = storage_write_sectors(IF_MD2(fat_bpb->drive,) | 
|  | 1012 | +                           fat_bpb->startsector + fat_bpb->bpb_fsinfo,1,fsinfo); | 
|  | 1013 | +    fat_release_sector_buffer(); | 
|  | 1014 | +    if (rc < 0) | 
|  | 1015 | +    { | 
|  | 1016 | +        DEBUGF( "update_fsinfo() - Couldn't write FSInfo (error code %d)", rc); | 
|  | 1017 | +        return rc * 10 - 2; | 
|  | 1018 | +    } | 
|  | 1019 | + | 
|  | 1020 | +    return 0; | 
|  | 1021 | +} | 
|  | 1022 | + | 
|  | 1023 | +static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb)) | 
|  | 1024 | +{ | 
|  | 1025 | +    int i; | 
|  | 1026 | +    int rc; | 
|  | 1027 | +    unsigned char *sec; | 
|  | 1028 | +    DEBUGF("flush_fat()"); | 
|  | 1029 | + | 
|  | 1030 | +    mutex_lock(&cache_mutex, TIMEOUT_BLOCK); | 
|  | 1031 | +    for(i = 0;i < FAT_CACHE_SIZE;i++) | 
|  | 1032 | +    { | 
|  | 1033 | +        struct fat_cache_entry *fce = &fat_cache[i]; | 
|  | 1034 | +        if(fce->valid | 
|  | 1035 | +#ifdef HAVE_MULTIVOLUME | 
|  | 1036 | +            && fce->fat_vol == fat_bpb | 
|  | 1037 | +#endif | 
|  | 1038 | +            && fce->dirty) | 
|  | 1039 | +        { | 
|  | 1040 | +            sec = fat_cache_sectors[i]; | 
|  | 1041 | +            flush_fat_sector(fce, sec); | 
|  | 1042 | +        } | 
|  | 1043 | +    } | 
|  | 1044 | +    mutex_unlock(&cache_mutex); | 
|  | 1045 | + | 
|  | 1046 | +    rc = update_fsinfo(IF_MV(fat_bpb)); | 
|  | 1047 | +    if (rc < 0) | 
|  | 1048 | +        return rc * 10 - 3; | 
|  | 1049 | + | 
|  | 1050 | +    return 0; | 
|  | 1051 | +} | 
|  | 1052 | + | 
|  | 1053 | +static void fat_time(unsigned short* date, | 
|  | 1054 | +                     unsigned short* time, | 
|  | 1055 | +                     unsigned short* tenth ) | 
|  | 1056 | +{ | 
|  | 1057 | +#if CONFIG_RTC | 
|  | 1058 | +    struct tm* tm = get_time(); | 
|  | 1059 | + | 
|  | 1060 | +    if (date) | 
|  | 1061 | +        *date = ((tm->tm_year - 80) << 9) | | 
|  | 1062 | +            ((tm->tm_mon + 1) << 5) | | 
|  | 1063 | +            tm->tm_mday; | 
|  | 1064 | + | 
|  | 1065 | +    if (time) | 
|  | 1066 | +        *time = (tm->tm_hour << 11) | | 
|  | 1067 | +            (tm->tm_min << 5) | | 
|  | 1068 | +            (tm->tm_sec >> 1); | 
|  | 1069 | + | 
|  | 1070 | +    if (tenth) | 
|  | 1071 | +        *tenth = (tm->tm_sec & 1) * 100; | 
|  | 1072 | +#else | 
|  | 1073 | + | 
|  | 1074 | +    if (date) *date = 0; | 
|  | 1075 | +    if (time) *time = 0; | 
|  | 1076 | +    if (tenth) *tenth = 0; | 
|  | 1077 | + | 
|  | 1078 | +#endif /* CONFIG_RTC */ | 
|  | 1079 | +} | 
|  | 1080 | + | 
|  | 1081 | +static int write_long_name(struct fat_file* file, | 
|  | 1082 | +                           unsigned int firstentry, | 
|  | 1083 | +                           unsigned int numentries, | 
|  | 1084 | +                           const unsigned char* name, | 
|  | 1085 | +                           const unsigned char* shortname, | 
|  | 1086 | +                           bool is_directory) | 
|  | 1087 | +{ | 
|  | 1088 | +    unsigned char* entry; | 
|  | 1089 | +    unsigned int idx = firstentry % DIR_ENTRIES_PER_SECTOR; | 
|  | 1090 | +    unsigned int sector = firstentry / DIR_ENTRIES_PER_SECTOR; | 
|  | 1091 | +    unsigned char chksum = 0; | 
|  | 1092 | +    unsigned int i, j=0; | 
|  | 1093 | +    unsigned int nameidx=0, namelen = strlen(name); | 
|  | 1094 | +    int rc; | 
|  | 1095 | +    unsigned short name_utf16[namelen + 1]; | 
|  | 1096 | + | 
|  | 1097 | +    DEBUGF("write_long_name(file:%lx, first:%d, num:%d, name:%s)", | 
|  | 1098 | +            file->firstcluster, firstentry, numentries, name); | 
|  | 1099 | + | 
|  | 1100 | +    rc = fat_seek(file, sector); | 
|  | 1101 | +    if (rc<0) | 
|  | 1102 | +        return rc * 10 - 1; | 
|  | 1103 | + | 
|  | 1104 | +    unsigned char* buf = fat_get_sector_buffer(); | 
|  | 1105 | +    rc = fat_readwrite(file, 1, buf, false); | 
|  | 1106 | +    if (rc<1) | 
|  | 1107 | +    { | 
|  | 1108 | +        fat_release_sector_buffer(); | 
|  | 1109 | +        return rc * 10 - 2; | 
|  | 1110 | +    } | 
|  | 1111 | + | 
|  | 1112 | +    /* calculate shortname checksum */ | 
|  | 1113 | +    for (i=11; i>0; i--) | 
|  | 1114 | +        chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++]; | 
|  | 1115 | + | 
|  | 1116 | +    /* calc position of last name segment */ | 
|  | 1117 | +    if ( namelen > NAME_BYTES_PER_ENTRY ) | 
|  | 1118 | +        for (nameidx=0; | 
|  | 1119 | +             nameidx < (namelen - NAME_BYTES_PER_ENTRY); | 
|  | 1120 | +             nameidx += NAME_BYTES_PER_ENTRY); | 
|  | 1121 | + | 
|  | 1122 | +    /* we need to convert the name first    */ | 
|  | 1123 | +    /* since it is written in reverse order */ | 
|  | 1124 | +    for (i = 0; i <= namelen; i++) | 
|  | 1125 | +        name_utf16[i] = *(name++); | 
|  | 1126 | + | 
|  | 1127 | +    for (i=0; i < numentries; i++) { | 
|  | 1128 | +        /* new sector? */ | 
|  | 1129 | +        if ( idx >= DIR_ENTRIES_PER_SECTOR ) { | 
|  | 1130 | +            /* update current sector */ | 
|  | 1131 | +            rc = fat_seek(file, sector); | 
|  | 1132 | +            if (rc<0) | 
|  | 1133 | +            { | 
|  | 1134 | +                fat_release_sector_buffer(); | 
|  | 1135 | +                return rc * 10 - 3; | 
|  | 1136 | +            } | 
|  | 1137 | + | 
|  | 1138 | +            rc = fat_readwrite(file, 1, buf, true); | 
|  | 1139 | +            if (rc<1) | 
|  | 1140 | +            { | 
|  | 1141 | +                fat_release_sector_buffer(); | 
|  | 1142 | +                return rc * 10 - 4; | 
|  | 1143 | +            } | 
|  | 1144 | + | 
|  | 1145 | +            /* read next sector */ | 
|  | 1146 | +            rc = fat_readwrite(file, 1, buf, false); | 
|  | 1147 | +            if (rc<0) { | 
|  | 1148 | +                fat_release_sector_buffer(); | 
|  | 1149 | +                DEBUGF("Failed writing new sector: %d",rc); | 
|  | 1150 | +                return rc * 10 - 5; | 
|  | 1151 | +            } | 
|  | 1152 | +            if (rc==0) | 
|  | 1153 | +                /* end of dir */ | 
|  | 1154 | +                memset(buf, 0, SECTOR_SIZE); | 
|  | 1155 | + | 
|  | 1156 | +            sector++; | 
|  | 1157 | +            idx = 0; | 
|  | 1158 | +        } | 
|  | 1159 | + | 
|  | 1160 | +        entry = buf + idx * DIR_ENTRY_SIZE; | 
|  | 1161 | + | 
|  | 1162 | +        /* verify this entry is free */ | 
|  | 1163 | +        if (entry[0] && entry[0] != 0xe5 ) | 
|  | 1164 | +        { | 
|  | 1165 | +            fat_release_sector_buffer(); | 
|  | 1166 | +            panicf(PANIC_KILLUSERTHREADS, "Dir entry %d in sector %x is not free! " | 
|  | 1167 | +                   "%02x %02x %02x %02x", | 
|  | 1168 | +                   idx, sector, | 
|  | 1169 | +                   entry[0], entry[1], entry[2], entry[3]); | 
|  | 1170 | +        } | 
|  | 1171 | + | 
|  | 1172 | +        memset(entry, 0, DIR_ENTRY_SIZE); | 
|  | 1173 | +        if ( i+1 < numentries ) { | 
|  | 1174 | +            /* longname entry */ | 
|  | 1175 | +            unsigned int k, l = nameidx; | 
|  | 1176 | + | 
|  | 1177 | +            entry[FATLONG_ORDER] = numentries-i-1; | 
|  | 1178 | +            if (i==0) { | 
|  | 1179 | +                /* mark this as last long entry */ | 
|  | 1180 | +                entry[FATLONG_ORDER] |= FATLONG_LAST_LONG_ENTRY; | 
|  | 1181 | + | 
|  | 1182 | +                /* pad name with 0xffff  */ | 
|  | 1183 | +                for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | 
|  | 1184 | +                for (k=14; k<26; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | 
|  | 1185 | +                for (k=28; k<32; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | 
|  | 1186 | +            }; | 
|  | 1187 | +            /* set name */ | 
|  | 1188 | +            for (k=0; k<5 && l <= namelen; k++) { | 
|  | 1189 | +                entry[k*2 + 1] = (unsigned char)(name_utf16[l] & 0xff); | 
|  | 1190 | +                entry[k*2 + 2] = (unsigned char)(name_utf16[l++] >> 8); | 
|  | 1191 | +            } | 
|  | 1192 | +            for (k=0; k<6 && l <= namelen; k++) { | 
|  | 1193 | +                entry[k*2 + 14] = (unsigned char)(name_utf16[l] & 0xff); | 
|  | 1194 | +                entry[k*2 + 15] = (unsigned char)(name_utf16[l++] >> 8); | 
|  | 1195 | +            } | 
|  | 1196 | +            for (k=0; k<2 && l <= namelen; k++) { | 
|  | 1197 | +                entry[k*2 + 28] = (unsigned char)(name_utf16[l] & 0xff); | 
|  | 1198 | +                entry[k*2 + 29] = (unsigned char)(name_utf16[l++] >> 8); | 
|  | 1199 | +            } | 
|  | 1200 | + | 
|  | 1201 | +            entry[FATDIR_ATTR] = FAT_ATTR_LONG_NAME; | 
|  | 1202 | +            entry[FATDIR_FSTCLUSLO] = 0; | 
|  | 1203 | +            entry[FATLONG_TYPE] = 0; | 
|  | 1204 | +            entry[FATLONG_CHKSUM] = chksum; | 
|  | 1205 | +            DEBUGF("Longname entry %d (%d): %s", idx, nameidx, name+nameidx); | 
|  | 1206 | +        } | 
|  | 1207 | +        else { | 
|  | 1208 | +            /* shortname entry */ | 
|  | 1209 | +            unsigned short date=0, time=0, tenth=0; | 
|  | 1210 | +            DEBUGF("Shortname entry: %s", shortname); | 
|  | 1211 | +            memcpy(entry + FATDIR_NAME, shortname, 11); | 
|  | 1212 | +            entry[FATDIR_ATTR] = is_directory?FAT_ATTR_DIRECTORY:0; | 
|  | 1213 | +            entry[FATDIR_NTRES] = 0; | 
|  | 1214 | + | 
|  | 1215 | +            fat_time(&date, &time, &tenth); | 
|  | 1216 | +            entry[FATDIR_CRTTIMETENTH] = tenth; | 
|  | 1217 | +            *(unsigned short*)(entry + FATDIR_CRTTIME) = htole16(time); | 
|  | 1218 | +            *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time); | 
|  | 1219 | +            *(unsigned short*)(entry + FATDIR_CRTDATE) = htole16(date); | 
|  | 1220 | +            *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date); | 
|  | 1221 | +            *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date); | 
|  | 1222 | +        } | 
|  | 1223 | +        idx++; | 
|  | 1224 | +        nameidx -= NAME_BYTES_PER_ENTRY; | 
|  | 1225 | +    } | 
|  | 1226 | + | 
|  | 1227 | +    /* update last sector */ | 
|  | 1228 | +    rc = fat_seek(file, sector); | 
|  | 1229 | +    if (rc<0) | 
|  | 1230 | +    { | 
|  | 1231 | +        fat_release_sector_buffer(); | 
|  | 1232 | +        return rc * 10 - 6; | 
|  | 1233 | +    } | 
|  | 1234 | + | 
|  | 1235 | +    rc = fat_readwrite(file, 1, buf, true); | 
|  | 1236 | +    fat_release_sector_buffer(); | 
|  | 1237 | +    if (rc<1) | 
|  | 1238 | +        return rc * 10 - 7; | 
|  | 1239 | + | 
|  | 1240 | +    DEBUGF("write_long_name: success"); | 
|  | 1241 | +    return 0; | 
|  | 1242 | +} | 
|  | 1243 | + | 
|  | 1244 | +static int fat_checkname(const unsigned char* newname) | 
|  | 1245 | +{ | 
|  | 1246 | +    static const char invalid_chars[] = "\"*/:<>?\\|"; | 
|  | 1247 | +    int len = strlen(newname); | 
|  | 1248 | +    /* More sanity checks are probably needed */ | 
|  | 1249 | +    if (len > 255 || newname[len - 1] == '.') | 
|  | 1250 | +    { | 
|  | 1251 | +        return -1; | 
|  | 1252 | +    } | 
|  | 1253 | +    while (*newname) | 
|  | 1254 | +    { | 
|  | 1255 | +        if (*newname < ' ' || strchr(invalid_chars, *newname) != NULL) | 
|  | 1256 | +            return -1; | 
|  | 1257 | +        newname++; | 
|  | 1258 | +    } | 
|  | 1259 | +    /* check trailing space(s) */ | 
|  | 1260 | +    if(*(--newname) == ' ') | 
|  | 1261 | +        return -1; | 
|  | 1262 | + | 
|  | 1263 | +    return 0; | 
|  | 1264 | +} | 
|  | 1265 | + | 
|  | 1266 | +static int add_dir_entry(struct fat_dir* dir, | 
|  | 1267 | +                         struct fat_file* file, | 
|  | 1268 | +                         const char* name, | 
|  | 1269 | +                         bool is_directory, | 
|  | 1270 | +                         bool dotdir) | 
|  | 1271 | +{ | 
|  | 1272 | +#ifdef HAVE_MULTIVOLUME | 
|  | 1273 | +    struct bpb* fat_bpb = &fat_bpbs[dir->file.volume]; | 
|  | 1274 | +#else | 
|  | 1275 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 1276 | +#endif | 
|  | 1277 | +    unsigned char shortname[12]; | 
|  | 1278 | +    int rc; | 
|  | 1279 | +    unsigned int sector; | 
|  | 1280 | +    bool done = false; | 
|  | 1281 | +    int entries_needed, entries_found = 0; | 
|  | 1282 | +    int firstentry; | 
|  | 1283 | + | 
|  | 1284 | +    DEBUGF( "add_dir_entry(%s,%lx)", | 
|  | 1285 | +             name, file->firstcluster); | 
|  | 1286 | + | 
|  | 1287 | +    /* Don't check dotdirs name for validity */ | 
|  | 1288 | +    if (dotdir == false) { | 
|  | 1289 | +        rc = fat_checkname(name); | 
|  | 1290 | +        if (rc < 0) { | 
|  | 1291 | +            /* filename is invalid */ | 
|  | 1292 | +            return rc * 10 - 1; | 
|  | 1293 | +        } | 
|  | 1294 | +    } | 
|  | 1295 | + | 
|  | 1296 | +#ifdef HAVE_MULTIVOLUME | 
|  | 1297 | +    file->volume = dir->file.volume; /* inherit the volume, to make sure */ | 
|  | 1298 | +#endif | 
|  | 1299 | + | 
|  | 1300 | +    /* The "." and ".." directory entries must not be long names */ | 
|  | 1301 | +    if(dotdir) { | 
|  | 1302 | +        int i; | 
|  | 1303 | +        strlcpy(shortname, name, 12); | 
|  | 1304 | +        for(i = strlen(shortname); i < 12; i++) | 
|  | 1305 | +            shortname[i] = ' '; | 
|  | 1306 | + | 
|  | 1307 | +        entries_needed = 1; | 
|  | 1308 | +    } else { | 
|  | 1309 | +        create_dos_name(name, shortname); | 
|  | 1310 | + | 
|  | 1311 | +        /* one dir entry needed for every 13 bytes of filename, | 
|  | 1312 | +           plus one entry for the short name */ | 
|  | 1313 | +        entries_needed = (strlen(name) + (NAME_BYTES_PER_ENTRY-1)) | 
|  | 1314 | +                         / NAME_BYTES_PER_ENTRY + 1; | 
|  | 1315 | +    } | 
|  | 1316 | + | 
|  | 1317 | +    unsigned char* buf = fat_get_sector_buffer(); | 
|  | 1318 | +  restart: | 
|  | 1319 | +    firstentry = -1; | 
|  | 1320 | + | 
|  | 1321 | +    rc = fat_seek(&dir->file, 0); | 
|  | 1322 | +    if (rc < 0) | 
|  | 1323 | +    { | 
|  | 1324 | +        fat_release_sector_buffer(); | 
|  | 1325 | +        return rc * 10 - 2; | 
|  | 1326 | +    } | 
|  | 1327 | + | 
|  | 1328 | +    /* step 1: search for free entries and check for duplicate shortname */ | 
|  | 1329 | +    for (sector = 0; !done; sector++) | 
|  | 1330 | +    { | 
|  | 1331 | +        unsigned int i; | 
|  | 1332 | + | 
|  | 1333 | +        rc = fat_readwrite(&dir->file, 1, buf, false); | 
|  | 1334 | +        if (rc < 0) { | 
|  | 1335 | +            fat_release_sector_buffer(); | 
|  | 1336 | +            DEBUGF( "add_dir_entry() - Couldn't read dir" | 
|  | 1337 | +                    " (error code %d)", rc); | 
|  | 1338 | +            return rc * 10 - 3; | 
|  | 1339 | +        } | 
|  | 1340 | + | 
|  | 1341 | +        if (rc == 0) { /* current end of dir reached */ | 
|  | 1342 | +            DEBUGF("End of dir on cluster boundary"); | 
|  | 1343 | +            break; | 
|  | 1344 | +        } | 
|  | 1345 | + | 
|  | 1346 | +        /* look for free slots */ | 
|  | 1347 | +        for (i = 0; i < DIR_ENTRIES_PER_SECTOR; i++) | 
|  | 1348 | +        { | 
|  | 1349 | +            switch (buf[i * DIR_ENTRY_SIZE]) { | 
|  | 1350 | +              case 0: | 
|  | 1351 | +                entries_found += DIR_ENTRIES_PER_SECTOR - i; | 
|  | 1352 | +                DEBUGF("Found end of dir %d", | 
|  | 1353 | +                        sector * DIR_ENTRIES_PER_SECTOR + i); | 
|  | 1354 | +                i = DIR_ENTRIES_PER_SECTOR - 1; | 
|  | 1355 | +                done = true; | 
|  | 1356 | +                break; | 
|  | 1357 | + | 
|  | 1358 | +              case 0xe5: | 
|  | 1359 | +                entries_found++; | 
|  | 1360 | +                DEBUGF("Found free entry %d (%d/%d)", | 
|  | 1361 | +                        sector * DIR_ENTRIES_PER_SECTOR + i, | 
|  | 1362 | +                        entries_found, entries_needed); | 
|  | 1363 | +                break; | 
|  | 1364 | + | 
|  | 1365 | +              default: | 
|  | 1366 | +                entries_found = 0; | 
|  | 1367 | + | 
|  | 1368 | +                /* check that our intended shortname doesn't already exist */ | 
|  | 1369 | +                if (!strncmp(shortname, buf + i * DIR_ENTRY_SIZE, 11)) { | 
|  | 1370 | +                    /* shortname exists already, make a new one */ | 
|  | 1371 | +                    randomize_dos_name(shortname); | 
|  | 1372 | +                    DEBUGF("Duplicate shortname, changing to %s", | 
|  | 1373 | +                            shortname); | 
|  | 1374 | + | 
|  | 1375 | +                    /* name has changed, we need to restart search */ | 
|  | 1376 | +                    goto restart; | 
|  | 1377 | +                } | 
|  | 1378 | +                break; | 
|  | 1379 | +            } | 
|  | 1380 | +            if (firstentry < 0 && (entries_found >= entries_needed)) | 
|  | 1381 | +                firstentry = sector * DIR_ENTRIES_PER_SECTOR + i + 1 | 
|  | 1382 | +                             - entries_found; | 
|  | 1383 | +        } | 
|  | 1384 | +    } | 
|  | 1385 | + | 
|  | 1386 | +    /* step 2: extend the dir if necessary */ | 
|  | 1387 | +    if (firstentry < 0) | 
|  | 1388 | +    { | 
|  | 1389 | +        DEBUGF("Adding new sector(s) to dir"); | 
|  | 1390 | +        rc = fat_seek(&dir->file, sector); | 
|  | 1391 | +        if (rc < 0) | 
|  | 1392 | +        { | 
|  | 1393 | +            fat_release_sector_buffer(); | 
|  | 1394 | +            return rc * 10 - 4; | 
|  | 1395 | +        } | 
|  | 1396 | +        memset(buf, 0, SECTOR_SIZE); | 
|  | 1397 | + | 
|  | 1398 | +        /* we must clear whole clusters */ | 
|  | 1399 | +        for (; (entries_found < entries_needed) || | 
|  | 1400 | +               (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); sector++) | 
|  | 1401 | +        { | 
|  | 1402 | +            if (sector >= (65536/DIR_ENTRIES_PER_SECTOR)) | 
|  | 1403 | +            { | 
|  | 1404 | +                fat_release_sector_buffer(); | 
|  | 1405 | +                return -5; /* dir too large -- FAT specification */ | 
|  | 1406 | +            } | 
|  | 1407 | + | 
|  | 1408 | +            rc = fat_readwrite(&dir->file, 1, buf, true); | 
|  | 1409 | +            if (rc < 1)  /* No more room or something went wrong */ | 
|  | 1410 | +            { | 
|  | 1411 | +                fat_release_sector_buffer(); | 
|  | 1412 | +                return rc * 10 - 6; | 
|  | 1413 | +            } | 
|  | 1414 | + | 
|  | 1415 | +            entries_found += DIR_ENTRIES_PER_SECTOR; | 
|  | 1416 | +        } | 
|  | 1417 | + | 
|  | 1418 | +        firstentry = sector * DIR_ENTRIES_PER_SECTOR - entries_found; | 
|  | 1419 | +    } | 
|  | 1420 | +    fat_release_sector_buffer(); | 
|  | 1421 | + | 
|  | 1422 | +    /* step 3: add entry */ | 
|  | 1423 | +    sector = firstentry / DIR_ENTRIES_PER_SECTOR; | 
|  | 1424 | +    DEBUGF("Adding longname to entry %d in sector %d", | 
|  | 1425 | +            firstentry, sector); | 
|  | 1426 | + | 
|  | 1427 | +    rc = write_long_name(&dir->file, firstentry, | 
|  | 1428 | +                         entries_needed, name, shortname, is_directory); | 
|  | 1429 | +    if (rc < 0) | 
|  | 1430 | +        return rc * 10 - 7; | 
|  | 1431 | + | 
|  | 1432 | +    /* remember where the shortname dir entry is located */ | 
|  | 1433 | +    file->direntry = firstentry + entries_needed - 1; | 
|  | 1434 | +    file->direntries = entries_needed; | 
|  | 1435 | +    file->dircluster = dir->file.firstcluster; | 
|  | 1436 | +    DEBUGF("Added new dir entry %d, using %d slots.", | 
|  | 1437 | +            file->direntry, file->direntries); | 
|  | 1438 | + | 
|  | 1439 | +    return 0; | 
|  | 1440 | +} | 
|  | 1441 | + | 
|  | 1442 | +static unsigned char char2dos(unsigned char c, int* randomize) | 
|  | 1443 | +{ | 
|  | 1444 | +    static const char invalid_chars[] = "\"*+,./:;<=>?[\\]|"; | 
|  | 1445 | + | 
|  | 1446 | +    if (c <= 0x20) | 
|  | 1447 | +        c = 0;   /* Illegal char, remove */ | 
|  | 1448 | +    else if (strchr(invalid_chars, c) != NULL) | 
|  | 1449 | +    { | 
|  | 1450 | +        /* Illegal char, replace */ | 
|  | 1451 | +        c = '_'; | 
|  | 1452 | +        *randomize = 1; /* as per FAT spec */ | 
|  | 1453 | +    } | 
|  | 1454 | +    else | 
|  | 1455 | +        c = toupper(c); | 
|  | 1456 | + | 
|  | 1457 | +    return c; | 
|  | 1458 | +} | 
|  | 1459 | + | 
|  | 1460 | +static void create_dos_name(const unsigned char *name, unsigned char *newname) | 
|  | 1461 | +{ | 
|  | 1462 | +    int i; | 
|  | 1463 | +    unsigned char *ext; | 
|  | 1464 | +    int randomize = 0; | 
|  | 1465 | + | 
|  | 1466 | +    /* Find extension part */ | 
|  | 1467 | +    ext = strrchr(name, '.'); | 
|  | 1468 | +    if (ext == name)         /* handle .dotnames */ | 
|  | 1469 | +        ext = NULL; | 
|  | 1470 | + | 
|  | 1471 | +    /* needs to randomize? */ | 
|  | 1472 | +    if((ext && (strlen(ext) > 4)) || | 
|  | 1473 | +       ((ext ? (unsigned int)(ext-name) : strlen(name)) > 8) ) | 
|  | 1474 | +        randomize = 1; | 
|  | 1475 | + | 
|  | 1476 | +    /* Name part */ | 
|  | 1477 | +    for (i = 0; *name && (!ext || name < ext) && (i < 8); name++) | 
|  | 1478 | +    { | 
|  | 1479 | +        unsigned char c = char2dos(*name, &randomize); | 
|  | 1480 | +        if (c) | 
|  | 1481 | +            newname[i++] = c; | 
|  | 1482 | +    } | 
|  | 1483 | + | 
|  | 1484 | +    /* Pad both name and extension */ | 
|  | 1485 | +    while (i < 11) | 
|  | 1486 | +        newname[i++] = ' '; | 
|  | 1487 | + | 
|  | 1488 | +    if (newname[0] == 0xe5) /* Special kanji character */ | 
|  | 1489 | +        newname[0] = 0x05; | 
|  | 1490 | + | 
|  | 1491 | +    if (ext) | 
|  | 1492 | +    {   /* Extension part */ | 
|  | 1493 | +        ext++; | 
|  | 1494 | +        for (i = 8; *ext && (i < 11); ext++) | 
|  | 1495 | +        { | 
|  | 1496 | +            unsigned char c = char2dos(*ext, &randomize); | 
|  | 1497 | +            if (c) | 
|  | 1498 | +                newname[i++] = c; | 
|  | 1499 | +        } | 
|  | 1500 | +    } | 
|  | 1501 | + | 
|  | 1502 | +    if(randomize) | 
|  | 1503 | +        randomize_dos_name(newname); | 
|  | 1504 | +} | 
|  | 1505 | + | 
|  | 1506 | +static void randomize_dos_name(unsigned char *name) | 
|  | 1507 | +{ | 
|  | 1508 | +    unsigned char* tilde = NULL;    /* ~ location */ | 
|  | 1509 | +    unsigned char* lastpt = NULL;   /* last point of filename */ | 
|  | 1510 | +    unsigned char* nameptr = name;  /* working copy of name pointer */ | 
|  | 1511 | +    unsigned char num[9];           /* holds number as string */ | 
|  | 1512 | +    int i = 0; | 
|  | 1513 | +    int cnt = 1; | 
|  | 1514 | +    int numlen; | 
|  | 1515 | +    int offset; | 
|  | 1516 | + | 
|  | 1517 | +    while(i++ < 8) | 
|  | 1518 | +    { | 
|  | 1519 | +        /* hunt for ~ and where to put it */ | 
|  | 1520 | +        if((!tilde) && (*nameptr == '~')) | 
|  | 1521 | +            tilde = nameptr; | 
|  | 1522 | +        if((!lastpt) && ((*nameptr == ' ' || *nameptr == '~'))) | 
|  | 1523 | +            lastpt = nameptr; | 
|  | 1524 | +        nameptr++; | 
|  | 1525 | +    } | 
|  | 1526 | +    if(tilde) | 
|  | 1527 | +    { | 
|  | 1528 | +        /* extract current count and increment */ | 
|  | 1529 | +        memcpy(num,tilde+1,7-(unsigned int)(tilde-name)); | 
|  | 1530 | +        num[7-(unsigned int)(tilde-name)] = 0; | 
|  | 1531 | +        cnt = atoi(num) + 1; | 
|  | 1532 | +    } | 
|  | 1533 | +    cnt %= 10000000; /* protection */ | 
|  | 1534 | +    snprintf(num, 9, "~%d", cnt);   /* allow room for trailing zero */ | 
|  | 1535 | +    numlen = strlen(num);           /* required space */ | 
|  | 1536 | +    offset = (unsigned int)(lastpt ? lastpt - name : 8); /* prev startpoint */ | 
|  | 1537 | +    if(offset > (8-numlen)) offset = 8-numlen;  /* correct for new numlen */ | 
|  | 1538 | + | 
|  | 1539 | +    memcpy(&name[offset], num, numlen); | 
|  | 1540 | + | 
|  | 1541 | +    /* in special case of counter overflow: pad with spaces */ | 
|  | 1542 | +    for(offset = offset+numlen; offset < 8; offset++) | 
|  | 1543 | +        name[offset] = ' '; | 
|  | 1544 | +} | 
|  | 1545 | + | 
|  | 1546 | +static int update_short_entry( struct fat_file* file, long size, int attr ) | 
|  | 1547 | +{ | 
|  | 1548 | +    int sector = file->direntry / DIR_ENTRIES_PER_SECTOR; | 
|  | 1549 | +    unsigned long* sizeptr; | 
|  | 1550 | +    unsigned short* clusptr; | 
|  | 1551 | +    struct fat_file dir; | 
|  | 1552 | +    int rc; | 
|  | 1553 | + | 
|  | 1554 | +    DEBUGF("update_file_size(cluster:%lx entry:%d size:%ld)", | 
|  | 1555 | +            file->firstcluster, file->direntry, size); | 
|  | 1556 | + | 
|  | 1557 | +    /* create a temporary file handle for the dir holding this file */ | 
|  | 1558 | +    rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL); | 
|  | 1559 | +    if (rc < 0) | 
|  | 1560 | +        return rc * 10 - 1; | 
|  | 1561 | + | 
|  | 1562 | +    rc = fat_seek( &dir, sector ); | 
|  | 1563 | +    if (rc<0) | 
|  | 1564 | +        return rc * 10 - 2; | 
|  | 1565 | + | 
|  | 1566 | +    unsigned char* buf = fat_get_sector_buffer(); | 
|  | 1567 | +    unsigned char* entry = | 
|  | 1568 | +        buf + DIR_ENTRY_SIZE * (file->direntry % DIR_ENTRIES_PER_SECTOR); | 
|  | 1569 | +    rc = fat_readwrite(&dir, 1, buf, false); | 
|  | 1570 | +    if (rc < 1) | 
|  | 1571 | +    { | 
|  | 1572 | +        fat_release_sector_buffer(); | 
|  | 1573 | +        return rc * 10 - 3; | 
|  | 1574 | +    } | 
|  | 1575 | + | 
|  | 1576 | +    if (!entry[0] || entry[0] == 0xe5) | 
|  | 1577 | +    { | 
|  | 1578 | +        fat_release_sector_buffer(); | 
|  | 1579 | +        panicf(PANIC_KILLUSERTHREADS, "Updating size on empty dir entry %d", file->direntry); | 
|  | 1580 | +    } | 
|  | 1581 | + | 
|  | 1582 | +    entry[FATDIR_ATTR] = attr & 0xFF; | 
|  | 1583 | + | 
|  | 1584 | +    clusptr = (short*)(entry + FATDIR_FSTCLUSHI); | 
|  | 1585 | +    *clusptr = htole16(file->firstcluster >> 16); | 
|  | 1586 | + | 
|  | 1587 | +    clusptr = (short*)(entry + FATDIR_FSTCLUSLO); | 
|  | 1588 | +    *clusptr = htole16(file->firstcluster & 0xffff); | 
|  | 1589 | + | 
|  | 1590 | +    sizeptr = (long*)(entry + FATDIR_FILESIZE); | 
|  | 1591 | +    *sizeptr = htole32(size); | 
|  | 1592 | + | 
|  | 1593 | +    { | 
|  | 1594 | +#if CONFIG_RTC | 
|  | 1595 | +        unsigned short time = 0; | 
|  | 1596 | +        unsigned short date = 0; | 
|  | 1597 | +#else | 
|  | 1598 | +        /* get old time to increment from */ | 
|  | 1599 | +        unsigned short time = htole16(*(unsigned short*)(entry+FATDIR_WRTTIME)); | 
|  | 1600 | +        unsigned short date = htole16(*(unsigned short*)(entry+FATDIR_WRTDATE)); | 
|  | 1601 | +#endif | 
|  | 1602 | +        fat_time(&date, &time, NULL); | 
|  | 1603 | +        *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time); | 
|  | 1604 | +        *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date); | 
|  | 1605 | +        *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date); | 
|  | 1606 | +    } | 
|  | 1607 | + | 
|  | 1608 | +    rc = fat_seek( &dir, sector ); | 
|  | 1609 | +    if (rc < 0) | 
|  | 1610 | +    { | 
|  | 1611 | +        fat_release_sector_buffer(); | 
|  | 1612 | +        return rc * 10 - 4; | 
|  | 1613 | +    } | 
|  | 1614 | + | 
|  | 1615 | +    rc = fat_readwrite(&dir, 1, buf, true); | 
|  | 1616 | +    fat_release_sector_buffer(); | 
|  | 1617 | +    if (rc < 1) | 
|  | 1618 | +        return rc * 10 - 5; | 
|  | 1619 | + | 
|  | 1620 | +    return 0; | 
|  | 1621 | +} | 
|  | 1622 | + | 
|  | 1623 | +static int parse_direntry(struct fat_direntry *de, const unsigned char *buf) | 
|  | 1624 | +{ | 
|  | 1625 | +    int i=0,j=0; | 
|  | 1626 | +    unsigned char c; | 
|  | 1627 | +    bool lowercase; | 
|  | 1628 | + | 
|  | 1629 | +    memset(de, 0, sizeof(struct fat_direntry)); | 
|  | 1630 | +    de->attr = buf[FATDIR_ATTR]; | 
|  | 1631 | +    de->crttimetenth = buf[FATDIR_CRTTIMETENTH]; | 
|  | 1632 | +    de->crtdate = BYTES2INT16(buf,FATDIR_CRTDATE); | 
|  | 1633 | +    de->crttime = BYTES2INT16(buf,FATDIR_CRTTIME); | 
|  | 1634 | +    de->wrtdate = BYTES2INT16(buf,FATDIR_WRTDATE); | 
|  | 1635 | +    de->wrttime = BYTES2INT16(buf,FATDIR_WRTTIME); | 
|  | 1636 | +    de->filesize = BYTES2INT32(buf,FATDIR_FILESIZE); | 
|  | 1637 | +    de->firstcluster = ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSLO)) | | 
|  | 1638 | +        ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSHI) << 16); | 
|  | 1639 | +    /* The double cast is to prevent a sign-extension to be done on CalmRISC16. | 
|  | 1640 | +       (the result of the shift is always considered signed) */ | 
|  | 1641 | + | 
|  | 1642 | +    /* fix the name */ | 
|  | 1643 | +    lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_NAME); | 
|  | 1644 | +    c = buf[FATDIR_NAME]; | 
|  | 1645 | +    if (c == 0x05)  /* special kanji char */ | 
|  | 1646 | +        c = 0xe5; | 
|  | 1647 | +    i = 0; | 
|  | 1648 | +    while (c != ' ') { | 
|  | 1649 | +        de->name[j++] = lowercase ? tolower(c) : c; | 
|  | 1650 | +        if (++i >= 8) | 
|  | 1651 | +            break; | 
|  | 1652 | +        c = buf[FATDIR_NAME+i]; | 
|  | 1653 | +    } | 
|  | 1654 | +    if (buf[FATDIR_NAME+8] != ' ') { | 
|  | 1655 | +        lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_EXT); | 
|  | 1656 | +        de->name[j++] = '.'; | 
|  | 1657 | +        for (i = 8; (i < 11) && ((c = buf[FATDIR_NAME+i]) != ' '); i++) | 
|  | 1658 | +            de->name[j++] = lowercase ? tolower(c) : c; | 
|  | 1659 | +    } | 
|  | 1660 | +    return 1; | 
|  | 1661 | +} | 
|  | 1662 | + | 
|  | 1663 | +int fat_open(IF_MV2(int volume,) | 
|  | 1664 | +             long startcluster, | 
|  | 1665 | +             struct fat_file *file, | 
|  | 1666 | +             const struct fat_dir* dir) | 
|  | 1667 | +{ | 
|  | 1668 | +    /* Remember where the file's dir entry is located | 
|  | 1669 | +     * Do it before assigning other fields so that fat_open | 
|  | 1670 | +     * can be called with file == &dir->file (see fat_opendir) */ | 
|  | 1671 | +    if ( dir ) { | 
|  | 1672 | +        file->direntry = dir->entry - 1; | 
|  | 1673 | +        file->direntries = dir->entrycount; | 
|  | 1674 | +        file->dircluster = dir->file.firstcluster; | 
|  | 1675 | +    } | 
|  | 1676 | + | 
|  | 1677 | +    file->firstcluster = startcluster; | 
|  | 1678 | +    file->lastcluster = startcluster; | 
|  | 1679 | +    file->lastsector = 0; | 
|  | 1680 | +    file->clusternum = 0; | 
|  | 1681 | +    file->sectornum = 0; | 
|  | 1682 | +    file->eof = false; | 
|  | 1683 | +#ifdef HAVE_MULTIVOLUME | 
|  | 1684 | +    file->volume = volume; | 
|  | 1685 | +    /* fixme: remove error check when done */ | 
|  | 1686 | +    if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted) | 
|  | 1687 | +    { | 
|  | 1688 | +        DEBUGF("fat_open() illegal volume %d", volume); | 
|  | 1689 | +        return -1; | 
|  | 1690 | +    } | 
|  | 1691 | +#endif | 
|  | 1692 | + | 
|  | 1693 | +    DEBUGF("fat_open(%lx), entry %d",startcluster,file->direntry); | 
|  | 1694 | +    return 0; | 
|  | 1695 | +} | 
|  | 1696 | + | 
|  | 1697 | +int fat_create_file(const char* name, | 
|  | 1698 | +                    struct fat_file* file, | 
|  | 1699 | +                    struct fat_dir* dir) | 
|  | 1700 | +{ | 
|  | 1701 | +    int rc; | 
|  | 1702 | + | 
|  | 1703 | +    DEBUGF("fat_create_file(\"%s\",%lx,%lx)",name,(long)file,(long)dir); | 
|  | 1704 | +    rc = add_dir_entry(dir, file, name, false, false); | 
|  | 1705 | +    if (!rc) { | 
|  | 1706 | +        file->firstcluster = 0; | 
|  | 1707 | +        file->lastcluster = 0; | 
|  | 1708 | +        file->lastsector = 0; | 
|  | 1709 | +        file->clusternum = 0; | 
|  | 1710 | +        file->sectornum = 0; | 
|  | 1711 | +        file->eof = false; | 
|  | 1712 | +    } | 
|  | 1713 | + | 
|  | 1714 | +    return rc; | 
|  | 1715 | +} | 
|  | 1716 | + | 
|  | 1717 | +int fat_create_dir(const char* name, | 
|  | 1718 | +                   struct fat_dir* dir) | 
|  | 1719 | +{ | 
|  | 1720 | +#ifdef HAVE_MULTIVOLUME | 
|  | 1721 | +    struct bpb* fat_bpb = &fat_bpbs[dir->file.volume]; | 
|  | 1722 | +#else | 
|  | 1723 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 1724 | +#endif | 
|  | 1725 | +    int i; | 
|  | 1726 | +    long sector; | 
|  | 1727 | +    int rc; | 
|  | 1728 | +    struct fat_file newdir; | 
|  | 1729 | + | 
|  | 1730 | +    DEBUGF("fat_create_dir(\"%s\",%lx)",name,(long)dir); | 
|  | 1731 | + | 
|  | 1732 | +    /* First, add the entry in the parent directory */ | 
|  | 1733 | +    rc = add_dir_entry(dir, &newdir, name, true, false); | 
|  | 1734 | +    if (rc < 0) | 
|  | 1735 | +        return rc * 10 - 1; | 
|  | 1736 | + | 
|  | 1737 | +    /* Allocate a new cluster for the directory */ | 
|  | 1738 | +    newdir.firstcluster = find_free_cluster(IF_MV2(fat_bpb,) | 
|  | 1739 | +                                            fat_bpb->fsinfo.nextfree); | 
|  | 1740 | +    if(newdir.firstcluster == 0) | 
|  | 1741 | +        return -6; | 
|  | 1742 | + | 
|  | 1743 | +    update_fat_entry(IF_MV2(fat_bpb,) newdir.firstcluster, FAT_EOF_MARK); | 
|  | 1744 | + | 
|  | 1745 | +    /* Clear the entire cluster */ | 
|  | 1746 | +    unsigned char* buf = fat_get_sector_buffer(); | 
|  | 1747 | +    sector = cluster2sec(IF_MV2(fat_bpb,) newdir.firstcluster); | 
|  | 1748 | +    for(i = 0;i < (int)fat_bpb->bpb_secperclus;i++) { | 
|  | 1749 | +        memset(buf, 0, SECTOR_SIZE); | 
|  | 1750 | +        if (!i) | 
|  | 1751 | +        { | 
|  | 1752 | +            memcpy(buf, ".          \0x10", 12); | 
|  | 1753 | +            memcpy(&buf[0x20], "..         \0x10", 12); | 
|  | 1754 | +            ((uint16_t*)buf)[0xd] = newdir.firstcluster; | 
|  | 1755 | +            ((uint16_t*)buf)[0xa] = newdir.firstcluster >> 16; | 
|  | 1756 | +            if(dir->file.firstcluster == fat_bpb->bpb_rootclus) | 
|  | 1757 | +            { | 
|  | 1758 | +                ((uint16_t*)buf)[0x1d] = fat_bpb->bpb_rootclus; | 
|  | 1759 | +                ((uint16_t*)buf)[0x1a] = fat_bpb->bpb_rootclus >> 16; | 
|  | 1760 | +            } | 
|  | 1761 | +        } | 
|  | 1762 | +        rc = transfer(IF_MV2(fat_bpb,) sector + i, 1, buf, true ); | 
|  | 1763 | +        if (rc < 0) | 
|  | 1764 | +        { | 
|  | 1765 | +            fat_release_sector_buffer(); | 
|  | 1766 | +            return rc * 10 - 2; | 
|  | 1767 | +        } | 
|  | 1768 | +    } | 
|  | 1769 | +    fat_release_sector_buffer(); | 
|  | 1770 | + | 
|  | 1771 | +    /* Set the firstcluster field in the direntry */ | 
|  | 1772 | +    update_short_entry(&newdir, 0, FAT_ATTR_DIRECTORY); | 
|  | 1773 | + | 
|  | 1774 | +    rc = flush_fat(IF_MV(fat_bpb)); | 
|  | 1775 | +    if (rc < 0) | 
|  | 1776 | +        return rc * 10 - 5; | 
|  | 1777 | + | 
|  | 1778 | +    return 0; | 
|  | 1779 | +} | 
|  | 1780 | + | 
|  | 1781 | +int fat_truncate(const struct fat_file *file) | 
|  | 1782 | +{ | 
|  | 1783 | +    /* truncate trailing clusters */ | 
|  | 1784 | +    long next; | 
|  | 1785 | +    long last = file->lastcluster; | 
|  | 1786 | +#ifdef HAVE_MULTIVOLUME | 
|  | 1787 | +    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
|  | 1788 | +#endif | 
|  | 1789 | + | 
|  | 1790 | +    DEBUGF("fat_truncate(%lx, %lx)", file->firstcluster, last); | 
|  | 1791 | + | 
|  | 1792 | +    for ( last = get_next_cluster(IF_MV2(fat_bpb,) last); last; last = next ) { | 
|  | 1793 | +        next = get_next_cluster(IF_MV2(fat_bpb,) last); | 
|  | 1794 | +        update_fat_entry(IF_MV2(fat_bpb,) last,0); | 
|  | 1795 | +    } | 
|  | 1796 | +    if (file->lastcluster) | 
|  | 1797 | +        update_fat_entry(IF_MV2(fat_bpb,) file->lastcluster,FAT_EOF_MARK); | 
|  | 1798 | + | 
|  | 1799 | +    return 0; | 
|  | 1800 | +} | 
|  | 1801 | + | 
|  | 1802 | +int fat_closewrite(struct fat_file *file, long size, int attr) | 
|  | 1803 | +{ | 
|  | 1804 | +    int rc; | 
|  | 1805 | +#ifdef HAVE_MULTIVOLUME | 
|  | 1806 | +    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
|  | 1807 | +#endif | 
|  | 1808 | +    DEBUGF("fat_closewrite(size=%ld)",size); | 
|  | 1809 | + | 
|  | 1810 | +    if (!size) { | 
|  | 1811 | +        /* empty file */ | 
|  | 1812 | +        if ( file->firstcluster ) { | 
|  | 1813 | +            update_fat_entry(IF_MV2(fat_bpb,) file->firstcluster, 0); | 
|  | 1814 | +            file->firstcluster = 0; | 
|  | 1815 | +        } | 
|  | 1816 | +    } | 
|  | 1817 | + | 
|  | 1818 | +    if (file->dircluster) { | 
|  | 1819 | +        rc = update_short_entry(file, size, attr); | 
|  | 1820 | +        if (rc < 0) | 
|  | 1821 | +            return rc * 10 - 1; | 
|  | 1822 | +    } | 
|  | 1823 | + | 
|  | 1824 | +    flush_fat(IF_MV(fat_bpb)); | 
|  | 1825 | + | 
|  | 1826 | +#ifdef TEST_FAT | 
|  | 1827 | +    if ( file->firstcluster ) { | 
|  | 1828 | +        /* debug */ | 
|  | 1829 | +#ifdef HAVE_MULTIVOLUME | 
|  | 1830 | +        struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
|  | 1831 | +#else | 
|  | 1832 | +        struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 1833 | +#endif | 
|  | 1834 | +        long count = 0; | 
|  | 1835 | +        long len; | 
|  | 1836 | +        long next; | 
|  | 1837 | +        for ( next = file->firstcluster; next; | 
|  | 1838 | +              next = get_next_cluster(IF_MV2(fat_bpb,) next) ) { | 
|  | 1839 | +            DEBUGF("cluster %ld: %lx", count, next); | 
|  | 1840 | +            count++; | 
|  | 1841 | +        } | 
|  | 1842 | +        len = count * fat_bpb->bpb_secperclus * SECTOR_SIZE; | 
|  | 1843 | +        DEBUGF("File is %ld clusters (chainlen=%ld, size=%ld)", | 
|  | 1844 | +                count, len, size ); | 
|  | 1845 | +        if ( len > size + fat_bpb->bpb_secperclus * SECTOR_SIZE) | 
|  | 1846 | +            panicf(PANIC_KILLUSERTHREADS, "Cluster chain is too long"); | 
|  | 1847 | +        if ( len < size ) | 
|  | 1848 | +            panicf(PANIC_KILLUSERTHREADS, "Cluster chain is too short"); | 
|  | 1849 | +    } | 
|  | 1850 | +#endif | 
|  | 1851 | + | 
|  | 1852 | +    return 0; | 
|  | 1853 | +} | 
|  | 1854 | + | 
|  | 1855 | +static int free_direntries(struct fat_file* file) | 
|  | 1856 | +{ | 
|  | 1857 | +    struct fat_file dir; | 
|  | 1858 | +    int numentries = file->direntries; | 
|  | 1859 | +    unsigned int entry = file->direntry - numentries + 1; | 
|  | 1860 | +    unsigned int sector = entry / DIR_ENTRIES_PER_SECTOR; | 
|  | 1861 | +    int i; | 
|  | 1862 | +    int rc; | 
|  | 1863 | + | 
|  | 1864 | +    /* create a temporary file handle for the dir holding this file */ | 
|  | 1865 | +    rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL); | 
|  | 1866 | +    if (rc < 0) | 
|  | 1867 | +        return rc * 10 - 1; | 
|  | 1868 | + | 
|  | 1869 | +    rc = fat_seek( &dir, sector ); | 
|  | 1870 | +    if (rc < 0) | 
|  | 1871 | +        return rc * 10 - 2; | 
|  | 1872 | + | 
|  | 1873 | +    unsigned char* buf = fat_get_sector_buffer(); | 
|  | 1874 | +    rc = fat_readwrite(&dir, 1, buf, false); | 
|  | 1875 | +    if (rc < 1) | 
|  | 1876 | +    { | 
|  | 1877 | +        fat_release_sector_buffer(); | 
|  | 1878 | +        return rc * 10 - 3; | 
|  | 1879 | +    } | 
|  | 1880 | + | 
|  | 1881 | +    for (i=0; i < numentries; i++) { | 
|  | 1882 | +        DEBUGF("Clearing dir entry %d (%d/%d)", | 
|  | 1883 | +                entry, i+1, numentries); | 
|  | 1884 | +        buf[(entry % DIR_ENTRIES_PER_SECTOR) * DIR_ENTRY_SIZE] = 0xe5; | 
|  | 1885 | +        entry++; | 
|  | 1886 | + | 
|  | 1887 | +        if ( (entry % DIR_ENTRIES_PER_SECTOR) == 0 ) { | 
|  | 1888 | +            /* flush this sector */ | 
|  | 1889 | +            rc = fat_seek(&dir, sector); | 
|  | 1890 | +            if (rc < 0) | 
|  | 1891 | +            { | 
|  | 1892 | +                fat_release_sector_buffer(); | 
|  | 1893 | +                return rc * 10 - 4; | 
|  | 1894 | +            } | 
|  | 1895 | + | 
|  | 1896 | +            rc = fat_readwrite(&dir, 1, buf, true); | 
|  | 1897 | +            if (rc < 1) | 
|  | 1898 | +            { | 
|  | 1899 | +                fat_release_sector_buffer(); | 
|  | 1900 | +                return rc * 10 - 5; | 
|  | 1901 | +            } | 
|  | 1902 | + | 
|  | 1903 | +            if ( i+1 < numentries ) { | 
|  | 1904 | +                /* read next sector */ | 
|  | 1905 | +                rc = fat_readwrite(&dir, 1, buf, false); | 
|  | 1906 | +                if (rc < 1) | 
|  | 1907 | +                { | 
|  | 1908 | +                    fat_release_sector_buffer(); | 
|  | 1909 | +                    return rc * 10 - 6; | 
|  | 1910 | +                } | 
|  | 1911 | +            } | 
|  | 1912 | +            sector++; | 
|  | 1913 | +        } | 
|  | 1914 | +    } | 
|  | 1915 | + | 
|  | 1916 | +    if ( entry % DIR_ENTRIES_PER_SECTOR ) { | 
|  | 1917 | +        /* flush this sector */ | 
|  | 1918 | +        rc = fat_seek(&dir, sector); | 
|  | 1919 | +        if (rc < 0) | 
|  | 1920 | +        { | 
|  | 1921 | +            fat_release_sector_buffer(); | 
|  | 1922 | +            return rc * 10 - 7; | 
|  | 1923 | +        } | 
|  | 1924 | + | 
|  | 1925 | +        rc = fat_readwrite(&dir, 1, buf, true); | 
|  | 1926 | +        if (rc < 1) | 
|  | 1927 | +        { | 
|  | 1928 | +            fat_release_sector_buffer(); | 
|  | 1929 | +            return rc * 10 - 8; | 
|  | 1930 | +        } | 
|  | 1931 | +    } | 
|  | 1932 | + | 
|  | 1933 | +    fat_release_sector_buffer(); | 
|  | 1934 | +    return 0; | 
|  | 1935 | +} | 
|  | 1936 | + | 
|  | 1937 | +int fat_remove(struct fat_file* file) | 
|  | 1938 | +{ | 
|  | 1939 | +    long next, last = file->firstcluster; | 
|  | 1940 | +    int rc; | 
|  | 1941 | +#ifdef HAVE_MULTIVOLUME | 
|  | 1942 | +    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
|  | 1943 | +#endif | 
|  | 1944 | + | 
|  | 1945 | +    DEBUGF("fat_remove(%lx)",last); | 
|  | 1946 | + | 
|  | 1947 | +    while ( last ) { | 
|  | 1948 | +        next = get_next_cluster(IF_MV2(fat_bpb,) last); | 
|  | 1949 | +        update_fat_entry(IF_MV2(fat_bpb,) last,0); | 
|  | 1950 | +        last = next; | 
|  | 1951 | +    } | 
|  | 1952 | + | 
|  | 1953 | +    if ( file->dircluster ) { | 
|  | 1954 | +        rc = free_direntries(file); | 
|  | 1955 | +        if (rc < 0) | 
|  | 1956 | +            return rc * 10 - 1; | 
|  | 1957 | +    } | 
|  | 1958 | + | 
|  | 1959 | +    file->firstcluster = 0; | 
|  | 1960 | +    file->dircluster = 0; | 
|  | 1961 | + | 
|  | 1962 | +    rc = flush_fat(IF_MV(fat_bpb)); | 
|  | 1963 | +    if (rc < 0) | 
|  | 1964 | +        return rc * 10 - 2; | 
|  | 1965 | + | 
|  | 1966 | +    return 0; | 
|  | 1967 | +} | 
|  | 1968 | + | 
|  | 1969 | +int fat_rename(struct fat_file* file, | 
|  | 1970 | +                struct fat_dir* dir, | 
|  | 1971 | +                const unsigned char* newname, | 
|  | 1972 | +                long size, | 
|  | 1973 | +                int attr) | 
|  | 1974 | +{ | 
|  | 1975 | +    int rc; | 
|  | 1976 | +    struct fat_file newfile = *file; | 
|  | 1977 | +    unsigned char* entry = NULL; | 
|  | 1978 | +    unsigned short* clusptr = NULL; | 
|  | 1979 | +    unsigned int parentcluster; | 
|  | 1980 | +#ifdef HAVE_MULTIVOLUME | 
|  | 1981 | +    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
|  | 1982 | + | 
|  | 1983 | +    if (file->volume != dir->file.volume) { | 
|  | 1984 | +        DEBUGF("No rename across volumes!"); | 
|  | 1985 | +        return -1; | 
|  | 1986 | +    } | 
|  | 1987 | +#else | 
|  | 1988 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 1989 | +#endif | 
|  | 1990 | + | 
|  | 1991 | +    if ( !file->dircluster ) { | 
|  | 1992 | +        DEBUGF("File has no dir cluster!"); | 
|  | 1993 | +        return -2; | 
|  | 1994 | +    } | 
|  | 1995 | + | 
|  | 1996 | +    /* create new name */ | 
|  | 1997 | +    rc = add_dir_entry(dir, &newfile, newname, false, false); | 
|  | 1998 | +    if (rc < 0) | 
|  | 1999 | +        return rc * 10 - 2; | 
|  | 2000 | + | 
|  | 2001 | +    /* write size and cluster link */ | 
|  | 2002 | +    rc = update_short_entry(&newfile, size, attr); | 
|  | 2003 | +    if (rc < 0) | 
|  | 2004 | +        return rc * 10 - 3; | 
|  | 2005 | + | 
|  | 2006 | +    /* remove old name */ | 
|  | 2007 | +    rc = free_direntries(file); | 
|  | 2008 | +    if (rc < 0) | 
|  | 2009 | +        return rc * 10 - 4; | 
|  | 2010 | + | 
|  | 2011 | +    rc = flush_fat(IF_MV(fat_bpb)); | 
|  | 2012 | +    if (rc < 0) | 
|  | 2013 | +        return rc * 10 - 5; | 
|  | 2014 | + | 
|  | 2015 | +    /* if renaming a directory, update the .. entry to make sure | 
|  | 2016 | +       it points to its parent directory (we don't check if it was a move) */ | 
|  | 2017 | +    if(FAT_ATTR_DIRECTORY == attr) { | 
|  | 2018 | +        /* open the dir that was renamed, we re-use the newfile struct */ | 
|  | 2019 | + | 
|  | 2020 | +        rc = fat_open(IF_MV2(volume,) newfile.firstcluster, &newfile, NULL); | 
|  | 2021 | +        if (rc < 0) | 
|  | 2022 | +            return rc * 10 - 6; | 
|  | 2023 | + | 
|  | 2024 | +        /* get the first sector of the dir */ | 
|  | 2025 | +        rc = fat_seek(&newfile, 0); | 
|  | 2026 | +        if (rc < 0) | 
|  | 2027 | +            return rc * 10 - 7; | 
|  | 2028 | + | 
|  | 2029 | +        unsigned char* buf = fat_get_sector_buffer(); | 
|  | 2030 | +        rc = fat_readwrite(&newfile, 1, buf, false); | 
|  | 2031 | +        if (rc < 0) | 
|  | 2032 | +        { | 
|  | 2033 | +            fat_release_sector_buffer(); | 
|  | 2034 | +            return rc * 10 - 8; | 
|  | 2035 | +        } | 
|  | 2036 | + | 
|  | 2037 | +        /* parent cluster is 0 if parent dir is the root - FAT spec (p.29) */ | 
|  | 2038 | +        if(dir->file.firstcluster == fat_bpb->bpb_rootclus) | 
|  | 2039 | +            parentcluster = 0; | 
|  | 2040 | +        else | 
|  | 2041 | +            parentcluster = dir->file.firstcluster; | 
|  | 2042 | + | 
|  | 2043 | +        entry = buf + DIR_ENTRY_SIZE; | 
|  | 2044 | +        if(strncmp("..         ", entry, 11)) | 
|  | 2045 | +        { | 
|  | 2046 | +            fat_release_sector_buffer(); | 
|  | 2047 | +            /* .. entry must be second entry according to FAT spec (p.29) */ | 
|  | 2048 | +            DEBUGF("Second dir entry is not double-dot!"); | 
|  | 2049 | +            return rc * 10 - 9; | 
|  | 2050 | +        } | 
|  | 2051 | +        clusptr = (short*)(entry + FATDIR_FSTCLUSHI); | 
|  | 2052 | +        *clusptr = htole16(parentcluster >> 16); | 
|  | 2053 | + | 
|  | 2054 | +        clusptr = (short*)(entry + FATDIR_FSTCLUSLO); | 
|  | 2055 | +        *clusptr = htole16(parentcluster & 0xffff); | 
|  | 2056 | + | 
|  | 2057 | +        /* write back this sector */ | 
|  | 2058 | +        rc = fat_seek(&newfile, 0); | 
|  | 2059 | +        if (rc < 0) | 
|  | 2060 | +        { | 
|  | 2061 | +            fat_release_sector_buffer(); | 
|  | 2062 | +            return rc * 10 - 7; | 
|  | 2063 | +        } | 
|  | 2064 | + | 
|  | 2065 | +        rc = fat_readwrite(&newfile, 1, buf, true); | 
|  | 2066 | +        fat_release_sector_buffer(); | 
|  | 2067 | +        if (rc < 1) | 
|  | 2068 | +            return rc * 10 - 8; | 
|  | 2069 | +    } | 
|  | 2070 | + | 
|  | 2071 | +    return 0; | 
|  | 2072 | +} | 
|  | 2073 | + | 
|  | 2074 | +static long next_write_cluster(struct fat_file* file, | 
|  | 2075 | +                              long oldcluster, | 
|  | 2076 | +                              long* newsector) | 
|  | 2077 | +{ | 
|  | 2078 | +#ifdef HAVE_MULTIVOLUME | 
|  | 2079 | +    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
|  | 2080 | +#else | 
|  | 2081 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 2082 | +#endif | 
|  | 2083 | +    long cluster = 0; | 
|  | 2084 | +    long sector; | 
|  | 2085 | + | 
|  | 2086 | +    DEBUGF("next_write_cluster(%lx,%lx)",file->firstcluster, oldcluster); | 
|  | 2087 | + | 
|  | 2088 | +    if (oldcluster) | 
|  | 2089 | +        cluster = get_next_cluster(IF_MV2(fat_bpb,) oldcluster); | 
|  | 2090 | + | 
|  | 2091 | +    if (!cluster) { | 
|  | 2092 | +        if (oldcluster > 0) | 
|  | 2093 | +            cluster = find_free_cluster(IF_MV2(fat_bpb,) oldcluster+1); | 
|  | 2094 | +        else if (oldcluster == 0) | 
|  | 2095 | +            cluster = find_free_cluster(IF_MV2(fat_bpb,) | 
|  | 2096 | +                                        fat_bpb->fsinfo.nextfree); | 
|  | 2097 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 2098 | +        else /* negative, pseudo-cluster of the root dir */ | 
|  | 2099 | +            return 0; /* impossible to append something to the root */ | 
|  | 2100 | +#endif | 
|  | 2101 | + | 
|  | 2102 | +        if (cluster) { | 
|  | 2103 | +            if (oldcluster) | 
|  | 2104 | +                update_fat_entry(IF_MV2(fat_bpb,) oldcluster, cluster); | 
|  | 2105 | +            else | 
|  | 2106 | +                file->firstcluster = cluster; | 
|  | 2107 | +            update_fat_entry(IF_MV2(fat_bpb,) cluster, FAT_EOF_MARK); | 
|  | 2108 | +        } | 
|  | 2109 | +        else { | 
|  | 2110 | +#ifdef TEST_FAT | 
|  | 2111 | +            if (fat_bpb->fsinfo.freecount>0) | 
|  | 2112 | +                panicf(PANIC_KILLUSERTHREADS, "There is free space, but find_free_cluster() " | 
|  | 2113 | +                       "didn't find it!"); | 
|  | 2114 | +#endif | 
|  | 2115 | +            DEBUGF("next_write_cluster(): Disk full!"); | 
|  | 2116 | +            return 0; | 
|  | 2117 | +        } | 
|  | 2118 | +    } | 
|  | 2119 | +    sector = cluster2sec(IF_MV2(fat_bpb,) cluster); | 
|  | 2120 | +    if (sector<0) | 
|  | 2121 | +        return 0; | 
|  | 2122 | + | 
|  | 2123 | +    *newsector = sector; | 
|  | 2124 | +    return cluster; | 
|  | 2125 | +} | 
|  | 2126 | + | 
|  | 2127 | +static int transfer(IF_MV2(struct bpb* fat_bpb,) | 
|  | 2128 | +                    unsigned long start, long count, char* buf, bool write ) | 
|  | 2129 | +{ | 
|  | 2130 | +#ifndef HAVE_MULTIVOLUME | 
|  | 2131 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 2132 | +#endif | 
|  | 2133 | +    int rc; | 
|  | 2134 | + | 
|  | 2135 | +    DEBUGF("transfer(s=%lx, c=%lx, %s)", | 
|  | 2136 | +        start+ fat_bpb->startsector, count, write?"write":"read"); | 
|  | 2137 | +    if (write) { | 
|  | 2138 | +        unsigned long firstallowed; | 
|  | 2139 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 2140 | +        if (fat_bpb->is_fat16) | 
|  | 2141 | +            firstallowed = fat_bpb->rootdirsector; | 
|  | 2142 | +        else | 
|  | 2143 | +#endif | 
|  | 2144 | +            firstallowed = fat_bpb->firstdatasector; | 
|  | 2145 | + | 
|  | 2146 | +        if (start < firstallowed) | 
|  | 2147 | +            panicf(PANIC_KILLUSERTHREADS, "Write %ld before data", firstallowed - start); | 
|  | 2148 | +        if (start + count > fat_bpb->totalsectors) | 
|  | 2149 | +            panicf(PANIC_KILLUSERTHREADS, "Write %ld after data", | 
|  | 2150 | +                start + count - fat_bpb->totalsectors); | 
|  | 2151 | +        rc = storage_write_sectors(IF_MD2(fat_bpb->drive,) | 
|  | 2152 | +                               start + fat_bpb->startsector, count, buf); | 
|  | 2153 | +    } | 
|  | 2154 | +    else | 
|  | 2155 | +        rc = storage_read_sectors(IF_MD2(fat_bpb->drive,) | 
|  | 2156 | +                              start + fat_bpb->startsector, count, buf); | 
|  | 2157 | +    if (rc < 0) { | 
|  | 2158 | +        DEBUGF( "transfer() - Couldn't %s sector %lx" | 
|  | 2159 | +                " (error code %d)", | 
|  | 2160 | +                write ? "write":"read", start, rc); | 
|  | 2161 | +        return rc; | 
|  | 2162 | +    } | 
|  | 2163 | +    return 0; | 
|  | 2164 | +} | 
|  | 2165 | + | 
|  | 2166 | + | 
|  | 2167 | +long fat_readwrite( struct fat_file *file, long sectorcount, | 
|  | 2168 | +                   void* buf, bool write ) | 
|  | 2169 | +{ | 
|  | 2170 | +#ifdef HAVE_MULTIVOLUME | 
|  | 2171 | +    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
|  | 2172 | +#else | 
|  | 2173 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 2174 | +#endif | 
|  | 2175 | +    long cluster = file->lastcluster; | 
|  | 2176 | +    long sector = file->lastsector; | 
|  | 2177 | +    long clusternum = file->clusternum; | 
|  | 2178 | +    long numsec = file->sectornum; | 
|  | 2179 | +    bool eof = file->eof; | 
|  | 2180 | +    long first=0, last=0; | 
|  | 2181 | +    long i; | 
|  | 2182 | +    int rc; | 
|  | 2183 | + | 
|  | 2184 | +    DEBUGF( "fat_readwrite(file:%lx,count:0x%lx,buf:%lx,%s)", | 
|  | 2185 | +             file->firstcluster,sectorcount,(long)buf,write?"write":"read"); | 
|  | 2186 | +    DEBUGF( "fat_readwrite: sec=%lx numsec=%ld eof=%d", | 
|  | 2187 | +             sector,numsec, eof?1:0); | 
|  | 2188 | + | 
|  | 2189 | +    if ( eof && !write) | 
|  | 2190 | +        return 0; | 
|  | 2191 | + | 
|  | 2192 | +    /* find sequential sectors and write them all at once */ | 
|  | 2193 | +    for (i=0; (i < sectorcount) && (sector > -1); i++ ) { | 
|  | 2194 | +        numsec++; | 
|  | 2195 | +        if ( numsec > (long)fat_bpb->bpb_secperclus || !cluster ) { | 
|  | 2196 | +            long oldcluster = cluster; | 
|  | 2197 | +            long oldsector = sector; | 
|  | 2198 | +            long oldnumsec = numsec; | 
|  | 2199 | +            if (write) | 
|  | 2200 | +                cluster = next_write_cluster(file, cluster, §or); | 
|  | 2201 | +            else { | 
|  | 2202 | +                cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster); | 
|  | 2203 | +                sector = cluster2sec(IF_MV2(fat_bpb,) cluster); | 
|  | 2204 | +            } | 
|  | 2205 | + | 
|  | 2206 | +            clusternum++; | 
|  | 2207 | +            numsec=1; | 
|  | 2208 | + | 
|  | 2209 | +            if (!cluster) { | 
|  | 2210 | +                eof = true; | 
|  | 2211 | +                if ( write ) { | 
|  | 2212 | +                    /* remember last cluster, in case | 
|  | 2213 | +                       we want to append to the file */ | 
|  | 2214 | +                    sector = oldsector; | 
|  | 2215 | +                    cluster = oldcluster; | 
|  | 2216 | +                    numsec = oldnumsec; | 
|  | 2217 | +                    clusternum--; | 
|  | 2218 | +                    i = -1; /* Error code */ | 
|  | 2219 | +                    break; | 
|  | 2220 | +                } | 
|  | 2221 | +            } | 
|  | 2222 | +            else | 
|  | 2223 | +                eof = false; | 
|  | 2224 | +        } | 
|  | 2225 | +        else { | 
|  | 2226 | +            if (sector) | 
|  | 2227 | +                sector++; | 
|  | 2228 | +            else { | 
|  | 2229 | +                /* look up first sector of file */ | 
|  | 2230 | +                sector = cluster2sec(IF_MV2(fat_bpb,) file->firstcluster); | 
|  | 2231 | +                numsec=1; | 
|  | 2232 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 2233 | +                if (file->firstcluster < 0) | 
|  | 2234 | +                {   /* FAT16 root dir */ | 
|  | 2235 | +                    sector += fat_bpb->rootdiroffset; | 
|  | 2236 | +                    numsec += fat_bpb->rootdiroffset; | 
|  | 2237 | +                } | 
|  | 2238 | +#endif | 
|  | 2239 | +            } | 
|  | 2240 | +        } | 
|  | 2241 | + | 
|  | 2242 | +        if (!first) | 
|  | 2243 | +            first = sector; | 
|  | 2244 | + | 
|  | 2245 | +        if ( ((sector != first) && (sector != last+1)) || /* not sequential */ | 
|  | 2246 | +             (last-first+1 == 256) ) { /* max 256 sectors per ata request */ | 
|  | 2247 | +            long count = last - first + 1; | 
|  | 2248 | +            rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write ); | 
|  | 2249 | +            if (rc < 0) | 
|  | 2250 | +                return rc * 10 - 1; | 
|  | 2251 | + | 
|  | 2252 | +            buf = (char *)buf + count * SECTOR_SIZE; | 
|  | 2253 | +            first = sector; | 
|  | 2254 | +        } | 
|  | 2255 | + | 
|  | 2256 | +        if ((i == sectorcount-1) && /* last sector requested */ | 
|  | 2257 | +            (!eof)) | 
|  | 2258 | +        { | 
|  | 2259 | +            long count = sector - first + 1; | 
|  | 2260 | +            rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write ); | 
|  | 2261 | +            if (rc < 0) | 
|  | 2262 | +                return rc * 10 - 2; | 
|  | 2263 | +        } | 
|  | 2264 | + | 
|  | 2265 | +        last = sector; | 
|  | 2266 | +    } | 
|  | 2267 | + | 
|  | 2268 | +    file->lastcluster = cluster; | 
|  | 2269 | +    file->lastsector = sector; | 
|  | 2270 | +    file->clusternum = clusternum; | 
|  | 2271 | +    file->sectornum = numsec; | 
|  | 2272 | +    file->eof = eof; | 
|  | 2273 | + | 
|  | 2274 | +    /* if eof, don't report last block as read/written */ | 
|  | 2275 | +    if (eof) | 
|  | 2276 | +        i--; | 
|  | 2277 | + | 
|  | 2278 | +    DEBUGF("Sectors written: %ld", i); | 
|  | 2279 | +    return i; | 
|  | 2280 | +} | 
|  | 2281 | + | 
|  | 2282 | +int fat_seek(struct fat_file *file, unsigned long seeksector ) | 
|  | 2283 | +{ | 
|  | 2284 | +#ifdef HAVE_MULTIVOLUME | 
|  | 2285 | +    struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 
|  | 2286 | +#else | 
|  | 2287 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 2288 | +#endif | 
|  | 2289 | +    long clusternum=0, numclusters=0, sectornum=0, sector=0; | 
|  | 2290 | +    long cluster = file->firstcluster; | 
|  | 2291 | +    long i; | 
|  | 2292 | + | 
|  | 2293 | +#ifdef HAVE_FAT16SUPPORT | 
|  | 2294 | +    if (cluster < 0) /* FAT16 root dir */ | 
|  | 2295 | +        seeksector += fat_bpb->rootdiroffset; | 
|  | 2296 | +#endif | 
|  | 2297 | + | 
|  | 2298 | +    file->eof = false; | 
|  | 2299 | +    if (seeksector) { | 
|  | 2300 | +        /* we need to find the sector BEFORE the requested, since | 
|  | 2301 | +           the file struct stores the last accessed sector */ | 
|  | 2302 | +        seeksector--; | 
|  | 2303 | +        numclusters = clusternum = seeksector / fat_bpb->bpb_secperclus; | 
|  | 2304 | +        sectornum = seeksector % fat_bpb->bpb_secperclus; | 
|  | 2305 | + | 
|  | 2306 | +        if (file->clusternum && clusternum >= file->clusternum) | 
|  | 2307 | +        { | 
|  | 2308 | +            cluster = file->lastcluster; | 
|  | 2309 | +            numclusters -= file->clusternum; | 
|  | 2310 | +        } | 
|  | 2311 | + | 
|  | 2312 | +        for (i=0; i<numclusters; i++) { | 
|  | 2313 | +            cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster); | 
|  | 2314 | +            if (!cluster) { | 
|  | 2315 | +                DEBUGF("Seeking beyond the end of the file! " | 
|  | 2316 | +                       "(sector %ld, cluster %ld)", seeksector, i); | 
|  | 2317 | +                return -1; | 
|  | 2318 | +            } | 
|  | 2319 | +        } | 
|  | 2320 | + | 
|  | 2321 | +        sector = cluster2sec(IF_MV2(fat_bpb,) cluster) + sectornum; | 
|  | 2322 | +    } | 
|  | 2323 | +    else { | 
|  | 2324 | +        sectornum = -1; | 
|  | 2325 | +    } | 
|  | 2326 | + | 
|  | 2327 | +    DEBUGF("fat_seek(%lx, %lx) == %lx, %lx, %lx", | 
|  | 2328 | +            file->firstcluster, seeksector, cluster, sector, sectornum); | 
|  | 2329 | + | 
|  | 2330 | +    file->lastcluster = cluster; | 
|  | 2331 | +    file->lastsector = sector; | 
|  | 2332 | +    file->clusternum = clusternum; | 
|  | 2333 | +    file->sectornum = sectornum + 1; | 
|  | 2334 | +    return 0; | 
|  | 2335 | +} | 
|  | 2336 | + | 
|  | 2337 | +int fat_opendir(IF_MV2(int volume,) | 
|  | 2338 | +                struct fat_dir *dir, unsigned long startcluster, | 
|  | 2339 | +                const struct fat_dir *parent_dir) | 
|  | 2340 | +{ | 
|  | 2341 | +#ifdef HAVE_MULTIVOLUME | 
|  | 2342 | +    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
|  | 2343 | +    /* fixme: remove error check when done */ | 
|  | 2344 | +    if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted) | 
|  | 2345 | +    { | 
|  | 2346 | +        DEBUGF("fat_open() illegal volume %d", volume); | 
|  | 2347 | +        return -1; | 
|  | 2348 | +    } | 
|  | 2349 | +#else | 
|  | 2350 | +    struct bpb* fat_bpb = &fat_bpbs[0]; | 
|  | 2351 | +#endif | 
|  | 2352 | +    int rc; | 
|  | 2353 | + | 
|  | 2354 | +    if (startcluster == 0) | 
|  | 2355 | +        startcluster = fat_bpb->bpb_rootclus; | 
|  | 2356 | + | 
|  | 2357 | +    rc = fat_open(IF_MV2(volume,) startcluster, &dir->file, parent_dir); | 
|  | 2358 | +    if(rc) | 
|  | 2359 | +    { | 
|  | 2360 | +        DEBUGF( "fat_opendir() - Couldn't open dir" | 
|  | 2361 | +                " (error code %d)", rc); | 
|  | 2362 | +        return rc * 10 - 1; | 
|  | 2363 | +    } | 
|  | 2364 | + | 
|  | 2365 | +    /* assign them after fat_open call so that fat_opendir can be called with the same | 
|  | 2366 | +     * fat_dir as parent and result */ | 
|  | 2367 | +    dir->entry = 0; | 
|  | 2368 | +    dir->sector = 0; | 
|  | 2369 | + | 
|  | 2370 | +    return 0; | 
|  | 2371 | +} | 
|  | 2372 | + | 
|  | 2373 | +int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) | 
|  | 2374 | +{ | 
|  | 2375 | +    bool done = false; | 
|  | 2376 | +    int i, j; | 
|  | 2377 | +    int rc; | 
|  | 2378 | +    int order; | 
|  | 2379 | +    unsigned char firstbyte; | 
|  | 2380 | +    /* Long file names are stored in special entries. Each entry holds | 
|  | 2381 | +       up to 13 characters. Names can be max 255 chars (not bytes!) long */ | 
|  | 2382 | +    /* The number of long entries in the long name can be retrieve from the first | 
|  | 2383 | +     * long entry because there are stored in reverse order and have an ordinal */ | 
|  | 2384 | +    int nb_longs = 0; | 
|  | 2385 | +    /* The long entries are expected to be in order, so remember the last ordinal */ | 
|  | 2386 | +    int last_long_ord = 0; | 
|  | 2387 | + | 
|  | 2388 | +    dir->entrycount = 0; | 
|  | 2389 | + | 
|  | 2390 | +    while(!done) | 
|  | 2391 | +    { | 
|  | 2392 | +        if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector ) | 
|  | 2393 | +        { | 
|  | 2394 | +            rc = fat_readwrite(&dir->file, 1, dir->sectorcache, false); | 
|  | 2395 | +            if (rc == 0) { | 
|  | 2396 | +                /* eof */ | 
|  | 2397 | +                entry->name[0] = 0; | 
|  | 2398 | +                break; | 
|  | 2399 | +            } | 
|  | 2400 | +            if (rc < 0) { | 
|  | 2401 | +                DEBUGF( "fat_getnext() - Couldn't read dir" | 
|  | 2402 | +                        " (error code %d)", rc); | 
|  | 2403 | +                return rc * 10 - 1; | 
|  | 2404 | +            } | 
|  | 2405 | +            dir->sector = dir->file.lastsector; | 
|  | 2406 | +        } | 
|  | 2407 | + | 
|  | 2408 | +        for (i = dir->entry % DIR_ENTRIES_PER_SECTOR; | 
|  | 2409 | +             i < DIR_ENTRIES_PER_SECTOR; i++) { | 
|  | 2410 | +            unsigned int entrypos = i * DIR_ENTRY_SIZE; | 
|  | 2411 | + | 
|  | 2412 | +            firstbyte = dir->sectorcache[entrypos]; | 
|  | 2413 | +            dir->entry++; | 
|  | 2414 | + | 
|  | 2415 | +            if (firstbyte == 0xe5) { | 
|  | 2416 | +                /* free entry */ | 
|  | 2417 | +                dir->entrycount = 0; | 
|  | 2418 | +                continue; | 
|  | 2419 | +            } | 
|  | 2420 | + | 
|  | 2421 | +            if (firstbyte == 0) { | 
|  | 2422 | +                /* last entry */ | 
|  | 2423 | +                entry->name[0] = 0; | 
|  | 2424 | +                dir->entrycount = 0; | 
|  | 2425 | +                return 0; | 
|  | 2426 | +            } | 
|  | 2427 | + | 
|  | 2428 | +            dir->entrycount++; | 
|  | 2429 | + | 
|  | 2430 | +            /* LFN entry? */ | 
|  | 2431 | +            if ( ( dir->sectorcache[entrypos + FATDIR_ATTR] & | 
|  | 2432 | +                   FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) { | 
|  | 2433 | +                /* extract ordinal */ | 
|  | 2434 | +                order = dir->sectorcache[entrypos + FATLONG_ORDER] & ~FATLONG_LAST_LONG_ENTRY; | 
|  | 2435 | +                /* is this entry the first long entry ? (first in order but containing last part) */ | 
|  | 2436 | +                if (dir->sectorcache[entrypos + FATLONG_ORDER] & FATLONG_LAST_LONG_ENTRY) { | 
|  | 2437 | +                    /* check that order is not too big ! (and non-zero) */ | 
|  | 2438 | +                    if(order <= 0 || order > FATLONG_MAX_ORDER) | 
|  | 2439 | +                        continue; /* ignore the whole LFN, will trigger lots of warnings */ | 
|  | 2440 | +                    nb_longs = order; | 
|  | 2441 | +                    last_long_ord = order; | 
|  | 2442 | +                } | 
|  | 2443 | +                else { | 
|  | 2444 | +                    /* check orphan entry */ | 
|  | 2445 | +                    if (nb_longs == 0) { | 
|  | 2446 | +                        DEBUGF("fat warning: orphan LFN entry"); | 
|  | 2447 | +                        /* ignore */ | 
|  | 2448 | +                        continue; | 
|  | 2449 | +                    } | 
|  | 2450 | + | 
|  | 2451 | +                    /* check order */ | 
|  | 2452 | +                    if (order != (last_long_ord - 1)) { | 
|  | 2453 | +                        DEBUGF("fat warning: wrong LFN ordinal"); | 
|  | 2454 | +                        /* ignore the whole LFN, will trigger lots of warnings */ | 
|  | 2455 | +                        nb_longs = 0; | 
|  | 2456 | +                    } | 
|  | 2457 | + | 
|  | 2458 | +                    last_long_ord = order; | 
|  | 2459 | +                } | 
|  | 2460 | + | 
|  | 2461 | +                /* copy part, reuse [order] for another purpose :) */ | 
|  | 2462 | +                order = (order - 1) * FATLONG_NAME_BYTES_PER_ENTRY; | 
|  | 2463 | +                for(j = 0; j < FATLONG_NAME_CHUNKS; j++) { | 
|  | 2464 | +                    memcpy(dir->longname + order, | 
|  | 2465 | +                            dir->sectorcache + entrypos + FATLONG_NAME_POS[j], | 
|  | 2466 | +                            FATLONG_NAME_SIZE[j]); | 
|  | 2467 | +                    order += FATLONG_NAME_SIZE[j]; | 
|  | 2468 | +                } | 
|  | 2469 | +            } | 
|  | 2470 | +            else { | 
|  | 2471 | +                if ( parse_direntry(entry, dir->sectorcache + entrypos) ) { | 
|  | 2472 | + | 
|  | 2473 | +                    /* don't return volume id entry */ | 
|  | 2474 | +                    if ( (entry->attr & | 
|  | 2475 | +                          (FAT_ATTR_VOLUME_ID|FAT_ATTR_DIRECTORY)) | 
|  | 2476 | +                         == FAT_ATTR_VOLUME_ID) | 
|  | 2477 | +                        continue; | 
|  | 2478 | + | 
|  | 2479 | +                    /* replace shortname with longname? */ | 
|  | 2480 | +                    /* check that the long name is complete */ | 
|  | 2481 | +                    if (nb_longs != 0 && last_long_ord == 1) { | 
|  | 2482 | +                        /* hold a copy of the shortname in case the long one is too long */ | 
|  | 2483 | +                        unsigned char shortname[13]; /* 8+3+dot+\0 */ | 
|  | 2484 | +                        int longname_utf8len = 0; | 
|  | 2485 | +                        /* One character at a time, add 1 for trailing \0, 4 is the maximum size | 
|  | 2486 | +                         * of a UTF8 encoded character in rockbox */ | 
|  | 2487 | +                        unsigned char longname_utf8segm[4 + 1]; | 
|  | 2488 | +                        unsigned short ucs; | 
|  | 2489 | +                        int segm_utf8len; | 
|  | 2490 | +                        /* Temporarily store short name */ | 
|  | 2491 | +                        strcpy(shortname, entry->name); | 
|  | 2492 | +                        entry->name[0] = 0; | 
|  | 2493 | + | 
|  | 2494 | +                        /* Convert the FAT name to a utf8-encoded one. | 
|  | 2495 | +                         * The name is not necessary NUL-terminated ! */ | 
|  | 2496 | +                        for (j = 0; j < nb_longs * FATLONG_NAME_BYTES_PER_ENTRY; j += 2) { | 
|  | 2497 | +                            ucs = dir->longname[j] | (dir->longname[j + 1] << 8); | 
|  | 2498 | +                            if(ucs == 0 || ucs == FAT_LONGNAME_PAD_UCS) | 
|  | 2499 | +                                break; | 
|  | 2500 | +                            /* utf8encode will return a pointer after the converted | 
|  | 2501 | +                             * string, subtract the pointer to the start to get the length of it */ | 
|  | 2502 | +                            segm_utf8len = 1; | 
|  | 2503 | + | 
|  | 2504 | +                            /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */ | 
|  | 2505 | +                            if (longname_utf8len + segm_utf8len >= FAT_FILENAME_BYTES) { | 
|  | 2506 | +                                /* force use of short name */ | 
|  | 2507 | +                                longname_utf8len = FAT_FILENAME_BYTES + 1; | 
|  | 2508 | +                                break; /* fallback later */ | 
|  | 2509 | +                            } | 
|  | 2510 | +                            else { | 
|  | 2511 | +                                if (ucs < 128) longname_utf8segm[0] = (unsigned char)ucs; | 
|  | 2512 | +                                else longname_utf8segm[0] = '?'; | 
|  | 2513 | +                                longname_utf8segm[segm_utf8len] = 0; | 
|  | 2514 | +                                strcat(entry->name + longname_utf8len, longname_utf8segm); | 
|  | 2515 | +                                longname_utf8len += segm_utf8len; | 
|  | 2516 | +                            } | 
|  | 2517 | +                        } | 
|  | 2518 | + | 
|  | 2519 | +                        /* Does the utf8-encoded name fit into the entry? */ | 
|  | 2520 | +                        /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */ | 
|  | 2521 | +                        if (longname_utf8len >= FAT_FILENAME_BYTES) { | 
|  | 2522 | +                            /* Take the short DOS name. Need to utf8-encode it | 
|  | 2523 | +                               since it may contain chars from the upper half of | 
|  | 2524 | +                               the OEM code page which wouldn't be a valid utf8. | 
|  | 2525 | +                               Beware: this file will be shown with strange | 
|  | 2526 | +                               glyphs in file browser since unicode 0x80 to 0x9F | 
|  | 2527 | +                               are control characters. */ | 
|  | 2528 | +                            DEBUGF("SN-DOS: %s", shortname); | 
|  | 2529 | +                            unsigned char *utf8; | 
|  | 2530 | +                            memcpy(entry->name, shortname, strlen(shortname)); | 
|  | 2531 | +                            *(entry->name + strlen(shortname)) = 0; | 
|  | 2532 | +                            DEBUGF("SN: %s", entry->name); | 
|  | 2533 | +                        } else { | 
|  | 2534 | +                            DEBUGF("LN: %s", entry->name); | 
|  | 2535 | +                            DEBUGF("LNLen: %d", longname_utf8len); | 
|  | 2536 | +                        } | 
|  | 2537 | +                    } | 
|  | 2538 | +                    done = true; | 
|  | 2539 | +                    i++; | 
|  | 2540 | +                    break; | 
|  | 2541 | +                } | 
|  | 2542 | +            } | 
|  | 2543 | +        } | 
|  | 2544 | +    } | 
|  | 2545 | +    return 0; | 
|  | 2546 | +} | 
|  | 2547 | + | 
|  | 2548 | +unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)) | 
|  | 2549 | +{ | 
|  | 2550 | +#ifndef HAVE_MULTIVOLUME | 
|  | 2551 | +    const int volume = 0; | 
|  | 2552 | +#endif | 
|  | 2553 | +    struct bpb* fat_bpb = &fat_bpbs[volume]; | 
|  | 2554 | +    return fat_bpb->bpb_secperclus * SECTOR_SIZE; | 
|  | 2555 | +} | 
|  | 2556 | + | 
|  | 2557 | +#ifdef HAVE_MULTIVOLUME | 
|  | 2558 | +bool fat_ismounted(int volume) | 
|  | 2559 | +{ | 
|  | 2560 | +    return (volume<NUM_VOLUMES && fat_bpbs[volume].mounted); | 
|  | 2561 | +} | 
|  | 2562 | +#endif | 
| Index: embios/trunk/dir.c | 
| — | — | @@ -1,4 +1,4 @@ | 
| 2 |  | -/***************************************************************************
 | 
|  | 2 | +/*************************************************************************** | 
| 3 | 3 | *             __________               __   ___. | 
| 4 | 4 | *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___ | 
| 5 | 5 | *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  / | 
| — | — | @@ -217,7 +217,6 @@ | 
| 218 | 218 | char *basename; | 
| 219 | 219 | char *parent; | 
| 220 | 220 | struct dirent *entry; | 
| 221 |  | -    struct fat_dir newdir;
 | 
| 222 | 221 | int rc; | 
| 223 | 222 |  | 
| 224 | 223 | if ( name[0] != '/' ) { | 
| — | — | @@ -262,9 +261,7 @@ | 
| 263 | 262 | } | 
| 264 | 263 | } | 
| 265 | 264 |  | 
| 266 |  | -    memset(&newdir, 0, sizeof(struct fat_dir));
 | 
| 267 |  | -    
 | 
| 268 |  | -    rc = fat_create_dir(basename, &newdir, &(dir->fatdir));
 | 
|  | 265 | +    rc = fat_create_dir(basename, &(dir->fatdir)); | 
| 269 | 266 | closedir(dir); | 
| 270 | 267 |  | 
| 271 | 268 | return rc; | 
| Index: embios/trunk/fat.h | 
| — | — | @@ -1,136 +1,135 @@ | 
| 2 |  | -/*************************************************************************** | 
| 3 |  | - *             __________               __   ___. | 
| 4 |  | - *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___ | 
| 5 |  | - *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  / | 
| 6 |  | - *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  < | 
| 7 |  | - *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \ | 
| 8 |  | - *                     \/            \/     \/    \/            \/ | 
| 9 |  | - * $Id$ | 
| 10 |  | - * | 
| 11 |  | - * Copyright (C) 2002 by Linus Nielsen Feltzing | 
| 12 |  | - * | 
| 13 |  | - * This program is free software; you can redistribute it and/or | 
| 14 |  | - * modify it under the terms of the GNU General Public License | 
| 15 |  | - * as published by the Free Software Foundation; either version 2 | 
| 16 |  | - * of the License, or (at your option) any later version. | 
| 17 |  | - * | 
| 18 |  | - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | 
| 19 |  | - * KIND, either express or implied. | 
| 20 |  | - * | 
| 21 |  | - ****************************************************************************/ | 
| 22 |  | - | 
| 23 |  | -#ifndef FAT_H | 
| 24 |  | -#define FAT_H | 
| 25 |  | - | 
| 26 |  | -#include "global.h" | 
| 27 |  | -#include "util.h" | 
| 28 |  | -#include "mv.h" /* for volume definitions */ | 
| 29 |  | - | 
| 30 |  | -#ifndef SECTOR_SIZE | 
| 31 |  | -#define SECTOR_SIZE 512 | 
| 32 |  | -#endif | 
| 33 |  | - | 
| 34 |  | -/* Number of bytes reserved for a file name (including the trailing \0). | 
| 35 |  | -   Since names are stored in the entry as UTF-8, we won't be able to | 
| 36 |  | -   store all names allowed by FAT. In FAT, a name can have max 255 | 
| 37 |  | -   characters (not bytes!). Since the UTF-8 encoding of a char may take | 
| 38 |  | -   up to 4 bytes, there will be names that we won't be able to store | 
| 39 |  | -   completely. For such names, the short DOS name is used. */ | 
| 40 |  | -#define FAT_FILENAME_BYTES 256 | 
| 41 |  | - | 
| 42 |  | -struct fat_direntry | 
| 43 |  | -{ | 
| 44 |  | -    unsigned char name[FAT_FILENAME_BYTES]; /* UTF-8 encoded name plus \0 */ | 
| 45 |  | -    unsigned short attr;            /* Attributes */ | 
| 46 |  | -    unsigned char crttimetenth;     /* Millisecond creation | 
| 47 |  | -                                       time stamp (0-199) */ | 
| 48 |  | -    unsigned short crttime;         /* Creation time */ | 
| 49 |  | -    unsigned short crtdate;         /* Creation date */ | 
| 50 |  | -    unsigned short lstaccdate;      /* Last access date */ | 
| 51 |  | -    unsigned short wrttime;         /* Last write time */ | 
| 52 |  | -    unsigned short wrtdate;         /* Last write date */ | 
| 53 |  | -    unsigned long filesize;          /* File size in bytes */ | 
| 54 |  | -    long firstcluster;               /* fstclusterhi<<16 + fstcluslo */ | 
| 55 |  | -}; | 
| 56 |  | - | 
| 57 |  | -#define FAT_ATTR_READ_ONLY   0x01 | 
| 58 |  | -#define FAT_ATTR_HIDDEN      0x02 | 
| 59 |  | -#define FAT_ATTR_SYSTEM      0x04 | 
| 60 |  | -#define FAT_ATTR_VOLUME_ID   0x08 | 
| 61 |  | -#define FAT_ATTR_DIRECTORY   0x10 | 
| 62 |  | -#define FAT_ATTR_ARCHIVE     0x20 | 
| 63 |  | -#define FAT_ATTR_VOLUME      0x40 /* this is a volume, not a real directory */ | 
| 64 |  | - | 
| 65 |  | -struct fat_file | 
| 66 |  | -{ | 
| 67 |  | -    long firstcluster;    /* first cluster in file */ | 
| 68 |  | -    long lastcluster;     /* cluster of last access */ | 
| 69 |  | -    long lastsector;      /* sector of last access */ | 
| 70 |  | -    long clusternum;      /* current clusternum */ | 
| 71 |  | -    long sectornum;       /* sector number in this cluster */ | 
| 72 |  | -    unsigned int direntry;   /* short dir entry index from start of dir */ | 
| 73 |  | -    unsigned int direntries; /* number of dir entries used by this file */ | 
| 74 |  | -    long dircluster;      /* first cluster of dir */ | 
| 75 |  | -    bool eof; | 
| 76 |  | -#ifdef HAVE_MULTIVOLUME | 
| 77 |  | -    int volume;          /* file resides on which volume */ | 
| 78 |  | -#endif | 
| 79 |  | -}; | 
| 80 |  | - | 
| 81 |  | -struct fat_dir | 
| 82 |  | -{ | 
| 83 |  | -    unsigned int entry; | 
| 84 |  | -    unsigned int entrycount; | 
| 85 |  | -    long sector; | 
| 86 |  | -    struct fat_file file; | 
| 87 |  | -    unsigned char sectorcache[SECTOR_SIZE] CACHEALIGN_ATTR; | 
| 88 |  | -    /* There are 2-bytes per characters. We don't want to bother too much, as LFN entries are | 
| 89 |  | -     * at much 255 characters longs, that's at most 20 LFN entries. Each entry hold at most | 
| 90 |  | -     * 13 characters, that a total of 260 characters. So we keep a buffer of that size. | 
| 91 |  | -     * Keep coherent with fat.c code. */ | 
| 92 |  | -    unsigned char longname[260 * 2]; | 
| 93 |  | -} CACHEALIGN_ATTR; | 
| 94 |  | - | 
| 95 |  | -#ifdef HAVE_HOTSWAP | 
| 96 |  | -extern void fat_lock(void); | 
| 97 |  | -extern void fat_unlock(void); | 
| 98 |  | -#endif | 
| 99 |  | - | 
| 100 |  | -extern void fat_init(void); | 
| 101 |  | -extern int fat_mount(IF_MV2(int volume,) IF_MD2(int drive,) long startsector); | 
| 102 |  | -extern int fat_unmount(int volume, bool flush); | 
| 103 |  | -extern void fat_size(IF_MV2(int volume,) /* public for info */ | 
| 104 |  | -                     unsigned long* size, | 
| 105 |  | -                     unsigned long* free); | 
| 106 |  | -extern void fat_recalc_free(IF_MV_NONVOID(int volume)); /* public for debug info screen */ | 
| 107 |  | -extern int fat_create_dir(const char* name, | 
| 108 |  | -                          struct fat_dir* newdir, | 
| 109 |  | -                          struct fat_dir* dir); | 
| 110 |  | -extern int fat_open(IF_MV2(int volume,) | 
| 111 |  | -                    long cluster, | 
| 112 |  | -                    struct fat_file* ent, | 
| 113 |  | -                    const struct fat_dir* dir); | 
| 114 |  | -extern int fat_create_file(const char* name, | 
| 115 |  | -                           struct fat_file* ent, | 
| 116 |  | -                           struct fat_dir* dir); | 
| 117 |  | -extern long fat_readwrite(struct fat_file *ent, long sectorcount, | 
| 118 |  | -                         void* buf, bool write ); | 
| 119 |  | -extern int fat_closewrite(struct fat_file *ent, long size, int attr); | 
| 120 |  | -extern int fat_seek(struct fat_file *ent, unsigned long sector ); | 
| 121 |  | -extern int fat_remove(struct fat_file *ent); | 
| 122 |  | -extern int fat_truncate(const struct fat_file *ent); | 
| 123 |  | -extern int fat_rename(struct fat_file* file, | 
| 124 |  | -                      struct fat_dir* dir, | 
| 125 |  | -                      const unsigned char* newname, | 
| 126 |  | -                      long size, int attr); | 
| 127 |  | - | 
| 128 |  | -extern int fat_opendir(IF_MV2(int volume,) | 
| 129 |  | -                       struct fat_dir *ent, unsigned long startcluster, | 
| 130 |  | -                       const struct fat_dir *parent_dir); | 
| 131 |  | -extern int fat_getnext(struct fat_dir *ent, struct fat_direntry *entry); | 
| 132 |  | -extern unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)); /* public for debug info screen */ | 
| 133 |  | -extern bool fat_ismounted(int volume); | 
| 134 |  | -extern void* fat_get_sector_buffer(void); | 
| 135 |  | -extern void fat_release_sector_buffer(void); | 
| 136 |  | - | 
| 137 |  | -#endif | 
|  | 2 | +/*************************************************************************** | 
|  | 3 | + *             __________               __   ___. | 
|  | 4 | + *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___ | 
|  | 5 | + *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  / | 
|  | 6 | + *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  < | 
|  | 7 | + *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \ | 
|  | 8 | + *                     \/            \/     \/    \/            \/ | 
|  | 9 | + * $Id$ | 
|  | 10 | + * | 
|  | 11 | + * Copyright (C) 2002 by Linus Nielsen Feltzing | 
|  | 12 | + * | 
|  | 13 | + * This program is free software; you can redistribute it and/or | 
|  | 14 | + * modify it under the terms of the GNU General Public License | 
|  | 15 | + * as published by the Free Software Foundation; either version 2 | 
|  | 16 | + * of the License, or (at your option) any later version. | 
|  | 17 | + * | 
|  | 18 | + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | 
|  | 19 | + * KIND, either express or implied. | 
|  | 20 | + * | 
|  | 21 | + ****************************************************************************/ | 
|  | 22 | + | 
|  | 23 | +#ifndef FAT_H | 
|  | 24 | +#define FAT_H | 
|  | 25 | + | 
|  | 26 | +#include "global.h" | 
|  | 27 | +#include "util.h" | 
|  | 28 | +#include "mv.h" /* for volume definitions */ | 
|  | 29 | + | 
|  | 30 | +#ifndef SECTOR_SIZE | 
|  | 31 | +#define SECTOR_SIZE 512 | 
|  | 32 | +#endif | 
|  | 33 | + | 
|  | 34 | +/* Number of bytes reserved for a file name (including the trailing \0). | 
|  | 35 | +   Since names are stored in the entry as UTF-8, we won't be able to | 
|  | 36 | +   store all names allowed by FAT. In FAT, a name can have max 255 | 
|  | 37 | +   characters (not bytes!). Since the UTF-8 encoding of a char may take | 
|  | 38 | +   up to 4 bytes, there will be names that we won't be able to store | 
|  | 39 | +   completely. For such names, the short DOS name is used. */ | 
|  | 40 | +#define FAT_FILENAME_BYTES 256 | 
|  | 41 | + | 
|  | 42 | +struct fat_direntry | 
|  | 43 | +{ | 
|  | 44 | +    unsigned char name[FAT_FILENAME_BYTES]; /* UTF-8 encoded name plus \0 */ | 
|  | 45 | +    unsigned short attr;            /* Attributes */ | 
|  | 46 | +    unsigned char crttimetenth;     /* Millisecond creation | 
|  | 47 | +                                       time stamp (0-199) */ | 
|  | 48 | +    unsigned short crttime;         /* Creation time */ | 
|  | 49 | +    unsigned short crtdate;         /* Creation date */ | 
|  | 50 | +    unsigned short lstaccdate;      /* Last access date */ | 
|  | 51 | +    unsigned short wrttime;         /* Last write time */ | 
|  | 52 | +    unsigned short wrtdate;         /* Last write date */ | 
|  | 53 | +    unsigned long filesize;          /* File size in bytes */ | 
|  | 54 | +    long firstcluster;               /* fstclusterhi<<16 + fstcluslo */ | 
|  | 55 | +}; | 
|  | 56 | + | 
|  | 57 | +#define FAT_ATTR_READ_ONLY   0x01 | 
|  | 58 | +#define FAT_ATTR_HIDDEN      0x02 | 
|  | 59 | +#define FAT_ATTR_SYSTEM      0x04 | 
|  | 60 | +#define FAT_ATTR_VOLUME_ID   0x08 | 
|  | 61 | +#define FAT_ATTR_DIRECTORY   0x10 | 
|  | 62 | +#define FAT_ATTR_ARCHIVE     0x20 | 
|  | 63 | +#define FAT_ATTR_VOLUME      0x40 /* this is a volume, not a real directory */ | 
|  | 64 | + | 
|  | 65 | +struct fat_file | 
|  | 66 | +{ | 
|  | 67 | +    long firstcluster;    /* first cluster in file */ | 
|  | 68 | +    long lastcluster;     /* cluster of last access */ | 
|  | 69 | +    long lastsector;      /* sector of last access */ | 
|  | 70 | +    long clusternum;      /* current clusternum */ | 
|  | 71 | +    long sectornum;       /* sector number in this cluster */ | 
|  | 72 | +    unsigned int direntry;   /* short dir entry index from start of dir */ | 
|  | 73 | +    unsigned int direntries; /* number of dir entries used by this file */ | 
|  | 74 | +    long dircluster;      /* first cluster of dir */ | 
|  | 75 | +    bool eof; | 
|  | 76 | +#ifdef HAVE_MULTIVOLUME | 
|  | 77 | +    int volume;          /* file resides on which volume */ | 
|  | 78 | +#endif | 
|  | 79 | +}; | 
|  | 80 | + | 
|  | 81 | +struct fat_dir | 
|  | 82 | +{ | 
|  | 83 | +    unsigned int entry; | 
|  | 84 | +    unsigned int entrycount; | 
|  | 85 | +    long sector; | 
|  | 86 | +    struct fat_file file; | 
|  | 87 | +    unsigned char sectorcache[SECTOR_SIZE] CACHEALIGN_ATTR; | 
|  | 88 | +    /* There are 2-bytes per characters. We don't want to bother too much, as LFN entries are | 
|  | 89 | +     * at much 255 characters longs, that's at most 20 LFN entries. Each entry hold at most | 
|  | 90 | +     * 13 characters, that a total of 260 characters. So we keep a buffer of that size. | 
|  | 91 | +     * Keep coherent with fat.c code. */ | 
|  | 92 | +    unsigned char longname[260 * 2]; | 
|  | 93 | +} CACHEALIGN_ATTR; | 
|  | 94 | + | 
|  | 95 | +#ifdef HAVE_HOTSWAP | 
|  | 96 | +extern void fat_lock(void); | 
|  | 97 | +extern void fat_unlock(void); | 
|  | 98 | +#endif | 
|  | 99 | + | 
|  | 100 | +extern void fat_init(void); | 
|  | 101 | +extern int fat_mount(IF_MV2(int volume,) IF_MD2(int drive,) long startsector); | 
|  | 102 | +extern int fat_unmount(int volume, bool flush); | 
|  | 103 | +extern void fat_size(IF_MV2(int volume,) /* public for info */ | 
|  | 104 | +                     unsigned long* size, | 
|  | 105 | +                     unsigned long* free); | 
|  | 106 | +extern void fat_recalc_free(IF_MV_NONVOID(int volume)); /* public for debug info screen */ | 
|  | 107 | +extern int fat_create_dir(const char* name, | 
|  | 108 | +                          struct fat_dir* dir); | 
|  | 109 | +extern int fat_open(IF_MV2(int volume,) | 
|  | 110 | +                    long cluster, | 
|  | 111 | +                    struct fat_file* ent, | 
|  | 112 | +                    const struct fat_dir* dir); | 
|  | 113 | +extern int fat_create_file(const char* name, | 
|  | 114 | +                           struct fat_file* ent, | 
|  | 115 | +                           struct fat_dir* dir); | 
|  | 116 | +extern long fat_readwrite(struct fat_file *ent, long sectorcount, | 
|  | 117 | +                         void* buf, bool write ); | 
|  | 118 | +extern int fat_closewrite(struct fat_file *ent, long size, int attr); | 
|  | 119 | +extern int fat_seek(struct fat_file *ent, unsigned long sector ); | 
|  | 120 | +extern int fat_remove(struct fat_file *ent); | 
|  | 121 | +extern int fat_truncate(const struct fat_file *ent); | 
|  | 122 | +extern int fat_rename(struct fat_file* file, | 
|  | 123 | +                      struct fat_dir* dir, | 
|  | 124 | +                      const unsigned char* newname, | 
|  | 125 | +                      long size, int attr); | 
|  | 126 | + | 
|  | 127 | +extern int fat_opendir(IF_MV2(int volume,) | 
|  | 128 | +                       struct fat_dir *ent, unsigned long startcluster, | 
|  | 129 | +                       const struct fat_dir *parent_dir); | 
|  | 130 | +extern int fat_getnext(struct fat_dir *ent, struct fat_direntry *entry); | 
|  | 131 | +extern unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)); /* public for debug info screen */ | 
|  | 132 | +extern bool fat_ismounted(int volume); | 
|  | 133 | +extern void* fat_get_sector_buffer(void); | 
|  | 134 | +extern void fat_release_sector_buffer(void); | 
|  | 135 | + | 
|  | 136 | +#endif |