Minimal fs module won't unmount

Malcolm Beattie (mbeattie@sable.ox.ac.uk)
Tue, 30 Nov 1999 13:00:44 +0000 (GMT)


I'm writing a little "fake" filesystem module which, when mounted on a
mountpoint, makes the root of the new filesystem be a "magic" symlink.
It all works fine except that the filesystem won't unmount. strace
shows that oldumount is returning EINVAL. What the "magic symlink"
does isn't important here and the following cut-down version displays
the same problem. Is it something to do with the fact that the core fs
code expects the root of the new filesystem to be an ordinary
directory or am I missing something else? Here's the cut-down module
which simply makes follow_link appear to be your cwd. You can compile
(under 2.2 or 2.3, though 2.3 is untested) by

cc -c -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -I/usr/src/linux/include nullfs.c

(modifying arch-specific options as necessary) and then doing
# insmod ./nullfs.o
# mkdir /tmp/nullcwd
# mount -t null none /tmp/nullcwd
# ls -l /tmp/nullcwd
lrwxrwxrwx 1 root root 0 Nov 30 12:21 /tmp/nullcwd -> foo
# ls -l /tmp/nullcwd/
...listing of your current working directory...
# umount /tmp/nullcwd
umount: none: not found
umount: /tmp/foo: not mounted
How can I get it to umount properly?

------------------------------ nullfs.c ------------------------------
#define NULL_ROOT_INO 1
#define NULL_SUPER_MAGIC 0x04ab

#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/malloc.h>
#include <linux/locks.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <asm/uaccess.h>

static struct dentry *
null_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
{
return dget(current->fs->pwd);
}

static int null_readlink(struct dentry *dentry, char *buffer, int buflen)
{
if (buflen > 4)
buflen = 4;
copy_to_user(buffer, "foo", buflen);
return buflen;
}

static struct file_operations null_file_operations = {
0 /* all of them */
};

static struct inode_operations null_inode_operations = {
&null_file_operations, /* fops */
0, /* create */
0, /* lookup */
0, /* link */
0, /* unlink */
0, /* symlink */
0, /* mkdir */
0, /* rmdir */
0, /* mknod */
0, /* rename */
null_readlink, /* readlink */
null_follow_link, /* follow_link */
0 /* and all the rest are zero */
};

static void null_put_inode(struct inode *inode)
{
if (inode->i_count == 1)
inode->i_nlink = 0;
}

static int null_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
struct statfs tmp;

tmp.f_type = NULL_SUPER_MAGIC;
tmp.f_bsize = PAGE_SIZE/sizeof(long);
tmp.f_blocks = 0;
tmp.f_bfree = 0;
tmp.f_bavail = 0;
tmp.f_files = 0;
tmp.f_ffree = 0;
tmp.f_namelen = NAME_MAX;
return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}

static void null_read_inode(struct inode * inode)
{
if (inode->i_ino != NULL_ROOT_INO)
printk("nullfs: bad inode number %lu\n", inode->i_ino);

inode->i_mode = S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO;
inode->i_uid = 0;
inode->i_gid = 0;
inode->i_nlink = 1;
inode->i_size = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_blocks = 0;
inode->i_blksize = 1024;
inode->i_op = &null_inode_operations;
}

static void null_put_super(struct super_block *sb)
{
MOD_DEC_USE_COUNT;
}

static struct super_operations null_sops = {
null_read_inode,
0, /* write_inode */
null_put_inode,
0, /* delete_inode */
0, /* notify_change */
null_put_super,
0, /* write_super */
null_statfs,
0, /* remount_fs */
0, /* clear_inode */
0 /* umount_begin */
};

static struct super_block *null_read_super(struct super_block *sb, void *data,
int silent)
{
struct inode *inode;

MOD_INC_USE_COUNT;
lock_super(sb);
sb->s_blocksize = 1024;
sb->s_blocksize_bits = 10;
sb->s_magic = NULL_SUPER_MAGIC;
sb->s_op = &null_sops;

inode = iget(sb, 1);
if (!inode)
goto out_no_root;
#if LINUX_VERSION_CODE < 0x020300
sb->s_root = d_alloc_root(inode, 0);
#else
sb->s_root = d_alloc_root(inode);
#endif
if (!sb->s_root)
goto out_no_root;
unlock_super(sb);
return sb;

out_no_root:
printk("null_read_super: get root inode failed\n");
if (inode)
iput(inode);
sb->s_dev = 0;
unlock_super(sb);
MOD_DEC_USE_COUNT;
return 0;
}

static struct file_system_type null_fs_type = {
"null", 0, null_read_super, 0
};

int init_null_fs(void)
{
return register_filesystem(&null_fs_type);
}

#ifdef MODULE
int init_module(void)
{
return init_null_fs();
}

void cleanup_module(void)
{
unregister_filesystem(&null_fs_type);
}
#endif
------------------------------ nullfs.c ------------------------------

--Malcolm

-- 
Malcolm Beattie <mbeattie@sable.ox.ac.uk>
Unix Systems Programmer
Oxford University Computing Services

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/