📄 core.c
字号:
effs_t is_fd_valid(fd_t fdi)
{
if (fdi >= fs.fd_max || fdi < 0 || fs.fd[fdi].options == 0)
return 0; // Not valid!
return 1;
}
effs_t is_offset_in_buf(int offset, fd_t fdi)
{
if (fs.fd[fdi].dirty == 1)
if (offset >= fs.fd[fdi].wfp &&
offset < fs.fd[fdi].wfp + fs.chunk_size_max)
return 1;
return 0;
}
/******************************************************************************
* Chunk Operations
******************************************************************************/
iref_t segment_create(const char *buf, int size, iref_t dir)
{
iref_t i;
struct inode_s *ip;
int realsize;
offset_t offset;
char *dataaddr;
ttw(ttr(TTrObj, "segc(%d, %d){" NL, size, dir));
tw(tr(TR_BEGIN, TrObject, "segment_create( ?, %d, %d) {\n", size, dir));
fs.journal.size = realsize = atomalign(size + 1);
// Init journal.diri before chunk_alloc() because it might trigger a
// data_reclaim() which can relocate the dir inode
fs.journal.diri = dir;
if ((i = chunk_alloc(realsize, 0, &offset)) < 0)
return i;
ip = inode_addr(i);
dataaddr = offset2addr(offset);
// Write data and null terminator. We null-terminate the data block,
// such that blocks_fsck() can determine the amount of used data block
// space correctly.
ffsdrv.write(dataaddr, buf, size);
dataaddr += size;
ffsdrv_write_byte(dataaddr, 0);
// Segments is linked together by the child link(create) or by the
// sibling link(update or relocate). A negativ dir indicate that it is a
// update or relocate and the sign must be reversed so the journal
// system will use the sibling link to link the inode together.
if (dir > 0)
fs.journal.diri = chunk_traverse(fs.journal.diri);
fs.journal.diri = -fs.journal.diri;
tw(tr(TR_END, TrObject, "} %d\n", i));
ttw(ttr(TTrObj, "} %d" NL,i));
return i;
}
int segment_read(iref_t i, char *buf, int size, int offset)
{
struct inode_s *ip;
char *p;
int chunk_size;
tw(tr(TR_BEGIN, TrObject, "segment_read(%d, 0x%x, %d, %d) {\n",
i, buf, offset, size));
if (buf == NULL) {
tw(tr(TR_END, TrObject, "} %d\n", EFFS_INVALID));
return EFFS_INVALID;
}
ip = inode_addr(i);
chunk_size = segment_datasize(ip);
// Saturate read buffer
if (size > chunk_size - offset)
size = chunk_size - offset;
p = offset2addr(location2offset(ip->location));
p = addr2data(p, ip);
memcpy(buf, &p[offset], size);
tw(tr(TR_END, TrObject, "} %d\n", size));
return size;
}
// Find next valid chunk
iref_t segment_next(iref_t i)
{
struct inode_s *ip = inode_addr(i);
tw(tr(TR_BEGIN, TrDirHigh, "ffs_segment_next(%d) {\n", i));
// Dir is not allowed to contain data
if (is_object(ip, OT_DIR)) {
tw(tr(TR_END, TrDirHigh, "} 0\n"));
return 0;
}
// Is this the last/only segment
if ((i = ip->child) == (iref_t) IREF_NULL) {
tw(tr(TR_END, TrDirHigh, "} 0\n"));
return 0;
}
// Get child (is valid?), search though segment by sibling link(is
// valid?), and again..
do {
i = ip->child;
ip = inode_addr(i);
if (is_object_valid(ip)) {
tw(tr(TR_END, TrDirHigh,"} %d\n", i));
return i;
}
while (ip->sibling != (iref_t) IREF_NULL) {
i = ip->sibling;
ip = inode_addr(i);
if (is_object_valid(ip)) {
tw(tr(TR_END, TrDirHigh,"} %d\n", i));
return i;
}
}
} while (ip->child != (iref_t) IREF_NULL);
// No segment found
tw(tr(TR_END, TrDirHigh,"} %d\n", i));
return 0;
}
// The output "inode" will be the inode that contains the requested data or
// the last inode in the segmentfile. The segmenthead will be skiped if it
// don't contains any data. inode_offset is the offset in the found inode
// pointed to by target_offset. If target_offset point past the last segment
// will inode_offset be the size of the last inode. The return value will be
// the same as target_offset but maximum the total size of the object.
int segfile_seek(iref_t seghead, int target_offset,
iref_t *inode, int *inode_offset)
{
int priv_count = 0, count_size = 0;
iref_t i = seghead;
struct inode_s *ip;
tw(tr(TR_BEGIN, TrObject, "segfile_seek(%d, %d, ?, ?) {\n",
seghead, target_offset));
if (!is_object_valid(inode_addr(seghead))) {
tw(tr(TR_END, TrAll, "FATAL: Invalid seghead!\n"));
return 0;
}
*inode = seghead;
while (1)
{
ip = inode_addr(i);
count_size += segment_datasize(ip);
// Seghead will be skiped if it don't contain any data
if (count_size > target_offset && count_size != 0) {
if (inode_offset != 0)
*inode_offset = target_offset - priv_count;
tw(tr(TR_END, TrObject, "} %d\n", target_offset));
return target_offset;
}
if ((i = segment_next(i)) == 0) {
tw(tr(TR_END, TrObject, "} (eof!?) %d\n", count_size));
if (inode_offset != 0)
*inode_offset = count_size - priv_count;
// *inode = 0;
return count_size; // No more segments
}
priv_count = count_size;
*inode = i;
}
}
// Calculate exact size of file data; without filename and null terminator
// and without data null terminator and succeeding alignment padding.
// NOTEME: Does this also work for empty files and directories?
int segment_datasize(const struct inode_s *ip)
{
char *p, *q;
int size;
p = offset2addr(location2offset(ip->location));
q = p + ip->size - 1;
// Segments is not allowed to contain any name
if (!is_object(ip, OT_SEGMENT)) {
// skip filename at start of data
while (*p)
p++;
}
else
// If it contained a name would p pointe to the null terminator of
// the name but because chunks don't have a name decrement we p to get
// the size correct
p--;
// skip padding at end of data
while (*q)
q--;
// If there are data, there is also a null-terminator. Otherwise
// there is no null-terminator
size = q - p;
if (size > 0)
size--;
tw(tr(TR_FUNC, TrObject, "segment_datasize(0x%x) %d\n", ip, size));
return size;
}
int object_truncate(const char *pathname, fd_t fdi, offset_t length)
{
int segment_offset, flength, realsize, offset;
iref_t i, dir, next_i;
char *name = 0, *olddata;
struct inode_s *oldip;
effs_t error;
tw(tr(TR_FUNC, TrObject, "ffs_object_truncate('%s', %d, %d) \n",
pathname, fdi, length));
if (length < 0) return EFFS_INVALID;
if (pathname == 0) {
// File descriptor must be open and it have to be in write mode
if (!is_fd_valid(fdi))
return EFFS_BADFD;;
if (!is_open_option(fs.fd[fdi].options, FFS_O_WRONLY))
return EFFS_INVALID;
// It is not possible to truncate an open file to a size less than
// the current file pointer
if (length < fs.fd[fdi].fp)
return EFFS_INVALID;
i = fs.fd[fdi].seghead;
}
else {
// File must exists and not be open
if ((i = object_lookup(pathname, &name, &dir)) < 0)
return i;
if (get_fdi(i) >= 0)
return EFFS_LOCKED;
oldip = inode_addr(i);
// Even though the ffs architecture allows to have data in directory
// objects, we don't want to complicate matters, so we return an error
if (is_object(oldip, OT_DIR) && !(fs.flags & FS_DIR_DATA))
return EFFS_NOTAFILE;
if ((i = is_readonly(i, pathname)) < 0)
return i;
}
// Find the segment which length points in to
flength = segfile_seek(i, length, &i, &segment_offset);
if (pathname == 0) {
if (is_offset_in_buf(length, fdi) == 1) {
fs.fd[fdi].size = (length > fs.fd[fdi].size ?
fs.fd[fdi].size : length); // Truncate the buffer
if (i == fs.fd[fdi].wch) {
next_i = segment_next(i);
if (next_i > 0)
if ((error = object_remove(next_i)) < 0)
return error;
}
return EFFS_OK;
}
}
if (flength < length)
return EFFS_OK;
journal_begin(i);
// Realsize do not always need to include a name but we simplify it.
realsize = atomalign(segment_offset + 1 + fs.filename_max + 1);
// Make sure that there is enough space to make the rename without
// object_create() trigger a data_reclaim() (awoid relocate oldi/data
// source)
if ((offset = data_prealloc(realsize)) <= 0)
return EFFS_NOSPACE;
// Find the next segment if any.
next_i = segment_next(fs.journal.oldi);
// Find old data source
oldip = inode_addr(fs.journal.oldi);
olddata = offset2addr(location2offset(oldip->location));
name = addr2name(olddata); // reinit name (maybe relocated)
olddata = addr2data(olddata, oldip);
if (is_object(oldip, OT_SEGMENT)) {
if (segment_offset == 0)
next_i = fs.journal.oldi; // Remove the found object
else {
if ((i = segment_create(olddata, segment_offset,
-fs.journal.oldi)) < 0)
return i;
fs.link_child = 0; //Do not link child
journal_end(0);
}
}
else {
if ((i = object_create(name, olddata, length, fs.journal.oldi)) < 0)
return i;
fs.link_child = 0; //Do not link child
journal_end(0);
if (is_fd_valid(fdi))
fs.fd[fdi].seghead = i;
}
if (is_fd_valid(fdi))
fs.fd[fdi].size = length;
// If any remaning segment exists then remove them
if (next_i > 0)
if ((error = object_remove(next_i)) < 0)
return error;
return EFFS_OK;
}
// Find the last segment valid or not valid
iref_t chunk_traverse(iref_t i)
{
struct inode_s *ip = inode_addr(i);
tw(tr(TR_BEGIN, TrDirHigh, "ffs_chunk_traverse(%d) {\n", i));
// Is this the last/only segment?
if (ip->child == (iref_t) IREF_NULL) {
tw(tr(TR_END, TrDirHigh, "} %d\n", i));
return i;
}
// Get child, find the last segment by sibling link, and again..
do {
i = ip->child;
ip = inode_addr(i);
while (ip->sibling != (iref_t) IREF_NULL) {
i = ip->sibling;
ip = inode_addr(i);
}
} while (ip->child != (iref_t) IREF_NULL);
// FIXME: remove this
if (ip->child != (iref_t) IREF_NULL || ip->sibling != (iref_t) IREF_NULL) {
tw(tr(TR_END, TrAll,
"FATAL: ffs_chunk_traverse() found invalid inode\n"));
return 0;
}
tw(tr(TR_END, TrDirHigh, "} %d\n", i));
return i;
}
// fdi include offset now but change this so core use pure fdi.
effs_t datasync(fd_t fdi)
{
int chunk_size;
iref_t i;
struct inode_s *ip;
char *name;
tw(tr(TR_FUNC, TrObject, "datasync(%d) \n", fdi));
ttw(ttr(TTrApi, "datasync(%d) {" NL, fdi));
// NOTEME: is this necessary?
if (!is_fd_valid(fdi))
return EFFS_BADFD;
if (fs.fd[fdi].dirty == 0)
return EFFS_OK;
// If size - wfp is more than max is the complete buffer valid or else
// is it only a part of it that consist valid data
chunk_size = fs.fd[fdi].size - fs.fd[fdi].wfp;
if (chunk_size > fs.chunk_size_max)
chunk_size = fs.chunk_size_max;
ip = inode_addr(fs.fd[fdi].wch);
// Create new chunk or update a old one
if (fs.fd[fdi].wch > 0) {
// Update existing chunk
// Negativ
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -