18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Optimized MPEG FS - inode and super operations.
48c2ecf20Sopenharmony_ci * Copyright (C) 2006 Bob Copeland <me@bobcopeland.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/sched.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/fs.h>
108c2ecf20Sopenharmony_ci#include <linux/vfs.h>
118c2ecf20Sopenharmony_ci#include <linux/cred.h>
128c2ecf20Sopenharmony_ci#include <linux/parser.h>
138c2ecf20Sopenharmony_ci#include <linux/buffer_head.h>
148c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
158c2ecf20Sopenharmony_ci#include <linux/writeback.h>
168c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
178c2ecf20Sopenharmony_ci#include <linux/crc-itu-t.h>
188c2ecf20Sopenharmony_ci#include "omfs.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bob Copeland <me@bobcopeland.com>");
218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OMFS (ReplayTV/Karma) Filesystem for Linux");
228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistruct buffer_head *omfs_bread(struct super_block *sb, sector_t block)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct omfs_sb_info *sbi = OMFS_SB(sb);
278c2ecf20Sopenharmony_ci	if (block >= sbi->s_num_blocks)
288c2ecf20Sopenharmony_ci		return NULL;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	return sb_bread(sb, clus_to_blk(sbi, block));
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistruct inode *omfs_new_inode(struct inode *dir, umode_t mode)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct inode *inode;
368c2ecf20Sopenharmony_ci	u64 new_block;
378c2ecf20Sopenharmony_ci	int err;
388c2ecf20Sopenharmony_ci	int len;
398c2ecf20Sopenharmony_ci	struct omfs_sb_info *sbi = OMFS_SB(dir->i_sb);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	inode = new_inode(dir->i_sb);
428c2ecf20Sopenharmony_ci	if (!inode)
438c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	err = omfs_allocate_range(dir->i_sb, sbi->s_mirrors, sbi->s_mirrors,
468c2ecf20Sopenharmony_ci			&new_block, &len);
478c2ecf20Sopenharmony_ci	if (err)
488c2ecf20Sopenharmony_ci		goto fail;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	inode->i_ino = new_block;
518c2ecf20Sopenharmony_ci	inode_init_owner(inode, NULL, mode);
528c2ecf20Sopenharmony_ci	inode->i_mapping->a_ops = &omfs_aops;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
558c2ecf20Sopenharmony_ci	switch (mode & S_IFMT) {
568c2ecf20Sopenharmony_ci	case S_IFDIR:
578c2ecf20Sopenharmony_ci		inode->i_op = &omfs_dir_inops;
588c2ecf20Sopenharmony_ci		inode->i_fop = &omfs_dir_operations;
598c2ecf20Sopenharmony_ci		inode->i_size = sbi->s_sys_blocksize;
608c2ecf20Sopenharmony_ci		inc_nlink(inode);
618c2ecf20Sopenharmony_ci		break;
628c2ecf20Sopenharmony_ci	case S_IFREG:
638c2ecf20Sopenharmony_ci		inode->i_op = &omfs_file_inops;
648c2ecf20Sopenharmony_ci		inode->i_fop = &omfs_file_operations;
658c2ecf20Sopenharmony_ci		inode->i_size = 0;
668c2ecf20Sopenharmony_ci		break;
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	insert_inode_hash(inode);
708c2ecf20Sopenharmony_ci	mark_inode_dirty(inode);
718c2ecf20Sopenharmony_ci	return inode;
728c2ecf20Sopenharmony_cifail:
738c2ecf20Sopenharmony_ci	make_bad_inode(inode);
748c2ecf20Sopenharmony_ci	iput(inode);
758c2ecf20Sopenharmony_ci	return ERR_PTR(err);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/*
798c2ecf20Sopenharmony_ci * Update the header checksums for a dirty inode based on its contents.
808c2ecf20Sopenharmony_ci * Caller is expected to hold the buffer head underlying oi and mark it
818c2ecf20Sopenharmony_ci * dirty.
828c2ecf20Sopenharmony_ci */
838c2ecf20Sopenharmony_cistatic void omfs_update_checksums(struct omfs_inode *oi)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	int xor, i, ofs = 0, count;
868c2ecf20Sopenharmony_ci	u16 crc = 0;
878c2ecf20Sopenharmony_ci	unsigned char *ptr = (unsigned char *) oi;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	count = be32_to_cpu(oi->i_head.h_body_size);
908c2ecf20Sopenharmony_ci	ofs = sizeof(struct omfs_header);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	crc = crc_itu_t(crc, ptr + ofs, count);
938c2ecf20Sopenharmony_ci	oi->i_head.h_crc = cpu_to_be16(crc);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	xor = ptr[0];
968c2ecf20Sopenharmony_ci	for (i = 1; i < OMFS_XOR_COUNT; i++)
978c2ecf20Sopenharmony_ci		xor ^= ptr[i];
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	oi->i_head.h_check_xor = xor;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int __omfs_write_inode(struct inode *inode, int wait)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct omfs_inode *oi;
1058c2ecf20Sopenharmony_ci	struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb);
1068c2ecf20Sopenharmony_ci	struct buffer_head *bh, *bh2;
1078c2ecf20Sopenharmony_ci	u64 ctime;
1088c2ecf20Sopenharmony_ci	int i;
1098c2ecf20Sopenharmony_ci	int ret = -EIO;
1108c2ecf20Sopenharmony_ci	int sync_failed = 0;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* get current inode since we may have written sibling ptrs etc. */
1138c2ecf20Sopenharmony_ci	bh = omfs_bread(inode->i_sb, inode->i_ino);
1148c2ecf20Sopenharmony_ci	if (!bh)
1158c2ecf20Sopenharmony_ci		goto out;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	oi = (struct omfs_inode *) bh->b_data;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	oi->i_head.h_self = cpu_to_be64(inode->i_ino);
1208c2ecf20Sopenharmony_ci	if (S_ISDIR(inode->i_mode))
1218c2ecf20Sopenharmony_ci		oi->i_type = OMFS_DIR;
1228c2ecf20Sopenharmony_ci	else if (S_ISREG(inode->i_mode))
1238c2ecf20Sopenharmony_ci		oi->i_type = OMFS_FILE;
1248c2ecf20Sopenharmony_ci	else {
1258c2ecf20Sopenharmony_ci		printk(KERN_WARNING "omfs: unknown file type: %d\n",
1268c2ecf20Sopenharmony_ci			inode->i_mode);
1278c2ecf20Sopenharmony_ci		goto out_brelse;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	oi->i_head.h_body_size = cpu_to_be32(sbi->s_sys_blocksize -
1318c2ecf20Sopenharmony_ci		sizeof(struct omfs_header));
1328c2ecf20Sopenharmony_ci	oi->i_head.h_version = 1;
1338c2ecf20Sopenharmony_ci	oi->i_head.h_type = OMFS_INODE_NORMAL;
1348c2ecf20Sopenharmony_ci	oi->i_head.h_magic = OMFS_IMAGIC;
1358c2ecf20Sopenharmony_ci	oi->i_size = cpu_to_be64(inode->i_size);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	ctime = inode->i_ctime.tv_sec * 1000LL +
1388c2ecf20Sopenharmony_ci		((inode->i_ctime.tv_nsec + 999)/1000);
1398c2ecf20Sopenharmony_ci	oi->i_ctime = cpu_to_be64(ctime);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	omfs_update_checksums(oi);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	mark_buffer_dirty(bh);
1448c2ecf20Sopenharmony_ci	if (wait) {
1458c2ecf20Sopenharmony_ci		sync_dirty_buffer(bh);
1468c2ecf20Sopenharmony_ci		if (buffer_req(bh) && !buffer_uptodate(bh))
1478c2ecf20Sopenharmony_ci			sync_failed = 1;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* if mirroring writes, copy to next fsblock */
1518c2ecf20Sopenharmony_ci	for (i = 1; i < sbi->s_mirrors; i++) {
1528c2ecf20Sopenharmony_ci		bh2 = omfs_bread(inode->i_sb, inode->i_ino + i);
1538c2ecf20Sopenharmony_ci		if (!bh2)
1548c2ecf20Sopenharmony_ci			goto out_brelse;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		memcpy(bh2->b_data, bh->b_data, bh->b_size);
1578c2ecf20Sopenharmony_ci		mark_buffer_dirty(bh2);
1588c2ecf20Sopenharmony_ci		if (wait) {
1598c2ecf20Sopenharmony_ci			sync_dirty_buffer(bh2);
1608c2ecf20Sopenharmony_ci			if (buffer_req(bh2) && !buffer_uptodate(bh2))
1618c2ecf20Sopenharmony_ci				sync_failed = 1;
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci		brelse(bh2);
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci	ret = (sync_failed) ? -EIO : 0;
1668c2ecf20Sopenharmony_ciout_brelse:
1678c2ecf20Sopenharmony_ci	brelse(bh);
1688c2ecf20Sopenharmony_ciout:
1698c2ecf20Sopenharmony_ci	return ret;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int omfs_write_inode(struct inode *inode, struct writeback_control *wbc)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	return __omfs_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ciint omfs_sync_inode(struct inode *inode)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	return __omfs_write_inode(inode, 1);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/*
1838c2ecf20Sopenharmony_ci * called when an entry is deleted, need to clear the bits in the
1848c2ecf20Sopenharmony_ci * bitmaps.
1858c2ecf20Sopenharmony_ci */
1868c2ecf20Sopenharmony_cistatic void omfs_evict_inode(struct inode *inode)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	truncate_inode_pages_final(&inode->i_data);
1898c2ecf20Sopenharmony_ci	clear_inode(inode);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (inode->i_nlink)
1928c2ecf20Sopenharmony_ci		return;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (S_ISREG(inode->i_mode)) {
1958c2ecf20Sopenharmony_ci		inode->i_size = 0;
1968c2ecf20Sopenharmony_ci		omfs_shrink_inode(inode);
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	omfs_clear_range(inode->i_sb, inode->i_ino, 2);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistruct inode *omfs_iget(struct super_block *sb, ino_t ino)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct omfs_sb_info *sbi = OMFS_SB(sb);
2058c2ecf20Sopenharmony_ci	struct omfs_inode *oi;
2068c2ecf20Sopenharmony_ci	struct buffer_head *bh;
2078c2ecf20Sopenharmony_ci	u64 ctime;
2088c2ecf20Sopenharmony_ci	unsigned long nsecs;
2098c2ecf20Sopenharmony_ci	struct inode *inode;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	inode = iget_locked(sb, ino);
2128c2ecf20Sopenharmony_ci	if (!inode)
2138c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2148c2ecf20Sopenharmony_ci	if (!(inode->i_state & I_NEW))
2158c2ecf20Sopenharmony_ci		return inode;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	bh = omfs_bread(inode->i_sb, ino);
2188c2ecf20Sopenharmony_ci	if (!bh)
2198c2ecf20Sopenharmony_ci		goto iget_failed;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	oi = (struct omfs_inode *)bh->b_data;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/* check self */
2248c2ecf20Sopenharmony_ci	if (ino != be64_to_cpu(oi->i_head.h_self))
2258c2ecf20Sopenharmony_ci		goto fail_bh;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	inode->i_uid = sbi->s_uid;
2288c2ecf20Sopenharmony_ci	inode->i_gid = sbi->s_gid;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	ctime = be64_to_cpu(oi->i_ctime);
2318c2ecf20Sopenharmony_ci	nsecs = do_div(ctime, 1000) * 1000L;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	inode->i_atime.tv_sec = ctime;
2348c2ecf20Sopenharmony_ci	inode->i_mtime.tv_sec = ctime;
2358c2ecf20Sopenharmony_ci	inode->i_ctime.tv_sec = ctime;
2368c2ecf20Sopenharmony_ci	inode->i_atime.tv_nsec = nsecs;
2378c2ecf20Sopenharmony_ci	inode->i_mtime.tv_nsec = nsecs;
2388c2ecf20Sopenharmony_ci	inode->i_ctime.tv_nsec = nsecs;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	inode->i_mapping->a_ops = &omfs_aops;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	switch (oi->i_type) {
2438c2ecf20Sopenharmony_ci	case OMFS_DIR:
2448c2ecf20Sopenharmony_ci		inode->i_mode = S_IFDIR | (S_IRWXUGO & ~sbi->s_dmask);
2458c2ecf20Sopenharmony_ci		inode->i_op = &omfs_dir_inops;
2468c2ecf20Sopenharmony_ci		inode->i_fop = &omfs_dir_operations;
2478c2ecf20Sopenharmony_ci		inode->i_size = sbi->s_sys_blocksize;
2488c2ecf20Sopenharmony_ci		inc_nlink(inode);
2498c2ecf20Sopenharmony_ci		break;
2508c2ecf20Sopenharmony_ci	case OMFS_FILE:
2518c2ecf20Sopenharmony_ci		inode->i_mode = S_IFREG | (S_IRWXUGO & ~sbi->s_fmask);
2528c2ecf20Sopenharmony_ci		inode->i_fop = &omfs_file_operations;
2538c2ecf20Sopenharmony_ci		inode->i_size = be64_to_cpu(oi->i_size);
2548c2ecf20Sopenharmony_ci		break;
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci	brelse(bh);
2578c2ecf20Sopenharmony_ci	unlock_new_inode(inode);
2588c2ecf20Sopenharmony_ci	return inode;
2598c2ecf20Sopenharmony_cifail_bh:
2608c2ecf20Sopenharmony_ci	brelse(bh);
2618c2ecf20Sopenharmony_ciiget_failed:
2628c2ecf20Sopenharmony_ci	iget_failed(inode);
2638c2ecf20Sopenharmony_ci	return ERR_PTR(-EIO);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic void omfs_put_super(struct super_block *sb)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct omfs_sb_info *sbi = OMFS_SB(sb);
2698c2ecf20Sopenharmony_ci	kfree(sbi->s_imap);
2708c2ecf20Sopenharmony_ci	kfree(sbi);
2718c2ecf20Sopenharmony_ci	sb->s_fs_info = NULL;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic int omfs_statfs(struct dentry *dentry, struct kstatfs *buf)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct super_block *s = dentry->d_sb;
2778c2ecf20Sopenharmony_ci	struct omfs_sb_info *sbi = OMFS_SB(s);
2788c2ecf20Sopenharmony_ci	u64 id = huge_encode_dev(s->s_bdev->bd_dev);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	buf->f_type = OMFS_MAGIC;
2818c2ecf20Sopenharmony_ci	buf->f_bsize = sbi->s_blocksize;
2828c2ecf20Sopenharmony_ci	buf->f_blocks = sbi->s_num_blocks;
2838c2ecf20Sopenharmony_ci	buf->f_files = sbi->s_num_blocks;
2848c2ecf20Sopenharmony_ci	buf->f_namelen = OMFS_NAMELEN;
2858c2ecf20Sopenharmony_ci	buf->f_fsid = u64_to_fsid(id);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	buf->f_bfree = buf->f_bavail = buf->f_ffree =
2888c2ecf20Sopenharmony_ci		omfs_count_free(s);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return 0;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci/*
2948c2ecf20Sopenharmony_ci * Display the mount options in /proc/mounts.
2958c2ecf20Sopenharmony_ci */
2968c2ecf20Sopenharmony_cistatic int omfs_show_options(struct seq_file *m, struct dentry *root)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct omfs_sb_info *sbi = OMFS_SB(root->d_sb);
2998c2ecf20Sopenharmony_ci	umode_t cur_umask = current_umask();
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (!uid_eq(sbi->s_uid, current_uid()))
3028c2ecf20Sopenharmony_ci		seq_printf(m, ",uid=%u",
3038c2ecf20Sopenharmony_ci			   from_kuid_munged(&init_user_ns, sbi->s_uid));
3048c2ecf20Sopenharmony_ci	if (!gid_eq(sbi->s_gid, current_gid()))
3058c2ecf20Sopenharmony_ci		seq_printf(m, ",gid=%u",
3068c2ecf20Sopenharmony_ci			   from_kgid_munged(&init_user_ns, sbi->s_gid));
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	if (sbi->s_dmask == sbi->s_fmask) {
3098c2ecf20Sopenharmony_ci		if (sbi->s_fmask != cur_umask)
3108c2ecf20Sopenharmony_ci			seq_printf(m, ",umask=%o", sbi->s_fmask);
3118c2ecf20Sopenharmony_ci	} else {
3128c2ecf20Sopenharmony_ci		if (sbi->s_dmask != cur_umask)
3138c2ecf20Sopenharmony_ci			seq_printf(m, ",dmask=%o", sbi->s_dmask);
3148c2ecf20Sopenharmony_ci		if (sbi->s_fmask != cur_umask)
3158c2ecf20Sopenharmony_ci			seq_printf(m, ",fmask=%o", sbi->s_fmask);
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic const struct super_operations omfs_sops = {
3228c2ecf20Sopenharmony_ci	.write_inode	= omfs_write_inode,
3238c2ecf20Sopenharmony_ci	.evict_inode	= omfs_evict_inode,
3248c2ecf20Sopenharmony_ci	.put_super	= omfs_put_super,
3258c2ecf20Sopenharmony_ci	.statfs		= omfs_statfs,
3268c2ecf20Sopenharmony_ci	.show_options	= omfs_show_options,
3278c2ecf20Sopenharmony_ci};
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci/*
3308c2ecf20Sopenharmony_ci * For Rio Karma, there is an on-disk free bitmap whose location is
3318c2ecf20Sopenharmony_ci * stored in the root block.  For ReplayTV, there is no such free bitmap
3328c2ecf20Sopenharmony_ci * so we have to walk the tree.  Both inodes and file data are allocated
3338c2ecf20Sopenharmony_ci * from the same map.  This array can be big (300k) so we allocate
3348c2ecf20Sopenharmony_ci * in units of the blocksize.
3358c2ecf20Sopenharmony_ci */
3368c2ecf20Sopenharmony_cistatic int omfs_get_imap(struct super_block *sb)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	unsigned int bitmap_size, array_size;
3398c2ecf20Sopenharmony_ci	int count;
3408c2ecf20Sopenharmony_ci	struct omfs_sb_info *sbi = OMFS_SB(sb);
3418c2ecf20Sopenharmony_ci	struct buffer_head *bh;
3428c2ecf20Sopenharmony_ci	unsigned long **ptr;
3438c2ecf20Sopenharmony_ci	sector_t block;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	bitmap_size = DIV_ROUND_UP(sbi->s_num_blocks, 8);
3468c2ecf20Sopenharmony_ci	array_size = DIV_ROUND_UP(bitmap_size, sb->s_blocksize);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (sbi->s_bitmap_ino == ~0ULL)
3498c2ecf20Sopenharmony_ci		goto out;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	sbi->s_imap_size = array_size;
3528c2ecf20Sopenharmony_ci	sbi->s_imap = kcalloc(array_size, sizeof(unsigned long *), GFP_KERNEL);
3538c2ecf20Sopenharmony_ci	if (!sbi->s_imap)
3548c2ecf20Sopenharmony_ci		goto nomem;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	block = clus_to_blk(sbi, sbi->s_bitmap_ino);
3578c2ecf20Sopenharmony_ci	if (block >= sbi->s_num_blocks)
3588c2ecf20Sopenharmony_ci		goto nomem;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	ptr = sbi->s_imap;
3618c2ecf20Sopenharmony_ci	for (count = bitmap_size; count > 0; count -= sb->s_blocksize) {
3628c2ecf20Sopenharmony_ci		bh = sb_bread(sb, block++);
3638c2ecf20Sopenharmony_ci		if (!bh)
3648c2ecf20Sopenharmony_ci			goto nomem_free;
3658c2ecf20Sopenharmony_ci		*ptr = kmemdup(bh->b_data, sb->s_blocksize, GFP_KERNEL);
3668c2ecf20Sopenharmony_ci		if (!*ptr) {
3678c2ecf20Sopenharmony_ci			brelse(bh);
3688c2ecf20Sopenharmony_ci			goto nomem_free;
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci		if (count < sb->s_blocksize)
3718c2ecf20Sopenharmony_ci			memset((void *)*ptr + count, 0xff,
3728c2ecf20Sopenharmony_ci				sb->s_blocksize - count);
3738c2ecf20Sopenharmony_ci		brelse(bh);
3748c2ecf20Sopenharmony_ci		ptr++;
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ciout:
3778c2ecf20Sopenharmony_ci	return 0;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cinomem_free:
3808c2ecf20Sopenharmony_ci	for (count = 0; count < array_size; count++)
3818c2ecf20Sopenharmony_ci		kfree(sbi->s_imap[count]);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	kfree(sbi->s_imap);
3848c2ecf20Sopenharmony_cinomem:
3858c2ecf20Sopenharmony_ci	sbi->s_imap = NULL;
3868c2ecf20Sopenharmony_ci	sbi->s_imap_size = 0;
3878c2ecf20Sopenharmony_ci	return -ENOMEM;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cienum {
3918c2ecf20Sopenharmony_ci	Opt_uid, Opt_gid, Opt_umask, Opt_dmask, Opt_fmask, Opt_err
3928c2ecf20Sopenharmony_ci};
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic const match_table_t tokens = {
3958c2ecf20Sopenharmony_ci	{Opt_uid, "uid=%u"},
3968c2ecf20Sopenharmony_ci	{Opt_gid, "gid=%u"},
3978c2ecf20Sopenharmony_ci	{Opt_umask, "umask=%o"},
3988c2ecf20Sopenharmony_ci	{Opt_dmask, "dmask=%o"},
3998c2ecf20Sopenharmony_ci	{Opt_fmask, "fmask=%o"},
4008c2ecf20Sopenharmony_ci	{Opt_err, NULL},
4018c2ecf20Sopenharmony_ci};
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic int parse_options(char *options, struct omfs_sb_info *sbi)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	char *p;
4068c2ecf20Sopenharmony_ci	substring_t args[MAX_OPT_ARGS];
4078c2ecf20Sopenharmony_ci	int option;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (!options)
4108c2ecf20Sopenharmony_ci		return 1;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	while ((p = strsep(&options, ",")) != NULL) {
4138c2ecf20Sopenharmony_ci		int token;
4148c2ecf20Sopenharmony_ci		if (!*p)
4158c2ecf20Sopenharmony_ci			continue;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci		token = match_token(p, tokens, args);
4188c2ecf20Sopenharmony_ci		switch (token) {
4198c2ecf20Sopenharmony_ci		case Opt_uid:
4208c2ecf20Sopenharmony_ci			if (match_int(&args[0], &option))
4218c2ecf20Sopenharmony_ci				return 0;
4228c2ecf20Sopenharmony_ci			sbi->s_uid = make_kuid(current_user_ns(), option);
4238c2ecf20Sopenharmony_ci			if (!uid_valid(sbi->s_uid))
4248c2ecf20Sopenharmony_ci				return 0;
4258c2ecf20Sopenharmony_ci			break;
4268c2ecf20Sopenharmony_ci		case Opt_gid:
4278c2ecf20Sopenharmony_ci			if (match_int(&args[0], &option))
4288c2ecf20Sopenharmony_ci				return 0;
4298c2ecf20Sopenharmony_ci			sbi->s_gid = make_kgid(current_user_ns(), option);
4308c2ecf20Sopenharmony_ci			if (!gid_valid(sbi->s_gid))
4318c2ecf20Sopenharmony_ci				return 0;
4328c2ecf20Sopenharmony_ci			break;
4338c2ecf20Sopenharmony_ci		case Opt_umask:
4348c2ecf20Sopenharmony_ci			if (match_octal(&args[0], &option))
4358c2ecf20Sopenharmony_ci				return 0;
4368c2ecf20Sopenharmony_ci			sbi->s_fmask = sbi->s_dmask = option;
4378c2ecf20Sopenharmony_ci			break;
4388c2ecf20Sopenharmony_ci		case Opt_dmask:
4398c2ecf20Sopenharmony_ci			if (match_octal(&args[0], &option))
4408c2ecf20Sopenharmony_ci				return 0;
4418c2ecf20Sopenharmony_ci			sbi->s_dmask = option;
4428c2ecf20Sopenharmony_ci			break;
4438c2ecf20Sopenharmony_ci		case Opt_fmask:
4448c2ecf20Sopenharmony_ci			if (match_octal(&args[0], &option))
4458c2ecf20Sopenharmony_ci				return 0;
4468c2ecf20Sopenharmony_ci			sbi->s_fmask = option;
4478c2ecf20Sopenharmony_ci			break;
4488c2ecf20Sopenharmony_ci		default:
4498c2ecf20Sopenharmony_ci			return 0;
4508c2ecf20Sopenharmony_ci		}
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci	return 1;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic int omfs_fill_super(struct super_block *sb, void *data, int silent)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	struct buffer_head *bh, *bh2;
4588c2ecf20Sopenharmony_ci	struct omfs_super_block *omfs_sb;
4598c2ecf20Sopenharmony_ci	struct omfs_root_block *omfs_rb;
4608c2ecf20Sopenharmony_ci	struct omfs_sb_info *sbi;
4618c2ecf20Sopenharmony_ci	struct inode *root;
4628c2ecf20Sopenharmony_ci	int ret = -EINVAL;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	sbi = kzalloc(sizeof(struct omfs_sb_info), GFP_KERNEL);
4658c2ecf20Sopenharmony_ci	if (!sbi)
4668c2ecf20Sopenharmony_ci		return -ENOMEM;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	sb->s_fs_info = sbi;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	sbi->s_uid = current_uid();
4718c2ecf20Sopenharmony_ci	sbi->s_gid = current_gid();
4728c2ecf20Sopenharmony_ci	sbi->s_dmask = sbi->s_fmask = current_umask();
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (!parse_options((char *) data, sbi))
4758c2ecf20Sopenharmony_ci		goto end;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	sb->s_maxbytes = 0xffffffff;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	sb->s_time_gran = NSEC_PER_MSEC;
4808c2ecf20Sopenharmony_ci	sb->s_time_min = 0;
4818c2ecf20Sopenharmony_ci	sb->s_time_max = U64_MAX / MSEC_PER_SEC;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	sb_set_blocksize(sb, 0x200);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	bh = sb_bread(sb, 0);
4868c2ecf20Sopenharmony_ci	if (!bh)
4878c2ecf20Sopenharmony_ci		goto end;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	omfs_sb = (struct omfs_super_block *)bh->b_data;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (omfs_sb->s_magic != cpu_to_be32(OMFS_MAGIC)) {
4928c2ecf20Sopenharmony_ci		if (!silent)
4938c2ecf20Sopenharmony_ci			printk(KERN_ERR "omfs: Invalid superblock (%x)\n",
4948c2ecf20Sopenharmony_ci				   omfs_sb->s_magic);
4958c2ecf20Sopenharmony_ci		goto out_brelse_bh;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci	sb->s_magic = OMFS_MAGIC;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	sbi->s_num_blocks = be64_to_cpu(omfs_sb->s_num_blocks);
5008c2ecf20Sopenharmony_ci	sbi->s_blocksize = be32_to_cpu(omfs_sb->s_blocksize);
5018c2ecf20Sopenharmony_ci	sbi->s_mirrors = be32_to_cpu(omfs_sb->s_mirrors);
5028c2ecf20Sopenharmony_ci	sbi->s_root_ino = be64_to_cpu(omfs_sb->s_root_block);
5038c2ecf20Sopenharmony_ci	sbi->s_sys_blocksize = be32_to_cpu(omfs_sb->s_sys_blocksize);
5048c2ecf20Sopenharmony_ci	mutex_init(&sbi->s_bitmap_lock);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (sbi->s_num_blocks > OMFS_MAX_BLOCKS) {
5078c2ecf20Sopenharmony_ci		printk(KERN_ERR "omfs: sysblock number (%llx) is out of range\n",
5088c2ecf20Sopenharmony_ci		       (unsigned long long)sbi->s_num_blocks);
5098c2ecf20Sopenharmony_ci		goto out_brelse_bh;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (sbi->s_sys_blocksize > PAGE_SIZE) {
5138c2ecf20Sopenharmony_ci		printk(KERN_ERR "omfs: sysblock size (%d) is out of range\n",
5148c2ecf20Sopenharmony_ci			sbi->s_sys_blocksize);
5158c2ecf20Sopenharmony_ci		goto out_brelse_bh;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	if (sbi->s_blocksize < sbi->s_sys_blocksize ||
5198c2ecf20Sopenharmony_ci	    sbi->s_blocksize > OMFS_MAX_BLOCK_SIZE) {
5208c2ecf20Sopenharmony_ci		printk(KERN_ERR "omfs: block size (%d) is out of range\n",
5218c2ecf20Sopenharmony_ci			sbi->s_blocksize);
5228c2ecf20Sopenharmony_ci		goto out_brelse_bh;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	/*
5268c2ecf20Sopenharmony_ci	 * Use sys_blocksize as the fs block since it is smaller than a
5278c2ecf20Sopenharmony_ci	 * page while the fs blocksize can be larger.
5288c2ecf20Sopenharmony_ci	 */
5298c2ecf20Sopenharmony_ci	sb_set_blocksize(sb, sbi->s_sys_blocksize);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/*
5328c2ecf20Sopenharmony_ci	 * ...and the difference goes into a shift.  sys_blocksize is always
5338c2ecf20Sopenharmony_ci	 * a power of two factor of blocksize.
5348c2ecf20Sopenharmony_ci	 */
5358c2ecf20Sopenharmony_ci	sbi->s_block_shift = get_bitmask_order(sbi->s_blocksize) -
5368c2ecf20Sopenharmony_ci		get_bitmask_order(sbi->s_sys_blocksize);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	bh2 = omfs_bread(sb, be64_to_cpu(omfs_sb->s_root_block));
5398c2ecf20Sopenharmony_ci	if (!bh2)
5408c2ecf20Sopenharmony_ci		goto out_brelse_bh;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	omfs_rb = (struct omfs_root_block *)bh2->b_data;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	sbi->s_bitmap_ino = be64_to_cpu(omfs_rb->r_bitmap);
5458c2ecf20Sopenharmony_ci	sbi->s_clustersize = be32_to_cpu(omfs_rb->r_clustersize);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (sbi->s_num_blocks != be64_to_cpu(omfs_rb->r_num_blocks)) {
5488c2ecf20Sopenharmony_ci		printk(KERN_ERR "omfs: block count discrepancy between "
5498c2ecf20Sopenharmony_ci			"super and root blocks (%llx, %llx)\n",
5508c2ecf20Sopenharmony_ci			(unsigned long long)sbi->s_num_blocks,
5518c2ecf20Sopenharmony_ci			(unsigned long long)be64_to_cpu(omfs_rb->r_num_blocks));
5528c2ecf20Sopenharmony_ci		goto out_brelse_bh2;
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	if (sbi->s_bitmap_ino != ~0ULL &&
5568c2ecf20Sopenharmony_ci	    sbi->s_bitmap_ino > sbi->s_num_blocks) {
5578c2ecf20Sopenharmony_ci		printk(KERN_ERR "omfs: free space bitmap location is corrupt "
5588c2ecf20Sopenharmony_ci			"(%llx, total blocks %llx)\n",
5598c2ecf20Sopenharmony_ci			(unsigned long long) sbi->s_bitmap_ino,
5608c2ecf20Sopenharmony_ci			(unsigned long long) sbi->s_num_blocks);
5618c2ecf20Sopenharmony_ci		goto out_brelse_bh2;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci	if (sbi->s_clustersize < 1 ||
5648c2ecf20Sopenharmony_ci	    sbi->s_clustersize > OMFS_MAX_CLUSTER_SIZE) {
5658c2ecf20Sopenharmony_ci		printk(KERN_ERR "omfs: cluster size out of range (%d)",
5668c2ecf20Sopenharmony_ci			sbi->s_clustersize);
5678c2ecf20Sopenharmony_ci		goto out_brelse_bh2;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	ret = omfs_get_imap(sb);
5718c2ecf20Sopenharmony_ci	if (ret)
5728c2ecf20Sopenharmony_ci		goto out_brelse_bh2;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	sb->s_op = &omfs_sops;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	root = omfs_iget(sb, be64_to_cpu(omfs_rb->r_root_dir));
5778c2ecf20Sopenharmony_ci	if (IS_ERR(root)) {
5788c2ecf20Sopenharmony_ci		ret = PTR_ERR(root);
5798c2ecf20Sopenharmony_ci		goto out_brelse_bh2;
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	sb->s_root = d_make_root(root);
5838c2ecf20Sopenharmony_ci	if (!sb->s_root) {
5848c2ecf20Sopenharmony_ci		ret = -ENOMEM;
5858c2ecf20Sopenharmony_ci		goto out_brelse_bh2;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "omfs: Mounted volume %s\n", omfs_rb->r_name);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	ret = 0;
5908c2ecf20Sopenharmony_ciout_brelse_bh2:
5918c2ecf20Sopenharmony_ci	brelse(bh2);
5928c2ecf20Sopenharmony_ciout_brelse_bh:
5938c2ecf20Sopenharmony_ci	brelse(bh);
5948c2ecf20Sopenharmony_ciend:
5958c2ecf20Sopenharmony_ci	if (ret)
5968c2ecf20Sopenharmony_ci		kfree(sbi);
5978c2ecf20Sopenharmony_ci	return ret;
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic struct dentry *omfs_mount(struct file_system_type *fs_type,
6018c2ecf20Sopenharmony_ci			int flags, const char *dev_name, void *data)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	return mount_bdev(fs_type, flags, dev_name, data, omfs_fill_super);
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic struct file_system_type omfs_fs_type = {
6078c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
6088c2ecf20Sopenharmony_ci	.name = "omfs",
6098c2ecf20Sopenharmony_ci	.mount = omfs_mount,
6108c2ecf20Sopenharmony_ci	.kill_sb = kill_block_super,
6118c2ecf20Sopenharmony_ci	.fs_flags = FS_REQUIRES_DEV,
6128c2ecf20Sopenharmony_ci};
6138c2ecf20Sopenharmony_ciMODULE_ALIAS_FS("omfs");
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic int __init init_omfs_fs(void)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	return register_filesystem(&omfs_fs_type);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic void __exit exit_omfs_fs(void)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	unregister_filesystem(&omfs_fs_type);
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cimodule_init(init_omfs_fs);
6268c2ecf20Sopenharmony_cimodule_exit(exit_omfs_fs);
627