diff --git a/pwiki2.js b/pwiki2.js
index 97a9327..65b6d58 100755
--- a/pwiki2.js
+++ b/pwiki2.js
@@ -712,6 +712,149 @@ module.localStorageNestedStore = {
 var fs = require('fs')
 var glob = require('glob')
 
+//	exists(base[, options])
+//		-> true/false
+//
+//	exists(base, path[, options])
+//		-> true/false
+//
+var exists =
+module.exists =
+async function(base, sub, options={index: '.index'}){
+	if(typeof(sub) != 'string'){
+		options = sub
+		sub = base
+		base = '' }
+	var {index} = options
+
+	var target = module.path.join(base, sub)
+	if(!fs.existsSync(target)){
+		return false }
+	var stat = await fs.promises.stat(target)
+	if(stat.isDirectory()){
+		return fs.existsSync(module.path.join(target, index)) }
+	return true }
+var read =
+module.read =
+async function(base, sub, options={index: '.index'}){
+	if(typeof(sub) != 'string'){
+		options = sub
+		sub = base
+		base = '' }
+	var {index} = options
+
+	var target = module.path.join(base, sub)
+	if(!fs.existsSync(target)){
+		return undefined }
+	// handle dir text...
+	var stat = await fs.promises.stat(target)
+	if(stat.isDirectory()){
+		var target = module.path.join(target, index) 
+		fs.existsSync(target)
+			|| (target = false) }
+	return target ?
+		fs.promises.readFile(target, {encoding: 'utf-8'})
+		: undefined }
+var mkdir = 
+module.mkdir =
+async function(base, sub, options={index: '.index'}){
+	if(typeof(sub) != 'string'){
+		options = sub
+		sub = base
+		base = '' }
+	var {index} = options
+
+	var levels = module.path.split(sub)
+	for(var level of levels){
+		base = module.path.join(base, level)
+		// nothing exists -- create dir and continue...
+		if(!fs.existsSync(base)){
+			await fs.promises.mkdir(base, {recursive: true}) 
+			continue }
+		// directory -- continue...
+		var stat = await fs.promises.stat(base)
+		if(stat.isDirectory()){
+			continue }
+		// file -- convert to dir...
+		await fs.promises.rename(base, base+'.pwiki-bak') 
+		await fs.promises.mkdir(base, {recursive: true}) 
+		await fs.promises.rename(base +'.pwiki-bak', base +'/'+ index) }
+	return base }
+// XXX error checking???
+// XXX metadata???
+// XXX modes???
+// XXX should this transform 
/.index into a file if nothing else exists in it???
+var update = 
+module.update =
+async function(base, sub, data, options={index: '.index'}){
+	if(typeof(sub) != 'string'){
+		options = sub
+		sub = base
+		base = '' }
+	var {index} = options
+
+	// path already exists...
+	if(fs.existsSync(module.path.join(base, sub))){
+		var stat = await fs.promises.stat(base)
+		if(stat.isDirectory()){
+			sub = module.path.join(sub, index) } 
+	// create path / parts of path...
+	} else {
+		var levels = module.path.split(sub)
+		var basename = levels.pop()
+		// ensure the parent dir exists...
+		await module.mkdir(base, levels, index) }
+	// write the data...
+	var target = module.path.join(base, sub)
+	var f = await fs.promises.open(target, 'w')
+	await f.writeFile(data)
+	f.close()
+	return target }
+var clear = 
+module.clear =
+async function(base, sub, options={index: '.index'}){
+	if(typeof(sub) != 'string'){
+		options = sub
+		sub = base
+		base = '' }
+	var {index} = options
+
+	// remove leaf...
+	var target = module.path.join(base, sub)
+	// dir...
+	if(fs.existsSync(target)){
+		var stat = await fs.promises.stat(target)
+		if(stat.isDirectory()){
+			var files = await fs.promises.readdir(target)
+			// remove index...
+			if(files.includes(index)){
+				await fs.promises.rm(module.path.join(target, index))
+				// NOTE: we do not care what we pop as long as the .length 
+				// 		is correct as we'll not be using the content after 
+				// 		this point...
+				files.pop() }
+			// remove dir if empty...
+			if(files.length == 0){
+				fs.promises.rmdir(target) }
+		// simple file...
+		} else {
+			await fs.promises.rm(target) } }
+	// cleanup path -- remove empty dirs... (XXX ???)
+	var levels = module.path.split(sub)
+		.slice(0, -1)
+	while(levels.length > 0){
+		var cur = module.path.join(base, ...levels)
+		if(fs.existsSync(cur)){
+			var stat = await fs.promises.stat(base)
+			if(stat.isDirectory()){
+				// stop cleanup if non-empty dir...
+				if((await fs.promises.readdir(cur)).length != 0){
+					break }
+				fs.promises.rmdir(cur) } }
+		levels.pop() } 
+	return target }
+
+
 // XXX add monitor API...
 // XXX backup files on write/delete...
 // XXX do a r/o version...
@@ -726,6 +869,7 @@ module.FileStore = {
 	__directory_text__: '.index',
 
 	// XXX do we remove the extension???
+	// XXX BUG? is this recursive???
 	// XXX cache???
 	__paths__: async function(){
 		var that = this
@@ -738,85 +882,27 @@ module.FileStore = {
 							return path
 								.slice(that.__path__.length) })) }) }) },
 	__exists__: async function(path){
-		var p = module.path.join(this.__path__, path)
-		try {
-			var stat = await fs.promises.stat(p)
-			// NOTE: we consider a directory as "existing" iff we can 
-			// 		produce text for it...
-			return stat.isDirectory() ?
-					(!!fs.existsSync(p +'/'+ this.__directory_text__) 
-						&& path)
-				: !!fs.existsSync(p) ?
-					path
-				: false
-		} catch(err){
-			return false } },
+		return module.exists(this.__path__, path, {index: this.__directory_text__}) 
+			&& path },
 	__get__: async function(path){
 		var p = module.path.join(this.__path__, path)
-		var stat = await fs.promises.stat(p)
-		var {atimeMs, mtimeMs, ctimeMs, birthtimeMs} = stat
+		var {atimeMs, mtimeMs, ctimeMs, birthtimeMs} = await fs.promises.stat(p)
 		return {
 			atime: atimeMs,
 			mtime: mtimeMs,
 			ctime: ctimeMs,
-			text: stat.isDirectory() ? 
-				fs.readFileSync(p +'/'+ this.__directory_text__).toString()
-				: fs.readFileSync(p).toString(),
+			text: module.read(p, {index: this.__directory_text__})
 		} },
-	//
-	// 	Add data to path...
-	// 	.__update_path__(, )
-	//
-	// 	Clear data from path...
-	// 	.__update_path__(, undefined)
-	//
-	// XXX this is FileStore-specific...
-	// XXX create a file path and set data...
-	// 		- convert files in path to dirs:
-	// 			a/b/c -> a/b/c/.text 
-	// 				- move "c" -> "c.bak"
-	// 				- mkdir "c" 
-	// 				- move "c.bak" -> "c/.text"
-	// 		- convert last dir to file if directory empty...
-	// 			a/b/c/.text -> a/b/c
-	// 				(same as above)
-	// 		- remove empty dirs from path...
-	// XXX might be a good idea to have a store-specific backup/tmp dir...
-	// XXX might be a good idea to move this functionality to 
-	// 		.__update__(..) and use it from .__delete__(..)...
-	__update_path__: async function(path, data, mode='update'){
-		var p = module.path.join(this.__path__, path)
-
-		// write/update...
-		if(data != null){
-			// XXX create/update basedir...
-
-		// clear/remove...
-		} else {
-			// XXX remove file...
-			// XXX recursively check/remove dirs...
-		}
-	},
-	// XXX handle writing to directories...
-	// 		i.e. write to "./"+ this.__directory_text__
-	// XXX would need to convert a file to a dir if writing to sub-path...
-	// XXX recursively create all dirs...
 	// XXX do we write all the data or only the .text???
 	__update__: async function(path, data, mode='update'){
-		var p = module.path.join(this.__path__, path)
-
-		var f = await fs.promises.open(p, 'w')
-		var size = await f.writeFile(data.text)
-		f.close()
-		// XXX check size...
-		// XXX
-	},
-	// XXX remove empty dirs (???)
-	// XXX convert a dir to a file if removing last sub-file/dir...
+		return module.update(
+			this.__path__, path, 
+			data.text, 
+			{index: this.__directory_text__}) },
     __delete__: async function(path){
-		var p = module.path.join(this.__path__, path)
-		// XXX
-	},
+		return module.clear(
+			this.__path__, path, 
+			{index: this.__directory_text__}) },
 	load: function(data){
 		// XXX
 	},