symlink loop testing
diff --git a/request-example.go b/request-example.go
index 0afaefb..0de41df 100644
--- a/request-example.go
+++ b/request-example.go
@@ -16,6 +16,10 @@
 	"time"
 )
 
+const maxSymlinkFollows = 5
+
+var errTooManySymlinks = errors.New("too many symbolic links")
+
 // InMemHandler returns a Hanlders object with the test handlers.
 func InMemHandler() Handlers {
 	root := &root{
@@ -87,6 +91,7 @@
 			return nil, os.ErrNotExist
 		}
 
+		var count int
 		// You can create files through dangling symlinks.
 		link, err := fs.lfetch(pathname)
 		for err == nil && link.symlink != "" {
@@ -95,6 +100,10 @@
 				return nil, os.ErrInvalid
 			}
 
+			if count++; count > maxSymlinkFollows {
+				return nil, errTooManySymlinks
+			}
+
 			pathname = link.symlink
 			link, err = fs.lfetch(pathname)
 		}
@@ -485,18 +494,11 @@
 func (fs *root) canonName(pathname string) (string, error) {
 	dirname, filename := path.Dir(pathname), path.Base(pathname)
 
-	dir, err := fs.lfetch(dirname)
+	dir, err := fs.fetch(dirname)
 	if err != nil {
 		return "", err
 	}
 
-	for dir.symlink != "" {
-		dir, err = fs.lfetch(dir.symlink)
-		if err != nil {
-			return "", err
-		}
-	}
-
 	if !dir.IsDir() {
 		return "", syscall.ENOTDIR
 	}
@@ -521,7 +523,12 @@
 		return nil, err
 	}
 
+	var count int
 	for file.symlink != "" {
+		if count++; count > maxSymlinkFollows {
+			return nil, errTooManySymlinks
+		}
+
 		file, err = fs.lfetch(file.symlink)
 		if err != nil {
 			return nil, err
diff --git a/request-server_test.go b/request-server_test.go
index 0f4d113..7161006 100644
--- a/request-server_test.go
+++ b/request-server_test.go
@@ -8,6 +8,7 @@
 	"net"
 	"os"
 	"testing"
+	"time"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -562,6 +563,44 @@
 	checkRequestServerAllocator(t, p)
 }
 
+func TestRequestSymlinkLoop(t *testing.T) {
+	p := clientRequestServerPair(t)
+	defer p.Close()
+
+	err := p.cli.Symlink("/foo", "/bar")
+	require.NoError(t, err)
+	err = p.cli.Symlink("/bar", "/baz")
+	require.NoError(t, err)
+	err = p.cli.Symlink("/baz", "/foo")
+	require.NoError(t, err)
+
+	// test should fail if we reach this point
+	timer := time.NewTimer(1 * time.Second)
+	defer timer.Stop()
+
+	var content []byte
+
+	done := make(chan struct{})
+	go func() {
+		defer close(done)
+
+		content, err = getTestFile(p.cli, "/bar")
+	}()
+
+	select {
+	case <-timer.C:
+		t.Fatal("symlink loop following timed out")
+		return // just to let the compiler be absolutely sure
+
+	case <-done:
+	}
+
+	assert.Error(t, err)
+	assert.Len(t, content, 0)
+
+	checkRequestServerAllocator(t, p)
+}
+
 func TestRequestSymlinkDanglingFiles(t *testing.T) {
 	p := clientRequestServerPair(t)
 	defer p.Close()