1065 lines
28 KiB
C
1065 lines
28 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"
|
|
#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;
|
|
}
|