diff --git a/Network-NineP.cabal b/Network-NineP.cabal
--- a/Network-NineP.cabal
+++ b/Network-NineP.cabal
@@ -1,5 +1,5 @@
 Name:                Network-NineP
-Version:             0.2.0
+Version:             0.3.0
 Description:         A library providing one with a somewhat higher level interface to 9P2000 protocol than existing implementations. Designed to facilitate rapid development of synthetic filesystems.
 Synopsis:            High-level abstraction over 9P protocol
 Maintainer:          Sergey Alirzaev <zl29ah@gmail.com>
@@ -9,7 +9,7 @@
 Build-Type:          Simple
 Cabal-Version:       >= 1.6
 Stability:           Experimental
-Tested-With:         GHC == 7.8.2
+Tested-With:         GHC == 7.8.4
 
 Source-repository head
   type:              git
@@ -18,7 +18,7 @@
 Source-repository this
   type:              git
   location:          https://github.com/l29ah/Network-NineP.git
-  tag:               0.2.0
+  tag:               0.3.0
 
 Library
   Build-Depends:
@@ -26,13 +26,13 @@
     bytestring >= 0.9.2.1 && < 0.11,
     containers >= 0.4.2.1 && < 0.6,
     NineP >= 0.0.2 && < 0.1,
-    network >= 2.3.0.14 && < 2.6,
+    network >= 2.3.0.14 && < 2.7,
     binary >= 0.5.1.0 && < 0.8,
-    mtl >= 2.1.2 && < 2.2,
+    mtl >= 2.1.2 && < 2.3,
     monad-loops >= 0.3.3.0 && < 0.5,
     regex-posix >= 0.95.2 && < 0.96,
     mstate >= 0.2.4 && < 0.3,
-    transformers >= 0.3.0.0 && < 0.4,
+    transformers >= 0.3.0.0 && < 0.5,
     stateref >= 0.3 && < 0.4,
     convertible >= 1.0.11.1 && < 1.2
   Exposed-modules:
diff --git a/Network/NineP/Error.hs b/Network/NineP/Error.hs
--- a/Network/NineP/Error.hs
+++ b/Network/NineP/Error.hs
@@ -25,11 +25,12 @@
 	noMsg = undefined
 	strMsg = OtherError
 
+-- |See also: @linux/net/9p/error.c@
 instance Show NineError where
 	show (ENotImplemented s) = s ++ " is not implemented"
-	show ENotADir = "tried to walk into a non-directory"
-	show EDir = "tried to write to a directory"
-	show (ENoFile s) = "file " ++ s ++ " not found"
+	show ENotADir = "not a directory"
+	show EDir = "Is a directory"
+	show (ENoFile s) = "file not found"
 	show (ENoFid i) = "fid " ++ show i ++ " is not registered on the server"
 	show ENoAuthRequired = "the server doesn't require any kind of authentication"
 	show EPermissionDenied = "permission denied"
diff --git a/Network/NineP/File.hs b/Network/NineP/File.hs
--- a/Network/NineP/File.hs
+++ b/Network/NineP/File.hs
@@ -6,9 +6,13 @@
 {-# LANGUAGE ScopedTypeVariables, FlexibleContexts, OverloadedStrings #-}
 
 module Network.NineP.File
-	( simpleFile
+	( isDir
+	, simpleFile
 	, simpleFileBy
+	, simpleDirectory
 	, rwFile
+	, memoryFile
+	, memoryDirectory
 	) where
 
 import Control.Concurrent.Chan
@@ -16,7 +20,9 @@
 import Control.Monad.EmbedIO
 import Data.ByteString.Lazy.Char8 (ByteString)
 import qualified Data.ByteString.Lazy.Char8 as B
+import Data.Bits
 import Data.Convertible.Base
+import Data.IORef
 import Data.StateRef
 import Data.Word
 import Prelude hiding (read)
@@ -24,14 +30,18 @@
 import Network.NineP.Error
 import Network.NineP.Internal.File
 
+-- |Tests if the file is a directory
+isDir :: Word32	-- ^Permissions
+	-> Bool
+isDir perms = testBit perms 31
+
 simpleRead :: (Monad m, EmbedIO m) => m ByteString -> Word64 -> Word32 -> ErrorT NineError m ByteString
-simpleRead get offset count = case offset of
-	_ -> do
+simpleRead get offset count = do
 		r <- lift $ tryE $ get
 		either (throwError . OtherError . (show :: SomeException -> String))
-				(return . B.take (fromIntegral count)) r
-	--_ -> throwError $ OtherError "can't read at offset"
+				(return . B.take (fromIntegral count) . B.drop (fromIntegral offset)) r
 
+-- TODO make it offset-aware
 simpleWrite :: (Monad m, EmbedIO m) => (ByteString -> m ()) -> Word64 -> ByteString -> ErrorT NineError m Word32
 simpleWrite put offset d = case offset of
 	_ -> do
@@ -51,24 +61,63 @@
 rwFile name rc wc = simpleFileBy name (maybe (fst nulls) id rc, maybe (snd nulls) id wc) (id, id)
 
 -- FIXME sane errors
+-- |Placeholder source and sink
 nulls :: MonadIO m => (m a, a -> m ())
 nulls = (throw $ Underflow, const $ return ())
 
 -- |A file that reads from and writes to the supplied 'Ref' instances, with converstion to the appropriate types. See 'Network.NineP.File.Instances', 'Data.Convertible.Instances' and 'Data.StateRef.Instances'. Use '()', if the file is meant to be read-only/write-only.
 simpleFile :: forall a b m rr wr. (Monad m, EmbedIO m, ReadRef rr m a, Convertible a ByteString, WriteRef wr m b, Convertible ByteString b)
-		=> String
-		-> rr
-		-> wr
+		=> String	-- ^File name
+		-> rr	-- ^Reading function
+		-> wr	-- ^Writing function
 		-> NineFile m
 simpleFile name rr wr = simpleFileBy name (readReference rr, writeReference wr) (convert, convert)
 
+-- Shouldn't we make it simpler?
 -- |Typeclass-free version of 'simpleFile'.
 simpleFileBy :: forall a b m. (Monad m, EmbedIO m)
-		=> String
-		-> (m a, b -> m ())
-		-> (a -> ByteString, ByteString -> b)
+		=> String	-- ^File name
+		-> (m a, b -> m ())	-- ^Reading and writing handle
+		-> (a -> ByteString, ByteString -> b)	-- ^Type conversion handles
 		-> NineFile m
 simpleFileBy name (rd, wr) (rdc, wrc) = (boringFile name :: NineFile m) {
 		read = simpleRead $ liftM rdc $ rd,
 		write = simpleWrite $ wr . wrc 
 	}
+
+-- |A file that stores its contents in the form of 'IORef' 'ByteString'
+memoryFile :: forall m. (Monad m, EmbedIO m)
+	=> String	-- ^File name
+	-> IO (NineFile m)
+memoryFile name = do
+	c <- newIORef "" :: IO (IORef ByteString)
+	return $ simpleFileBy name (
+			liftIO $ readIORef c,
+			liftIO . writeIORef c
+		) (id, id)
+
+-- |A directory that stores its contents in the form of 'IORef [(String, NineFile m)]'
+simpleDirectory :: forall m. (Monad m, EmbedIO m)
+			=> String	-- ^File name
+			-> (String -> ErrorT NineError m (NineFile m))	-- ^A function for creating new files
+			-> (String -> ErrorT NineError m (NineFile m))	-- ^A function for creating new directories
+			-> IO (NineFile m)
+simpleDirectory name newfile newdir = do
+	files <- newIORef [] :: IO (IORef [(String, NineFile m)])
+	return (boringDir name [] :: NineFile m) {
+		create = \name perms -> do
+			nf <- (if isDir perms then newdir else newfile) name
+			let nelem = (name, nf)
+			liftIO $ atomicModifyIORef' files (\l -> (nelem:l, ()))
+			return nf,
+		getFiles = liftIO $ liftM (map snd) $ readIORef files,
+		descend = \name -> do
+			d <- liftIO $ readIORef files
+			maybe (throwError $ ENoFile name) (return) $ lookup name d
+	}
+
+-- |A composition of a 'simpleDirectory' and a 'memoryFile'
+memoryDirectory :: forall m. (Monad m, EmbedIO m)
+			=> String	-- ^File name
+			-> IO (NineFile m)
+memoryDirectory name = simpleDirectory name (liftIO . memoryFile) (liftIO . memoryDirectory)
diff --git a/Network/NineP/Internal/File.hs b/Network/NineP/Internal/File.hs
--- a/Network/NineP/Internal/File.hs
+++ b/Network/NineP/Internal/File.hs
@@ -32,6 +32,8 @@
 		parent :: m (Maybe (NineFile m)),
 		-- |A callback to address a specific file by its name. @.@ and @..@ are handled in the library.
 		descend :: String -> ErrorT NineError m (NineFile m),
+		-- |Create a file under this directory.
+		create :: String -> Word32 -> ErrorT NineError m (NineFile m),
 		remove :: m (),
 		-- |The directory stat must return only stat for @.@.
 		stat :: m Stat,
@@ -52,16 +54,17 @@
         (const $ return ())
 	(return 0)
 
--- |A dumb directory that can't do anything but provide the files it contains.
+-- |A dumb directory that can't do anything but provide the files it contains. An user can create files, but they won't show up in listing and will essentially be 'boringFile's.
 boringDir :: (Monad m, EmbedIO m) => String -> [(String, NineFile m)] -> NineFile m
 boringDir name contents = let m = M.fromList contents in Directory {
 	getFiles = (return $ map snd $ contents),
 	descend = (\x -> case M.lookup x m of
 		Nothing -> throwError $ ENoFile x
 		Just f -> return f),
-        remove = (return ()),
-        stat = (return $ boringStat {st_name = name}),
-        wstat = (const $ return ()),
+	create = (\name perms -> return $ boringFile name),
+	remove = (return ()),
+	stat = (return $ boringStat {st_name = name}),
+	wstat = (const $ return ()),
 	version = (return 0),
 	parent = return Nothing }
 
diff --git a/Network/NineP/Internal/Msg.hs b/Network/NineP/Internal/Msg.hs
--- a/Network/NineP/Internal/Msg.hs
+++ b/Network/NineP/Internal/Msg.hs
@@ -10,10 +10,10 @@
 	, rclunk
 	, rauth
 	, ropen
+	, rcreate
 	, rread
 	, rwrite
 	, rremove
-	, rcreate
 	, rflush
 	) where
 
@@ -134,10 +134,22 @@
 ropen (Msg _ t (Topen fid mode)) = do
 	f <- lookup fid
 	checkPerms f mode
-	q <- open f
+	qid <- open f
 	iou <- iounit
-	return $ return $ Msg TRopen t $ Ropen q iou
+	return $ return $ Msg TRopen t $ Ropen qid iou
 
+rcreate (Msg _ t (Tcreate fid name perm mode)) = do
+	f <- lookup fid
+	-- TODO check permissions to create
+	case f of
+		RegularFile {} -> throwError ENotADir
+		Directory {} -> do
+			nf <- mapErrorT call $ (create f) name perm
+			insert fid nf
+			qid <- open f
+			iou <- iounit
+			return $ return $ Msg TRcreate t $ Rcreate qid iou
+
 rread :: (Monad m, EmbedIO m) => Msg -> Nine m [Msg]
 rread (Msg _ t (Tread fid offset count)) = do
 	f <- lookup fid
@@ -169,23 +181,12 @@
 rwstat (Msg _ t (Twstat fid stat)) = do
 	-- TODO check perms
 	f <- lookup fid
-	throwError $ ENotImplemented "wstat"
+	-- TODO implement
+	return $ return $ Msg TRwstat t $ Rwstat
 
 rremove (Msg _ t (Tremove fid)) = do
 	-- TODO check perms
 	f <- lookup fid
 	throwError $ ENotImplemented "remove"
-
-rcreate (Msg _ t (Tcreate fid name perm mode)) = do
-	-- TODO check perms
-	--dir <- lookup fid
-	if testBit mode 31
-		then do 
-			throwError $ ENotImplemented "create directory"
-		else do
-			throwError $ ENotImplemented "create file"
-	--q <- open f
-	-- TODO iounit
-	--return $ return $ Msg TRcreate t $ Rcreate
 
 rflush _ = return []
diff --git a/Test.hs b/Test.hs
--- a/Test.hs
+++ b/Test.hs
@@ -17,9 +17,10 @@
 
 boringwr _ c = return $ B.take (fromIntegral c) "i am so very boring"
 
-cfg = Config {
-	root = boringDir "/" [("lol", (boringFile "lol") { write = spamwr })],
-	addr = "unix!/tmp/h9pt"
-}
-
-main = run9PServer cfg
+main = do
+	memd <- memoryDirectory "/" :: IO (NineFile IO)
+	run9PServer $ Config {
+			root = boringDir "/" [("lol", (boringFile "lol") { write = spamwr }), ("memd", memd)],
+			addr = "unix!/tmp/h9pt",
+			monadState = undefined
+		}
