| /**************************************************************************** |
| * fs/mnemofs/mnemofs_master.c |
| * Master node of mnemofs. |
| * |
| * 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. |
| * |
| * Alternatively, the contents of this file may be used under the terms of |
| * the BSD-3-Clause license: |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| * |
| * Copyright (c) 2024 Saurav Pal |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the author nor the names of its contributors may |
| * be used to endorse or promote products derived from this software |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * In mnemofs, the master node points to the root of the file system. It |
| * contains the information about the root, and when the root is updated, |
| * the master node needs to point to the updated location, and thus, needs to |
| * update the master node. |
| * |
| * Master nodes sit at the very end of the journal. The last two blocks of |
| * the journal are called master blocks, and they are filled with a new |
| * entry for a master node everytime it is updated. They are filled in a |
| * sequential manner, and thus, the latest master node can be found easily. |
| * The two master blocks contain identical information, and exist to be as a |
| * backup. |
| * |
| * The stored master nodes are basically `struct mfs_mn_s` without the |
| * redundant `pg` member. |
| * |
| * The master node also points to the start of the journal, and thus, when |
| * the journal moves, a new master node entry is added. |
| * |
| * A master node update, when written to the file system, marks the end of |
| * an update of the file system tree. Thus, at this point, any obsolete data |
| * that can be erased, will be erased by the block allocator. Only after |
| * writing the master block is the file system tree updated. Before this, |
| * the old file system tree is accessible through the older master node, and |
| * can be accessed again during power loss. |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/kmalloc.h> |
| #include <sys/stat.h> |
| |
| #include "mnemofs.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static FAR char *ser_mn(const struct mfs_mn_s mn, |
| FAR char * const out); |
| static FAR const char *deser_mn(FAR const char * const in, |
| FAR struct mfs_mn_s *mn, FAR uint16_t *hash); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Public Data |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: ser_mn |
| * |
| * Description: |
| * Serialize master node. |
| * |
| * Input Parameters: |
| * mn - Master node. |
| * out - Out buffer. |
| * |
| * Returned Value: |
| * Pointer to the end of the serialized data in `out`. |
| * |
| * Assumptions/Limitations: |
| * Out should contain enough space for `mn` and 1 byte extra for the hash. |
| * |
| ****************************************************************************/ |
| |
| static FAR char *ser_mn(const struct mfs_mn_s mn, FAR char * const out) |
| { |
| FAR char *tmp = out; |
| |
| tmp = mfs_ser_mfs(mn.jrnl_blk, tmp); |
| tmp = mfs_ser_mfs(mn.mblk_idx, tmp); |
| tmp = mfs_ser_ctz(&mn.root_ctz, tmp); |
| tmp = mfs_ser_mfs(mn.root_sz, tmp); |
| tmp = mfs_ser_timespec(&mn.ts, tmp); |
| tmp = mfs_ser_16(mfs_hash(out, tmp - out), tmp); |
| |
| /* TODO: Update this, and the make a macro for size of MN. */ |
| |
| return tmp; |
| } |
| |
| /**************************************************************************** |
| * Name: ser_mn |
| * |
| * Description: |
| * Deserialize master node. |
| * |
| * Input Parameters: |
| * in - In buffer. |
| * mn - Master node to populate. |
| * hash - Stored hash (of serialized data) to populate. |
| * |
| * Returned Value: |
| * Pointer to the end of the serialized data in `in`. |
| * |
| * Assumptions/Limitations: |
| * In should contain enough space for `mn` and 1 byte extra for the hash. |
| * |
| ****************************************************************************/ |
| |
| static FAR const char *deser_mn(FAR const char * const in, |
| FAR struct mfs_mn_s *mn, FAR uint16_t *hash) |
| { |
| FAR const char *tmp = in; |
| |
| tmp = mfs_deser_mfs(tmp, &mn->jrnl_blk); |
| tmp = mfs_deser_mfs(tmp, &mn->mblk_idx); |
| tmp = mfs_deser_ctz(tmp, &mn->root_ctz); |
| tmp = mfs_deser_mfs(tmp, &mn->root_sz); |
| tmp = mfs_deser_timespec(tmp, &mn->ts); |
| tmp = mfs_deser_16(tmp, hash); |
| |
| /* TODO: Update this, and the make a macro for size of MN. */ |
| |
| return tmp; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| int mfs_mn_init(FAR struct mfs_sb_s * const sb, const mfs_t jrnl_blk) |
| { |
| int ret = OK; |
| mfs_t i = 0; |
| mfs_t mblk1; |
| mfs_t mblk2; |
| mfs_t jrnl_blk_tmp; |
| bool found = false; |
| uint16_t hash; |
| struct mfs_mn_s mn; |
| const mfs_t sz = sizeof(struct mfs_mn_s) - sizeof(mn.pg); |
| char buftmp[4]; |
| char buf[sz + 1]; |
| |
| mblk1 = mfs_jrnl_blkidx2blk(sb, MFS_JRNL(sb).n_blks); |
| mblk2 = mfs_jrnl_blkidx2blk(sb, MFS_JRNL(sb).n_blks + 1); |
| mn.jrnl_blk = mn.jrnl_blk; |
| mn.mblk_idx = 0; |
| mn.pg = MFS_BLK2PG(sb, mblk1); |
| |
| for (i = 0; i < MFS_PGINBLK(sb); i++) |
| { |
| mfs_read_page(sb, buftmp, 4, mn.pg, 0); |
| mfs_deser_mfs(buftmp, &jrnl_blk_tmp); |
| |
| if (jrnl_blk_tmp == 0) |
| { |
| break; |
| } |
| |
| if (jrnl_blk_tmp != jrnl_blk) |
| { |
| break; |
| } |
| |
| found = true; |
| mn.mblk_idx++; |
| mn.pg++; |
| } |
| |
| if (found == false) |
| { |
| ret = -EINVAL; |
| goto errout; |
| } |
| |
| if (i == MFS_PGINBLK(sb)) |
| { |
| ret = -ENOSPC; |
| goto errout; |
| } |
| else |
| { |
| mn.pg--; |
| } |
| |
| mfs_read_page(sb, buf, sz + 1, mn.pg, 0); |
| |
| /* Deserialize. */ |
| |
| deser_mn(buf, &mn, &hash); |
| if (hash != mfs_hash(buf, sz)) |
| { |
| ret = -EINVAL; |
| goto errout; |
| } |
| |
| /* FUTURE TODO: Recovery in case of hash not matching, or page not |
| * readable. |
| */ |
| |
| MFS_MN(sb) = mn; |
| |
| errout: |
| return ret; |
| } |
| |
| int mfs_mn_fmt(FAR struct mfs_sb_s * const sb, const mfs_t mblk1, |
| const mfs_t mblk2, const mfs_t jrnl_blk) |
| { |
| int ret = OK; |
| mfs_t pg; |
| struct mfs_mn_s mn; |
| struct timespec ts; |
| const mfs_t sz = sizeof(struct mfs_mn_s) - sizeof(mn.pg); |
| char buf[sz + 1]; |
| |
| clock_gettime(CLOCK_REALTIME, &ts); |
| |
| memset(buf, 0, sz + 1); |
| |
| pg = mfs_ba_getpg(sb); |
| if (predict_false(pg == 0)) |
| { |
| ret = -ENOSPC; |
| goto errout; |
| } |
| |
| finfo("Root formatted to be at Page %u", pg); |
| |
| mn.root_ctz.idx_e = 0; |
| mn.root_ctz.pg_e = pg; |
| mn.jrnl_blk = jrnl_blk; |
| mn.mblk_idx = 0; |
| mn.pg = MFS_BLK2PG(sb, mblk1); |
| mn.root_sz = 0; |
| mn.ts = ts; |
| mn.root_st_atim = ts; |
| mn.root_st_ctim = ts; |
| mn.root_st_mtim = ts; |
| mn.root_mode = 0777 | S_IFDIR; |
| |
| /* Serialize. */ |
| |
| ser_mn(mn, buf); |
| |
| ret = mfs_write_page(sb, buf, sz, MFS_BLK2PG(sb, mblk1), 0); |
| if (predict_false(ret < 0)) |
| { |
| goto errout; |
| } |
| |
| ret = mfs_write_page(sb, buf, sz, MFS_BLK2PG(sb, mblk2), 0); |
| if (predict_false(ret < 0)) |
| { |
| goto errout; |
| } |
| |
| mn.mblk_idx = 1; |
| MFS_MN(sb) = mn; |
| finfo("Master node written. Now at page %d, timestamp %lld.%.9ld.", |
| MFS_MN(sb).pg, (long long)MFS_MN(sb).ts.tv_sec, |
| MFS_MN(sb).ts.tv_nsec); |
| |
| errout: |
| return ret; |
| } |
| |
| int mfs_mn_move(FAR struct mfs_sb_s * const sb, struct mfs_ctz_s root, |
| const mfs_t root_sz) |
| { |
| int ret = OK; |
| mfs_t mblk1; |
| mfs_t mblk2; |
| struct mfs_mn_s mn; |
| const mfs_t sz = sizeof(struct mfs_mn_s) - sizeof(mn.pg); |
| char buf[sz + 1]; |
| |
| if (MFS_MN(sb).mblk_idx == MFS_PGINBLK(sb) - 1) |
| { |
| /* TODO: Move journal. Master blocks are full. */ |
| } |
| |
| mblk1 = mfs_jrnl_blkidx2blk(sb, MFS_JRNL(sb).n_blks); |
| mblk2 = mfs_jrnl_blkidx2blk(sb, MFS_JRNL(sb).n_blks + 1); |
| mn = MFS_MN(sb); |
| |
| mn.root_ctz = root; |
| mn.root_sz = root_sz; |
| mn.mblk_idx++; /* TODO */ |
| mn.pg++; |
| |
| ser_mn(mn, buf); |
| |
| ret = mfs_write_page(sb, buf, sz + 1, mn.pg, 0); |
| if (predict_false(ret < 0)) |
| { |
| goto errout; |
| } |
| |
| MFS_MN(sb) = mn; |
| |
| errout: |
| return ret; |
| } |
| |
| int mfs_mn_sync(FAR struct mfs_sb_s *sb, |
| FAR struct mfs_path_s * const new_loc, |
| const mfs_t blk1, const mfs_t blk2, const mfs_t jrnl_blk) |
| { |
| int ret = OK; |
| struct timespec ts; |
| struct mfs_mn_s mn; |
| const mfs_t sz = sizeof(struct mfs_mn_s) - sizeof(mn.pg); |
| char buf[sz + 1]; |
| |
| mn = MFS_MN(sb); |
| |
| clock_gettime(CLOCK_REALTIME, &ts); |
| |
| if (mn.mblk_idx == MFS_PGINBLK(sb)) |
| { |
| /* New blocks have been already allocated by the journal. */ |
| |
| mn.mblk_idx = 0; |
| mn.pg = MFS_BLK2PG(sb, blk1); |
| } |
| |
| mn.ts = ts; |
| mn.root_sz = new_loc->sz; |
| mn.root_ctz = new_loc->ctz; |
| mn.root_mode = 0777 | S_IFDIR; |
| |
| /* TODO: Root timestamps. */ |
| |
| /* Serialize. */ |
| |
| ser_mn(mn, buf); |
| |
| ret = mfs_write_page(sb, buf, sz, MFS_BLK2PG(sb, blk1) + mn.mblk_idx, 0); |
| if (predict_false(ret < 0)) |
| { |
| goto errout; |
| } |
| |
| ret = mfs_write_page(sb, buf, sz, MFS_BLK2PG(sb, blk2) + mn.mblk_idx, 0); |
| if (predict_false(ret < 0)) |
| { |
| goto errout; |
| } |
| |
| mn.mblk_idx++; |
| MFS_MN(sb) = mn; |
| |
| errout: |
| return ret; |
| } |