shithub: riscv

Download patch

ref: 64d18a993fec0fc3d69e58514341d6afcea6c092
parent: f39e53151bfcbecdced66cf9fcec75a27507ee54
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Wed Feb 14 14:22:49 EST 2024

ext4srv: clean up and fix a couple bugs

* replace nonsensical string matching with explicit mount point arg
* fix ext4_fseek _subtracting_ the offset if seeking from the EOF
* fix ext4_fwrite - always set the number of bytes written
* remove symlink resolving - it's Plan 9. give up.
* reduce the number of allocations

--- a/sys/man/4/ext4srv
+++ b/sys/man/4/ext4srv
@@ -4,7 +4,7 @@
 .SH SYNOPSIS
 .B ext4srv
 [
-.B -Clrs
+.B -Crs
 ] [
 .B -g
 .I groupfile
@@ -95,11 +95,6 @@
 in this case is
 .IR 0 .
 .PP
-Optional flag
-.B -l
-enables symlink resolving, otherwise symlinks are hidden by
-default entirely, as Plan 9 does not have that concept.
-.PP
 Some file system state is cached in memory, and may
 be flushed only when the file system is unmounted if
 .B -C
@@ -133,6 +128,7 @@
 Yes.
 .PP
 Permission checking is very basic and may not be complete.
+Symlinks are not resolved.
 There may be many bugs.
 It is advisable to use
 .I ext4srv
--- a/sys/src/cmd/ext4srv/common.h
+++ b/sys/src/cmd/ext4srv/common.h
@@ -1,13 +1,9 @@
 typedef struct Opts Opts;
 typedef struct Part Part;
 
-#pragma varargck type "Ð" Part*
-#pragma varargck type "M" Part*
-
 struct Opts {
 	char *group;
 	int cachewb;
-	int linkmode;
 	int asroot;
 	int rdonly;
 
@@ -23,10 +19,9 @@
 	QLock;
 	Part *prev, *next;
 
-	char dev[32];
-	char mnt[32];
 	char *partdev;
 
+	struct ext4_mountpoint mp;
 	struct ext4_blockdev bdev;
 	struct ext4_blockdev_iface bdif;
 	struct ext4_sblock *sb;
@@ -36,11 +31,6 @@
 	Groups groups;
 	int f;
 	uchar blkbuf[];
-};
-
-enum {
-	Lhide,
-	Lresolve = 1,
 };
 
 Part *openpart(char *dev, Opts *opts);
--- a/sys/src/cmd/ext4srv/ext4.c
+++ b/sys/src/cmd/ext4srv/ext4.c
@@ -18,6 +18,14 @@
 char Eperm[] = "permission denied";
 char Erdonlyfs[] = "read-only fs";
 
+static char *
+basename(char *s)
+{
+	char *e;
+	e = utfrrune(s, '/');
+	return e == nil ? s : e+1;
+}
+
 /**@brief   Mount point OS dependent lock*/
 #define EXT4_MP_LOCK(_m)                                               \
 	do {                                                               \
@@ -32,98 +40,6 @@
 			(_m)->os_locks->unlock((_m)->os_locks->p_user);            \
 	} while (0)
 
-/**@brief   Mount point descriptor.*/
-struct ext4_mountpoint {
-
-	/**@brief   Mount done flag.*/
-	bool mounted;
-
-	/**@brief   Mount point name (@ref ext4_mount)*/
-	char name[CONFIG_EXT4_MAX_MP_NAME + 1];
-
-	/**@brief   OS dependent lock/unlock functions.*/
-	const struct ext4_lock *os_locks;
-
-	/**@brief   Ext4 filesystem internals.*/
-	struct ext4_fs fs;
-
-	/**@brief   JBD fs.*/
-	struct jbd_fs jbd_fs;
-
-	/**@brief   Journal.*/
-	struct jbd_journal jbd_journal;
-
-	/**@brief   Block cache.*/
-	struct ext4_bcache bc;
-};
-
-/**@brief   Block devices descriptor.*/
-struct ext4_block_devices {
-
-	/**@brief   Block device name.*/
-	char name[CONFIG_EXT4_MAX_BLOCKDEV_NAME + 1];
-
-	/**@brief   Block device handle.*/
-	struct ext4_blockdev *bd;
-};
-
-/**@brief   Block devices.*/
-static struct ext4_block_devices s_bdevices[CONFIG_EXT4_BLOCKDEVS_COUNT];
-
-/**@brief   Mountpoints.*/
-static struct ext4_mountpoint s_mp[CONFIG_EXT4_MOUNTPOINTS_COUNT];
-
-int ext4_device_register(struct ext4_blockdev *bd,
-			 const char *dev_name)
-{
-	assert(bd && dev_name);
-
-	if (strlen(dev_name) > CONFIG_EXT4_MAX_BLOCKDEV_NAME) {
-		werrstr("dev name too long: %s", dev_name);
-		return -1;
-	}
-
-	for (usize i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
-		if (!strcmp(s_bdevices[i].name, dev_name)) {
-			werrstr("dev already exists: %s", dev_name);
-			return -1;
-		}
-	}
-
-	for (usize i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
-		if (!s_bdevices[i].bd) {
-			strcpy(s_bdevices[i].name, dev_name);
-			s_bdevices[i].bd = bd;
-			return 0;
-		}
-	}
-
-	werrstr("dev limit reached");
-	return -1;
-}
-
-int ext4_device_unregister(const char *dev_name)
-{
-	assert(dev_name);
-
-	for (usize i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
-		if (strcmp(s_bdevices[i].name, dev_name) == 0) {
-            memset(&s_bdevices[i], 0, sizeof(s_bdevices[i]));
-            return 0;
-        }
-	}
-
-	werrstr("dev not found: %s", dev_name);
-	return -1;
-}
-
-int ext4_device_unregister_all(void)
-{
-	memset(s_bdevices, 0, sizeof(s_bdevices));
-
-	return 0;
-}
-
 static bool ext4_is_dots(const u8int *name, usize name_size)
 {
 	if ((name_size == 1) && (name[0] == '.'))
@@ -270,8 +186,7 @@
 
 static int ext4_unlink(struct ext4_mountpoint *mp,
 		       struct ext4_inode_ref *parent,
-		       struct ext4_inode_ref *child, const char *name,
-		       u32int name_len)
+		       struct ext4_inode_ref *child, const char *name)
 {
 	bool has_children;
 	int rc = ext4_has_children(&has_children, child);
@@ -285,7 +200,8 @@
 	}
 
 	/* Remove entry from parent directory */
-	rc = ext4_dir_remove_entry(parent, name, name_len);
+	name = basename(name);
+	rc = ext4_dir_remove_entry(parent, name, strlen(name));
 	if (rc != 0)
 		return rc;
 
@@ -321,58 +237,14 @@
 	return 0;
 }
 
-int ext4_mount(const char *dev_name, const char *mount_point,
-	       bool read_only)
+int ext4_mount(struct ext4_mountpoint *mp, struct ext4_blockdev *bd, bool read_only)
 {
 	int r;
 	u32int bsize;
 	struct ext4_bcache *bc;
-	struct ext4_blockdev *bd = 0;
-	struct ext4_mountpoint *mp = 0;
 
-	assert(mount_point && dev_name);
+	memset(mp, 0, sizeof(*mp));
 
-	usize mp_len = strlen(mount_point);
-
-	if (mp_len > CONFIG_EXT4_MAX_MP_NAME) {
-		werrstr("mount point name too long: %s", mount_point);
-		return -1;
-	}
-
-	if (mount_point[mp_len - 1] != '/') {
-		werrstr("invalid mount point: %s", mount_point);
-		return -1;
-	}
-
-	for (usize i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
-		if (!strcmp(dev_name, s_bdevices[i].name)) {
-			bd = s_bdevices[i].bd;
-			break;
-		}
-	}
-
-	if (!bd) {
-		werrstr("dev not found: %s", dev_name);
-		return -1;
-	}
-
-	for (usize i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
-		if (!s_mp[i].mounted) {
-			strcpy(s_mp[i].name, mount_point);
-			s_mp[i].mounted = 1;
-			mp = &s_mp[i];
-			break;
-		}
-
-		if (!strcmp(s_mp[i].name, mount_point))
-			return 0;
-	}
-
-	if (!mp) {
-		werrstr("memory");
-		return -1;
-	}
-
 	r = ext4_block_init(bd);
 	if (r != 0)
 		return r;
@@ -379,6 +251,7 @@
 
 	r = ext4_fs_init(&mp->fs, bd, read_only);
 	if (r != 0) {
+err:
 		ext4_block_fini(bd);
 		return r;
 	}
@@ -388,14 +261,13 @@
 	bc = &mp->bc;
 
 	r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE, bsize);
-	if (r != 0) {
-		ext4_block_fini(bd);
-		return r;
-	}
+	if (r != 0)
+		goto err;
 
 	if (bsize != bc->itemsize) {
 		werrstr("unsupported block size: %d", bsize);
-		return -1;
+		ext4_bcache_fini_dynamic(bc);
+		goto err;
 	}
 
 	/*Bind block cache to block device*/
@@ -402,41 +274,24 @@
 	r = ext4_block_bind_bcache(bd, bc);
 	if (r != 0) {
 		ext4_bcache_cleanup(bc);
-		ext4_block_fini(bd);
 		ext4_bcache_fini_dynamic(bc);
-		return r;
+		goto err;
 	}
 
 	bd->fs = &mp->fs;
+	mp->mounted = true;
 	return r;
 }
 
-static struct ext4_mountpoint *ext4_get_mount(const char *path)
+int ext4_umount(struct ext4_mountpoint *mp)
 {
-	for (usize i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
-		if (!s_mp[i].mounted)
-			continue;
-		if (!strncmp(s_mp[i].name, path, strlen(s_mp[i].name)))
-			return &s_mp[i];
-	}
-
-	werrstr("mount point not found: %s", path);
-	return nil;
-}
-
-int ext4_umount(const char *mount_point)
-{
 	int r;
-	struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
 
-	if (!mp)
-		return -1;
-
 	r = ext4_fs_fini(&mp->fs);
 	if (r != 0)
 		goto Finish;
 
-	mp->mounted = 0;
+	mp->mounted = false;
 
 	ext4_bcache_cleanup(mp->fs.bdev->bc);
 	ext4_bcache_fini_dynamic(mp->fs.bdev->bc);
@@ -447,13 +302,10 @@
 	return r;
 }
 
-int ext4_journal_start(const char *mount_point)
+int ext4_journal_start(struct ext4_mountpoint *mp)
 {
 	int r;
-	struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
 
-	if (!mp)
-		return -1;
 	if (mp->fs.read_only)
 		return 0;
 	if (!ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_HAS_JOURNAL))
@@ -476,13 +328,10 @@
 	return r;
 }
 
-int ext4_journal_stop(const char *mount_point)
+int ext4_journal_stop(struct ext4_mountpoint *mp)
 {
 	int r;
-	struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
 
-	if (!mp)
-		return -1;
 	if (mp->fs.read_only)
 		return 0;
 	if (!ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_HAS_JOURNAL))
@@ -510,14 +359,10 @@
 	return r;
 }
 
-int ext4_recover(const char *mount_point)
+int ext4_recover(struct ext4_mountpoint *mp)
 {
-	struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
 	int r;
 
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
 	if (!ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_HAS_JOURNAL))
 		return 0;
@@ -612,14 +457,9 @@
 	}
 }
 
-int ext4_mount_point_stats(const char *mount_point,
+int ext4_mount_point_stats(struct ext4_mountpoint *mp,
 			   struct ext4_mount_stats *stats)
 {
-	struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
-
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
 	stats->inodes_count = ext4_get32(&mp->fs.sb, inodes_count);
 	stats->free_inodes_count = ext4_get32(&mp->fs.sb, free_inodes_count);
@@ -637,23 +477,9 @@
 	return 0;
 }
 
-int ext4_mount_setup_locks(const char *mount_point,
+int ext4_mount_setup_locks(struct ext4_mountpoint *mp,
 			   const struct ext4_lock *locks)
 {
-	u32int i;
-	struct ext4_mountpoint *mp = nil;
-
-	for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
-		if (!strcmp(s_mp[i].name, mount_point)) {
-			mp = &s_mp[i];
-			break;
-		}
-	}
-	if (!mp) {
-		werrstr("mount point not found: %s", mount_point);
-		return -1;
-	}
-
 	mp->os_locks = locks;
 	return 0;
 }
@@ -664,8 +490,7 @@
 {
 	int i;
 
-	for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {
-
+	for(i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {
 		if (path[i] == '/') {
 			*is_goal = false;
 			return i;
@@ -828,9 +653,9 @@
  * NOTICE: if filetype is equal to EXT4_DIRENTRY_UNKNOWN,
  * any filetype of the target dir entry will be accepted.
  */
-static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
-			      int ftype, u32int *parent_inode,
-			      u32int *name_off)
+static int ext4_generic_open2(struct ext4_mountpoint *mp,
+			      ext4_file *f, const char *path, int flags,
+			      int ftype, u32int *parent_inode)
 {
 	bool is_goal = false;
 	u32int imode = EXT4_INODE_MODE_DIRECTORY;
@@ -838,15 +663,11 @@
 
 	int r;
 	int len;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	struct ext4_dir_search_result result;
 	struct ext4_inode_ref ref;
 
 	f->mp = 0;
 
-	if (!mp)
-		return -1;
-
 	struct ext4_fs *const fs = &mp->fs;
 	struct ext4_sblock *const sb = &mp->fs.sb;
 
@@ -857,12 +678,6 @@
 
 	f->flags = flags;
 
-	/*Skip mount point*/
-	path += strlen(mp->name);
-
-	if (name_off)
-		*name_off = strlen(mp->name);
-
 	/*Load root*/
 	r = ext4_fs_get_inode_ref(fs, EXT4_INODE_ROOT_INDEX, &ref);
 	if (r != 0)
@@ -967,9 +782,6 @@
 			break;
 
 		path += len + 1;
-
-		if (name_off)
-			*name_off += len + 1;
 	}
 
 	if (r != 0) {
@@ -1000,14 +812,13 @@
 
 /****************************************************************************/
 
-static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,
-			     bool file_expect, u32int *parent_inode,
-			     u32int *name_off)
+static int ext4_generic_open(struct ext4_mountpoint *mp,
+			     ext4_file *f, const char *path, const char *flags,
+			     bool file_expect, u32int *parent_inode)
 {
 	u32int iflags;
 	int filetype;
 	int r;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 
 	if (ext4_parse_flags(flags, &iflags) == false)
 		return -1;
@@ -1020,7 +831,7 @@
 	if (iflags & O_CREAT)
 		ext4_trans_start(mp);
 
-	r = ext4_generic_open2(f, path, iflags, filetype, parent_inode, name_off);
+	r = ext4_generic_open2(mp, f, path, iflags, filetype, parent_inode);
 
 	if (iflags & O_CREAT) {
 		if (r == 0)
@@ -1032,7 +843,8 @@
 	return r;
 }
 
-static int ext4_create_hardlink(const char *path,
+static int ext4_create_hardlink(struct ext4_mountpoint *mp,
+		const char *path,
 		struct ext4_inode_ref *child_ref, bool rename)
 {
 	bool is_goal = false;
@@ -1041,19 +853,12 @@
 
 	int r;
 	int len;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	struct ext4_dir_search_result result;
 	struct ext4_inode_ref ref;
 
-	if (!mp)
-		return -1;
-
 	struct ext4_fs *const fs = &mp->fs;
 	struct ext4_sblock *const sb = &mp->fs.sb;
 
-	/*Skip mount point*/
-	path += strlen(mp->name);
-
 	/*Load root*/
 	r = ext4_fs_get_inode_ref(fs, EXT4_INODE_ROOT_INDEX, &ref);
 	if (r != 0)
@@ -1136,25 +941,15 @@
 	return r;
 }
 
-static int ext4_remove_orig_reference(const char *path, u32int name_off,
+static int ext4_remove_orig_reference(struct ext4_mountpoint *mp,
+				      const char *path,
 				      struct ext4_inode_ref *parent_ref,
 				      struct ext4_inode_ref *child_ref)
 {
-	bool is_goal;
 	int r;
-	int len;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 
-	if (!mp)
-		return -1;
-
-	/*Set path*/
-	path += name_off;
-
-	len = ext4_path_check(path, &is_goal);
-
 	/* Remove entry from parent directory */
-	r = ext4_dir_remove_entry(parent_ref, path, len);
+	r = ext4_dir_remove_entry(parent_ref, path, strlen(path));
 	if (r != 0)
 		goto Finish;
 
@@ -1167,33 +962,21 @@
 	return r;
 }
 
-int ext4_flink(const char *path, const char *hardlink_path)
+int ext4_flink(struct ext4_mountpoint *mp, const char *path, const char *hardlink_path)
 {
 	int r;
 	ext4_file f;
-	u32int name_off;
 	bool child_loaded = false;
 	u32int parent_inode, child_inode;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
-	struct ext4_mountpoint *target_mp = ext4_get_mount(hardlink_path);
 	struct ext4_inode_ref child_ref;
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
 	}
 
-	/* Will that happen? */
-	if (mp != target_mp) {
-		werrstr("mount point must be the same: %s vs %s", path, hardlink_path);
-		return -1;
-	}
-
 	EXT4_MP_LOCK(mp);
-	r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, &parent_inode, &name_off);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, EXT4_DE_UNKNOWN, &parent_inode);
 	if (r != 0) {
 		EXT4_MP_UNLOCK(mp);
 		return r;
@@ -1217,7 +1000,7 @@
 		goto Finish;
 	}
 
-	r = ext4_create_hardlink(hardlink_path, &child_ref, false);
+	r = ext4_create_hardlink(mp, hardlink_path, &child_ref, false);
 
 Finish:
 	if (child_loaded)
@@ -1233,19 +1016,14 @@
 
 }
 
-int ext4_frename(const char *path, const char *new_path)
+int ext4_frename(struct ext4_mountpoint *mp, const char *path, const char *new_path)
 {
 	int r;
 	ext4_file f;
-	u32int name_off;
 	bool parent_loaded = false, child_loaded = false;
 	u32int parent_inode, child_inode;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	struct ext4_inode_ref child_ref, parent_ref;
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -1253,8 +1031,7 @@
 
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN,
-				&parent_inode, &name_off);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, EXT4_DE_UNKNOWN, &parent_inode);
 	if (r != 0) {
 		EXT4_MP_UNLOCK(mp);
 		return r;
@@ -1278,11 +1055,11 @@
 
 	child_loaded = true;
 
-	r = ext4_create_hardlink(new_path, &child_ref, true);
+	r = ext4_create_hardlink(mp, new_path, &child_ref, true);
 	if (r != 0)
 		goto Finish;
 
-	r = ext4_remove_orig_reference(path, name_off, &parent_ref, &child_ref);
+	r = ext4_remove_orig_reference(mp, basename(path), &parent_ref, &child_ref);
 	if (r != 0)
 		goto Finish;
 
@@ -1305,25 +1082,16 @@
 
 /****************************************************************************/
 
-int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb)
+int ext4_get_sblock(struct ext4_mountpoint *mp, struct ext4_sblock **sb)
 {
-	struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
-
-	if (!mp)
-		return -1;
-
 	*sb = &mp->fs.sb;
 	return 0;
 }
 
-int ext4_cache_write_back(const char *path, bool on)
+int ext4_cache_write_back(struct ext4_mountpoint *mp, bool on)
 {
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	int ret;
 
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
 	ret = ext4_block_cache_write_back(mp->fs.bdev, on);
 	EXT4_MP_UNLOCK(mp);
@@ -1330,14 +1098,10 @@
 	return ret;
 }
 
-int ext4_cache_flush(const char *path)
+int ext4_cache_flush(struct ext4_mountpoint *mp)
 {
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	int ret;
 
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
 	ret = ext4_block_cache_flush(mp->fs.bdev);
 	EXT4_MP_UNLOCK(mp);
@@ -1344,22 +1108,15 @@
 	return ret;
 }
 
-int ext4_fremove(const char *path)
+int ext4_fremove(struct ext4_mountpoint *mp, const char *path)
 {
 	ext4_file f;
 	u32int parent_inode;
 	u32int child_inode;
-	u32int name_off;
-	bool is_goal;
 	int r;
-	int len;
 	struct ext4_inode_ref child;
 	struct ext4_inode_ref parent;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -1366,7 +1123,7 @@
 	}
 
 	EXT4_MP_LOCK(mp);
-	r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, &parent_inode, &name_off);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, EXT4_DE_UNKNOWN, &parent_inode);
 	if (r != 0) {
 		EXT4_MP_UNLOCK(mp);
 		return r;
@@ -1416,13 +1173,8 @@
 		ext4_block_cache_write_back(mp->fs.bdev, 0);
 	}
 
-	/*Set path*/
-	path += name_off;
-
-	len = ext4_path_check(path, &is_goal);
-
 	/*Unlink from parent*/
-	r = ext4_unlink(mp, &parent, &child, path, len);
+	r = ext4_unlink(mp, &parent, &child, path);
 	if (r != 0)
 		goto Finish;
 
@@ -1448,18 +1200,14 @@
 	return r;
 }
 
-int ext4_fopen(ext4_file *file, const char *path, const char *flags)
+int ext4_fopen(struct ext4_mountpoint *mp, ext4_file *file, const char *path, const char *flags)
 {
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	int r;
 
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
 
 	ext4_block_cache_write_back(mp->fs.bdev, 1);
-	r = ext4_generic_open(file, path, flags, true, 0, 0);
+	r = ext4_generic_open(mp, file, path, flags, true, nil);
 	ext4_block_cache_write_back(mp->fs.bdev, 0);
 
 	EXT4_MP_UNLOCK(mp);
@@ -1466,15 +1214,11 @@
 	return r;
 }
 
-int ext4_fopen2(ext4_file *file, const char *path, int flags)
+int ext4_fopen2(struct ext4_mountpoint *mp, ext4_file *file, const char *path, int flags)
 {
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	int r;
 	int filetype;
 
-	if (!mp)
-		return -1;
-
 	filetype = EXT4_DE_REG_FILE;
 
 	EXT4_MP_LOCK(mp);
@@ -1483,7 +1227,7 @@
 	if (flags & O_CREAT)
 		ext4_trans_start(mp);
 
-	r = ext4_generic_open2(file, path, flags, filetype, nil, nil);
+	r = ext4_generic_open2(mp, file, path, flags, filetype, nil);
 
 	if (flags & O_CREAT) {
 		if (r == 0)
@@ -1765,6 +1509,9 @@
 
 	assert(file && file->mp);
 
+	if(wcnt)
+		*wcnt = 0;
+
 	if (file->mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -1784,9 +1531,6 @@
 	struct ext4_fs *const fs = &file->mp->fs;
 	struct ext4_sblock *const sb = &file->mp->fs.sb;
 
-	if (wcnt)
-		*wcnt = 0;
-
 	r = ext4_fs_get_inode_ref(fs, file->inode, &ref);
 	if (r != 0) {
 		ext4_trans_abort(file->mp);
@@ -1798,12 +1542,11 @@
 	file->fsize = ext4_inode_get_size(sb, ref.inode);
 	block_size = ext4_sb_get_block_size(sb);
 
-	iblock_last = (u32int)((file->fpos + size) / block_size);
-	iblk_idx = (u32int)(file->fpos / block_size);
-	ifile_blocks = (u32int)((file->fsize + block_size - 1) / block_size);
+	iblock_last = (file->fpos + size) / block_size;
+	iblk_idx = file->fpos / block_size;
+	ifile_blocks = (file->fsize + block_size - 1) / block_size;
+	unalg = file->fpos % block_size;
 
-	unalg = (file->fpos) % block_size;
-
 	if (unalg) {
 		usize len =  size;
 		u64int off;
@@ -1941,33 +1684,27 @@
 	return r;
 }
 
-int ext4_fseek(ext4_file *file, s64int offset, u32int origin)
+int ext4_fseek(ext4_file *file, s64int offset, int origin)
 {
 	switch (origin) {
+	case 2:
+		offset += file->fsize;
+	if(0){
+	case 1:
+		offset += file->fpos;
+		/* slippery slope */
+	}
 	case 0:
-		if (offset < 0 || (u64int)offset > file->fsize)
-			break;
+		if(offset < 0)
+			offset = 0;
+		else if(offset > file->fsize)
+			offset = file->fsize;
 
 		file->fpos = offset;
-		return 0;
-	case 1:
-		if ((offset < 0 && (u64int)(-offset) > file->fpos) ||
-		    (offset > 0 &&
-		     (u64int)offset > (file->fsize - file->fpos)))
-			break;
-
-		file->fpos += offset;
-		return 0;
-	case 2:
-		if (offset < 0 || (u64int)offset > file->fsize)
-			break;
-
-		file->fpos = file->fsize - offset;
-		return 0;
+		break;
 	}
 
-	werrstr(Einval);
-	return -1;
+	return 0;
 }
 
 u64int ext4_ftell(ext4_file *file)
@@ -1981,14 +1718,14 @@
 }
 
 
-static int ext4_trans_get_inode_ref(const char *path,
-				    struct ext4_mountpoint *mp,
+static int ext4_trans_get_inode_ref(struct ext4_mountpoint *mp,
+				    const char *path,
 				    struct ext4_inode_ref *inode_ref)
 {
 	int r;
 	ext4_file f;
 
-	r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil, nil);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil);
 	if (r != 0)
 		return r;
 
@@ -2018,20 +1755,17 @@
 }
 
 
-int ext4_raw_inode_fill(const char *path, u32int *ret_ino,
+int ext4_raw_inode_fill(struct ext4_mountpoint *mp,
+			const char *path, u32int *ret_ino,
 			struct ext4_inode *inode)
 {
 	int r;
 	ext4_file f;
 	struct ext4_inode_ref inode_ref;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil, nil);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil);
 	if (r != 0) {
 		EXT4_MP_UNLOCK(mp);
 		return r;
@@ -2055,32 +1789,24 @@
 	return r;
 }
 
-int ext4_inode_exist(const char *path, int type)
+int ext4_inode_exist(struct ext4_mountpoint *mp, const char *path, int type)
 {
 	int r;
 	ext4_file f;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
-	r = ext4_generic_open2(&f, path, O_RDONLY, type, nil, nil);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, type, nil);
 	EXT4_MP_UNLOCK(mp);
 
 	return r;
 }
 
-int ext4_mode_set(const char *path, u32int mode)
+int ext4_mode_set(struct ext4_mountpoint *mp, const char *path, u32int mode)
 {
 	int r;
 	u32int orig_mode;
 	struct ext4_inode_ref inode_ref;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -2088,7 +1814,7 @@
 
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_trans_get_inode_ref(path, mp, &inode_ref);
+	r = ext4_trans_get_inode_ref(mp, path, &inode_ref);
 	if (r != 0)
 		goto Finish;
 
@@ -2106,15 +1832,11 @@
 	return r;
 }
 
-int ext4_owner_set(const char *path, u32int uid, u32int gid)
+int ext4_owner_set(struct ext4_mountpoint *mp, const char *path, u32int uid, u32int gid)
 {
 	int r;
 	struct ext4_inode_ref inode_ref;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -2122,7 +1844,7 @@
 
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_trans_get_inode_ref(path, mp, &inode_ref);
+	r = ext4_trans_get_inode_ref(mp, path, &inode_ref);
 	if (r != 0)
 		goto Finish;
 
@@ -2138,19 +1860,15 @@
 	return r;
 }
 
-int ext4_mode_get(const char *path, u32int *mode)
+int ext4_mode_get(struct ext4_mountpoint *mp, const char *path, u32int *mode)
 {
 	struct ext4_inode_ref inode_ref;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	ext4_file f;
 	int r;
 
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil, nil);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil);
 	if (r != 0)
 		goto Finish;
 
@@ -2167,19 +1885,14 @@
 	return r;
 }
 
-int ext4_owner_get(const char *path, u32int *uid, u32int *gid)
+int ext4_owner_get(struct ext4_mountpoint *mp, const char *path, u32int *uid, u32int *gid)
 {
 	struct ext4_inode_ref inode_ref;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	ext4_file f;
 	int r;
-
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil, nil);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil);
 	if (r != 0)
 		goto Finish;
 
@@ -2197,15 +1910,11 @@
 	return r;
 }
 
-int ext4_atime_set(const char *path, u32int atime)
+int ext4_atime_set(struct ext4_mountpoint *mp, const char *path, u32int atime)
 {
 	struct ext4_inode_ref inode_ref;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	int r;
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -2213,7 +1922,7 @@
 
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_trans_get_inode_ref(path, mp, &inode_ref);
+	r = ext4_trans_get_inode_ref(mp, path, &inode_ref);
 	if (r != 0)
 		goto Finish;
 
@@ -2227,15 +1936,11 @@
 	return r;
 }
 
-int ext4_mtime_set(const char *path, u32int mtime)
+int ext4_mtime_set(struct ext4_mountpoint *mp, const char *path, u32int mtime)
 {
 	struct ext4_inode_ref inode_ref;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	int r;
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -2243,7 +1948,7 @@
 
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_trans_get_inode_ref(path, mp, &inode_ref);
+	r = ext4_trans_get_inode_ref(mp, path, &inode_ref);
 	if (r != 0)
 		goto Finish;
 
@@ -2257,15 +1962,11 @@
 	return r;
 }
 
-int ext4_ctime_set(const char *path, u32int ctime)
+int ext4_ctime_set(struct ext4_mountpoint *mp, const char *path, u32int ctime)
 {
 	struct ext4_inode_ref inode_ref;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	int r;
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -2273,7 +1974,7 @@
 
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_trans_get_inode_ref(path, mp, &inode_ref);
+	r = ext4_trans_get_inode_ref(mp, path, &inode_ref);
 	if (r != 0)
 		goto Finish;
 
@@ -2287,19 +1988,15 @@
 	return r;
 }
 
-int ext4_atime_get(const char *path, u32int *atime)
+int ext4_atime_get(struct ext4_mountpoint *mp, const char *path, u32int *atime)
 {
 	struct ext4_inode_ref inode_ref;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	ext4_file f;
 	int r;
 
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil, nil);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil);
 	if (r != 0)
 		goto Finish;
 
@@ -2316,19 +2013,15 @@
 	return r;
 }
 
-int ext4_mtime_get(const char *path, u32int *mtime)
+int ext4_mtime_get(struct ext4_mountpoint *mp, const char *path, u32int *mtime)
 {
 	struct ext4_inode_ref inode_ref;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	ext4_file f;
 	int r;
 
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil, nil);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil);
 	if (r != 0)
 		goto Finish;
 
@@ -2345,19 +2038,15 @@
 	return r;
 }
 
-int ext4_ctime_get(const char *path, u32int *ctime)
+int ext4_ctime_get(struct ext4_mountpoint *mp, const char *path, u32int *ctime)
 {
 	struct ext4_inode_ref inode_ref;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	ext4_file f;
 	int r;
 
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
 
-	r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil, nil);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, EXT4_DE_UNKNOWN, nil);
 	if (r != 0)
 		goto Finish;
 
@@ -2443,16 +2132,12 @@
 	return r;
 }
 
-int ext4_fsymlink(const char *target, const char *path)
+int ext4_fsymlink(struct ext4_mountpoint *mp, const char *target, const char *path)
 {
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	int r;
 	ext4_file f;
 	int filetype;
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -2464,7 +2149,7 @@
 	ext4_block_cache_write_back(mp->fs.bdev, 1);
 	ext4_trans_start(mp);
 
-	r = ext4_generic_open2(&f, path, O_RDWR | O_CREAT, filetype, nil, nil);
+	r = ext4_generic_open2(mp, &f, path, O_RDWR | O_CREAT, filetype, nil);
 	if (r == 0)
 		r = ext4_fsymlink_set(&f, target, strlen(target));
 	else
@@ -2483,9 +2168,8 @@
 	return r;
 }
 
-int ext4_readlink(const char *path, char *buf, usize bufsize, usize *rcnt)
+int ext4_readlink(struct ext4_mountpoint *mp, const char *path, char *buf, usize bufsize, usize *rcnt)
 {
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	int r;
 	ext4_file f;
 	int filetype;
@@ -2492,14 +2176,11 @@
 
 	assert(buf != nil);
 
-	if (!mp)
-		return -1;
-
 	filetype = EXT4_DE_SYMLINK;
 
 	EXT4_MP_LOCK(mp);
 	ext4_block_cache_write_back(mp->fs.bdev, 1);
-	r = ext4_generic_open2(&f, path, O_RDONLY, filetype, nil, nil);
+	r = ext4_generic_open2(mp, &f, path, O_RDONLY, filetype, nil);
 	if (r == 0)
 		r = ext4_fread(&f, buf, bufsize, rcnt);
 	else
@@ -2536,15 +2217,11 @@
 	return r;
 }
 
-int ext4_mknod(const char *path, int filetype, u32int dev)
+int ext4_mknod(struct ext4_mountpoint *mp, const char *path, int filetype, u32int dev)
 {
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	int r;
 	ext4_file f;
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -2576,7 +2253,7 @@
 	ext4_block_cache_write_back(mp->fs.bdev, 1);
 	ext4_trans_start(mp);
 
-	r = ext4_generic_open2(&f, path, O_RDWR | O_CREAT, filetype, nil, nil);
+	r = ext4_generic_open2(mp, &f, path, O_RDWR | O_CREAT, filetype, nil);
 	if (r == 0) {
 		if (filetype == EXT4_DE_CHRDEV ||
 		    filetype == EXT4_DE_BLKDEV)
@@ -2600,29 +2277,22 @@
 
 /*********************************DIRECTORY OPERATION************************/
 
-int ext4_dir_rm(const char *path)
+int ext4_dir_rm(struct ext4_mountpoint *mp, const char *path)
 {
 	int r;
-	int len;
 	ext4_file f;
 
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	struct ext4_inode_ref act;
 	struct ext4_inode_ref child;
 	struct ext4_dir_iter it;
 
-	u32int name_off;
 	u32int inode_up;
 	u32int inode_current;
 	u32int depth = 1;
 
 	bool has_children;
-	bool is_goal;
 	bool dir_end;
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -2633,20 +2303,17 @@
 	struct ext4_fs *const fs = &mp->fs;
 
 	/*Check if exist.*/
-	r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
+	r = ext4_generic_open(mp, &f, path, "r", false, &inode_up);
 	if (r != 0) {
 		EXT4_MP_UNLOCK(mp);
 		return r;
 	}
 
-	path += name_off;
-	len = ext4_path_check(path, &is_goal);
 	inode_current = f.inode;
 
 	ext4_block_cache_write_back(mp->fs.bdev, 1);
 
 	do {
-
 		u64int act_curr_pos = 0;
 		has_children = false;
 		dir_end = false;
@@ -2723,9 +2390,7 @@
 
 				/*No children in child directory or file. Just
 				 * unlink.*/
-				r = ext4_unlink(f.mp, &act, &child,
-						(char *)it.curr->name,
-						it.curr->name_len);
+				r = ext4_unlink(f.mp, &act, &child, (char *)it.curr->name);
 				if (r != 0) {
 					ext4_fs_put_inode_ref(&child);
 					goto End;
@@ -2798,8 +2463,7 @@
 		/* In this place all directories should be
 		 * unlinked.
 		 * Last unlink from root of current directory*/
-		r = ext4_unlink(f.mp, &parent, &act,
-				(char *)path, len);
+		r = ext4_unlink(f.mp, &parent, &act, (char *)path);
 		if (r != 0) {
 			ext4_fs_put_inode_ref(&parent);
 			ext4_fs_put_inode_ref(&act);
@@ -2844,20 +2508,16 @@
 	return r;
 }
 
-int ext4_dir_mv(const char *path, const char *new_path)
+int ext4_dir_mv(struct ext4_mountpoint *mp, const char *path, const char *new_path)
 {
-	return ext4_frename(path, new_path);
+	return ext4_frename(mp, path, new_path);
 }
 
-int ext4_dir_mk(const char *path)
+int ext4_dir_mk(struct ext4_mountpoint *mp, const char *path)
 {
 	int r;
 	ext4_file f;
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 
-	if (!mp)
-		return -1;
-
 	if (mp->fs.read_only) {
 		werrstr(Erdonlyfs);
 		return -1;
@@ -2866,7 +2526,7 @@
 	EXT4_MP_LOCK(mp);
 
 	/*Check if exist.*/
-	r = ext4_generic_open(&f, path, "r", false, 0, 0);
+	r = ext4_generic_open(mp, &f, path, "r", false, nil);
 	if (r == 0) {
 		werrstr(Eexists);
 		r = -1;
@@ -2874,7 +2534,7 @@
 	}
 
 	/*Create new directory.*/
-	r = ext4_generic_open(&f, path, "w", false, 0, 0);
+	r = ext4_generic_open(mp, &f, path, "w", false, nil);
 
 Finish:
 	EXT4_MP_UNLOCK(mp);
@@ -2881,16 +2541,12 @@
 	return r;
 }
 
-int ext4_dir_open(ext4_dir *dir, const char *path)
+int ext4_dir_open(struct ext4_mountpoint *mp, ext4_dir *dir, const char *path)
 {
-	struct ext4_mountpoint *mp = ext4_get_mount(path);
 	int r;
 
-	if (!mp)
-		return -1;
-
 	EXT4_MP_LOCK(mp);
-	r = ext4_generic_open(&dir->f, path, "r", false, 0, 0);
+	r = ext4_generic_open(mp, &dir->f, path, "r", false, nil);
 	dir->next_off = 0;
 	EXT4_MP_UNLOCK(mp);
 	return r;
--- a/sys/src/cmd/ext4srv/ext4_dir.c
+++ b/sys/src/cmd/ext4srv/ext4_dir.c
@@ -462,6 +462,7 @@
 			return r;
 	}
 
+	werrstr(Enotfound);
 	return EXT4_ERR_NOT_FOUND;
 }
 
@@ -636,6 +637,7 @@
 	}
 
 	/* Entry not found */
+	werrstr(Enotfound);
 	return EXT4_ERR_NOT_FOUND;
 }
 
--- a/sys/src/cmd/ext4srv/ext4_dir_idx.c
+++ b/sys/src/cmd/ext4srv/ext4_dir_idx.c
@@ -661,6 +661,7 @@
 		p->position = p->entries;
 	}
 
+	werrstr(Enotfound);
 	return EXT4_ERR_NOT_FOUND;
 }
 
--- a/sys/src/cmd/ext4srv/ext4srv.c
+++ b/sys/src/cmd/ext4srv/ext4srv.c
@@ -36,7 +36,6 @@
 	.cachewb = 0,
 	.asroot = 0,
 	.rdonly = 0,
-	.linkmode = Lhide,
 
 	.fstype = -1,
 	.blksz = 1024,
@@ -48,60 +47,14 @@
 static u8int zero[65536];
 static char *srvname = "ext4";
 
-static char *
-linkresolve(Aux *a, char *s, char **value)
-{
-	char *q, buf[4096+1];
-	usize sz;
-	int res;
-
-	res = 0;
-	if(opts.linkmode == Lresolve && (res = ext4_readlink(s, buf, sizeof(buf), &sz)) == 0){
-		if(sz == sizeof(buf)){
-			werrstr("readlink: %s: path too long", s);
-			free(s);
-			return nil;
-		}
-
-		buf[sz] = 0;
-		if(value != nil)
-			*value = strdup(buf);
-		cleanname(buf);
-		if(buf[0] == '/'){
-			free(s);
-			s = smprint("%M%s", a->p, buf);
-		}else{
-			q = strrchr(s, '/');
-			*q = 0;
-			q = s;
-			s = smprint("%s/%s", q, buf);
-			free(q);
-			cleanname(strchr(s+1, '/'));
-		}
-	}else{
-		if(res != 0)
-			werrstr("readlink: %s: %r", s);
-		if(value != nil)
-			*value = nil;
-	}
-
-	return s;
-}
-
-static char *
-fullpath(Aux *a)
-{
-	return linkresolve(a, smprint("%M/%s", a->p, a->path), nil);
-}
-
 static int
 haveperm(Aux *a, int p, struct ext4_inode *inodeout)
 {
+	struct ext4_mountpoint *mp;
 	struct ext4_inode inode;
 	u32int ino, id;
 	int m, fm;
 	Group *g;
-	char *s;
 
 	switch(p & 3){
 	case OREAD:
@@ -122,14 +75,11 @@
 	if(p & OTRUNC)
 		p |= AWRITE;
 
-	if((s = fullpath(a)) == nil)
+	mp = &a->p->mp;
+	if(ext4_raw_inode_fill(mp, a->path, &ino, &inode) != 0){
+		werrstr("%s: %r", a->path);
 		return -1;
-	if(ext4_raw_inode_fill(s, &ino, &inode) != 0){
-		werrstr("%s: %r", s);
-		free(s);
-		return -1;
 	}
-	free(s);
 
 	if(inodeout != nil)
 		memmove(inodeout, &inode, sizeof(inode));
@@ -178,7 +128,7 @@
 
 		incref(a->p);
 		a->type = Adir;
-		a->path = strdup("");
+		a->path = estrdup9p("");
 		r->ofcall.qid = a->p->qidmask;
 		r->fid->qid = a->p->qidmask;
 		r->fid->aux = a;
@@ -217,11 +167,13 @@
 static void
 ropen(Req *r)
 {
+	struct ext4_mountpoint *mp;
 	char *path;
 	int res;
 	Aux *a;
 
 	a = r->fid->aux;
+	mp = &a->p->mp;
 	switch(a->type){
 	case Adir:
 		if(r->ifcall.mode != OREAD || !haveperm(a, r->ifcall.mode, nil)){
@@ -234,12 +186,12 @@
 		}
 		if((a->dir = malloc(sizeof(*a->dir))) == nil)
 			goto Nomem;
-		if((path = smprint("%M/%s", a->p, a->path)) == nil){
+		if((path = estrdup9p(a->path)) == nil){
 			free(a->dir);
 			a->dir = nil;
 			goto Nomem;
 		}
-		res = ext4_dir_open(a->dir, path);
+		res = ext4_dir_open(mp, a->dir, path);
 		free(path);
 		if(res != 0){
 			free(a->dir);
@@ -260,12 +212,12 @@
 		}
 		if((a->file = malloc(sizeof(*a->file))) == nil)
 			goto Nomem;
-		if((path = smprint("%M/%s", a->p, a->path)) == nil){
+		if((path = estrdup9p(a->path)) == nil){
 			free(a->file);
 			a->file = nil;
 			goto Nomem;
 		}
-		res = ext4_fopen2(a->file, path, toext4mode(r->ifcall.mode, 0, 0));
+		res = ext4_fopen2(mp, a->file, path, toext4mode(r->ifcall.mode, 0, 0));
 		free(path);
 		if(res != 0){
 			free(a->file);
@@ -288,14 +240,16 @@
 static void
 rcreate(Req *r)
 {
+	struct ext4_mountpoint *mp;
 	u32int perm, dirperm, t;
 	struct ext4_inode inode;
-	char *s, *q;
-	int mkdir;
+	int mkdir, isroot;
 	long tm;
+	char *s;
 	Aux *a;
 
 	a = r->fid->aux;
+	mp = &a->p->mp;
 	s = nil;
 
 	if(a->file != nil || a->dir != nil){
@@ -308,26 +262,19 @@
 	}
 
 	/* first make sure this is a directory */
-	t = ext4_inode_type(a->p->sb, &inode);
-	if(t != EXT4_INODE_MODE_DIRECTORY){
-		werrstr("create in non-directory");
-		goto error;
+	isroot = r->fid->qid.path == a->p->qidmask.path;
+	if(!isroot){
+		t = ext4_inode_type(a->p->sb, &inode);
+		if(t != EXT4_INODE_MODE_DIRECTORY){
+			werrstr("create in non-directory");
+			goto error;
+		}
 	}
+	ext4_mode_get(mp, a->path, &dirperm);
 
-	if((s = fullpath(a)) == nil)
-		goto error;
-	ext4_mode_get(s, &dirperm);
-
 	/* check if the entry already exists */
-	if((q = smprint("%s/%s", s, r->ifcall.name)) == nil){
-Nomem:
-		werrstr("memory");
-		goto error;
-	}
-	free(s);
-	s = q;
-	cleanname(s);
-	if(ext4_inode_exist(s, EXT4_DE_UNKNOWN) == 0){
+	s = isroot ? estrdup9p(r->ifcall.name) : smprint("%s/%s", a->path, r->ifcall.name);
+	if(ext4_inode_exist(mp, s, EXT4_DE_UNKNOWN) == 0){
 		werrstr("file already exists");
 		goto error;
 	}
@@ -338,11 +285,10 @@
 
 	if(mkdir){
 		a->type = Adir;
-		if(ext4_dir_mk(s) != 0)
+		if(ext4_dir_mk(mp, s) < 0)
 			goto error;
-		if((a->dir = malloc(sizeof(*a->dir))) == nil)
-			goto Nomem;
-		if(ext4_dir_open(a->dir, s) < 0){
+		a->dir = emalloc9p(sizeof(*a->dir));
+		if(ext4_dir_open(mp, a->dir, s) < 0){
 			free(a->dir);
 			a->dir = nil;
 			goto ext4errorrm;
@@ -349,9 +295,8 @@
 		}
 	}else{
 		a->type = Afile;
-		if((a->file = malloc(sizeof(*a->file))) == nil)
-			goto Nomem;
-		if(ext4_fopen2(a->file, s, toext4mode(r->ifcall.mode, perm, 1)) < 0){
+		a->file = emalloc9p(sizeof(*a->file));
+		if(ext4_fopen2(mp, a->file, s, toext4mode(r->ifcall.mode, perm, 1)) < 0){
 			free(a->file);
 			a->file = nil;
 			goto error;
@@ -358,12 +303,12 @@
 		}
 	}
 
-	if(ext4_mode_set(s, perm) < 0)
+	if(ext4_mode_set(mp, s, perm) < 0)
 		goto ext4errorrm;
-	ext4_owner_set(s, a->uid, a->uid);
+	ext4_owner_set(mp, s, a->uid, a->uid);
 	tm = time(nil);
-	ext4_mtime_set(s, tm);
-	ext4_ctime_set(s, tm);
+	ext4_mtime_set(mp, s, tm);
+	ext4_ctime_set(mp, s, tm);
 
 	r->fid->qid.path = a->p->qidmask.path | a->file->inode;
 	r->fid->qid.vers = 0;
@@ -371,8 +316,7 @@
 	r->ofcall.qid = r->fid->qid;
 
 	free(a->path);
-	a->path = strdup(strchr(s+1, '/')+1);
-	free(s);
+	a->path = s;
 	r->ofcall.iounit = 0;
 	respond(r, nil);
 	return;
@@ -379,9 +323,9 @@
 
 ext4errorrm:
 	if(mkdir)
-		ext4_dir_rm(s);
+		ext4_dir_rm(mp, s);
 	else
-		ext4_fremove(s);
+		ext4_fremove(mp, s);
 error:
 	free(s);
 	responderror(r);
@@ -390,34 +334,26 @@
 static int
 dirfill(Dir *dir, Aux *a, char *path)
 {
+	struct ext4_mountpoint *mp;
 	struct ext4_inode inode;
 	u32int t, ino, id;
 	char tmp[16];
-	char *s, *q;
 	Group *g;
+	char *s;
+	int r;
 
 	memset(dir, 0, sizeof(*dir));
+	mp = &a->p->mp;
 
 	if(path == nil){
-		path = a->path;
-		s = smprint("%M/%s", a->p, a->path);
+		r = ext4_raw_inode_fill(mp, a->path, &ino, &inode);
 	}else{
-		if(*a->path == 0 && *path == 0)
-			path = "/";
-		s = smprint("%M%s%s/%s", a->p, *a->path ? "/" : "", a->path, path);
-	}
-	if((s = linkresolve(a, s, nil)) == nil)
-		return -1;
-	if(ext4_raw_inode_fill(s, &ino, &inode) < 0){
-		werrstr("inode: %s: %r", s);
+		s = smprint("%s%s%s", a->path, *a->path ? "/" : "", path);
+		r = ext4_raw_inode_fill(mp, s, &ino, &inode);
 		free(s);
-		return -1;
 	}
-
-	t = ext4_inode_type(a->p->sb, &inode);
-	if(opts.linkmode == Lhide && t == EXT4_INODE_MODE_SOFTLINK){
-		werrstr("softlinks resolving disabled");
-		free(s);
+	if(r < 0){
+		werrstr("inode: %s: %r", path ? path : "/");
 		return -1;
 	}
 
@@ -425,6 +361,7 @@
 	dir->qid.path = a->p->qidmask.path | ino;
 	dir->qid.vers = ext4_inode_get_generation(&inode);
 	dir->qid.type = 0;
+	t = ext4_inode_type(a->p->sb, &inode);
 	if(t == EXT4_INODE_MODE_DIRECTORY){
 		dir->qid.type |= QTDIR;
 		dir->mode |= DMDIR;
@@ -435,9 +372,9 @@
 		dir->mode |= DMAPPEND;
 	}
 
-	if((q = strrchr(path, '/')) != nil)
-		path = q+1;
-	dir->name = estrdup9p(path);
+	if(path != nil && (s = strrchr(path, '/')) != nil)
+		path = s+1;
+	dir->name = estrdup9p(path != nil ? path : "");
 	dir->atime = ext4_inode_get_access_time(&inode);
 	dir->mtime = ext4_inode_get_modif_time(&inode);
 
@@ -447,8 +384,6 @@
 	sprint(tmp, "%ud", id = ext4_inode_get_gid(&inode));
 	dir->gid = estrdup9p((g = findgroupid(&a->p->groups, id)) != nil ? g->name : tmp);
 
-	free(s);
-
 	return 0;
 }
 
@@ -470,7 +405,7 @@
 				return -1;
 		}while(e->name == nil || strcmp((char*)e->name, ".") == 0 || strcmp((char*)e->name, "..") == 0);
 
-		if(opts.linkmode == Lhide && e->inode_type == EXT4_DE_SYMLINK)
+		if(e->inode_type != EXT4_DE_REG_FILE && e->inode_type != EXT4_DE_DIR)
 			continue;
 
 		if(a->doff++ != n)
@@ -513,9 +448,7 @@
 	Aux *a;
 
 	a = r->fid->aux;
-	if(a->type == Adir){
-		respond(r, "can't write to dir");
-	}else if(a->type == Afile){
+	if(a->type == Afile){
 		while(ext4_fsize(a->file) < r->ifcall.offset){
 			ext4_fseek(a->file, 0, 2);
 			sz = MIN(r->ifcall.offset-ext4_fsize(a->file), sizeof(zero));
@@ -524,15 +457,21 @@
 		}
 		if(ext4_fseek(a->file, r->ifcall.offset, 0) < 0)
 			goto error;
+		if(ext4_ftell(a->file) != r->ifcall.offset){
+			werrstr("could not seek");
+			goto error;
+		}
 		if(ext4_fwrite(a->file, r->ifcall.data, r->ifcall.count, &n) < 0)
 			goto error;
 
+		assert(r->ifcall.count >= n);
 		r->ofcall.count = n;
 		respond(r, nil);
+		return;
+	}else{
+		werrstr("can only write to files");
 	}
 
-	return;
-
 error:
 	responderror(r);
 }
@@ -540,27 +479,24 @@
 static void
 rremove(Req *r)
 {
+	struct ext4_mountpoint *mp;
 	struct ext4_inode inode;
 	const ext4_direntry *e;
 	u32int ino, t, empty;
 	ext4_dir dir;
 	Group *g;
-	char *s;
 	Aux *a;
 
 	a = r->fid->aux;
+	mp = &a->p->mp;
 
 	/* do not resolve links here as most likely it's JUST the link we want to remove */
-	if((s = smprint("%M/%s", a->p, a->path)) == nil){
-		werrstr("memory");
+	if(ext4_raw_inode_fill(mp, a->path, &ino, &inode) < 0)
 		goto error;
-	}
-	if(ext4_raw_inode_fill(s, &ino, &inode) < 0)
-		goto error;
 
 	if(a->uid == Root || ((g = findgroupid(&a->p->groups, ext4_inode_get_uid(&inode))) != nil && g->id == a->uid)){
 		t = ext4_inode_type(a->p->sb, &inode);
-		if(t == EXT4_INODE_MODE_DIRECTORY && ext4_dir_open(&dir, s) == 0){
+		if(t == EXT4_INODE_MODE_DIRECTORY && ext4_dir_open(mp, &dir, a->path) == 0){
 			for(empty = 1; empty;){
 				if((e = ext4_dir_entry_next(&dir)) == nil)
 					break;
@@ -570,9 +506,9 @@
 			if(!empty){
 				werrstr("directory not empty");
 				goto error;
-			}else if(ext4_dir_rm(s) < 0)
+			}else if(ext4_dir_rm(mp, a->path) < 0)
 				goto error;
-		}else if(ext4_fremove(s) < 0)
+		}else if(ext4_fremove(mp, a->path) < 0)
 			goto error;
 	}else{
 		werrstr(Eperm);
@@ -579,12 +515,10 @@
 		goto error;
 	}
 
-	free(s);
 	respond(r, nil);
 	return;
 
 error:
-	free(s);
 	responderror(r);
 }
 
@@ -604,29 +538,23 @@
 rwstat(Req *r)
 {
 	int res, isdir, wrperm, isowner, n;
+	struct ext4_mountpoint *mp;
 	struct ext4_inode inode;
-	char *old, *new, *s;
 	u32int uid, gid;
 	ext4_file f;
 	Aux *a, o;
 	Group *g;
+	char *s;
 
 	a = r->fid->aux;
-	old = nil;
-	new = nil;
+	mp = &a->p->mp;
 
 	/* can't do anything to root, can't change the owner */
-	if(a->path[0] == 0 || (r->d.uid != nil && r->d.uid[0] != 0)){
+	if(*a->path == 0 || (r->d.uid != nil && r->d.uid[0] != 0)){
 		werrstr(Eperm);
 		goto error;
 	}
 
-	if((old = smprint("%M/%s", a->p, a->path)) == nil){
-		werrstr("memory");
-		goto error;
-	}
-	new = old;
-
 	wrperm = haveperm(a, OWRITE, &inode);
 	uid = ext4_inode_get_uid(&inode);
 	isowner = uid == Root || a->uid == uid;
@@ -638,31 +566,6 @@
 		goto error;
 	}
 
-	/* permission to rename */
-	if(r->d.name != nil && r->d.name[0] != 0){
-		if((s = strrchr(old, '/')) == nil){
-			werrstr("botched name");
-			goto error;
-		}
-		n = s - old;
-		if((new = malloc(n + 1 + strlen(r->d.name) + 1)) == nil){
-			werrstr("memory");
-			goto error;
-		}
-		memmove(new, old, n);
-		new[n++] = '/';
-		strcpy(new+n, r->d.name);
-
-		/* check parent write permission */
-		o = *a;
-		o.path = old;
-		if(!haveperm(&o, OWRITE, nil)){
-			werrstr(Eperm);
-			goto error;
-		}
-		*s = '/';
-	}
-
 	/* permission to change mode */
 	if(r->d.mode != ~0){
 		/* has to be owner and can't change dir bit */
@@ -687,24 +590,37 @@
 		}
 	}
 
-	/* done checking permissions, now apply all the changes and hope it all works */
-
-	/* rename */
+	/* check for permission to rename and do the rename */
 	if(r->d.name != nil && r->d.name[0] != 0){
-		if(ext4_frename(old, new) < 0)
+		/* check parent write permission */
+		o = *a;
+		o.path = a->path;
+		if(!haveperm(&o, OWRITE, nil)){
+			werrstr(Eperm);
 			goto error;
+		}
 
-		free(old);
-		old = new;
-		new = nil;
+		if((s = strrchr(a->path, '/')) != nil){
+			n = s - a->path;
+			s = emalloc9p(n + 1 + strlen(r->d.name) + 1);
+			memmove(s, a->path, n);
+			s[n++] = '/';
+			strcpy(s+n, r->d.name);
+		}else{
+			s = estrdup9p(r->d.name);
+		}
 
+		if(ext4_frename(mp, a->path, s) < 0){
+			free(s);
+			goto error;
+		}
 		free(a->path);
-		a->path = strdup(strchr(old+1, '/')+1);
+		a->path = s;
 	}
 
 	/* truncate */
 	if(r->d.length >= 0){
-		if(ext4_fopen2(&f, new, toext4mode(OWRITE, 0, 0)) < 0)
+		if(ext4_fopen2(mp, &f, a->path, toext4mode(OWRITE, 0, 0)) < 0)
 			goto error;
 		res = ext4_ftruncate(&f, r->d.length);
 		ext4_fclose(&f);
@@ -713,27 +629,21 @@
 	}
 
 	/* mode */
-	if(r->d.mode != ~0 && ext4_mode_set(new, r->d.mode & 0x1ff) < 0)
+	if(r->d.mode != ~0 && ext4_mode_set(mp, a->path, r->d.mode & 0x1ff) < 0)
 		goto error;
 
 	/* mtime */
-	if(r->d.mtime != ~0 && ext4_mtime_set(new, r->d.mtime) < 0)
+	if(r->d.mtime != ~0 && ext4_mtime_set(mp, a->path, r->d.mtime) < 0)
 		goto error;
 
 	/* gid */
-	if(r->d.gid != nil && r->d.gid[0] != 0 && ext4_owner_set(new, uid, gid) < 0)
+	if(r->d.gid != nil && r->d.gid[0] != 0 && ext4_owner_set(mp, a->path, uid, gid) < 0)
 		goto error;
 
-	free(old);
-	if(new != old)
-		free(new);
 	respond(r, nil);
 	return;
 
 error:
-	free(old);
-	if(new != old)
-		free(new);
 	responderror(r);
 }
 
@@ -740,52 +650,50 @@
 static char *
 rwalk1(Fid *fid, char *name, Qid *qid)
 {
+	struct ext4_mountpoint *mp;
 	static char errbuf[ERRMAX];
 	struct ext4_inode inode;
 	u32int ino, t;
 	Aux *a, dir;
-	char *s, *q;
+	int isroot;
+	char *s;
 
 	a = fid->aux;
+	mp = &a->p->mp;
+	isroot = fid->qid.path == a->p->qidmask.path;
+	s = nil;
 
-	/* try walking to the real file first */
-	if((s = fullpath(a)) == nil){
-		/* else try link itself. might want to just remove it anyway */
-		if((s = smprint("%M/%s", a->p, a->path)) == nil)
-			return "memory";
+	if(isroot && strcmp(name, "..") == 0){
+		*qid = a->p->qidmask;
+		fid->qid = a->p->qidmask;
+		return nil;
 	}
-	if(ext4_raw_inode_fill(s, &ino, &inode) < 0)
-		goto error;
-	t = ext4_inode_type(a->p->sb, &inode);
-	if(t != EXT4_INODE_MODE_DIRECTORY){
-		free(s);
-		return "not a directory";
-	}
-	dir = *a;
-	dir.path = strchr(s+1, '/')+1;
-	if(!haveperm(&dir, OEXEC, nil)){
-		free(s);
-		return Eperm;
-	}
 
-	q = s;
-	s = smprint("%s/%s", q, name);
-	cleanname(strchr(s+1, '/'));
-	free(q);
-	if((q = linkresolve(a, s, nil)) == nil){
-error:
+	if(ext4_raw_inode_fill(mp, a->path, &ino, &inode) < 0){
+err:
 		free(s);
 		rerrstr(errbuf, sizeof(errbuf));
 		return errbuf;
 	}
-	s = q;
-	if(ext4_raw_inode_fill(s, &ino, &inode) < 0)
-		goto error;
+
 	t = ext4_inode_type(a->p->sb, &inode);
-	if(opts.linkmode == Lhide && t == EXT4_INODE_MODE_SOFTLINK){
+	if(t != EXT4_INODE_MODE_DIRECTORY)
+		return "not a directory";
+	dir = *a;
+	dir.path = a->path;
+	if(!haveperm(&dir, OEXEC, nil))
+		return Eperm;
+
+	s = isroot ? estrdup9p(name) : smprint("%s/%s", a->path, name);
+	cleanname(s);
+	if(ext4_raw_inode_fill(mp, s, &ino, &inode) < 0)
+		goto err;
+	t = ext4_inode_type(a->p->sb, &inode);
+	if(t != EXT4_INODE_MODE_FILE && t != EXT4_INODE_MODE_DIRECTORY){
 		free(s);
 		return "not found";
 	}
+
 	qid->type = 0;
 	qid->path = a->p->qidmask.path | ino;
 	qid->vers = ext4_inode_get_generation(&inode);
@@ -797,8 +705,7 @@
 	if(ext4_inode_get_flags(&inode) & EXT4_INODE_FLAG_APPEND)
 		qid->type |= QTAPPEND;
 	free(a->path);
-	a->path = strdup(strchr(s+1, '/')+1);
-	free(s);
+	a->path = s;
 	fid->qid = *qid;
 
 	return nil;
@@ -814,7 +721,7 @@
 	if((c = calloc(1, sizeof(*c))) == nil)
 		return "memory";
 	memmove(c, a, sizeof(*c));
-	c->path = strdup(a->path);
+	c->path = estrdup9p(a->path);
 	c->file = nil;
 	c->dir = nil;
 
@@ -935,7 +842,7 @@
 static void
 usage(void)
 {
-	fprint(2, "usage: %s [-Clrs] [-g groupfile] [-R uid] [srvname]\n", argv0);
+	fprint(2, "usage: %s [-Crs] [-g groupfile] [-R uid] [srvname]\n", argv0);
 	fprint(2, "mkfs:  %s -M (2|3|4) [-L label] [-b blksize] [-N numinodes] [-I inodesize] device\n", argv0);
 	threadexitsall("usage");
 }
@@ -963,9 +870,6 @@
 		break;
 	case 'C':
 		opts.cachewb = 1;
-		goto nomkfs;
-	case 'l':
-		opts.linkmode = Lresolve;
 		goto nomkfs;
 	case 'g':
 		gr = EARGF(usage());
--- a/sys/src/cmd/ext4srv/include/ext4.h
+++ b/sys/src/cmd/ext4srv/include/ext4.h
@@ -3,9 +3,31 @@
 #include "ext4_types.h"
 #include "ext4_debug.h"
 #include "ext4_blockdev.h"
+#include "ext4_fs.h"
+#include "ext4_journal.h"
 
-#pragma incomplete struct ext4_mountpoint
+/**@brief   Mount point descriptor.*/
+struct ext4_mountpoint {
 
+	/**@brief   Mount done flag.*/
+	bool mounted;
+
+	/**@brief   OS dependent lock/unlock functions.*/
+	const struct ext4_lock *os_locks;
+
+	/**@brief   Ext4 filesystem internals.*/
+	struct ext4_fs fs;
+
+	/**@brief   JBD fs.*/
+	struct jbd_fs jbd_fs;
+
+	/**@brief   Journal.*/
+	struct jbd_journal jbd_journal;
+
+	/**@brief   Block cache.*/
+	struct ext4_bcache bc;
+};
+
 /********************************OS LOCK INFERFACE***************************/
 
 /**@brief   OS dependent lock interface.*/
@@ -65,79 +87,12 @@
 
 /********************************MOUNT OPERATIONS****************************/
 
-/**@brief   Register block device.
- *
- * @param   bd Block device.
- * @param   dev_name Block device name.
- *
- * @return  Standard error code.*/
-int ext4_device_register(struct ext4_blockdev *bd,
-			 const char *dev_name);
+int ext4_mount(struct ext4_mountpoint *mp, struct ext4_blockdev *bd, bool read_only);
+int ext4_umount(struct ext4_mountpoint *mp);
+int ext4_journal_start(struct ext4_mountpoint *mp);
+int ext4_journal_stop(struct ext4_mountpoint *mp);
+int ext4_recover(struct ext4_mountpoint *mp);
 
-/**@brief   Un-register block device.
- *
- * @param   dev_name Block device name.
- *
- * @return  Standard error code.*/
-int ext4_device_unregister(const char *dev_name);
-
-/**@brief   Un-register all block devices.
- *
- * @return  Standard error code.*/
-int ext4_device_unregister_all(void);
-
-/**@brief   Mount a block device with EXT4 partition to the mount point.
- *
- * @param   dev_name Block device name (@ref ext4_device_register).
- * @param   mount_point Mount point, for example:
- *          -   /
- *          -   /my_partition/
- *          -   /my_second_partition/
- * @param   read_only mount as read-only mode.
- *
- * @return Standard error code */
-int ext4_mount(const char *dev_name,
-	       const char *mount_point,
-	       bool read_only);
-
-/**@brief   Umount operation.
- *
- * @param   mount_point Mount point.
- *
- * @return  Standard error code */
-int ext4_umount(const char *mount_point);
-
-/**@brief   Starts journaling. Journaling start/stop functions are transparent
- *          and might be used on filesystems without journaling support.
- * @warning Usage:
- *              ext4_mount("sda1", "/");
- *              ext4_journal_start("/");
- *
- *              //File operations here...
- *
- *              ext4_journal_stop("/");
- *              ext4_umount("/");
- * @param   mount_point Mount point.
- *
- * @return  Standard error code. */
-int ext4_journal_start(const char *mount_point);
-
-/**@brief   Stops journaling. Journaling start/stop functions are transparent
- *          and might be used on filesystems without journaling support.
- *
- * @param   mount_point Mount point name.
- *
- * @return  Standard error code. */
-int ext4_journal_stop(const char *mount_point);
-
-/**@brief   Journal recovery.
- * @warning Must be called after @ref ext4_mount.
- *
- * @param   mount_point Mount point.
- *
- * @return Standard error code. */
-int ext4_recover(const char *mount_point);
-
 /**@brief   Some of the filesystem stats. */
 struct ext4_mount_stats {
 	u32int inodes_count;
@@ -153,77 +108,12 @@
 	char volume_name[16];
 };
 
-/**@brief   Get file mount point stats.
- *
- * @param   mount_point Mount point.
- * @param   stats Filesystem stats.
- *
- * @return Standard error code. */
-int ext4_mount_point_stats(const char *mount_point,
-			   struct ext4_mount_stats *stats);
+int ext4_mount_point_stats(struct ext4_mountpoint *mp, struct ext4_mount_stats *stats);
+int ext4_mount_setup_locks(struct ext4_mountpoint *mp, const struct ext4_lock *locks);
+int ext4_get_sblock(struct ext4_mountpoint *mp, struct ext4_sblock **sb);
+int ext4_cache_write_back(struct ext4_mountpoint *mp, bool on);
+int ext4_cache_flush(struct ext4_mountpoint *mp);
 
-/**@brief   Setup OS lock routines.
- *
- * @param   mount_point Mount point.
- * @param   locks  Lock and unlock functions
- *
- * @return Standard error code. */
-int ext4_mount_setup_locks(const char *mount_point,
-			   const struct ext4_lock *locks);
-
-/**@brief   Acquire the filesystem superblock pointer of a mp.
- *
- * @param   mount_point Mount point.
- * @param   sb Superblock handle
- *
- * @return Standard error code. */
-int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb);
-
-/**@brief   Enable/disable write back cache mode.
- * @warning Default model of cache is write through. It means that when you do:
- *
- *          ext4_fopen(...);
- *          ext4_fwrite(...);
- *                           < --- data is flushed to physical drive
- *
- *          When you do:
- *          ext4_cache_write_back(..., 1);
- *          ext4_fopen(...);
- *          ext4_fwrite(...);
- *                           < --- data is NOT flushed to physical drive
- *          ext4_cache_write_back(..., 0);
- *                           < --- when write back mode is disabled all
- *                                 cache data will be flushed
- * To enable write back mode permanently just call this function
- * once after ext4_mount (and disable before ext4_umount).
- *
- * Some of the function use write back cache mode internally.
- * If you enable write back mode twice you have to disable it twice
- * to flush all data:
- *
- *      ext4_cache_write_back(..., 1);
- *      ext4_cache_write_back(..., 1);
- *
- *      ext4_cache_write_back(..., 0);
- *      ext4_cache_write_back(..., 0);
- *
- * Write back mode is useful when you want to create a lot of empty
- * files/directories.
- *
- * @param   path Path.
- * @param   on Enable/disable cache writeback mode.
- *
- * @return Standard error code. */
-int ext4_cache_write_back(const char *path, bool on);
-
-
-/**@brief   Force cache flush.
- *
- * @param   path Path.
- *
- * @return  Standard error code. */
-int ext4_cache_flush(const char *path);
-
 /********************************FILE OPERATIONS*****************************/
 
 /**@brief   Remove file by path.
@@ -231,7 +121,7 @@
  * @param   path Path to file.
  *
  * @return  Standard error code. */
-int ext4_fremove(const char *path);
+int ext4_fremove(struct ext4_mountpoint *mp, const char *path);
 
 /**@brief   Create a hardlink for a file.
  *
@@ -239,13 +129,13 @@
  * @param   hardlink_path Path of hardlink.
  *
  * @return  Standard error code. */
-int ext4_flink(const char *path, const char *hardlink_path);
+int ext4_flink(struct ext4_mountpoint *mp, const char *path, const char *hardlink_path);
 
 /**@brief Rename file.
  * @param path Source.
  * @param new_path Destination.
  * @return  Standard error code. */
-int ext4_frename(const char *path, const char *new_path);
+int ext4_frename(struct ext4_mountpoint *mp, const char *path, const char *new_path);
 
 /**@brief   File open function.
  *
@@ -267,7 +157,7 @@
  *  |---------------------------------------------------------------|
  *
  * @return  Standard error code.*/
-int ext4_fopen(ext4_file *file, const char *path, const char *flags);
+int ext4_fopen(struct ext4_mountpoint *mp, ext4_file *file, const char *path, const char *flags);
 
 /**@brief   Alternate file open function.
  *
@@ -276,7 +166,7 @@
  * @param   flags File open flags.
  *
  * @return  Standard error code.*/
-int ext4_fopen2(ext4_file *file, const char *path, int flags);
+int ext4_fopen2(struct ext4_mountpoint *mp, ext4_file *file, const char *path, int flags);
 
 /**@brief   File close function.
  *
@@ -319,12 +209,12 @@
  * @param   file File handle.
  * @param   offset Offset to seek.
  * @param   origin Seek type:
- *              @ref SEEK_SET
- *              @ref SEEK_CUR
- *              @ref SEEK_END
+ *              @ref 0 (set)
+ *              @ref 1 (cur)
+ *              @ref 2 (end)
  *
  * @return  Standard error code.*/
-int ext4_fseek(ext4_file *file, s64int offset, u32int origin);
+int ext4_fseek(ext4_file *file, s64int offset, int origin);
 
 /**@brief   Get file position.
  *
@@ -348,7 +238,7 @@
  * @param inode   Inode internals.
  *
  * @return  Standard error code.*/
-int ext4_raw_inode_fill(const char *path, u32int *ret_ino,
+int ext4_raw_inode_fill(struct ext4_mountpoint *mp, const char *path, u32int *ret_ino,
 			struct ext4_inode *inode);
 
 /**@brief Check if inode exists.
@@ -365,7 +255,7 @@
  *                @ref EXT4_DE_SYMLINK
  *
  * @return  Standard error code.*/
-int ext4_inode_exist(const char *path, int type);
+int ext4_inode_exist(struct ext4_mountpoint *mp, const char *path, int type);
 
 /**@brief Change file/directory/link mode bits.
  *
@@ -373,7 +263,7 @@
  * @param mode New mode bits (for example 0777).
  *
  * @return  Standard error code.*/
-int ext4_mode_set(const char *path, u32int mode);
+int ext4_mode_set(struct ext4_mountpoint *mp, const char *path, u32int mode);
 
 
 /**@brief Get file/directory/link mode bits.
@@ -382,7 +272,7 @@
  * @param mode New mode bits (for example 0777).
  *
  * @return  Standard error code.*/
-int ext4_mode_get(const char *path, u32int *mode);
+int ext4_mode_get(struct ext4_mountpoint *mp, const char *path, u32int *mode);
 
 /**@brief Change file owner and group.
  *
@@ -391,7 +281,7 @@
  * @param gid  Group id.
  *
  * @return  Standard error code.*/
-int ext4_owner_set(const char *path, u32int uid, u32int gid);
+int ext4_owner_set(struct ext4_mountpoint *mp, const char *path, u32int uid, u32int gid);
 
 /**@brief Get file/directory/link owner and group.
  *
@@ -400,7 +290,7 @@
  * @param gid  Group id.
  *
  * @return  Standard error code.*/
-int ext4_owner_get(const char *path, u32int *uid, u32int *gid);
+int ext4_owner_get(struct ext4_mountpoint *mp, const char *path, u32int *uid, u32int *gid);
 
 /**@brief Set file/directory/link access time.
  *
@@ -408,7 +298,7 @@
  * @param atime Access timestamp.
  *
  * @return  Standard error code.*/
-int ext4_atime_set(const char *path, u32int atime);
+int ext4_atime_set(struct ext4_mountpoint *mp, const char *path, u32int atime);
 
 /**@brief Set file/directory/link modify time.
  *
@@ -416,7 +306,7 @@
  * @param mtime Modify timestamp.
  *
  * @return  Standard error code.*/
-int ext4_mtime_set(const char *path, u32int mtime);
+int ext4_mtime_set(struct ext4_mountpoint *mp, const char *path, u32int mtime);
 
 /**@brief Set file/directory/link change time.
  *
@@ -424,7 +314,7 @@
  * @param ctime Change timestamp.
  *
  * @return  Standard error code.*/
-int ext4_ctime_set(const char *path, u32int ctime);
+int ext4_ctime_set(struct ext4_mountpoint *mp, const char *path, u32int ctime);
 
 /**@brief Get file/directory/link access time.
  *
@@ -432,7 +322,7 @@
  * @param atime Access timestamp.
  *
  * @return  Standard error code.*/
-int ext4_atime_get(const char *path, u32int *atime);
+int ext4_atime_get(struct ext4_mountpoint *mp, const char *path, u32int *atime);
 
 /**@brief Get file/directory/link modify time.
  *
@@ -440,7 +330,7 @@
  * @param mtime Modify timestamp.
  *
  * @return  Standard error code.*/
-int ext4_mtime_get(const char *path, u32int *mtime);
+int ext4_mtime_get(struct ext4_mountpoint *mp, const char *path, u32int *mtime);
 
 /**@brief Get file/directory/link change time.
  *
@@ -448,7 +338,7 @@
  * @param ctime Change timestamp.
  *
  * @return  standard error code*/
-int ext4_ctime_get(const char *path, u32int *ctime);
+int ext4_ctime_get(struct ext4_mountpoint *mp, const char *path, u32int *ctime);
 
 /**@brief Create symbolic link.
  *
@@ -456,7 +346,7 @@
  * @param path   Source entry path.
  *
  * @return  Standard error code.*/
-int ext4_fsymlink(const char *target, const char *path);
+int ext4_fsymlink(struct ext4_mountpoint *mp, const char *target, const char *path);
 
 /**@brief Create special file.
  * @param path     Path to new special file.
@@ -465,7 +355,7 @@
  * @param dev      If filetype is char device or block device,
  * 	           the device number will become the payload in the inode.
  * @return  Standard error code.*/
-int ext4_mknod(const char *path, int filetype, u32int dev);
+int ext4_mknod(struct ext4_mountpoint *mp, const char *path, int filetype, u32int dev);
 
 /**@brief Read symbolic link payload.
  *
@@ -475,7 +365,7 @@
  * @param rcnt    Bytes read.
  *
  * @return  Standard error code.*/
-int ext4_readlink(const char *path, char *buf, usize bufsize, usize *rcnt);
+int ext4_readlink(struct ext4_mountpoint *mp, const char *path, char *buf, usize bufsize, usize *rcnt);
 
 /*********************************DIRECTORY OPERATION***********************/
 
@@ -484,7 +374,7 @@
  * @param   path Directory path to remove
  *
  * @return  Standard error code.*/
-int ext4_dir_rm(const char *path);
+int ext4_dir_rm(struct ext4_mountpoint *mp, const char *path);
 
 /**@brief Rename/move directory.
  *
@@ -492,7 +382,7 @@
  * @param new_path Destination path.
  *
  * @return  Standard error code. */
-int ext4_dir_mv(const char *path, const char *new_path);
+int ext4_dir_mv(struct ext4_mountpoint *mp, const char *path, const char *new_path);
 
 /**@brief   Create new directory.
  *
@@ -499,7 +389,7 @@
  * @param   path Directory name.
  *
  * @return  Standard error code.*/
-int ext4_dir_mk(const char *path);
+int ext4_dir_mk(struct ext4_mountpoint *mp, const char *path);
 
 /**@brief   Directory open.
  *
@@ -507,7 +397,7 @@
  * @param   path Directory path.
  *
  * @return  Standard error code.*/
-int ext4_dir_open(ext4_dir *dir, const char *path);
+int ext4_dir_open(struct ext4_mountpoint *mp, ext4_dir *dir, const char *path);
 
 /**@brief   Directory close.
  *
--- a/sys/src/cmd/ext4srv/part.c
+++ b/sys/src/cmd/ext4srv/part.c
@@ -161,8 +161,8 @@
 	d = nil;
 	while(*path == '/')
 		path++;
-	s = smprint("%M/%s", p, path);
-	r = ext4_fopen2(&f, s, O_RDONLY);
+	s = smprint("/%s", path);
+	r = ext4_fopen2(&p->mp, &f, s, O_RDONLY);
 	free(s);
 
 	if(r == 0){
@@ -197,47 +197,37 @@
 static int
 mountpart(Part *p, Opts *opts)
 {
+	struct ext4_mountpoint *mp;
 	usize sz;
 	char *gr;
 	int r;
 
-	r = 0;
-	if(snprint(p->dev, sizeof(p->dev), "%Ð", p) >= sizeof(p->dev)){
-		werrstr("part path too long");
-		goto error;
-	}
-	if(snprint(p->mnt, sizeof(p->mnt), "%M/", p) >= sizeof(p->mnt)){
-		werrstr("part path too long");
-		goto error;
-	}
-	if(ext4_device_register(&p->bdev, p->dev) < 0){
-		werrstr("register: %r");
-		goto error;
-	}
-	if(ext4_mount(p->dev, p->mnt, opts->rdonly) < 0){
+	mp = &p->mp;
+	if(ext4_mount(mp, &p->bdev, opts->rdonly) < 0){
 		werrstr("mount: %r");
 		goto error;
 	}
-	if(ext4_mount_setup_locks(p->mnt, &p->oslocks) < 0){
+	if(ext4_mount_setup_locks(mp, &p->oslocks) < 0){
 		werrstr("locks: %r");
 		goto error;
 	}
-	if(ext4_recover(p->mnt) < 0){
+	if(ext4_recover(mp) < 0){
 		werrstr("recover: %r");
 		goto error;
 	}
-	if(ext4_journal_start(p->mnt) < 0){
+	if(ext4_journal_start(mp) < 0){
 		werrstr("journal: %r");
 		goto error;
 	}
 	if(opts->cachewb)
-		ext4_cache_write_back(p->mnt, 1);
+		ext4_cache_write_back(mp, 1);
 
-	if(ext4_get_sblock(p->mnt, &p->sb) < 0){
+	if(ext4_get_sblock(mp, &p->sb) < 0){
 		werrstr("sblock: %r");
 		goto error;
 	}
 
+	r = 0;
 	if(opts->group != nil){
 		r = loadgroups(&p->groups, opts->group);
 	}else if((gr = readfile(p, "/etc/group", &sz)) != nil){
@@ -288,9 +278,6 @@
 	s = nil;
 	qlock(&sv);
 
-	fmtinstall(L'Ð', fmtpart);
-	fmtinstall('M', fmtpart);
-
 	f = open(dev, ORDWR);
 	if(f < 0 || (d = dirfstat(f)) == nil)
 		goto error;
@@ -367,13 +354,14 @@
 static void
 _closepart(Part *p)
 {
-	ext4_cache_write_back(p->mnt, 0);
-	if(ext4_journal_stop(p->mnt) < 0)
-		fprint(2, "closepart: journal %s: %r\n", p->mnt);
-	if(ext4_umount(p->mnt) < 0)
-		fprint(2, "closepart: umount %s: %r\n", p->mnt);
-	if(ext4_device_unregister(p->dev) < 0)
-		fprint(2, "closepart: unregister %s: %r\n", p->dev);
+	struct ext4_mountpoint *mp;
+
+	mp = &p->mp;
+	ext4_cache_write_back(mp, 0);
+	if(ext4_journal_stop(mp) < 0)
+		fprint(2, "closepart: journal: %s: %r\n", p->partdev);
+	if(ext4_umount(mp) < 0)
+		fprint(2, "closepart: umount %s: %r\n", p->partdev);
 	close(p->f);
 	if(p->prev != nil)
 		p->prev = p->next;
@@ -411,7 +399,7 @@
 
 	qlock(&sv);
 	for(p = sv.ps; p != nil; p = p->next){
-		if(ext4_mount_point_stats(p->mnt, &s) < 0){
+		if(ext4_mount_point_stats(&p->mp, &s) < 0){
 			fprint(2, "%s: %r\n", p->partdev);
 		}else{
 			print(
@@ -447,7 +435,7 @@
 	Part *p;
 	qlock(&sv);
 	for(p = sv.ps; p != nil; p = p->next){
-		if(ext4_cache_flush(p->mnt) < 0)
+		if(ext4_cache_flush(&p->mp) < 0)
 			fprint(2, "%s: %r\n", p->partdev);
 	}
 	qunlock(&sv);