diff --git a/fs/bootfs.cc b/fs/bootfs.cc
index 1b563fc4439b3eee2432dab157e4457da44d2018..8159f8d26750fa8018d7771c30954be4cbb04690 100644
--- a/fs/bootfs.cc
+++ b/fs/bootfs.cc
@@ -53,7 +53,7 @@ bootfs::dir::dir(bootfs& fs, std::string path)
 {
 }
 
-fileref bootfs::dir::open(std::string name)
+fileref bootfs::dir::do_open(std::string name)
 {
     return _fs.do_open(_path + name);
 }
diff --git a/fs/bootfs.hh b/fs/bootfs.hh
index a3d332eb71f8c7b3707004721ccb5d7ef3dffd9b..63f29cdc83555c4f95daead502a697e4e85ea39a 100644
--- a/fs/bootfs.hh
+++ b/fs/bootfs.hh
@@ -33,7 +33,7 @@ private:
 class bootfs::dir : public ::dir {
 public:
     dir(bootfs& fs, std::string path);
-    virtual fileref open(std::string name);
+    virtual fileref do_open(std::string name);
     virtual uint64_t size();
     virtual void read(void* buffer, uint64_t offset, uint64_t len);
 private:
diff --git a/fs/fs.cc b/fs/fs.cc
index 68c2af78cbb41b223945410638fb4b19a36647b4..2dab7d45118a5ef4982b99544a3033dde5067c7e 100644
--- a/fs/fs.cc
+++ b/fs/fs.cc
@@ -2,6 +2,18 @@
 
 filesystem* rootfs;
 
+namespace std {
+
+template <>
+struct hash<file::cache_key> {
+    size_t operator()(file::cache_key k) const {
+        return reinterpret_cast<uintptr_t>(k.first.get())
+               ^ std::hash<std::string>()(k.second);
+    }
+};
+
+}
+
 file::file()
     : _refs(0)
 {
@@ -43,8 +55,21 @@ fileref filesystem::open(std::string name)
     return d->open(name.substr(s, name.npos));
 }
 
+fileref dir::open(std::string name)
+{
+    cache_key key(this, name);
+    auto ret = _cache.find(key);
+    if (ret == _cache.end()) {
+        auto f = do_open(name);
+        ret = _cache.insert(cache_type::value_type(key, f)).first;
+    }
+    return ret->second;
+}
+
 dirref dir::subdir(std::string name)
 {
     // trivial implementation, can be overridden
     return boost::dynamic_pointer_cast<dir>(open(name));
 }
+
+file::cache_type file::_cache;
diff --git a/fs/fs.hh b/fs/fs.hh
index 85d1ccc77eed23aab36297f08a5c2b52099bb5eb..570f35ac98c6b75c764fc641997db2b54f95f761 100644
--- a/fs/fs.hh
+++ b/fs/fs.hh
@@ -4,6 +4,7 @@
 #include <string>
 #include <cstdint>
 #include <boost/intrusive_ptr.hpp>
+#include <unordered_map>
 
 class file;
 class dir;
@@ -24,12 +25,23 @@ private:
     unsigned _refs; // FIXME: make atomic
     friend void intrusive_ptr_add_ref(file* f) { f->ref(); }
     friend void intrusive_ptr_release(file* f) { f->unref(); }
+    friend class dir;
+private:
+    typedef std::pair<dirref, std::string> cache_key;
+    // FIXME: an intrusive container
+    typedef std::unordered_map<cache_key, fileref> cache_type;
+    static cache_type _cache;
+    friend struct std::hash<cache_key>;
 };
 
 class dir : public file {
 public:
-    virtual fileref open(std::string name) = 0;
+    fileref open(std::string name);
+    virtual fileref do_open(std::string name) = 0;
     dirref subdir(std::string name);
+private:
+    dirref _parent;
+    std::string _name;
 };
 
 class filesystem {