📄 fs2.c
字号:
_set_errno(ENOTB);
}
else {
_set_errno(0);
if (((FS_b *)maplog)->seq != seq)
_set_errno(EBADSEQ);
if (name && ((FS_b *)maplog)->filenum != name)
_set_errno(EBADFNUM);
maplog += sizeof(FS_b) + offs;
rc = lxd->d_size - offs;
if (rc > len)
rc = len;
memcpy(buf, maplog, rc);
}
#ifndef __TESTENV__
#asm
FS_RESTORE_XPC
#endasm
#endif
return rc;
}
#ifndef __TESTENV__
fs_nodebug root int fs_read_pbuf(FS_lxd * lxd, long dev_offs, word len)
{
// This is a bit tricky since can't use root2xmem. However, can
// take advantage of the even number of bytes (<= 8k).
// len must be > 1. _fs.pbuf must be even aligned to avoid ldp
// wrap problems.
#asm
FS_SAVE_XPC
c FS_CALL_MAP(lxd, dev_offs);
ld ix,hl ; Address of mapped area in XPC window
ld hl,(sp+@sp+len)
or a
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 iy,(_fs_pbuf) ; Dest physical address
ld a,(_fs_pbuf+2)
ld de,2 ; Increment amount (word)
loop:
ld hl,(ix) ; Get word
add ix,de
ldp (iy),hl ; Put word
add iy,de
adc a,0 ; Update bits 16-23 if wrap
djnz loop ; Count units
dec c
jr nz,loop ; Count multiples of 256
FS_RESTORE_XPC
#endasm
return 0;
}
#endif
fs_nodebug int fs_verify_headers(FS_lxd * lxd, FS_w * w, FS_h * h)
{
// Verify header(s) using the appropriate checksum. Returns 0 for OK,
// 1 for fail. LSs starting with a bad block indicator always return
// 0 since this will prevent reformatting attempts.
auto FSchecksum chk;
auto FS_b * b;
chk = 0;
if (lxd->wear_leveling) {
if (w->magic == FSMAGIC_BAD)
return 0;
fs_checksum(&chk, w, sizeof(*w));
}
switch (h->magic) {
case FSMAGIC_H:
return fs_checksum(&chk, h, sizeof(*h));
case FSMAGIC_B:
b = (FS_b *)h;
return fs_checksum(&chk, b, sizeof(*b));
case FSMAGIC_BAD:
return lxd->wear_leveling; // Bad only if wear-leveling
case FSMAGIC_F:
// If h is all 0xFF, we return
// OK since freshly erased sectors are acceptable. Otherwise, we would
// mark it as deleted, which would waste time and money. Note that this
// presumes that if the headers are 0xFF, then the rest of the LS is
// also erased.
return fs_checksum(NULL, h, sizeof(h)); // Returns OK if all 0xFF.
}
return 1;
}
// Overall LS block classes - this determines the overall sorting order.
#define FSBCLASS_FREE 1
#define FSBCLASS_BAD 2
#define FSBCLASS_B 3
#define FSBCLASS_H 4
#define FSBCLASS_DEL 5 // "Deleted" class must be highest numerically
#ifdef FS_BENCHMARK
static int lv, lvmax;
static long ncomp, nqsort, tt;
#endif
fs_nodebug int fs_compare_ls(FS_lxd * lxd, FSLSnum a, FSLSnum b)
{
auto FS_w wa, wb;
auto FS_h ha, hb;
auto int rc;
auto int aclass, bclass;
auto char erased;
auto long wdiff;
#ifdef FS_BENCHMARK
ncomp++;
#endif
fs_get_headers(lxd, a, NULL, &ha, &wa);
fs_get_headers(lxd, b, NULL, &hb, &wb);
#define FS_MAGIC2CLASS(mag, class, lxd) \
switch (mag) { \
default: class = FSBCLASS_BAD; break; \
case FSMAGIC_F: class = FSBCLASS_FREE; break; \
case FSMAGIC_DEL: class = lxd->ps_size <= lxd->ls_size ? FSBCLASS_FREE : FSBCLASS_DEL; break; \
case FSMAGIC_BAD: class = FSBCLASS_BAD; break; \
case FSMAGIC_B: class = FSBCLASS_B; break; \
case FSMAGIC_H: class = FSBCLASS_H; break; \
}
FS_MAGIC2CLASS(ha.magic, aclass, lxd)
FS_MAGIC2CLASS(hb.magic, bclass, lxd)
if (aclass != bclass)
rc = aclass - bclass;
else switch (aclass) {
case FSBCLASS_FREE: case FSBCLASS_DEL:
if (lxd->wear_leveling) {
wdiff = wa.wear - wb.wear;
if (wdiff > 0) {
rc = 1; break;
}
if (wdiff < 0) {
rc = -1; break;
}
}
// fall through
default:
rc = (int)(a - b);
break;
case FSBCLASS_B: case FSBCLASS_H:
rc = (int)(ha.filenum - hb.filenum);
if (rc) break;
rc = (short)(ha.seq - hb.seq); // This works with wrap-around too, assuming
// number in sequence <= sequence-max-length / 2.
if (rc) break;
// We don't handle version wrap-around
if (hb.version > ha.version)
rc = 1;
else if (hb.version == ha.version)
rc = (int)(a - b);
else
rc = -1;
break;
}
return rc;
}
fs_nodebug int fs_qsort(FS_lxd * lxd, long base, FSLSnum n)
{
auto FSLSnum i, j, piv, lo, hi, atpiv, temp;
auto long pivot, p2;
#ifdef FS_BENCHMARK
lv++;
nqsort++;
if (lv > lvmax) lvmax = lv;
#endif
lo = 0;
hi = n - 1;
if (n == 2) {
// Sort 2 elements quickly
pivot = FS_LS2TABENT(base, 0);
atpiv = fs_lstabent(pivot);
p2 = FS_LS2TABENT(base, 1);
temp = fs_lstabent(p2);
if (fs_compare_ls(lxd, atpiv, temp) > 0) {
fs_set_lstabent(pivot, temp);
fs_set_lstabent(p2, atpiv);
}
#ifdef FS_BENCHMARK
lv--;
#endif
return 0;
}
//if (lo < hi) // This test omitted: caller must set n >= 2!
//{
piv = (lo + hi) >> 1; // center pivot
i = lo;
j = hi;
pivot = FS_LS2TABENT(base, piv);
atpiv = fs_lstabent(pivot);
while (i < j)
{
while (i < j && fs_compare_ls(lxd, fs_lstabent(FS_LS2TABENT(base, i)), atpiv) <= 0)
++i;
while (i < j && fs_compare_ls(lxd, fs_lstabent(FS_LS2TABENT(base, j)), atpiv) >= 0)
--j;
if (i < j) {
// Swap entries i, j.
temp = fs_lstabent(FS_LS2TABENT(base, j));
fs_set_lstabent(FS_LS2TABENT(base, j), fs_lstabent(FS_LS2TABENT(base, i)));
fs_set_lstabent(FS_LS2TABENT(base, i), temp);
atpiv = fs_lstabent(pivot);
}
}
if (piv < i && fs_compare_ls(lxd, fs_lstabent(FS_LS2TABENT(base, i)), atpiv) > 0)
--i;
// Swap i and pivot
if (i != piv) {
fs_set_lstabent(pivot, fs_lstabent(FS_LS2TABENT(base, i)));
fs_set_lstabent(FS_LS2TABENT(base, i), atpiv);
}
if ((int)(i - lo) <= (int)(hi - i))
{ // control stack
if ((temp = i - lo) > 1)
fs_qsort(lxd, FS_LS2TABENT(base, lo), temp);
if ((temp = hi - i) > 1)
fs_qsort(lxd, FS_LS2TABENT(base, i+1), temp);
}
else
{
if ((temp = hi - i) > 1)
fs_qsort(lxd, FS_LS2TABENT(base, i+1), temp);
if ((temp = i - lo) > 1)
fs_qsort(lxd, FS_LS2TABENT(base, lo), temp);
}
//}
#ifdef FS_BENCHMARK
lv--;
#endif
return 0;
}
fs_nodebug FS_ef * fs_find_create_ef(FSfilenum name, FSLXnum mlx, FSLXnum dlx)
{
// Find existing-file descriptor, or create if not found. Returns NULL if
// cannot create. name is file number, mlx/dlx are LX numbers for metadata
// and data respectively. If either is zero, then anything matches. If
// the EF exists, but mlx/dlx disagree with the EF, return NULL. For NULL
// returns, errno will be set to EEXIST for mismatch with existing, or
// ENFILE if no free entries remain.
auto int i;
auto FS_ef * ef;
auto FSLXnum emlx, edlx;
if (FS_EXISTS(name)) {
ef = FS_EF_OF_FILE(name);
_set_errno(EEXIST);
emlx = ef->metalx;
edlx = ef->datalx;
if (mlx && emlx && emlx != mlx)
return NULL;
if (dlx && edlx && edlx != dlx)
return NULL;
if (!emlx)
ef->metalx = mlx;
if (!edlx)
ef->datalx = dlx;
return FS_EF_OF_FILE(name);
}
for (i = 1; i <= FS_MAX_FILES; i++) {
ef = _fs.ef + i;
if (!ef->in_use) {
_fs.eftab[name] = (FSEFnum)i;
memset(ef, 0, sizeof(*ef));
ef->name = name;
ef->in_use = 1;
ef->metalx = mlx;
ef->datalx = dlx;
ef->attrs = 0xFF; // FIXME: set appropriately
ef->first_mls = FS_INVALID_LS;
ef->first_dls = FS_INVALID_LS;
return ef;
}
}
_set_errno(ENFILE);
return NULL;
}
fs_nodebug int fs_init_lstab(FS_lxd * lxd)
{
// Initialise the lstab by reading the header blocks of each LS. The lstab itself is
// already allocated. A temporary buffer of the same size is allocated for the
// purpose of sorting the lstab. The sort is performed in order:
// Free (available) LSs
// Bad
// B-blocks
// H-blocks
// Deleted (i.e. freed LSs within larger PSs)
// For H- and B-blocks, the subsequence is in order of file number, sequence number
// within file, then version number within sequence.
// For F- and DEL- blocks, the subsequence is in order of increasing wear level if
// applicable or by LS number if not. Wear levels for LSs which are not the first
// in their PS (i.e. have dummy W-blocks) take the wear level of the first LS in the PS.
// An initial pass is performed to verify that the LSs to be sorted have consistent
// checksums - any which do not are erased if possible. Then they are placed at the
// end of the table (with the DEL entries), and not sorted further.
auto FS_w w, w1;
auto FS_h h;
auto long ttab;
auto long ttabsize;
auto FSLSnum i;
auto int pclass, kclass;
auto long lphy;
auto FSLSnum * counter;
auto FS_ef * ef;
auto int igchain;
auto FSLSnum ls;
auto FSfilenum pfile;
auto FSversion pvers;
auto char erased;
auto FSLSnum num_to_sort;
auto FSLSnum num_deleted;
ttab = xalloc(ttabsize = (long)sizeof(FSLSnum) * lxd->num_ls);
if (!ttab) {
_set_errno(ENOMEM);
return 1;
}
// Perform an initial pass through the LX. Look for bad header checksums and
// erase those LSs. Also look for uninitialised wear blocks: if found, set them
// to wear count 1.
#ifdef FS_BENCHMARK
tt = MS_TIMER;
#endif
num_to_sort = 0;
num_deleted = 0;
for (i = 0; i < lxd->num_ls; i++) {
fs_get_headers(lxd, i, &w, &h, &w1);
if (fs_verify_headers(lxd, &w, &h)) {
// Bad header checksum.
TRACE(("Bad hdr chk LS %u\n", i));
if (fs_erase_logical(lxd, i, &erased) || !erased) {
// Put unerasable or deleted LSs at end of table (speeds sort).
num_deleted++;
fs_set_lstabent(FS_LS2TABENT(ttab, lxd->num_ls - num_deleted), i);
continue;
}
}
fs_set_lstabent(FS_LS2TABENT(ttab, num_to_sort), i);
num_to_sort++;
}
#ifdef FS_BENCHMARK
tt = MS_TIMER - tt;
printf("Took %ld ms to scan\n", tt);
#endif
num_to_sort += num_deleted; // Sort everything
// Specialised qsort algorithm.
#ifdef FS_BENCHMARK
lv = 0;
lvmax = 0;
nqsort = 0;
ncomp = 0;
tt = MS_TIMER;
#endif
if (num_to_sort > 1)
fs_qsort(lxd, ttab, num_to_sort);
#ifdef FS_BENCHMARK
tt = MS_TIMER - tt;
printf("Max qsort levels = %d, qsort entry=%ld, comparisons=%ld, time=%ld ms\n", lvmax, nqsort, ncomp, tt);
#endif
// Scan the generated list, looking for 'breaks'. Initialise the lstab. After
// this, the lstab file chains may include multiple LSs with the same seq but
// different version. The most recent version with valid checksum will be retained.
igchain = 1;
pclass = 0;
lxd->first_free = FS_INVALID_LS;
lxd->first_deleted = FS_INVALID_LS;
lxd->first_bad = FS_INVALID_LS;
lxd->num_free = 0;
lxd->num_deleted = 0;
lxd->num_bad = 0;
for (i = 0; i < lxd->num_ls; i++) {
ls = fs_lstabent(FS_LS2TABENT(ttab, i)); // Get sorted LS number
fs_get_headers(lxd, ls, &w, &h, NULL);
if (i >= num_to_sort)
kclass = FSBCLASS_DEL; // deleted
else
FS_MAGIC2CLASS(h.magic, kclass, lxd)
if (kclass != pclass || (kclass == FSBCLASS_H || kclass == FSBCLASS_B) && h.filenum != pfile) {
// New list starting. Finish old list, start new
if (!igchain)
fs_set_lstabent(lphy, FS_INVALID_LS);
pclass = kclass;
pfile = h.filenum;
igchain = 0;
switch (kclass) {
case FSBCLASS_FREE: lphy = paddr(&lxd->first_free); counter = &lxd->num_free; break;
case FSBCLASS_DEL: lphy = paddr(&lxd->first_deleted); counter = &lxd->num_deleted; break;
default: lphy = paddr(&lxd->first_bad); counter = &lxd->num_bad; break;
case FSBCLASS_B: // Data LSs
ef = fs_find_create_ef(h.filenum, 0, FS_PTR2LXN(lxd));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -