/**************************************************************************** * fs/mnemofs/mnemofs_fsobj.c * * 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, all the FS object methods (ie. methods in this file), * interface directly with the LRU. To these methods, only the methods * exposed by the LRU are visible, nothing else. The LRU will give them the * most updated data, which includes data from the flash, the updates from * the journal and the LRU deltas as well. * * TODO: The above menetioned concept. ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include "mnemofs.h" #include "fs_heap.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** * Private Types ****************************************************************************/ /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static mfs_t nobjs_in_path(FAR const char * relpath); static const char *next_child(FAR const char *relpath); static const char *last_child(FAR const char *relpath); static FAR char *mfs_ser_dirent(FAR const struct mfs_dirent_s * const x, FAR char * const out); static FAR const char *mfs_deser_dirent(FAR const char * const in, FAR struct mfs_dirent_s * const x); static int search_ctz_by_name(FAR const struct mfs_sb_s * const sb, FAR struct mfs_path_s * const path, const mfs_t depth, FAR const char * const name, const mfs_t namelen, FAR mfs_t *off, FAR struct mfs_dirent_s **dirent); /**************************************************************************** * Private Data ****************************************************************************/ const struct mfs_path_s empty_fsobj = { 0 }; /**************************************************************************** * Public Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: nobjs_in_path * * Description: * Count number of file system objects in path. This includes root in the * count. * * Input Parameters: * relpath - Relative Path. * * Returned Value: * The number of file system objects in the path including the root. * ****************************************************************************/ static mfs_t nobjs_in_path(FAR const char *relpath) { mfs_t count; /* If mount point is "/hi", then operations on "/hi/bye" and "/hi/hello/a" * give respective relpaths as "bye" and "hello/a". Since mnemofs counts * the root as 1 FS object, these respectively contain 2 and 3 FS objects * in the path. The last FS object might be a file or a directory, but * everything else is a directory. The number of FS objects in the path * is the depth of the FS object the path refers to. */ if (*relpath == 0) { return 1; } count = 2; while (*relpath != 0) { if (*relpath == '/') { count++; } relpath++; } return count; } /**************************************************************************** * Name: next_child * * Description: * Give the pointer to next child that appears in the path. * * Input Parameters: * relpath - Relative Path. * * Returned Value: * The pointer to the next child. This is not allocated, but points to the * inside relpath itself. * ****************************************************************************/ static const char *next_child(FAR const char *relpath) { while (*relpath != 0) { if (*relpath == '/') { return relpath + 1; } relpath++; } return relpath; } /**************************************************************************** * Name: next_child * * Description: * Give the pointer to next child that appears in the path. * * Input Parameters: * relpath - Relative Path. * * Returned Value: * The pointer to the next child. This is not allocated, but points to the * inside relpath itself. * ****************************************************************************/ static const char *last_child(FAR const char *relpath) { const mfs_t len = strlen(relpath); mfs_t i; for (i = len - 1; i > 0; i--) { if (relpath[i - 1] == '/') { return relpath + i; } } return relpath; } /**************************************************************************** * Name: mfs_ser_dirent * * Description: * Serialize a direntry. * * Input Parameters: * x - Direntry. * out - Buffer to populate. * * Returned Value: * Pointer to after the end of serialized content in out. * ****************************************************************************/ static FAR char *mfs_ser_dirent(FAR const struct mfs_dirent_s * const x, FAR char * const out) { FAR char *o = out; o = mfs_ser_16(x->name_hash, o); o = mfs_ser_16(x->mode, o); o = mfs_ser_mfs(x->sz, o); o = mfs_ser_timespec(&x->st_atim, o); o = mfs_ser_timespec(&x->st_mtim, o); o = mfs_ser_timespec(&x->st_ctim, o); o = mfs_ser_ctz(&x->ctz, o); o = mfs_ser_8(x->namelen, o); o = mfs_ser_str(x->name, x->namelen, o); return o; } /**************************************************************************** * Name: mfs_deser_dirent * * Description: * Deserialize a direntry. * * Input Parameters: * in - Buffer. * x - Direntry to populate. * * Returned Value: * Pointer to after the end of deserialized content in in. * ****************************************************************************/ static FAR const char *mfs_deser_dirent(FAR const char * const in, FAR struct mfs_dirent_s * const x) { FAR const char *i = in; i = mfs_deser_16(i, &x->name_hash); i = mfs_deser_16(i, &x->mode); i = mfs_deser_mfs(i, &x->sz); i = mfs_deser_timespec(i, &x->st_atim); i = mfs_deser_timespec(i, &x->st_mtim); i = mfs_deser_timespec(i, &x->st_ctim); i = mfs_deser_ctz(i, &x->ctz); i = mfs_deser_8(i, &x->namelen); i = mfs_deser_str(i, x->name, x->namelen); return i; } int pitr_traverse(FAR struct mfs_sb_s *sb, FAR struct mfs_path_s *path, mfs_t depth, FAR mfs_t *cap) { int ret = OK; mfs_t i; mfs_t pg; struct mfs_pitr_s pitr; struct mfs_ctz_s ctz; FAR struct mfs_dirent_s *dirent = NULL; /* TODO: Double traversal can be made faster into a single traversal. */ ctz = path[depth - 1].ctz; if (ctz.idx_e == 0 && ctz.pg_e == 0) { /* Not a valid one. TODO: Does this happens? */ goto errout; } for (i = ctz.idx_e; i > 0; i--) { mfs_ba_markusedpg(sb, pg); pg = mfs_ctz_travel(sb, i, pg, i - 1); if (pg == 0) { break; } } memset(path + depth, 0, *cap - depth); if (depth == *cap) { *cap = (*cap * 3) / 2; /* Don't want to double it for memory. */ path = fs_heap_realloc(path, (*cap) * sizeof(struct mfs_path_s)); if (predict_false(path == NULL)) { ret = -ENOMEM; goto errout; } } mfs_pitr_init(sb, path, depth, &pitr, true); while (true) { mfs_pitr_readdirent(sb, path, &pitr, &dirent); if (dirent == NULL) { break; } if (S_ISDIR(dirent->mode)) { path[(depth + 1) - 1].ctz = dirent->ctz; ret = pitr_traverse(sb, path, depth + 1, cap); if (predict_false(ret < 0)) { mfs_free_dirent(dirent); goto errout; } } mfs_pitr_adv_bydirent(&pitr, dirent); mfs_free_dirent(dirent); } errout: return ret; } /**************************************************************************** * Public Function Prototypes ****************************************************************************/ FAR const char *mfs_path2childname(FAR const char *relpath) { FAR const char *last = relpath + strlen(relpath) - 1; while (last >= relpath && *last != '/') { last--; } return last + 1; } mfs_t mfs_get_fsz(FAR struct mfs_sb_s * const sb, FAR const struct mfs_path_s * const path, const mfs_t depth) { mfs_t sz; if (depth == 0) { /* Master node. */ return 0; } else if (depth == 1) { sz = MFS_MN(sb).root_sz; /* Updated size. */ /* Journal updated to the root creates a new master node entry. TODO * this and moving of the journal. */ finfo("File size got as %u for root.", sz); return sz; } return path[depth - 1].sz; } bool mfs_obj_isempty(FAR struct mfs_sb_s * const sb, FAR struct mfs_path_s *path, FAR struct mfs_pitr_s * const pitr) { bool ret; FAR struct mfs_dirent_s *dirent = NULL; mfs_pitr_readdirent(sb, path, pitr, &dirent); ret = (dirent->sz == 0); mfs_free_dirent(dirent); return ret; } void mfs_free_dirent(FAR struct mfs_dirent_s *dirent) { fs_heap_free(dirent); finfo("Dirent freed."); } bool mfs_searchfopen(FAR const struct mfs_sb_s * const sb, FAR const struct mfs_path_s * const path, const mfs_t depth) { FAR struct mfs_ofd_s *ofd = NULL; list_for_every_entry(&sb->of, ofd, struct mfs_ofd_s, list) { if (ofd->com->depth != depth) { continue; } if (mfs_path_eq(&ofd->com->path[depth - 1], &path[depth - 1])) { return true; } } return false; } int mfs_pitr_rmdirent(FAR struct mfs_sb_s * const sb, FAR struct mfs_path_s * const path, const mfs_t depth, FAR struct mfs_pitr_s * const pitr, FAR const struct mfs_dirent_s * const dirent) { int ret = OK; struct mfs_pitr_s p_pitr; FAR struct mfs_dirent_s *p_dirent = NULL; mfs_pitr_init(sb, path, depth - 1, &p_pitr, true); mfs_pitr_adv_tochild(&p_pitr, path); mfs_pitr_readdirent(sb, path, &p_pitr, &p_dirent); ret = mfs_lru_del(sb, pitr->c_off, MFS_DIRENTSZ(dirent), path, depth); mfs_free_dirent(p_dirent); mfs_pitr_free(&p_pitr); return ret; } int mfs_pitr_rm(FAR struct mfs_sb_s * const sb, FAR struct mfs_path_s * const path, const mfs_t depth, bool rm_child) { int ret = OK; struct mfs_pitr_s pitr; FAR struct mfs_dirent_s *dirent = NULL; mfs_pitr_init(sb, path, depth, &pitr, true); mfs_pitr_readdirent(sb, path, &pitr, &dirent); ret = mfs_pitr_rmdirent(sb, path, depth, &pitr, dirent); if (predict_false(ret < 0)) { goto errout; } if (rm_child) { ret = mfs_lru_del(sb, 0, path[depth - 1].sz, path, depth); if (predict_false(ret < 0)) { goto errout; } } errout: mfs_free_dirent(dirent); mfs_pitr_free(&pitr); return ret; } int mfs_pitr_init(FAR const struct mfs_sb_s * const sb, FAR const struct mfs_path_s * const path, const mfs_t depth, FAR struct mfs_pitr_s * const pitr, bool child) { /* Ensure updated CTZ location from the journal before this. */ int ret = OK; const int diff = child ? 1 : 0; const mfs_t p_depth = depth - diff; if (predict_false(depth < diff)) { ret = -EINVAL; goto errout; } pitr->c_off = 0; pitr->depth = p_depth; if (predict_true(p_depth != 0)) { pitr->p = path[p_depth - 1]; } else { /* 0 or gabage value is fine for master node, not required. */ pitr->p.ctz.idx_e = 0; pitr->p.ctz.pg_e = 0; pitr->p.off = 0; pitr->p.sz = 1; /* For 1 traversal to get root. */ } finfo("Pitr initialized at depth %u, with CTZ (%u, %u) and size %u.", p_depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, pitr->p.sz); errout: return ret; } void mfs_pitr_free(FAR const struct mfs_pitr_s * const pitr) { finfo("Pitr at depth %u with CTZ (%u, %u) freed.", pitr->depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e); } void mfs_pitr_adv_off(FAR struct mfs_pitr_s * const pitr, const mfs_t off) { pitr->c_off += off; finfo("Pitr at depth %u with CTZ (%u, %u) advanced by %u to %u offset.", pitr->depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, off, pitr->c_off); } void mfs_pitr_adv_bydirent(FAR struct mfs_pitr_s * const pitr, FAR const struct mfs_dirent_s * const dirent) { mfs_pitr_adv_off(pitr, MFS_DIRENTSZ(dirent)); finfo("Pitr at depth %u with CTZ (%u, %u) advanced by %u to %u offset.", pitr->depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, MFS_DIRENTSZ(dirent), pitr->c_off); } void mfs_pitr_adv_tochild(FAR struct mfs_pitr_s * const pitr, FAR const struct mfs_path_s * const path) { /* (pitr->depth + 1) - 1 is the child's index. */ pitr->c_off = path[pitr->depth].off; finfo("Pitr at depth %u with CTZ (%u, %u) advanced to %u offset.", pitr->depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, pitr->c_off); } int mfs_pitr_readdirent(FAR const struct mfs_sb_s * const sb, FAR struct mfs_path_s *path, FAR struct mfs_pitr_s * const pitr, FAR struct mfs_dirent_s **dirent) { int ret = OK; mfs_t sz; const mfs_t len = sizeof(struct mfs_dirent_s) \ + NAME_MAX + 1; char rd[len]; FAR struct mfs_dirent_s *d = NULL; FAR struct mfs_dirent_s *tmp = NULL; if (dirent == NULL) { return ret; } *dirent = NULL; memset(rd, 0, len); d = fs_heap_zalloc(len); if (predict_false(d == NULL)) { ret = -ENOMEM; goto errout; } else if (pitr->c_off >= pitr->p.sz) { goto errout_with_d; } else if (pitr->depth == 0) { d->name[0] = 0; d->namelen = 0; d->ctz = MFS_MN(sb).root_ctz; d->mode = MFS_MN(sb).root_mode; d->name_hash = mfs_hash(d->name, d->namelen); d->st_atim = MFS_MN(sb).root_st_atim; d->st_ctim = MFS_MN(sb).root_st_ctim; d->st_mtim = MFS_MN(sb).root_st_mtim; d->sz = MFS_MN(sb).root_sz; pitr->c_off = 1; /* To prevent infinite loop. */ } else { ret = mfs_lru_rdfromoff(sb, pitr->c_off, path, pitr->depth, rd, len); if (predict_false(ret < 0)) { goto errout_with_d; } mfs_deser_dirent(rd, d); } sz = MFS_DIRENTSZ(d); tmp = fs_heap_realloc(d, sz); if (predict_true(tmp != NULL)) { d = tmp; } *dirent = d; DEBUGASSERT(pitr->depth == 0 || strcmp(d->name, "")); finfo("Read direntry at %u offset, %u depth for CTZ (%u, %u). " \ "Direntry name: \"%.*s\" with name length %u and size %u.", pitr->c_off, pitr->depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, d->namelen, d->name, d->namelen, d->sz); return ret; errout_with_d: fs_heap_free(d); if (ret < 0) { finfo("Direntry could not be allocated."); } else if (*dirent == NULL) { finfo("No direntry found."); } errout: return ret; } int mfs_pitr_adv(FAR struct mfs_sb_s * const sb, FAR struct mfs_path_s *path, FAR struct mfs_pitr_s * const pitr) { int ret = OK; FAR struct mfs_dirent_s *dirent; ret = mfs_pitr_readdirent(sb, path, pitr, &dirent); if (predict_false(ret < 0)) { goto errout; } mfs_pitr_adv_bydirent(pitr, dirent); mfs_free_dirent(dirent); finfo("Pitr for CTZ (%u, %u) advanced to offset %u.", pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, pitr->c_off); errout: return ret; } static int search_ctz_by_name(FAR const struct mfs_sb_s * const sb, FAR struct mfs_path_s * const path, const mfs_t depth, FAR const char * const name, const mfs_t namelen, FAR mfs_t *off, FAR struct mfs_dirent_s **dirent) { /* NOTE: depth is of the parent here. */ /* Applies LRU updates. */ int ret = OK; bool found = false; uint16_t name_hash; struct mfs_pitr_s pitr; FAR struct mfs_dirent_s *nd; *dirent = NULL; if (depth == 0) { DEBUGASSERT(namelen == 0); nd = fs_heap_zalloc(sizeof(struct mfs_dirent_s)); if (predict_false(nd == NULL)) { ret = -ENOMEM; goto errout; } *off = 0; nd->namelen = namelen; nd->ctz = MFS_MN(sb).root_ctz; nd->mode = MFS_MN(sb).root_mode; nd->name_hash = mfs_hash(nd->name, nd->namelen); nd->st_atim = MFS_MN(sb).root_st_atim; nd->st_ctim = MFS_MN(sb).root_st_ctim; nd->st_mtim = MFS_MN(sb).root_st_mtim; nd->sz = MFS_MN(sb).root_sz; *dirent = nd; goto errout; } name_hash = mfs_hash(name, namelen); ret = mfs_lru_updatedinfo(sb, path, depth); if (predict_false(ret < 0)) { goto errout; } ret = mfs_pitr_init(sb, path, depth, &pitr, false); if (predict_false(ret < 0)) { goto errout; } for (; ; ) { /* Readdirent takes care of LRU updates. */ ret = mfs_pitr_readdirent(sb, path, &pitr, &nd); if (predict_false(ret < 0 || nd == NULL)) { ret = -ENONET; goto errout; } if (nd->name_hash == name_hash && !strncmp(nd->name, name, MIN(nd->namelen, namelen))) { found = true; path[depth].sz = nd->sz; path[depth].ctz = nd->ctz; path[depth].off = pitr.c_off; *off = pitr.c_off; break; } mfs_pitr_adv_bydirent(&pitr, nd); mfs_free_dirent(nd); } errout: if (found) { finfo("Searched \"%.*s\" direntry inside CTZ (%u, %u) at depth %u," " size %u.", namelen, name, path[depth - 1].ctz.idx_e, path[depth - 1].ctz.pg_e, depth, path[depth - 1].sz); *dirent = nd; } else { ret = -ENOENT; finfo("Can not find requested direntry in parent. Ret: %d.", ret); } return ret; } int mfs_get_patharr(FAR const struct mfs_sb_s * const sb, FAR const char * relpath, FAR struct mfs_path_s **path, FAR mfs_t *depth) { int ret = OK; int ret_flags = 0; mfs_t i; mfs_t sz; mfs_t off; mfs_t n_objs; mfs_t name_len; FAR const char *cur = NULL; FAR const char *next = NULL; struct mfs_ctz_s ctz; FAR struct mfs_path_s *np = NULL; FAR struct mfs_dirent_s *dirent = NULL; *path = NULL; n_objs = nobjs_in_path(relpath); np = fs_heap_zalloc(n_objs * sizeof(struct mfs_path_s)); if (predict_false(np == NULL)) { ret = -ENOMEM; goto errout; } ctz = MFS_MN(sb).root_ctz; sz = MFS_MN(sb).root_sz; np[0].sz = sz; np[0].ctz = ctz; np[0].off = 0; cur = relpath; next = next_child(cur); name_len = *next == 0 ? next - cur : next - cur - 1; if (predict_false(n_objs == 1)) { ret_flags |= MFS_ISDIR | MFS_EXIST; /* This will not go into the loop. */ } else if (predict_false(n_objs == 2)) { ret_flags |= MFS_P_EXIST | MFS_P_ISDIR; } /* MFS_MN(sb).root_* is always up to date, no need for journal update. */ for (i = 1; i < n_objs; i++) { /* np[i] is the fs object at depth i + 1. */ /* Need to update journal for every level in the path as, for eg., the * child can be deleted, etc. Same goes for LRU, which is taken care of * by search_ctz_by_name function. */ ret = search_ctz_by_name(sb, np, i, cur, name_len, &off, &dirent); if (predict_false(ret < 0)) { goto errout_with_ret_flags; } if (i < n_objs - 2 && !S_ISDIR(dirent->mode)) { ret_flags |= MFS_FINPATH; goto errout_with_ret_flags; } else if (i == n_objs - 2) { ret_flags |= MFS_P_EXIST; if (S_ISDIR(dirent->mode)) { ret_flags |= MFS_P_ISDIR; } else { ret_flags |= MFS_FINPATH; goto errout_with_ret_flags; } } else /* if (i == n_objs - 1) */ { ret_flags |= MFS_EXIST; if (S_ISDIR(dirent->mode)) { ret_flags |= MFS_ISDIR; } else { ret_flags |= MFS_ISFILE; } } np[i].ctz = dirent->ctz; np[i].off = off; np[i].sz = dirent->sz; ctz = dirent->ctz; mfs_free_dirent(dirent); cur = next; next = next_child(cur); name_len = *next == 0 ? next - cur : next - cur - 1; } ret = ret_flags; *depth = n_objs; *path = np; finfo("Got path array with flags %u, depth %u.", ret, n_objs); return ret; errout_with_ret_flags: ret = ret_flags; *depth = n_objs; *path = np; /* mfs_free_patharr(np) : All callers will free np (ie. path) when done * with it. */ errout: finfo("Got path array with flags %u, depth %u.", ret, n_objs); return ret; } void mfs_free_patharr(FAR struct mfs_path_s *path) { fs_heap_free(path); } void mfs_pitr_reset(FAR struct mfs_pitr_s * const pitr) { pitr->c_off = 0; finfo("Pitr for CTZ (%u, %u) reset.", pitr->p.ctz.idx_e, pitr->p.ctz.pg_e); } int mfs_pitr_appenddirent(FAR struct mfs_sb_s * const sb, FAR struct mfs_path_s * const path, const mfs_t depth, FAR struct mfs_pitr_s * const pitr, FAR const struct mfs_dirent_s * const dirent) { /* Depth is depth of the child to be appended. */ int ret = OK; const mfs_t len = MFS_DIRENTSZ(dirent); char wd[len]; if (pitr->depth == 0) { /* Root is the only child of the master node. */ ret = -EINVAL; goto errout; } /* TODO: If the parent directory is newly formed (ie. size is 0), then * allocate space for it. This can be done better. Just allocate page when * its created and added first to LRU, and then add a check to ensure it * doesn't get re-allocated when written. A field like "new" would be * helpful in the LRU node for this. */ memset(wd, 0, len); mfs_ser_dirent(dirent, wd); ret = mfs_lru_wr(sb, pitr->p.sz, len, path, pitr->depth, wd); if (predict_false(ret < 0)) { goto errout; } errout: return ret; } int mfs_pitr_appendnew(FAR struct mfs_sb_s * const sb, FAR struct mfs_path_s * const path, const mfs_t depth, FAR struct mfs_pitr_s * const pitr, FAR const char * const relpath, const mode_t mode) { /* Depth is depth of the child to be appended. */ int ret = OK; FAR const char *cur = last_child(relpath); FAR const char *next = next_child(cur); const mfs_t len = *next == 0 ? next - cur : next - cur - 1; struct timespec ts; FAR struct mfs_dirent_s *d = NULL; DEBUGASSERT(depth > 0); d = fs_heap_zalloc(sizeof(struct mfs_dirent_s) + len); if (predict_false(d == NULL)) { ret = -ENOMEM; goto errout; } clock_gettime(CLOCK_REALTIME, &ts); d->ctz = empty_fsobj.ctz; d->mode = mode; d->st_atim = ts; d->st_mtim = ts; d->st_ctim = ts; d->namelen = len; strncpy(d->name, cur, d->namelen); d->name_hash = mfs_hash(d->name, d->namelen); d->sz = 0; /* Add the new direntry in this path. */ path[depth - 1].ctz = d->ctz; path[depth - 1].off = pitr->p.sz; path[depth - 1].sz = d->sz; ret = mfs_pitr_appenddirent(sb, path, depth, pitr, d); if (predict_false(ret < 0)) { goto errout_with_d; } finfo("Direntry appended to Pitr with %u depth, and CTZ (%u, %u). " \ "Direntry name: \"%.*s\" with name length %u at offset %u.", pitr->depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, d->namelen, d->name, d->namelen, path[depth - 1].off); errout_with_d: mfs_free_dirent(d); errout: return ret; } /* Only for initialization of the block allocator. */ int mfs_pitr_traversefs(FAR struct mfs_sb_s * sb, const struct mfs_ctz_s ctz, int type) { /* type takes in MFS_ISFILE & MFS_ISDIR. */ int ret = OK; mfs_t capacity; FAR struct mfs_path_s *path = NULL; capacity = MFS_TRAVERSE_INITSZ; path = fs_heap_zalloc(capacity * sizeof(struct mfs_path_s)); if (predict_false(path == NULL)) { ret = -ENOMEM; goto errout; } path[0].off = 0; path[0].ctz = MFS_MN(sb).root_ctz; path[0].sz = MFS_MN(sb).root_sz; ret = pitr_traverse(sb, path, 1, &capacity); mfs_free_patharr(path); errout: return ret; }