📄 core.c
字号:
}
}
// We don't actually journal deletions, this is why we call
// journal_commit() instead of journal_end(). We have to set
// journal.location to something else, otherwise journal_commit() will
// not discount the number of bytes lost by this delete.
if (is_object(ip, OTE_DIR)) {
journal_begin(i);
fs.journal_ram.location = 0;
journal_commit(0);
}
else {
// NOTE: This is not nice if we get a break down however the
// remaning chunks will be removed later by a block reclaim.
do {
journal_begin(i);
fs.journal_ram.location = 0;
journal_commit(0);
} while ((i = segment_next(i)) != 0);
}
tw(tr(TR_END, TrObject, "} %d\n", EFFS_OK));
return EFFS_OK;
}
// Rename an object. <newname> is the new name.
iref_t object_rename(iref_t oldi, const char *newname, iref_t newdir)
{
iref_t newi;
struct inode_s *oldip;
char *olddata;
int oldsize, namelength, realsize, offset;
tw(tr(TR_BEGIN, TrObject, "object_rename(%d, '%s', %d) {\n",
oldi, newname, newdir));
oldip = inode_addr(oldi);
oldsize = segment_datasize(oldip);
// Make sure that there is enough space to make the rename without
// object_create() trigger a data_reclaim() (awoid relocate oldi/data
// source)
namelength = is_filename(newname);
realsize = namelength + 1 + oldsize + (oldsize > 0 ? 1 : 0);
realsize = atomalign(realsize);
// Save newdir in fs.xx because it will be updated if it is relocated.
fs.i_backup = newdir;
if ((offset = data_prealloc(realsize)) <= 0)
return EFFS_NOSPACE;
// Use fs.journal.oldi because i would have been updated if
// data_reclaim() relocate oldi
oldip = inode_addr(fs.journal.oldi);
olddata = offset2addr(location2offset(oldip->location));
olddata = addr2data(olddata, oldip);
newi = object_create(newname, olddata, oldsize, fs.i_backup);
tw(tr(TR_END, TrObject, "} %d\n", newi));
return newi;
}
/******************************************************************************
* Object Lookup
******************************************************************************/
// Lookup an object. Symlinks are followed.
iref_t object_lookup(const char *path, char **leaf, iref_t *dir)
{
iref_t i;
struct inode_s *ip;
tw(tr(TR_BEGIN, TrLookup, "object_lookup('%s', ?, ?) {\n", path));
ttw(ttr(TTrInode, "olu(%s){" NL, path));
i = object_lookup_once(path, leaf, dir);
ip = inode_addr(i);
if (i > 0 && is_object(ip, OTE_LINK)) {
path = offset2addr(location2offset(ip->location));
path += ffs_strlen(path) + 1; // point to data portion
i = object_lookup_once(path, leaf, dir);
// Links may only point to regular files...
ip = inode_addr(i);
if (+i > 0 && !is_object(ip, OTE_FILE))
i = EFFS_NOTAFILE;
}
else {
if (leaf) leaf = 0;
if (dir) dir = 0;
}
tw(tr(TR_END, TrLookup, "} (%d, '%s') %d\n",
(dir ? *dir : 0), (leaf ? *leaf : ""), i));
ttw(ttr(TTrInode, "} %d" NL, i));
return i;
}
// Lookup an object. If object is found: Return iref of object and
// directory of object in <dir>. If object is not found: Return
// EFFS_NOTFOUND and last directory component of path in <dir> and leafname
// of pathname in <leaf>
iref_t object_lookup_once(const char *path, char **leaf, iref_t *dir)
{
iref_t i, j, d;
struct inode_s *ip;
const char *p, *q;
uint8 depth = 1;
tw(tr(TR_FUNC, TrLookup, "object_lookup_once('%s', ?, ?) { ", path));
if (path == NULL)
return EFFS_BADNAME;
d = fs.root;
if (*path == '/') {
path++; // silently ignore and skip prefix slash
// root directory is a special case
if (*path == 0) {
j = d;
if (leaf) *leaf = (char *) path;
if (dir) *dir = 0;
tw(tr(TR_NULL, TrLookup, "} ('%s', %d) %d\n", path, 0, j));
return j;
}
}
else
return EFFS_BADNAME;
// set default return value if root dir is completely empty
// (child link empty)
j = EFFS_NOTFOUND;
ip = inode_addr(d);
while ((i = get_child(ip)) != (iref_t) IREF_NULL)
{
j = 0; // default to not found
do {
tw(tr(TR_NULL, TrLookup, "i%d ", (int) i));
p = path;
ip = inode_addr(i);
if (is_object_valid(ip) && !is_object(ip, OTE_SEGMENT)) {
q = addr2name(offset2addr(location2offset(ip->location)));
tw(tr(TR_NULL, TrLookup, "%s ", q));
while (*p == *q && *p != 0 && *q != 0) {
p++;
q++;
}
if (*q == 0 && (*p == 0 || *p == '/')) {
j = i;
break;
}
}
} while ((i = get_sibling(ip)) != (iref_t) IREF_NULL);
if (j == 0) {
// we did not find this component of the path. Let's
// see if this was the leafname component or not...
while (*p != 0 && *p != '/')
p++;
if (*p == 0)
// The path component was indeed the leafname
j = EFFS_NOTFOUND;
else
// The path component was not the last, so it
// obviously contained an object that was not a
// directory.
j = EFFS_NOTADIR;
break;
}
if (*p == '/') {
// if there are more path components, the object found
// must be a directory...
if (!is_object(ip, OTE_DIR)) {
j = EFFS_NOTADIR;
break;
}
if (++depth > fs.path_depth_max) {
j = EFFS_PATHTOODEEP;
break;
}
path = p + 1;
d = i;
// if this dir inode has no children, we will leave the
// while loop, so we preset the return error code. NOTEME:
// Not strictly correct because if we still have a lot of
// the pathname left, it should return the error
// EFFS_NOTADIR
j = EFFS_NOTFOUND;
tw(tr(TR_NULL, TrLookup, "/ "));
}
else {
// It is a fact that *p == 0. So we found the object!
break;
}
}
if (leaf) *leaf = (char *) path;
if (dir) *dir = d;
tw(tr(TR_NULL, TrLookup, "} (%d, '%s') %d\n", d, path, j));
return j;
}
/******************************************************************************
* Directory Operations
******************************************************************************/
// Open a directory, returning the iref of the directory's inode.
iref_t dir_open(const char *name)
{
iref_t i;
struct inode_s *ip;
tw(tr(TR_BEGIN, TrDirHigh, "dir_open('%s') {\n", name));
if ((i = object_lookup(name, 0, 0)) < 0) {
tw(tr(TR_END, TrDirHigh, "} %d\n", i));
return i;
}
ip = inode_addr(i);
if (!is_object(ip, OTE_DIR))
i = EFFS_NOTADIR;
tw(tr(TR_END, TrDirHigh, "} %d\n", i));
return i;
}
// Return name and iref of next entry in directory <dir>. <i> is the last
// entry we returned from this function. In case this is the first call
// after the initial call to dir_open(), <i> equals <dir>.
iref_t dir_next(iref_t dir, iref_t i, char *name, int8 size)
{
struct inode_s *ip = inode_addr(i);
char *p;
tw(tr(TR_BEGIN, TrDirHigh, "dir_next(%d, %d, ?, %d) {\n", dir, i, size));
i = (i == dir ? get_child(ip) : get_sibling(ip));
while (i != (iref_t) IREF_NULL) {
ip = inode_addr(i);
if (is_object_valid(ip)) {
p = offset2addr(location2offset(ip->location));
while (size-- && (*name++ = *p++))
;
break;
}
i = get_sibling(ip);
}
if (i == (iref_t) IREF_NULL)
i = 0;
tw(tr(TR_END, TrDirHigh, "} %d\n", i));
return i;
}
// Traverse a directory given by inode reference <i>. If <i> is negative, it
// refers to the actual directory so we start by traversing the child link.
// Otherwise if <i> is positive, it refers to an entry within the directory
// and we only traverse sibling links. Returns iref of last object in
// directory (or negative iref of directory if the child link is empty).
// <entries> is number of non-deleted objects in the dir (only valid if
// traversed from the start, eg. with negative <i>).
iref_t dir_traverse(iref_t i, iref_t *entries)
{
iref_t j = 0, valid = 0, erased = 0, invalid = 0;
struct inode_s *ip;
tw(tr(TR_FUNC, TrDirLow, "dir_traverse(%d, ?) { ", i));
if (i < 0) {
// If directory's child is empty, this is a virgin directory and we
// return negative iref of the directory itself.
j = i;
i = -i;
ip = inode_addr(i);
i = get_child(ip);
}
if (i != (iref_t) IREF_NULL) {
do {
if (j == i) {
tw(tr(TR_NULL, TrDirLow, "LOOP! "));
return EFFS_SIBLINGLOOP;
}
j = i;
ip = inode_addr(j);
tw(tr(TR_NULL, TrDirLow, "%d/%x ", j, ip->flags));
if (is_object_valid(ip))
valid++;
else if (is_object_erased(ip))
erased++;
else
invalid++;
} while ((i = get_sibling(ip)) != (iref_t) IREF_NULL);
}
if (entries != 0)
*entries = valid;
tw(tr(TR_NULL, TrDirLow, "} (valid = %d, erased = %d, invalid = %d) %d\n",
valid, erased, invalid, j));
return j;
}
/******************************************************************************
* Block, Inode and Data Allocation
******************************************************************************/
// Find the youngest free block. Return block index on success. If the
// argument <priority> is zero, this is a normal alloc and it will leave at
// least fs.blocks_free_min spare blocks. Otherwise, if it is non-zero, it
// is a privileged alloc (initiated by a reclaim operation) and it will not
// necessarily leave any spare blocks.
bref_t block_alloc(bref_t priority, uint16 flags)
{
bref_t i, b, b_min, b_max, blocks_free;
struct block_header_s *bhp;
age_t age, age_min, age_max;
tw(tr(TR_BEGIN, TrBlock, "block_alloc(%d, 0x%x) {\n", priority, flags));
ttw(ttr(TTrData, "ba(%d,0x%x) {" NL, priority, flags));
age_min = BLOCK_AGE_MAX;
age_max = 0;
blocks_free = 0;
b_min = b_max = -1;
tw(tr(TR_FUNC, TrBlock, "blocks(age): "));
for (i = dev.numblocks - 1; i >= 0; i--)
{
if (is_block(i, BF_IS_FREE))
{
blocks_free++;
bhp = (struct block_header_s *) offset2addr(dev.binfo[i].offset);
age = bhp->age;
tw(tr(TR_NULL, TrBlock, "%d(%d) ", i, age));
// Remember index of block found. We use '<=' and '>=' operators
// (instead of '<' and '>') to ensure we have both limits
// properly set on exit from this loop.
if (age <= age_min) {
b_min = i;
age_min = age;
}
if (age >= age_max) {
b_max = i;
age_max = age;
}
}
}
tw(tr(TR_NULL, TrBlock, "\n"));
// Handle age wrap around
b = b_min;
if (b_min != -1) {
// Either age_max really is max age, so b_min is youngest block OR
// age_max really is min age, so b_max is youngest block
b = (age_max - age_min) < 0x8000 ? b_min : b_max;
}
// Only privileged allocs will get the last free block
if (blocks_free <= fs.blocks_free_min - priority) {
b = -1;
tw(tr(TR_FUNC, TrBlock, "Only %d block(s) left, required = %d\n",
blocks_free, fs.blocks_free_min - priority));
}
else {
// Prepare/format the block for holding data/inodes
if (flags == BF_DATA) {
bstat[b].used = BHEADER_SIZE;
bstat[b].lost = 0;
bstat[b].objects = 0;
block_flags_write(b, BF_DATA);
}
else if (flags == BF_COPYING) {
// This code is used on a fresh format and when allocating a new
// block for reclaiming inodes
block_flags_write(b, BF_COPYING);
bstat[b].used = 0;
bstat[b].lost = 0;
bstat[b].objects = 1; // first inode to be allocated
}
else {
tw(tr(TR_FUNC, TrBlock, "FATAL: Bad input (flags = 0x%X)\n", flags));
}
}
tw(tr(TR_END, TrBlock, "} (%d) %d\n", blocks_free, b));
ttw(ttr(TTrData, "} 0x%x" NL, b));
return b;
}
// Free and schedule a block for erase.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -