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);
 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 = mydata->data_begin +
 506                                clustnum * mydata->clust_size;
 507        else
 508                startsect = mydata->rootdir_sect;
 509
 510        debug("clustnum: %d, startsect: %d\n", clustnum, startsect);
 511
 512        if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
 513                ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 514
 515                printf("FAT: Misaligned buffer address (%p)\n", buffer);
 516
 517                while (size >= mydata->sect_size) {
 518                        memcpy(tmpbuf, buffer, mydata->sect_size);
 519                        ret = disk_write(startsect++, 1, tmpbuf);
 520                        if (ret != 1) {
 521                                debug("Error writing data (got %d)\n", ret);
 522                                return -1;
 523                        }
 524
 525                        buffer += mydata->sect_size;
 526                        size -= mydata->sect_size;
 527                }
 528        } else if (size >= mydata->sect_size) {
 529                idx = size / mydata->sect_size;
 530                ret = disk_write(startsect, idx, buffer);
 531                if (ret != idx) {
 532                        debug("Error writing data (got %d)\n", ret);
 533                        return -1;
 534                }
 535
 536                startsect += idx;
 537                idx *= mydata->sect_size;
 538                buffer += idx;
 539                size -= idx;
 540        }
 541
 542        if (size) {
 543                ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 544
 545                memcpy(tmpbuf, buffer, size);
 546                ret = disk_write(startsect, 1, tmpbuf);
 547                if (ret != 1) {
 548                        debug("Error writing data (got %d)\n", ret);
 549                        return -1;
 550                }
 551        }
 552
 553        return 0;
 554}
 555
 556/*
 557 * Find the first empty cluster
 558 */
 559static int find_empty_cluster(fsdata *mydata)
 560{
 561        __u32 fat_val, entry = 3;
 562
 563        while (1) {
 564                fat_val = get_fatent(mydata, entry);
 565                if (fat_val == 0)
 566                        break;
 567                entry++;
 568        }
 569
 570        return entry;
 571}
 572
 573/*
 574 * Write directory entries in 'get_dentfromdir_block' to block device
 575 */
 576static void flush_dir_table(fsdata *mydata, dir_entry **dentptr)
 577{
 578        int dir_newclust = 0;
 579
 580        if (set_cluster(mydata, dir_curclust,
 581                    get_dentfromdir_block,
 582                    mydata->clust_size * mydata->sect_size) != 0) {
 583                printf("error: wrinting directory entry\n");
 584                return;
 585        }
 586        dir_newclust = find_empty_cluster(mydata);
 587        set_fatent_value(mydata, dir_curclust, dir_newclust);
 588        if (mydata->fatsize == 32)
 589                set_fatent_value(mydata, dir_newclust, 0xffffff8);
 590        else if (mydata->fatsize == 16)
 591                set_fatent_value(mydata, dir_newclust, 0xfff8);
 592        else if (mydata->fatsize == 12)
 593                set_fatent_value(mydata, dir_newclust, 0xff8);
 594
 595        dir_curclust = dir_newclust;
 596
 597        if (flush_dirty_fat_buffer(mydata) < 0)
 598                return;
 599
 600        memset(get_dentfromdir_block, 0x00,
 601                mydata->clust_size * mydata->sect_size);
 602
 603        *dentptr = (dir_entry *) get_dentfromdir_block;
 604}
 605
 606/*
 607 * Set empty cluster from 'entry' to the end of a file
 608 */
 609static int clear_fatent(fsdata *mydata, __u32 entry)
 610{
 611        __u32 fat_val;
 612
 613        while (!CHECK_CLUST(entry, mydata->fatsize)) {
 614                fat_val = get_fatent(mydata, entry);
 615                if (fat_val != 0)
 616                        set_fatent_value(mydata, entry, 0);
 617                else
 618                        break;
 619
 620                entry = fat_val;
 621        }
 622
 623        /* Flush fat buffer */
 624        if (flush_dirty_fat_buffer(mydata) < 0)
 625                return -1;
 626
 627        return 0;
 628}
 629
 630/*
 631 * Write at most 'maxsize' bytes from 'buffer' into
 632 * the file associated with 'dentptr'
 633 * Update the number of bytes written in *gotsize and return 0
 634 * or return -1 on fatal errors.
 635 */
 636static int
 637set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
 638              loff_t maxsize, loff_t *gotsize)
 639{
 640        loff_t filesize = FAT2CPU32(dentptr->size);
 641        unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
 642        __u32 curclust = START(dentptr);
 643        __u32 endclust = 0, newclust = 0;
 644        loff_t actsize;
 645
 646        *gotsize = 0;
 647        debug("Filesize: %llu bytes\n", filesize);
 648
 649        if (maxsize > 0 && filesize > maxsize)
 650                filesize = maxsize;
 651
 652        debug("%llu bytes\n", filesize);
 653
 654        if (!curclust) {
 655                if (filesize) {
 656                        debug("error: nonempty clusterless file!\n");
 657                        return -1;
 658                }
 659                return 0;
 660        }
 661
 662        actsize = bytesperclust;
 663        endclust = curclust;
 664        do {
 665                /* search for consecutive clusters */
 666                while (actsize < filesize) {
 667                        newclust = determine_fatent(mydata, endclust);
 668
 669                        if ((newclust - 1) != endclust)
 670                                goto getit;
 671
 672                        if (CHECK_CLUST(newclust, mydata->fatsize)) {
 673                                debug("newclust: 0x%x\n", newclust);
 674                                debug("Invalid FAT entry\n");
 675                                return 0;
 676                        }
 677                        endclust = newclust;
 678                        actsize += bytesperclust;
 679                }
 680
 681                /* set remaining bytes */
 682                actsize = filesize;
 683                if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
 684                        debug("error: writing cluster\n");
 685                        return -1;
 686                }
 687                *gotsize += actsize;
 688
 689                /* Mark end of file in FAT */
 690                if (mydata->fatsize == 12)
 691                        newclust = 0xfff;
 692                else if (mydata->fatsize == 16)
 693                        newclust = 0xffff;
 694                else if (mydata->fatsize == 32)
 695                        newclust = 0xfffffff;
 696                set_fatent_value(mydata, endclust, newclust);
 697
 698                return 0;
 699getit:
 700                if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
 701                        debug("error: writing cluster\n");
 702                        return -1;
 703                }
 704                *gotsize += actsize;
 705                filesize -= actsize;
 706                buffer += actsize;
 707
 708                if (CHECK_CLUST(newclust, mydata->fatsize)) {
 709                        debug("newclust: 0x%x\n", newclust);
 710                        debug("Invalid FAT entry\n");
 711                        return 0;
 712                }
 713                actsize = bytesperclust;
 714                curclust = endclust = newclust;
 715        } while (1);
 716}
 717
 718/*
 719 * Set start cluster in directory entry
 720 */
 721static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
 722                                __u32 start_cluster)
 723{
 724        if (mydata->fatsize == 32)
 725                dentptr->starthi =
 726                        cpu_to_le16((start_cluster & 0xffff0000) >> 16);
 727        dentptr->start = cpu_to_le16(start_cluster & 0xffff);
 728}
 729
 730/*
 731 * Fill dir_entry
 732 */
 733static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
 734        const char *filename, __u32 start_cluster, __u32 size, __u8 attr)
 735{
 736        set_start_cluster(mydata, dentptr, start_cluster);
 737        dentptr->size = cpu_to_le32(size);
 738
 739        dentptr->attr = attr;
 740
 741        set_name(dentptr, filename);
 742}
 743
 744/*
 745 * Check whether adding a file makes the file system to
 746 * exceed the size of the block device
 747 * Return -1 when overflow occurs, otherwise return 0
 748 */
 749static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
 750{
 751        __u32 startsect, sect_num, offset;
 752
 753        if (clustnum > 0) {
 754                startsect = mydata->data_begin +
 755                                clustnum * mydata->clust_size;
 756        } else {
 757                startsect = mydata->rootdir_sect;
 758        }
 759
 760        sect_num = div_u64_rem(size, mydata->sect_size, &offset);
 761
 762        if (offset != 0)
 763                sect_num++;
 764
 765        if (startsect + sect_num > cur_part_info.start + total_sector)
 766                return -1;
 767        return 0;
 768}
 769
 770/*
 771 * Check if adding several entries exceed one cluster boundary
 772 */
 773static int is_next_clust(fsdata *mydata, dir_entry *dentptr)
 774{
 775        int cur_position;
 776
 777        cur_position = (__u8 *)dentptr - get_dentfromdir_block;
 778
 779        if (cur_position >= mydata->clust_size * mydata->sect_size)
 780                return 1;
 781        else
 782                return 0;
 783}
 784
 785static dir_entry *empty_dentptr;
 786/*
 787 * Find a directory entry based on filename or start cluster number
 788 * If the directory entry is not found,
 789 * the new position for writing a directory entry will be returned
 790 */
 791static dir_entry *find_directory_entry(fsdata *mydata, int startsect,
 792        char *filename, dir_entry *retdent, __u32 start)
 793{
 794        __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size;
 795
 796        debug("get_dentfromdir: %s\n", filename);
 797
 798        while (1) {
 799                dir_entry *dentptr;
 800
 801                int i;
 802
 803                if (get_cluster(mydata, curclust, get_dentfromdir_block,
 804                            mydata->clust_size * mydata->sect_size) != 0) {
 805                        printf("Error: reading directory block\n");
 806                        return NULL;
 807                }
 808
 809                dentptr = (dir_entry *)get_dentfromdir_block;
 810
 811                dir_curclust = curclust;
 812
 813                for (i = 0; i < DIRENTSPERCLUST; i++) {
 814                        char s_name[14], l_name[VFAT_MAXLEN_BYTES];
 815
 816                        l_name[0] = '\0';
 817                        if (dentptr->name[0] == DELETED_FLAG) {
 818                                dentptr++;
 819                                if (is_next_clust(mydata, dentptr))
 820                                        break;
 821                                continue;
 822                        }
 823                        if ((dentptr->attr & ATTR_VOLUME)) {
 824                                if (vfat_enabled &&
 825                                    (dentptr->attr & ATTR_VFAT) &&
 826                                    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
 827                                        get_long_file_name(mydata, curclust,
 828                                                     get_dentfromdir_block,
 829                                                     &dentptr, l_name);
 830                                        debug("vfatname: |%s|\n", l_name);
 831                                } else {
 832                                        /* Volume label or VFAT entry */
 833                                        dentptr++;
 834                                        if (is_next_clust(mydata, dentptr))
 835                                                break;
 836                                        continue;
 837                                }
 838                        }
 839                        if (dentptr->name[0] == 0) {
 840                                debug("Dentname == NULL - %d\n", i);
 841                                empty_dentptr = dentptr;
 842                                return NULL;
 843                        }
 844
 845                        get_name(dentptr, s_name);
 846
 847                        if (strcmp(filename, s_name)
 848                            && strcmp(filename, l_name)) {
 849                                debug("Mismatch: |%s|%s|\n",
 850                                        s_name, l_name);
 851                                dentptr++;
 852                                if (is_next_clust(mydata, dentptr))
 853                                        break;
 854                                continue;
 855                        }
 856
 857                        memcpy(retdent, dentptr, sizeof(dir_entry));
 858
 859                        debug("DentName: %s", s_name);
 860                        debug(", start: 0x%x", START(dentptr));
 861                        debug(", size:  0x%x %s\n",
 862                              FAT2CPU32(dentptr->size),
 863                              (dentptr->attr & ATTR_DIR) ?
 864                              "(DIR)" : "");
 865
 866                        return dentptr;
 867                }
 868
 869                /*
 870                 * In FAT16/12, the root dir is locate before data area, shows
 871                 * in following:
 872                 * -------------------------------------------------------------
 873                 * | Boot | FAT1 & 2 | Root dir | Data (start from cluster #2) |
 874                 * -------------------------------------------------------------
 875                 *
 876                 * As a result if curclust is in Root dir, it is a negative
 877                 * number or 0, 1.
 878                 *
 879                 */
 880                if (mydata->fatsize != 32 && (int)curclust <= 1) {
 881                        /* Current clust is in root dir, set to next clust */
 882                        curclust++;
 883                        if ((int)curclust <= 1)
 884                                continue;       /* continue to find */
 885
 886                        /* Reach the end of root dir */
 887                        empty_dentptr = dentptr;
 888                        return NULL;
 889                }
 890
 891                curclust = get_fatent(mydata, dir_curclust);
 892                if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
 893                        empty_dentptr = dentptr;
 894                        return NULL;
 895                }
 896                if (CHECK_CLUST(curclust, mydata->fatsize)) {
 897                        debug("curclust: 0x%x\n", curclust);
 898                        debug("Invalid FAT entry\n");
 899                        return NULL;
 900                }
 901        }
 902
 903        return NULL;
 904}
 905
 906static int do_fat_write(const char *filename, void *buffer, loff_t size,
 907                        loff_t *actwrite)
 908{
 909        dir_entry *dentptr, *retdent;
 910        __u32 startsect;
 911        __u32 start_cluster;
 912        boot_sector bs;
 913        volume_info volinfo;
 914        fsdata datablock;
 915        fsdata *mydata = &datablock;
 916        int cursect;
 917        int ret = -1, name_len;
 918        char l_filename[VFAT_MAXLEN_BYTES];
 919
 920        *actwrite = size;
 921        dir_curclust = 0;
 922
 923        if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
 924                debug("error: reading boot sector\n");
 925                return -1;
 926        }
 927
 928        total_sector = bs.total_sect;
 929        if (total_sector == 0)
 930                total_sector = (int)cur_part_info.size; /* cast of lbaint_t */
 931
 932        if (mydata->fatsize == 32)
 933                mydata->fatlength = bs.fat32_length;
 934        else
 935                mydata->fatlength = bs.fat_length;
 936
 937        mydata->fat_sect = bs.reserved;
 938
 939        cursect = mydata->rootdir_sect
 940                = mydata->fat_sect + mydata->fatlength * bs.fats;
 941        num_of_fats = bs.fats;
 942
 943        mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
 944        mydata->clust_size = bs.cluster_size;
 945
 946        if (mydata->fatsize == 32) {
 947                mydata->data_begin = mydata->rootdir_sect -
 948                                        (mydata->clust_size * 2);
 949        } else {
 950                int rootdir_size;
 951
 952                rootdir_size = ((bs.dir_entries[1]  * (int)256 +
 953                                 bs.dir_entries[0]) *
 954                                 sizeof(dir_entry)) /
 955                                 mydata->sect_size;
 956                mydata->data_begin = mydata->rootdir_sect +
 957                                        rootdir_size -
 958                                        (mydata->clust_size * 2);
 959        }
 960
 961        mydata->fatbufnum = -1;
 962        mydata->fat_dirty = 0;
 963        mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
 964        if (mydata->fatbuf == NULL) {
 965                debug("Error: allocating memory\n");
 966                return -1;
 967        }
 968
 969        if (disk_read(cursect,
 970                (mydata->fatsize == 32) ?
 971                (mydata->clust_size) :
 972                PREFETCH_BLOCKS, do_fat_read_at_block) < 0) {
 973                debug("Error: reading rootdir block\n");
 974                goto exit;
 975        }
 976        dentptr = (dir_entry *) do_fat_read_at_block;
 977
 978        name_len = strlen(filename);
 979        if (name_len >= VFAT_MAXLEN_BYTES)
 980                name_len = VFAT_MAXLEN_BYTES - 1;
 981
 982        memcpy(l_filename, filename, name_len);
 983        l_filename[name_len] = 0; /* terminate the string */
 984        downcase(l_filename);
 985
 986        startsect = mydata->rootdir_sect;
 987        retdent = find_directory_entry(mydata, startsect,
 988                                l_filename, dentptr, 0);
 989        if (retdent) {
 990                /* Update file size and start_cluster in a directory entry */
 991                retdent->size = cpu_to_le32(size);
 992                start_cluster = START(retdent);
 993
 994                if (start_cluster) {
 995                        if (size) {
 996                                ret = check_overflow(mydata, start_cluster,
 997                                                        size);
 998                                if (ret) {
 999                                        printf("Error: %llu overflow\n", size);
1000                                        goto exit;
1001                                }
1002                        }
1003
1004                        ret = clear_fatent(mydata, start_cluster);
1005                        if (ret) {
1006                                printf("Error: clearing FAT entries\n");
1007                                goto exit;
1008                        }
1009
1010                        if (!size)
1011                                set_start_cluster(mydata, retdent, 0);
1012                } else if (size) {
1013                        ret = start_cluster = find_empty_cluster(mydata);
1014                        if (ret < 0) {
1015                                printf("Error: finding empty cluster\n");
1016                                goto exit;
1017                        }
1018
1019                        ret = check_overflow(mydata, start_cluster, size);
1020                        if (ret) {
1021                                printf("Error: %llu overflow\n", size);
1022                                goto exit;
1023                        }
1024
1025                        set_start_cluster(mydata, retdent, start_cluster);
1026                }
1027        } else {
1028                /* Set short name to set alias checksum field in dir_slot */
1029                set_name(empty_dentptr, filename);
1030                fill_dir_slot(mydata, &empty_dentptr, filename);
1031
1032                if (size) {
1033                        ret = start_cluster = find_empty_cluster(mydata);
1034                        if (ret < 0) {
1035                                printf("Error: finding empty cluster\n");
1036                                goto exit;
1037                        }
1038
1039                        ret = check_overflow(mydata, start_cluster, size);
1040                        if (ret) {
1041                                printf("Error: %llu overflow\n", size);
1042                                goto exit;
1043                        }
1044                } else {
1045                        start_cluster = 0;
1046                }
1047
1048                /* Set attribute as archieve for regular file */
1049                fill_dentry(mydata, empty_dentptr, filename,
1050                        start_cluster, size, 0x20);
1051
1052                retdent = empty_dentptr;
1053        }
1054
1055        ret = set_contents(mydata, retdent, buffer, size, actwrite);
1056        if (ret < 0) {
1057                printf("Error: writing contents\n");
1058                goto exit;
1059        }
1060        debug("attempt to write 0x%llx bytes\n", *actwrite);
1061
1062        /* Flush fat buffer */
1063        ret = flush_dirty_fat_buffer(mydata);
1064        if (ret) {
1065                printf("Error: flush fat buffer\n");
1066                goto exit;
1067        }
1068
1069        /* Write directory table to device */
1070        ret = set_cluster(mydata, dir_curclust, get_dentfromdir_block,
1071                        mydata->clust_size * mydata->sect_size);
1072        if (ret)
1073                printf("Error: writing directory entry\n");
1074
1075exit:
1076        free(mydata->fatbuf);
1077        return ret;
1078}
1079
1080int file_fat_write(const char *filename, void *buffer, loff_t offset,
1081                   loff_t maxsize, loff_t *actwrite)
1082{
1083        if (offset != 0) {
1084                printf("Error: non zero offset is currently not supported.\n");
1085                return -1;
1086        }
1087
1088        printf("writing %s\n", filename);
1089        return do_fat_write(filename, buffer, maxsize, actwrite);
1090}
1091