<commit_before>/*
 * Copyright 2014-2017 <NAME>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define _GNU_SOURCE // for memmem in string.h

#include "fatfs.h"

#include "3rdparty/fatfs/source/ff.h"
#include "3rdparty/fatfs/source/diskio.h"  /* FatFs lower layer API */
#include "util.h"
#include "block_cache.h"

#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

// Globals since that's how the FatFS code likes to work.
static struct block_cache *output_;
static off_t block_offset_;
static size_t block_count_ = 0;
static char *current_file_ = NULL;
static FATFS fs_;
static FIL fil_;
static DWORD fattime_;

const char *fatfs_error_to_string(FRESULT err)
{
    switch (err) {
    case FR_OK: return "Succeeded";
    case FR_DISK_ERR: return "FATFS: A hard error occurred in the low level disk I/O layer";
    case FR_INT_ERR: return "FATFS: Assertion failed (check for corruption)";
    case FR_NOT_READY: return "FATFS: The physical drive cannot work";
    case FR_NO_FILE: return "FATFS: Could not find the file";
    case FR_NO_PATH: return "FATFS: Could not find the path";
    case FR_INVALID_NAME: return "FATFS: The path name format is invalid";
    case FR_DENIED: return "FATFS: Access denied due to prohibited access or directory full";
    case FR_EXIST: return "FATFS: Destination file already exists";
    case FR_INVALID_OBJECT: return "FATFS: The file/directory object is invalid";
    case FR_WRITE_PROTECTED: return "FATFS: The physical drive is write protected";
    case FR_INVALID_DRIVE: return "FATFS: The logical drive number is invalid";
    case FR_NOT_ENABLED: return "FATFS: The volume has no work area";
    case FR_NO_FILESYSTEM: return "FATFS: There is no valid FAT volume";
    case FR_MKFS_ABORTED: return "FATFS: f_mkfs() aborted due to a parameter error. Try adjusting the partition size.";
    case FR_TIMEOUT: return "FATFS: Could not get a grant to access the volume within defined period";
    case FR_LOCKED: return "FATFS: The operation is rejected according to the file sharing policy";
    case FR_NOT_ENOUGH_CORE: return "FATFS: LFN working buffer could not be allocated";
    case FR_TOO_MANY_OPEN_FILES: return "FATFS: Number of open files > _FS_SHARE";
    default:
    case FR_INVALID_PARAMETER: return "FATFS: Invalid parameter";
    }
}

static FRESULT fatfs_error(const char *context, const char *filename, FRESULT rc)
{
    if (rc != FR_OK) {
        set_last_error("%s(%s): %s", context, filename ? filename : "", fatfs_error_to_string(rc));
    }

    return rc;
}

#define CHECK(CONTEXT, FILENAME, CMD) do { if (fatfs_error(CONTEXT, FILENAME, CMD) != FR_OK) return -1; } while (0)
#define CHECK_CLEANUP(CONTEXT, FILENAME, CMD) do { if (fatfs_error(CONTEXT, FILENAME, CMD) != FR_OK) { rc = -1; goto cleanup; } } while (0)
#define MAYBE_MOUNT(BLOCK_CACHE, BLOCK_OFFSET) do { if (output_ != BLOCK_CACHE || block_offset_ != BLOCK_OFFSET) { output_ = BLOCK_CACHE; block_offset_ = BLOCK_OFFSET; CHECK("fat_mount", NULL, f_mount(&fs_, "", 0)); } } while (0)
#define CHECK_SYNC(FILENAME, FIL) CHECK("sync", FILENAME, f_sync(FIL))

/**
 * @brief fatfs_mkfs Make a new FAT filesystem
 * @param block_writer the file to contain the raw filesystem data
 * @param block_offset the offset within fatfp for where to start
 * @param block_count how many FWUP_BLOCK_SIZE blocks
 * @return 0 on success
 */
int fatfs_mkfs(struct block_cache *output, off_t block_offset, size_t block_count)
{
    // The block count is only used for f_mkfs according to the docs. Store
    // it here for the call to f_mkfs since there's no way to pass it through.
    block_count_ = block_count;

    MAYBE_MOUNT(output, block_offset);

    // Since we're going to format, clear out all blocks in the cache
    // in the formatted range. Additionally, mark these blocks so that
    // they don't need to be written to disk. If the format code writes
    // to them, they'll be marked dirty. However, if any code tries to
    // read them, they'll get back zeros without any I/O. This is best
    // effort.
    OK_OR_RETURN_MSG(block_cache_trim(output, block_offset * FWUP_BLOCK_SIZE, block_count * FWUP_BLOCK_SIZE, true),
                     "Error trimming blocks affacted by fat_mkfs");

    // The au_size is the cluster size. We set it low so
    // that we have enough clusters to easily bump the cluster count
    // above the FAT32 threshold. The minimum number of clusters to
    // get FAT32 is 65526. This is important for the Raspberry Pi since
    // it only boots off FAT32 partitions and we don't want a huge
    // boot partition.
    //
    // NOTE2: FAT file system usage with fwup generally has been for the small
    // boot partitions on platforms and not huge partitions. If this
    // changes, it would be good to make this configurable so that massive
    // partitions could be made.
    //
    // NOTE3: Specify FM_SFD (super-floppy disk) to avoid fatfs wanting to create
    // a master boot record.
    char buffer[FF_MAX_SS];
    MKFS_PARM mkfs_parms;
    memset(&mkfs_parms, 0, sizeof(mkfs_parms));
    mkfs_parms.fmt = FM_SFD | FM_FAT | FM_FAT32;
    mkfs_parms.n_fat = 2;
    mkfs_parms.align = 0;  // Use default
    mkfs_parms.n_root = 0; // Use default
    mkfs_parms.au_size = FWUP_BLOCK_SIZE;
    CHECK("fat_mkfs", NULL, f_mkfs("", &mkfs_parms, buffer, sizeof(buffer)));

    return 0;
}

// Helper method to close the file that we keep open to avoid unnecessary
// FAT chain traversals.
static void close_open_files()<commit_msg>fat_mkfs: clear out the final block on format<commit_after>/*
 * Copyright 2014-2017 <NAME>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define _GNU_SOURCE // for memmem in string.h

#include "fatfs.h"

#include "3rdparty/fatfs/source/ff.h"
#include "3rdparty/fatfs/source/diskio.h"  /* FatFs lower layer API */
#include "util.h"
#include "block_cache.h"

#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

// Globals since that's how the FatFS code likes to work.
static struct block_cache *output_;
static off_t block_offset_;
static size_t block_count_ = 0;
static char *current_file_ = NULL;
static FATFS fs_;
static FIL fil_;
static DWORD fattime_;

const char *fatfs_error_to_string(FRESULT err)
{
    switch (err) {
    case FR_OK: return "Succeeded";
    case FR_DISK_ERR: return "FATFS: A hard error occurred in the low level disk I/O layer";
    case FR_INT_ERR: return "FATFS: Assertion failed (check for corruption)";
    case FR_NOT_READY: return "FATFS: The physical drive cannot work";
    case FR_NO_FILE: return "FATFS: Could not find the file";
    case FR_NO_PATH: return "FATFS: Could not find the path";
    case FR_INVALID_NAME: return "FATFS: The path name format is invalid";
    case FR_DENIED: return "FATFS: Access denied due to prohibited access or directory full";
    case FR_EXIST: return "FATFS: Destination file already exists";
    case FR_INVALID_OBJECT: return "FATFS: The file/directory object is invalid";
    case FR_WRITE_PROTECTED: return "FATFS: The physical drive is write protected";
    case FR_INVALID_DRIVE: return "FATFS: The logical drive number is invalid";
    case FR_NOT_ENABLED: return "FATFS: The volume has no work area";
    case FR_NO_FILESYSTEM: return "FATFS: There is no valid FAT volume";
    case FR_MKFS_ABORTED: return "FATFS: f_mkfs() aborted due to a parameter error. Try adjusting the partition size.";
    case FR_TIMEOUT: return "FATFS: Could not get a grant to access the volume within defined period";
    case FR_LOCKED: return "FATFS: The operation is rejected according to the file sharing policy";
    case FR_NOT_ENOUGH_CORE: return "FATFS: LFN working buffer could not be allocated";
    case FR_TOO_MANY_OPEN_FILES: return "FATFS: Number of open files > _FS_SHARE";
    default:
    case FR_INVALID_PARAMETER: return "FATFS: Invalid parameter";
    }
}

static FRESULT fatfs_error(const char *context, const char *filename, FRESULT rc)
{
    if (rc != FR_OK) {
        set_last_error("%s(%s): %s", context, filename ? filename : "", fatfs_error_to_string(rc));
    }

    return rc;
}

#define CHECK(CONTEXT, FILENAME, CMD) do { if (fatfs_error(CONTEXT, FILENAME, CMD) != FR_OK) return -1; } while (0)
#define CHECK_CLEANUP(CONTEXT, FILENAME, CMD) do { if (fatfs_error(CONTEXT, FILENAME, CMD) != FR_OK) { rc = -1; goto cleanup; } } while (0)
#define MAYBE_MOUNT(BLOCK_CACHE, BLOCK_OFFSET) do { if (output_ != BLOCK_CACHE || block_offset_ != BLOCK_OFFSET) { output_ = BLOCK_CACHE; block_offset_ = BLOCK_OFFSET; CHECK("fat_mount", NULL, f_mount(&fs_, "", 0)); } } while (0)
#define CHECK_SYNC(FILENAME, FIL) CHECK("sync", FILENAME, f_sync(FIL))

/**
 * @brief fatfs_mkfs Make a new FAT filesystem
 * @param block_writer the file to contain the raw filesystem data
 * @param block_offset the offset within fatfp for where to start
 * @param block_count how many FWUP_BLOCK_SIZE blocks
 * @return 0 on success
 */
int fatfs_mkfs(struct block_cache *output, off_t block_offset, size_t block_count)
{
    // The block count is only used for f_mkfs according to the docs. Store
    // it here for the call to f_mkfs since there's no way to pass it through.
    block_count_ = block_count;

    MAYBE_MOUNT(output, block_offset);

    // Since we're going to format, clear out all blocks in the cache
    // in the formatted range. Additionally, mark these blocks so that
    // they don't need to be written to disk. If the format code writes
    // to them, they'll be marked dirty. However, if any code tries to
    // read them, they'll get back zeros without any I/O. This is best
    // effort.
    OK_OR_RETURN_MSG(block_cache_trim(output, block_offset * FWUP_BLOCK_SIZE, block_count * FWUP_BLOCK_SIZE, true),
                     "Error trimming blocks affacted by fat_mkfs");

    // Clear out the final block. This has to purposes:
    //
    // 1. To check that it can be written and isn't past the end of the device.
    // 2. If writing a disk image, this expands the disk image so that other
    //    tools are happy writing to the FAT filesystem created here.
    uint8_t ones[FWUP_BLOCK_SIZE];
    memset(ones, 0xff, sizeof(ones));
    OK_OR_RETURN_MSG(block_cache_pwrite(output, ones, FWUP_BLOCK_SIZE, FWUP_BLOCK_SIZE * (block_offset + block_count - 1), true),
                     "Error clearing final FAT sector");

    // The au_size is the cluster size. We set it low so
    // that we have enough clusters to easily bump the cluster count
    // above the FAT32 threshold. The minimum number of clusters to
    // get FAT32 is 65526. This is important for the Raspberry Pi since
    // it only boots off FAT32 partitions and we don't want a huge
    // boot partition.
    //
    // NOTE2: FAT file system usage with fwup generally has been for the small
    // boot partitions on platforms and not huge partitions. If this
    // changes, it would be good to make this configurable so that massive
    // partitions could be made.
    //
    // NOTE3: Specify FM_SFD (super-floppy disk) to avoid fatfs wanting to create
    // a master boot record.
    char buffer[FF_MAX_SS];
    MKFS_PARM mkfs_parms;
    memset(&mkfs_parms, 0, sizeof(mkfs_parms));
    mkfs_parms.fmt = FM_SFD | FM_FAT | FM_FAT32;
    mkfs_parms.n_fat = 2;
    mkfs_parms.align = 0;  // Use default
    mkfs_parms.n_root = 0; // Use default
    mkfs_parms.au_size = FWUP_BLOCK_SIZE;
    CHECK("fat_mkfs", NULL, f_mkfs("", &mkfs_parms, buffer, sizeof(buffer)));

    return 0;
}

// Helper method to close the file that we keep open to avoid unnecessary
// FAT chain traversals.
static void close_open_files()
{'fraction_non_alphanumeric': 0.08738247610018171, 'fraction_numerical': 0.007031682073161097, 'mean_word_length': 3.6777531411677753, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3979877', 'n_tokens_mistral': 4117, 'n_tokens_neox': 3910, 'n_words': 1565}