diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index 53e6d22..8afc270 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -319,5 +319,11 @@ func (rfs *ReverseFS) Readlink(cipherPath string, context *fuse.Context) (string // Symlinks are encrypted like file contents and base64-encoded cBinTarget := rfs.contentEnc.EncryptBlockNonce([]byte(plainTarget), 0, nil, nonce) cTarget := rfs.nameTransform.B64.EncodeToString(cBinTarget) + // The kernel will reject a symlink target above 4096 chars and return + // and I/O error to the user. Better emit the proper error ourselves. + const PATH_MAX = 4096 // not defined on Darwin + if len(cTarget) > PATH_MAX { + return "", fuse.Status(syscall.ENAMETOOLONG) + } return cTarget, fuse.OK } diff --git a/tests/reverse/correctness_test.go b/tests/reverse/correctness_test.go index ee0a5b0..63043b7 100644 --- a/tests/reverse/correctness_test.go +++ b/tests/reverse/correctness_test.go @@ -147,3 +147,25 @@ func TestEnoent(t *testing.T) { t.Errorf("want ENOENT, got: %v", err) } } + +// If the symlink target gets too long due to base64 encoding, we should +// return ENAMETOOLONG instead of having the kernel reject the data and +// returning an I/O error to the user. +// https://github.com/rfjakob/gocryptfs/issues/167 +func TestTooLongSymlink(t *testing.T) { + fn := dirA + "/TooLongSymlink" + target := string(bytes.Repeat([]byte("x"), 4000)) + err := os.Symlink(target, fn) + if err != nil { + t.Fatal(err) + } + _, err = os.Readlink(dirC + "/TooLongSymlink") + if err == nil { + return + } + err2 := err.(*os.PathError) + if err2.Err != syscall.ENAMETOOLONG { + t.Errorf("Expected %q error, got %q instead", syscall.ENAMETOOLONG, + err2.Err) + } +}