119ea8026Sopenharmony_ci/*
219ea8026Sopenharmony_ci * Block device emulated in a file
319ea8026Sopenharmony_ci *
419ea8026Sopenharmony_ci * Copyright (c) 2022, The littlefs authors.
519ea8026Sopenharmony_ci * Copyright (c) 2017, Arm Limited. All rights reserved.
619ea8026Sopenharmony_ci * SPDX-License-Identifier: BSD-3-Clause
719ea8026Sopenharmony_ci */
819ea8026Sopenharmony_ci#include "bd/lfs_filebd.h"
919ea8026Sopenharmony_ci
1019ea8026Sopenharmony_ci#include <fcntl.h>
1119ea8026Sopenharmony_ci#include <unistd.h>
1219ea8026Sopenharmony_ci#include <errno.h>
1319ea8026Sopenharmony_ci
1419ea8026Sopenharmony_ci#ifdef _WIN32
1519ea8026Sopenharmony_ci#include <windows.h>
1619ea8026Sopenharmony_ci#endif
1719ea8026Sopenharmony_ci
1819ea8026Sopenharmony_ciint lfs_filebd_create(const struct lfs_config *cfg, const char *path,
1919ea8026Sopenharmony_ci        const struct lfs_filebd_config *bdcfg) {
2019ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_create(%p {.context=%p, "
2119ea8026Sopenharmony_ci                ".read=%p, .prog=%p, .erase=%p, .sync=%p}, "
2219ea8026Sopenharmony_ci                "\"%s\", "
2319ea8026Sopenharmony_ci                "%p {.read_size=%"PRIu32", .prog_size=%"PRIu32", "
2419ea8026Sopenharmony_ci                ".erase_size=%"PRIu32", .erase_count=%"PRIu32"})",
2519ea8026Sopenharmony_ci            (void*)cfg, cfg->context,
2619ea8026Sopenharmony_ci            (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
2719ea8026Sopenharmony_ci            (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
2819ea8026Sopenharmony_ci            path,
2919ea8026Sopenharmony_ci            (void*)bdcfg,
3019ea8026Sopenharmony_ci            bdcfg->read_size, bdcfg->prog_size, bdcfg->erase_size,
3119ea8026Sopenharmony_ci            bdcfg->erase_count);
3219ea8026Sopenharmony_ci    lfs_filebd_t *bd = cfg->context;
3319ea8026Sopenharmony_ci    bd->cfg = bdcfg;
3419ea8026Sopenharmony_ci
3519ea8026Sopenharmony_ci    // open file
3619ea8026Sopenharmony_ci    #ifdef _WIN32
3719ea8026Sopenharmony_ci    bd->fd = open(path, O_RDWR | O_CREAT | O_BINARY, 0666);
3819ea8026Sopenharmony_ci    #else
3919ea8026Sopenharmony_ci    bd->fd = open(path, O_RDWR | O_CREAT, 0666);
4019ea8026Sopenharmony_ci    #endif
4119ea8026Sopenharmony_ci
4219ea8026Sopenharmony_ci    if (bd->fd < 0) {
4319ea8026Sopenharmony_ci        int err = -errno;
4419ea8026Sopenharmony_ci        LFS_FILEBD_TRACE("lfs_filebd_create -> %d", err);
4519ea8026Sopenharmony_ci        return err;
4619ea8026Sopenharmony_ci    }
4719ea8026Sopenharmony_ci
4819ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_create -> %d", 0);
4919ea8026Sopenharmony_ci    return 0;
5019ea8026Sopenharmony_ci}
5119ea8026Sopenharmony_ci
5219ea8026Sopenharmony_ciint lfs_filebd_destroy(const struct lfs_config *cfg) {
5319ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)cfg);
5419ea8026Sopenharmony_ci    lfs_filebd_t *bd = cfg->context;
5519ea8026Sopenharmony_ci    int err = close(bd->fd);
5619ea8026Sopenharmony_ci    if (err < 0) {
5719ea8026Sopenharmony_ci        err = -errno;
5819ea8026Sopenharmony_ci        LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", err);
5919ea8026Sopenharmony_ci        return err;
6019ea8026Sopenharmony_ci    }
6119ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_destroy -> %d", 0);
6219ea8026Sopenharmony_ci    return 0;
6319ea8026Sopenharmony_ci}
6419ea8026Sopenharmony_ci
6519ea8026Sopenharmony_ciint lfs_filebd_read(const struct lfs_config *cfg, lfs_block_t block,
6619ea8026Sopenharmony_ci        lfs_off_t off, void *buffer, lfs_size_t size) {
6719ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_read(%p, "
6819ea8026Sopenharmony_ci                "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
6919ea8026Sopenharmony_ci            (void*)cfg, block, off, buffer, size);
7019ea8026Sopenharmony_ci    lfs_filebd_t *bd = cfg->context;
7119ea8026Sopenharmony_ci
7219ea8026Sopenharmony_ci    // check if read is valid
7319ea8026Sopenharmony_ci    LFS_ASSERT(block < bd->cfg->erase_count);
7419ea8026Sopenharmony_ci    LFS_ASSERT(off  % bd->cfg->read_size == 0);
7519ea8026Sopenharmony_ci    LFS_ASSERT(size % bd->cfg->read_size == 0);
7619ea8026Sopenharmony_ci    LFS_ASSERT(off+size <= bd->cfg->erase_size);
7719ea8026Sopenharmony_ci
7819ea8026Sopenharmony_ci    // zero for reproducibility (in case file is truncated)
7919ea8026Sopenharmony_ci    memset(buffer, 0, size);
8019ea8026Sopenharmony_ci
8119ea8026Sopenharmony_ci    // read
8219ea8026Sopenharmony_ci    off_t res1 = lseek(bd->fd,
8319ea8026Sopenharmony_ci            (off_t)block*bd->cfg->erase_size + (off_t)off, SEEK_SET);
8419ea8026Sopenharmony_ci    if (res1 < 0) {
8519ea8026Sopenharmony_ci        int err = -errno;
8619ea8026Sopenharmony_ci        LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
8719ea8026Sopenharmony_ci        return err;
8819ea8026Sopenharmony_ci    }
8919ea8026Sopenharmony_ci
9019ea8026Sopenharmony_ci    ssize_t res2 = read(bd->fd, buffer, size);
9119ea8026Sopenharmony_ci    if (res2 < 0) {
9219ea8026Sopenharmony_ci        int err = -errno;
9319ea8026Sopenharmony_ci        LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
9419ea8026Sopenharmony_ci        return err;
9519ea8026Sopenharmony_ci    }
9619ea8026Sopenharmony_ci
9719ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_read -> %d", 0);
9819ea8026Sopenharmony_ci    return 0;
9919ea8026Sopenharmony_ci}
10019ea8026Sopenharmony_ci
10119ea8026Sopenharmony_ciint lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
10219ea8026Sopenharmony_ci        lfs_off_t off, const void *buffer, lfs_size_t size) {
10319ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_prog(%p, "
10419ea8026Sopenharmony_ci                "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
10519ea8026Sopenharmony_ci            (void*)cfg, block, off, buffer, size);
10619ea8026Sopenharmony_ci    lfs_filebd_t *bd = cfg->context;
10719ea8026Sopenharmony_ci
10819ea8026Sopenharmony_ci    // check if write is valid
10919ea8026Sopenharmony_ci    LFS_ASSERT(block < bd->cfg->erase_count);
11019ea8026Sopenharmony_ci    LFS_ASSERT(off  % bd->cfg->prog_size == 0);
11119ea8026Sopenharmony_ci    LFS_ASSERT(size % bd->cfg->prog_size == 0);
11219ea8026Sopenharmony_ci    LFS_ASSERT(off+size <= bd->cfg->erase_size);
11319ea8026Sopenharmony_ci
11419ea8026Sopenharmony_ci    // program data
11519ea8026Sopenharmony_ci    off_t res1 = lseek(bd->fd,
11619ea8026Sopenharmony_ci            (off_t)block*bd->cfg->erase_size + (off_t)off, SEEK_SET);
11719ea8026Sopenharmony_ci    if (res1 < 0) {
11819ea8026Sopenharmony_ci        int err = -errno;
11919ea8026Sopenharmony_ci        LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
12019ea8026Sopenharmony_ci        return err;
12119ea8026Sopenharmony_ci    }
12219ea8026Sopenharmony_ci
12319ea8026Sopenharmony_ci    ssize_t res2 = write(bd->fd, buffer, size);
12419ea8026Sopenharmony_ci    if (res2 < 0) {
12519ea8026Sopenharmony_ci        int err = -errno;
12619ea8026Sopenharmony_ci        LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
12719ea8026Sopenharmony_ci        return err;
12819ea8026Sopenharmony_ci    }
12919ea8026Sopenharmony_ci
13019ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", 0);
13119ea8026Sopenharmony_ci    return 0;
13219ea8026Sopenharmony_ci}
13319ea8026Sopenharmony_ci
13419ea8026Sopenharmony_ciint lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) {
13519ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
13619ea8026Sopenharmony_ci            (void*)cfg, block, ((lfs_file_t*)cfg->context)->cfg->erase_size);
13719ea8026Sopenharmony_ci    lfs_filebd_t *bd = cfg->context;
13819ea8026Sopenharmony_ci
13919ea8026Sopenharmony_ci    // check if erase is valid
14019ea8026Sopenharmony_ci    LFS_ASSERT(block < bd->cfg->erase_count);
14119ea8026Sopenharmony_ci
14219ea8026Sopenharmony_ci    // erase is a noop
14319ea8026Sopenharmony_ci    (void)block;
14419ea8026Sopenharmony_ci
14519ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", 0);
14619ea8026Sopenharmony_ci    return 0;
14719ea8026Sopenharmony_ci}
14819ea8026Sopenharmony_ci
14919ea8026Sopenharmony_ciint lfs_filebd_sync(const struct lfs_config *cfg) {
15019ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)cfg);
15119ea8026Sopenharmony_ci
15219ea8026Sopenharmony_ci    // file sync
15319ea8026Sopenharmony_ci    lfs_filebd_t *bd = cfg->context;
15419ea8026Sopenharmony_ci    #ifdef _WIN32
15519ea8026Sopenharmony_ci    int err = FlushFileBuffers((HANDLE) _get_osfhandle(bd->fd)) ? 0 : -1;
15619ea8026Sopenharmony_ci    #else
15719ea8026Sopenharmony_ci    int err = fsync(bd->fd);
15819ea8026Sopenharmony_ci    #endif
15919ea8026Sopenharmony_ci    if (err) {
16019ea8026Sopenharmony_ci        err = -errno;
16119ea8026Sopenharmony_ci        LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0);
16219ea8026Sopenharmony_ci        return err;
16319ea8026Sopenharmony_ci    }
16419ea8026Sopenharmony_ci
16519ea8026Sopenharmony_ci    LFS_FILEBD_TRACE("lfs_filebd_sync -> %d", 0);
16619ea8026Sopenharmony_ci    return 0;
16719ea8026Sopenharmony_ci}
168