diff --git a/Makefile.am b/Makefile.am index 00798573..cd0ff2f3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -214,6 +214,17 @@ tests_LTszoff_CFLAGS = @LSOF_TEST_CFLAGS@ -I$(top_srcdir)/include tests_LTunix_SOURCES = tests/LTunix.c tests/LTlib.c tests_LTunix_CFLAGS = @LSOF_TEST_CFLAGS@ -I$(top_srcdir)/include +# Internal cleanup tests also run when the shared library is disabled. +check_LIBRARIES = tests/liblsof_test.a +tests_liblsof_test_a_SOURCES = $(LIBLSOF_SOURCES) +tests_liblsof_test_a_CPPFLAGS = $(lsof_CPPFLAGS) +TESTS += tests/LTfsnames +check_PROGRAMS += tests/LTfsnames +tests_LTfsnames_SOURCES = tests/LTfsnames.c +tests_LTfsnames_CPPFLAGS = $(lsof_CPPFLAGS) +tests_LTfsnames_LDADD = tests/liblsof_test.a +tests/LTfsnames-LTfsnames.$(OBJEXT): $(top_srcdir)/lib/proc.c $(top_srcdir)/lib/lsof.c + # Library tests, only build when liblsof is enabled if INSTALL_LIBLSOF TESTS += tests/LTbasic2 tests/LTnull diff --git a/lib/lsof.c b/lib/lsof.c index 46a30500..e75a9531 100644 --- a/lib/lsof.c +++ b/lib/lsof.c @@ -299,6 +299,8 @@ enum lsof_error lsof_gather(struct lsof_context *ctx, CLEAN(ctx->cur_file->dev_ch); CLEAN(ctx->cur_file->nm); CLEAN(ctx->cur_file->nma); + CLEAN(ctx->cur_file->fsdir); + CLEAN(ctx->cur_file->fsdev); CLEAN(ctx->cur_file); } diff --git a/lib/proc.c b/lib/proc.c index ec43b4c0..ce007040 100644 --- a/lib/proc.c +++ b/lib/proc.c @@ -101,6 +101,8 @@ void alloc_lfile(struct lsof_context *ctx, (void)free((FREE_P *)Lf->nm); if (Lf->nma) (void)free((FREE_P *)Lf->nma); + CLEAN(Lf->fsdir); + CLEAN(Lf->fsdev); #if defined(HASLFILEADD) && defined(CLRLFILEADD) CLRLFILEADD(Lf) @@ -480,6 +482,8 @@ void free_lproc(struct lproc *lp) { (void)free((FREE_P *)lf->nma); lf->nma = (char *)NULL; } + CLEAN(lf->fsdir); + CLEAN(lf->fsdev); #if defined(HASLFILEADD) && defined(CLRLFILEADD) CLRLFILEADD(lf) diff --git a/tests/LTfsnames.c b/tests/LTfsnames.c new file mode 100644 index 00000000..87c593de --- /dev/null +++ b/tests/LTfsnames.c @@ -0,0 +1,90 @@ +/* LTfsnames.c -- filesystem-name ownership in file record cleanup. */ + +#include "common.h" + +static char *names[2]; + +static void tracked_free(void *ptr) { + int i; + for (i = 0; i < 2; i++) { + if (ptr && ptr == names[i]) + names[i] = NULL; + } + free(ptr); +} + +/* No kernel access: leave the test's orphaned file for lsof_gather to free. */ +static void test_gather_proc_info(struct lsof_context *ctx) { (void)ctx; } + +/* Intercept only these implementations' frees, without production test hooks + * or platform-specific allocator/linker support. Link other sources normally. */ +#define free tracked_free +#define gather_proc_info test_gather_proc_info +#include "../lib/proc.c" +#include "../lib/lsof.c" +#undef gather_proc_info +#undef free + +static void set_names(struct lfile *lf) { + names[0] = lf->fsdir = mkstrcpy("/mountpoint", NULL); + names[1] = lf->fsdev = mkstrcpy("/dev/filesystem", NULL); + if (!names[0] || !names[1]) { + fprintf(stderr, "cannot allocate filesystem names\n"); + exit(1); + } +} + +static int expect_freed(const char *test) { + int i, failed = 0; + for (i = 0; i < 2; i++) { + if (names[i]) { + fprintf(stderr, "FAIL: %s did not free %s\n", test, + i ? "fsdev" : "fsdir"); + failed = 1; + /* Reclaim missed allocations so all cases can run on broken code. */ + CLEAN(names[i]); + } + } + if (!failed) + printf("PASS: %s frees both filesystem names\n", test); + return failed; +} + +int main(int argc, char *argv[]) { + struct lsof_context *ctx = lsof_new(); + struct lproc lp = {0}; + struct lsof_result *result = NULL; + int failed = 0; + (void)argc; + (void)argv; + if (!ctx) { + fprintf(stderr, "lsof_new failed\n"); + return 1; + } + ctx->cur_proc = &lp; + + alloc_lfile(ctx, LSOF_FD_NUMERIC, 0); + set_names(ctx->cur_file); + alloc_lfile(ctx, LSOF_FD_NUMERIC, 1); + failed |= expect_freed("reusing an unselected file record"); + + set_names(ctx->cur_file); + lp.file = ctx->cur_file; + ctx->cur_file = NULL; + free_lproc(&lp); + failed |= expect_freed("freeing a process's file records"); + + alloc_lfile(ctx, LSOF_FD_NUMERIC, 2); + set_names(ctx->cur_file); + ctx->frozen = 1; /* Skip kernel initialization as well as enumeration. */ + if (lsof_gather(ctx, &result) != LSOF_SUCCESS) { + fprintf(stderr, "lsof_gather failed\n"); + failed = 1; + } + failed |= expect_freed("discarding an orphaned file after gathering"); + + lsof_free_result(result); + ctx->cur_proc = NULL; + lsof_destroy(ctx); + return failed; +}