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