Implement Tasks #19 and #20: ppagecache + pfs helper extraction
Task #19 — ppagecache.c decomposition (ppagecache_helpers.c/h)
ppagecache_compute_page_priority(usecnt): pure function returning the
LRU eviction tier (0–4) that matches the five pagecache_entry_cmp_*
sort comparators in ppagecache.c (thresholds 2/4/8/16).
ppagecache_verify_crc(data, size, stored_crc): wraps pcrc32c_compute
and compares; returns 0 on match, -1 on mismatch.
ppagecache_get_download_urls(fileid, hash, nout): __attribute__((weak))
URL-injection seam; default returns NULL (falls through to real API).
ppagecache.c updated to include ppagecache_helpers.h.
test_ppagecache.c: 10 tests covering tier boundary conditions (0/1/2/3/4
including UINT32_MAX), CRC match, single-bit flip, wrong stored CRC,
zero-length buffer, and weak URL override.
Task #20 — pfs.c helper extraction (pfs_helpers.c/h)
pfs_row_to_folder_stat(row, stbuf): converts psql folder row → struct
stat; uses pfs_task_get_folder_tasks_rdlocked for in-memory mtime.
pfs_row_to_file_stat(row, stbuf, flags): converts psql file row → stat;
encrypted path calls pfs_crpt_plain_size.
pfs_mkdir_to_folder_stat(mk, stbuf): converts in-memory mkdir task →
stat (no SQL).
pfs_apply_task_overlay(stbuf, folder, name, flags): applies pending
mkdir/rmdir/unlink/creat-new overlays from the in-memory task queue;
returns 1/2/-1/0.
pfs_stat_uid/gid: exported globals; pfs.c syncs them from myuid/mygid.
pfs.c updated to #include pfs_helpers.h and sync the uid/gid globals.
pfsfolder.c: pfs_fldr_resolve_path decorated __attribute__((weak)) so
tests can inject fake path resolution without a FUSE mount or psql.
test_pfs_helpers.c: 7 tests covering folder/file stat field correctness,
overlay NULL/mkdir/rmdir/no-match cases, and weak path override; uses
--wrap for pfs_task_get_folder_tasks_rdlocked, pfs_crpt_plain_size, and
ptimer_time to stay SQL/crypto/timer-free.
Production build clean; make check exits 0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>