uboot/fs/fat/fat_write.c
<<
>>
Prefs
   1/*
   2 * fat_write.c
   3 *
   4 * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim
   5 *
   6 * SPDX-License-Identifier:     GPL-2.0+
   7 */
   8
   9#include <common.h>
  10#include <command.h>
  11#include <config.h>
  12#include <fat.h>
  13#include <asm/byteorder.h>
  14#include <part.h>
  15#include <linux/ctype.h>
  16#include <div64.h>
  17#include <linux/math64.h>
  18#include "fat.c"
  19
  20static void uppercase(char *str, int len)
  21{
  22        int i;
  23
  24        for (i = 0; i < len; i++) {
  25                *str = toupper(*str);
  26                str++;
  27        }
  28}
  29
  30static int total_sector;
  31static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
  32{
  33        ulong ret;
  34
  35        if (!cur_dev)
  36                return -1;
  37
  38        if (cur_part_info.start + block + nr_blocks >
  39                cur_part_info.start + total_sector) {
  40                printf("error: overflow occurs\n");
  41                return -1;
  42        }
  43
  44        ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf);
  45        if (nr_blocks && ret == 0)
  46                return -1;
  47
  48        return ret;
  49}
  50
  51/*
  52 * Set short name in directory entry
  53 */
  54static void set_name(dir_entry *dirent, const char *filename)
  55{
  56        char s_name[VFAT_MAXLEN_BYTES];
  57        char *period;
  58        int period_location, len, i, ext_num;
  59
  60        if (filename == NULL)
  61                return;
  62
  63        len = strlen(filename);
  64        if (len == 0)
  65                return;
  66
  67        strcpy(s_name, filename);
  68        uppercase(s_name, len);
  69
  70        period = strchr(s_name, '.');
  71        if (period == NULL) {
  72                period_location = len;
  73                ext_num = 0;
  74        } else {
  75                period_location = period - s_name;
  76                ext_num = len - period_location - 1;
  77        }
  78
  79        /* Pad spaces when the length of file name is shorter than eight */
  80        if (period_location < 8) {
  81                memcpy(dirent->name, s_name, period_location);
  82                for (i = period_location; i < 8; i++)
  83                        dirent->name[i] = ' ';
  84        } else if (period_location == 8) {
  85                memcpy(dirent->name, s_name, period_location);
  86        } else {
  87                memcpy(dirent->name, s_name, 6);
  88                dirent->name[6] = '~';
  89                dirent->name[7] = '1';
  90        }
  91
  92        if (ext_num < 3) {
  93                memcpy(dirent->ext, s_name + period_location + 1, ext_num);
  94                for (i = ext_num; i < 3; i++)
  95                        dirent->ext[i] = ' ';
  96        } else
  97                memcpy(dirent->ext, s_name + period_location + 1, 3);
  98
  99        debug("name : %s\n", dirent->name);
 100        debug("ext : %s\n", dirent->ext);
 101}
 102
 103static __u8 num_of_fats;
 104/*
 105 * Write fat buffer into block device
 106 */
 107static int flush_dirty_fat_buffer(fsdata *mydata)
 108{
 109        int getsize = FATBUFBLOCKS;
 110        __u32 fatlength = mydata->fatlength;
 111        __u8 *bufptr = mydata->fatbuf;
 112        __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
 113
 114        debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum,
 115              (int)mydata->fat_dirty);
 116
 117        if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1))
 118                return 0;
 119
 120        /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
 121        if (startblock + getsize > fatlength)
 122                getsize = fatlength - startblock;
 123
 124        startblock += mydata->fat_sect;
 125
 126        /* Write FAT buf */
 127        if (disk_write(startblock, getsize, bufptr) < 0) {
 128                debug("error: writing FAT blocks\n");
 129                return -1;
 130        }
 131
 132        if (num_of_fats == 2) {
 133                /* Update corresponding second FAT blocks */
 134                startblock += mydata->fatlength;
 135                if (disk_write(startblock, getsize, bufptr) < 0) {
 136                        debug("error: writing second FAT blocks\n");
 137                        return -1;
 138                }
 139        }
 140        mydata->fat_dirty = 0;
 141
 142        return 0;
 143}
 144
 145/*
 146 * Set the file name information from 'name' into 'slotptr',
 147 */
 148static int str2slot(dir_slot *slotptr, const char *name, int *idx)
 149{
 150        int j, end_idx = 0;
 151
 152        for (j = 0; j <= 8; j += 2) {
 153                if (name[*idx] == 0x00) {
 154                        slotptr->name0_4[j] = 0;
 155                        slotptr->name0_4[j + 1] = 0;
 156                        end_idx++;
 157                        goto name0_4;
 158                }
 159                slotptr->name0_4[j] = name[*idx];
 160                (*idx)++;
 161                end_idx++;
 162        }
 163        for (j = 0; j <= 10; j += 2) {
 164                if (name[*idx] == 0x00) {
 165                        slotptr->name5_10[j] = 0;
 166                        slotptr->name5_10[j + 1] = 0;
 167                        end_idx++;
 168                        goto name5_10;
 169                }
 170                slotptr->name5_10[j] = name[*idx];
 171                (*idx)++;
 172                end_idx++;
 173        }
 174        for (j = 0; j <= 2; j += 2) {
 175                if (name[*idx] == 0x00) {
 176                        slotptr->name11_12[j] = 0;
 177                        slotptr->name11_12[j + 1] = 0;
 178                        end_idx++;
 179                        goto name11_12;
 180                }
 181                slotptr->name11_12[j] = name[*idx];
 182                (*idx)++;
 183                end_idx++;
 184        }
 185
 186        if (name[*idx] == 0x00)
 187                return 1;
 188
 189        return 0;
 190/* Not used characters are filled with 0xff 0xff */
 191name0_4:
 192        for (; end_idx < 5; end_idx++) {
 193                slotptr->name0_4[end_idx * 2] = 0xff;
 194                slotptr->name0_4[end_idx * 2 + 1] = 0xff;
 195        }
 196        end_idx = 5;
 197name5_10:
 198        end_idx -= 5;
 199        for (; end_idx < 6; end_idx++) {
 200                slotptr->name5_10[end_idx * 2] = 0xff;
 201                slotptr->name5_10[end_idx * 2 + 1] = 0xff;
 202        }
 203        end_idx = 11;
 204name11_12:
 205        end_idx -= 11;
 206        for (; end_idx < 2; end_idx++) {
 207                slotptr->name11_12[end_idx * 2] = 0xff;
 208                slotptr->name11_12[end_idx * 2 + 1] = 0xff;
 209        }
 210
 211        return 1;
 212}
 213
 214static int is_next_clust(fsdata *mydata, dir_entry *dentptr);
 215static void flush_dir_table(fsdata *mydata, dir_entry **dentptr);
 216
 217/*
 218 * Fill dir_slot entries with appropriate name, id, and attr
 219 * The real directory entry is returned by 'dentptr'
 220 */
 221static void
 222fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name)
 223{
 224        __u8 temp_dir_slot_buffer[MAX_LFN_SLOT * sizeof(dir_slot)];
 225        dir_slot *slotptr = (dir_slot *)temp_dir_slot_buffer;
 226        __u8 counter = 0, checksum;
 227        int idx = 0, ret;
 228
 229        /* Get short file name checksum value */
 230        checksum = mkcksum((*dentptr)->name, (*dentptr)->ext);
 231
 232        do {
 233                memset(slotptr, 0x00, sizeof(dir_slot));
 234                ret = str2slot(slotptr, l_name, &idx);
 235                slotptr->id = ++counter;
 236                slotptr->attr = ATTR_VFAT;
 237                slotptr->alias_checksum = checksum;
 238                slotptr++;
 239        } while (ret == 0);
 240
 241        slotptr--;
 242        slotptr->id |= LAST_LONG_ENTRY_MASK;
 243
 244        while (counter >= 1) {
 245                if (is_next_clust(mydata, *dentptr)) {
 246                        /* A new cluster is allocated for directory table */
 247                        flush_dir_table(mydata, dentptr);
 248                }
 249                memcpy(*dentptr, slotptr, sizeof(dir_slot));
 250                (*dentptr)++;
 251                slotptr--;
 252                counter--;
 253        }
 254
 255        if (is_next_clust(mydata, *dentptr)) {
 256                /* A new cluster is allocated for directory table */
 257                flush_dir_table(mydata, dentptr);
 258        }
 259}
 260
 261static __u32 dir_curclust;
 262
 263/*
 264 * Extract the full long filename starting at 'retdent' (which is really
 265 * a slot) into 'l_name'. If successful also copy the real directory entry
 266 * into 'retdent'
 267 * If additional adjacent cluster for directory entries is read into memory,
 268 * then 'get_contents_vfatname_block' is copied into 'get_dentfromdir_block' and
 269 * the location of the real directory entry is returned by 'retdent'
 270 * Return 0 on success, -1 otherwise.
 271 */
 272static int
 273get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster,
 274              dir_entry **retdent, char *l_name)
 275{
 276        dir_entry *realdent;
 277        dir_slot *slotptr = (dir_slot *)(*retdent);
 278        dir_slot *slotptr2 = NULL;
 279        __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
 280                                                        PREFETCH_BLOCKS :
 281                                                        mydata->clust_size);
 282        __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
 283        int idx = 0, cur_position = 0;
 284
 285        if (counter > VFAT_MAXSEQ) {
 286                debug("Error: VFAT name is too long\n");
 287                return -1;
 288        }
 289
 290        while ((__u8 *)slotptr < buflimit) {
 291                if (counter == 0)
 292                        break;
 293                if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
 294                        return -1;
 295                slotptr++;
 296                counter--;
 297        }
 298
 299        if ((__u8 *)slotptr >= buflimit) {
 300                if (curclust == 0)
 301                        return -1;
 302                curclust = get_fatent(mydata, dir_curclust);
 303                if (CHECK_CLUST(curclust, mydata->fatsize)) {
 304                        debug("curclust: 0x%x\n", curclust);
 305                        printf("Invalid FAT entry\n");
 306                        return -1;
 307                }
 308
 309                dir_curclust = curclust;
 310
 311                if (get_cluster(mydata, curclust, get_contents_vfatname_block,
 312                                mydata->clust_size * mydata->sect_size) != 0) {
 313                        debug("Error: reading directory block\n");
 314                        return -1;
 315                }
 316
 317                slotptr2 = (dir_slot *)get_contents_vfatname_block;
 318                while (counter > 0) {
 319                        if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
 320                            & 0xff) != counter)
 321                                return -1;
 322                        slotptr2++;
 323                        counter--;
 324                }
 325
 326                /* Save the real directory entry */
 327                realdent = (dir_entry *)slotptr2;
 328                while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
 329                        slotptr2--;
 330                        slot2str(slotptr2, l_name, &idx);
 331                }
 332        } else {
 333                /* Save the real directory entry */
 334                realdent = (dir_entry *)slotptr;
 335        }
 336
 337        do {
 338                slotptr--;
 339                if (slot2str(slotptr, l_name, &idx))
 340                        break;
 341        } while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
 342
 343        l_name[idx] = '\0';
 344        if (*l_name == DELETED_FLAG)
 345                *l_name = '\0';
 346        else if (*l_name == aRING)
 347                *l_name = DELETED_FLAG;
 348        downcase(l_name, INT_MAX);
 349
 350        /* Return the real directory entry */
 351        *retdent = realdent;
 352
 353        if (slotptr2) {
 354                memcpy(get_dentfromdir_block, get_contents_vfatname_block,
 355                        mydata->clust_size * mydata->sect_size);
 356                cur_position = (__u8 *)realdent - get_contents_vfatname_block;
 357                *retdent = (dir_entry *) &get_dentfromdir_block[cur_position];
 358        }
 359
 360        return 0;
 361}
 362
 363/*
 364 * Set the entry at index 'entry' in a FAT (12/16/32) table.
 365 */
 366static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
 367{
 368        __u32 bufnum, offset, off16;
 369        __u16 val1, val2;
 370
 371        switch (mydata->fatsize) {
 372        case 32:
 373                bufnum = entry / FAT32BUFSIZE;
 374                offset = entry - bufnum * FAT32BUFSIZE;
 375                break;
 376        case 16:
 377                bufnum = entry / FAT16BUFSIZE;
 378                offset = entry - bufnum * FAT16BUFSIZE;
 379                break;
 380        case 12:
 381                bufnum = entry / FAT12BUFSIZE;
 382                offset = entry - bufnum * FAT12BUFSIZE;
 383                break;
 384        default:
 385                /* Unsupported FAT size */
 386                return -1;
 387        }
 388
 389        /* Read a new block of FAT entries into the cache. */
 390        if (bufnum != mydata->fatbufnum) {
 391                int getsize = FATBUFBLOCKS;
 392                __u8 *bufptr = mydata->fatbuf;
 393                __u32 fatlength = mydata->fatlength;
 394                __u32 startblock = bufnum * FATBUFBLOCKS;
 395
 396                /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
 397                if (startblock + getsize > fatlength)
 398                        getsize = fatlength - startblock;
 399
 400                if (flush_dirty_fat_buffer(mydata) < 0)
 401                        return -1;
 402
 403                startblock += mydata->fat_sect;
 404
 405                if (disk_read(startblock, getsize, bufptr) < 0) {
 406                        debug("Error reading FAT blocks\n");
 407                        return -1;
 408                }
 409                mydata->fatbufnum = bufnum;
 410        }
 411
 412        /* Mark as dirty */
 413        mydata->fat_dirty = 1;
 414
 415        /* Set the actual entry */
 416        switch (mydata->fatsize) {
 417        case 32:
 418                ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
 419                break;
 420        case 16:
 421                ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
 422                break;
 423        case 12:
 424                off16 = (offset * 3) / 4;
 425
 426                switch (offset & 0x3) {
 427                case 0:
 428                        val1 = cpu_to_le16(entry_value) & 0xfff;
 429                        ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff;
 430                        ((__u16 *)mydata->fatbuf)[off16] |= val1;
 431                        break;
 432                case 1:
 433                        val1 = cpu_to_le16(entry_value) & 0xf;
 434                        val2 = (cpu_to_le16(entry_value) >> 4) & 0xff;
 435
 436                        ((__u16 *)mydata->fatbuf)[off16] &= ~0xf000;
 437                        ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 12);
 438
 439                        ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xff;
 440                        ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
 441                        break;
 442                case 2:
 443                        val1 = cpu_to_le16(entry_value) & 0xff;
 444                        val2 = (cpu_to_le16(entry_value) >> 8) & 0xf;
 445
 446                        ((__u16 *)mydata->fatbuf)[off16] &= ~0xff00;
 447                        ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 8);
 448
 449                        ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xf;
 450                        ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
 451                        break;
 452                case 3:
 453                        val1 = cpu_to_le16(entry_value) & 0xfff;
 454                        ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff0;
 455                        ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 4);
 456                        break;
 457                default:
 458                        break;
 459                }
 460
 461                break;
 462        default:
 463                return -1;
 464        }
 465
 466        return 0;
 467}
 468
 469/*
 470 * Determine the next free cluster after 'entry' in a FAT (12/16/32) table
 471 * and link it to 'entry'. EOC marker is not set on returned entry.
 472 */
 473static __u32 determine_fatent(fsdata *mydata, __u32 entry)
 474{
 475        __u32 next_fat, next_entry = entry + 1;
 476
 477        while (1) {
 478                next_fat = get_fatent(mydata, next_entry);
 479                if (next_fat == 0) {
 480                        /* found free entry, link to entry */
 481                        set_fatent_value(mydata, entry, next_entry);
 482                        break;
 483                }
 484                next_entry++;
 485        }
 486        debug("FAT%d: entry: %08x, entry_value: %04x\n",
 487               mydata->fatsize, entry, next_entry);
 488
 489        return next_entry;
 490}
 491
 492/*
 493 * Write at most 'size' bytes from 'buffer' into the specified cluster.
 494 * Return 0 on success, -1 otherwise.
 495 */
 496static int
 497set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
 498             unsigned long size)
 499{
 500        __u32 idx = 0;
 501        __u32 startsect;
 502        int ret;
 503
 504        if (clustnum > 0)
 505                startsect = clust_to_sect(mydata, clustnum);
 506        else
 507                startsect = mydata->rootdir_sect;
 508
 509        debug("clustnum: %d, startsect: %d\n", clustnum, startsect);
 510
 511        if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
 512                ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 513
 514                printf("FAT: Misaligned buffer address (%p)\n", buffer);
 515
 516                while (size >= mydata->sect_size) {
 517                        memcpy(tmpbuf, buffer, mydata->sect_size);
 518                        ret = disk_write(startsect++, 1, tmpbuf);
 519                        if (ret != 1) {
 520                                debug("Error writing data (got %d)\n", ret);
 521                                return -1;
 522                        }
 523
 524                        buffer += mydata->sect_size;
 525                        size -= mydata->sect_size;
 526                }
 527        } else if (size >= mydata->sect_size) {
 528                idx = size / mydata->sect_size;
 529                ret = disk_write(startsect, idx, buffer);
 530                if (ret != idx) {
 531                        debug("Error writing data (got %d)\n", ret);
 532                        return -1;
 533                }
 534
 535                startsect += idx;
 536                idx *= mydata->sect_size;
 537                buffer += idx;
 538                size -= idx;
 539        }
 540
 541        if (size) {
 542                ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 543
 544                memcpy(tmpbuf, buffer, size);
 545                ret = disk_write(startsect, 1, tmpbuf);
 546                if (ret != 1) {
 547                        debug("Error writing data (got %d)\n", ret);
 548                        return -1;
 549                }
 550        }
 551
 552        return 0;
 553}
 554
 555/*
 556 * Find the first empty cluster
 557 */
 558static int find_empty_cluster(fsdata *mydata)
 559{
 560        __u32 fat_val, entry = 3;
 561
 562        while (1) {
 563                fat_val = get_fatent(mydata, entry);
 564                if (fat_val == 0)
 565                        break;
 566                entry++;
 567        }
 568
 569        return entry;
 570}
 571
 572/*
 573 * Write directory entries in 'get_dentfromdir_block' to block device
 574 */
 575static void flush_dir_table(fsdata *mydata, dir_entry **dentptr)
 576{
 577        int dir_newclust = 0;
 578
 579        if (set_cluster(mydata, dir_curclust,
 580                    get_dentfromdir_block,
 581                    mydata->clust_size * mydata->sect_size) != 0) {
 582                printf("error: wrinting directory entry\n");
 583                return;
 584        }
 585        dir_newclust = find_empty_cluster(mydata);
 586        set_fatent_value(mydata, dir_curclust, dir_newclust);
 587        if (mydata->fatsize == 32)
 588                set_fatent_value(mydata, dir_newclust, 0xffffff8);
 589        else if (mydata->fatsize == 16)
 590                set_fatent_value(mydata, dir_newclust, 0xfff8);
 591        else if (mydata->fatsize == 12)
 592                set_fatent_value(mydata, dir_newclust, 0xff8);
 593
 594        dir_curclust = dir_newclust;
 595
 596        if (flush_dirty_fat_buffer(mydata) < 0)
 597                return;
 598
 599        memset(get_dentfromdir_block, 0x00,
 600                mydata->clust_size * mydata->sect_size);
 601
 602        *dentptr = (dir_entry *) get_dentfromdir_block;
 603}
 604
 605/*
 606 * Set empty cluster from 'entry' to the end of a file
 607 */
 608static int clear_fatent(fsdata *mydata, __u32 entry)
 609{
 610        __u32 fat_val;
 611
 612        while (!CHECK_CLUST(entry, mydata->fatsize)) {
 613                fat_val = get_fatent(mydata, entry);
 614                if (fat_val != 0)
 615                        set_fatent_value(mydata, entry, 0);
 616                else
 617                        break;
 618
 619                entry = fat_val;
 620        }
 621
 622        /* Flush fat buffer */
 623        if (flush_dirty_fat_buffer(mydata) < 0)
 624                return -1;
 625
 626        return 0;
 627}
 628
 629/*
 630 * Write at most 'maxsize' bytes from 'buffer' into
 631 * the file associated with 'dentptr'
 632 * Update the number of bytes written in *gotsize and return 0
 633 * or return -1 on fatal errors.
 634 */
 635static int
 636set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
 637              loff_t maxsize, loff_t *gotsize)
 638{
 639        loff_t filesize = FAT2CPU32(dentptr->size);
 640        unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
 641        __u32 curclust = START(dentptr);
 642        __u32 endclust = 0, newclust = 0;
 643        loff_t actsize;
 644
 645        *gotsize = 0;
 646        debug("Filesize: %llu bytes\n", filesize);
 647
 648        if (maxsize > 0 && filesize > maxsize)
 649                filesize = maxsize;
 650
 651        debug("%llu bytes\n", filesize);
 652
 653        if (!curclust) {
 654                if (filesize) {
 655                        debug("error: nonempty clusterless file!\n");
 656                        return -1;
 657                }
 658                return 0;
 659        }
 660
 661        actsize = bytesperclust;
 662        endclust = curclust;
 663        do {
 664                /* search for consecutive clusters */
 665                while (actsize < filesize) {
 666                        newclust = determine_fatent(mydata, endclust);
 667
 668                        if ((newclust - 1) != endclust)
 669                                goto getit;
 670
 671                        if (CHECK_CLUST(newclust, mydata->fatsize)) {
 672                                debug("newclust: 0x%x\n", newclust);
 673                                debug("Invalid FAT entry\n");
 674                                return 0;
 675                        }
 676                        endclust = newclust;
 677                        actsize += bytesperclust;
 678                }
 679
 680                /* set remaining bytes */
 681                actsize = filesize;
 682                if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
 683                        debug("error: writing cluster\n");
 684                        return -1;
 685                }
 686                *gotsize += actsize;
 687
 688                /* Mark end of file in FAT */
 689                if (mydata->fatsize == 12)
 690                        newclust = 0xfff;
 691                else if (mydata->fatsize == 16)
 692                        newclust = 0xffff;
 693                else if (mydata->fatsize == 32)
 694                        newclust = 0xfffffff;
 695                set_fatent_value(mydata, endclust, newclust);
 696
 697                return 0;
 698getit:
 699                if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
 700                        debug("error: writing cluster\n");
 701                        return -1;
 702                }
 703                *gotsize += actsize;
 704                filesize -= actsize;
 705                buffer += actsize;
 706
 707                if (CHECK_CLUST(newclust, mydata->fatsize)) {
 708                        debug("newclust: 0x%x\n", newclust);
 709                        debug("Invalid FAT entry\n");
 710                        return 0;
 711                }
 712                actsize = bytesperclust;
 713                curclust = endclust = newclust;
 714        } while (1);
 715}
 716
 717/*
 718 * Set start cluster in directory entry
 719 */
 720static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
 721                                __u32 start_cluster)
 722{
 723        if (mydata->fatsize == 32)
 724                dentptr->starthi =
 725                        cpu_to_le16((start_cluster & 0xffff0000) >> 16);
 726        dentptr->start = cpu_to_le16(start_cluster & 0xffff);
 727}
 728
 729/*
 730 * Fill dir_entry
 731 */
 732static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
 733        const char *filename, __u32 start_cluster, __u32 size, __u8 attr)
 734{
 735        set_start_cluster(mydata, dentptr, start_cluster);
 736        dentptr->size = cpu_to_le32(size);
 737
 738        dentptr->attr = attr;
 739
 740        set_name(dentptr, filename);
 741}
 742
 743/*
 744 * Check whether adding a file makes the file system to
 745 * exceed the size of the block device
 746 * Return -1 when overflow occurs, otherwise return 0
 747 */
 748static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
 749{
 750        __u32 startsect, sect_num, offset;
 751
 752        if (clustnum > 0) {
 753                startsect = clust_to_sect(mydata, clustnum);
 754        } else {
 755                startsect = mydata->rootdir_sect;
 756        }
 757
 758        sect_num = div_u64_rem(size, mydata->sect_size, &offset);
 759
 760        if (offset != 0)
 761                sect_num++;
 762
 763        if (startsect + sect_num > total_sector)
 764                return -1;
 765        return 0;
 766}
 767
 768/*
 769 * Check if adding several entries exceed one cluster boundary
 770 */
 771static int is_next_clust(fsdata *mydata, dir_entry *dentptr)
 772{
 773        int cur_position;
 774
 775        cur_position = (__u8 *)dentptr - get_dentfromdir_block;
 776
 777        if (cur_position >= mydata->clust_size * mydata->sect_size)
 778                return 1;
 779        else
 780                return 0;
 781}
 782
 783static dir_entry *empty_dentptr;
 784/*
 785 * Find a directory entry based on filename or start cluster number
 786 * If the directory entry is not found,
 787 * the new position for writing a directory entry will be returned
 788 */
 789static dir_entry *find_directory_entry(fsdata *mydata, int startsect,
 790        char *filename, dir_entry *retdent, __u32 start)
 791{
 792        __u32 curclust = sect_to_clust(mydata, startsect);
 793
 794        debug("get_dentfromdir: %s\n", filename);
 795
 796        while (1) {
 797                dir_entry *dentptr;
 798
 799                int i;
 800
 801                if (get_cluster(mydata, curclust, get_dentfromdir_block,
 802                            mydata->clust_size * mydata->sect_size) != 0) {
 803                        printf("Error: reading directory block\n");
 804                        return NULL;
 805                }
 806
 807                dentptr = (dir_entry *)get_dentfromdir_block;
 808
 809                dir_curclust = curclust;
 810
 811                for (i = 0; i < DIRENTSPERCLUST; i++) {
 812                        char s_name[14], l_name[VFAT_MAXLEN_BYTES];
 813
 814                        l_name[0] = '\0';
 815                        if (dentptr->name[0] == DELETED_FLAG) {
 816                                dentptr++;
 817                                if (is_next_clust(mydata, dentptr))
 818                                        break;
 819                                continue;
 820                        }
 821                        if ((dentptr->attr & ATTR_VOLUME)) {
 822                                if (vfat_enabled &&
 823                                    (dentptr->attr & ATTR_VFAT) &&
 824                                    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
 825                                        get_long_file_name(mydata, curclust,
 826                                                     get_dentfromdir_block,
 827                                                     &dentptr, l_name);
 828                                        debug("vfatname: |%s|\n", l_name);
 829                                } else {
 830                                        /* Volume label or VFAT entry */
 831                                        dentptr++;
 832                                        if (is_next_clust(mydata, dentptr))
 833                                                break;
 834                                        continue;
 835                                }
 836                        }
 837                        if (dentptr->name[0] == 0) {
 838                                debug("Dentname == NULL - %d\n", i);
 839                                empty_dentptr = dentptr;
 840                                return NULL;
 841                        }
 842
 843                        get_name(dentptr, s_name);
 844
 845                        if (strcmp(filename, s_name)
 846                            && strcmp(filename, l_name)) {
 847                                debug("Mismatch: |%s|%s|\n",
 848                                        s_name, l_name);
 849                                dentptr++;
 850                                if (is_next_clust(mydata, dentptr))
 851                                        break;
 852                                continue;
 853                        }
 854
 855                        memcpy(retdent, dentptr, sizeof(dir_entry));
 856
 857                        debug("DentName: %s", s_name);
 858                        debug(", start: 0x%x", START(dentptr));
 859                        debug(", size:  0x%x %s\n",
 860                              FAT2CPU32(dentptr->size),
 861                              (dentptr->attr & ATTR_DIR) ?
 862                              "(DIR)" : "");
 863
 864                        return dentptr;
 865                }
 866
 867                /*
 868                 * In FAT16/12, the root dir is locate before data area, shows
 869                 * in following:
 870                 * -------------------------------------------------------------
 871                 * | Boot | FAT1 & 2 | Root dir | Data (start from cluster #2) |
 872                 * -------------------------------------------------------------
 873                 *
 874                 * As a result if curclust is in Root dir, it is a negative
 875                 * number or 0, 1.
 876                 *
 877                 */
 878                if (mydata->fatsize != 32 && (int)curclust <= 1) {
 879                        /* Current clust is in root dir, set to next clust */
 880                        curclust++;
 881                        if ((int)curclust <= 1)
 882                                continue;       /* continue to find */
 883
 884                        /* Reach the end of root dir */
 885                        empty_dentptr = dentptr;
 886                        return NULL;
 887                }
 888
 889                curclust = get_fatent(mydata, dir_curclust);
 890                if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
 891                        empty_dentptr = dentptr;
 892                        return NULL;
 893                }
 894                if (CHECK_CLUST(curclust, mydata->fatsize)) {
 895                        debug("curclust: 0x%x\n", curclust);
 896                        debug("Invalid FAT entry\n");
 897                        return NULL;
 898                }
 899        }
 900
 901        return NULL;
 902}
 903
 904static int do_fat_write(const char *filename, void *buffer, loff_t size,
 905                        loff_t *actwrite)
 906{
 907        dir_entry *dentptr, *retdent;
 908        __u32 startsect;
 909        __u32 start_cluster;
 910        boot_sector bs;
 911        volume_info volinfo;
 912        fsdata datablock;
 913        fsdata *mydata = &datablock;
 914        int cursect;
 915        int ret = -1, name_len;
 916        char l_filename[VFAT_MAXLEN_BYTES];
 917
 918        *actwrite = size;
 919        dir_curclust = 0;
 920
 921        if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
 922                debug("error: reading boot sector\n");
 923                return -1;
 924        }
 925
 926        total_sector = bs.total_sect;
 927        if (total_sector == 0)
 928                total_sector = (int)cur_part_info.size; /* cast of lbaint_t */
 929
 930        if (mydata->fatsize == 32)
 931                mydata->fatlength = bs.fat32_length;
 932        else
 933                mydata->fatlength = bs.fat_length;
 934
 935        mydata->fat_sect = bs.reserved;
 936
 937        cursect = mydata->rootdir_sect
 938                = mydata->fat_sect + mydata->fatlength * bs.fats;
 939        num_of_fats = bs.fats;
 940
 941        mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
 942        mydata->clust_size = bs.cluster_size;
 943
 944        if (mydata->fatsize == 32) {
 945                mydata->data_begin = mydata->rootdir_sect -
 946                                        (mydata->clust_size * 2);
 947        } else {
 948                int rootdir_size;
 949
 950                rootdir_size = ((bs.dir_entries[1]  * (int)256 +
 951                                 bs.dir_entries[0]) *
 952                                 sizeof(dir_entry)) /
 953                                 mydata->sect_size;
 954                mydata->data_begin = mydata->rootdir_sect +
 955                                        rootdir_size -
 956                                        (mydata->clust_size * 2);
 957        }
 958
 959        mydata->fatbufnum = -1;
 960        mydata->fat_dirty = 0;
 961        mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
 962        if (mydata->fatbuf == NULL) {
 963                debug("Error: allocating memory\n");
 964                return -1;
 965        }
 966
 967        if (disk_read(cursect,
 968                (mydata->fatsize == 32) ?
 969                (mydata->clust_size) :
 970                PREFETCH_BLOCKS, do_fat_read_at_block) < 0) {
 971                debug("Error: reading rootdir block\n");
 972                goto exit;
 973        }
 974        dentptr = (dir_entry *) do_fat_read_at_block;
 975
 976        name_len = strlen(filename);
 977        if (name_len >= VFAT_MAXLEN_BYTES)
 978                name_len = VFAT_MAXLEN_BYTES - 1;
 979
 980        memcpy(l_filename, filename, name_len);
 981        l_filename[name_len] = 0; /* terminate the string */
 982        downcase(l_filename, INT_MAX);
 983
 984        startsect = mydata->rootdir_sect;
 985        retdent = find_directory_entry(mydata, startsect,
 986                                l_filename, dentptr, 0);
 987        if (retdent) {
 988                /* Update file size and start_cluster in a directory entry */
 989                retdent->size = cpu_to_le32(size);
 990                start_cluster = START(retdent);
 991
 992                if (start_cluster) {
 993                        if (size) {
 994                                ret = check_overflow(mydata, start_cluster,
 995                                                        size);
 996                                if (ret) {
 997                                        printf("Error: %llu overflow\n", size);
 998                                        goto exit;
 999                                }
1000                        }
1001
1002                        ret = clear_fatent(mydata, start_cluster);
1003                        if (ret) {
1004                                printf("Error: clearing FAT entries\n");
1005                                goto exit;
1006                        }
1007
1008                        if (!size)
1009                                set_start_cluster(mydata, retdent, 0);
1010                } else if (size) {
1011                        ret = start_cluster = find_empty_cluster(mydata);
1012                        if (ret < 0) {
1013                                printf("Error: finding empty cluster\n");
1014                                goto exit;
1015                        }
1016
1017                        ret = check_overflow(mydata, start_cluster, size);
1018                        if (ret) {
1019                                printf("Error: %llu overflow\n", size);
1020                                goto exit;
1021                        }
1022
1023                        set_start_cluster(mydata, retdent, start_cluster);
1024                }
1025        } else {
1026                /* Set short name to set alias checksum field in dir_slot */
1027                set_name(empty_dentptr, filename);
1028                fill_dir_slot(mydata, &empty_dentptr, filename);
1029
1030                if (size) {
1031                        ret = start_cluster = find_empty_cluster(mydata);
1032                        if (ret < 0) {
1033                                printf("Error: finding empty cluster\n");
1034                                goto exit;
1035                        }
1036
1037                        ret = check_overflow(mydata, start_cluster, size);
1038                        if (ret) {
1039                                printf("Error: %llu overflow\n", size);
1040                                goto exit;
1041                        }
1042                } else {
1043                        start_cluster = 0;
1044                }
1045
1046                /* Set attribute as archieve for regular file */
1047                fill_dentry(mydata, empty_dentptr, filename,
1048                        start_cluster, size, 0x20);
1049
1050                retdent = empty_dentptr;
1051        }
1052
1053        ret = set_contents(mydata, retdent, buffer, size, actwrite);
1054        if (ret < 0) {
1055                printf("Error: writing contents\n");
1056                goto exit;
1057        }
1058        debug("attempt to write 0x%llx bytes\n", *actwrite);
1059
1060        /* Flush fat buffer */
1061        ret = flush_dirty_fat_buffer(mydata);
1062        if (ret) {
1063                printf("Error: flush fat buffer\n");
1064                goto exit;
1065        }
1066
1067        /* Write directory table to device */
1068        ret = set_cluster(mydata, dir_curclust, get_dentfromdir_block,
1069                        mydata->clust_size * mydata->sect_size);
1070        if (ret)
1071                printf("Error: writing directory entry\n");
1072
1073exit:
1074        free(mydata->fatbuf);
1075        return ret;
1076}
1077
1078int file_fat_write(const char *filename, void *buffer, loff_t offset,
1079                   loff_t maxsize, loff_t *actwrite)
1080{
1081        if (offset != 0) {
1082                printf("Error: non zero offset is currently not supported.\n");
1083                return -1;
1084        }
1085
1086        printf("writing %s\n", filename);
1087        return do_fat_write(filename, buffer, maxsize, actwrite);
1088}
1089