📄 fs2.c
字号:
if (!skip_erase) {
// Unerased byte.
#ifndef __TESTENV__
#asm
FS_RESTORE_XPC2
#endasm
#endif
w.magic = FSMAGIC_F; // Set to erased value (F-block)
if (wblock) {
memcpy(&w.w, maplog, sizeof(w.w)); // Save WLU header
pw = paddr(&w);
len = sizeof(FS_w) + 1;
if (w.w.magic == FSMAGIC_W) {
// Existing valid W-block. Update erase counts
if (wearlevel) {
w.w.wear = wearlevel;
w.w.rwear = 0;
}
else {
w.w.wear++;
w.w.rwear++;
if (!w.w.rwear)
w.w.rwear--;
}
}
else /* if (w.w.magic != FSMAGIC_BAD) */ {
// Not a valid W-block. Set it to nominated wear.
w.w.magic = FSMAGIC_W;
if (wearlevel)
w.w.wear = wearlevel;
else
w.w.wear = 1;
w.w.rwear = 0;
w.w.wlo_num = lxd->high_wlo;
}
}
else {
pw = paddr(&w.magic);
len = sizeof(w.magic);
}
if (FS_IS_SSW(lxd)) {
// Small-sector devices don't really have pure "erase" function, however rewriting
// a sector with 0xFF makes sense when initially formating, when completely
// deleting a file, or when shifting a file. Only the magic ID is set to 0xFF.
rc = FS_CALL_WRITE(lxd, pw, len, maplog);
}
else {
rc = FS_CALL_ERASE(lxd, maplog);
if (!rc)
rc = FS_CALL_ANDOVER(lxd, pw, len, maplog);
}
}
#ifndef __TESTENV__
#asm
FS_RESTORE_XPC
#endasm
#endif
if (rc)
_set_errno(EIO); // I/O error
return rc;
}
fs_nodebug root int fs_erase_logical(FS_lxd * lxd, FSLSnum ls, char * erased)
{
// Erase a logical sector in the given LX. Since LSs may be less than or equal PSs
// (in the case of BW device class), greater than or equal (for SSW), or don't care
// (for NVRAM), we have to handle each case separately.
// If PS == 0, must be NVRAM device. Mark LS as erased by setting magic number to 0xFF.
// If LS < PS, must be a BW device. We AND out the B- or H-block magic byte to FSMAGIC_DEL.
// This marks the LS as deleted, however no physical erase is performed since we don't want
// other LSs on this PS to be erased. These LSs will be reclaimed when necessary.
// If LS >= PS, is either BW or SSW. In either case, we perform a physical erase of all
// PSs within this LS.
// Note that this routine does _not_ alter any of the lstab chains.
// *erased is set to 1 if the underlying PS(s) were actually erased, or 0 if this LS
// was only marked for erasure because the PS size was larger than the LS.
auto FSPSnum p, i;
auto char * maplog;
auto long offs;
auto FSmagic mag;
auto long pmag;
auto int rc;
if (lxd->ps_per_ls) {
*erased = 1;
for (p = ls * lxd->ps_per_ls, i = 0;
i < lxd->ps_per_ls;
p++, i++)
if (fs_erase_physical(lxd, p, 0))
return 1;
return 0;
}
// Must be BW or NVRAM. Flip some bits.
offs = FS_LS2OFFSET(lxd, ls);
if (lxd->ps_size)
mag = FSMAGIC_DEL, *erased = 0;
else
mag = FSMAGIC_F, *erased = 1;
pmag = paddr(&mag);
#ifndef __TESTENV__
#asm
FS_SAVE_XPC
#endasm
#endif
maplog = FS_CALL_MAP(lxd, offs);
if (lxd->wear_leveling)
maplog += sizeof(FS_w); // Skip W-block if necessary
rc = FS_CALL_ANDOVER(lxd, pmag, sizeof(mag), maplog);
#ifndef __TESTENV__
#asm
FS_RESTORE_XPC
#endasm
#endif
if (rc)
_set_errno(EIO);
return rc;
}
#ifndef __TESTENV__
fs_nodebug void fs_erase_pbuf(word len)
{
// Set _fs.pbuf to all 0xFF to make it look "erased". len must be even and > 0.
#asm xmemok
ld hl,(sp+@sp+len)
rr hl ; Convert to words
ld b,l
ld c,h ; CB is counter
ld a,b
or a
jr z,noinc
inc c ; Inc to exit loop properly
noinc:
ld ix,(_fs_pbuf) ; Dest physical address
ld a,(_fs_pbuf+2)
ld de,2 ; Increment amount (word)
ld hl,0FFFFh ; Data to store
loop:
ldp (ix),hl ; Put word
add ix,de
adc a,0 ; Update bits 16-23 if wrap
djnz loop ; Count units
dec c
jr nz,loop ; Count multiples of 256
#endasm
}
#endif
fs_nodebug int fs_purge_file(FS_ef * ef)
{
// Delete this file by freeing all blocks in both metadata and data chains.
// This routine is called during initialisation, where a file has unrecoverable
// inconsistencies, such as missing data or metadata or bad sequencing.
if (ef->datalx)
fs_erase_chain(FS_LXN2PTR(ef->datalx), paddr(&ef->first_dls), FS_REALLY_ERASE, 0, 0, NULL);
if (ef->metalx)
fs_erase_chain(FS_LXN2PTR(ef->metalx), paddr(&ef->first_mls), FS_REALLY_ERASE, 0, 0, NULL);
// Delete the EF and eftab entry
ef->in_use = 0;
_fs.eftab[ef->name] = 0;
return 0;
}
fs_nodebug int fs_erase_chain(FS_lxd * lxd, long lsp, unsigned flags, int max_count,
FSseq while_seq, FSLSnum * count)
{
// Erase a chain of LSs. Each erased LS is put on the list of free LSs.
// The free LS list is maintained in order of increasing wear count.
// After each LS is "erased", the LS is moved to the free or deleted list
// as appropriate.
// On entry, lsp is the paddr of the entry which indicates the first actual
// LS in the chain to be erased. This is necessary to allow the chain
// pointers to be updated properly. lsp need not point into the lstab,
// however subsequent entries are assumed to be in the lstab.
// flags indicates the halt conditions (one or more of the FSHALT_* bits)
// with following parameters giving the halt condition parameters. If flags
// is zero, then all LSs in the chain are erased.
// If count is not NULL, then that counter is decremented for every LS erased.
auto FSLSnum zapls; // LS currently being zapped
auto long zapp; // Physical address (paddr) of above lstab entry
auto FSLSnum nextls;
auto long nextp;
auto char erased;
auto int num_erased;
auto FS_h hdr;
TRACE(("erase_chain: flags=%x, max_count=%d, while_seq=%u, count=%u\n",
flags, max_count, while_seq, count ? *count : -1));
// prime the pipeline
zapp = lsp;
nextls = fs_lstabent(zapp);
num_erased = 0;
for (;;) {
zapls = nextls;
if (zapls == FS_INVALID_LS)
break; // End of chain.
if (flags & FSHALT_COUNT && num_erased >= max_count)
break;
if (flags & FSHALT_SEQ) {
fs_get_headers(lxd, zapls, NULL, &hdr, NULL);
if (hdr.seq != while_seq)
break;
}
nextp = FS_LSTABPHYS(lxd, zapls);
nextls = fs_lstabent(nextp);
fs_set_lstabent(lsp, nextls); // Remove zapls from head of chain
TRACE((" removed LS %u\n", zapls));
if (count)
(*count)--;
if (fs_free_logical(lxd, zapp, zapls))
return 1; // Maybe I/O error
if (FS_IS_SSW(lxd) && flags & FS_REALLY_ERASE)
fs_erase_logical(lxd, zapls, &erased);
zapp = nextp;
num_erased++;
}
return 0;
}
fs_nodebug int fs_expurgate(FS_lxd * lxd, FSfilenum fn)
{
// This is only called when files on a SSW LX are deleted. It scans the free
// chain looking for LSs which used to belong to this file. These LSs are
// physically erased so that they won't get confused with blocks that belong
// to the same file number (should it be re-created). This is an unfortunate
// necessity of our "lazy erase" strategy for SSW devices, however it is much
// better than always physically erasing for any update.
auto FSLSnum nextls;
auto long nextp;
auto char erased;
auto FS_h hdr;
TRACE(("fs_expurgate: lxn=%d fn=%d", (int)lxd->this, (int)fn));
// prime the pipeline
nextls = lxd->first_free;
for (;;) {
if (nextls == FS_INVALID_LS)
break; // End of chain.
fs_get_headers(lxd, nextls, NULL, &hdr, NULL);
if (hdr.filenum == fn)
fs_erase_logical(lxd, nextls, &erased);
nextp = FS_LSTABPHYS(lxd, nextls);
nextls = fs_lstabent(nextp);
}
return 0;
}
fs_nodebug int fs_free_logical(FS_lxd * lxd, long lstabp, FSLSnum ls)
{
// Send a logical sector to the free (or del) chain.
// For SSW devices, we don't really need to erase the sector, since the version
// number in the updated LS will be higher than this LS -- this is picked up
// at fs_init time. So, to save wear, we just put the LS on the free list without
// really erasing it.
auto char erased;
TRACE(("fs_free_logical: ls=%d\n", (int)ls));
if (FS_IS_SSW(lxd))
erased = 1; // Fake it
else
if (fs_erase_logical(lxd, ls, &erased))
return 1;
if (erased) {
fs_insert_chain(lxd, paddr(&lxd->first_free), ls);
lxd->num_free++;
}
else {
// Deleted, not erased, because this LS part of larger PS.
fs_insert_chain(lxd, paddr(&lxd->first_deleted), ls);
lxd->num_deleted++;
}
return 0;
}
/*** BeginHeader fs_verify_data, fs_get_meta, fs_read_data, fs_read_pbuf,
fs_verify_headers, fs_compare_ls, fs_qsort, fs_find_create_ef, fs_init_lstab, fs_scan_file,
fs_open, fs_locate */
root int fs_verify_data(FS_lxd * lxd, FSLSnum ls);
root FSoffset fs_get_meta(FS_lxd * lxd, FSLSnum ls, FS_h * hdr);
root int fs_read_data(FS_lxd * lxd, FSLSnum ls, FSoffset offs, char * buf, int len,
FSseq seq, FSfilenum name);
root int fs_read_pbuf(FS_lxd * lxd, long dev_offs, word len);
int fs_verify_headers(FS_lxd * lxd, FS_w * w, FS_h * h);
int fs_compare_ls(FS_lxd * lxd, FSLSnum a, FSLSnum b);
int fs_qsort(FS_lxd * lxd, long base, FSLSnum n);
FS_ef * fs_find_create_ef(FSfilenum name, FSLXnum mlx, FSLXnum dlx);
int fs_init_lstab(FS_lxd * lxd);
int fs_scan_file(FS_ef * ef);
int fs_open(File * f, FileNumber name);
int fs_locate(FS_ef * ef, long pos, FSLSnum * ls, FSoffset * offs,
long * lstabpp, FSseq * seq, FSLSnum * prevlsp);
/*** EndHeader */
fs_nodebug root int fs_verify_data(FS_lxd * lxd, FSLSnum ls)
{
// Read the specified data LS then verify the final checksum. This
// function should only be called for B-blocks which are complete (i.e.
// not the last in a file).
// Returns 0 if OK, 1 if failed.
auto char * maplog;
auto FS_h * h;
auto int rc;
#ifdef FS_DEBUG
auto FSchecksum chk;
#endif
#ifndef __TESTENV__
#asm
FS_SAVE_XPC
#endasm
#endif
maplog = FS_CALL_MAP(lxd, FS_LS2OFFSET(lxd, ls));
rc = fs_checksum(NULL, maplog, lxd->ls_size);
#ifdef FS_DEBUG
if (rc) {
chk = 0;
fs_checksum(&chk, maplog, lxd->ls_size);
printf("Bad complete data checksum: %x\n", chk);
}
#endif
#ifndef __TESTENV__
#asm
FS_RESTORE_XPC
#endasm
#endif
return rc;
}
fs_nodebug root FSoffset fs_get_meta(FS_lxd * lxd, FSLSnum ls, FS_h * hdr)
{
// Extract results from metadata LS. *hdr is set up with initial H-block, then
// the first_offs, first_seq, last_offs and last_chk are updated with the most recent
// valid log entry.
// Returns the next available offset in the metadata log.
auto char * maplog;
auto FS_l * lastlog;
auto int rc;
auto FS_l * log;
auto FSoffset offs;
#ifndef __TESTENV__
#asm
FS_SAVE_XPC
#endasm
#endif
maplog = FS_CALL_MAP(lxd, FS_LS2OFFSET(lxd, ls));
if (lxd->wear_leveling)
maplog += sizeof(FS_w);
memcpy(hdr, maplog, sizeof(*hdr));
maplog += sizeof(*hdr);
log = (FS_l *)maplog;
lastlog = log + (lxd->d_size - FS_INIT_LOGOFFS) / sizeof(FS_l);
while (log < lastlog) {
if (log->log_chk == FS_INVALID_CHK || fs_checksum(NULL, log, sizeof(*log)))
break; // Bad entry checksum
offs = log->new_offs & FS_OFFS_MASK;
if (log->new_offs & FS_LOG_DEL) {
hdr->first_offs = offs;
hdr->first_seq = log->log_data;
}
else {
hdr->last_offs = offs;
hdr->last_chk = log->log_data;
}
log++;
}
#ifndef __TESTENV__
#asm
FS_RESTORE_XPC
#endasm
#endif
return (FSoffset)((char *)log - maplog + FS_INIT_LOGOFFS);
}
fs_nodebug root int fs_read_data(FS_lxd * lxd, FSLSnum ls, FSoffset offs,
char * buf, int len, FSseq seq, FSfilenum name)
{
// Read given LS, starting at local offset, into buf of given length.
// Returns actual number of chars read, which will be the lesser of len
// and lxd->d_size - offs, or 0 if error. buf must _not_ point into XPC window.
// seq is the expected sequence number of ls. If there is a mismatch, the read
// 'works' but EBADSEQ will be set. Similarly for the file number.
auto char * maplog;
auto int rc;
#ifndef __TESTENV__
#asm
FS_SAVE_XPC
#endasm
#endif
maplog = FS_CALL_MAP(lxd, FS_LS2OFFSET(lxd, ls));
if (lxd->wear_leveling)
maplog += sizeof(FS_w);
if ((FSmagic)*maplog != FSMAGIC_B) {
rc = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -