uboot/fs/fat/fat_write.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * fat_write.c
   4 *
   5 * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim
   6 */
   7
   8#include <common.h>
   9#include <command.h>
  10#include <config.h>
  11#include <fat.h>
  12#include <log.h>
  13#include <malloc.h>
  14#include <asm/byteorder.h>
  15#include <part.h>
  16#include <asm/cache.h>
  17#include <linux/ctype.h>
  18#include <div64.h>
  19#include <linux/math64.h>
  20#include "fat.c"
  21
  22static void uppercase(char *str, int len)
  23{
  24        int i;
  25
  26        for (i = 0; i < len; i++) {
  27                *str = toupper(*str);
  28                str++;
  29        }
  30}
  31
  32static int total_sector;
  33static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
  34{
  35        ulong ret;
  36
  37        if (!cur_dev)
  38                return -1;
  39
  40        if (cur_part_info.start + block + nr_blocks >
  41                cur_part_info.start + total_sector) {
  42                printf("error: overflow occurs\n");
  43                return -1;
  44        }
  45
  46        ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf);
  47        if (nr_blocks && ret == 0)
  48                return -1;
  49
  50        return ret;
  51}
  52
  53/**
  54 * set_name() - set short name in directory entry
  55 *
  56 * @dirent:     directory entry
  57 * @filename:   long file name
  58 */
  59static void set_name(dir_entry *dirent, const char *filename)
  60{
  61        char s_name[VFAT_MAXLEN_BYTES];
  62        char *period;
  63        int period_location, len, i, ext_num;
  64
  65        if (filename == NULL)
  66                return;
  67
  68        len = strlen(filename);
  69        if (len == 0)
  70                return;
  71
  72        strncpy(s_name, filename, VFAT_MAXLEN_BYTES - 1);
  73        s_name[VFAT_MAXLEN_BYTES - 1] = '\0';
  74        uppercase(s_name, len);
  75
  76        period = strchr(s_name, '.');
  77        if (period == NULL) {
  78                period_location = len;
  79                ext_num = 0;
  80        } else {
  81                period_location = period - s_name;
  82                ext_num = len - period_location - 1;
  83        }
  84
  85        /* Pad spaces when the length of file name is shorter than eight */
  86        if (period_location < 8) {
  87                memcpy(dirent->name, s_name, period_location);
  88                for (i = period_location; i < 8; i++)
  89                        dirent->name[i] = ' ';
  90        } else if (period_location == 8) {
  91                memcpy(dirent->name, s_name, period_location);
  92        } else {
  93                memcpy(dirent->name, s_name, 6);
  94                /*
  95                 * TODO: Translating two long names with the same first six
  96                 *       characters to the same short name is utterly wrong.
  97                 *       Short names must be unique.
  98                 */
  99                dirent->name[6] = '~';
 100                dirent->name[7] = '1';
 101        }
 102
 103        if (ext_num < 3) {
 104                memcpy(dirent->ext, s_name + period_location + 1, ext_num);
 105                for (i = ext_num; i < 3; i++)
 106                        dirent->ext[i] = ' ';
 107        } else
 108                memcpy(dirent->ext, s_name + period_location + 1, 3);
 109
 110        debug("name : %s\n", dirent->name);
 111        debug("ext : %s\n", dirent->ext);
 112}
 113
 114/*
 115 * Write fat buffer into block device
 116 */
 117static int flush_dirty_fat_buffer(fsdata *mydata)
 118{
 119        int getsize = FATBUFBLOCKS;
 120        __u32 fatlength = mydata->fatlength;
 121        __u8 *bufptr = mydata->fatbuf;
 122        __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
 123
 124        debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum,
 125              (int)mydata->fat_dirty);
 126
 127        if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1))
 128                return 0;
 129
 130        /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
 131        if (startblock + getsize > fatlength)
 132                getsize = fatlength - startblock;
 133
 134        startblock += mydata->fat_sect;
 135
 136        /* Write FAT buf */
 137        if (disk_write(startblock, getsize, bufptr) < 0) {
 138                debug("error: writing FAT blocks\n");
 139                return -1;
 140        }
 141
 142        if (mydata->fats == 2) {
 143                /* Update corresponding second FAT blocks */
 144                startblock += mydata->fatlength;
 145                if (disk_write(startblock, getsize, bufptr) < 0) {
 146                        debug("error: writing second FAT blocks\n");
 147                        return -1;
 148                }
 149        }
 150        mydata->fat_dirty = 0;
 151
 152        return 0;
 153}
 154
 155/*
 156 * Set the file name information from 'name' into 'slotptr',
 157 */
 158static int str2slot(dir_slot *slotptr, const char *name, int *idx)
 159{
 160        int j, end_idx = 0;
 161
 162        for (j = 0; j <= 8; j += 2) {
 163                if (name[*idx] == 0x00) {
 164                        slotptr->name0_4[j] = 0;
 165                        slotptr->name0_4[j + 1] = 0;
 166                        end_idx++;
 167                        goto name0_4;
 168                }
 169                slotptr->name0_4[j] = name[*idx];
 170                (*idx)++;
 171                end_idx++;
 172        }
 173        for (j = 0; j <= 10; j += 2) {
 174                if (name[*idx] == 0x00) {
 175                        slotptr->name5_10[j] = 0;
 176                        slotptr->name5_10[j + 1] = 0;
 177                        end_idx++;
 178                        goto name5_10;
 179                }
 180                slotptr->name5_10[j] = name[*idx];
 181                (*idx)++;
 182                end_idx++;
 183        }
 184        for (j = 0; j <= 2; j += 2) {
 185                if (name[*idx] == 0x00) {
 186                        slotptr->name11_12[j] = 0;
 187                        slotptr->name11_12[j + 1] = 0;
 188                        end_idx++;
 189                        goto name11_12;
 190                }
 191                slotptr->name11_12[j] = name[*idx];
 192                (*idx)++;
 193                end_idx++;
 194        }
 195
 196        if (name[*idx] == 0x00)
 197                return 1;
 198
 199        return 0;
 200/* Not used characters are filled with 0xff 0xff */
 201name0_4:
 202        for (; end_idx < 5; end_idx++) {
 203                slotptr->name0_4[end_idx * 2] = 0xff;
 204                slotptr->name0_4[end_idx * 2 + 1] = 0xff;
 205        }
 206        end_idx = 5;
 207name5_10:
 208        end_idx -= 5;
 209        for (; end_idx < 6; end_idx++) {
 210                slotptr->name5_10[end_idx * 2] = 0xff;
 211                slotptr->name5_10[end_idx * 2 + 1] = 0xff;
 212        }
 213        end_idx = 11;
 214name11_12:
 215        end_idx -= 11;
 216        for (; end_idx < 2; end_idx++) {
 217                slotptr->name11_12[end_idx * 2] = 0xff;
 218                slotptr->name11_12[end_idx * 2 + 1] = 0xff;
 219        }
 220
 221        return 1;
 222}
 223
 224static int new_dir_table(fat_itr *itr);
 225static int flush_dir(fat_itr *itr);
 226
 227/*
 228 * Fill dir_slot entries with appropriate name, id, and attr
 229 * 'itr' will point to a next entry
 230 */
 231static int
 232fill_dir_slot(fat_itr *itr, const char *l_name)
 233{
 234        __u8 temp_dir_slot_buffer[MAX_LFN_SLOT * sizeof(dir_slot)];
 235        dir_slot *slotptr = (dir_slot *)temp_dir_slot_buffer;
 236        __u8 counter = 0, checksum;
 237        int idx = 0, ret;
 238
 239        /* Get short file name checksum value */
 240        checksum = mkcksum(itr->dent->name, itr->dent->ext);
 241
 242        do {
 243                memset(slotptr, 0x00, sizeof(dir_slot));
 244                ret = str2slot(slotptr, l_name, &idx);
 245                slotptr->id = ++counter;
 246                slotptr->attr = ATTR_VFAT;
 247                slotptr->alias_checksum = checksum;
 248                slotptr++;
 249        } while (ret == 0);
 250
 251        slotptr--;
 252        slotptr->id |= LAST_LONG_ENTRY_MASK;
 253
 254        while (counter >= 1) {
 255                memcpy(itr->dent, slotptr, sizeof(dir_slot));
 256                slotptr--;
 257                counter--;
 258
 259                if (itr->remaining == 0)
 260                        flush_dir(itr);
 261
 262                /* allocate a cluster for more entries */
 263                if (!fat_itr_next(itr) && !itr->dent)
 264                        if ((itr->is_root && itr->fsdata->fatsize != 32) ||
 265                            new_dir_table(itr))
 266                                return -1;
 267        }
 268
 269        return 0;
 270}
 271
 272/*
 273 * Set the entry at index 'entry' in a FAT (12/16/32) table.
 274 */
 275static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
 276{
 277        __u32 bufnum, offset, off16;
 278        __u16 val1, val2;
 279
 280        switch (mydata->fatsize) {
 281        case 32:
 282                bufnum = entry / FAT32BUFSIZE;
 283                offset = entry - bufnum * FAT32BUFSIZE;
 284                break;
 285        case 16:
 286                bufnum = entry / FAT16BUFSIZE;
 287                offset = entry - bufnum * FAT16BUFSIZE;
 288                break;
 289        case 12:
 290                bufnum = entry / FAT12BUFSIZE;
 291                offset = entry - bufnum * FAT12BUFSIZE;
 292                break;
 293        default:
 294                /* Unsupported FAT size */
 295                return -1;
 296        }
 297
 298        /* Read a new block of FAT entries into the cache. */
 299        if (bufnum != mydata->fatbufnum) {
 300                int getsize = FATBUFBLOCKS;
 301                __u8 *bufptr = mydata->fatbuf;
 302                __u32 fatlength = mydata->fatlength;
 303                __u32 startblock = bufnum * FATBUFBLOCKS;
 304
 305                /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
 306                if (startblock + getsize > fatlength)
 307                        getsize = fatlength - startblock;
 308
 309                if (flush_dirty_fat_buffer(mydata) < 0)
 310                        return -1;
 311
 312                startblock += mydata->fat_sect;
 313
 314                if (disk_read(startblock, getsize, bufptr) < 0) {
 315                        debug("Error reading FAT blocks\n");
 316                        return -1;
 317                }
 318                mydata->fatbufnum = bufnum;
 319        }
 320
 321        /* Mark as dirty */
 322        mydata->fat_dirty = 1;
 323
 324        /* Set the actual entry */
 325        switch (mydata->fatsize) {
 326        case 32:
 327                ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
 328                break;
 329        case 16:
 330                ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
 331                break;
 332        case 12:
 333                off16 = (offset * 3) / 4;
 334
 335                switch (offset & 0x3) {
 336                case 0:
 337                        val1 = cpu_to_le16(entry_value) & 0xfff;
 338                        ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff;
 339                        ((__u16 *)mydata->fatbuf)[off16] |= val1;
 340                        break;
 341                case 1:
 342                        val1 = cpu_to_le16(entry_value) & 0xf;
 343                        val2 = (cpu_to_le16(entry_value) >> 4) & 0xff;
 344
 345                        ((__u16 *)mydata->fatbuf)[off16] &= ~0xf000;
 346                        ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 12);
 347
 348                        ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xff;
 349                        ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
 350                        break;
 351                case 2:
 352                        val1 = cpu_to_le16(entry_value) & 0xff;
 353                        val2 = (cpu_to_le16(entry_value) >> 8) & 0xf;
 354
 355                        ((__u16 *)mydata->fatbuf)[off16] &= ~0xff00;
 356                        ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 8);
 357
 358                        ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xf;
 359                        ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
 360                        break;
 361                case 3:
 362                        val1 = cpu_to_le16(entry_value) & 0xfff;
 363                        ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff0;
 364                        ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 4);
 365                        break;
 366                default:
 367                        break;
 368                }
 369
 370                break;
 371        default:
 372                return -1;
 373        }
 374
 375        return 0;
 376}
 377
 378/*
 379 * Determine the next free cluster after 'entry' in a FAT (12/16/32) table
 380 * and link it to 'entry'. EOC marker is not set on returned entry.
 381 */
 382static __u32 determine_fatent(fsdata *mydata, __u32 entry)
 383{
 384        __u32 next_fat, next_entry = entry + 1;
 385
 386        while (1) {
 387                next_fat = get_fatent(mydata, next_entry);
 388                if (next_fat == 0) {
 389                        /* found free entry, link to entry */
 390                        set_fatent_value(mydata, entry, next_entry);
 391                        break;
 392                }
 393                next_entry++;
 394        }
 395        debug("FAT%d: entry: %08x, entry_value: %04x\n",
 396               mydata->fatsize, entry, next_entry);
 397
 398        return next_entry;
 399}
 400
 401/**
 402 * set_sectors() - write data to sectors
 403 *
 404 * Write 'size' bytes from 'buffer' into the specified sector.
 405 *
 406 * @mydata:     data to be written
 407 * @startsect:  sector to be written to
 408 * @buffer:     data to be written
 409 * @size:       bytes to be written (but not more than the size of a cluster)
 410 * Return:      0 on success, -1 otherwise
 411 */
 412static int
 413set_sectors(fsdata *mydata, u32 startsect, u8 *buffer, u32 size)
 414{
 415        u32 nsects = 0;
 416        int ret;
 417
 418        debug("startsect: %d\n", startsect);
 419
 420        if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
 421                ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 422
 423                debug("FAT: Misaligned buffer address (%p)\n", buffer);
 424
 425                while (size >= mydata->sect_size) {
 426                        memcpy(tmpbuf, buffer, mydata->sect_size);
 427                        ret = disk_write(startsect++, 1, tmpbuf);
 428                        if (ret != 1) {
 429                                debug("Error writing data (got %d)\n", ret);
 430                                return -1;
 431                        }
 432
 433                        buffer += mydata->sect_size;
 434                        size -= mydata->sect_size;
 435                }
 436        } else if (size >= mydata->sect_size) {
 437                nsects = size / mydata->sect_size;
 438                ret = disk_write(startsect, nsects, buffer);
 439                if (ret != nsects) {
 440                        debug("Error writing data (got %d)\n", ret);
 441                        return -1;
 442                }
 443
 444                startsect += nsects;
 445                buffer += nsects * mydata->sect_size;
 446                size -= nsects * mydata->sect_size;
 447        }
 448
 449        if (size) {
 450                ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 451                /* Do not leak content of stack */
 452                memset(tmpbuf, 0, mydata->sect_size);
 453                memcpy(tmpbuf, buffer, size);
 454                ret = disk_write(startsect, 1, tmpbuf);
 455                if (ret != 1) {
 456                        debug("Error writing data (got %d)\n", ret);
 457                        return -1;
 458                }
 459        }
 460
 461        return 0;
 462}
 463
 464/**
 465 * set_cluster() - write data to cluster
 466 *
 467 * Write 'size' bytes from 'buffer' into the specified cluster.
 468 *
 469 * @mydata:     data to be written
 470 * @clustnum:   cluster to be written to
 471 * @buffer:     data to be written
 472 * @size:       bytes to be written (but not more than the size of a cluster)
 473 * Return:      0 on success, -1 otherwise
 474 */
 475static int
 476set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size)
 477{
 478        return set_sectors(mydata, clust_to_sect(mydata, clustnum),
 479                           buffer, size);
 480}
 481
 482static int
 483flush_dir(fat_itr *itr)
 484{
 485        fsdata *mydata = itr->fsdata;
 486        u32 startsect, sect_offset, nsects;
 487
 488        if (!itr->is_root || mydata->fatsize == 32)
 489                return set_cluster(mydata, itr->clust, itr->block,
 490                                   mydata->clust_size * mydata->sect_size);
 491
 492        sect_offset = itr->clust * mydata->clust_size;
 493        startsect = mydata->rootdir_sect + sect_offset;
 494        /* do not write past the end of rootdir */
 495        nsects = min_t(u32, mydata->clust_size,
 496                       mydata->rootdir_size - sect_offset);
 497
 498        return set_sectors(mydata, startsect, itr->block,
 499                           nsects * mydata->sect_size);
 500}
 501
 502/*
 503 * Read and modify data on existing and consecutive cluster blocks
 504 */
 505static int
 506get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
 507                loff_t size, loff_t *gotsize)
 508{
 509        static u8 *tmpbuf_cluster;
 510        unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
 511        __u32 startsect;
 512        loff_t wsize;
 513        int clustcount, i, ret;
 514
 515        *gotsize = 0;
 516        if (!size)
 517                return 0;
 518
 519        if (!tmpbuf_cluster) {
 520                tmpbuf_cluster = memalign(ARCH_DMA_MINALIGN, MAX_CLUSTSIZE);
 521                if (!tmpbuf_cluster)
 522                        return -1;
 523        }
 524
 525        assert(pos < bytesperclust);
 526        startsect = clust_to_sect(mydata, clustnum);
 527
 528        debug("clustnum: %d, startsect: %d, pos: %lld\n",
 529              clustnum, startsect, pos);
 530
 531        /* partial write at beginning */
 532        if (pos) {
 533                wsize = min(bytesperclust - pos, size);
 534                ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
 535                if (ret != mydata->clust_size) {
 536                        debug("Error reading data (got %d)\n", ret);
 537                        return -1;
 538                }
 539
 540                memcpy(tmpbuf_cluster + pos, buffer, wsize);
 541                ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
 542                if (ret != mydata->clust_size) {
 543                        debug("Error writing data (got %d)\n", ret);
 544                        return -1;
 545                }
 546
 547                size -= wsize;
 548                buffer += wsize;
 549                *gotsize += wsize;
 550
 551                startsect += mydata->clust_size;
 552
 553                if (!size)
 554                        return 0;
 555        }
 556
 557        /* full-cluster write */
 558        if (size >= bytesperclust) {
 559                clustcount = lldiv(size, bytesperclust);
 560
 561                if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) {
 562                        wsize = clustcount * bytesperclust;
 563                        ret = disk_write(startsect,
 564                                         clustcount * mydata->clust_size,
 565                                         buffer);
 566                        if (ret != clustcount * mydata->clust_size) {
 567                                debug("Error writing data (got %d)\n", ret);
 568                                return -1;
 569                        }
 570
 571                        size -= wsize;
 572                        buffer += wsize;
 573                        *gotsize += wsize;
 574
 575                        startsect += clustcount * mydata->clust_size;
 576                } else {
 577                        for (i = 0; i < clustcount; i++) {
 578                                memcpy(tmpbuf_cluster, buffer, bytesperclust);
 579                                ret = disk_write(startsect,
 580                                                 mydata->clust_size,
 581                                                 tmpbuf_cluster);
 582                                if (ret != mydata->clust_size) {
 583                                        debug("Error writing data (got %d)\n",
 584                                              ret);
 585                                        return -1;
 586                                }
 587
 588                                size -= bytesperclust;
 589                                buffer += bytesperclust;
 590                                *gotsize += bytesperclust;
 591
 592                                startsect += mydata->clust_size;
 593                        }
 594                }
 595        }
 596
 597        /* partial write at end */
 598        if (size) {
 599                wsize = size;
 600                ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
 601                if (ret != mydata->clust_size) {
 602                        debug("Error reading data (got %d)\n", ret);
 603                        return -1;
 604                }
 605                memcpy(tmpbuf_cluster, buffer, wsize);
 606                ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
 607                if (ret != mydata->clust_size) {
 608                        debug("Error writing data (got %d)\n", ret);
 609                        return -1;
 610                }
 611
 612                size -= wsize;
 613                buffer += wsize;
 614                *gotsize += wsize;
 615        }
 616
 617        assert(!size);
 618
 619        return 0;
 620}
 621
 622/*
 623 * Find the first empty cluster
 624 */
 625static int find_empty_cluster(fsdata *mydata)
 626{
 627        __u32 fat_val, entry = 3;
 628
 629        while (1) {
 630                fat_val = get_fatent(mydata, entry);
 631                if (fat_val == 0)
 632                        break;
 633                entry++;
 634        }
 635
 636        return entry;
 637}
 638
 639/*
 640 * Allocate a cluster for additional directory entries
 641 */
 642static int new_dir_table(fat_itr *itr)
 643{
 644        fsdata *mydata = itr->fsdata;
 645        int dir_newclust = 0;
 646        unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
 647
 648        dir_newclust = find_empty_cluster(mydata);
 649        set_fatent_value(mydata, itr->clust, dir_newclust);
 650        if (mydata->fatsize == 32)
 651                set_fatent_value(mydata, dir_newclust, 0xffffff8);
 652        else if (mydata->fatsize == 16)
 653                set_fatent_value(mydata, dir_newclust, 0xfff8);
 654        else if (mydata->fatsize == 12)
 655                set_fatent_value(mydata, dir_newclust, 0xff8);
 656
 657        itr->clust = dir_newclust;
 658        itr->next_clust = dir_newclust;
 659
 660        if (flush_dirty_fat_buffer(mydata) < 0)
 661                return -1;
 662
 663        memset(itr->block, 0x00, bytesperclust);
 664
 665        itr->dent = (dir_entry *)itr->block;
 666        itr->last_cluster = 1;
 667        itr->remaining = bytesperclust / sizeof(dir_entry) - 1;
 668
 669        return 0;
 670}
 671
 672/*
 673 * Set empty cluster from 'entry' to the end of a file
 674 */
 675static int clear_fatent(fsdata *mydata, __u32 entry)
 676{
 677        __u32 fat_val;
 678
 679        while (!CHECK_CLUST(entry, mydata->fatsize)) {
 680                fat_val = get_fatent(mydata, entry);
 681                if (fat_val != 0)
 682                        set_fatent_value(mydata, entry, 0);
 683                else
 684                        break;
 685
 686                entry = fat_val;
 687        }
 688
 689        /* Flush fat buffer */
 690        if (flush_dirty_fat_buffer(mydata) < 0)
 691                return -1;
 692
 693        return 0;
 694}
 695
 696/*
 697 * Set start cluster in directory entry
 698 */
 699static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
 700                              __u32 start_cluster)
 701{
 702        if (mydata->fatsize == 32)
 703                dentptr->starthi =
 704                        cpu_to_le16((start_cluster & 0xffff0000) >> 16);
 705        dentptr->start = cpu_to_le16(start_cluster & 0xffff);
 706}
 707
 708/*
 709 * Check whether adding a file makes the file system to
 710 * exceed the size of the block device
 711 * Return -1 when overflow occurs, otherwise return 0
 712 */
 713static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
 714{
 715        __u32 startsect, sect_num, offset;
 716
 717        if (clustnum > 0)
 718                startsect = clust_to_sect(mydata, clustnum);
 719        else
 720                startsect = mydata->rootdir_sect;
 721
 722        sect_num = div_u64_rem(size, mydata->sect_size, &offset);
 723
 724        if (offset != 0)
 725                sect_num++;
 726
 727        if (startsect + sect_num > total_sector)
 728                return -1;
 729        return 0;
 730}
 731
 732/*
 733 * Write at most 'maxsize' bytes from 'buffer' into
 734 * the file associated with 'dentptr'
 735 * Update the number of bytes written in *gotsize and return 0
 736 * or return -1 on fatal errors.
 737 */
 738static int
 739set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer,
 740             loff_t maxsize, loff_t *gotsize)
 741{
 742        unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
 743        __u32 curclust = START(dentptr);
 744        __u32 endclust = 0, newclust = 0;
 745        u64 cur_pos, filesize;
 746        loff_t offset, actsize, wsize;
 747
 748        *gotsize = 0;
 749        filesize = pos + maxsize;
 750
 751        debug("%llu bytes\n", filesize);
 752
 753        if (!filesize) {
 754                if (!curclust)
 755                        return 0;
 756                if (!CHECK_CLUST(curclust, mydata->fatsize) ||
 757                    IS_LAST_CLUST(curclust, mydata->fatsize)) {
 758                        clear_fatent(mydata, curclust);
 759                        set_start_cluster(mydata, dentptr, 0);
 760                        return 0;
 761                }
 762                debug("curclust: 0x%x\n", curclust);
 763                debug("Invalid FAT entry\n");
 764                return -1;
 765        }
 766
 767        if (!curclust) {
 768                assert(pos == 0);
 769                goto set_clusters;
 770        }
 771
 772        /* go to cluster at pos */
 773        cur_pos = bytesperclust;
 774        while (1) {
 775                if (pos <= cur_pos)
 776                        break;
 777                if (IS_LAST_CLUST(curclust, mydata->fatsize))
 778                        break;
 779
 780                newclust = get_fatent(mydata, curclust);
 781                if (!IS_LAST_CLUST(newclust, mydata->fatsize) &&
 782                    CHECK_CLUST(newclust, mydata->fatsize)) {
 783                        debug("curclust: 0x%x\n", curclust);
 784                        debug("Invalid FAT entry\n");
 785                        return -1;
 786                }
 787
 788                cur_pos += bytesperclust;
 789                curclust = newclust;
 790        }
 791        if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
 792                assert(pos == cur_pos);
 793                goto set_clusters;
 794        }
 795
 796        assert(pos < cur_pos);
 797        cur_pos -= bytesperclust;
 798
 799        /* overwrite */
 800        assert(IS_LAST_CLUST(curclust, mydata->fatsize) ||
 801               !CHECK_CLUST(curclust, mydata->fatsize));
 802
 803        while (1) {
 804                /* search for allocated consecutive clusters */
 805                actsize = bytesperclust;
 806                endclust = curclust;
 807                while (1) {
 808                        if (filesize <= (cur_pos + actsize))
 809                                break;
 810
 811                        newclust = get_fatent(mydata, endclust);
 812
 813                        if (newclust != endclust + 1)
 814                                break;
 815                        if (IS_LAST_CLUST(newclust, mydata->fatsize))
 816                                break;
 817                        if (CHECK_CLUST(newclust, mydata->fatsize)) {
 818                                debug("curclust: 0x%x\n", curclust);
 819                                debug("Invalid FAT entry\n");
 820                                return -1;
 821                        }
 822
 823                        actsize += bytesperclust;
 824                        endclust = newclust;
 825                }
 826
 827                /* overwrite to <curclust..endclust> */
 828                if (pos < cur_pos)
 829                        offset = 0;
 830                else
 831                        offset = pos - cur_pos;
 832                wsize = min_t(unsigned long long, actsize, filesize - cur_pos);
 833                wsize -= offset;
 834
 835                if (get_set_cluster(mydata, curclust, offset,
 836                                    buffer, wsize, &actsize)) {
 837                        printf("Error get-and-setting cluster\n");
 838                        return -1;
 839                }
 840                buffer += wsize;
 841                *gotsize += wsize;
 842                cur_pos += offset + wsize;
 843
 844                if (filesize <= cur_pos)
 845                        break;
 846
 847                if (IS_LAST_CLUST(newclust, mydata->fatsize))
 848                        /* no more clusters */
 849                        break;
 850
 851                curclust = newclust;
 852        }
 853
 854        if (filesize <= cur_pos) {
 855                /* no more write */
 856                newclust = get_fatent(mydata, endclust);
 857                if (!IS_LAST_CLUST(newclust, mydata->fatsize)) {
 858                        /* truncate the rest */
 859                        clear_fatent(mydata, newclust);
 860
 861                        /* Mark end of file in FAT */
 862                        if (mydata->fatsize == 12)
 863                                newclust = 0xfff;
 864                        else if (mydata->fatsize == 16)
 865                                newclust = 0xffff;
 866                        else if (mydata->fatsize == 32)
 867                                newclust = 0xfffffff;
 868                        set_fatent_value(mydata, endclust, newclust);
 869                }
 870
 871                return 0;
 872        }
 873
 874        curclust = endclust;
 875        filesize -= cur_pos;
 876        assert(!do_div(cur_pos, bytesperclust));
 877
 878set_clusters:
 879        /* allocate and write */
 880        assert(!pos);
 881
 882        /* Assure that curclust is valid */
 883        if (!curclust) {
 884                curclust = find_empty_cluster(mydata);
 885                set_start_cluster(mydata, dentptr, curclust);
 886        } else {
 887                newclust = get_fatent(mydata, curclust);
 888
 889                if (IS_LAST_CLUST(newclust, mydata->fatsize)) {
 890                        newclust = determine_fatent(mydata, curclust);
 891                        set_fatent_value(mydata, curclust, newclust);
 892                        curclust = newclust;
 893                } else {
 894                        debug("error: something wrong\n");
 895                        return -1;
 896                }
 897        }
 898
 899        /* TODO: already partially written */
 900        if (check_overflow(mydata, curclust, filesize)) {
 901                printf("Error: no space left: %llu\n", filesize);
 902                return -1;
 903        }
 904
 905        actsize = bytesperclust;
 906        endclust = curclust;
 907        do {
 908                /* search for consecutive clusters */
 909                while (actsize < filesize) {
 910                        newclust = determine_fatent(mydata, endclust);
 911
 912                        if ((newclust - 1) != endclust)
 913                                /* write to <curclust..endclust> */
 914                                goto getit;
 915
 916                        if (CHECK_CLUST(newclust, mydata->fatsize)) {
 917                                debug("newclust: 0x%x\n", newclust);
 918                                debug("Invalid FAT entry\n");
 919                                return 0;
 920                        }
 921                        endclust = newclust;
 922                        actsize += bytesperclust;
 923                }
 924
 925                /* set remaining bytes */
 926                actsize = filesize;
 927                if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
 928                        debug("error: writing cluster\n");
 929                        return -1;
 930                }
 931                *gotsize += actsize;
 932
 933                /* Mark end of file in FAT */
 934                if (mydata->fatsize == 12)
 935                        newclust = 0xfff;
 936                else if (mydata->fatsize == 16)
 937                        newclust = 0xffff;
 938                else if (mydata->fatsize == 32)
 939                        newclust = 0xfffffff;
 940                set_fatent_value(mydata, endclust, newclust);
 941
 942                return 0;
 943getit:
 944                if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
 945                        debug("error: writing cluster\n");
 946                        return -1;
 947                }
 948                *gotsize += actsize;
 949                filesize -= actsize;
 950                buffer += actsize;
 951
 952                if (CHECK_CLUST(newclust, mydata->fatsize)) {
 953                        debug("newclust: 0x%x\n", newclust);
 954                        debug("Invalid FAT entry\n");
 955                        return 0;
 956                }
 957                actsize = bytesperclust;
 958                curclust = endclust = newclust;
 959        } while (1);
 960
 961        return 0;
 962}
 963
 964/*
 965 * Fill dir_entry
 966 */
 967static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
 968        const char *filename, __u32 start_cluster, __u32 size, __u8 attr)
 969{
 970        set_start_cluster(mydata, dentptr, start_cluster);
 971        dentptr->size = cpu_to_le32(size);
 972
 973        dentptr->attr = attr;
 974
 975        set_name(dentptr, filename);
 976}
 977
 978/*
 979 * Find a directory entry based on filename or start cluster number
 980 * If the directory entry is not found,
 981 * the new position for writing a directory entry will be returned
 982 */
 983static dir_entry *find_directory_entry(fat_itr *itr, char *filename)
 984{
 985        int match = 0;
 986
 987        while (fat_itr_next(itr)) {
 988                /* check both long and short name: */
 989                if (!strcasecmp(filename, itr->name))
 990                        match = 1;
 991                else if (itr->name != itr->s_name &&
 992                         !strcasecmp(filename, itr->s_name))
 993                        match = 1;
 994
 995                if (!match)
 996                        continue;
 997
 998                if (itr->dent->name[0] == '\0')
 999                        return NULL;
1000                else
1001                        return itr->dent;
1002        }
1003
1004        /* allocate a cluster for more entries */
1005        if (!itr->dent &&
1006            (!itr->is_root || itr->fsdata->fatsize == 32) &&
1007            new_dir_table(itr))
1008                /* indicate that allocating dent failed */
1009                itr->dent = NULL;
1010
1011        return NULL;
1012}
1013
1014static int split_filename(char *filename, char **dirname, char **basename)
1015{
1016        char *p, *last_slash, *last_slash_cont;
1017
1018again:
1019        p = filename;
1020        last_slash = NULL;
1021        last_slash_cont = NULL;
1022        while (*p) {
1023                if (ISDIRDELIM(*p)) {
1024                        last_slash = p;
1025                        last_slash_cont = p;
1026                        /* continuous slashes */
1027                        while (ISDIRDELIM(*p))
1028                                last_slash_cont = p++;
1029                        if (!*p)
1030                                break;
1031                }
1032                p++;
1033        }
1034
1035        if (last_slash) {
1036                if (last_slash_cont == (filename + strlen(filename) - 1)) {
1037                        /* remove trailing slashes */
1038                        *last_slash = '\0';
1039                        goto again;
1040                }
1041
1042                if (last_slash == filename) {
1043                        /* avoid ""(null) directory */
1044                        *dirname = "/";
1045                } else {
1046                        *last_slash = '\0';
1047                        *dirname = filename;
1048                }
1049
1050                *last_slash_cont = '\0';
1051                *basename = last_slash_cont + 1;
1052        } else {
1053                *dirname = "/"; /* root by default */
1054                *basename = filename;
1055        }
1056
1057        return 0;
1058}
1059
1060/**
1061 * normalize_longname() - check long file name and convert to lower case
1062 *
1063 * We assume here that the FAT file system is using an 8bit code page.
1064 * Linux typically uses CP437, EDK2 assumes CP1250.
1065 *
1066 * @l_filename: preallocated buffer receiving the normalized name
1067 * @filename:   filename to normalize
1068 * Return:      0 on success, -1 on failure
1069 */
1070static int normalize_longname(char *l_filename, const char *filename)
1071{
1072        const char *p, illegal[] = "<>:\"/\\|?*";
1073
1074        if (strlen(filename) >= VFAT_MAXLEN_BYTES)
1075                return -1;
1076
1077        for (p = filename; *p; ++p) {
1078                if ((unsigned char)*p < 0x20)
1079                        return -1;
1080                if (strchr(illegal, *p))
1081                        return -1;
1082        }
1083
1084        strcpy(l_filename, filename);
1085        downcase(l_filename, VFAT_MAXLEN_BYTES);
1086
1087        return 0;
1088}
1089
1090int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
1091                      loff_t size, loff_t *actwrite)
1092{
1093        dir_entry *retdent;
1094        fsdata datablock = { .fatbuf = NULL, };
1095        fsdata *mydata = &datablock;
1096        fat_itr *itr = NULL;
1097        int ret = -1;
1098        char *filename_copy, *parent, *basename;
1099        char l_filename[VFAT_MAXLEN_BYTES];
1100
1101        debug("writing %s\n", filename);
1102
1103        filename_copy = strdup(filename);
1104        if (!filename_copy)
1105                return -ENOMEM;
1106
1107        split_filename(filename_copy, &parent, &basename);
1108        if (!strlen(basename)) {
1109                ret = -EINVAL;
1110                goto exit;
1111        }
1112
1113        filename = basename;
1114        if (normalize_longname(l_filename, filename)) {
1115                printf("FAT: illegal filename (%s)\n", filename);
1116                ret = -EINVAL;
1117                goto exit;
1118        }
1119
1120        itr = malloc_cache_aligned(sizeof(fat_itr));
1121        if (!itr) {
1122                ret = -ENOMEM;
1123                goto exit;
1124        }
1125
1126        ret = fat_itr_root(itr, &datablock);
1127        if (ret)
1128                goto exit;
1129
1130        total_sector = datablock.total_sect;
1131
1132        ret = fat_itr_resolve(itr, parent, TYPE_DIR);
1133        if (ret) {
1134                printf("%s: doesn't exist (%d)\n", parent, ret);
1135                goto exit;
1136        }
1137
1138        retdent = find_directory_entry(itr, l_filename);
1139
1140        if (retdent) {
1141                if (fat_itr_isdir(itr)) {
1142                        ret = -EISDIR;
1143                        goto exit;
1144                }
1145
1146                /* A file exists */
1147                if (pos == -1)
1148                        /* Append to the end */
1149                        pos = FAT2CPU32(retdent->size);
1150                if (pos > retdent->size) {
1151                        /* No hole allowed */
1152                        ret = -EINVAL;
1153                        goto exit;
1154                }
1155
1156                /* Update file size in a directory entry */
1157                retdent->size = cpu_to_le32(pos + size);
1158        } else {
1159                /* Create a new file */
1160
1161                if (itr->is_root) {
1162                        /* root dir cannot have "." or ".." */
1163                        if (!strcmp(l_filename, ".") ||
1164                            !strcmp(l_filename, "..")) {
1165                                ret = -EINVAL;
1166                                goto exit;
1167                        }
1168                }
1169
1170                if (!itr->dent) {
1171                        printf("Error: allocating new dir entry\n");
1172                        ret = -EIO;
1173                        goto exit;
1174                }
1175
1176                if (pos) {
1177                        /* No hole allowed */
1178                        ret = -EINVAL;
1179                        goto exit;
1180                }
1181
1182                memset(itr->dent, 0, sizeof(*itr->dent));
1183
1184                /* Calculate checksum for short name */
1185                set_name(itr->dent, filename);
1186
1187                /* Set long name entries */
1188                if (fill_dir_slot(itr, filename)) {
1189                        ret = -EIO;
1190                        goto exit;
1191                }
1192
1193                /* Set short name entry */
1194                fill_dentry(itr->fsdata, itr->dent, filename, 0, size,
1195                            ATTR_ARCH);
1196
1197                retdent = itr->dent;
1198        }
1199
1200        ret = set_contents(mydata, retdent, pos, buffer, size, actwrite);
1201        if (ret < 0) {
1202                printf("Error: writing contents\n");
1203                ret = -EIO;
1204                goto exit;
1205        }
1206        debug("attempt to write 0x%llx bytes\n", *actwrite);
1207
1208        /* Flush fat buffer */
1209        ret = flush_dirty_fat_buffer(mydata);
1210        if (ret) {
1211                printf("Error: flush fat buffer\n");
1212                ret = -EIO;
1213                goto exit;
1214        }
1215
1216        /* Write directory table to device */
1217        ret = flush_dir(itr);
1218        if (ret) {
1219                printf("Error: writing directory entry\n");
1220                ret = -EIO;
1221        }
1222
1223exit:
1224        free(filename_copy);
1225        free(mydata->fatbuf);
1226        free(itr);
1227        return ret;
1228}
1229
1230int file_fat_write(const char *filename, void *buffer, loff_t offset,
1231                   loff_t maxsize, loff_t *actwrite)
1232{
1233        return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
1234}
1235
1236static int fat_dir_entries(fat_itr *itr)
1237{
1238        fat_itr *dirs;
1239        fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata;
1240                                                /* for FATBUFSIZE */
1241        int count;
1242
1243        dirs = malloc_cache_aligned(sizeof(fat_itr));
1244        if (!dirs) {
1245                debug("Error: allocating memory\n");
1246                count = -ENOMEM;
1247                goto exit;
1248        }
1249
1250        /* duplicate fsdata */
1251        fat_itr_child(dirs, itr);
1252        fsdata = *dirs->fsdata;
1253
1254        /* allocate local fat buffer */
1255        fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE);
1256        if (!fsdata.fatbuf) {
1257                debug("Error: allocating memory\n");
1258                count = -ENOMEM;
1259                goto exit;
1260        }
1261        fsdata.fatbufnum = -1;
1262        dirs->fsdata = &fsdata;
1263
1264        for (count = 0; fat_itr_next(dirs); count++)
1265                ;
1266
1267exit:
1268        free(fsdata.fatbuf);
1269        free(dirs);
1270        return count;
1271}
1272
1273static int delete_dentry(fat_itr *itr)
1274{
1275        fsdata *mydata = itr->fsdata;
1276        dir_entry *dentptr = itr->dent;
1277
1278        /* free cluster blocks */
1279        clear_fatent(mydata, START(dentptr));
1280        if (flush_dirty_fat_buffer(mydata) < 0) {
1281                printf("Error: flush fat buffer\n");
1282                return -EIO;
1283        }
1284
1285        /*
1286         * update a directory entry
1287         * TODO:
1288         *  - long file name support
1289         *  - find and mark the "new" first invalid entry as name[0]=0x00
1290         */
1291        memset(dentptr, 0, sizeof(*dentptr));
1292        dentptr->name[0] = 0xe5;
1293
1294        if (flush_dir(itr)) {
1295                printf("error: writing directory entry\n");
1296                return -EIO;
1297        }
1298
1299        return 0;
1300}
1301
1302int fat_unlink(const char *filename)
1303{
1304        fsdata fsdata = { .fatbuf = NULL, };
1305        fat_itr *itr = NULL;
1306        int n_entries, ret;
1307        char *filename_copy, *dirname, *basename;
1308
1309        filename_copy = strdup(filename);
1310        if (!filename_copy) {
1311                printf("Error: allocating memory\n");
1312                ret = -ENOMEM;
1313                goto exit;
1314        }
1315        split_filename(filename_copy, &dirname, &basename);
1316
1317        if (!strcmp(dirname, "/") && !strcmp(basename, "")) {
1318                printf("Error: cannot remove root\n");
1319                ret = -EINVAL;
1320                goto exit;
1321        }
1322
1323        itr = malloc_cache_aligned(sizeof(fat_itr));
1324        if (!itr) {
1325                printf("Error: allocating memory\n");
1326                ret = -ENOMEM;
1327                goto exit;
1328        }
1329
1330        ret = fat_itr_root(itr, &fsdata);
1331        if (ret)
1332                goto exit;
1333
1334        total_sector = fsdata.total_sect;
1335
1336        ret = fat_itr_resolve(itr, dirname, TYPE_DIR);
1337        if (ret) {
1338                printf("%s: doesn't exist (%d)\n", dirname, ret);
1339                ret = -ENOENT;
1340                goto exit;
1341        }
1342
1343        if (!find_directory_entry(itr, basename)) {
1344                printf("%s: doesn't exist\n", basename);
1345                ret = -ENOENT;
1346                goto exit;
1347        }
1348
1349        if (fat_itr_isdir(itr)) {
1350                n_entries = fat_dir_entries(itr);
1351                if (n_entries < 0) {
1352                        ret = n_entries;
1353                        goto exit;
1354                }
1355                if (n_entries > 2) {
1356                        printf("Error: directory is not empty: %d\n",
1357                               n_entries);
1358                        ret = -EINVAL;
1359                        goto exit;
1360                }
1361        }
1362
1363        ret = delete_dentry(itr);
1364
1365exit:
1366        free(fsdata.fatbuf);
1367        free(itr);
1368        free(filename_copy);
1369
1370        return ret;
1371}
1372
1373int fat_mkdir(const char *new_dirname)
1374{
1375        dir_entry *retdent;
1376        fsdata datablock = { .fatbuf = NULL, };
1377        fsdata *mydata = &datablock;
1378        fat_itr *itr = NULL;
1379        char *dirname_copy, *parent, *dirname;
1380        char l_dirname[VFAT_MAXLEN_BYTES];
1381        int ret = -1;
1382        loff_t actwrite;
1383        unsigned int bytesperclust;
1384        dir_entry *dotdent = NULL;
1385
1386        dirname_copy = strdup(new_dirname);
1387        if (!dirname_copy)
1388                goto exit;
1389
1390        split_filename(dirname_copy, &parent, &dirname);
1391        if (!strlen(dirname)) {
1392                ret = -EINVAL;
1393                goto exit;
1394        }
1395
1396        if (normalize_longname(l_dirname, dirname)) {
1397                printf("FAT: illegal filename (%s)\n", dirname);
1398                ret = -EINVAL;
1399                goto exit;
1400        }
1401
1402        itr = malloc_cache_aligned(sizeof(fat_itr));
1403        if (!itr) {
1404                ret = -ENOMEM;
1405                goto exit;
1406        }
1407
1408        ret = fat_itr_root(itr, &datablock);
1409        if (ret)
1410                goto exit;
1411
1412        total_sector = datablock.total_sect;
1413
1414        ret = fat_itr_resolve(itr, parent, TYPE_DIR);
1415        if (ret) {
1416                printf("%s: doesn't exist (%d)\n", parent, ret);
1417                goto exit;
1418        }
1419
1420        retdent = find_directory_entry(itr, l_dirname);
1421
1422        if (retdent) {
1423                printf("%s: already exists\n", l_dirname);
1424                ret = -EEXIST;
1425                goto exit;
1426        } else {
1427                if (itr->is_root) {
1428                        /* root dir cannot have "." or ".." */
1429                        if (!strcmp(l_dirname, ".") ||
1430                            !strcmp(l_dirname, "..")) {
1431                                ret = -EINVAL;
1432                                goto exit;
1433                        }
1434                }
1435
1436                if (!itr->dent) {
1437                        printf("Error: allocating new dir entry\n");
1438                        ret = -EIO;
1439                        goto exit;
1440                }
1441
1442                memset(itr->dent, 0, sizeof(*itr->dent));
1443
1444                /* Set short name to set alias checksum field in dir_slot */
1445                set_name(itr->dent, dirname);
1446                fill_dir_slot(itr, dirname);
1447
1448                /* Set attribute as archive for regular file */
1449                fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0,
1450                            ATTR_DIR | ATTR_ARCH);
1451
1452                retdent = itr->dent;
1453        }
1454
1455        /* Default entries */
1456        bytesperclust = mydata->clust_size * mydata->sect_size;
1457        dotdent = malloc_cache_aligned(bytesperclust);
1458        if (!dotdent) {
1459                ret = -ENOMEM;
1460                goto exit;
1461        }
1462        memset(dotdent, 0, bytesperclust);
1463
1464        memcpy(dotdent[0].name, ".       ", 8);
1465        memcpy(dotdent[0].ext, "   ", 3);
1466        dotdent[0].attr = ATTR_DIR | ATTR_ARCH;
1467
1468        memcpy(dotdent[1].name, "..      ", 8);
1469        memcpy(dotdent[1].ext, "   ", 3);
1470        dotdent[1].attr = ATTR_DIR | ATTR_ARCH;
1471        set_start_cluster(mydata, &dotdent[1], itr->start_clust);
1472
1473        ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
1474                           bytesperclust, &actwrite);
1475        if (ret < 0) {
1476                printf("Error: writing contents\n");
1477                goto exit;
1478        }
1479        /* Write twice for "." */
1480        set_start_cluster(mydata, &dotdent[0], START(retdent));
1481        ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
1482                           bytesperclust, &actwrite);
1483        if (ret < 0) {
1484                printf("Error: writing contents\n");
1485                goto exit;
1486        }
1487
1488        /* Flush fat buffer */
1489        ret = flush_dirty_fat_buffer(mydata);
1490        if (ret) {
1491                printf("Error: flush fat buffer\n");
1492                goto exit;
1493        }
1494
1495        /* Write directory table to device */
1496        ret = flush_dir(itr);
1497        if (ret)
1498                printf("Error: writing directory entry\n");
1499
1500exit:
1501        free(dirname_copy);
1502        free(mydata->fatbuf);
1503        free(itr);
1504        free(dotdent);
1505        return ret;
1506}
1507