# LuaFileSystem: directory and attribute access that the Lua standard library omits

> LuaFileSystem is a small C library that complements the file related functions in the standard Lua distribution with a portable way to read the directory structure and the attributes of files, and it is installed with a single LuaRocks command. It suits a Lua program that needs to know what is on disk rather than only how to read a known path, and the build system is a single C file and a shared object.

**lunarmodules/luafilesystem** — LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution.

- Repository: https://github.com/lunarmodules/luafilesystem
- Website: https://lunarmodules.github.io/luafilesystem/
- Stars: 1,017 · Forks: 304
- Language: C
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/lunarmodules-luafilesystem

## What the standard library does not cover

LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution, and the README states the scope in one sentence: it offers a portable way to access the underlying directory structure and file attributes.

That gap is easy to underestimate until you write the code. The standard Lua distribution gives you io.open and the io library, which is everything you need if you know the name of the file you want. It gives you nothing for the questions that come before that. Is this path a directory or a file. What is in this directory. When was this file last modified. How big is it. Is this a symbolic link and what does it point at.

The usual answers in a script are to shell out to ls or stat, which ties the code to a platform and to the presence of a shell, or to bind a C library yourself, which is what this project is. The word portable in the README is doing the real work: the same calls behave the same way on the systems the library supports.

The library is free software and uses the same licence as Lua 5.x, MIT, and the repository is under the lunarmodules organisation. The homepage is at lunarmodules.github.io/luafilesystem, and the last push to the repository was on 2026-09-03, with the current release being 1.9.0 from 2026-07-18.

## Installing it with LuaRocks

The installation is one command.

```bash
luarocks install luafilesystem
```

The repository carries a rockspec, luafilesystem-scm-1.rockspec, which is the file that tells LuaRocks what the package is, what it depends on and how to build it. The name follows the LuaRocks convention of carrying the version in the suffix, and this one describes a package built from the source tree, which is how a rockspec for a repository rather than a tagged tarball is usually named.

That is the whole installation story, and it is worth noticing how little of it there is. There is no configuration step, no generated header to place, and no build directory to point at, because the library is a single C file compiled into a single shared object that the Lua loader finds through the usual package path search.

The project also publishes documentation in the repository, in a docs directory, and the README points to both that and the project website for more information. Having the reference in the repository rather than only on a site is a small but real convenience, since it means the documentation version matches the code version you have.

For anyone who would rather not use a package manager, the build is a make invocation, which the next section covers.

## The build system is one C file and one shared object

The Makefile is short enough to read in full, and it tells you almost everything about the project.

The module name is set with a variable, T equals lfs, and the configuration is included from a file called config, which means the platform specific settings are kept out of the build rules. The source list is a single file, src/lfs.c, and the object file is src/lfs.o.

```
lib: src/lfs.so

src/lfs.so: $(OBJS)
	MACOSX_DEPLOYMENT_TARGET=$(MACOSX_DEPLOYMENT_TARGET); export MACOSX_DEPLOYMENT_TARGET; $(CC) $(LIB_OPTION) -o src/lfs.so $(OBJS)
```

The one unusual line is the macOS deployment target, which is exported before the link step so that the shared object is marked with a minimum macOS version. That is a detail you only need on one platform, and it is exactly the kind of thing that belongs in a platform config file rather than in the rules, which is why the config file exists at all.

The install target is equally direct. It creates the Lua library directory under the install prefix and copies the shared object into it, which means the installation is the same operation on every platform, and the path comes from the config file as LUA_LIBDIR.

There is a second Makefile, Makefile.win, and a second config file, config.win, and the pairing is the clearest evidence of the portability claim. The build is split so that the rules stay identical and only the platform variables change, which is the arrangement that lets the same source compile on a Unix-like system and on Windows without a branch in the middle of the build logic.

## The test target and what it proves

The Makefile has a test target that depends on the library, so the tests run against a freshly built shared object rather than against something already installed on the machine.

```
test: lib
	LUA_CPATH=./src/?.so lua tests/test.lua
```

The LUA_CPATH assignment is the important part. The Lua module search path normally looks in the installed directories, so without that assignment the test would either fail to find the module or, worse, find and test a different, previously installed version of the library. Setting the path to the local source directory means the test exercises the code you just compiled.

That is a small piece of engineering that many projects skip, and it is the difference between a test suite that verifies the change and one that verifies whatever happens to be installed.

The tests themselves live in tests/ and are driven by a single Lua script, test.lua, which is the usual arrangement for a C extension: the C side exposes functions and constants, and the test script exercises them and reports the result.

Continuous integration is set up in the repository as well, with a GitHub Actions workflow, an AppVeyor configuration, and a Coveralls configuration for coverage reporting. Both services are still configured in the repository, which is what a portable library needs.

## How old this code is, and why that is not a criticism

The first line of the Makefile is an identifier with a date in it.

```
# $Id: Makefile,v 1.36 2009/09/21 17:02:44 mascarenhas Exp $
```

That is a revision identifier from the era when the convention was common, and it says the Makefile has been in the project since 2009 with the original author still named in it. The library is older than that in practice, since it predates that Makefile.

For a file system library, age is a feature rather than a liability, and the reason is stability. A library that has been in wide use for a decade and a half has an API that people depend on, and the semantics of a function that has not changed in fifteen years are known rather than guessed at. The counterargument is that a new system feature, a new platform or a new Lua version will not be supported until someone bothers to add it, and that is a real cost.

The release record supports the same picture. The current release is 1.9.0, from July 2026. The maintenance is the same kind of maintenance a mature C library needs: the build still works on current toolchains, the CI still runs, and the bugs that get fixed are the ones that break a build or corrupt a result.

The stewardship detail is worth noticing as well. The repository sits under lunarmodules, the group that maintains the community C modules for Lua, and the original author is still credited in the file header. That is a common pattern for these libraries, where a small number of long lived modules are maintained by an umbrella organisation.

## Where it fits in a project today

The realistic uses are the ones the library's scope describes.

A build or deployment tool written in Lua needs to check whether an output directory exists and is a directory, to list what is inside it, and to know whether a file is newer than another. Doing that with io.open and string manipulation is possible and unpleasant, and shelling out to ls is platform dependent.

A game mod or a plugin loader needs to scan a directory for its own resources, and the same questions come up.

A tool that reports on a filesystem, for a status command or a manifest generator, needs the file attributes and the directory structure, and it needs them to mean the same thing on the developer's machine and on the server.

What the library does not do is write files, and it is worth being clear about that from the start, because the name suggests more than it delivers. The standard io library remains the tool for reading and writing a file you know the name of, and LuaFileSystem is the tool for asking questions about the shape of the tree those files live in.

The dependency is also cheap in the way that matters for a scripting language. It is one C file, one shared object and no runtime service, so adding it to a project does not add an operational surface, and the licensing is the same permissive MIT licence that Lua itself uses, so it does not complicate distribution.

## Conclusion

Adopt LuaFileSystem if your Lua code has to walk a directory, check whether a path is a directory or a file, or read modification times and sizes, because the standard library covers none of that and this library does it portably in one dependency. Do not adopt it if you need to write files, since the library's job is reading the shape of the filesystem. Before you install it, check that your Lua version matches the release you take, and if you build from source, run the test target so the shared object is verified against the test suite rather than assumed to work.

## FAQ

### What does LuaFileSystem add to the Lua standard library?

The standard Lua distribution gives you the io library for reading and writing a file whose name you know. LuaFileSystem complements that with a portable way to access the underlying directory structure and file attributes, and the README describes it as free software under the same MIT licence as Lua 5.x.

### How do I install LuaFileSystem?

With LuaRocks, using luarocks install luafilesystem. The repository ships a rockspec, luafilesystem-scm-1.rockspec, and the documentation is in the docs directory as well as on the project website.

### How do I build and test LuaFileSystem from source?

The build is a single C file compiled into a shared object, with the platform settings in a config file that the Makefile includes, and a separate Makefile.win and config.win for Windows. The test target depends on the library and runs the test script with LUA_CPATH pointed at the local source directory, so the tests exercise the build you just made.

## Sources

- [License: MIT](https://github.com/lunarmodules/luafilesystem/blob/master/LICENSE)
- [lunarmodules/luafilesystem on GitHub](https://github.com/lunarmodules/luafilesystem)
- [Project website](https://lunarmodules.github.io/luafilesystem/)
- [README](https://github.com/lunarmodules/luafilesystem/blob/master/README.md)
- [Releases](https://github.com/lunarmodules/luafilesystem/releases)

---

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