I’ve been mucking a bit with go recently. Nothing special, just doing some small problems that I needed to solve.
At work, I have built up a challenge for new candidates. One of them is the old trick of using all the inodes on a file-system. This is a quick one to rule out the ones who have real world experience.
So I wrote some code:
package main
import (
"fmt"
"os"
"strconv"
"github.com/cloudfoundry/gosigar"
)
func main() {
usage := sigar.FileSystemUsage{}
usage.Get("/var")
fmt.Println(strconv.FormatUint(usage.FreeFiles,10))
for i, val := uint64(0), uint64(usage.FreeFiles); i < val; i++ {
os.Create(strconv.FormatUint(i,10))
}
}
go run ./fill_the_disk.go
But this wouldn't use all the inodes. Why not?
Let's add some debug:
package main
import (
"fmt"
"os"
"strconv"
"github.com/cloudfoundry/gosigar"
)
func main() {
usage := sigar.FileSystemUsage{}
usage.Get("/var")
fmt.Println(strconv.FormatUint(usage.FreeFiles,10))
for i, val := uint64(0), uint64(usage.FreeFiles); i < val; i++ {
_, err := os.Create(strconv.FormatUint(i,10))
if err != nil {
fmt.Printf("%+v", err)
os.Exit(1)
}
}
}
go run ./fill_the_disk.go
946247
open 1362: too many open filesexit status 1
Ahh there we go, we are running out of open file descriptors. Of course.
So the final code becomes:
package main
import (
"fmt"
"os"
"strconv"
"github.com/cloudfoundry/gosigar"
)
func main() {
usage := sigar.FileSystemUsage{}
usage.Get("/var")
fmt.Println(strconv.FormatUint(usage.FreeFiles,10))
for i, val := uint64(0), uint64(usage.FreeFiles); i < val; i++ {
fd, err := os.Create(strconv.FormatUint(i,10))
if err != nil {
fmt.Printf("%+v", err)
os.Exit(1)
}
erra := fd.Close();
if erra != nil {
fmt.Printf("%+v", erra)
os.Exit(1)
}
}
}
and we can compile it like so:
golang-go build fill_the_disk.go