In principle,
keeping track of free space is not strictly necessary :
every block is either allocated or free,
so the free space can be computed by assuming everything is free and then subtracting out everything that's allocated;
and the allocated space can be found by traversing the entire filesystem from the root.
Any block that cannot be found by traversal from the root is,
by definition,
free.