Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

#include <fs.h>
#include <mgmt/mgmt.h>
#include <fs_mgmt/fs_mgmt_impl.h>

int
fs_mgmt_impl_filelen(const char *path, size_t *out_len)
{
    struct fs_dirent dirent;
    int rc;

    rc = fs_stat(path, &dirent);
    if (rc != 0) {
        return MGMT_ERR_EUNKNOWN;
    }

    if (dirent.type != FS_DIR_ENTRY_FILE) {
        return MGMT_ERR_EUNKNOWN;
    }

    *out_len = dirent.size;

    return 0;
}

int
fs_mgmt_impl_read(const char *path, size_t offset, size_t len,
                  void *out_data, size_t *out_len)
{
    struct fs_file_t file;
    ssize_t bytes_read;
    int rc;

    rc = fs_open(&file, path);
    if (rc != 0) {
        return MGMT_ERR_EUNKNOWN;
    }

    rc = fs_seek(&file, offset, FS_SEEK_SET);
    if (rc != 0) {
        goto done;
    }

    bytes_read = fs_read(&file, out_data, len);
    if (bytes_read < 0) {
        goto done;
    }

    *out_len = bytes_read;

done:
    fs_close(&file);

    if (rc != 0) {
        return MGMT_ERR_EUNKNOWN;
    } else {
        return 0;
    }
}

static int
zephyr_fs_mgmt_truncate(const char *path)
{
    size_t len;
    int rc;

    /* Attempt to get the length of the file at the specified path.  This is a
     * quick way to determine if there is already a file there.
     */
    rc = fs_mgmt_impl_filelen(path, &len);
    if (rc == 0) {
        /* There is already a file with the specified path.  Unlink it to
         * simulate a truncate operation.
         *
         * XXX: This isn't perfect - if the file is currently open, the unlink
         * operation won't actually delete the file.  Consequently, the file
         * will get partially overwritten rather than truncated.  The NFFS port
         * doesn't support the truncate operation, so this is an imperfect
         * workaround.
         */
        rc = fs_unlink(path);
        if (rc != 0) {
            return MGMT_ERR_EUNKNOWN;
        }
    }

    return 0;
}

int
fs_mgmt_impl_write(const char *path, size_t offset, const void *data,
                   size_t len)
{
    struct fs_file_t file;
    int rc;
 
    /* Truncate the file before writing the first chunk.  This is done to
     * properly handle an overwrite of an existing file.
     *
     */
    if (offset == 0) {
        rc = zephyr_fs_mgmt_truncate(path);
        if (rc != 0) {
            return rc;
        }
    }

    rc = fs_open(&file, path);
    if (rc != 0) {
        return MGMT_ERR_EUNKNOWN;
    }

    rc = fs_seek(&file, offset, FS_SEEK_SET);
    if (rc != 0) {
        goto done;
    }

    rc = fs_write(&file, data, len);
    if (rc < 0) {
        goto done;
    }

    rc = 0;

done:
    fs_close(&file);

    if (rc != 0) {
        return MGMT_ERR_EUNKNOWN;
    } else {
        return 0;
    }
}