| /**************************************************************************** |
| * 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 <nuttx/kmalloc.h> |
| #include <sys/param.h> |
| #include <sys/stat.h> |
| |
| #include "mnemofs.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 = kmm_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) |
| { |
| kmm_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 = kmm_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 = kmm_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: |
| kmm_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 = kmm_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 = kmm_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) |
| { |
| kmm_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 = kmm_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 = kmm_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, 0, &capacity); |
| |
| mfs_free_patharr(path); |
| |
| errout: |
| return ret; |
| } |