# whatfiles: a plain log of every file a Linux program touches

> whatfiles is a small C utility that attaches to a running process or launches one, then writes a line per file operation with the syscall, the process and the kernel return value. It is aimed at the person who wants to see what an installer or a vendor binary actually did, and it deliberately covers less than strace while reading more easily.

**spieglt/whatfiles** — Log what files are accessed by any Linux process

- Repository: https://github.com/spieglt/whatfiles
- Stars: 950 · Forks: 32
- Language: C
- License: GPL-3.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/spieglt-whatfiles

## A question lsof cannot answer and strace answers too loudly

The motivation in the project is written in the first person and is easy to read: the author was frustrated at the lack of a simple utility to see which files a process touches from main() to exit. The use cases given are not trusting a software vendor and being concerned about malware, and in both cases the question is what a program or an installer did to the system. The two tools that come to mind do not answer it in the form people want. lsof observes a moment in time rather than a run, and strace is described as large and somewhat complicated. whatfiles is positioned between them: narrower than strace, more readable in output, and covering a run from start to exit rather than a snapshot.

The scope is file operations. It records reads, writes, creates and deletes, and it follows new processes and threads created by the targeted process, so a program that spawns helpers does not lose its trail. Each operation also records whether it succeeded, which is the part that separates this from a list of paths: a file that was opened and failed to open are different facts, and the tool keeps both.

## Each log line names the operation, the syscall and the kernel's answer

The output format is a fixed line per event, and the sample in the project documentation shows the fields. The mode says what was done, the file is the path involved, the syscall names the call, the PID and process identify who did it, and the result is the value the kernel returned. A file that could not be opened appears with a negative result and the reason in parentheses, while a successful open appears with the descriptor number as the result, so a reader can tell failure from success without guessing.

```bash
mode:   read, file: /tmp/demo/copy.txt, syscall: openat(), PID: 17004, process: /usr/bin/cp, result: -1 (No such file or directory)
mode:   read, file: /etc/hostname, syscall: openat(), PID: 17004, process: /usr/bin/cp, result: 3
mode: write+create, file: /tmp/demo/copy.txt, syscall: openat(), PID: 17004, process: /usr/bin/cp, result: 4
mode: rename, file: /tmp/demo/copy.txt, to: /tmp/demo/renamed.txt, syscall: renameat2(), PID: 17006, process: /usr/bin/mv, result: 0
mode: delete, file: /tmp/demo/renamed.txt, syscall: unlinkat(), PID: 17008, process: /usr/bin/rm, result: 0
```

Paths are always absolute, and the documentation explains the reason: relative paths are resolved against the working directory of the process, or against the directory passed to an *at() syscall, which means the log would be ambiguous if it printed them as the program wrote them. Rename events carry both the old and the new path, so a file that moved is visible as a move and not as an unexplained disappearance and reappearance.

## Launching a program, attaching to one, and choosing where the log goes

The command line has four modes that matter in practice. The simplest launches a program and writes the log to a file in the current directory, and the -o flag chooses a different path. The -d flag adds debug output and -s sends the log to stdout instead of a file, which is the combination to reach for when you want to watch a run in a terminal. Attaching to a process that is already running needs root, and -p takes the process id. The -k flag makes whatfiles kill the traced program if whatfiles itself is killed, instead of letting it continue untraced, which is the safer default for tracing something you do not trust.

```bash
$ whatfiles ls -lah ~/Documents
$ whatfiles -o MyLogFile ls ..
$ whatfiles -d -s apt install zoom
$ sudo whatfiles -p 1234
$ whatfiles -k ./installer.sh
```

Interrupting it is handled explicitly. Pressing Ctrl-C at any time makes whatfiles detach from everything it is tracing, leaves those processes running, and finishes writing the log, so an interrupted run still produces a usable file and does not leave the traced program in a broken state.

## Building it takes make, and the Makefile picks the architecture for you

The build requirements are named directly: gcc and make. The Makefile sets the compiler flags, the standard, the warning set and a default install prefix, then resolves the current architecture into a directory name for the per-architecture register file. That resolution is explicit about what is supported: x86_64, x86, arm32 and arm64, and anything else stops the build with an error rather than producing a binary that decodes syscalls wrongly.

```bash
$ cd whatfiles
$ make
$ sudo make install
```

The install step honours PREFIX and DESTDIR, so a packaged build can be staged into a different root without patching the Makefile. The source list in the Makefile also shows the shape of the program: separate files for the tracer itself, the attach path, the syscall table, the string handling and a hashmap, plus a per-architecture registers file.

The kernel requirement is stated as Linux 3.4 or newer. On Linux 5.3 and newer the tool asks the kernel directly about each syscall stop, which is what makes 32-bit syscalls on a 64-bit machine decode correctly, and on older kernels it falls back to reading registers, which is the less accurate path.

## Beyond open and unlink: rename, link, symlink, mkdir and exec

The set of reported operations goes past the obvious ones. Besides opening, creating and deleting files, the tool reports rename, link, symlink, mkdir, rmdir, truncate, chmod, chown and exec of a program. That list matters for the questions people actually ask, because an installer that changes permissions, swaps a symlink or replaces a binary has done something a read and write log would not show. The exec entry matters too, because it marks the moment a process became a different program.

The project also keeps a file called SYSCALLS.md, which covers the operations that are not reported yet and explains why each addition would be worth making. That is a useful artefact for judging the tool honestly, since it converts an open-ended wish list into a stated backlog. A separate file, RELEASE_NOTES.md, sits alongside it, and the repository root also carries a CODE_REVIEW.md, a notes directory, a scraps directory, a tests directory and a Geany project file.

## Hand-off to a copy of the process is the failure mode to plan around

The limitation listed first is the one that will waste your afternoon if you hit it blind. When a program hands off to a copy of itself, whatfiles has nothing left to trace and stops, while the work you wanted to see is done by the untraced copy. Browsers are named as the typical case, because an already running instance takes the request and exits, so the new process ends immediately. The documented workaround is to trace a separate instance with a profile directory that does not exist yet, or to quit the running copy first.

The second limitation is a boundary rather than a bug. The log is a description of what a program did, not a security boundary, because a program that does not want to be watched can tell that it is being traced, and io_uring performs file operations without the syscalls the tool watches. So the absence of a file in the log does not prove the file was never touched. A third practical point is platform coverage: there are no Mac and Windows versions. The author explains that tracing syscalls on Mac needs task_for_pid() and code signing, and points at dtruss, fs_usage and Process Monitor as things that do the job on those systems instead.

## How it compares to strace, and what the project admits about it

The project puts the obvious objection in its own documentation, in the form of a question: is this just a reimplementation of strace with a file filter on it? The answer given is yes, with the qualification that the aim is to be simpler and more user friendly. That is the honest framing, and it is also the right way to evaluate the tool. The syscall filter idea, the trace-everything approach and the per-syscall result reporting are all strace's shape. whatfiles changes the surface: a fixed set of file operations, a plain text line format, absolute paths and a result column that keeps failures legible.

The comparison also tells you what you give up. strace with a broad filter and a decoder will show you every call, including ones that are not about files at all, and you can build a filter over the whole syscall surface. whatfiles covers files and the metadata operations around them, and for anything outside that list SYSCALLS.md is where the answer lives. There is a plain answer to the question of when to reach for the other tool: when you need to see what a program does beyond the filesystem.

## Version 2.0, a GPL licence, and an Android target with its own rules

The project ships a version 2.0 release dated 2026-09-17, and the last push was on the same day, so the tree is actively worked on. The earlier version 1.0 dates from 2020-11-07, which means the release history is thin but the current work is not. The licence is GPL-3.0, which matters if you intend to ship whatfiles inside a product, because the copyleft terms reach into distributed combinations, and a log of file activity produced by the tool is data rather than code, so the practical exposure depends on how you embed it.

Distribution is handled for you in three ways. Ready-to-use binaries are on the releases page, the tool was added to the Arch user repository, and a GitLab pipeline builds it as well. The Makefile also contains an Android target, which is a separate story worth knowing before you plan a mobile workflow.

The Android section is unusually specific, which is a good sign about the state of the port. You cross-compile with the NDK and push the binary to the device.

```bash
$ make android NDK=~/Android/Sdk/ndk/<version>
$ adb push bin/whatfiles-android /data/local/tmp/whatfiles
$ adb shell chmod 755 /data/local/tmp/whatfiles
$ adb shell /data/local/tmp/whatfiles -o /data/local/tmp/ls.log ls /sdcard
```

ANDROID_ABI selects arm64 by default, with arm32, x86_64 and x86 also available, and ANDROID_API sets the minimum API level and defaults to 21. Several device rules follow from how Android is built rather than from the tool: the binary has to go in /data/local/tmp because /sdcard is mounted without execute permission, and the working directory in an adb shell is not writable, so the log path has to be under /data/local/tmp or you use -s to print to stdout. Attaching to an app rather than to a command started by the ordinary shell user needs root, and the author notes that this worked with SELinux enforcing on an Android 14 emulator, while a production device policy may still refuse. One path is explicitly untested: tracing a 32-bit app from an arm64 build has not been exercised on real hardware, because the emulator used has no 32-bit ABI.

## Conclusion

Adopt whatfiles when you need to answer what a specific program did to your filesystem, especially an installer, a vendor binary or anything you do not trust, and keep away from it when you need syscall-level coverage beyond files, when you need a security guarantee, or when you are on Mac or Windows. Before you rely on the output, read the limitations section, because a program that hands off to a copy of itself, or a program that uses io_uring, produces a log that is incomplete by design, and treat every entry as a description of what a process did rather than as a record of what it was prevented from doing.

## FAQ

### How do I build and install whatfiles on Linux?

The build needs gcc and make. Run make in the whatfiles directory, then sudo make install. The install step honours PREFIX and DESTDIR, and x86_64, x86, arm32 and arm64 are supported.

### How do I attach whatfiles to a program that is already running?

Use the -p flag with the process id, and run it with root privileges, for example sudo whatfiles -p 1234. Pressing Ctrl-C detaches from everything being traced, leaves those processes running, and finishes writing the log.

### Does whatfiles work on Mac or Windows?

No. The project states there are no Mac and Windows versions, and points at dtruss and fs_usage on Mac and Process Monitor on Windows as tools that do comparable work.

### What does the result column in the whatfiles log mean?

It is the return value of the syscall, so failed and successful access can be told apart. A failed open shows a negative value with the reason in parentheses, while a successful one shows the descriptor number.

### What is the known weakness of whatfiles when tracing a browser?

If a browser is already running, the new instance hands the request to it and exits, so whatfiles has nothing left to trace. The project suggests tracing a separate instance with a fresh profile directory, or quitting the running copy first.

## Sources

- [Issues](https://github.com/spieglt/whatfiles/issues)
- [License: GPL-3.0](https://github.com/spieglt/whatfiles/blob/master/LICENSE)
- [README](https://github.com/spieglt/whatfiles/blob/master/README.md)
- [Releases](https://github.com/spieglt/whatfiles/releases)
- [spieglt/whatfiles on GitHub](https://github.com/spieglt/whatfiles)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/spieglt-whatfiles
