nuttx/fs/mnemofs/mnemofs_fsobj.c
Saurav Pal 0be6dfb552 fs/mnemofs: Refactor path logic, direntry size bug fix, open free bug fix
Refactoring path logic to prevent logic flaws, direntry size bug fix to allow proper direntry traversal, open free bug fix to prevent memory leak after close.

Signed-off-by: Saurav Pal <resyfer.dev@gmail.com>
2024-08-09 09:00:17 +02:00

945 lines
26 KiB
C

/****************************************************************************
* 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;
}
/****************************************************************************
* 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;
}
/* TODO: Ensure when an LRU's delta is flushed to the journal, the
* new location is updated in the LRU AND the open files, if it is
* open.
*/
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)
{
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);
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.",
pitr->c_off, pitr->depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e,
d->name, d->namelen);
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;
uint16_t name_hash;
struct mfs_pitr_s pitr;
FAR struct mfs_dirent_s *nd;
bool found = false;
*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;
}
/* TOFO: 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;
struct timespec ts;
FAR const char *cur = last_child(relpath);
FAR const char *next = next_child(cur);
FAR struct mfs_dirent_s *d = NULL;
const mfs_t len = *next == 0 ? next - cur : next - cur - 1;
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;
}