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