# B6J2 — rmtfs v1.3 source audit: RAM shadow lifetime with `rmtfs -r -P -v` (no -s) Source: `linux/initramfs-modem1/dl/rmtfs-src` = git tag v1.3 (b30a3eb). Board binary: Alpine `rmtfs 1.3-r0` (aports commit 1628fd4c), sha256 1bf6b195… (same in initramfs-mm1); v1.3-specific strings present ("write to %zd bytes exceededs max size", "[RMTFS] bye from %d"). ## Data structures (storage.c) - `static struct rmtfd rmtfds[MAX_CALLERS=10]` — process-global static array; each slot: `id`, `node`, `fd`, `partition`, `shadow_buf`, `shadow_len`. - `storage_read_only` (= -r) is a process-global flag set once in `storage_init()`. - `storage_init()` is called exactly once in `main()` (rmtfs.c:575) and only initialises slots to fd=-1 / shadow_buf=NULL. ## open/read path - QMI OPEN (rmtfs.c:55 `rmtfs_open`) → `storage_open(pkt->node, path)` (storage.c:112). - storage.c:131-137: **if a slot with `(fd != -1 || shadow_buf) && node == node && partition == part` exists, that slot is returned as-is** — no `open()`, no `read()` of the partition. - Only for a free slot: `fd_open()` → with -r `storage_populate_shadow_buf()` (storage.c:280): open(O_RDONLY) the by-partlabel device, `calloc(len)`, one `read()` of the whole partition into `shadow_buf`, close the fd. This is the only place the backing partition is read. ## request handling - RW_IOVEC read → `storage_pread()`: with -r, memcpy from `shadow_buf` (storage.c:228-231). - RW_IOVEC write → `storage_pwrite()`: with -r, memcpy into `shadow_buf` (realloc if it grows), never touches the fd (storage.c:247-267); `storage_sync()` is a no-op with -r (storage.c:274). - QMI CLOSE → `storage_close()` → `free(shadow_buf)` (storage.c:176-188). This and `storage_exit()` (process exit) are the ONLY frees. ## reconnect behaviour (MSS stop/start while rmtfs lives) - QRTR BYE (`rmtfs_bye`) and DEL_CLIENT (`rmtfs_del_client`) handlers only dbgprintf and return 0 (rmtfs.c:346-358): nothing is closed or freed. - ENETRESET on the QRTR socket: `main()` loops `do { run_rmtfs() } while (ret == -ENETRESET)` (rmtfs.c:581-583); `run_rmtfs` only re-opens/re-publishes the socket; `storage_init/exit` are outside the loop → shadows survive. - Without -s: `rprocfd = -1` → rmtfs never writes remoteproc state; SIGTERM → loop `break` → `storage_exit()` → process exits (rmtfs.c:453-455). - The modem on this board never sends QMI CLOSE: session-28 rmtfs.log (11 MSS boots) has 0 "close" lines; each MSS stop shows only `del_client` + `bye from 0`. The next MSS boot re-opens the same 4 paths from the same node 0 and gets the same caller ids 0..3. ## Answer With a single long-lived `rmtfs -r -P -v` process, a modem_fs1/fs2/fsg/fsc write received from MSS stays in that slot's `shadow_buf` for the lifetime of the process, and a later MSS boot (same node, no CLOSE) is served from that modified RAM copy — **the backing partitions are NOT re-read on MSS reconnect**. They are re-read only by a new rmtfs process (or after an explicit QMI CLOSE, which this modem does not send). The old B6 flow started a new rmtfs per cycle, so every MSS boot saw the on-disk EFS image. → **GO for B6J3.** Caveats: (1) a hypothetical CLOSE from the modem would free the shadow (debug printf in `rmtfs_close` also passes an int for %s — would be UB with -v); not observed. (2) The shadow is only the EFS part of the modem's persistent state; USIM files (EF_EPSLOCI etc.) persist across MSS restarts in both flows.