162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Optimized MPEG FS - inode and super operations. 462306a36Sopenharmony_ci * Copyright (C) 2006 Bob Copeland <me@bobcopeland.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/sched.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/vfs.h> 1162306a36Sopenharmony_ci#include <linux/cred.h> 1262306a36Sopenharmony_ci#include <linux/parser.h> 1362306a36Sopenharmony_ci#include <linux/buffer_head.h> 1462306a36Sopenharmony_ci#include <linux/vmalloc.h> 1562306a36Sopenharmony_ci#include <linux/writeback.h> 1662306a36Sopenharmony_ci#include <linux/seq_file.h> 1762306a36Sopenharmony_ci#include <linux/crc-itu-t.h> 1862306a36Sopenharmony_ci#include "omfs.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciMODULE_AUTHOR("Bob Copeland <me@bobcopeland.com>"); 2162306a36Sopenharmony_ciMODULE_DESCRIPTION("OMFS (ReplayTV/Karma) Filesystem for Linux"); 2262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct buffer_head *omfs_bread(struct super_block *sb, sector_t block) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(sb); 2762306a36Sopenharmony_ci if (block >= sbi->s_num_blocks) 2862306a36Sopenharmony_ci return NULL; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return sb_bread(sb, clus_to_blk(sbi, block)); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct inode *omfs_new_inode(struct inode *dir, umode_t mode) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct inode *inode; 3662306a36Sopenharmony_ci u64 new_block; 3762306a36Sopenharmony_ci int err; 3862306a36Sopenharmony_ci int len; 3962306a36Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(dir->i_sb); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci inode = new_inode(dir->i_sb); 4262306a36Sopenharmony_ci if (!inode) 4362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci err = omfs_allocate_range(dir->i_sb, sbi->s_mirrors, sbi->s_mirrors, 4662306a36Sopenharmony_ci &new_block, &len); 4762306a36Sopenharmony_ci if (err) 4862306a36Sopenharmony_ci goto fail; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci inode->i_ino = new_block; 5162306a36Sopenharmony_ci inode_init_owner(&nop_mnt_idmap, inode, NULL, mode); 5262306a36Sopenharmony_ci inode->i_mapping->a_ops = &omfs_aops; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode); 5562306a36Sopenharmony_ci switch (mode & S_IFMT) { 5662306a36Sopenharmony_ci case S_IFDIR: 5762306a36Sopenharmony_ci inode->i_op = &omfs_dir_inops; 5862306a36Sopenharmony_ci inode->i_fop = &omfs_dir_operations; 5962306a36Sopenharmony_ci inode->i_size = sbi->s_sys_blocksize; 6062306a36Sopenharmony_ci inc_nlink(inode); 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci case S_IFREG: 6362306a36Sopenharmony_ci inode->i_op = &omfs_file_inops; 6462306a36Sopenharmony_ci inode->i_fop = &omfs_file_operations; 6562306a36Sopenharmony_ci inode->i_size = 0; 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci insert_inode_hash(inode); 7062306a36Sopenharmony_ci mark_inode_dirty(inode); 7162306a36Sopenharmony_ci return inode; 7262306a36Sopenharmony_cifail: 7362306a36Sopenharmony_ci make_bad_inode(inode); 7462306a36Sopenharmony_ci iput(inode); 7562306a36Sopenharmony_ci return ERR_PTR(err); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * Update the header checksums for a dirty inode based on its contents. 8062306a36Sopenharmony_ci * Caller is expected to hold the buffer head underlying oi and mark it 8162306a36Sopenharmony_ci * dirty. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_cistatic void omfs_update_checksums(struct omfs_inode *oi) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci int xor, i, ofs = 0, count; 8662306a36Sopenharmony_ci u16 crc = 0; 8762306a36Sopenharmony_ci unsigned char *ptr = (unsigned char *) oi; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci count = be32_to_cpu(oi->i_head.h_body_size); 9062306a36Sopenharmony_ci ofs = sizeof(struct omfs_header); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci crc = crc_itu_t(crc, ptr + ofs, count); 9362306a36Sopenharmony_ci oi->i_head.h_crc = cpu_to_be16(crc); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci xor = ptr[0]; 9662306a36Sopenharmony_ci for (i = 1; i < OMFS_XOR_COUNT; i++) 9762306a36Sopenharmony_ci xor ^= ptr[i]; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci oi->i_head.h_check_xor = xor; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int __omfs_write_inode(struct inode *inode, int wait) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct omfs_inode *oi; 10562306a36Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb); 10662306a36Sopenharmony_ci struct buffer_head *bh, *bh2; 10762306a36Sopenharmony_ci u64 ctime; 10862306a36Sopenharmony_ci int i; 10962306a36Sopenharmony_ci int ret = -EIO; 11062306a36Sopenharmony_ci int sync_failed = 0; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* get current inode since we may have written sibling ptrs etc. */ 11362306a36Sopenharmony_ci bh = omfs_bread(inode->i_sb, inode->i_ino); 11462306a36Sopenharmony_ci if (!bh) 11562306a36Sopenharmony_ci goto out; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci oi = (struct omfs_inode *) bh->b_data; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci oi->i_head.h_self = cpu_to_be64(inode->i_ino); 12062306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 12162306a36Sopenharmony_ci oi->i_type = OMFS_DIR; 12262306a36Sopenharmony_ci else if (S_ISREG(inode->i_mode)) 12362306a36Sopenharmony_ci oi->i_type = OMFS_FILE; 12462306a36Sopenharmony_ci else { 12562306a36Sopenharmony_ci printk(KERN_WARNING "omfs: unknown file type: %d\n", 12662306a36Sopenharmony_ci inode->i_mode); 12762306a36Sopenharmony_ci goto out_brelse; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci oi->i_head.h_body_size = cpu_to_be32(sbi->s_sys_blocksize - 13162306a36Sopenharmony_ci sizeof(struct omfs_header)); 13262306a36Sopenharmony_ci oi->i_head.h_version = 1; 13362306a36Sopenharmony_ci oi->i_head.h_type = OMFS_INODE_NORMAL; 13462306a36Sopenharmony_ci oi->i_head.h_magic = OMFS_IMAGIC; 13562306a36Sopenharmony_ci oi->i_size = cpu_to_be64(inode->i_size); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ctime = inode_get_ctime(inode).tv_sec * 1000LL + 13862306a36Sopenharmony_ci ((inode_get_ctime(inode).tv_nsec + 999)/1000); 13962306a36Sopenharmony_ci oi->i_ctime = cpu_to_be64(ctime); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci omfs_update_checksums(oi); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci mark_buffer_dirty(bh); 14462306a36Sopenharmony_ci if (wait) { 14562306a36Sopenharmony_ci sync_dirty_buffer(bh); 14662306a36Sopenharmony_ci if (buffer_req(bh) && !buffer_uptodate(bh)) 14762306a36Sopenharmony_ci sync_failed = 1; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* if mirroring writes, copy to next fsblock */ 15162306a36Sopenharmony_ci for (i = 1; i < sbi->s_mirrors; i++) { 15262306a36Sopenharmony_ci bh2 = omfs_bread(inode->i_sb, inode->i_ino + i); 15362306a36Sopenharmony_ci if (!bh2) 15462306a36Sopenharmony_ci goto out_brelse; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci memcpy(bh2->b_data, bh->b_data, bh->b_size); 15762306a36Sopenharmony_ci mark_buffer_dirty(bh2); 15862306a36Sopenharmony_ci if (wait) { 15962306a36Sopenharmony_ci sync_dirty_buffer(bh2); 16062306a36Sopenharmony_ci if (buffer_req(bh2) && !buffer_uptodate(bh2)) 16162306a36Sopenharmony_ci sync_failed = 1; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci brelse(bh2); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci ret = (sync_failed) ? -EIO : 0; 16662306a36Sopenharmony_ciout_brelse: 16762306a36Sopenharmony_ci brelse(bh); 16862306a36Sopenharmony_ciout: 16962306a36Sopenharmony_ci return ret; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int omfs_write_inode(struct inode *inode, struct writeback_control *wbc) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci return __omfs_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciint omfs_sync_inode(struct inode *inode) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci return __omfs_write_inode(inode, 1); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* 18362306a36Sopenharmony_ci * called when an entry is deleted, need to clear the bits in the 18462306a36Sopenharmony_ci * bitmaps. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistatic void omfs_evict_inode(struct inode *inode) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci truncate_inode_pages_final(&inode->i_data); 18962306a36Sopenharmony_ci clear_inode(inode); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (inode->i_nlink) 19262306a36Sopenharmony_ci return; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) { 19562306a36Sopenharmony_ci inode->i_size = 0; 19662306a36Sopenharmony_ci omfs_shrink_inode(inode); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci omfs_clear_range(inode->i_sb, inode->i_ino, 2); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistruct inode *omfs_iget(struct super_block *sb, ino_t ino) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(sb); 20562306a36Sopenharmony_ci struct omfs_inode *oi; 20662306a36Sopenharmony_ci struct buffer_head *bh; 20762306a36Sopenharmony_ci u64 ctime; 20862306a36Sopenharmony_ci unsigned long nsecs; 20962306a36Sopenharmony_ci struct inode *inode; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci inode = iget_locked(sb, ino); 21262306a36Sopenharmony_ci if (!inode) 21362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 21462306a36Sopenharmony_ci if (!(inode->i_state & I_NEW)) 21562306a36Sopenharmony_ci return inode; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci bh = omfs_bread(inode->i_sb, ino); 21862306a36Sopenharmony_ci if (!bh) 21962306a36Sopenharmony_ci goto iget_failed; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci oi = (struct omfs_inode *)bh->b_data; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* check self */ 22462306a36Sopenharmony_ci if (ino != be64_to_cpu(oi->i_head.h_self)) 22562306a36Sopenharmony_ci goto fail_bh; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci inode->i_uid = sbi->s_uid; 22862306a36Sopenharmony_ci inode->i_gid = sbi->s_gid; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ctime = be64_to_cpu(oi->i_ctime); 23162306a36Sopenharmony_ci nsecs = do_div(ctime, 1000) * 1000L; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci inode->i_atime.tv_sec = ctime; 23462306a36Sopenharmony_ci inode->i_mtime.tv_sec = ctime; 23562306a36Sopenharmony_ci inode_set_ctime(inode, ctime, nsecs); 23662306a36Sopenharmony_ci inode->i_atime.tv_nsec = nsecs; 23762306a36Sopenharmony_ci inode->i_mtime.tv_nsec = nsecs; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci inode->i_mapping->a_ops = &omfs_aops; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci switch (oi->i_type) { 24262306a36Sopenharmony_ci case OMFS_DIR: 24362306a36Sopenharmony_ci inode->i_mode = S_IFDIR | (S_IRWXUGO & ~sbi->s_dmask); 24462306a36Sopenharmony_ci inode->i_op = &omfs_dir_inops; 24562306a36Sopenharmony_ci inode->i_fop = &omfs_dir_operations; 24662306a36Sopenharmony_ci inode->i_size = sbi->s_sys_blocksize; 24762306a36Sopenharmony_ci inc_nlink(inode); 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci case OMFS_FILE: 25062306a36Sopenharmony_ci inode->i_mode = S_IFREG | (S_IRWXUGO & ~sbi->s_fmask); 25162306a36Sopenharmony_ci inode->i_fop = &omfs_file_operations; 25262306a36Sopenharmony_ci inode->i_size = be64_to_cpu(oi->i_size); 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci brelse(bh); 25662306a36Sopenharmony_ci unlock_new_inode(inode); 25762306a36Sopenharmony_ci return inode; 25862306a36Sopenharmony_cifail_bh: 25962306a36Sopenharmony_ci brelse(bh); 26062306a36Sopenharmony_ciiget_failed: 26162306a36Sopenharmony_ci iget_failed(inode); 26262306a36Sopenharmony_ci return ERR_PTR(-EIO); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void omfs_put_super(struct super_block *sb) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(sb); 26862306a36Sopenharmony_ci kfree(sbi->s_imap); 26962306a36Sopenharmony_ci kfree(sbi); 27062306a36Sopenharmony_ci sb->s_fs_info = NULL; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int omfs_statfs(struct dentry *dentry, struct kstatfs *buf) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct super_block *s = dentry->d_sb; 27662306a36Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(s); 27762306a36Sopenharmony_ci u64 id = huge_encode_dev(s->s_bdev->bd_dev); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci buf->f_type = OMFS_MAGIC; 28062306a36Sopenharmony_ci buf->f_bsize = sbi->s_blocksize; 28162306a36Sopenharmony_ci buf->f_blocks = sbi->s_num_blocks; 28262306a36Sopenharmony_ci buf->f_files = sbi->s_num_blocks; 28362306a36Sopenharmony_ci buf->f_namelen = OMFS_NAMELEN; 28462306a36Sopenharmony_ci buf->f_fsid = u64_to_fsid(id); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci buf->f_bfree = buf->f_bavail = buf->f_ffree = 28762306a36Sopenharmony_ci omfs_count_free(s); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return 0; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/* 29362306a36Sopenharmony_ci * Display the mount options in /proc/mounts. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_cistatic int omfs_show_options(struct seq_file *m, struct dentry *root) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(root->d_sb); 29862306a36Sopenharmony_ci umode_t cur_umask = current_umask(); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (!uid_eq(sbi->s_uid, current_uid())) 30162306a36Sopenharmony_ci seq_printf(m, ",uid=%u", 30262306a36Sopenharmony_ci from_kuid_munged(&init_user_ns, sbi->s_uid)); 30362306a36Sopenharmony_ci if (!gid_eq(sbi->s_gid, current_gid())) 30462306a36Sopenharmony_ci seq_printf(m, ",gid=%u", 30562306a36Sopenharmony_ci from_kgid_munged(&init_user_ns, sbi->s_gid)); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (sbi->s_dmask == sbi->s_fmask) { 30862306a36Sopenharmony_ci if (sbi->s_fmask != cur_umask) 30962306a36Sopenharmony_ci seq_printf(m, ",umask=%o", sbi->s_fmask); 31062306a36Sopenharmony_ci } else { 31162306a36Sopenharmony_ci if (sbi->s_dmask != cur_umask) 31262306a36Sopenharmony_ci seq_printf(m, ",dmask=%o", sbi->s_dmask); 31362306a36Sopenharmony_ci if (sbi->s_fmask != cur_umask) 31462306a36Sopenharmony_ci seq_printf(m, ",fmask=%o", sbi->s_fmask); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic const struct super_operations omfs_sops = { 32162306a36Sopenharmony_ci .write_inode = omfs_write_inode, 32262306a36Sopenharmony_ci .evict_inode = omfs_evict_inode, 32362306a36Sopenharmony_ci .put_super = omfs_put_super, 32462306a36Sopenharmony_ci .statfs = omfs_statfs, 32562306a36Sopenharmony_ci .show_options = omfs_show_options, 32662306a36Sopenharmony_ci}; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/* 32962306a36Sopenharmony_ci * For Rio Karma, there is an on-disk free bitmap whose location is 33062306a36Sopenharmony_ci * stored in the root block. For ReplayTV, there is no such free bitmap 33162306a36Sopenharmony_ci * so we have to walk the tree. Both inodes and file data are allocated 33262306a36Sopenharmony_ci * from the same map. This array can be big (300k) so we allocate 33362306a36Sopenharmony_ci * in units of the blocksize. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_cistatic int omfs_get_imap(struct super_block *sb) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci unsigned int bitmap_size, array_size; 33862306a36Sopenharmony_ci int count; 33962306a36Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(sb); 34062306a36Sopenharmony_ci struct buffer_head *bh; 34162306a36Sopenharmony_ci unsigned long **ptr; 34262306a36Sopenharmony_ci sector_t block; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci bitmap_size = DIV_ROUND_UP(sbi->s_num_blocks, 8); 34562306a36Sopenharmony_ci array_size = DIV_ROUND_UP(bitmap_size, sb->s_blocksize); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (sbi->s_bitmap_ino == ~0ULL) 34862306a36Sopenharmony_ci goto out; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci sbi->s_imap_size = array_size; 35162306a36Sopenharmony_ci sbi->s_imap = kcalloc(array_size, sizeof(unsigned long *), GFP_KERNEL); 35262306a36Sopenharmony_ci if (!sbi->s_imap) 35362306a36Sopenharmony_ci goto nomem; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci block = clus_to_blk(sbi, sbi->s_bitmap_ino); 35662306a36Sopenharmony_ci if (block >= sbi->s_num_blocks) 35762306a36Sopenharmony_ci goto nomem; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ptr = sbi->s_imap; 36062306a36Sopenharmony_ci for (count = bitmap_size; count > 0; count -= sb->s_blocksize) { 36162306a36Sopenharmony_ci bh = sb_bread(sb, block++); 36262306a36Sopenharmony_ci if (!bh) 36362306a36Sopenharmony_ci goto nomem_free; 36462306a36Sopenharmony_ci *ptr = kmemdup(bh->b_data, sb->s_blocksize, GFP_KERNEL); 36562306a36Sopenharmony_ci if (!*ptr) { 36662306a36Sopenharmony_ci brelse(bh); 36762306a36Sopenharmony_ci goto nomem_free; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci if (count < sb->s_blocksize) 37062306a36Sopenharmony_ci memset((void *)*ptr + count, 0xff, 37162306a36Sopenharmony_ci sb->s_blocksize - count); 37262306a36Sopenharmony_ci brelse(bh); 37362306a36Sopenharmony_ci ptr++; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ciout: 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cinomem_free: 37962306a36Sopenharmony_ci for (count = 0; count < array_size; count++) 38062306a36Sopenharmony_ci kfree(sbi->s_imap[count]); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci kfree(sbi->s_imap); 38362306a36Sopenharmony_cinomem: 38462306a36Sopenharmony_ci sbi->s_imap = NULL; 38562306a36Sopenharmony_ci sbi->s_imap_size = 0; 38662306a36Sopenharmony_ci return -ENOMEM; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cienum { 39062306a36Sopenharmony_ci Opt_uid, Opt_gid, Opt_umask, Opt_dmask, Opt_fmask, Opt_err 39162306a36Sopenharmony_ci}; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic const match_table_t tokens = { 39462306a36Sopenharmony_ci {Opt_uid, "uid=%u"}, 39562306a36Sopenharmony_ci {Opt_gid, "gid=%u"}, 39662306a36Sopenharmony_ci {Opt_umask, "umask=%o"}, 39762306a36Sopenharmony_ci {Opt_dmask, "dmask=%o"}, 39862306a36Sopenharmony_ci {Opt_fmask, "fmask=%o"}, 39962306a36Sopenharmony_ci {Opt_err, NULL}, 40062306a36Sopenharmony_ci}; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int parse_options(char *options, struct omfs_sb_info *sbi) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci char *p; 40562306a36Sopenharmony_ci substring_t args[MAX_OPT_ARGS]; 40662306a36Sopenharmony_ci int option; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (!options) 40962306a36Sopenharmony_ci return 1; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci while ((p = strsep(&options, ",")) != NULL) { 41262306a36Sopenharmony_ci int token; 41362306a36Sopenharmony_ci if (!*p) 41462306a36Sopenharmony_ci continue; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci token = match_token(p, tokens, args); 41762306a36Sopenharmony_ci switch (token) { 41862306a36Sopenharmony_ci case Opt_uid: 41962306a36Sopenharmony_ci if (match_int(&args[0], &option)) 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci sbi->s_uid = make_kuid(current_user_ns(), option); 42262306a36Sopenharmony_ci if (!uid_valid(sbi->s_uid)) 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci case Opt_gid: 42662306a36Sopenharmony_ci if (match_int(&args[0], &option)) 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci sbi->s_gid = make_kgid(current_user_ns(), option); 42962306a36Sopenharmony_ci if (!gid_valid(sbi->s_gid)) 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci break; 43262306a36Sopenharmony_ci case Opt_umask: 43362306a36Sopenharmony_ci if (match_octal(&args[0], &option)) 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci sbi->s_fmask = sbi->s_dmask = option; 43662306a36Sopenharmony_ci break; 43762306a36Sopenharmony_ci case Opt_dmask: 43862306a36Sopenharmony_ci if (match_octal(&args[0], &option)) 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_ci sbi->s_dmask = option; 44162306a36Sopenharmony_ci break; 44262306a36Sopenharmony_ci case Opt_fmask: 44362306a36Sopenharmony_ci if (match_octal(&args[0], &option)) 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci sbi->s_fmask = option; 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci default: 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci return 1; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int omfs_fill_super(struct super_block *sb, void *data, int silent) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct buffer_head *bh, *bh2; 45762306a36Sopenharmony_ci struct omfs_super_block *omfs_sb; 45862306a36Sopenharmony_ci struct omfs_root_block *omfs_rb; 45962306a36Sopenharmony_ci struct omfs_sb_info *sbi; 46062306a36Sopenharmony_ci struct inode *root; 46162306a36Sopenharmony_ci int ret = -EINVAL; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci sbi = kzalloc(sizeof(struct omfs_sb_info), GFP_KERNEL); 46462306a36Sopenharmony_ci if (!sbi) 46562306a36Sopenharmony_ci return -ENOMEM; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci sb->s_fs_info = sbi; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci sbi->s_uid = current_uid(); 47062306a36Sopenharmony_ci sbi->s_gid = current_gid(); 47162306a36Sopenharmony_ci sbi->s_dmask = sbi->s_fmask = current_umask(); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (!parse_options((char *) data, sbi)) 47462306a36Sopenharmony_ci goto end; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci sb->s_maxbytes = 0xffffffff; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci sb->s_time_gran = NSEC_PER_MSEC; 47962306a36Sopenharmony_ci sb->s_time_min = 0; 48062306a36Sopenharmony_ci sb->s_time_max = U64_MAX / MSEC_PER_SEC; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci sb_set_blocksize(sb, 0x200); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci bh = sb_bread(sb, 0); 48562306a36Sopenharmony_ci if (!bh) 48662306a36Sopenharmony_ci goto end; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci omfs_sb = (struct omfs_super_block *)bh->b_data; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (omfs_sb->s_magic != cpu_to_be32(OMFS_MAGIC)) { 49162306a36Sopenharmony_ci if (!silent) 49262306a36Sopenharmony_ci printk(KERN_ERR "omfs: Invalid superblock (%x)\n", 49362306a36Sopenharmony_ci omfs_sb->s_magic); 49462306a36Sopenharmony_ci goto out_brelse_bh; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci sb->s_magic = OMFS_MAGIC; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci sbi->s_num_blocks = be64_to_cpu(omfs_sb->s_num_blocks); 49962306a36Sopenharmony_ci sbi->s_blocksize = be32_to_cpu(omfs_sb->s_blocksize); 50062306a36Sopenharmony_ci sbi->s_mirrors = be32_to_cpu(omfs_sb->s_mirrors); 50162306a36Sopenharmony_ci sbi->s_root_ino = be64_to_cpu(omfs_sb->s_root_block); 50262306a36Sopenharmony_ci sbi->s_sys_blocksize = be32_to_cpu(omfs_sb->s_sys_blocksize); 50362306a36Sopenharmony_ci mutex_init(&sbi->s_bitmap_lock); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (sbi->s_num_blocks > OMFS_MAX_BLOCKS) { 50662306a36Sopenharmony_ci printk(KERN_ERR "omfs: sysblock number (%llx) is out of range\n", 50762306a36Sopenharmony_ci (unsigned long long)sbi->s_num_blocks); 50862306a36Sopenharmony_ci goto out_brelse_bh; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (sbi->s_sys_blocksize > PAGE_SIZE) { 51262306a36Sopenharmony_ci printk(KERN_ERR "omfs: sysblock size (%d) is out of range\n", 51362306a36Sopenharmony_ci sbi->s_sys_blocksize); 51462306a36Sopenharmony_ci goto out_brelse_bh; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (sbi->s_blocksize < sbi->s_sys_blocksize || 51862306a36Sopenharmony_ci sbi->s_blocksize > OMFS_MAX_BLOCK_SIZE) { 51962306a36Sopenharmony_ci printk(KERN_ERR "omfs: block size (%d) is out of range\n", 52062306a36Sopenharmony_ci sbi->s_blocksize); 52162306a36Sopenharmony_ci goto out_brelse_bh; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* 52562306a36Sopenharmony_ci * Use sys_blocksize as the fs block since it is smaller than a 52662306a36Sopenharmony_ci * page while the fs blocksize can be larger. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ci sb_set_blocksize(sb, sbi->s_sys_blocksize); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* 53162306a36Sopenharmony_ci * ...and the difference goes into a shift. sys_blocksize is always 53262306a36Sopenharmony_ci * a power of two factor of blocksize. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci sbi->s_block_shift = get_bitmask_order(sbi->s_blocksize) - 53562306a36Sopenharmony_ci get_bitmask_order(sbi->s_sys_blocksize); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci bh2 = omfs_bread(sb, be64_to_cpu(omfs_sb->s_root_block)); 53862306a36Sopenharmony_ci if (!bh2) 53962306a36Sopenharmony_ci goto out_brelse_bh; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci omfs_rb = (struct omfs_root_block *)bh2->b_data; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci sbi->s_bitmap_ino = be64_to_cpu(omfs_rb->r_bitmap); 54462306a36Sopenharmony_ci sbi->s_clustersize = be32_to_cpu(omfs_rb->r_clustersize); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (sbi->s_num_blocks != be64_to_cpu(omfs_rb->r_num_blocks)) { 54762306a36Sopenharmony_ci printk(KERN_ERR "omfs: block count discrepancy between " 54862306a36Sopenharmony_ci "super and root blocks (%llx, %llx)\n", 54962306a36Sopenharmony_ci (unsigned long long)sbi->s_num_blocks, 55062306a36Sopenharmony_ci (unsigned long long)be64_to_cpu(omfs_rb->r_num_blocks)); 55162306a36Sopenharmony_ci goto out_brelse_bh2; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (sbi->s_bitmap_ino != ~0ULL && 55562306a36Sopenharmony_ci sbi->s_bitmap_ino > sbi->s_num_blocks) { 55662306a36Sopenharmony_ci printk(KERN_ERR "omfs: free space bitmap location is corrupt " 55762306a36Sopenharmony_ci "(%llx, total blocks %llx)\n", 55862306a36Sopenharmony_ci (unsigned long long) sbi->s_bitmap_ino, 55962306a36Sopenharmony_ci (unsigned long long) sbi->s_num_blocks); 56062306a36Sopenharmony_ci goto out_brelse_bh2; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci if (sbi->s_clustersize < 1 || 56362306a36Sopenharmony_ci sbi->s_clustersize > OMFS_MAX_CLUSTER_SIZE) { 56462306a36Sopenharmony_ci printk(KERN_ERR "omfs: cluster size out of range (%d)", 56562306a36Sopenharmony_ci sbi->s_clustersize); 56662306a36Sopenharmony_ci goto out_brelse_bh2; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ret = omfs_get_imap(sb); 57062306a36Sopenharmony_ci if (ret) 57162306a36Sopenharmony_ci goto out_brelse_bh2; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci sb->s_op = &omfs_sops; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci root = omfs_iget(sb, be64_to_cpu(omfs_rb->r_root_dir)); 57662306a36Sopenharmony_ci if (IS_ERR(root)) { 57762306a36Sopenharmony_ci ret = PTR_ERR(root); 57862306a36Sopenharmony_ci goto out_brelse_bh2; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci sb->s_root = d_make_root(root); 58262306a36Sopenharmony_ci if (!sb->s_root) { 58362306a36Sopenharmony_ci ret = -ENOMEM; 58462306a36Sopenharmony_ci goto out_brelse_bh2; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci printk(KERN_DEBUG "omfs: Mounted volume %s\n", omfs_rb->r_name); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ret = 0; 58962306a36Sopenharmony_ciout_brelse_bh2: 59062306a36Sopenharmony_ci brelse(bh2); 59162306a36Sopenharmony_ciout_brelse_bh: 59262306a36Sopenharmony_ci brelse(bh); 59362306a36Sopenharmony_ciend: 59462306a36Sopenharmony_ci if (ret) 59562306a36Sopenharmony_ci kfree(sbi); 59662306a36Sopenharmony_ci return ret; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic struct dentry *omfs_mount(struct file_system_type *fs_type, 60062306a36Sopenharmony_ci int flags, const char *dev_name, void *data) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci return mount_bdev(fs_type, flags, dev_name, data, omfs_fill_super); 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic struct file_system_type omfs_fs_type = { 60662306a36Sopenharmony_ci .owner = THIS_MODULE, 60762306a36Sopenharmony_ci .name = "omfs", 60862306a36Sopenharmony_ci .mount = omfs_mount, 60962306a36Sopenharmony_ci .kill_sb = kill_block_super, 61062306a36Sopenharmony_ci .fs_flags = FS_REQUIRES_DEV, 61162306a36Sopenharmony_ci}; 61262306a36Sopenharmony_ciMODULE_ALIAS_FS("omfs"); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int __init init_omfs_fs(void) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci return register_filesystem(&omfs_fs_type); 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic void __exit exit_omfs_fs(void) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci unregister_filesystem(&omfs_fs_type); 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cimodule_init(init_omfs_fs); 62562306a36Sopenharmony_cimodule_exit(exit_omfs_fs); 626