This patch introduces the Lanyard Filesystem (LanyFS), a filesystem
for highly mobile and removable storage devices.
Signed-off-by: Dan Luedtke <[email protected]>
---
"Release early, release often" they said. Here is my work of the
past weeks. This is a RFC patch, so please comment, criticize, suggest
and test test test. Of course patches are welcome, too.
LanyFS specification and further documentation as long as essential
filesystem utilities can be found at https://www.nonattached.net/lanyfs/
Dan
---
fs/Kconfig | 1 +
fs/Makefile | 5 +-
fs/lanyfs/Kconfig | 19 ++
fs/lanyfs/Makefile | 12 +
fs/lanyfs/btree.c | 405 +++++++++++++++++++++++++++++++++++
fs/lanyfs/chain.c | 173 +++++++++++++++
fs/lanyfs/dir.c | 393 ++++++++++++++++++++++++++++++++++
fs/lanyfs/extender.c | 352 +++++++++++++++++++++++++++++++
fs/lanyfs/file.c | 114 ++++++++++
fs/lanyfs/icache.c | 113 ++++++++++
fs/lanyfs/inode.c | 357 +++++++++++++++++++++++++++++++
fs/lanyfs/lanyfs_km.c | 60 ++++++
fs/lanyfs/lanyfs_km.h | 234 ++++++++++++++++++++
fs/lanyfs/lanyfs_lnx.h | 306 +++++++++++++++++++++++++++
fs/lanyfs/misc.c | 129 +++++++++++
fs/lanyfs/msg.c | 99 +++++++++
fs/lanyfs/super.c | 549 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/magic.h | 1 +
18 files changed, 3320 insertions(+), 2 deletions(-)
create mode 100644 fs/lanyfs/Kconfig
create mode 100644 fs/lanyfs/Makefile
create mode 100644 fs/lanyfs/btree.c
create mode 100644 fs/lanyfs/chain.c
create mode 100644 fs/lanyfs/dir.c
create mode 100644 fs/lanyfs/extender.c
create mode 100644 fs/lanyfs/file.c
create mode 100644 fs/lanyfs/icache.c
create mode 100644 fs/lanyfs/inode.c
create mode 100644 fs/lanyfs/lanyfs_km.c
create mode 100644 fs/lanyfs/lanyfs_km.h
create mode 100644 fs/lanyfs/lanyfs_lnx.h
create mode 100644 fs/lanyfs/misc.c
create mode 100644 fs/lanyfs/msg.c
create mode 100644 fs/lanyfs/super.c
diff --git a/fs/Kconfig b/fs/Kconfig
index f95ae3a..4001e30 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -206,6 +206,7 @@ source "fs/efs/Kconfig"
source "fs/jffs2/Kconfig"
# UBIFS File system configuration
source "fs/ubifs/Kconfig"
+source "fs/lanyfs/Kconfig"
source "fs/logfs/Kconfig"
source "fs/cramfs/Kconfig"
source "fs/squashfs/Kconfig"
diff --git a/fs/Makefile b/fs/Makefile
index 2fb9779..31f0642 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -3,7 +3,7 @@
#
# 14 Sep 2000, Christoph Hellwig <[email protected]>
# Rewritten to use lists instead of if-statements.
-#
+#
obj-y := open.o read_write.o file_table.o super.o \
char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
@@ -60,7 +60,7 @@ obj-y += devpts/
obj-$(CONFIG_PROFILING) += dcookies.o
obj-$(CONFIG_DLM) += dlm/
-
+
# Do not add any filesystems before this line
obj-$(CONFIG_FSCACHE) += fscache/
obj-$(CONFIG_REISERFS_FS) += reiserfs/
@@ -97,6 +97,7 @@ obj-$(CONFIG_NTFS_FS) += ntfs/
obj-$(CONFIG_UFS_FS) += ufs/
obj-$(CONFIG_EFS_FS) += efs/
obj-$(CONFIG_JFFS2_FS) += jffs2/
+obj-$(CONFIG_LANYFS_FS) += lanyfs/
obj-$(CONFIG_LOGFS) += logfs/
obj-$(CONFIG_UBIFS_FS) += ubifs/
obj-$(CONFIG_AFFS_FS) += affs/
diff --git a/fs/lanyfs/Kconfig b/fs/lanyfs/Kconfig
new file mode 100644
index 0000000..f4307bf
--- /dev/null
+++ b/fs/lanyfs/Kconfig
@@ -0,0 +1,19 @@
+config LANYFS_FS
+ tristate "Lanyard file system (LanyFS) (EXPERIMENTAL)"
+ help
+ The lanyard file system (LanyFS) is designed for removable storage
+ devices, particularly those small gadgets one would carry around
+ using a lanyard.
+
+ LanyFS is highly experimental.
+
+ If unsure, say N.
+
+config LANYFS_DEBUG
+ bool "LanyFS debugging support"
+ depends on LANYFS_FS
+ help
+ Enables debugging for the lanyard file system (LanyFS).
+
+ Selecting Y will produce a lot of kernel messages and it will slow
+ down the system while accessing LanyFS-formatted devices.
diff --git a/fs/lanyfs/Makefile b/fs/lanyfs/Makefile
new file mode 100644
index 0000000..f7adf51
--- /dev/null
+++ b/fs/lanyfs/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the linux lanyard filesystem routines.
+#
+
+obj-$(CONFIG_LANYFS_FS) += lanyfs.o
+
+lanyfs-y := msg.o misc.o icache.o inode.o \
+ btree.o dir.o extender.o file.o \
+ chain.o super.o lanyfs_km.o
+
+ccflags-$(CONFIG_LANYFS_DEBUG) += -DLANYFS_DEBUG
+
diff --git a/fs/lanyfs/btree.c b/fs/lanyfs/btree.c
new file mode 100644
index 0000000..024f48d
--- /dev/null
+++ b/fs/lanyfs/btree.c
@@ -0,0 +1,405 @@
+/*
+ * btree.c - Lanyard Filesystem Binary Tree Operations
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include "lanyfs_km.h"
+
+/**
+ * lanyfs_btree_any_link() - Returns left or right link of an inode.
+ * @inode: inode
+ *
+ * Left link will be preferred, returns 0 if no link is found.
+ */
+static lanyfs_blk_t lanyfs_btree_any_link(struct inode *inode)
+{
+ struct lanyfs_lii *lii;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = LANYFS_I(inode);
+ if (lii->left)
+ return lii->left;
+ else
+ return lii->right;
+}
+
+/**
+ * lanyfs_btree_set_left() - Sets left link of inode to given address.
+ * @inode: inode
+ * @addr: new target address of link
+ */
+static void lanyfs_btree_set_left(struct inode *inode, lanyfs_blk_t addr)
+{
+ struct lanyfs_lii *lii;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = LANYFS_I(inode);
+ spin_lock(&lii->lock);
+ lii->left = addr;
+ spin_unlock(&lii->lock);
+ mark_inode_dirty(inode);
+}
+
+/**
+ * lanyfs_btree_set_right() - Sets right link of inode to given address.
+ * @inode: inode
+ * @addr: new target address of link
+ */
+static void lanyfs_btree_set_right(struct inode *inode, lanyfs_blk_t addr)
+{
+ struct lanyfs_lii *lii;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = LANYFS_I(inode);
+ spin_lock(&lii->lock);
+ lii->right = addr;
+ spin_unlock(&lii->lock);
+ mark_inode_dirty(inode);
+}
+
+/**
+ * lanyfs_btree_rpl_link() - Replaces a link of an inode.
+ * @inode: inode
+ * @old: link to be replaced
+ * @new: replacement
+ *
+ * Only one link will be replaced even if both links match @old. Left link
+ * is always preferred.
+ */
+static void lanyfs_btree_rpl_link(struct inode *inode, lanyfs_blk_t old,
+ lanyfs_blk_t new)
+{
+ struct lanyfs_lii *lii;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = LANYFS_I(inode);
+ if (lii->left == old) {
+ spin_lock(&lii->lock);
+ lii->left = new;
+ spin_unlock(&lii->lock);
+ } else if (lii->right == old) {
+ spin_lock(&lii->lock);
+ lii->right = new;
+ spin_unlock(&lii->lock);
+ }
+ mark_inode_dirty(inode);
+}
+
+/**
+ * lanyfs_btree_set_subtree() - Sets subtree of a directory.
+ * @dir: directory to modify
+ * @addr: new address for subtree
+ */
+static void lanyfs_btree_set_subtree(struct inode *dir, lanyfs_blk_t addr)
+{
+ struct lanyfs_lii *lii;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = LANYFS_I(dir);
+ spin_lock(&lii->lock);
+ lii->subtree = addr;
+ spin_unlock(&lii->lock);
+ mark_inode_dirty(dir);
+}
+
+/**
+ * __lanyfs_btree_add_inode() - Adds an inode to a binary tree.
+ * @cur: current inode
+ * @rookie: inode to be added
+ *
+ * This function is internal and is best be called by its wrapper function.
+ */
+static int __lanyfs_btree_add_inode(struct inode *cur, struct inode *rookie)
+{
+ struct lanyfs_lii *lii_cur;
+ struct lanyfs_lii *lii_rki;
+ struct inode *tmp;
+ int cmp;
+ int ret;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii_cur = LANYFS_I(cur);
+ lii_rki = LANYFS_I(rookie);
+ cmp = strncmp(lii_cur->name, lii_rki->name, LANYFS_NAME_LENGTH);
+
+ /* insert node or dig deeper if necessary */
+ ret = 0;
+ if (cmp < 0) {
+ if (lii_cur->left) {
+ tmp = lanyfs_iget(cur->i_sb, lii_cur->left);
+ if (!tmp)
+ return -EINVAL;
+ ret = __lanyfs_btree_add_inode(tmp, rookie);
+ iput(tmp);
+ } else {
+ lanyfs_btree_set_left(cur, rookie->i_ino);
+ }
+ } else if (cmp > 0) {
+ if (lii_cur->right) {
+ tmp = lanyfs_iget(cur->i_sb, lii_cur->right);
+ if (!tmp)
+ return -EINVAL;
+ ret = __lanyfs_btree_add_inode(tmp, rookie);
+ iput(tmp);
+ } else {
+ lanyfs_btree_set_right(cur, rookie->i_ino);
+ }
+ } else {
+ ret = -EEXIST;
+ }
+ return ret;
+}
+
+/**
+ * lanyfs_btree_add_inode() - Adds an inode to a binary tree.
+ * @dir: directory to insert inode into
+ * @rookie: inode to be added
+ */
+int lanyfs_btree_add_inode(struct inode *dir, struct inode *rookie)
+{
+ struct lanyfs_lii *lii;
+ struct inode *root;
+ int ret;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = LANYFS_I(dir);
+ ret = 0;
+ if (lii->subtree) {
+ ret = -EINVAL;
+ root = lanyfs_iget(dir->i_sb, lii->subtree);
+ if (root) {
+ ret = __lanyfs_btree_add_inode(root, rookie);
+ iput(root);
+ }
+ } else {
+ lanyfs_btree_set_subtree(dir, rookie->i_ino);
+ }
+ return ret;
+}
+
+/**
+ * __lanyfs_btree_del_inode() - Removes an inode from a binary tree.
+ * @dir: directory containing the tree
+ * @par: parent inode of @cur in tree
+ * @cur: node to be tested for liquidation
+ * @name: directory or file name of inode to be removed
+ *
+ * This function is internal and is best be called by its wrapper function.
+ */
+static int __lanyfs_btree_del_inode(struct inode *dir, struct inode *par,
+ struct inode *cur, const char *name)
+{
+ struct inode *lm; /* left-most inode of right subtree */
+ struct inode *lmp; /* left-most's parent */
+ struct inode *tmp;
+ struct lanyfs_lii *lii_cur;
+ struct lanyfs_lii *lii_lm;
+ int cmp;
+ int ret;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lm = lmp = tmp = NULL;
+ lii_cur = LANYFS_I(cur);
+ cmp = strncmp(lii_cur->name, name, LANYFS_NAME_LENGTH);
+ if (cmp < 0 && lii_cur->left) {
+ /* object we are looking for must be in left subtree */
+ tmp = lanyfs_iget(dir->i_sb, lii_cur->left);
+ if (tmp) {
+ ret = __lanyfs_btree_del_inode(dir, cur, tmp, name);
+ iput(tmp);
+ return ret;
+ }
+ } else if (cmp > 0 && lii_cur->right) {
+ /* object we are looking for must be in right subtree */
+ tmp = lanyfs_iget(dir->i_sb, lii_cur->right);
+ if (tmp) {
+ ret = __lanyfs_btree_del_inode(dir, cur, tmp, name);
+ iput(tmp);
+ return ret;
+ }
+ } else if (cmp == 0 && !lii_cur->left && !lii_cur->right) {
+ /* case I: node is a leaf */
+ if (par) {
+ /* inode has parent */
+ lanyfs_btree_rpl_link(par, cur->i_ino, 0);
+ } else {
+ /* last inode in tree just died */
+ lanyfs_btree_set_subtree(dir, 0);
+ }
+ } else if (cmp == 0 && lii_cur->left && lii_cur->right) {
+ /* case II: node has two subtrees */
+
+ /* find leftmost in right subtree of p */
+ lmp = cur;
+ lm = lanyfs_iget(dir->i_sb, lii_cur->right);
+ if (!lm)
+ goto exit_clean;
+ lii_lm = LANYFS_I(lm);
+ while (lii_lm->left) {
+ if (lmp != cur)
+ iput(lmp);
+ lmp = lm;
+ lm = lanyfs_iget(dir->i_sb, lii_lm->left);
+ lii_lm = LANYFS_I(lm);
+ }
+
+ if (lmp != cur) {
+ /* get left-most's child */
+ tmp = lanyfs_iget(dir->i_sb, lanyfs_btree_any_link(lm));
+ if (!tmp)
+ goto exit_clean;
+ lanyfs_btree_rpl_link(lmp, lm->i_ino, tmp->i_ino);
+ lanyfs_btree_set_right(lm, lii_cur->right);
+ iput(tmp);
+ }
+ lanyfs_btree_set_left(lm, lii_cur->left);
+
+ if (par)
+ lanyfs_btree_rpl_link(par, cur->i_ino, lm->i_ino);
+ else
+ lanyfs_btree_set_subtree(dir, lm->i_ino);
+ iput(lm);
+ } else if (cmp == 0 && (lii_cur->left || lii_cur->right)) {
+ /* case III: node has one subtree */
+ tmp = lanyfs_iget(dir->i_sb, lanyfs_btree_any_link(cur));
+ if (!tmp)
+ goto exit_clean;
+ if (par)
+ lanyfs_btree_rpl_link(par, cur->i_ino, tmp->i_ino);
+ else
+ lanyfs_btree_set_subtree(dir, tmp->i_ino);
+ iput(tmp);
+ } else {
+ return -ENOENT;
+ }
+ return 0;
+exit_clean:
+ if (tmp)
+ iput(tmp);
+ if (lmp && lmp != cur)
+ iput(lmp);
+ if (lm)
+ iput(lm);
+ return -ENOENT;
+}
+
+/**
+ * lanyfs_btree_del_inode() - Removes an inode from a binary tree.
+ * @dir: directory containing victim inode
+ * @name: directory or file name of inode to be removed
+ *
+ * Deleting a node from a binary tree often leads to resorting the tree.
+ * Sometimes the root node changes, and this is why we have @dir as argument.
+ * It will automatically be updated by this function to ensure proper directory
+ * listings and overall consistency.
+ */
+int lanyfs_btree_del_inode(struct inode *dir, const char *name)
+{
+ struct lanyfs_lii *lii;
+ struct inode *root;
+ int ret;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ ret = -ENOENT;
+ lii = LANYFS_I(dir);
+ if (lii->subtree) {
+ root = lanyfs_iget(dir->i_sb, lii->subtree);
+ if (root) {
+ ret = __lanyfs_btree_del_inode(dir, NULL, root, name);
+ iput(root);
+ } else {
+ ret = -ENOMEM;
+ }
+ }
+ return ret;
+}
+
+/**
+ * __lanyfs_btree_lookup() - Looks up a inode in a binary tree by name.
+ * @cur: current inode in binary search tree
+ * @name: name to look up
+ *
+ * This function is internal and is best be called by its wrapper function.
+ */
+static struct inode *__lanyfs_btree_lookup(struct inode *cur, const char *name)
+{
+ struct lanyfs_lii *lii;
+ struct inode *next;
+ struct inode *ret;
+ int cmp;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = LANYFS_I(cur);
+ cmp = strncmp(lii->name, name, LANYFS_NAME_LENGTH);
+ ret = NULL;
+ if (cmp < 0 && lii->left) {
+ /* left tree */
+ next = lanyfs_iget(cur->i_sb, lii->left);
+ if (next) {
+ ret = __lanyfs_btree_lookup(next, name);
+ if (ret != next)
+ iput(next);
+ }
+ } else if (cmp > 0 && lii->right) {
+ /* right tree */
+ next = lanyfs_iget(cur->i_sb, lii->right);
+ if (next) {
+ ret = __lanyfs_btree_lookup(next, name);
+ if (ret != next)
+ iput(next);
+ }
+ } else if (cmp == 0) {
+ /* we found it */
+ return cur;
+ }
+ return ret;
+}
+
+/**
+ * lanyfs_btree_lookup() - Looks up an inode in a directory by name.
+ * @dir: directory to look into
+ * @name: name to look up
+ *
+ * Returns inode with increased reference count.
+ */
+struct inode *lanyfs_btree_lookup(struct inode *dir, const char *name)
+{
+ struct lanyfs_lii *lii;
+ struct inode *root;
+ struct inode *ret;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = LANYFS_I(dir);
+ if (lii->subtree) {
+ root = lanyfs_iget(dir->i_sb, lii->subtree);
+ if (root) {
+ ret = __lanyfs_btree_lookup(root, name);
+ if (ret != root)
+ iput(root);
+ return ret;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * lanyfs_btree_clear_inode() - Sets all links of an inode to 0.
+ * @inode: inode to clear
+ */
+void lanyfs_btree_clear_inode(struct inode *inode)
+{
+ struct lanyfs_lii *lii;
+ lanyfs_debug_function(__FILE__, __func__);
+ lii = LANYFS_I(inode);
+ spin_lock(&lii->lock);
+ lii->left = 0;
+ lii->right = 0;
+ spin_unlock(&lii->lock);
+ mark_inode_dirty(inode);
+}
diff --git a/fs/lanyfs/chain.c b/fs/lanyfs/chain.c
new file mode 100644
index 0000000..ada35d9
--- /dev/null
+++ b/fs/lanyfs/chain.c
@@ -0,0 +1,173 @@
+/*
+ * chain.c - Lanyard Filesystem Chain Operations
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include "lanyfs_km.h"
+
+/**
+ * lanyfs_chain_set_next() - Sets a chain block's successor.
+ * @sb: superblock
+ * @addr: address of chain block to manipulate
+ * @next: address of next chain block in chain
+ */
+int lanyfs_chain_set_next(struct super_block *sb, lanyfs_blk_t addr,
+ lanyfs_blk_t next)
+{
+ struct buffer_head *bh;
+ struct lanyfs_chain *chain;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ bh = sb_bread(sb, addr);
+ if (unlikely(!bh)) {
+ lanyfs_err(sb, "block #%llu read error", (u64) addr);
+ return -EIO;
+ }
+ chain = (struct lanyfs_chain *) bh->b_data;
+ lock_buffer(bh);
+ chain->next = next;
+ le16_add_cpu(&chain->wrcnt, 1);
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ if (LANYFS_SB(sb)->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ return 0;
+}
+
+/**
+ * lanyfs_chain_create() - Creates a new chain block.
+ * @sb: superblock
+ * @addr: address of empty block
+ */
+int lanyfs_chain_create(struct super_block *sb, lanyfs_blk_t addr)
+{
+ struct lanyfs_fsi *fsi;
+ struct buffer_head *bh;
+ struct lanyfs_chain *chain;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ fsi = LANYFS_SB(sb);
+ bh = sb_bread(sb, addr);
+ if (unlikely(!bh)) {
+ lanyfs_err(sb, "block #%llu read error", (u64) addr);
+ return -EIO;
+ }
+ chain = (struct lanyfs_chain *) bh->b_data;
+ lock_buffer(bh);
+ memset(chain, 0x00, 1 << fsi->blocksize);
+ chain->type = LANYFS_TYPE_CHAIN;
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ if (LANYFS_SB(sb)->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ return 0;
+}
+
+/**
+ * lanyfs_chain_pop() - Gets address in first non-empty slot of a chain block.
+ * @sb: superblock
+ * @addr: address of chain block to read
+ * @res: result (address popped from chain block)
+ */
+int lanyfs_chain_pop(struct super_block *sb, lanyfs_blk_t addr,
+ lanyfs_blk_t *res)
+{
+ struct lanyfs_fsi *fsi;
+ struct buffer_head *bh;
+ struct lanyfs_chain *chain;
+ unsigned char *p;
+ unsigned char *end;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ fsi = LANYFS_SB(sb);
+ bh = sb_bread(sb, addr);
+ if (unlikely(!bh)) {
+ lanyfs_err(sb, "block #%llu read error", (u64) addr);
+ return -EIO;
+ }
+ chain = (struct lanyfs_chain *) bh->b_data;
+ p = &chain->stream;
+ end = p + ((fsi->chainmax - 1) * fsi->addrlen);
+ lock_buffer(bh);
+ while (p < end) {
+ *res = 0;
+ memcpy(res, p, fsi->addrlen);
+ *res = le64_to_cpu(*res);
+ if (*res) {
+ memset(p, 0x00, fsi->addrlen);
+ le16_add_cpu(&chain->wrcnt, 1);
+ break;
+ }
+ p += fsi->addrlen;
+ }
+ unlock_buffer(bh);
+ if (!*res) {
+ *res = le64_to_cpu(chain->next);
+ bforget(bh);
+ return -LANYFS_ENOTAKEN;
+ }
+ mark_buffer_dirty(bh);
+ if (fsi->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ return 0;
+}
+
+/**
+ * lanyfs_chain_push() - Stores an address at first empty slot of a chain block.
+ * @sb: superblock
+ * @addr: address of chain block to manipulate
+ * @rookie: address to store
+ */
+int lanyfs_chain_push(struct super_block *sb, lanyfs_blk_t addr,
+ lanyfs_blk_t rookie)
+{
+ struct lanyfs_fsi *fsi;
+ struct buffer_head *bh;
+ struct lanyfs_chain *chain;
+ unsigned char *p;
+ unsigned char *end;
+ lanyfs_blk_t tmp;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ fsi = LANYFS_SB(sb);
+ bh = sb_bread(sb, addr);
+ if (unlikely(!bh)) {
+ lanyfs_err(sb, "block #%llu read error", (u64) addr);
+ return -EIO;
+ }
+ chain = (struct lanyfs_chain *) bh->b_data;
+ p = &chain->stream;
+ end = p + ((fsi->chainmax - 1) * fsi->addrlen);
+ lock_buffer(bh);
+ while (p < end) {
+ tmp = 0;
+ memcpy(&tmp, p, fsi->addrlen);
+ tmp = le64_to_cpu(tmp);
+ if (!tmp) {
+ rookie = cpu_to_le64(rookie);
+ memcpy(p, &rookie, fsi->addrlen);
+ le16_add_cpu(&chain->wrcnt, 1);
+ break;
+ }
+ p += fsi->addrlen;
+ }
+ unlock_buffer(bh);
+ if (tmp) {
+ bforget(bh);
+ return -LANYFS_ENOEMPTY;
+ }
+ mark_buffer_dirty(bh);
+ if (fsi->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ return 0;
+}
diff --git a/fs/lanyfs/dir.c b/fs/lanyfs/dir.c
new file mode 100644
index 0000000..76b31d1
--- /dev/null
+++ b/fs/lanyfs/dir.c
@@ -0,0 +1,393 @@
+/*
+ * dir.c - Lanyard Filesystem Directory Operations
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include "lanyfs_km.h"
+
+/**
+ * lanyfs_empty() - Test wether a directory is empty or not.
+ * @inode: directory inode to test
+ */
+static int lanyfs_empty(struct inode *inode)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ if (unlikely(!inode) || unlikely(!S_ISDIR(inode->i_mode)))
+ return 0;
+ return !LANYFS_I(inode)->subtree;
+}
+
+/**
+ * __lanyfs_readdir() - Lists directory contents using recursion.
+ * @n: root node of tree to list
+ * @fp: file pointer
+ * @dirent: pointer to directory entry
+ * @filldir: function pointer, provides function 'filldir'
+ *
+ * This function is internal and is best be called by its wrapper function.
+ */
+static int __lanyfs_readdir(lanyfs_blk_t n, struct file *fp, void *dirent,
+ filldir_t filldir)
+{
+ struct inode *inode;
+ struct lanyfs_lii *lii;
+ int err;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ /* this entry */
+ err = 0;
+ inode = lanyfs_iget(fp->f_dentry->d_sb, n);
+ if (!inode)
+ return -ENOMEM;
+ lii = LANYFS_I(inode);
+ err = filldir(dirent, lii->name, lii->len, fp->f_pos, inode->i_ino,
+ (inode->i_mode >> 12) & 0xF);
+ if (err)
+ goto exit_err;
+ fp->f_pos++;
+ /* left entry */
+ if (lii->left)
+ __lanyfs_readdir(lii->left, fp, dirent, filldir);
+ /* right entry */
+ if (lii->right)
+ __lanyfs_readdir(lii->right, fp, dirent, filldir);
+exit_err:
+ iput(inode);
+ return err;
+}
+
+/**
+ * lanyfs_readdir() - Lists directory contents.
+ * @fp: file pointer
+ * @dirent: pointer to directory entry
+ * @filldir: function pointer, provides function 'filldir'
+ */
+static int lanyfs_readdir(struct file *fp, void *dirent, filldir_t filldir)
+{
+ ino_t ino;
+ lanyfs_blk_t subtree;
+ int err;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ switch (fp->f_pos) {
+ case 0:
+ /* this dir */
+ ino = fp->f_dentry->d_inode->i_ino;
+ err = filldir(dirent, ".", 1, fp->f_pos, ino, DT_DIR);
+ if (err)
+ return err;
+ fp->f_pos++;
+ /* fall through */
+ case 1:
+ /* parent dir */
+ ino = parent_ino(fp->f_dentry);
+ err = filldir(dirent, "..", 2, fp->f_pos, ino, DT_DIR);
+ if (err)
+ return err;
+ fp->f_pos++;
+ /* fall through */
+ case 2:
+ /* this dir's entries */
+ subtree = LANYFS_I(fp->f_dentry->d_inode)->subtree;
+ if (subtree)
+ __lanyfs_readdir(subtree, fp, dirent, filldir);
+ break;
+ default:
+ return -ENOENT;
+ break;
+ }
+ return 0;
+}
+
+/**
+ * lanyfs_mkdir() - Creates a new directory.
+ * @pdir: current directory
+ * @dentry: directory to create
+ * @mode: requested mode of new directory
+ */
+static int lanyfs_mkdir(struct inode *pdir, struct dentry *dentry, umode_t mode)
+{
+ struct super_block *sb;
+ lanyfs_blk_t addr;
+ struct buffer_head *bh;
+ struct lanyfs_dir *dir;
+ struct inode *inode;
+ int err;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ /* length check */
+ if (dentry->d_name.len >= LANYFS_NAME_LENGTH)
+ return -ENAMETOOLONG;
+
+ sb = pdir->i_sb;
+ /* get free block */
+ addr = lanyfs_enslave(sb);
+ if (!addr)
+ return -ENOSPC;
+
+ /* create directory block */
+ bh = sb_bread(sb, addr);
+
+ if (!bh) {
+ lanyfs_err(sb, "error reading block #%llu", (u64) addr);
+ err = -EIO;
+ goto exit_release;
+ }
+ dir = (struct lanyfs_dir *) bh->b_data;
+
+ lock_buffer(bh);
+ memset(bh->b_data, 0x00, 1 << LANYFS_SB(sb)->blocksize);
+ dir->type = LANYFS_TYPE_DIR;
+ lanyfs_time_lts_now(&dir->meta.created);
+ dir->meta.modified = dir->meta.created;
+ dir->meta.attr = lanyfs_mode_to_attr(mode, 0);
+ strncpy(dir->meta.name, dentry->d_name.name, LANYFS_NAME_LENGTH - 1);
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ if (LANYFS_SB(sb)->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+
+ inode = lanyfs_iget(sb, addr);
+ if (!inode) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+ err = lanyfs_btree_add_inode(pdir, inode);
+ if (err)
+ goto exit_ino;
+ inc_nlink(inode);
+ d_instantiate(dentry, inode);
+ mark_inode_dirty(inode);
+ return 0;
+
+exit_ino:
+ drop_nlink(inode);
+ iput(inode);
+exit_release:
+ lanyfs_release(sb, addr);
+ return err;
+}
+
+/**
+ * lanyfs_rmdir() - Deletes a directory.
+ * @dir: parent directory
+ * @dentry: directory to remove
+ */
+static int lanyfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ lanyfs_blk_t addr;
+ int err;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ /* length check */
+ if (dentry->d_name.len >= LANYFS_NAME_LENGTH)
+ return -ENAMETOOLONG;
+
+ /* empty check */
+ if (!lanyfs_empty(dentry->d_inode))
+ return -ENOTEMPTY;
+
+ addr = dentry->d_inode->i_ino;
+
+ /* remove block from binary tree */
+ err = lanyfs_btree_del_inode(dir, dentry->d_name.name);
+ if (err)
+ return err;
+ drop_nlink(dir);
+ clear_nlink(dentry->d_inode);
+ d_delete(dentry);
+
+ /* set block free */
+ lanyfs_release(dir->i_sb, addr);
+ return 0;
+}
+
+/**
+ * lanyfs_unlink() - Deletes a file.
+ * @dir: containing directory
+ * @dentry: directory entry to remove
+ */
+static int lanyfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct super_block *sb;
+ struct inode *inode;
+ lanyfs_blk_t addr;
+ int err;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ sb = dir->i_sb;
+ inode = dentry->d_inode;
+ addr = inode->i_ino;
+
+ /* free space used by inode */
+ err = vmtruncate(inode, 0);
+ if (err)
+ return err;
+
+ err = lanyfs_btree_del_inode(dir, dentry->d_name.name);
+ if (err)
+ return err;
+
+ drop_nlink(inode);
+ lanyfs_inode_poke(dir);
+ lanyfs_release(sb, addr);
+ return 0;
+}
+
+/**
+ * lanyfs_rename() - Renames and/or moves a directory or file.
+ * @old_dir: old directory
+ * @old_dentry: old directory entry
+ * @new_dir: new directory
+ * @new_dentry: new directory entry
+ *
+ * Case I:
+ * Just rename a/foo to a/bar.
+ *
+ * Case II:
+ * Just move a/foo to b/foo.
+ *
+ * Case III:
+ * Rename and move a/foo to b/bar.
+ *
+ * Caution:
+ * Operations may overwrite existing objects!
+ */
+static int lanyfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct super_block *sb;
+ const char *old_name;
+ const char *new_name;
+ struct inode *old_inode;
+ struct inode *new_inode;
+ int err;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ sb = old_dir->i_sb;
+ old_name = old_dentry->d_name.name;
+ new_name = new_dentry->d_name.name;
+ old_inode = old_dentry->d_inode;
+ new_inode = new_dentry->d_inode;
+
+ /* remove target if it exists */
+ if (new_inode) {
+ if (S_ISDIR(old_inode->i_mode)) {
+ if (!lanyfs_empty(new_inode))
+ return -ENOTEMPTY;
+ lanyfs_rmdir(new_dir, new_dentry);
+ } else {
+ lanyfs_unlink(new_dir, new_dentry);
+ }
+ }
+
+ /* remove node from old binary tree */
+ lanyfs_btree_del_inode(old_dir, old_name);
+ lanyfs_btree_clear_inode(old_inode);
+
+ /* change name */
+ lanyfs_inode_rename(old_inode, new_name);
+
+ /* add node to new binary tree */
+ err = lanyfs_btree_add_inode(new_dir, old_inode);
+ if (err)
+ return err;
+ lanyfs_inode_poke(old_inode);
+ lanyfs_inode_poke(old_dir);
+ lanyfs_inode_poke(new_dir);
+ return 0;
+}
+
+/**
+ * lanyfs_create() - Creates a new file.
+ * @dir: parent directory
+ * @dentry: directory entry of file to create
+ * @mode: file mode
+ * @excl: exclusive flag
+ *
+ * Creates a new file in @dir with mode @mode and the name requested in @dentry.
+ * @excl is ignored by LanyFS. There are not many filesystems
+ * using the flag at all. This was nameidata before 3.5 which LanyFS
+ * did not use either.
+ */
+static int lanyfs_create(struct inode *dir, struct dentry *dentry,
+ umode_t mode, bool excl)
+{
+ struct super_block *sb;
+ struct lanyfs_fsi *fsi;
+ lanyfs_blk_t addr;
+ struct buffer_head *bh;
+ struct inode *inode;
+ struct lanyfs_file *file;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ sb = dir->i_sb;
+ fsi = LANYFS_SB(sb);
+
+ /* length check */
+ if (dentry->d_name.len >= LANYFS_NAME_LENGTH)
+ return -ENAMETOOLONG;
+
+ /* get free block */
+ addr = lanyfs_enslave(sb);
+ if (!addr)
+ return -ENOSPC;
+
+ /* create file block */
+ bh = sb_bread(sb, addr);
+ if (!bh) {
+ lanyfs_err(sb, "error reading block #%llu", (u64) addr);
+ return -EIO;
+ }
+ file = (struct lanyfs_file *) bh->b_data;
+ lock_buffer(bh);
+ memset(bh->b_data, 0x00, 1 << fsi->blocksize);
+ file->type = LANYFS_TYPE_FILE;
+ lanyfs_time_lts_now(&file->meta.created);
+ file->meta.modified = file->meta.created;
+ file->meta.attr = lanyfs_mode_to_attr(mode, 0);
+ strncpy(file->meta.name, dentry->d_name.name, LANYFS_NAME_LENGTH - 1);
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ if (fsi->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+
+ /* VFS */
+ inode = lanyfs_iget(sb, addr);
+ if (!inode) {
+ dput(dentry);
+ drop_nlink(inode);
+ iput(inode);
+ lanyfs_release(sb, addr);
+ return -ENOMEM;
+ }
+ lanyfs_btree_add_inode(dir, inode);
+ d_instantiate(dentry, inode);
+ mark_inode_dirty(inode);
+ return 0;
+}
+
+/* lanyfs dir operations */
+const struct file_operations lanyfs_dir_operations = {
+ .readdir = lanyfs_readdir,
+};
+
+/* lanyfs dir inode operations */
+const struct inode_operations lanyfs_dir_inode_operations = {
+ .lookup = lanyfs_lookup,
+ .create = lanyfs_create,
+ .mkdir = lanyfs_mkdir,
+ .rmdir = lanyfs_rmdir,
+ .rename = lanyfs_rename,
+ .unlink = lanyfs_unlink,
+};
+
diff --git a/fs/lanyfs/extender.c b/fs/lanyfs/extender.c
new file mode 100644
index 0000000..6dcd378
--- /dev/null
+++ b/fs/lanyfs/extender.c
@@ -0,0 +1,352 @@
+/*
+ * extender.c - Lanyard Filesystem Extender Operations
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include "lanyfs_km.h"
+
+/**
+ * intpow() - Simple exponentiation function.
+ * @b: base
+ * @n: exponent
+ *
+ * This function does not cover special cases where @n equals zero and @b does
+ * not equal zero.
+ */
+static inline int intpow(int b, int n)
+{
+ if (!n)
+ return 1;
+ while (n--)
+ b *= b;
+ return b;
+}
+
+/**
+ * lanyfs_ext_get_slot() - Returns the address stored in an extender block slot.
+ * @ext: extender block to read from
+ * @addrlen: address length
+ * @slot: number of slot to read
+ */
+static inline lanyfs_blk_t lanyfs_ext_get_slot(struct lanyfs_ext *ext,
+ unsigned int addrlen,
+ unsigned int slot)
+{
+ lanyfs_blk_t addr;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ addr = 0;
+ memcpy(&addr, &ext->stream + (slot * addrlen), addrlen);
+ return le64_to_cpu(addr);
+}
+
+/**
+ * lanyfs_ext_set_slot() - Stores an address in an extender block slot.
+ * @ext: extender block to write to
+ * @addrlen: address length
+ * @slot: number of slot to write
+ * @addr: address to store
+ */
+static inline void lanyfs_ext_set_slot(struct lanyfs_ext *ext,
+ unsigned int addrlen,
+ unsigned int slot, lanyfs_blk_t addr)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ addr = cpu_to_le64(addr);
+ memcpy(&ext->stream + (slot * addrlen), &addr, addrlen);
+}
+
+/**
+ * lanyfs_ext_kill_slot() - Resets the slot of an extender block to zero.
+ * @ext: extender block to write to
+ * @addrlen: address length
+ * @slot: number of slot to kill
+ */
+static inline void lanyfs_ext_kill_slot(struct lanyfs_ext *ext,
+ unsigned int addrlen,
+ unsigned int slot)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ memset(&ext->stream + (slot * addrlen), 0x00, addrlen);
+}
+
+/**
+ * lanyfs_ext_iblock() - Gets the address of data block in a file.
+ * @sb: superblock
+ * @addr: address of extender block to read
+ * @iblock: file-internal block number
+ * @res: pointer to result storage
+ *
+ * Mapping a file-internal block (called iblock) to the correct on-disk block
+ * requires reading its address from an extender block. Larger files use
+ * multiple levels of extender blocks, so this function sometimes calls itselfs
+ * when going down extender blocks level by level. On-disk addresses are always
+ * stored in extender blocks of level 0. Once the on-disk address is found it is
+ * saved to @res.
+ */
+int lanyfs_ext_iblock(struct super_block *sb, lanyfs_blk_t addr,
+ lanyfs_blk_t iblock, lanyfs_blk_t *res)
+{
+ struct lanyfs_fsi *fsi;
+ struct buffer_head *bh;
+ struct lanyfs_ext *ext;
+ unsigned int slot;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ if (unlikely(!addr))
+ return -EINVAL;
+
+ fsi = LANYFS_SB(sb);
+ bh = sb_bread(sb, addr);
+ if (unlikely(!bh)) {
+ lanyfs_err(sb, "block #%llu read error", (u64) addr);
+ return -EIO;
+ }
+ ext = (struct lanyfs_ext *) bh->b_data;
+ if (ext->level) {
+ slot = (iblock / intpow(fsi->extmax, ext->level)) % fsi->extmax;
+ addr = lanyfs_ext_get_slot(ext, fsi->addrlen, slot);
+ brelse(bh);
+ if (addr)
+ return lanyfs_ext_iblock(sb, addr, iblock, res);
+ return -EINVAL;
+ }
+ *res = lanyfs_ext_get_slot(ext, fsi->addrlen, (iblock % fsi->extmax));
+ brelse(bh);
+ return 0;
+}
+
+/**
+ * lanyfs_ext_truncate() - Sets the on-disk size of a file.
+ * @sb: superblock
+ * @addr: address of extender block to read
+ * @iblock: new size in file-internal blocks
+ *
+ * Once again recursion is used to walk through all levels of extender blocks.
+ * Blocks that are not needed anymore are returned to the free blocks pool by
+ * this function. This is the lowest level of file size changes and usually
+ * happens after VFS has already truncated the file's in-memory representation.
+ */
+int lanyfs_ext_truncate(struct super_block *sb, lanyfs_blk_t addr,
+ lanyfs_blk_t iblock)
+{
+ struct lanyfs_fsi *fsi;
+ struct buffer_head *bh;
+ struct lanyfs_ext *ext;
+ unsigned int slot;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ if (unlikely(!addr))
+ return -EINVAL;
+
+ fsi = LANYFS_SB(sb);
+ bh = sb_bread(sb, addr);
+ if (unlikely(!bh)) {
+ lanyfs_err(sb, "block #%llu read error", (u64) addr);
+ return -EIO;
+ }
+ ext = (struct lanyfs_ext *) bh->b_data;
+ slot = (iblock / intpow(fsi->extmax, ext->level)) % fsi->extmax;
+ lock_buffer(bh);
+ while (slot < fsi->extmax) {
+ addr = lanyfs_ext_get_slot(ext, fsi->addrlen, slot);
+ if (addr) {
+ if (ext->level) {
+ /* barrier below this slot */
+ lanyfs_ext_truncate(sb, addr, iblock);
+ iblock = 0;
+ } else {
+ lanyfs_ext_kill_slot(ext, fsi->addrlen, slot);
+ lanyfs_release(sb, addr);
+ }
+ }
+ slot++;
+ }
+ le16_add_cpu(&ext->wrcnt, 1);
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ if (fsi->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ return 0;
+}
+
+/**
+ * lanyfs_ext_create() - Creates a new extender block.
+ * @sb: superblock
+ * @level: level of new extender block
+ */
+lanyfs_blk_t lanyfs_ext_create(struct super_block *sb, unsigned short level)
+{
+ struct lanyfs_fsi *fsi;
+ struct buffer_head *bh;
+ struct lanyfs_ext *ext;
+ lanyfs_blk_t addr;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ fsi = LANYFS_SB(sb);
+
+ addr = lanyfs_enslave(sb);
+ if (!addr)
+ return 0;
+ bh = sb_bread(sb, addr);
+ if (unlikely(!bh)) {
+ lanyfs_err(sb, "block #%llu read error", (u64) addr);
+ return 0;
+ }
+ ext = (struct lanyfs_ext *) bh->b_data;
+ lock_buffer(bh);
+ memset(ext, 0x00, 1 << fsi->blocksize);
+ ext->type = LANYFS_TYPE_EXT;
+ ext->level = level;
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ if (fsi->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ return addr;
+}
+
+/**
+ * __lanyfs_ext_grow() - Increases the on-disk size of a file.
+ * @sb: superblock
+ * @addr: address of extender block to start at
+ *
+ * This function is internal and is best be called by its wrapper function.
+ */
+static int __lanyfs_ext_grow(struct super_block *sb, lanyfs_blk_t addr)
+{
+ struct lanyfs_fsi *fsi;
+ struct buffer_head *bh;
+ struct lanyfs_ext *ext;
+ unsigned int slot;
+ lanyfs_blk_t new;
+ lanyfs_blk_t tmp;
+ int ret;
+ lanyfs_debug_function(__FILE__, __func__);
+
+
+ if (unlikely(!addr))
+ return -LANYFS_EPROTECTED;
+
+ fsi = LANYFS_SB(sb);
+ ret = -LANYFS_ENOTAKEN;
+ bh = sb_bread(sb, addr);
+ if (unlikely(!bh)) {
+ lanyfs_err(sb, "block #%llu read error", (u64) addr);
+ return -EIO;
+ }
+ ext = (struct lanyfs_ext *) bh->b_data;
+ if (ext->level) {
+ for (slot = fsi->extmax; slot; slot--) {
+ tmp = lanyfs_ext_get_slot(ext, fsi->addrlen, slot - 1);
+ if (!tmp && slot > 1)
+ continue;
+ ret = __lanyfs_ext_grow(sb, tmp);
+ if (ret != -LANYFS_ENOEMPTY)
+ goto exit_ret;
+ if (slot >= fsi->extmax) {
+ ret = -LANYFS_ENOEMPTY;
+ goto exit_ret;
+ }
+ new = lanyfs_ext_create(sb, ext->level - 1);
+ if (!new) {
+ ret = -ENOSPC;
+ goto exit_ret;
+ }
+ lock_buffer(bh);
+ lanyfs_ext_set_slot(ext, fsi->addrlen, slot, new);
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ slot++;
+ }
+ } else {
+ for (slot = 0; slot < fsi->extmax; slot++) {
+ tmp = lanyfs_ext_get_slot(ext, fsi->addrlen, slot);
+ if (tmp)
+ continue;
+ new = lanyfs_enslave(sb);
+ if (!new) {
+ ret = -ENOSPC;
+ goto exit_ret;
+ }
+ lock_buffer(bh);
+ lanyfs_ext_set_slot(ext, fsi->addrlen, slot, new);
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ ret = 0;
+ goto exit_ret;
+ }
+ ret = -LANYFS_ENOEMPTY;
+ }
+exit_ret:
+ if (fsi->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ return ret;
+}
+
+/**
+ * lanyfs_ext_grow() - Increases the on-disk size of a file by one block.
+ * @sb: superblock
+ * @addr: address of top-level extender block
+ *
+ * If all slots of all extender blocks of a file are occupied, a new level of
+ * extender blocks has to be introduced. The new level extender block becomes
+ * the new entry point thus changing the corresponding inodes private data. If
+ * a new entry point is created, its address is stored in @addr. Upper layer
+ * functions must update inode private data accordingly.
+ */
+int lanyfs_ext_grow(struct super_block *sb, lanyfs_blk_t *addr)
+{
+ struct lanyfs_fsi *fsi;
+ struct buffer_head *bh;
+ struct lanyfs_ext *ext;
+ lanyfs_blk_t new;
+ int ret;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ if (unlikely(!*addr))
+ return -LANYFS_EPROTECTED;
+
+ fsi = LANYFS_SB(sb);
+ ret = __lanyfs_ext_grow(sb, *addr);
+
+ if (ret == -LANYFS_ENOEMPTY) {
+ /* all extender blocks are occupied, go one level up */
+ bh = sb_bread(sb, *addr);
+ if (unlikely(!bh)) {
+ lanyfs_err(sb, "block #%llu read error", (u64) *addr);
+ return -EIO;
+ }
+ ext = (struct lanyfs_ext *) bh->b_data;
+ brelse(bh);
+
+ new = lanyfs_ext_create(sb, ext->level + 1);
+ if (!new)
+ return -ENOSPC;
+ bh = sb_bread(sb, new);
+ if (unlikely(!bh)) {
+ lanyfs_err(sb, "block #%llu read error", (u64) new);
+ return -EIO;
+ }
+ ext = (struct lanyfs_ext *) bh->b_data;
+ lock_buffer(bh);
+ lanyfs_ext_set_slot(ext, fsi->addrlen, 0, *addr);
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ if (fsi->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ *addr = new;
+ }
+ return ret;
+}
diff --git a/fs/lanyfs/file.c b/fs/lanyfs/file.c
new file mode 100644
index 0000000..236f4fe
--- /dev/null
+++ b/fs/lanyfs/file.c
@@ -0,0 +1,114 @@
+/*
+ * file.c - Lanyard Filesystem File Operations
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include "lanyfs_km.h"
+
+/**
+ * lanyfs_getblk() - Maps a file-internal block to a on-disk block.
+ * @inode: file inode
+ * @iblock: file-internal block
+ * @bh_result: buffer head for result
+ * @create: create the requested block
+ */
+static int lanyfs_getblk(struct inode *inode, sector_t iblock,
+ struct buffer_head *bh_result, int create)
+{
+ struct super_block *sb;
+ struct lanyfs_fsi *fsi;
+ struct lanyfs_lii *lii;
+ lanyfs_blk_t addr;
+ int err;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ sb = inode->i_sb;
+ fsi = LANYFS_SB(sb);
+ lii = LANYFS_I(inode);
+
+ if (!lii->data) {
+ spin_lock(&lii->lock);
+ lii->data = lanyfs_ext_create(sb, 0);
+ spin_unlock(&lii->lock);
+ if (!lii->data)
+ return -ENOSPC;
+ }
+ if (create) {
+ spin_lock(&lii->lock);
+ lanyfs_ext_grow(sb, &lii->data);
+ spin_unlock(&lii->lock);
+ set_buffer_new(bh_result);
+ inode_add_bytes(inode, sb->s_blocksize);
+ mark_inode_dirty(inode);
+ }
+ err = lanyfs_ext_iblock(sb, lii->data, iblock, &addr);
+ if (err)
+ return err;
+ map_bh(bh_result, sb, addr);
+ return 0;
+}
+
+/**
+ * lanyfs_writepage() - Writes a full page to disk.
+ * @page: page to write
+ * @wbc: writeback control
+ */
+static int lanyfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ return block_write_full_page(page, lanyfs_getblk, wbc);
+}
+
+/**
+ * lanyfs_readpage() - Reads a full page from disk.
+ * @fp: file pointer
+ * @page: page to read
+ */
+static int lanyfs_readpage(struct file *fp, struct page *page)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ return block_read_full_page(page, lanyfs_getblk);
+}
+
+/**
+ * lanyfs_bmap() - Maps an on-disk block to a page.
+ * @mapping: address space mapping information
+ * @block: block to map
+ */
+static sector_t lanyfs_bmap(struct address_space *mapping, sector_t block)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ return generic_block_bmap(mapping, block, lanyfs_getblk);
+}
+
+/* lanyfs address space operations */
+const struct address_space_operations lanyfs_address_space_operations = {
+ .readpage = lanyfs_readpage,
+ .writepage = lanyfs_writepage,
+ .write_begin = simple_write_begin,
+ .write_end = simple_write_end,
+ .bmap = lanyfs_bmap,
+};
+
+/* lanyfs file operations */
+const struct file_operations lanyfs_file_operations = {
+ .open = generic_file_open,
+ .read = do_sync_read,
+ .write = do_sync_write,
+ .aio_read = generic_file_aio_read,
+ .aio_write = generic_file_aio_write,
+ .mmap = generic_file_mmap,
+ .fsync = generic_file_fsync,
+ .splice_read = generic_file_splice_read,
+/* .release = generic_file_release, */
+ .llseek = generic_file_llseek,
+};
diff --git a/fs/lanyfs/icache.c b/fs/lanyfs/icache.c
new file mode 100644
index 0000000..25641bd
--- /dev/null
+++ b/fs/lanyfs/icache.c
@@ -0,0 +1,113 @@
+/*
+ * icache.c - Lanyard Filesystem Inode Cache
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include "lanyfs_km.h"
+
+/**
+ * DOC: LanyFS Inode Cache
+ *
+ * LanyFS uses the Kernel's slab cache API for maintaining a common cache for
+ * VFS inodes and LanyFS inode private data.
+ */
+
+/* inode cache pointer */
+static struct kmem_cache *lanyfs_inode_cachep;
+
+/**
+ * LANYFS_I() - Returns pointer to inode's private data.
+ * @inode: vfs inode
+ */
+struct lanyfs_lii *LANYFS_I(struct inode *inode)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ return list_entry(inode, struct lanyfs_lii, vfs_inode);
+}
+
+/**
+ * lanyfs_inodecache_kcminit() - Initializes an inode cache element.
+ *
+ * This function has to take care of initializing the inode pointed to by
+ * vfs_inode! Also, this is not the inodecache initialization function, only
+ * single elements are initialzed here.
+ *
+ * @ptr: pointer to inode private data
+ */
+static void lanyfs_inodecache_kmcinit(void *ptr)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ inode_init_once(&((struct lanyfs_lii *) ptr)->vfs_inode);
+}
+
+/**
+ * lanyfs_inodecache_init() - Initializes the inode cache.
+ *
+ * If compiled with debug enabled, the cache is initialized with special flags
+ * set. Mostly to catch references to uninitialized memory and to check for
+ * buffer overruns.
+ */
+int lanyfs_inodecache_init(void)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lanyfs_inode_cachep = kmem_cache_create("lanyfs_inode_cache",
+ sizeof(struct lanyfs_lii),
+ 0,
+#ifdef LANYFS_DEBUG
+ (SLAB_RED_ZONE | SLAB_POISON),
+#else
+ 0,
+#endif /* LANYFS_DEBUG */
+ lanyfs_inodecache_kmcinit);
+ if (!lanyfs_inode_cachep)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * lanyfs_inodecache_destroy() - Destroys the inode cache.
+ */
+void lanyfs_inodecache_destroy(void)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ kmem_cache_destroy(lanyfs_inode_cachep);
+}
+
+/**
+ * lanyfs_alloc_inode() - Allocates an inode using the inode cache.
+ * @sb: superblock
+ */
+struct inode *lanyfs_alloc_inode(struct super_block *sb)
+{
+ struct lanyfs_lii *lii;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = kmem_cache_alloc(lanyfs_inode_cachep, GFP_NOFS);
+ if (!lii)
+ return NULL;
+ spin_lock_init(&lii->lock);
+ return &lii->vfs_inode;
+}
+
+/**
+ * lanyfs_destroy_inode() - Removes an inode from inode cache.
+ * @inode: inode
+ */
+void lanyfs_destroy_inode(struct inode *inode)
+{
+ struct lanyfs_lii *lii;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = LANYFS_I(inode);
+ kmem_cache_free(lanyfs_inode_cachep, lii);
+}
diff --git a/fs/lanyfs/inode.c b/fs/lanyfs/inode.c
new file mode 100644
index 0000000..5bb8f9a
--- /dev/null
+++ b/fs/lanyfs/inode.c
@@ -0,0 +1,357 @@
+/*
+ * inode.c - Lanyard Filesystem Inode Operations
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include "lanyfs_km.h"
+
+static const struct inode_operations lanyfs_file_inode_operations;
+
+/**
+ * lanyfs_inode_poke() - Updates all timestamps of an inode.
+ * @inode: inode to update
+ *
+ * Don't do this to unhashed inodes.
+ */
+void lanyfs_inode_poke(struct inode *inode)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ if (inode) {
+ spin_lock(&inode->i_lock);
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ spin_unlock(&inode->i_lock);
+ mark_inode_dirty(inode);
+ }
+}
+
+/**
+ * lanyfs_inode_rename() - Sets name of a directory or file.
+ * @inode: inode to rename
+ * @name: new name
+ *
+ * Attention! Callers must remove the inode from any binary tree *before*
+ * setting a new name otherwise the tree will break.
+ */
+void lanyfs_inode_rename(struct inode *inode, const char *name)
+{
+ struct lanyfs_lii *lii;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = LANYFS_I(inode);
+ spin_lock(&lii->lock);
+ spin_lock(&inode->i_lock);
+ memset(lii->name, 0x00, LANYFS_NAME_LENGTH);
+ strncpy(lii->name, name, LANYFS_NAME_LENGTH - 1);
+ lii->len = strlen(lii->name);
+ spin_unlock(&inode->i_lock);
+ spin_unlock(&lii->lock);
+}
+
+/**
+ * lanyfs_iget() - Turns a file or directory block into an inode.
+ * @sb: superblock
+ * @addr: number of block to awake
+ *
+ * Checks for inode state, thus overloading an inode already woken up will
+ * will just return that inode with increased reference count. Make sure to
+ * always decrease the reference count after use. VFS recklessly kills all
+ * referenced inodes on unmount which may lead to data loss.
+ * Real overloading would endanger consistency.
+ */
+struct inode *lanyfs_iget(struct super_block *sb, lanyfs_blk_t addr)
+{
+ struct lanyfs_fsi *fsi;
+ struct lanyfs_lii *lii;
+ struct buffer_head *bh;
+ union lanyfs_b *b;
+ struct inode *inode;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ if (!addr)
+ return NULL;
+ fsi = LANYFS_SB(sb);
+ inode = iget_locked(sb, addr); /* !: implicit cast to unsigned long */
+ if (!inode)
+ return NULL;
+ if (!(inode->i_state & I_NEW))
+ return inode;
+ lii = LANYFS_I(inode);
+ bh = sb_bread(sb, addr);
+ if (!bh) {
+ lanyfs_debug("error reading block #%llu", (u64) addr);
+ iget_failed(inode);
+ return NULL;
+ }
+ b = (union lanyfs_b *) bh->b_data;
+ switch (b->raw.type) {
+ case LANYFS_TYPE_DIR:
+ /* directory specific fields */
+ lii->subtree = le64_to_cpu(b->dir.subtree);
+ inode->i_op = &lanyfs_dir_inode_operations;
+ inode->i_fop = &lanyfs_dir_operations;
+ inode->i_mode = lanyfs_attr_to_mode(sb,
+ le16_to_cpu(b->vi_meta.attr), S_IFDIR);
+ inode->i_size = 1 << fsi->blocksize;
+ break;
+ case LANYFS_TYPE_FILE:
+ /* file specific fields */
+ lii->data = le64_to_cpu(b->file.data);
+ inode->i_op = &lanyfs_file_inode_operations;
+ inode->i_fop = &lanyfs_file_operations;
+ inode->i_mapping->a_ops = &lanyfs_address_space_operations;
+ inode->i_mode = lanyfs_attr_to_mode(sb,
+ le16_to_cpu(b->vi_meta.attr), S_IFREG);
+ inode->i_size = le64_to_cpu(b->file.size);
+ break;
+ default:
+ brelse(bh);
+ iget_failed(inode);
+ return NULL;
+ break;
+ }
+ /* binary tree */
+ lii->left = le64_to_cpu(b->vi_btree.left);
+ lii->right = le64_to_cpu(b->vi_btree.right);
+ /* times */
+ lanyfs_time_lts_to_kts(&b->vi_meta.modified, &inode->i_mtime);
+ inode->i_atime = inode->i_ctime = inode->i_mtime;
+ lanyfs_time_lts_to_kts(&b->vi_meta.created, &lii->created);
+ /* name */
+ memset(lii->name, 0x00, LANYFS_NAME_LENGTH);
+ strncpy(lii->name, b->vi_meta.name, LANYFS_NAME_LENGTH - 1);
+ lii->len = strlen(lii->name);
+ /* uid, gid */
+ inode->i_uid = fsi->opts.uid;
+ inode->i_gid = fsi->opts.gid;
+ /* blksize */
+ inode->i_blkbits = fsi->blocksize;
+ unlock_new_inode(inode);
+ brelse(bh);
+ return inode;
+}
+
+/**
+ * lanyfs_lookup() - Looks up an inode in a directory by name.
+ * @dir: inode of containing directory
+ * @dentry: directory entry to look up
+ * @flags: lookup flags
+ *
+ * The @flags are ignored by LanyFS. There are not many filesystems
+ * using the flags at all. This was nameidata before 3.5 which LanyFS
+ * did not use either.
+ */
+struct dentry *lanyfs_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags)
+{
+ struct inode *inode;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ /* length check */
+ if (dentry->d_name.len >= LANYFS_NAME_LENGTH)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ inode = lanyfs_btree_lookup(dir, dentry->d_name.name);
+ if (inode)
+ return d_splice_alias(inode, dentry);
+ return NULL;
+}
+
+/**
+ * lanyfs_write_inode() - Writes inode to disk.
+ * @inode: VFS inode
+ * @wbc: writeback control (unused)
+ */
+int lanyfs_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+ struct buffer_head *bh;
+ struct lanyfs_lii *lii;
+ union lanyfs_b *b;
+ u16 attr;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ if (!inode->i_nlink)
+ return 0;
+
+ lii = LANYFS_I(inode);
+ bh = sb_bread(inode->i_sb, inode->i_ino);
+ if (!bh) {
+ lanyfs_debug("error reading block #%llu", (u64) inode->i_ino);
+ return -EIO;
+ }
+ b = (union lanyfs_b *) bh->b_data;
+ spin_lock(&lii->lock);
+ spin_lock(&inode->i_lock);
+ lock_buffer(bh);
+ switch (b->raw.type) {
+ case LANYFS_TYPE_DIR:
+ /* directory specific fields */
+ b->dir.subtree = cpu_to_le64(lii->subtree);
+ break;
+ case LANYFS_TYPE_FILE:
+ /* file specific fields */
+ b->file.data = cpu_to_le64(lii->data);
+ b->file.size = cpu_to_le64(inode->i_size);
+ break;
+ default:
+ spin_unlock(&inode->i_lock);
+ spin_unlock(&lii->lock);
+ unlock_buffer(bh);
+ bforget(bh);
+ return -EINVAL;
+ break;
+ }
+ /* name */
+ memset(b->vi_meta.name, 0x00, LANYFS_NAME_LENGTH);
+ strncpy(b->vi_meta.name, lii->name, LANYFS_NAME_LENGTH - 1);
+ /* latest time *anything* changed always becomes modification time */
+ lanyfs_time_sync_inode(inode);
+ lanyfs_time_kts_to_lts(&inode->i_mtime, &b->vi_meta.modified);
+ /* mode */
+ attr = le16_to_cpu(b->vi_meta.attr);
+ attr = lanyfs_mode_to_attr(inode->i_mode, attr);
+ b->vi_meta.attr = cpu_to_le16(attr);
+ /* binary tree */
+ b->vi_btree.left = cpu_to_le64(lii->left);
+ b->vi_btree.right = cpu_to_le64(lii->right);
+ /* write counter */
+ le16_add_cpu(&b->raw.wrcnt, 1);
+
+ spin_unlock(&inode->i_lock);
+ spin_unlock(&lii->lock);
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ if (LANYFS_SB(inode->i_sb)->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+ return 0;
+}
+
+/**
+ * lanyfs_setattr() - Sets the attributes of an directory entry.
+ * @dentry: directory entry to manipulate
+ * @attr: new attributes
+ *
+ * This is the point where VFS tells us what it likes to change. We can then
+ * decide what changes we like and what changes we would like to reject.
+ * File size changes are also invoked from here and delegated to vmtruncate,
+ * which in turn calls lanyfs_truncate() after some checks.
+ */
+static int lanyfs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+ struct lanyfs_fsi *fsi;
+ struct inode *inode;
+ int err;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ inode = dentry->d_inode;
+ fsi = LANYFS_SB(inode->i_sb);
+
+/*
+ * TODO
+ if ((err = inode_change_ok(inode, attr)));
+ return err;
+*/
+ /* no uid changes */
+ if ((attr->ia_valid & ATTR_UID) &&
+ (attr->ia_uid != fsi->opts.uid))
+ return 0;
+ /* no gid changes */
+ if ((attr->ia_valid & ATTR_GID) &&
+ (attr->ia_gid != fsi->opts.gid))
+ return 0;
+ /* directories and files can be set read-only */
+ if (attr->ia_valid & ATTR_MODE) {
+ if (attr->ia_mode & S_IWUSR)
+ attr->ia_mode = inode->i_mode | S_IWUGO;
+ else
+ attr->ia_mode = inode->i_mode & ~S_IWUGO;
+ }
+ /* files can be set non-executable */
+ if ((attr->ia_valid & ATTR_MODE) && !S_ISDIR(inode->i_mode)) {
+ if (attr->ia_mode & S_IXUSR)
+ attr->ia_mode = inode->i_mode | S_IXUGO;
+ else
+ attr->ia_mode = inode->i_mode & ~S_IXUGO;
+ }
+ /* apply masks */
+ if (S_ISDIR(inode->i_mode))
+ attr->ia_mode &= ~fsi->opts.dmask;
+ else
+ attr->ia_mode &= ~fsi->opts.fmask;
+ /* size change */
+ if ((attr->ia_valid & ATTR_SIZE) &&
+ attr->ia_size != i_size_read(inode)) {
+ inode_dio_wait(inode);
+ err = vmtruncate(inode, attr->ia_size);
+ if (err)
+ return err;
+ }
+ setattr_copy(inode, attr);
+ mark_inode_dirty(inode);
+ return 0;
+}
+
+/** lanyfs_getattr() - Gets directory entry attributes.
+ * @mnt: VFS mount
+ * @dentry: directory entry to read
+ * @kstat: pointer to result storage
+ *
+ * This function does not differ much from the standard VFS getattr() currently.
+ */
+static int lanyfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *kstat)
+{
+ struct inode *inode;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ inode = dentry->d_inode;
+ kstat->dev = inode->i_sb->s_dev;
+ kstat->ino = inode->i_ino;
+ kstat->mode = inode->i_mode;
+ kstat->nlink = inode->i_nlink;
+ kstat->uid = inode->i_uid;
+ kstat->gid = inode->i_gid;
+ kstat->rdev = inode->i_rdev;
+ kstat->size = i_size_read(inode);
+ kstat->atime = inode->i_atime;
+ kstat->mtime = inode->i_mtime;
+ kstat->ctime = inode->i_ctime;
+ kstat->blksize = (1 << inode->i_blkbits);
+ kstat->blocks = inode->i_blocks;
+ return 0;
+}
+
+/** lanyfs_truncate() - Truncates a file.
+ * @inode: inode of file to truncate
+ */
+static void lanyfs_truncate(struct inode *inode)
+{
+ struct lanyfs_fsi *fsi;
+ struct lanyfs_lii *lii;
+ lanyfs_blk_t iblock;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lii = LANYFS_I(inode);
+ if (!lii->data)
+ return;
+ fsi = LANYFS_SB(inode->i_sb);
+ iblock = inode->i_size / (1 << fsi->blocksize);
+ if (inode->i_size % (1 << fsi->blocksize))
+ iblock++;
+ lanyfs_ext_truncate(inode->i_sb, lii->data, iblock);
+}
+
+/* lanyfs file inode operations */
+static const struct inode_operations lanyfs_file_inode_operations = {
+ .lookup = lanyfs_lookup,
+ .setattr = lanyfs_setattr,
+ .getattr = lanyfs_getattr,
+ .truncate = lanyfs_truncate,
+};
diff --git a/fs/lanyfs/lanyfs_km.c b/fs/lanyfs/lanyfs_km.c
new file mode 100644
index 0000000..d7bd4e0
--- /dev/null
+++ b/fs/lanyfs/lanyfs_km.c
@@ -0,0 +1,60 @@
+/*
+ * lanyfs_km.c - Lanyard Filesystem Kernel Module
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include "lanyfs_km.h"
+
+/**
+ * lanyfs_init() - Initialize LanyFS module.
+ *
+ * Initializes the inode cache and registers the filesystem.
+ */
+static int __init lanyfs_init(void)
+{
+ int err;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ pr_info("lanyfs: register filesystem\n");
+ lanyfs_debug("debug=enabled");
+ err = lanyfs_inodecache_init();
+ if (err)
+ goto exit_err;
+ err = register_filesystem(&lanyfs_file_system_type);
+ if (err)
+ goto exit_ino;
+ return 0;
+
+exit_ino:
+ lanyfs_inodecache_destroy();
+exit_err:
+ pr_err("lanyfs: register filesystem failed\n");
+ return err;
+}
+
+/**
+ * lanyfs_exit() - Exit LanyFS module.
+ *
+ * Takes care of destroying inode cache and unregistering the filesystem.
+ */
+static void __exit lanyfs_exit(void)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+ pr_info("lanyfs: unregister filesystem\n");
+
+ lanyfs_inodecache_destroy();
+ unregister_filesystem(&lanyfs_file_system_type);
+}
+
+module_init(lanyfs_init);
+module_exit(lanyfs_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Luedtke <[email protected]>");
+MODULE_DESCRIPTION("Lanyard Filesystem (LanyFS)");
diff --git a/fs/lanyfs/lanyfs_km.h b/fs/lanyfs/lanyfs_km.h
new file mode 100644
index 0000000..9c4bd07
--- /dev/null
+++ b/fs/lanyfs/lanyfs_km.h
@@ -0,0 +1,234 @@
+/*
+ * lanyfs_km.h - Lanyard Filesystem Header for Kernel Module
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#ifndef __LANYFS_KM_H_
+#define __LANYFS_KM_H_
+
+#include <linux/module.h>
+#include <linux/slab.h> /* kmemcache */
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/magic.h> /* LANYFS_SUPER_MAGIC */
+#include <linux/writeback.h> /* current_uid() etc. */
+#include <linux/parser.h> /* mount option parser */
+#include <linux/blkdev.h> /* issue discard */
+#include <linux/statfs.h> /* struct kstatfs */
+#include <linux/seq_file.h> /* seq_puts() */
+#include <linux/namei.h> /* struct nameidata */
+#include <linux/spinlock.h>
+
+#include "lanyfs_lnx.h" /* kernel space data structures */
+
+/*
+ * error codes
+ * lanyfs uses standard error codes whenever possible
+ */
+#define LANYFS_ERRNO_BASE 2050
+#define LANYFS_EPROTECTED (LANYFS_ERRNO_BASE + 0)
+#define LANYFS_ENOEMPTY (LANYFS_ERRNO_BASE + 1)
+#define LANYFS_ENOTAKEN (LANYFS_ERRNO_BASE + 2)
+
+/* messaging */
+#define lanyfs_info(sb, fmt, ...) \
+ { \
+ if (sb) \
+ pr_info("lanyfs (%s): " pr_fmt(fmt) "\n", \
+ sb->s_id, ##__VA_ARGS__); \
+ }
+#define lanyfs_err(sb, fmt, ...) \
+ { \
+ if (sb) \
+ pr_err("lanyfs (%s): " pr_fmt(fmt) "\n", \
+ sb->s_id, ##__VA_ARGS__); \
+ }
+#define lanyfs_warn(sb, fmt, ...) \
+ { \
+ if (sb) \
+ pr_warn("lanyfs (%s): " pr_fmt(fmt) "\n", \
+ sb->s_id, ##__VA_ARGS__); \
+ }
+
+/* debug messaging */
+#ifdef LANYFS_DEBUG
+#define lanyfs_debug(fmt, ...) \
+ printk(KERN_DEBUG "lanyfs: " pr_fmt(fmt) "\n", ##__VA_ARGS__)
+#else
+#define lanyfs_debug(fmt, ...) \
+ do { } while (0)
+#endif /* !LANYFS_DEBUG */
+
+/**
+ * typedef lanyfs_blk_t - the address of a logical block
+ *
+ * Every time you typedef without need, a kitten dies somewhere!
+ * However, sector_t assumes 512-byte sectors and blkcnt_t is for the number
+ * of total blocks. Please eliminate this typedef if you find a reasonable type.
+ * May equal sector in some configurations, so basically it is like sector_t,
+ * but not the same.
+ */
+typedef u64 lanyfs_blk_t;
+
+/**
+ * struct lanyfs_opts - mount options
+ * @uid: userid of all files and directories
+ * @gid: grouid of all files and direcotries
+ * @dmask: directory mask
+ * @fmask: file mask
+ * @discard: issue discard requests on block freeing
+ * @flush: force instant writing of changed data
+ */
+struct lanyfs_opts {
+ uid_t uid;
+ gid_t gid;
+ unsigned int dmask;
+ unsigned int fmask;
+ unsigned int discard:1,
+ flush:1;
+};
+
+/**
+ * struct lanyfs_fsi - filesystem private data
+ * @blocksize: blocksize (exponent to base 2)
+ * @addrlen: address length in bytes
+ * @rootdir: address of root directory
+ * @blocks: number of good blocks on the device
+ * @freehead: address of first extender for free blocks
+ * @freetail: address of last extender for free blocks
+ * @freeblocks: number of free blocks
+ * @updated: date and time of last superblock field change
+ * @chainmax: maximum number of slots per chain block
+ * @extmax: maximum number of slots per extender block
+ * @opts: mount options
+ * @lock: spinlock for filesystem private data
+ *
+ * Elements @freehead, @freetail, @blocks, @freeblocks, and @updated will be
+ * written back to disk on change or when VFS is syncing superblocks.
+ * Other elements are informational and must not be changed, but even if
+ * changed, their values won't be written back to disk.
+ */
+struct lanyfs_fsi {
+ unsigned int blocksize;
+ unsigned int addrlen;
+ lanyfs_blk_t rootdir;
+ lanyfs_blk_t blocks;
+ lanyfs_blk_t freehead;
+ lanyfs_blk_t freetail;
+ lanyfs_blk_t freeblocks;
+ struct timespec updated;
+ unsigned int chainmax;
+ unsigned int extmax;
+ struct lanyfs_opts opts;
+ spinlock_t lock;
+};
+
+/**
+ * struct lanyfs_lii - inode private data
+ * @left: address of left node of binary tree
+ * @right: address of right node of binary tree
+ * @subtree: binary tree root (directory only)
+ * @data: address of first extender (file only)
+ * @created: directory or file creation time
+ * @name: directory or file name
+ * @len: length of directory or file name
+ * @vfs_inode: virtual filesystem inode
+ * @lock: spinlock for inode private data
+ *
+ * Field @created is not synced back to disk, even if changed.
+ *
+ * We could save up to 8 byte of memory per inode if we union @subtree and
+ * @data, but then we must distinct between directory and file when destroying
+ * inode private data. Maybe later :)
+ */
+struct lanyfs_lii {
+ lanyfs_blk_t left;
+ lanyfs_blk_t right;
+ union {
+ lanyfs_blk_t subtree; /* directory only */
+ lanyfs_blk_t data; /* file only */
+ };
+ struct timespec created;
+ char name[LANYFS_NAME_LENGTH];
+ unsigned int len;
+ struct inode vfs_inode;
+ spinlock_t lock;
+};
+
+/* msg.c */
+extern void lanyfs_debug_function(const char *, const char *);
+extern void lanyfs_debug_ts(const char *, struct lanyfs_ts *);
+extern void lanyfs_debug_block(union lanyfs_b *);
+
+/* misc.c */
+extern void lanyfs_time_lts_now(struct lanyfs_ts *);
+extern void lanyfs_time_kts_to_lts(struct timespec *, struct lanyfs_ts *);
+extern void lanyfs_time_lts_to_kts(struct lanyfs_ts *, struct timespec *);
+extern void lanyfs_time_poke_inode(struct inode *);
+extern void lanyfs_time_sync_inode(struct inode *);
+extern umode_t lanyfs_attr_to_mode(struct super_block *, u16, umode_t);
+extern u16 lanyfs_mode_to_attr(mode_t, u16);
+
+/* icache.c */
+extern struct lanyfs_lii *LANYFS_I(struct inode *);
+extern int lanyfs_inodecache_init(void);
+extern void lanyfs_inodecache_destroy(void);
+extern struct inode *lanyfs_alloc_inode(struct super_block *);
+extern void lanyfs_destroy_inode(struct inode *);
+
+/* inode.c */
+extern void lanyfs_inode_poke(struct inode *inode);
+extern void lanyfs_inode_rename(struct inode *inode, const char *name);
+extern struct inode *lanyfs_iget(struct super_block *sb, lanyfs_blk_t ino);
+extern struct dentry *lanyfs_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags);
+extern int lanyfs_write_inode(struct inode *inode,
+ struct writeback_control *wbc);
+
+/* btree.c */
+extern int lanyfs_btree_add_inode(struct inode *dir, struct inode *rookie);
+extern int lanyfs_btree_del_inode(struct inode *dir, const char *name);
+extern struct inode *lanyfs_btree_lookup(struct inode *dir, const char *name);
+extern void lanyfs_btree_clear_inode(struct inode *inode);
+
+/* dir.c */
+extern const struct file_operations lanyfs_dir_operations;
+extern const struct inode_operations lanyfs_dir_inode_operations;
+
+/* extender.c */
+extern int lanyfs_ext_iblock(struct super_block *sb, lanyfs_blk_t addr,
+ lanyfs_blk_t iblock, lanyfs_blk_t *res);
+extern int lanyfs_ext_truncate(struct super_block *sb, lanyfs_blk_t addr,
+ lanyfs_blk_t iblock);
+extern lanyfs_blk_t lanyfs_ext_create(struct super_block *sb,
+ unsigned short level);
+extern int lanyfs_ext_grow(struct super_block *sb, lanyfs_blk_t *addr);
+
+/* file.c */
+extern const struct address_space_operations lanyfs_address_space_operations;
+extern const struct file_operations lanyfs_file_operations;
+
+/* chain.c */
+extern int lanyfs_chain_set_next(struct super_block *sb, lanyfs_blk_t addr,
+ lanyfs_blk_t next);
+extern int lanyfs_chain_create(struct super_block *sb, lanyfs_blk_t addr);
+extern int lanyfs_chain_pop(struct super_block *sb, lanyfs_blk_t addr,
+ lanyfs_blk_t *res);
+extern int lanyfs_chain_push(struct super_block *sb, lanyfs_blk_t addr,
+ lanyfs_blk_t rookie);
+
+/* super.c */
+extern struct lanyfs_fsi *LANYFS_SB(struct super_block *sb);
+extern lanyfs_blk_t lanyfs_enslave(struct super_block *sb);
+extern int lanyfs_release(struct super_block *sb, lanyfs_blk_t addr);
+extern const struct super_operations lanyfs_super_operations;
+extern struct file_system_type lanyfs_file_system_type;
+
+#endif /* __LANYFS_KM_H_ */
diff --git a/fs/lanyfs/lanyfs_lnx.h b/fs/lanyfs/lanyfs_lnx.h
new file mode 100644
index 0000000..5c670e7
--- /dev/null
+++ b/fs/lanyfs/lanyfs_lnx.h
@@ -0,0 +1,306 @@
+/*
+ * lanyfs_lnx.h - Lanyard Filesystem Header for Linux Kernel
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#ifndef __LANYFS_LNX_H_
+#define __LANYFS_LNX_H_
+
+/* filesystem version */
+#define LANYFS_MAJOR_VERSION 1
+#define LANYFS_MINOR_VERSION 4
+
+/* important numbers */
+#define LANYFS_SUPERBLOCK 0 /* address of on-disk superblock */
+#define LANYFS_MIN_ADDRLEN 1 /* mimimun address length (in bytes) */
+#define LANYFS_MAX_ADDRLEN 8 /* maximum address length (in bytes) */
+#define LANYFS_MIN_BLOCKSIZE 9 /* mimimun blocksize 2**9 */
+#define LANYFS_MAX_BLOCKSIZE 12 /* maximum blocksize 2**12 */
+#define LANYFS_NAME_LENGTH 256 /* maximum length of label/name */
+
+/* block type identifiers */
+#define LANYFS_TYPE_DIR 0x10
+#define LANYFS_TYPE_FILE 0x20
+#define LANYFS_TYPE_CHAIN 0x70
+#define LANYFS_TYPE_EXT 0x80
+#define LANYFS_TYPE_SB 0xD0
+
+/* directory and file attributes */
+#define LANYFS_ATTR_NOWRITE (1<<0)
+#define LANYFS_ATTR_NOEXEC (1<<1)
+#define LANYFS_ATTR_HIDDEN (1<<2)
+#define LANYFS_ATTR_ARCHIVE (1<<3)
+
+/**
+ * struct lanyfs_ts - ISO8601-like LanyFS timestamp
+ * @year: gregorian year (0 to 9999)
+ * @mon: month of year (1 to 12)
+ * @day: day of month (1 to 31)
+ * @hour: hour of day (0 to 23)
+ * @min: minute of hour (0 to 59)
+ * @sec: second of minute (0 to 59 normal, 0 to 60 if
+ * leap second)
+ * @__reserved_0: reserved
+ * @nsec: nanosecond (0 to 10^9)
+ * @offset: signed UTC offset in minutes
+ * @__reserved_1: reserved
+ */
+struct lanyfs_ts {
+ __le16 year;
+ __u8 mon;
+ __u8 day;
+ __u8 hour;
+ __u8 min;
+ __u8 sec;
+ unsigned char __reserved_0[1];
+ __le32 nsec;
+ __s16 offset;
+ unsigned char __reserved_1[2];
+};
+
+/**
+ * struct lanyfs_raw - LanyFS raw block
+ * @type: identifies the blocks purpose
+ * @__reserved_0: reserved
+ * @wrcnt: write counter
+ * @data: first byte of data
+ */
+struct lanyfs_raw {
+ __u8 type;
+ unsigned char __reserved_0;
+ __le16 wrcnt;
+ unsigned char data;
+};
+
+/**
+ * struct lanyfs_sb - LanyFS superblock
+ * @type: identifies the blocks purpose
+ * @__reserved_0: reserved
+ * @wrcnt: write counter
+ * @major: major version of filesystem
+ * @__reserved_1: reserved
+ * @minor: minor version of filesystem
+ * @__reserved_2: reserved
+ * @magic: identifies the filesystem
+ * @blocksize: blocksize (exponent to base 2)
+ * @__reserved_3: reserved
+ * @addrlen: length of block addresses in bytes
+ * @__reserved_4: reserved
+ * @rootdir: address of root directory block
+ * @blocks: number of blocks on the device
+ * @freehead: start of free blocks chain
+ * @freetail: end of free blocks chain
+ * @freeblocks: number of free blocks
+ * @created: date and time of filesystem creation
+ * @updated: date and time of last superblock field change
+ * @checked: date and time of last successful filesystem
+ * check
+ * @badblocks: start of bad blocks chain
+ * @__reserved_5: reserved
+ * @label: optional label for the filesystem
+ */
+struct lanyfs_sb {
+ __u8 type;
+ unsigned char __reserved_0;
+ __le16 wrcnt;
+ __le32 magic;
+ __u8 major;
+ unsigned char __reserved_1;
+ __u8 minor;
+ unsigned char __reserved_2;
+ __u8 blocksize;
+ unsigned char __reserved_3;
+ __u8 addrlen;
+ unsigned char __reserved_4;
+ __le64 rootdir;
+ __le64 blocks;
+ __le64 freehead;
+ __le64 freetail;
+ __le64 freeblocks;
+ struct lanyfs_ts created;
+ struct lanyfs_ts updated;
+ struct lanyfs_ts checked;
+ __le64 badblocks;
+ unsigned char __reserved_5[8];
+ char label[LANYFS_NAME_LENGTH];
+};
+
+/**
+ * struct lanyfs_btree - binary tree components
+ * @left: address of left node of binary tree
+ * @right: address of right node of binary tree
+ */
+struct lanyfs_btree {
+ __le64 left;
+ __le64 right;
+};
+
+/**
+ * struct lanyfs_vi_btree - aligned binary tree components
+ * @__padding_0: padding
+ * @left: address of left node of binary tree
+ * @right: address of right node of binary tree
+ *
+ * Used to access binary tree components independent from underlying block type.
+ * This creates a virtual block.
+ */
+struct lanyfs_vi_btree {
+ unsigned char __padding_0[8];
+ __le64 left;
+ __le64 right;
+};
+
+/**
+ * struct lanyfs_meta - lanyfs metadata
+ * @created: date and time of creation
+ * @modified: date and time of last modification
+ * @__reserved_0: reserved
+ * @attr: directory or file attributes
+ * @name: name of file or directory
+ */
+struct lanyfs_meta {
+ struct lanyfs_ts created;
+ struct lanyfs_ts modified;
+ unsigned char __reserved_0[14];
+ __le16 attr;
+ char name[LANYFS_NAME_LENGTH];
+};
+
+/**
+ * struct lanyfs_vi_meta - aligned lanyfs metadata
+ * @__padding_0: padding
+ * @created: date and time of creation
+ * @modified: date and time of last modification
+ * @__reserved_0: reserved
+ * @attr: directory or file attributes
+ * @name: name of file or directory
+ *
+ * Used to access meta data independent from underlying block type.
+ * This creates a virtual block.
+ */
+struct lanyfs_vi_meta {
+ unsigned char __padding_0[56];
+ struct lanyfs_ts created;
+ struct lanyfs_ts modified;
+ unsigned char __reserved_0[14];
+ __le16 attr;
+ unsigned char name[LANYFS_NAME_LENGTH];
+};
+
+/**
+ * struct lanyfs_dir - LanyFS directory block
+ * @type: identifies the blocks purpose
+ * @__reserved_0: reserved
+ * @wrcnt: write counter
+ * @__reserved_1: reserved
+ * @btree: binary tree components
+ * @subtree: binary tree root of directory's contents
+ * @__reserved_2: reserved
+ * @meta: directory metadata
+ */
+struct lanyfs_dir {
+ __u8 type;
+ unsigned char __reserved_0;
+ __le16 wrcnt;
+ unsigned char __reserved_1[4];
+ struct lanyfs_btree btree;
+ __le64 subtree;
+ unsigned char __reserved_2[24];
+ struct lanyfs_meta meta;
+};
+
+/**
+ * struct lanyfs_file - LanyFS file block
+ * @type: identifies the blocks purpose
+ * @__reserved_0: reserved
+ * @wrcnt: write counter
+ * @__reserved_1: reserved
+ * @btree: binary tree components
+ * @data: address of extender for data blocks
+ * @size: size of file in bytes
+ * @__reserved_2: reserved
+ * @meta: file metadata
+ */
+struct lanyfs_file {
+ __u8 type;
+ unsigned char __reserved_0;
+ __le16 wrcnt;
+ unsigned char __reserved_1[4];
+ struct lanyfs_btree btree;
+ __le64 data;
+ __le64 size;
+ unsigned char __reserved_2[16];
+ struct lanyfs_meta meta;
+};
+
+/**
+ * struct lanyfs_chain - LanyFS chain block (size-independent)
+ * @type: identifies the blocks purpose
+ * @__reserved_0: reserved
+ * @wrcnt: write counter
+ * @__reserved_1: reserved
+ * @next: address of next chain block
+ * @stream: start of block address stream
+ */
+struct lanyfs_chain {
+ __u8 type;
+ unsigned char __reserved_0;
+ __le16 wrcnt;
+ unsigned char __reserved_1[4];
+ __le64 next;
+ unsigned char stream;
+};
+
+/**
+ * struct lanyfs_ext - LanyFS extender block (size-independent)
+ * @type: identifies the blocks purpose
+ * @__reserved_0: reserved
+ * @wrcnt: write counter
+ * @level: depth of indirection
+ * @stream: start of block address stream
+ */
+struct lanyfs_ext {
+ __u8 type;
+ unsigned char __reserved_0;
+ __le16 wrcnt;
+ __u8 level;
+ unsigned char stream;
+};
+
+/**
+ * struct lanyfs_data - LanyFS data block
+ * @stream: start of data stream
+ */
+struct lanyfs_data {
+ unsigned char stream;
+};
+
+/** union lanyfs_b - lanyfs block
+ * @raw: raw block
+ * @sb: superblock
+ * @dir: directory block
+ * @file: file block
+ * @ext: extender block
+ * @data: data block
+ * @vi_btree: binary tree virtual block
+ * @vi_meta: metadata virtual block
+ */
+union lanyfs_b {
+ struct lanyfs_raw raw;
+ struct lanyfs_sb sb;
+ struct lanyfs_dir dir;
+ struct lanyfs_file file;
+ struct lanyfs_chain chain;
+ struct lanyfs_ext ext;
+ struct lanyfs_data data;
+ struct lanyfs_vi_btree vi_btree;
+ struct lanyfs_vi_meta vi_meta;
+};
+
+#endif /* __LANYFS_LNX_H */
diff --git a/fs/lanyfs/misc.c b/fs/lanyfs/misc.c
new file mode 100644
index 0000000..fcb1238
--- /dev/null
+++ b/fs/lanyfs/misc.c
@@ -0,0 +1,129 @@
+/*
+ * misc.c - Lanyard Filesystem Miscellaneous Operations
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include "lanyfs_km.h"
+
+/* --- time ----------------------------------------------------------------- */
+
+/**
+ * lanyfs_time_lts_to_kts() - Converts LanyFS timestamp to Kernel timespec.
+ * @lts: lanyfs timestamp (source)
+ * @kts: kernel timespec (target)
+ *
+ * WARNING: This function will overflow on 2106-02-07 06:28:16 on
+ * machines where long is only 32-bit! Replace mktime() before that date!
+ */
+void lanyfs_time_lts_to_kts(struct lanyfs_ts *lts, struct timespec *kts)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ kts->tv_sec = mktime(le16_to_cpu(lts->year), lts->mon, lts->day,
+ lts->hour, lts->min, lts->sec);
+ kts->tv_sec += le16_to_cpu(lts->offset) * 60;
+ kts->tv_nsec = le32_to_cpu(lts->nsec);
+}
+
+/**
+ * lanyfs_time_kts_to_lts() - Converts Kernel timespec to LanyFS timestamp.
+ * @kts: kernel timespec (source)
+ * @lts: lanyfs timestamp (target)
+ *
+ * Depends on global variable 'sys_tz' of type 'timezone'.
+ */
+void lanyfs_time_kts_to_lts(struct timespec *kts, struct lanyfs_ts *lts)
+{
+ struct tm tm;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ time_to_tm(kts->tv_sec, 0, &tm);
+ lts->year = cpu_to_le16(tm.tm_year + 1900);
+ lts->mon = tm.tm_mon + 1;
+ lts->day = tm.tm_mday;
+ lts->hour = tm.tm_hour;
+ lts->min = tm.tm_min;
+ lts->sec = tm.tm_sec;
+ lts->nsec = cpu_to_le32(kts->tv_nsec);
+ lts->offset = cpu_to_le16(sys_tz.tz_minuteswest * -1);
+}
+
+/**
+ * lanyfs_time_lts_now() - Convert current time to LanyFS timestamp.
+ * @lts: lanyfs timestamp (target)
+ */
+void lanyfs_time_lts_now(struct lanyfs_ts *lts)
+{
+ struct timespec now;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ now = current_kernel_time();
+ lanyfs_time_kts_to_lts(&now, lts);
+}
+
+/**
+ * lanyfs_time_sync_inode() - Syncs inode's timestamps.
+ * @inode: inode to sync
+ *
+ * All times (atime, mtime, ctime) will be set to the latest timestamp.
+ */
+void lanyfs_time_sync_inode(struct inode *inode)
+{
+ if (!inode)
+ return;
+ if (timespec_compare(&inode->i_mtime, &inode->i_ctime) > 0)
+ inode->i_ctime = inode->i_mtime;
+ else
+ inode->i_mtime = inode->i_ctime;
+ if (timespec_compare(&inode->i_atime, &inode->i_mtime) > 0)
+ inode->i_mtime = inode->i_ctime = inode->i_atime;
+ else
+ inode->i_atime = inode->i_mtime;
+}
+
+/* --- mode ----------------------------------------------------------------- */
+
+/**
+ * lanyfs_attr_to_mode() - Converts LanyFS metadata attributes to unix mode.
+ * @sb: superblock
+ * @attr: lanyfs metadata attributes
+ * @t: type of file (S_IFDIR, S_IFREG)
+ */
+umode_t lanyfs_attr_to_mode(struct super_block *sb, u16 attr, umode_t t)
+{
+ umode_t mode;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ mode = S_IRWXUGO;
+ if (attr & LANYFS_ATTR_NOWRITE)
+ mode &= ~S_IWUGO;
+ if (attr & LANYFS_ATTR_NOEXEC)
+ mode &= ~S_IXUGO;
+ if (t == S_IFDIR)
+ mode &= ~LANYFS_SB(sb)->opts.dmask;
+ else if (t == S_IFREG)
+ mode &= ~LANYFS_SB(sb)->opts.fmask;
+ return mode | t;
+}
+
+/**
+ * lanyfs_mode_to_attr() - Convert unix mode to LanyFS metadata attributes.
+ * @mode: mode to convert
+ * @base: lanyfs metadata attributes to preserve
+ */
+inline u16 lanyfs_mode_to_attr(mode_t mode, u16 base)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ if (!(mode & S_IWUGO))
+ base |= LANYFS_ATTR_NOWRITE;
+ if (!(mode & S_IXUGO))
+ base |= LANYFS_ATTR_NOEXEC;
+ return base;
+}
diff --git a/fs/lanyfs/msg.c b/fs/lanyfs/msg.c
new file mode 100644
index 0000000..2d7212e
--- /dev/null
+++ b/fs/lanyfs/msg.c
@@ -0,0 +1,99 @@
+/*
+ * msg.c - Lanyard Filesystem Log Message Handling
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include "lanyfs_km.h"
+
+/**
+ * lanyfs_debug_function() - Prints the currents function name and file.
+ * @file: file name
+ * @func: function name
+ *
+ * Produces call traces that help debugging a lot.
+ */
+void lanyfs_debug_function(const char *file, const char *func)
+{
+ /* reverse order of arguments is intended */
+ lanyfs_debug("%s (%s)", func, file);
+}
+
+/**
+ * lanyfs_debug_ts() - Prints human readable LanyFS timestamp.
+ * @lts: timestamp
+ * @desc: description
+ */
+void lanyfs_debug_ts(const char *desc, struct lanyfs_ts *lts)
+{
+ lanyfs_debug("%s=%04u-%02u-%02uT%02u:%02u:%02u.%u%+03d:%02ld",
+ desc, le16_to_cpu(lts->year), lts->mon, lts->day,
+ lts->hour, lts->min, lts->sec, lts->nsec,
+ lts->offset / 60, abs(lts->offset % 60));
+}
+
+/**
+ * lanyfs_debug_block() - Prints block's type and content.
+ * @b: block
+ *
+ * This is probably the most useful debug function. Use it to dump blocks
+ * whenever you are unsure of its contents. It will slow down the
+ * filesystem, though.
+ */
+void lanyfs_debug_block(union lanyfs_b *b)
+{
+ lanyfs_debug("dumping block at %p", b);
+ lanyfs_debug("type=0x%x", b->raw.type);
+ lanyfs_debug("wrcnt=%u", le16_to_cpu(b->raw.wrcnt));
+ /* sb */
+ if (b->raw.type == LANYFS_TYPE_SB) {
+ lanyfs_debug("magic=0x%x", le32_to_cpu(b->sb.magic));
+ lanyfs_debug("major_version=%u", b->sb.major);
+ lanyfs_debug("minor_version=%u", b->sb.minor);
+ lanyfs_debug("address_length=%u", b->sb.addrlen);
+ lanyfs_debug("blocksize=%u", b->sb.blocksize);
+ lanyfs_debug("root_directory=%llu", le64_to_cpu(b->sb.rootdir));
+ lanyfs_debug("total_blocks=%llu", le64_to_cpu(b->sb.blocks));
+ lanyfs_debug("free_head=%llu", le64_to_cpu(b->sb.freehead));
+ lanyfs_debug("free_tail=%llu", le64_to_cpu(b->sb.freetail));
+ lanyfs_debug("free_blocks=%llu", le64_to_cpu(b->sb.freeblocks));
+ lanyfs_debug_ts("created", &b->sb.created);
+ lanyfs_debug_ts("checked", &b->sb.checked);
+ lanyfs_debug_ts("updated", &b->sb.updated);
+ lanyfs_debug("volume_label=%s", b->sb.label);
+ }
+ /* chain */
+ if (b->raw.type == LANYFS_TYPE_CHAIN)
+ lanyfs_debug("next=%llu", le64_to_cpu(b->chain.next));
+ /* extender */
+ if (b->raw.type == LANYFS_TYPE_EXT)
+ lanyfs_debug("level=%u", b->ext.level);
+ /* btree */
+ if (b->raw.type == LANYFS_TYPE_DIR ||
+ b->raw.type == LANYFS_TYPE_FILE) {
+ lanyfs_debug("btree_left=%llu", le64_to_cpu(b->vi_btree.left));
+ lanyfs_debug("btree_right=%llu",
+ le64_to_cpu(b->vi_btree.right));
+ }
+ /* file */
+ if (b->raw.type == LANYFS_TYPE_FILE) {
+ lanyfs_debug("data=%llu", le64_to_cpu(b->file.data));
+ lanyfs_debug("size=%llu", le64_to_cpu(b->file.size));
+ }
+ /* dir */
+ if (b->raw.type == LANYFS_TYPE_DIR)
+ lanyfs_debug("subtree=%llu", le64_to_cpu(b->dir.subtree));
+ /* meta */
+ if (b->raw.type == LANYFS_TYPE_DIR ||
+ b->raw.type == LANYFS_TYPE_FILE) {
+ lanyfs_debug_ts("meta_created", &b->vi_meta.created);
+ lanyfs_debug_ts("meta_modified", &b->vi_meta.modified);
+ lanyfs_debug("meta_attr=%u", le16_to_cpu(b->vi_meta.attr));
+ lanyfs_debug("meta_name=%s", b->vi_meta.name);
+ }
+}
diff --git a/fs/lanyfs/super.c b/fs/lanyfs/super.c
new file mode 100644
index 0000000..aa6c14b
--- /dev/null
+++ b/fs/lanyfs/super.c
@@ -0,0 +1,549 @@
+/*
+ * super.c - Lanyard Filesystem Superblock Operations
+ *
+ * Copyright (C) 2012 Dan Luedtke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include "lanyfs_km.h"
+
+/**
+ * LANYFS_SB() - Returns pointer to filesystem private data.
+ * @sb: superblock
+ */
+struct lanyfs_fsi *LANYFS_SB(struct super_block *sb)
+{
+ /*
+ * Disabled by default, it produces a lot of noise.
+ * lanyfs_debug_function(__FILE__, __func__);
+ */
+ return (struct lanyfs_fsi *) sb->s_fs_info;
+}
+
+/* --- mount options -------------------------------------------------------- */
+
+enum {
+ Opt_uid,
+ Opt_gid,
+ Opt_dmask,
+ Opt_fmask,
+ Opt_discard,
+ Opt_nodiscard,
+ Opt_flush,
+ Opt_noflush,
+ Opt_err
+};
+
+static const match_table_t lanyfs_super_tokens = {
+ {Opt_uid, "uid=%u"},
+ {Opt_gid, "gid=%u"},
+ {Opt_dmask, "dmask=%u"},
+ {Opt_fmask, "fmask=%u"},
+ {Opt_discard, "discard"},
+ {Opt_nodiscard, "nodiscard"},
+ {Opt_flush, "flush"},
+ {Opt_noflush, "noflush"},
+ {Opt_err, NULL}
+};
+
+/**
+ * lanyfs_super_options() - Parses and saves mount options.
+ * @sb: superblock
+ * @data: mount options raw data string
+ * @silent: whether or not to be silent on error
+ */
+static int lanyfs_super_options(struct super_block *sb, char *data, int silent)
+{
+ struct lanyfs_opts *opts;
+ char *p;
+ substring_t args[MAX_OPT_ARGS];
+ int option;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ opts = &(LANYFS_SB(sb)->opts);
+
+ /* defaults */
+ opts->uid = current_uid();
+ opts->uid = current_gid();
+ opts->dmask = 0;
+ opts->fmask = 0;
+ opts->discard = 0;
+ opts->flush = 0;
+
+ /* no options given */
+ if (!data)
+ goto exit_ok;
+
+ /* parse and apply given options */
+ while ((p = strsep(&data, ",")) != NULL) {
+ if (!*p)
+ continue;
+ switch (match_token(p, lanyfs_super_tokens, args)) {
+ case Opt_uid:
+ if (match_int(&args[0], &option))
+ goto exit_invalid;
+ opts->uid = option;
+ break;
+ case Opt_gid:
+ if (match_int(&args[0], &option))
+ goto exit_invalid;
+ opts->gid = option;
+ break;
+ case Opt_dmask:
+ if (match_int(&args[0], &option))
+ goto exit_invalid;
+ opts->dmask = option;
+ break;
+ case Opt_fmask:
+ if (match_int(&args[0], &option))
+ goto exit_invalid;
+ opts->fmask = option;
+ break;
+ case Opt_discard:
+ opts->discard = 1;
+ break;
+ case Opt_nodiscard:
+ opts->discard = 0;
+ break;
+ case Opt_flush:
+ opts->flush = 1;
+ break;
+ case Opt_noflush:
+ opts->flush = 0;
+ break;
+ default:
+ goto exit_invalid;
+ break;
+ }
+ }
+exit_ok:
+ lanyfs_debug("option_uid=%u", opts->uid);
+ lanyfs_debug("option_gid=%u", opts->gid);
+ lanyfs_debug("option_dmask=%u", opts->dmask);
+ lanyfs_debug("option_fmask=%u", opts->fmask);
+ lanyfs_debug("option_discard=%u", opts->discard);
+ lanyfs_debug("option_flush=%u", opts->flush);
+ return 0;
+exit_invalid:
+ if (!silent)
+ lanyfs_err(sb,
+ "invalid mount option or bad parameter \"%s\"", p);
+ return -EINVAL;
+}
+
+/* --- superblock ----------------------------------------------------------- */
+
+/**
+ * lanyfs_super_sync() - Syncs the superblock to disk.
+ * @sb: superblock
+ *
+ * This function does the same as old VFS write_super(), back in the days
+ * when VFS invoked the syncing by looking for ->sb_dirt every five seconds.
+ * Today this function is invoked by LanyFS itself whenever it seems reasonable.
+ */
+static void lanyfs_super_sync(struct super_block *sb)
+{
+ struct lanyfs_fsi *fsi;
+ struct buffer_head *bh;
+ struct lanyfs_sb *rawsb;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ fsi = LANYFS_SB(sb);
+ bh = sb_bread(sb, LANYFS_SUPERBLOCK);
+ if (!bh) {
+ lanyfs_err(sb, "error reading block #%llu",
+ (u64) LANYFS_SUPERBLOCK);
+ return;
+ }
+ rawsb = (struct lanyfs_sb *) bh->b_data;
+ fsi->updated = current_kernel_time();
+ lock_buffer(bh);
+ le16_add_cpu(&rawsb->wrcnt, 1);
+ rawsb->freehead = cpu_to_le64(fsi->freehead);
+ rawsb->freetail = cpu_to_le64(fsi->freetail);
+ rawsb->freeblocks = cpu_to_le64(fsi->freeblocks);
+ /*
+ * number of valid blocks is not synced back at the moment, but it may
+ * as soon as a reliable badblocks-detection is implemented
+ * lanysb->blocks = cpu_to_le64(fsi->blocks);
+ */
+ lanyfs_time_kts_to_lts(&fsi->updated, &rawsb->updated);
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ if (fsi->opts.flush)
+ sync_dirty_buffer(bh);
+ brelse(bh);
+}
+
+/**
+ * lanyfs_put_super() - Prepare the superblock for unmounting.
+ * @sb: superblock
+ *
+ * This function is called by VFS with the superblock lock held.
+ */
+static void lanyfs_put_super(struct super_block *sb)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ lanyfs_super_sync(sb);
+ kfree(sb->s_fs_info);
+ sb->s_fs_info = NULL;
+}
+
+/**
+ * lanyfs_kill_super() - Safely closes the filesystem.
+ * @sb: superblock
+ *
+ * Cleanup of filesystem private data is done in lanyfs_put_super().
+ */
+static void lanyfs_kill_super(struct super_block *sb)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ kill_block_super(sb);
+}
+
+
+/**
+ * lanyfs_fill_super() - Initialize the superblock.
+ * @sb: superblock
+ * @options: arbitrary mount options
+ * @silent: whether or not to be silent on error
+ *
+ * This is the most important function for LanyFS since all device-specific
+ * configuration like address length and blocksize takes place here. It is also
+ * an implementation as close to the specifications as possible, thus serving
+ * as an example implementation for other operating systems or alternate kernel
+ * modules.
+ */
+static int lanyfs_fill_super(struct super_block *sb, void *options, int silent)
+{
+ struct lanyfs_fsi *fsi;
+ struct buffer_head *bh;
+ struct lanyfs_sb *lanysb;
+ struct inode *inode;
+ int err;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ inode = NULL;
+ err = 0;
+
+ /* allocate filesystem private data */
+ fsi = kzalloc(sizeof(*fsi), GFP_KERNEL);
+ if (!fsi)
+ return -ENOMEM;
+ spin_lock_init(&fsi->lock);
+ sb->s_fs_info = fsi;
+
+ /* set blocksize to minimum size for fetching superblock */
+ if (!sb_set_blocksize(sb, 1 << LANYFS_MIN_BLOCKSIZE)) {
+ if (!silent)
+ lanyfs_err(sb, "error setting blocksize to %d bytes",
+ 1 << LANYFS_MIN_BLOCKSIZE);
+ return -EIO;
+ }
+
+ /* fetch superblock */
+ bh = sb_bread(sb, LANYFS_SUPERBLOCK);
+ if (!bh) {
+ if (!silent)
+ lanyfs_err(sb, "error reading superblock");
+ return -EIO;
+ }
+ lanysb = (struct lanyfs_sb *) bh->b_data;
+
+ /* check magic */
+ if (lanysb->magic != cpu_to_le32(LANYFS_SUPER_MAGIC)) {
+ if (!silent)
+ lanyfs_info(sb, "bad magic 0x%x",
+ lanysb->magic);
+ goto exit_invalid;
+ }
+ sb->s_magic = LANYFS_SUPER_MAGIC;
+
+ /* check block type */
+ if (lanysb->type != LANYFS_TYPE_SB) {
+ if (!silent)
+ lanyfs_err(sb, "bad block type 0x%x", lanysb->type);
+ goto exit_invalid;
+ }
+
+ /* check version */
+ if (lanysb->major > LANYFS_MAJOR_VERSION) {
+ if (!silent)
+ lanyfs_err(sb, "major version mismatch");
+ goto exit_invalid;
+ }
+
+ /* check address length */
+ if (lanysb->addrlen < LANYFS_MIN_ADDRLEN ||
+ lanysb->addrlen > LANYFS_MAX_ADDRLEN) {
+ if (!silent)
+ lanyfs_err(sb, "unsupported address length");
+ goto exit_invalid;
+ }
+ fsi->addrlen = lanysb->addrlen;
+
+ /* check blocksize */
+ if (lanysb->blocksize < LANYFS_MIN_BLOCKSIZE ||
+ lanysb->blocksize > LANYFS_MAX_BLOCKSIZE) {
+ if (!silent)
+ lanyfs_err(sb, "unsupported blocksize");
+ goto exit_invalid;
+ }
+ fsi->blocksize = lanysb->blocksize;
+
+ /* more filesystem private data */
+ fsi->rootdir = le64_to_cpu(lanysb->rootdir);
+ fsi->freehead = le64_to_cpu(lanysb->freehead);
+ fsi->freetail = le64_to_cpu(lanysb->freehead);
+ fsi->freeblocks = le64_to_cpu(lanysb->freeblocks);
+ fsi->blocks = le64_to_cpu(lanysb->blocks);
+ fsi->chainmax = ((1 << fsi->blocksize) \
+ - offsetof(struct lanyfs_chain, stream)) / fsi->addrlen;
+ fsi->extmax = ((1 << fsi->blocksize) \
+ - offsetof(struct lanyfs_ext, stream)) / fsi->addrlen;
+ lanyfs_time_lts_to_kts(&lanysb->updated, &fsi->updated);
+
+ /* superblock debug messages */
+ lanyfs_debug_block((union lanyfs_b *) bh->b_data);
+
+ /* release block buffer */
+ brelse(bh);
+
+ /* parse mount options */
+ save_mount_options(sb, options);
+ err = lanyfs_super_options(sb, (char *) options, silent);
+ if (err)
+ return err;
+
+ /* set blocksize to correct size */
+ if (!sb_set_blocksize(sb, 1 << fsi->blocksize)) {
+ if (!silent)
+ lanyfs_err(sb, "error setting blocksize to %d bytes",
+ 1 << fsi->blocksize);
+ return -EIO;
+ }
+ /* default flags */
+ sb->s_maxbytes = 0xffffffff; /* TODO: hmmmmm */
+ sb->s_op = &lanyfs_super_operations;
+ sb->s_time_gran = 1;
+ sb->s_flags = MS_NOSUID | MS_NOATIME | MS_NODIRATIME;
+
+ /* make root directory */
+ inode = lanyfs_iget(sb, fsi->rootdir);
+ if (!inode)
+ return -ENOMEM;
+
+ sb->s_root = d_make_root(inode);
+ if (!sb->s_root) {
+ iput(inode);
+ return -ENOMEM;
+ }
+ return 0;
+
+exit_invalid:
+ brelse(bh);
+ if (!silent)
+ lanyfs_info(sb, "no valid lanyard filesystem found");
+ return -EINVAL;
+}
+
+/**
+ * lanyfs_mount() - Mounts a LanyFS device.
+ * @fs_type: describes the filesystem
+ * @flags: mount flags
+ * @device_name: the device name we are mounting
+ * @data: arbitrary mount options
+ */
+static struct dentry *lanyfs_mount(struct file_system_type *fs_type, int flags,
+ const char *device_name, void *data)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ return mount_bdev(fs_type, flags, device_name, data, lanyfs_fill_super);
+}
+
+/* --- free space management ------------------------------------------------ */
+
+/**
+ * lanyfs_enslave() - Picks a block from the free blocks pool.
+ * @sb: superblock
+ *
+ * Returns zero on error, e.g. on ENOSPC.
+ */
+lanyfs_blk_t lanyfs_enslave(struct super_block *sb)
+{
+ struct lanyfs_fsi *fsi;
+ lanyfs_blk_t addr;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ fsi = LANYFS_SB(sb);
+ if (unlikely(!fsi->freehead || !fsi->freetail || !fsi->freeblocks))
+ return 0;
+
+ spin_lock(&fsi->lock);
+ switch (lanyfs_chain_pop(sb, fsi->freehead, &addr)) {
+ case -LANYFS_ENOTAKEN:
+ /* no occupied slot left, enslave chain block itself instead */
+ swap(addr, fsi->freehead);
+ if (addr == fsi->freetail)
+ fsi->freetail = fsi->freehead;
+ /* fall through */
+ case 0:
+ fsi->freeblocks--;
+ spin_unlock(&fsi->lock);
+ lanyfs_super_sync(sb);
+ lanyfs_debug("enslaved block #%llu on %s", addr, sb->s_id);
+ return addr;
+ break;
+ default:
+ spin_unlock(&fsi->lock);
+ break;
+ }
+ return 0;
+
+}
+
+/**
+ * lanyfs_release() - Returns a block to the free blocks pool.
+ * @sb: superblock
+ * @addr: address of block to be returned
+ *
+ * Blocks are literally recycled, blocks remain unused as long as possible to
+ * distribute write cycles all over the device.
+ */
+int lanyfs_release(struct super_block *sb, lanyfs_blk_t addr)
+{
+ struct lanyfs_fsi *fsi;
+ int err;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ if (!likely(addr))
+ return -LANYFS_EPROTECTED;
+
+ fsi = LANYFS_SB(sb);
+
+ /* device was full, released block becomes chain block */
+ if (unlikely(!fsi->freehead || !fsi->freetail || !fsi->freeblocks)) {
+ err = lanyfs_chain_create(sb, addr);
+ if (err)
+ goto exit_err;
+ spin_lock(&fsi->lock);
+ fsi->freehead = fsi->freetail = addr;
+ fsi->freeblocks = 0;
+ spin_unlock(&fsi->lock);
+ goto exit_ok;
+ }
+
+ /* append block to existing chain */
+ err = lanyfs_chain_push(sb, fsi->freetail, addr);
+ if (err == -LANYFS_ENOEMPTY) {
+ /* chain block was full, create a new one and append it */
+ err = lanyfs_chain_create(sb, addr);
+ if (err)
+ goto exit_err;
+ err = lanyfs_chain_set_next(sb, fsi->freetail, addr);
+ spin_lock(&fsi->lock);
+ fsi->freetail = addr;
+ spin_unlock(&fsi->lock);
+ goto exit_ok;
+ } else if (err) {
+ goto exit_err;
+ }
+exit_ok:
+ spin_lock(&fsi->lock);
+ fsi->freeblocks++;
+ spin_unlock(&fsi->lock);
+ lanyfs_super_sync(sb);
+ lanyfs_debug("released block #%llu on %s", addr, sb->s_id);
+ return 0;
+exit_err:
+ lanyfs_err(sb, "error freeing block #%llu", (u64) addr);
+ return err;
+}
+
+/* --- statistics ----------------------------------------------------------- */
+
+/**
+ * lanyfs_show_stats() - Eventually shows extended filesystem statistics.
+ * @m: seq-file to write to
+ * @dentry: root directory entry
+ *
+ * This function is still in development.
+ * Currently unknown: Where does the output (read: seq_file writes) of this
+ * function show up?
+ */
+static int lanyfs_show_stats(struct seq_file *m, struct dentry *dentry)
+{
+ lanyfs_debug_function(__FILE__, __func__);
+
+ seq_printf(m, "Can we try with real bullets now? (Mathilda)\n");
+ return 0;
+}
+
+/**
+ * lanyfs_statfs() - Provides filesystem statistics.
+ * @dentry: directory entry
+ * @buf: buffer for filesystem statistics
+ */
+static int lanyfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ struct lanyfs_fsi *fsi;
+ u64 fsid;
+ lanyfs_debug_function(__FILE__, __func__);
+
+ fsi = LANYFS_SB(dentry->d_sb);
+ fsid = huge_encode_dev(dentry->d_sb->s_bdev->bd_dev);
+ buf->f_type = LANYFS_SUPER_MAGIC;
+ buf->f_bsize = 1 << fsi->blocksize;
+ buf->f_blocks = fsi->blocks;
+ buf->f_bfree = fsi->freeblocks;
+ buf->f_bavail = buf->f_bfree;
+ buf->f_files = fsi->blocks;
+ buf->f_ffree = fsi->freeblocks;
+ /* Nobody knows what f_fsid is supposed to contain, cp. statfs(2)! */
+ buf->f_fsid.val[0] = (u32) fsid;
+ buf->f_fsid.val[1] = (u32) (fsid >> 32);
+ buf->f_namelen = LANYFS_NAME_LENGTH;
+ return 0;
+}
+
+/* --- vfs interface -------------------------------------------------------- */
+
+/* lanyfs filesystem type */
+struct file_system_type lanyfs_file_system_type = {
+ .name = "lanyfs",
+ .fs_flags = FS_REQUIRES_DEV,
+ .mount = lanyfs_mount,
+ .kill_sb = lanyfs_kill_super,
+ .owner = THIS_MODULE,
+};
+
+/* lanyfs superblock operations */
+const struct super_operations lanyfs_super_operations = {
+ .alloc_inode = lanyfs_alloc_inode,
+ .destroy_inode = lanyfs_destroy_inode,
+ .dirty_inode = NULL,
+ .write_inode = lanyfs_write_inode,
+ .drop_inode = generic_drop_inode, /* generic is fine */
+ .evict_inode = NULL,
+ .put_super = lanyfs_put_super,
+ .sync_fs = NULL,
+ .freeze_fs = NULL, /* for LVM */
+ .unfreeze_fs = NULL, /* for LVM */
+ .statfs = lanyfs_statfs,
+ .remount_fs = NULL,
+ .umount_begin = NULL,
+ .show_options = generic_show_options, /* generic is fine */
+ .show_devname = NULL, /* default is fine for lanyfs */
+ .show_path = NULL, /* default is fine for lanyfs */
+ .show_stats = lanyfs_show_stats,
+ .bdev_try_to_free_page = NULL,
+ .nr_cached_objects = NULL, /* for sb cache shrinking */
+ .free_cached_objects = NULL, /* for sb cache shrinking */
+};
diff --git a/include/linux/magic.h b/include/linux/magic.h
index e15192c..6569ee3 100644
--- a/include/linux/magic.h
+++ b/include/linux/magic.h
@@ -27,6 +27,7 @@
#define ISOFS_SUPER_MAGIC 0x9660
#define JFFS2_SUPER_MAGIC 0x72b6
#define PSTOREFS_MAGIC 0x6165676C
+#define LANYFS_SUPER_MAGIC 0x594e414c /* le16 LANY */
#define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */
#define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */
--
1.7.8.6
On Sun, 19 Aug 2012 01:38:37 +0200
Dan Luedtke <[email protected]> wrote:
> This patch introduces the Lanyard Filesystem (LanyFS), a filesystem
> for highly mobile and removable storage devices.
>
> Signed-off-by: Dan Luedtke <[email protected]>
> ---
> "Release early, release often" they said. Here is my work of the
> past weeks. This is a RFC patch, so please comment, criticize, suggest
> and test test test. Of course patches are welcome, too.
Starting from first principles a bit you might want to read/review Arnd
Bergmanns work on Flash drive performance and behaviour. It has some
fairly significant impact on how you lay out your file system and in
particular questions like "where should I put stuff that is updated
regularly' (in the same blocks a FAT fs would put the FAT being one clear
answer)
http://lwn.net/Articles/428584/
and a pile of other stuff.
On Sun, Aug 19, 2012 at 1:38 AM, Dan Luedtke <[email protected]> wrote:
> This patch introduces the Lanyard Filesystem (LanyFS), a filesystem
> for highly mobile and removable storage devices.
What are the use cases of this filesystem?
It looks very minimal without much features.
--
Thanks,
//richard
On Sun, Aug 19, 2012 at 12:22 AM, Dan Luedtke <[email protected]> wrote:
> That's the feature, actually. Think of the Arduino platform or other
> embedded devices (TV, Car Entertainment) that just want to read/play files.
> You're right, no big features, but that's by design.
So your fs is designed to run on top of NAND/NOR flash memory?
--
Thanks,
//richard
(resent)
On Sun, 2012-08-19 at 00:16 +0200, richard -rw- weinberger wrote:
> What are the use cases of this filesystem?
> It looks very minimal without much features.
That's the feature, actually. Think of the Arduino platform or other embedded devices (TV, Car Entertainment) that just want to read/play files. You're right, no big features, but that's by design.
Dan
--
Sent from my mobile phone. Please excuse my brevity.
Il 19/08/2012 12:12, Dan Luedtke ha scritto:
> (resent)
> On Sun, 2012-08-19 at 00:16 +0200, richard -rw- weinberger wrote:
>> What are the use cases of this filesystem?
>> It looks very minimal without much features.
> That's the feature, actually. Think of the Arduino platform or other embedded devices (TV, Car Entertainment) that just want to read/play files. You're right, no big features, but that's by design.
>
> Dan
>
You say that you wrote a new fs because of some lacks in the other fs
("...I kind of invented a very simple filesystem that solves the issues
I had with other filesystems...."). I read the website (very quickly
actually) but I didn't find the point: what are pros and cons of this fs
compared with existing fs? What problems does it solve?
Marco
On Sun, 2012-08-19 at 12:14 +0200, Marco Stornelli wrote:
> You say that you wrote a new fs because of some lacks in the other fs
> ("...I kind of invented a very simple filesystem that solves the issues
> I had with other filesystems...."). I read the website (very quickly
> actually) but I didn't find the point:
I had to be a little bit stingy with that, I haven't submitted my thesis
document yet, and I don't want to publish it before that. I may not be
allowed to do so before it is officially submission, IANAL.
At the moment, the website lacks of the information you were looking
for, sorry for that. The thesis contains also a lot of statistical data
and fancy diagrams and stuff like that. I analyzed about 600k file
stored on various removable storage devices. 80 volunteers sent in data
about their devices, generated by a program (windows) and scripts
(linux, bsd, osx) I wrote for that purpose. The data shows that people
use more complex filesystems as soon as they are confronted with
problems (mostly the 4GB limit). After that they have problems getting
their data accessed by other systems. I derived from that, that we need
a filesystem that is so simple that even unpopular operating systems can
implement it without having their business plan explode. The data will
be made public (like open data) after submitting the document. Others
are then free to derive statements from it, may then verify or
contradict my conclusions.
> what are pros and cons of this fs
> compared with existing fs?
Pros:
- Simplicity. LanyFS avoids any unnecessary complexity.
Example: I had a lot of problems reading and writing data on the Arduino
platform. Most of it was, because the platform wasn't capable of dealing
with FAT32 when files grew. I don't know if LanyFS performs better on
Arduino, I haven't written a driver for Arduino yet, I am glad I managed
to get it working on Linux for the moment.
- Interoperability. LanyFS was designed to unite those features which
are common on most operating systems. [...] All information, including
file and directory metadata, is stored in the most purposive format
without honoring the habits of any particular operating system.
Example: I have a nice FullHD movie file which is about 8GB in size. I
wanted to play that video file on a RaspberryPI connected to a TV. FAT32
wasn't able to store the file because it was larger than 4GB. I then
tried using ext3, but the ownership information from my workstation
(were the file was copied from) did not match the ones the RaspberryPI
had, since I usually do not synchronize user profiles between
workstations and embedded devices. There are workarounds, one might say,
but shouldn't it be much more easier to transfer files from one device
to another? There are use cases were modes/permissions and ownership of
files does not matter, where quota and accounting isn't necessary, where
access times don't need to be stored since they would never get updated
anyway.
Flexibility. LanyFS adopts to the underlying storage device by
adjusting its parameters accordingly. It can address block
devices starting from 4 KiB up to 64 ZiB with minimal overhead by
parameterizing the filesystem at formatting time.
Example: At the university we sometimes work with smart cards and their
sometimes "strange" filesystems. I am not an expert on smart cards and
stuff like that, but I was being told that a more simple filesystem
would be welcome. AFAIK we are talking about "disks" with a size of less
than 1MB. I don't think this is a big feature, most other fs can be
adjusted to perfectly fit a particular purpose, too. Yet there still is
the demand for something "much more simpler".
Cons:
- LanyFS is featureless. It lacks of features. This is considered a
feature by some people, and a big con by others :)
- Use of recursion. It may not scale in some cases (e.g. very large
files on embedded platforms).
- Current restrictions due to early stage. Max blocksize is 4k, but 4MB
might be better. The LWN article pointed to by Alan talks about this.
- No use of MTD/UBI features. LanyFS expects a block layer abstraction
beneath it, this is because it targets highly mobile devices, and those
devices also happen to be rotating disk (external hard drives)
sometimes. It is not optimized for flash-only devices, although most of
the devices it may run on one day will probably be flash-based.
> What problems does it solve?
- I can now watch >4GB movie files from USB thumb drive :)
- Providing a storage device interface will be easier for vendors. TVs,
car entertainment systems, digital cameras, sensor devices, navigation
systems and all kind of other devices often use FAT32 since it just
works on everyones computer, but it is limit in size. Other fs are there
to hop in, but vendors often refuse to implement them since they have to
be compatible with a lot of features. They won't face such problems with
LanyFS, it just has no features. Of course, I am just a student, I have
no money and no market power, so LanyFS might never make it into other
major operating systems or all the devices around us.
- Look around, there are so many devices that do not provide a storage
interface at the moment, but they easily could. I'd rather see my
electricity meter log my power consumption to a thumb drive than sending
it into the cloud every 5 minutes.
This whole thing isn't about the patch anymore, is it? Should we go off
list to not make so much noise disturbing other developers?
Thank you very much for your interest in LanyFS and please don't
hesitate to point out errors in the code, too.
Thanks
Dan
--
Dan Luedtke
http://www.danrl.de
Hello,
On Sun, Aug 19, 2012 at 03:34:24PM +0200, Dan Luedtke wrote:
> tried using ext3, but the ownership information from my workstation
> (were the file was copied from) did not match the ones the RaspberryPI
> had, since I usually do not synchronize user profiles between
> workstations and embedded devices. There are workarounds, one might say,
You wrote a new fs just because you didn't bother to use the existing
ones as intended?
Puzzled,
Jochen.
Il 19/08/2012 15:34, Dan Luedtke ha scritto:
> On Sun, 2012-08-19 at 12:14 +0200, Marco Stornelli wrote:
>> what are pros and cons of this fs
>> compared with existing fs?
> Pros:
> - Simplicity. LanyFS avoids any unnecessary complexity.
> Example: I had a lot of problems reading and writing data on the Arduino
> platform. Most of it was, because the platform wasn't capable of dealing
> with FAT32 when files grew. I don't know if LanyFS performs better on
> Arduino, I haven't written a driver for Arduino yet, I am glad I managed
> to get it working on Linux for the moment.
>
> - Interoperability. LanyFS was designed to unite those features which
> are common on most operating systems. [...] All information, including
> file and directory metadata, is stored in the most purposive format
> without honoring the habits of any particular operating system.
> Example: I have a nice FullHD movie file which is about 8GB in size. I
> wanted to play that video file on a RaspberryPI connected to a TV. FAT32
> wasn't able to store the file because it was larger than 4GB. I then
> tried using ext3, but the ownership information from my workstation
> (were the file was copied from) did not match the ones the RaspberryPI
> had, since I usually do not synchronize user profiles between
> workstations and embedded devices. There are workarounds, one might say,
> but shouldn't it be much more easier to transfer files from one device
> to another? There are use cases were modes/permissions and ownership of
> files does not matter, where quota and accounting isn't necessary, where
> access times don't need to be stored since they would never get updated
> anyway.
>
> Flexibility. LanyFS adopts to the underlying storage device by
> adjusting its parameters accordingly. It can address block
> devices starting from 4 KiB up to 64 ZiB with minimal overhead by
> parameterizing the filesystem at formatting time.
> Example: At the university we sometimes work with smart cards and their
> sometimes "strange" filesystems. I am not an expert on smart cards and
> stuff like that, but I was being told that a more simple filesystem
> would be welcome. AFAIK we are talking about "disks" with a size of less
> than 1MB. I don't think this is a big feature, most other fs can be
> adjusted to perfectly fit a particular purpose, too. Yet there still is
> the demand for something "much more simpler".
>
> Cons:
> - LanyFS is featureless. It lacks of features. This is considered a
> feature by some people, and a big con by others :)
>
> - Use of recursion. It may not scale in some cases (e.g. very large
> files on embedded platforms).
Recursion can be a problem, we have got a little stack, I don't know if
it's a good idea.
>
> - Current restrictions due to early stage. Max blocksize is 4k, but 4MB
> might be better. The LWN article pointed to by Alan talks about this.
>
Ok, I try to do a summary. You are trying to write a new general and
minimal fs for mobile storage device, minimal enough to be easy ported
on several fs. So at the end you are trying to replace the solution is
used today on many platforms (fat32). Without other consideration about
"no-feature is a feature", it seems to me really challenging because the
project can fail its goal no because there is design problem, bugs and
so on but because of limited use. We are talking about interoperability
problem, and we really know some companies out there from this point of
view :)
Marco
On Sun, 2012-08-19 at 14:02 +0200, Jochen Striepe wrote:
> You wrote a new fs just because you didn't bother to use the existing
> ones as intended?
You are over-estimating my motivation.
I use many fs as intended, and they do a great job. I would not replace
them, not on my workstation and not on my servers. But from time to time
I get into trouble when I start transferring data from system A to
system B by using removable storage devices. While this can be solved as
long as I know what the target platform/os will be, it becomes more
difficult when the target platform/os is unknown.
Poorly crafted example:
Let's say you have a 6GB video file you want to give somebody (e.g. a
video cutter) on a thumb drive. The cutter wants to edit the file, so he
needs read and write access to it. After cutting the file is to be
played on a TV screen with USB-port. What if the cutter does use two
different, major, non-Linux operating systems, let's say one for cutting
and the other one for adding visual effects? Now imagine the cutter (and
of course the TV screen vendor) don't care much about filesystems as
they are just generating costs (implementation), so it will only come
with minimal compatibility.
- What filesystem would you recommend to share that video file?
There is a small niche which LanyFS tries to fit in. It is for those who
do not want to bother about how to use a fs when they are in a hurry or
when they just want to listen to music in the car. It is for the
it-must-be-easy-enough-for-my-gradma fraction. It is for those who think
that data stored on a thumb drive intended for use with
unknown/untrusted/undocumented systems should not be critical data
anyway. I do not recommend storing vital files or data worth protecting
from unauthorized modification/access on a "highly mobile" thumb drive,
especially not one formatted with LanyFS. After all, it is a fs that
tries to grant access to the data at any cost*.
Still wondering if anyone bothers to actually look at the code?
Although I appreciate any feedback about why-would-one-even-write-a-fs,
I would also be very happy about comments that help improve the code.
regards,
Dan
*This one is worth a discussion, but LKML might not be the right place?
--
Dan Luedtke
http://www.danrl.de
On Sun, 2012-08-19 at 15:25 +0200, Marco Stornelli wrote:
> Ok, I try to do a summary. You are trying to write a new general and
> minimal fs for mobile storage device, minimal enough to be easy ported
> on several fs. So at the end you are trying to replace the solution is
> used today on many platforms (fat32). Without other consideration about
> "no-feature is a feature", it seems to me really challenging because the
> project can fail its goal no because there is design problem, bugs and
> so on but because of limited use. We are talking about interoperability
> problem, and we really know some companies out there from this point of
> view :)
You nailed it and you made me feel like Don Quijote.
I'd like to give it a try anyway.
regards,
Dan
--
Dan Luedtke
http://www.danrl.de
Hi again,
On Sun, Aug 19, 2012 at 05:33:52PM +0200, Dan Luedtke wrote:
> - What filesystem would you recommend to share that video file?
You pointed out you tried ext3, so I thought ext3 was available on your
target platforms. I'm sorry I don't understand your reasoning about its
shortcomings.
So long,
Jochen.
P.S.: [email protected] removed from Cc: since it is obviously a
closed list, answering my previous email with a subscription
confirmation request.
On Sun, Aug 19, 2012 at 05:33:52PM +0200, Dan Luedtke wrote:
> Still wondering if anyone bothers to actually look at the code?
Some obvious notes:
* unlimited recursion is a killer; here its depth is controlled
by the fs image contents and it's trivial to cook one that would overflow
kernel stack. Seeing that you want to use it for removable media, that's
a gaping security hole right there
* unlink() does *not* truncate the file contents; file that had been
opened and unlinked should keep its contents until it's closed. The same
goes for overwriting rename().
* while we are at it, neither of those should free the on-disk
inode; again, that should happen only when the inode is evicted.
* I might be missing something, but copying a bunch of files
with something like cp /foo/* /mnt seems to be guaranteed to create
really lousy binary tree in target directory (they will go in lexicographical
order and you don't seem to rebalance the tree at all)
* you are really abusing iget() there. Leaving the locking issues
aside, that's going to get you icache filled with irrelevant stuff. Moreover,
it's far too heavy a club; allocating and filling struct inode when all you
really need is name and a couple of pointers in the disk block?
* minor point, but endianness-flipping in place is *the* way to get
hard-to-catch endianness bugs. foo = cpu_to_le64(foo) is a bloody bad idea;
either use object for host-endian all along, or use it only for (in your
case) little-endian.
On Sun, 2012-08-19 at 15:27 +0100, Al Viro wrote:
> * unlimited recursion
I am already working on that one, but it's tricky.
> * unlink() does *not* truncate the file contents;
I did not know that.
> * while we are at it, neither of those should free the on-disk
> inode; again, that should happen only when the inode is evicted.
Makes sense now. Thanks!
> * I might be missing something, but copying a bunch of files
> with something like cp /foo/* /mnt seems to be guaranteed to create
> really lousy binary tree in target directory (they will go in lexicographical
> order and you don't seem to rebalance the tree at all)
You missed nothing, there is no rebalancing yet. Thats why performance
is bad at the moment as soon as the "tree" stops being a tree.
> * you are really abusing iget() there.
Noted. Thanks!
> * minor point, but endianness-flipping in place is *the* way to get
> hard-to-catch endianness bugs. foo = cpu_to_le64(foo) is a bloody bad idea;
> either use object for host-endian all along, or use it only for (in your
> case) little-endian.
I am not sure I understood this right.
At what point should I convert e.g. the file size (little endian 64bit
value stored on disk) to host endianess? When filling the inode?
Is inode->i_size = le64_to_cpu(size) bad, too?
Thank you very much for your comments! That'll keep me busy a few weeks.
regards,
Dan
PS: As Jochen Striepe pointed out, [email protected] behaves badly, I
removed it.
--
Dan Luedtke
http://www.danrl.de
On Sun, Aug 19, 2012 at 06:53:37PM +0200, Dan Luedtke wrote:
> > * minor point, but endianness-flipping in place is *the* way to get
> > hard-to-catch endianness bugs. foo = cpu_to_le64(foo) is a bloody bad idea;
> > either use object for host-endian all along, or use it only for (in your
> > case) little-endian.
> I am not sure I understood this right.
> At what point should I convert e.g. the file size (little endian 64bit
> value stored on disk) to host endianess? When filling the inode?
> Is inode->i_size = le64_to_cpu(size) bad, too?
Conversions *in* *place* are bad. The above is fine if you have 'size'
variable used only for little-endian values (and declare it __le64, then).
The thing is, thinking of endianness conversions as architecture-conditional
byteswaps is wrong. Treat them as you would treat dealing with some
encoding; that's the reason why we have separate le64_to_cpu() and
cpu_to_le64(), even though they are identical as functions - on all
architectures we support. And don't use the same variable for both the
host- and fixed-endian values; it's far too easy to get confused on
code modifications and get the situation when two code paths lead to
the same point, one with host-endian value in that variable and another
with fixed-endian. Real fun to debug, especially when one of the codepaths
is rarely taken...
Il 19/08/2012 18:53, Dan Luedtke ha scritto:
> On Sun, 2012-08-19 at 15:27 +0100, Al Viro wrote:
>> * unlimited recursion
> I am already working on that one, but it's tricky.
>
>> * unlink() does *not* truncate the file contents;
> I did not know that.
I add that vmtruncate is deprecated and I see a call to inode_dio_wait
but no support for direct IO. In addition the lock rules seem strange, I
would avoid playing with inode->i_lock.
>
>> * while we are at it, neither of those should free the on-disk
>> inode; again, that should happen only when the inode is evicted.
> Makes sense now. Thanks!
And I think you'll call d_delete two times.
As general suggestion: to have a general view how things can work you
can look at other fs. Maybe ramfs or tmpfs are simple enough to
understand the general concepts.
Marco
On Sun, Aug 19, 2012 at 05:33:52PM +0200, Dan Luedtke wrote:
>
> Poorly crafted example:
> Let's say you have a 6GB video file you want to give somebody (e.g. a
> video cutter) on a thumb drive. The cutter wants to edit the file, so he
> needs read and write access to it. After cutting the file is to be
> played on a TV screen with USB-port. What if the cutter does use two
> different, major, non-Linux operating systems, let's say one for cutting
> and the other one for adding visual effects? Now imagine the cutter (and
> of course the TV screen vendor) don't care much about filesystems as
> they are just generating costs (implementation), so it will only come
> with minimal compatibility.
> - What filesystem would you recommend to share that video file?
So the problem with using interoperability as your prime driver is
that other operating systems won't have implemented your LanyFS, and
because of licensing issues, it will be very hard to get even a
shareware distribution of LanyFS for Windows (because of licensing
issues around Window's IFS SDK).
In practice, the solution of using either FAT for most cases as the
interchange format works well enough for most purposes. Granted it
doesn't in the case of your example of a 6GB video file (which I'd
probably transmit using a networking protocol, since a thumb drive
would be really slow.) What I would do if I needed to transfer such a
file, and I didn't have access to high speed networking, would be to
use ext2, and then either use the ext2 FUSE driver with FUSE for
Windows or Macintosh --- or, I would port the userspace e2tools
package to the target OS, and use that to access the ext2 file ssytem.
And I'd do that because the software is available today, right now,
without having to figure out how to port LanyFS to the operating
system.
> There is a small niche which LanyFS tries to fit in. It is for those who
> do not want to bother about how to use a fs when they are in a hurry or
> when they just want to listen to music in the car. It is for the
> it-must-be-easy-enough-for-my-gradma fraction.
Music doesn't require > 4GB files, and there are plenty of very easy
to use solutions that utilize streaming over the network. That is
*always* going to be easier than figuring out ahead of time which
files you want, and then manually copying them onto a thumb drive, and
then taking the thumb drive to the car.... Somehow I can't quite
imagine your grandma manually copying files over using LanyFS. :-)
I also seriously question the niche of people who want to use a thumb
drive to transfer > 4GB files. Try it sometime and see what a painful
user experience it is....
Regards,
- Ted
Theodore Ts'o <[email protected]> writes:
>
> In practice, the solution of using either FAT
When the answer is FAT the question usually didn't make much sense.
-Andi
--
[email protected] -- Speaking for myself only
On 19/08/12 23:04, Theodore Ts'o wrote:
>> There is a small niche which LanyFS tries to fit in. It is for those who
>> > do not want to bother about how to use a fs when they are in a hurry or
>> > when they just want to listen to music in the car. It is for the
>> > it-must-be-easy-enough-for-my-gradma fraction.
> Music doesn't require > 4GB files, and there are plenty of very easy
> to use solutions that utilize streaming over the network. That is
> *always* going to be easier than figuring out ahead of time which
> files you want, and then manually copying them onto a thumb drive, and
> then taking the thumb drive to the car.... Somehow I can't quite
> imagine your grandma manually copying files over using LanyFS. :-)
>
Microsoft is pushing exFAT [1] as the successor of FAT32 for this kind
of use cases. The problem is that exFAT is full of patents and they
require you to purchase a license for use.
I think that this LanyFS could be a great free alternative to exFAT when
the time of 4+GB-for-a-movie will became the norm.
The problem will be that IMHO Microsoft won't be interested in
implementing this FS on their OS despite of the license since their
interest is to push exFAT.
> I also seriously question the niche of people who want to use a thumb
> drive to transfer > 4GB files. Try it sometime and see what a painful
> user experience it is....
Think for example on consumer devices, for example on most moderns TV
you can plug a USB memory disk with videos and play them.
And videos are getting bigger and bigger. Many FullHD movies that you
can download or record are bigger than 4GB, and in a few years this will
be the norm.
And I doubt that the majority of this consumer devices are able to read
nothing more than FAT32 file-systems, so the 4GB limit is a big problem.
And here is where Microsoft is pushing their exFAT FS since it allows
working with 4GB+ files without the NTFS overhead.
As a side note, it would be possible to write a driver for exFAT and get
it merged upstream on the Linux Kernel without "breaking any law"?
Goggling I found an attempt to write such driver but seems that never
got merged: https://lkml.org/lkml/2009/2/8/24
Regards!
--------
[1] http://en.wikipedia.org/wiki/ExFAT
On Mon, Aug 20, 2012 at 01:06:20AM +0200, Carlos Alberto Lopez Perez wrote:
>
> > I also seriously question the niche of people who want to use a thumb
> > drive to transfer > 4GB files. Try it sometime and see what a painful
> > user experience it is....
>
> Think for example on consumer devices, for example on most moderns TV
> you can plug a USB memory disk with videos and play them.
More and more consumer devices, including TV's, are network-enabled.
I'm not at all convinced the USB memory disk model is the one which
makes sense --- you can make a much better user experience work if you
can rely on networking. That way you don't have to move USB storage
devices around, and USB storage devices are *slow* when the most
common types are HDD's and crappy flash devices. How many people are
going to drop several hundred dollars for a USB-attached SSD, when
using a networking transfer mechanism is much more convenient?
> And I doubt that the majority of this consumer devices are able to read
> nothing more than FAT32 file-systems, so the 4GB limit is a big problem.
> And here is where Microsoft is pushing their exFAT FS since it allows
> working with 4GB+ files without the NTFS overhead.
We'll see how popular a heavily IP-encumbered file system will be,
especially given that its main use case is for devices which are so
constrained that they can't afford to use a "real file system" (like
ntfs or ext4 or some other more sophisticated file system), but which
nevertheless needs to be able to handle 4GB+ files.
I'm sure there will be some use cases that might fit that niche, but
it seems pretty tiny. And this is completely ignoring what might
happen if in the future people take 1gig fiber connections to the home
(such as what many people in Kansas City will be enjoying very
shortly) for granted....
> As a side note, it would be possible to write a driver for exFAT and get
> it merged upstream on the Linux Kernel without "breaking any law"?
> Goggling I found an attempt to write such driver but seems that never
> got merged: https://lkml.org/lkml/2009/2/8/24
You'll need to talk to a lawyer about that, since that's fundamentally
a legal question.
Regards,
- Ted
On Sun, 2012-08-19 at 20:47 -0400, Theodore Ts'o wrote:
> On Mon, Aug 20, 2012 at 01:06:20AM +0200, Carlos Alberto Lopez Perez wrote:
> >
> > > I also seriously question the niche of people who want to use a thumb
> > > drive to transfer > 4GB files. Try it sometime and see what a painful
> > > user experience it is....
> >
> > Think for example on consumer devices, for example on most moderns TV
> > you can plug a USB memory disk with videos and play them.
>
> More and more consumer devices, including TV's, are network-enabled.
> I'm not at all convinced the USB memory disk model is the one which
> makes sense --- you can make a much better user experience work if you
> can rely on networking. That way you don't have to move USB storage
> devices around, and USB storage devices are *slow* when the most
> common types are HDD's and crappy flash devices. How many people are
> going to drop several hundred dollars for a USB-attached SSD, when
> using a networking transfer mechanism is much more convenient?
>
> > And I doubt that the majority of this consumer devices are able to read
> > nothing more than FAT32 file-systems, so the 4GB limit is a big problem.
> > And here is where Microsoft is pushing their exFAT FS since it allows
> > working with 4GB+ files without the NTFS overhead.
>
> We'll see how popular a heavily IP-encumbered file system will be,
> especially given that its main use case is for devices which are so
> constrained that they can't afford to use a "real file system" (like
> ntfs or ext4 or some other more sophisticated file system), but which
> nevertheless needs to be able to handle 4GB+ files.
My two cents:
After seeing microsoft's attack on TomTom over the vfat patents I
honesstly would consider it a good move to have an alternative free
format available.
> I'm sure there will be some use cases that might fit that niche, but
> it seems pretty tiny. And this is completely ignoring what might
> happen if in the future people take 1gig fiber connections to the home
> (such as what many people in Kansas City will be enjoying very
> shortly) for granted....
>
> > As a side note, it would be possible to write a driver for exFAT and get
> > it merged upstream on the Linux Kernel without "breaking any law"?
> > Goggling I found an attempt to write such driver but seems that never
> > got merged: https://lkml.org/lkml/2009/2/8/24
>
> You'll need to talk to a lawyer about that, since that's fundamentally
> a legal question.
>
> Regards,
>
> - Ted
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
On Sunday 19 August 2012 23:07:32 Raymond Jennings wrote:
> On Sun, 2012-08-19 at 20:47 -0400, Theodore Ts'o wrote:
> > On Mon, Aug 20, 2012 at 01:06:20AM +0200, Carlos Alberto Lopez Perez wrote:
> > >
> > > > I also seriously question the niche of people who want to use a thumb
> > > > drive to transfer > 4GB files. Try it sometime and see what a painful
> > > > user experience it is....
> > >
> > > Think for example on consumer devices, for example on most moderns TV
> > > you can plug a USB memory disk with videos and play them.
> >
> > More and more consumer devices, including TV's, are network-enabled.
> > I'm not at all convinced the USB memory disk model is the one which
> > makes sense --- you can make a much better user experience work if you
> > can rely on networking. That way you don't have to move USB storage
> > devices around, and USB storage devices are slow when the most
> > common types are HDD's and crappy flash devices. How many people are
> > going to drop several hundred dollars for a USB-attached SSD, when
> > using a networking transfer mechanism is much more convenient?
The cost of such drives will not stay so high and the speed will leap drastically
once UAS over USB 3.0 will have become established. And I am sure that you
don't want every content to go over the network. And far from everywhere will have
the bandwidth to transfer so much data. And of course you assume that you
know where you are going to need your content before you leave or have remote
access to your base system.
You may argue that networked transfers will take up a bigger slice of the cake, but
storage devices are far from dead, even for large amounts of data. And the definition
of largeness is growing.
Regards
Oliver
On Mon, Aug 20, 2012 at 2:47 AM, Theodore Ts'o <[email protected]> wrote:
> More and more consumer devices, including TV's, are network-enabled.
> I'm not at all convinced the USB memory disk model is the one which
> makes sense --- you can make a much better user experience work if you
> can rely on networking. That way you don't have to move USB storage
> devices around, and USB storage devices are *slow* when the most
> common types are HDD's and crappy flash devices. How many people are
> going to drop several hundred dollars for a USB-attached SSD, when
> using a networking transfer mechanism is much more convenient?
My two cents:
Flash drives are getting faster as well. Copying an 8GB file to/from a
USB drive is not excruciatingly slow and may be quicker and more
certain than figuring out how to get a working network connection in
some random place, if possible at all. If it is some lousy WiFi with
the base station at a distance, a flash drive will be faster. And
sometimes people just want to be sure that their data will be at a
certain place at a certain time without having to rely on a network
that may go down due to external reasons.
I also believe LanyFS (assuming that will be well-implemented) would
be a nice alternative to exFAT, and of course the kludge that is
FAT32. I do have serious doubts about how well it would be adopted.
Even if a single major vendor does not want to implement it or worse,
actively resists attempts to make it work on their platform, then it
already misses its most important goal. Of course, if we give up in
advance then it will certainly never happen?
Alexander
Hi!
> > > I also seriously question the niche of people who want to use a thumb
> > > drive to transfer > 4GB files. Try it sometime and see what a painful
> > > user experience it is....
> >
> > Think for example on consumer devices, for example on most moderns TV
> > you can plug a USB memory disk with videos and play them.
>
> More and more consumer devices, including TV's, are network-enabled.
> I'm not at all convinced the USB memory disk model is the one which
> makes sense --- you can make a much better user experience work if
> you
More and more, but USB sticks are still more common... and exchange
format would be nice.
Currently, I'm just using VFAT + NTFS, but both have disadvantages.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
The question is less about the importance of flash drives (or any
portable hard drive) today or embedded appliances, but the importance
of them in 3-5+ years time (dev time + adoption time).
On Mon, Aug 20, 2012 at 11:12:07AM +0200, Alexander Thomas wrote:
>
> Flash drives are getting faster as well. Copying an 8GB file to/from a
> USB drive is not excruciatingly slow and may be quicker and more
> certain than figuring out how to get a working network connection in
> some random place, if possible at all. If it is some lousy WiFi with
> the base station at a distance, a flash drive will be faster. And
> sometimes people just want to be sure that their data will be at a
> certain place at a certain time without having to rely on a network
> that may go down due to external reasons.
OK --- and how many of these situations will you be using such a
stripped down operating system that you can't afford to implement ntfs
or ext4? Samsung is considering an Android-power point-and-shoot
digital camera[1]. And mobile phones are using Android phones where
implementation of ext4 is a worked example (and not particularly
difficult, either).
If this file system had gotten implemented for Arduino first (and
someone actually had a worked example of why you would need > 4GB
vfiles for an Arduino device --- if I were going to be implementing
something with video I'd probably be using a full Linux kernel), the
claimed use case might be more compelling. It would also demonstrate
that in fact this was a decent file system for an Arduino device (and
to demonstrate the infinite stack recursion problem wouldn't be a
gaping security hole problem for them either).
I used to think that we would need an IP unencumbered file system,
given issues around TomTom and Microsoft, but these days, given how
quickly Linux has taken over the embedded and mobile landscape[2] for
all but the most tiniest of devices, I don't think that's as important
of an issue, since we can just simply use a native linux file system.
In the time that it would take to get some other new file system
adopted across the industry, it's likely Linux will have enough market
share to perhaps compel the other OS vendors to provide
interoperability solutions. (Just as the BSD folks have implemented
ext2 support; Linux hasn't bothered to implement FFS2 support....)
- Ted
[1] http://www.engadget.com/2012/03/14/samsung-researching-android-based-digital-camera/
[2] http://money.cnn.com/2012/08/08/technology/smartphone-market-share/index.html
On Sun, 2012-08-19 at 16:12 +0100, Al Viro wrote:
> Conversions *in* *place* are bad. [+explanation]
I think I got it now.
On Sun, 2012-08-19 at 17:24 +0200, Marco Stornelli wrote:
> [vmtruncate, dio_wait, locking, d_delete]
Noted!
Thanks both of you!
Regards
Dan
--
Dan Luedtke
http://www.danrl.de
On Sun, 2012-08-19 at 17:04 -0400, Theodore Ts'o wrote:
> I also seriously question the niche of people who want to use a thumb
> drive to transfer > 4GB files. Try it sometime and see what a painful
> user experience it is....
I don't know if LanyFS will it ever make, and to be honest there are
some corporations _not_ interested in its success, but if it makes it,
this process might be less painful in the future. At least I have to
try, and if we have a better solution in a few years, I'd be happy to
drop the idea and go back to watching my movies again.
However, I watch this thread and I take all the answers and mails into
consideration. You are the experts, and I appreciate you take the time
to discuss the general problem of compatibility and LanyFS in
particular.
I don't want to go to deep into the network vs. removable storage
discussion, although it is an interesting one. For some reason this
issue made a lot of noise, and I have to concentrate on replying to
various mails and modifying a lot of code eventually.
Thanks all of you for your comments.
Regards
Dan
--
Dan Luedtke
http://www.danrl.de
Hi,
On Sun, 2012-08-19 at 01:38 +0200, Dan Luedtke wrote:
> This patch introduces the Lanyard Filesystem (LanyFS), a filesystem
> for highly mobile and removable storage devices.
>
Did you have any performance comparison of your file system with others?
Have you any benchmark results? I think that simplicity can be a
valuable thing but performance is a key factor, especially for business
guys.
I think that maybe compression or/and encryption support can be a
valuable feature for such niche of file system that you declared.
Efficient compression support is very important feature for embedded
solutions. Moreover, using of hardware opportunity in the field of
compression or encryption can keep driver code simple.
By the way, what about fault-tolerance of your file system? I don't dive
deeply in documentation of your file systems. But, I think that for USB
sticks or removable storages it is very common situation of sudden
switch off. So, it is very important for your file system to be a very
tolerant to such use-cases. How can you estimate tolerance of your file
system architecture for failure as normal situation?
Moreover, I think that simplicity and strong tolerance to file system
corruption can be a feature. I mean that if you have simple on-disk
layout then, maybe, it is possible to try working in very corrupted
environment also. For the end user, from my point of view, possibility
to work in the case of file system corruption can be very precious
feature.
I think that also for your file system such feature as easy
recoverability of user information in the case of complete corruption of
file system can be very useful thing for an end user. Such easy
recoverability can be achieved by means of on-disk layout and file
system driver's techniques, I think. So, simplicity and easy
recoverability of user data can be a valuable feature also.
With the best regards,
Vyacheslav Dubeyko.
On Monday 20 August 2012, Theodore Ts'o wrote:
> On Mon, Aug 20, 2012 at 11:12:07AM +0200, Alexander Thomas wrote:
> >
> > Flash drives are getting faster as well. Copying an 8GB file to/from a
> > USB drive is not excruciatingly slow and may be quicker and more
> > certain than figuring out how to get a working network connection in
> > some random place, if possible at all. If it is some lousy WiFi with
> > the base station at a distance, a flash drive will be faster. And
> > sometimes people just want to be sure that their data will be at a
> > certain place at a certain time without having to rely on a network
> > that may go down due to external reasons.
Actually, flash drives are not really getting faster as much as you
think, at least not any more. The main source for performance improvements
over the last few years was from increasing the flash page and block
sizes. Writing single bits may get slightly faster over time, but
we are writing more of them at the same time now, which leads to
significant problems:
* The page size is now effectively 16kb on most flash drives, and
writing in 4kb alignments as practically all our file systems do
(including the lanyfs proposal) means we're actually slower now
than we would be with smaller pages, unless you get into the
special case of writing large chunks of data.
* The erase block size is increasing insanely. We're seeing 12MB
and 16MB block size devices now, and our simulations have shown that
our file systems that are not aware of this block size are hitting
a wall at around 4MB.
> I used to think that we would need an IP unencumbered file system,
> given issues around TomTom and Microsoft, but these days, given how
> quickly Linux has taken over the embedded and mobile landscape[2] for
> all but the most tiniest of devices, I don't think that's as important
> of an issue, since we can just simply use a native linux file system.
> In the time that it would take to get some other new file system
> adopted across the industry, it's likely Linux will have enough market
> share to perhaps compel the other OS vendors to provide
> interoperability solutions. (Just as the BSD folks have implemented
> ext2 support; Linux hasn't bothered to implement FFS2 support....)
There will be patches very soon for a new file system from a major
flash vendor that I'm cooperating with. I haven't seen the patches
myself yet, but the design is similar to a prototype that was done
as a thesis I supervised [1]. I hope that the new implementation is
similarly simple to this design, and also able to provide optimum
performance on most flash media.
Arnd
[1] https://wiki.linaro.org/WorkingGroups/Kernel/Specs/flash-file-system-prototype
On Sunday 2012-08-19 15:34, Dan Luedtke wrote:
>I analyzed about 600k file stored on various removable storage devices.
>80 volunteers sent in data about their devices, generated by a program
>(windows) and scripts (linux, bsd, osx) I wrote for that purpose. The
>data shows that people use more complex filesystems as soon as they are
>confronted with problems (mostly the 4GB limit). After that they have
>problems getting their data accessed by other systems. I derived from
>that, that we need a filesystem that is so simple that even unpopular
>operating systems can implement it without having their business plan
>explode.
Those unpopular OSes do not care about *any other* filesystem at all.
The only way to get a filesystem accepted is by making an ISO standard
out of it - or something in that direction - *and* use it thoroughly
in products, like UDF. Even then, support for new revisions of UDF
has been rather slow-coming.
Dan Luedtke <[email protected]> writes:
> +/**
> + * struct lanyfs_opts - mount options
> + * @uid: userid of all files and directories
> + * @gid: grouid of all files and direcotries
> + * @dmask: directory mask
> + * @fmask: file mask
> + * @discard: issue discard requests on block freeing
> + * @flush: force instant writing of changed data
> + */
> +struct lanyfs_opts {
> + uid_t uid;
> + gid_t gid;
A small nit. Those above two lines should be:
kuid_t uid;
kgid_t gid;
> + unsigned int dmask;
> + unsigned int fmask;
> + unsigned int discard:1,
> + flush:1;
> +};
Eric
Hi,
On Sun, Aug 19, 2012 at 5:08 AM, Dan Luedtke <[email protected]> wrote:
> +
> + /* allocate filesystem private data */
> + fsi = kzalloc(sizeof(*fsi), GFP_KERNEL);
> + if (!fsi)
> + return -ENOMEM;
> + spin_lock_init(&fsi->lock);
> + sb->s_fs_info = fsi;
> +
> + /* set blocksize to minimum size for fetching superblock */
> + if (!sb_set_blocksize(sb, 1 << LANYFS_MIN_BLOCKSIZE)) {
> + if (!silent)
> + lanyfs_err(sb, "error setting blocksize to %d bytes",
> + 1 << LANYFS_MIN_BLOCKSIZE);
> + return -EIO;
> + }
> +
> + /* fetch superblock */
> + bh = sb_bread(sb, LANYFS_SUPERBLOCK);
> + if (!bh) {
> + if (!silent)
> + lanyfs_err(sb, "error reading superblock");
> + return -EIO;
> + }
> + lanysb = (struct lanyfs_sb *) bh->b_data;
> +
> + /* check magic */
> + if (lanysb->magic != cpu_to_le32(LANYFS_SUPER_MAGIC)) {
> + if (!silent)
> + lanyfs_info(sb, "bad magic 0x%x",
> + lanysb->magic);
> + goto exit_invalid;
> + }
> + sb->s_magic = LANYFS_SUPER_MAGIC;
> +
> + /* check block type */
> + if (lanysb->type != LANYFS_TYPE_SB) {
> + if (!silent)
> + lanyfs_err(sb, "bad block type 0x%x", lanysb->type);
> + goto exit_invalid;
> + }
> +
> + /* check version */
> + if (lanysb->major > LANYFS_MAJOR_VERSION) {
> + if (!silent)
> + lanyfs_err(sb, "major version mismatch");
> + goto exit_invalid;
> + }
> +
> + /* check address length */
> + if (lanysb->addrlen < LANYFS_MIN_ADDRLEN ||
> + lanysb->addrlen > LANYFS_MAX_ADDRLEN) {
> + if (!silent)
> + lanyfs_err(sb, "unsupported address length");
> + goto exit_invalid;
> + }
> + fsi->addrlen = lanysb->addrlen;
> +
> + /* check blocksize */
> + if (lanysb->blocksize < LANYFS_MIN_BLOCKSIZE ||
> + lanysb->blocksize > LANYFS_MAX_BLOCKSIZE) {
> + if (!silent)
> + lanyfs_err(sb, "unsupported blocksize");
> + goto exit_invalid;
> + }
> + fsi->blocksize = lanysb->blocksize;
> +
> +
> + /* release block buffer */
> + brelse(bh);
> +
> + /* parse mount options */
> + save_mount_options(sb, options);
> + err = lanyfs_super_options(sb, (char *) options, silent);
> + if (err)
> + return err;
> +
> + /* set blocksize to correct size */
> + if (!sb_set_blocksize(sb, 1 << fsi->blocksize)) {
> + if (!silent)
> + lanyfs_err(sb, "error setting blocksize to %d bytes",
> + 1 << fsi->blocksize);
> + return -EIO;
> + }
> + /* default flags */
> + sb->s_maxbytes = 0xffffffff; /* TODO: hmmmmm */
> + sb->s_op = &lanyfs_super_operations;
> + sb->s_time_gran = 1;
> + sb->s_flags = MS_NOSUID | MS_NOATIME | MS_NODIRATIME;
> +
> + /* make root directory */
> + inode = lanyfs_iget(sb, fsi->rootdir);
> + if (!inode)
> + return -ENOMEM;
> +
> + sb->s_root = d_make_root(inode);
> + if (!sb->s_root) {
> + iput(inode);
> + return -ENOMEM;
> + }
> + return 0;
> +
> +exit_invalid:
> + brelse(bh);
> + if (!silent)
> + lanyfs_info(sb, "no valid lanyard filesystem found");
> + return -EINVAL;
> +}
You should free the memory for "fsi" on error.
Regards.