text: var path = require('path');
  var once = require('once');
  var concat = require('concat-stream');
  var octal = require('octal')
  var stat = require('./stat');
  var xtend = require('xtend');
  var errno = require('./errno');
  
  var ROOT = stat({
  	type: 'directory',
  	mode: octal(777),
  	size: 4096
  });
  
  var normalize = function(key) {
  	key = key[0] === '/' ? key : '/' + key;
  	key = path.normalize(key);
  	if (key === '/') return key;
  	return key[key.length-1] === '/' ? key.slice(0, -1) : key;
  };
  
  var prefix = function(key) {
  	var depth = key.split('/').length.toString(36);
  	return '0000000000'.slice(depth.length)+depth+key;
  };
  
  module.exports = function(db) {
  	var that = {};
  
  	that.normalize = normalize;
  
  	that.get = function(key, cb) {
  		key = normalize(key);
  		if (key === '/') return process.nextTick(cb.bind(null, null, ROOT, '/'));
  		db.get(prefix(key), {valueEncoding:'json'}, function(err, doc) {
  			if (err && err.notFound) return cb(errno.ENOENT(key), null, key);
  			if (err) return cb(err, null, key);
  			cb(null, stat(doc), key);
  		});
  	};
  
  	that.writable = function(key, cb) {
  		key = normalize(key);
  		if (key === '/') return process.nextTick(cb.bind(null, errno.EPERM(key)));
  		that.follow(path.dirname(key), function(err, parent) {
  			if (err) return cb(err);
  			if (!parent.isDirectory()) return cb(errno.ENOTDIR(key));
  			cb(null, key);
  		});
  	};
  
  	that.list = function(key, cb) {
  		key = normalize(key);
  
  		var start = prefix(key === '/' ? key : key + '/');
  		var keys = db.createKeyStream({start: start, end: start+'\xff'});
  
  		cb = once(cb);
  
  		keys.on('error', cb);
  		keys.pipe(concat({encoding:'object'}, function(files) {
  			files = files.map(function(file) {
  				return file.split('/').pop();
  			});
  
  			cb(null, files);
  		}));
  	};
  
  	var resolve = function(dir, cb) {
  		var root = '/';
  		var parts = dir.split('/').slice(1);
  
  		var loop = function() {
  			that.get(path.join(root, parts.shift()), function(err, doc, key) {
  				if (err) return cb(err, doc, dir);
  				root = doc.target || key;
  				if (!parts.length) return cb(null, doc, key);
  				loop();
  			});
  		};
  
  		loop();
  	};
  
  	that.follow = function(key, cb) {
  		resolve(normalize(key), function loop(err, doc, key) {
  			if (err) return cb(err, null, key);
  			if (doc.target) return that.get(doc.target, loop);
  			cb(null, stat(doc), key);
  		});
  	};
  
  	that.update = function(key, opts, cb) {
  		that.get(key, function(err, doc, key) {
  			if (err) return cb(err);
  			if (key === '/') return cb(errno.EPERM(key));
  			that.put(key, xtend(doc, opts), cb);
  		});
  	};
  
  	that.put = function(key, opts, cb) {
  		that.writable(key, function(err, key) {
  			if (err) return cb(err);
  			db.put(prefix(key), stat(opts), {valueEncoding:'json'}, cb);
  		});
  	};
  
  	that.del = function(key, cb) {
  		key = normalize(key);
  		if (key === '/') return process.nextTick(cb.bind(null, errno.EPERM(key)));
  		db.del(prefix(key), cb);
  	};
  
  	return that;
  };
  