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