18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/hpfs/super.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  mounting, unmounting, error handling
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "hpfs_fn.h"
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/parser.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/statfs.h>
158c2ecf20Sopenharmony_ci#include <linux/magic.h>
168c2ecf20Sopenharmony_ci#include <linux/sched.h>
178c2ecf20Sopenharmony_ci#include <linux/bitmap.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic void mark_dirty(struct super_block *s, int remount)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	if (hpfs_sb(s)->sb_chkdsk && (remount || !sb_rdonly(s))) {
268c2ecf20Sopenharmony_ci		struct buffer_head *bh;
278c2ecf20Sopenharmony_ci		struct hpfs_spare_block *sb;
288c2ecf20Sopenharmony_ci		if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
298c2ecf20Sopenharmony_ci			sb->dirty = 1;
308c2ecf20Sopenharmony_ci			sb->old_wrote = 0;
318c2ecf20Sopenharmony_ci			mark_buffer_dirty(bh);
328c2ecf20Sopenharmony_ci			sync_dirty_buffer(bh);
338c2ecf20Sopenharmony_ci			brelse(bh);
348c2ecf20Sopenharmony_ci		}
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Mark the filesystem clean (mark it dirty for chkdsk if chkdsk==2 or if there
398c2ecf20Sopenharmony_ci   were errors) */
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void unmark_dirty(struct super_block *s)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct buffer_head *bh;
448c2ecf20Sopenharmony_ci	struct hpfs_spare_block *sb;
458c2ecf20Sopenharmony_ci	if (sb_rdonly(s)) return;
468c2ecf20Sopenharmony_ci	sync_blockdev(s->s_bdev);
478c2ecf20Sopenharmony_ci	if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
488c2ecf20Sopenharmony_ci		sb->dirty = hpfs_sb(s)->sb_chkdsk > 1 - hpfs_sb(s)->sb_was_error;
498c2ecf20Sopenharmony_ci		sb->old_wrote = hpfs_sb(s)->sb_chkdsk >= 2 && !hpfs_sb(s)->sb_was_error;
508c2ecf20Sopenharmony_ci		mark_buffer_dirty(bh);
518c2ecf20Sopenharmony_ci		sync_dirty_buffer(bh);
528c2ecf20Sopenharmony_ci		brelse(bh);
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/* Filesystem error... */
578c2ecf20Sopenharmony_civoid hpfs_error(struct super_block *s, const char *fmt, ...)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct va_format vaf;
608c2ecf20Sopenharmony_ci	va_list args;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	va_start(args, fmt);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	vaf.fmt = fmt;
658c2ecf20Sopenharmony_ci	vaf.va = &args;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	pr_err("filesystem error: %pV", &vaf);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	va_end(args);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (!hpfs_sb(s)->sb_was_error) {
728c2ecf20Sopenharmony_ci		if (hpfs_sb(s)->sb_err == 2) {
738c2ecf20Sopenharmony_ci			pr_cont("; crashing the system because you wanted it\n");
748c2ecf20Sopenharmony_ci			mark_dirty(s, 0);
758c2ecf20Sopenharmony_ci			panic("HPFS panic");
768c2ecf20Sopenharmony_ci		} else if (hpfs_sb(s)->sb_err == 1) {
778c2ecf20Sopenharmony_ci			if (sb_rdonly(s))
788c2ecf20Sopenharmony_ci				pr_cont("; already mounted read-only\n");
798c2ecf20Sopenharmony_ci			else {
808c2ecf20Sopenharmony_ci				pr_cont("; remounting read-only\n");
818c2ecf20Sopenharmony_ci				mark_dirty(s, 0);
828c2ecf20Sopenharmony_ci				s->s_flags |= SB_RDONLY;
838c2ecf20Sopenharmony_ci			}
848c2ecf20Sopenharmony_ci		} else if (sb_rdonly(s))
858c2ecf20Sopenharmony_ci				pr_cont("; going on - but anything won't be destroyed because it's read-only\n");
868c2ecf20Sopenharmony_ci		else
878c2ecf20Sopenharmony_ci			pr_cont("; corrupted filesystem mounted read/write - your computer will explode within 20 seconds ... but you wanted it so!\n");
888c2ecf20Sopenharmony_ci	} else
898c2ecf20Sopenharmony_ci		pr_cont("\n");
908c2ecf20Sopenharmony_ci	hpfs_sb(s)->sb_was_error = 1;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/*
948c2ecf20Sopenharmony_ci * A little trick to detect cycles in many hpfs structures and don't let the
958c2ecf20Sopenharmony_ci * kernel crash on corrupted filesystem. When first called, set c2 to 0.
968c2ecf20Sopenharmony_ci *
978c2ecf20Sopenharmony_ci * BTW. chkdsk doesn't detect cycles correctly. When I had 2 lost directories
988c2ecf20Sopenharmony_ci * nested each in other, chkdsk locked up happilly.
998c2ecf20Sopenharmony_ci */
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ciint hpfs_stop_cycles(struct super_block *s, int key, int *c1, int *c2,
1028c2ecf20Sopenharmony_ci		char *msg)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	if (*c2 && *c1 == key) {
1058c2ecf20Sopenharmony_ci		hpfs_error(s, "cycle detected on key %08x in %s", key, msg);
1068c2ecf20Sopenharmony_ci		return 1;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	(*c2)++;
1098c2ecf20Sopenharmony_ci	if (!((*c2 - 1) & *c2)) *c1 = key;
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic void free_sbi(struct hpfs_sb_info *sbi)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	kfree(sbi->sb_cp_table);
1168c2ecf20Sopenharmony_ci	kfree(sbi->sb_bmp_dir);
1178c2ecf20Sopenharmony_ci	kfree(sbi);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic void lazy_free_sbi(struct rcu_head *rcu)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	free_sbi(container_of(rcu, struct hpfs_sb_info, rcu));
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void hpfs_put_super(struct super_block *s)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	hpfs_lock(s);
1288c2ecf20Sopenharmony_ci	unmark_dirty(s);
1298c2ecf20Sopenharmony_ci	hpfs_unlock(s);
1308c2ecf20Sopenharmony_ci	call_rcu(&hpfs_sb(s)->rcu, lazy_free_sbi);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct quad_buffer_head qbh;
1368c2ecf20Sopenharmony_ci	unsigned long *bits;
1378c2ecf20Sopenharmony_ci	unsigned count;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	bits = hpfs_map_4sectors(s, secno, &qbh, 0);
1408c2ecf20Sopenharmony_ci	if (!bits)
1418c2ecf20Sopenharmony_ci		return (unsigned)-1;
1428c2ecf20Sopenharmony_ci	count = bitmap_weight(bits, 2048 * BITS_PER_BYTE);
1438c2ecf20Sopenharmony_ci	hpfs_brelse4(&qbh);
1448c2ecf20Sopenharmony_ci	return count;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic unsigned count_bitmaps(struct super_block *s)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	unsigned n, count, n_bands;
1508c2ecf20Sopenharmony_ci	n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14;
1518c2ecf20Sopenharmony_ci	count = 0;
1528c2ecf20Sopenharmony_ci	for (n = 0; n < COUNT_RD_AHEAD; n++) {
1538c2ecf20Sopenharmony_ci		hpfs_prefetch_bitmap(s, n);
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci	for (n = 0; n < n_bands; n++) {
1568c2ecf20Sopenharmony_ci		unsigned c;
1578c2ecf20Sopenharmony_ci		hpfs_prefetch_bitmap(s, n + COUNT_RD_AHEAD);
1588c2ecf20Sopenharmony_ci		c = hpfs_count_one_bitmap(s, le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[n]));
1598c2ecf20Sopenharmony_ci		if (c != (unsigned)-1)
1608c2ecf20Sopenharmony_ci			count += c;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci	return count;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ciunsigned hpfs_get_free_dnodes(struct super_block *s)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct hpfs_sb_info *sbi = hpfs_sb(s);
1688c2ecf20Sopenharmony_ci	if (sbi->sb_n_free_dnodes == (unsigned)-1) {
1698c2ecf20Sopenharmony_ci		unsigned c = hpfs_count_one_bitmap(s, sbi->sb_dmap);
1708c2ecf20Sopenharmony_ci		if (c == (unsigned)-1)
1718c2ecf20Sopenharmony_ci			return 0;
1728c2ecf20Sopenharmony_ci		sbi->sb_n_free_dnodes = c;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	return sbi->sb_n_free_dnodes;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int hpfs_statfs(struct dentry *dentry, struct kstatfs *buf)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct super_block *s = dentry->d_sb;
1808c2ecf20Sopenharmony_ci	struct hpfs_sb_info *sbi = hpfs_sb(s);
1818c2ecf20Sopenharmony_ci	u64 id = huge_encode_dev(s->s_bdev->bd_dev);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	hpfs_lock(s);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (sbi->sb_n_free == (unsigned)-1)
1868c2ecf20Sopenharmony_ci		sbi->sb_n_free = count_bitmaps(s);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	buf->f_type = s->s_magic;
1898c2ecf20Sopenharmony_ci	buf->f_bsize = 512;
1908c2ecf20Sopenharmony_ci	buf->f_blocks = sbi->sb_fs_size;
1918c2ecf20Sopenharmony_ci	buf->f_bfree = sbi->sb_n_free;
1928c2ecf20Sopenharmony_ci	buf->f_bavail = sbi->sb_n_free;
1938c2ecf20Sopenharmony_ci	buf->f_files = sbi->sb_dirband_size / 4;
1948c2ecf20Sopenharmony_ci	buf->f_ffree = hpfs_get_free_dnodes(s);
1958c2ecf20Sopenharmony_ci	buf->f_fsid = u64_to_fsid(id);
1968c2ecf20Sopenharmony_ci	buf->f_namelen = 254;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	hpfs_unlock(s);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return 0;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cilong hpfs_ioctl(struct file *file, unsigned cmd, unsigned long arg)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	switch (cmd) {
2078c2ecf20Sopenharmony_ci		case FITRIM: {
2088c2ecf20Sopenharmony_ci			struct fstrim_range range;
2098c2ecf20Sopenharmony_ci			secno n_trimmed;
2108c2ecf20Sopenharmony_ci			int r;
2118c2ecf20Sopenharmony_ci			if (!capable(CAP_SYS_ADMIN))
2128c2ecf20Sopenharmony_ci				return -EPERM;
2138c2ecf20Sopenharmony_ci			if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range)))
2148c2ecf20Sopenharmony_ci				return -EFAULT;
2158c2ecf20Sopenharmony_ci			r = hpfs_trim_fs(file_inode(file)->i_sb, range.start >> 9, (range.start + range.len) >> 9, (range.minlen + 511) >> 9, &n_trimmed);
2168c2ecf20Sopenharmony_ci			if (r)
2178c2ecf20Sopenharmony_ci				return r;
2188c2ecf20Sopenharmony_ci			range.len = (u64)n_trimmed << 9;
2198c2ecf20Sopenharmony_ci			if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range)))
2208c2ecf20Sopenharmony_ci				return -EFAULT;
2218c2ecf20Sopenharmony_ci			return 0;
2228c2ecf20Sopenharmony_ci		}
2238c2ecf20Sopenharmony_ci		default: {
2248c2ecf20Sopenharmony_ci			return -ENOIOCTLCMD;
2258c2ecf20Sopenharmony_ci		}
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic struct kmem_cache * hpfs_inode_cachep;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic struct inode *hpfs_alloc_inode(struct super_block *sb)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct hpfs_inode_info *ei;
2358c2ecf20Sopenharmony_ci	ei = kmem_cache_alloc(hpfs_inode_cachep, GFP_NOFS);
2368c2ecf20Sopenharmony_ci	if (!ei)
2378c2ecf20Sopenharmony_ci		return NULL;
2388c2ecf20Sopenharmony_ci	return &ei->vfs_inode;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic void hpfs_free_inode(struct inode *inode)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	kmem_cache_free(hpfs_inode_cachep, hpfs_i(inode));
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic void init_once(void *foo)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	inode_init_once(&ei->vfs_inode);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic int init_inodecache(void)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	hpfs_inode_cachep = kmem_cache_create("hpfs_inode_cache",
2568c2ecf20Sopenharmony_ci					     sizeof(struct hpfs_inode_info),
2578c2ecf20Sopenharmony_ci					     0, (SLAB_RECLAIM_ACCOUNT|
2588c2ecf20Sopenharmony_ci						SLAB_MEM_SPREAD|SLAB_ACCOUNT),
2598c2ecf20Sopenharmony_ci					     init_once);
2608c2ecf20Sopenharmony_ci	if (hpfs_inode_cachep == NULL)
2618c2ecf20Sopenharmony_ci		return -ENOMEM;
2628c2ecf20Sopenharmony_ci	return 0;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic void destroy_inodecache(void)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	/*
2688c2ecf20Sopenharmony_ci	 * Make sure all delayed rcu free inodes are flushed before we
2698c2ecf20Sopenharmony_ci	 * destroy cache.
2708c2ecf20Sopenharmony_ci	 */
2718c2ecf20Sopenharmony_ci	rcu_barrier();
2728c2ecf20Sopenharmony_ci	kmem_cache_destroy(hpfs_inode_cachep);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci/*
2768c2ecf20Sopenharmony_ci * A tiny parser for option strings, stolen from dosfs.
2778c2ecf20Sopenharmony_ci * Stolen again from read-only hpfs.
2788c2ecf20Sopenharmony_ci * And updated for table-driven option parsing.
2798c2ecf20Sopenharmony_ci */
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cienum {
2828c2ecf20Sopenharmony_ci	Opt_help, Opt_uid, Opt_gid, Opt_umask, Opt_case_lower, Opt_case_asis,
2838c2ecf20Sopenharmony_ci	Opt_check_none, Opt_check_normal, Opt_check_strict,
2848c2ecf20Sopenharmony_ci	Opt_err_cont, Opt_err_ro, Opt_err_panic,
2858c2ecf20Sopenharmony_ci	Opt_eas_no, Opt_eas_ro, Opt_eas_rw,
2868c2ecf20Sopenharmony_ci	Opt_chkdsk_no, Opt_chkdsk_errors, Opt_chkdsk_always,
2878c2ecf20Sopenharmony_ci	Opt_timeshift, Opt_err,
2888c2ecf20Sopenharmony_ci};
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic const match_table_t tokens = {
2918c2ecf20Sopenharmony_ci	{Opt_help, "help"},
2928c2ecf20Sopenharmony_ci	{Opt_uid, "uid=%u"},
2938c2ecf20Sopenharmony_ci	{Opt_gid, "gid=%u"},
2948c2ecf20Sopenharmony_ci	{Opt_umask, "umask=%o"},
2958c2ecf20Sopenharmony_ci	{Opt_case_lower, "case=lower"},
2968c2ecf20Sopenharmony_ci	{Opt_case_asis, "case=asis"},
2978c2ecf20Sopenharmony_ci	{Opt_check_none, "check=none"},
2988c2ecf20Sopenharmony_ci	{Opt_check_normal, "check=normal"},
2998c2ecf20Sopenharmony_ci	{Opt_check_strict, "check=strict"},
3008c2ecf20Sopenharmony_ci	{Opt_err_cont, "errors=continue"},
3018c2ecf20Sopenharmony_ci	{Opt_err_ro, "errors=remount-ro"},
3028c2ecf20Sopenharmony_ci	{Opt_err_panic, "errors=panic"},
3038c2ecf20Sopenharmony_ci	{Opt_eas_no, "eas=no"},
3048c2ecf20Sopenharmony_ci	{Opt_eas_ro, "eas=ro"},
3058c2ecf20Sopenharmony_ci	{Opt_eas_rw, "eas=rw"},
3068c2ecf20Sopenharmony_ci	{Opt_chkdsk_no, "chkdsk=no"},
3078c2ecf20Sopenharmony_ci	{Opt_chkdsk_errors, "chkdsk=errors"},
3088c2ecf20Sopenharmony_ci	{Opt_chkdsk_always, "chkdsk=always"},
3098c2ecf20Sopenharmony_ci	{Opt_timeshift, "timeshift=%d"},
3108c2ecf20Sopenharmony_ci	{Opt_err, NULL},
3118c2ecf20Sopenharmony_ci};
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic int parse_opts(char *opts, kuid_t *uid, kgid_t *gid, umode_t *umask,
3148c2ecf20Sopenharmony_ci		      int *lowercase, int *eas, int *chk, int *errs,
3158c2ecf20Sopenharmony_ci		      int *chkdsk, int *timeshift)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	char *p;
3188c2ecf20Sopenharmony_ci	int option;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (!opts)
3218c2ecf20Sopenharmony_ci		return 1;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/*pr_info("Parsing opts: '%s'\n",opts);*/
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	while ((p = strsep(&opts, ",")) != NULL) {
3268c2ecf20Sopenharmony_ci		substring_t args[MAX_OPT_ARGS];
3278c2ecf20Sopenharmony_ci		int token;
3288c2ecf20Sopenharmony_ci		if (!*p)
3298c2ecf20Sopenharmony_ci			continue;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		token = match_token(p, tokens, args);
3328c2ecf20Sopenharmony_ci		switch (token) {
3338c2ecf20Sopenharmony_ci		case Opt_help:
3348c2ecf20Sopenharmony_ci			return 2;
3358c2ecf20Sopenharmony_ci		case Opt_uid:
3368c2ecf20Sopenharmony_ci			if (match_int(args, &option))
3378c2ecf20Sopenharmony_ci				return 0;
3388c2ecf20Sopenharmony_ci			*uid = make_kuid(current_user_ns(), option);
3398c2ecf20Sopenharmony_ci			if (!uid_valid(*uid))
3408c2ecf20Sopenharmony_ci				return 0;
3418c2ecf20Sopenharmony_ci			break;
3428c2ecf20Sopenharmony_ci		case Opt_gid:
3438c2ecf20Sopenharmony_ci			if (match_int(args, &option))
3448c2ecf20Sopenharmony_ci				return 0;
3458c2ecf20Sopenharmony_ci			*gid = make_kgid(current_user_ns(), option);
3468c2ecf20Sopenharmony_ci			if (!gid_valid(*gid))
3478c2ecf20Sopenharmony_ci				return 0;
3488c2ecf20Sopenharmony_ci			break;
3498c2ecf20Sopenharmony_ci		case Opt_umask:
3508c2ecf20Sopenharmony_ci			if (match_octal(args, &option))
3518c2ecf20Sopenharmony_ci				return 0;
3528c2ecf20Sopenharmony_ci			*umask = option;
3538c2ecf20Sopenharmony_ci			break;
3548c2ecf20Sopenharmony_ci		case Opt_case_lower:
3558c2ecf20Sopenharmony_ci			*lowercase = 1;
3568c2ecf20Sopenharmony_ci			break;
3578c2ecf20Sopenharmony_ci		case Opt_case_asis:
3588c2ecf20Sopenharmony_ci			*lowercase = 0;
3598c2ecf20Sopenharmony_ci			break;
3608c2ecf20Sopenharmony_ci		case Opt_check_none:
3618c2ecf20Sopenharmony_ci			*chk = 0;
3628c2ecf20Sopenharmony_ci			break;
3638c2ecf20Sopenharmony_ci		case Opt_check_normal:
3648c2ecf20Sopenharmony_ci			*chk = 1;
3658c2ecf20Sopenharmony_ci			break;
3668c2ecf20Sopenharmony_ci		case Opt_check_strict:
3678c2ecf20Sopenharmony_ci			*chk = 2;
3688c2ecf20Sopenharmony_ci			break;
3698c2ecf20Sopenharmony_ci		case Opt_err_cont:
3708c2ecf20Sopenharmony_ci			*errs = 0;
3718c2ecf20Sopenharmony_ci			break;
3728c2ecf20Sopenharmony_ci		case Opt_err_ro:
3738c2ecf20Sopenharmony_ci			*errs = 1;
3748c2ecf20Sopenharmony_ci			break;
3758c2ecf20Sopenharmony_ci		case Opt_err_panic:
3768c2ecf20Sopenharmony_ci			*errs = 2;
3778c2ecf20Sopenharmony_ci			break;
3788c2ecf20Sopenharmony_ci		case Opt_eas_no:
3798c2ecf20Sopenharmony_ci			*eas = 0;
3808c2ecf20Sopenharmony_ci			break;
3818c2ecf20Sopenharmony_ci		case Opt_eas_ro:
3828c2ecf20Sopenharmony_ci			*eas = 1;
3838c2ecf20Sopenharmony_ci			break;
3848c2ecf20Sopenharmony_ci		case Opt_eas_rw:
3858c2ecf20Sopenharmony_ci			*eas = 2;
3868c2ecf20Sopenharmony_ci			break;
3878c2ecf20Sopenharmony_ci		case Opt_chkdsk_no:
3888c2ecf20Sopenharmony_ci			*chkdsk = 0;
3898c2ecf20Sopenharmony_ci			break;
3908c2ecf20Sopenharmony_ci		case Opt_chkdsk_errors:
3918c2ecf20Sopenharmony_ci			*chkdsk = 1;
3928c2ecf20Sopenharmony_ci			break;
3938c2ecf20Sopenharmony_ci		case Opt_chkdsk_always:
3948c2ecf20Sopenharmony_ci			*chkdsk = 2;
3958c2ecf20Sopenharmony_ci			break;
3968c2ecf20Sopenharmony_ci		case Opt_timeshift:
3978c2ecf20Sopenharmony_ci		{
3988c2ecf20Sopenharmony_ci			int m = 1;
3998c2ecf20Sopenharmony_ci			char *rhs = args[0].from;
4008c2ecf20Sopenharmony_ci			if (!rhs || !*rhs)
4018c2ecf20Sopenharmony_ci				return 0;
4028c2ecf20Sopenharmony_ci			if (*rhs == '-') m = -1;
4038c2ecf20Sopenharmony_ci			if (*rhs == '+' || *rhs == '-') rhs++;
4048c2ecf20Sopenharmony_ci			*timeshift = simple_strtoul(rhs, &rhs, 0) * m;
4058c2ecf20Sopenharmony_ci			if (*rhs)
4068c2ecf20Sopenharmony_ci				return 0;
4078c2ecf20Sopenharmony_ci			break;
4088c2ecf20Sopenharmony_ci		}
4098c2ecf20Sopenharmony_ci		default:
4108c2ecf20Sopenharmony_ci			return 0;
4118c2ecf20Sopenharmony_ci		}
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci	return 1;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic inline void hpfs_help(void)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	pr_info("\n\
4198c2ecf20Sopenharmony_ciHPFS filesystem options:\n\
4208c2ecf20Sopenharmony_ci      help              do not mount and display this text\n\
4218c2ecf20Sopenharmony_ci      uid=xxx           set uid of files that don't have uid specified in eas\n\
4228c2ecf20Sopenharmony_ci      gid=xxx           set gid of files that don't have gid specified in eas\n\
4238c2ecf20Sopenharmony_ci      umask=xxx         set mode of files that don't have mode specified in eas\n\
4248c2ecf20Sopenharmony_ci      case=lower        lowercase all files\n\
4258c2ecf20Sopenharmony_ci      case=asis         do not lowercase files (default)\n\
4268c2ecf20Sopenharmony_ci      check=none        no fs checks - kernel may crash on corrupted filesystem\n\
4278c2ecf20Sopenharmony_ci      check=normal      do some checks - it should not crash (default)\n\
4288c2ecf20Sopenharmony_ci      check=strict      do extra time-consuming checks, used for debugging\n\
4298c2ecf20Sopenharmony_ci      errors=continue   continue on errors\n\
4308c2ecf20Sopenharmony_ci      errors=remount-ro remount read-only if errors found (default)\n\
4318c2ecf20Sopenharmony_ci      errors=panic      panic on errors\n\
4328c2ecf20Sopenharmony_ci      chkdsk=no         do not mark fs for chkdsking even if there were errors\n\
4338c2ecf20Sopenharmony_ci      chkdsk=errors     mark fs dirty if errors found (default)\n\
4348c2ecf20Sopenharmony_ci      chkdsk=always     always mark fs dirty - used for debugging\n\
4358c2ecf20Sopenharmony_ci      eas=no            ignore extended attributes\n\
4368c2ecf20Sopenharmony_ci      eas=ro            read but do not write extended attributes\n\
4378c2ecf20Sopenharmony_ci      eas=rw            r/w eas => enables chmod, chown, mknod, ln -s (default)\n\
4388c2ecf20Sopenharmony_ci      timeshift=nnn	add nnn seconds to file times\n\
4398c2ecf20Sopenharmony_ci\n");
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	kuid_t uid;
4458c2ecf20Sopenharmony_ci	kgid_t gid;
4468c2ecf20Sopenharmony_ci	umode_t umask;
4478c2ecf20Sopenharmony_ci	int lowercase, eas, chk, errs, chkdsk, timeshift;
4488c2ecf20Sopenharmony_ci	int o;
4498c2ecf20Sopenharmony_ci	struct hpfs_sb_info *sbi = hpfs_sb(s);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	sync_filesystem(s);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	*flags |= SB_NOATIME;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	hpfs_lock(s);
4568c2ecf20Sopenharmony_ci	uid = sbi->sb_uid; gid = sbi->sb_gid;
4578c2ecf20Sopenharmony_ci	umask = 0777 & ~sbi->sb_mode;
4588c2ecf20Sopenharmony_ci	lowercase = sbi->sb_lowercase;
4598c2ecf20Sopenharmony_ci	eas = sbi->sb_eas; chk = sbi->sb_chk; chkdsk = sbi->sb_chkdsk;
4608c2ecf20Sopenharmony_ci	errs = sbi->sb_err; timeshift = sbi->sb_timeshift;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase,
4638c2ecf20Sopenharmony_ci	    &eas, &chk, &errs, &chkdsk, &timeshift))) {
4648c2ecf20Sopenharmony_ci		pr_err("bad mount options.\n");
4658c2ecf20Sopenharmony_ci		goto out_err;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci	if (o == 2) {
4688c2ecf20Sopenharmony_ci		hpfs_help();
4698c2ecf20Sopenharmony_ci		goto out_err;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci	if (timeshift != sbi->sb_timeshift) {
4728c2ecf20Sopenharmony_ci		pr_err("timeshift can't be changed using remount.\n");
4738c2ecf20Sopenharmony_ci		goto out_err;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	unmark_dirty(s);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	sbi->sb_uid = uid; sbi->sb_gid = gid;
4798c2ecf20Sopenharmony_ci	sbi->sb_mode = 0777 & ~umask;
4808c2ecf20Sopenharmony_ci	sbi->sb_lowercase = lowercase;
4818c2ecf20Sopenharmony_ci	sbi->sb_eas = eas; sbi->sb_chk = chk; sbi->sb_chkdsk = chkdsk;
4828c2ecf20Sopenharmony_ci	sbi->sb_err = errs; sbi->sb_timeshift = timeshift;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (!(*flags & SB_RDONLY)) mark_dirty(s, 1);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	hpfs_unlock(s);
4878c2ecf20Sopenharmony_ci	return 0;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ciout_err:
4908c2ecf20Sopenharmony_ci	hpfs_unlock(s);
4918c2ecf20Sopenharmony_ci	return -EINVAL;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic int hpfs_show_options(struct seq_file *seq, struct dentry *root)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	struct hpfs_sb_info *sbi = hpfs_sb(root->d_sb);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	seq_printf(seq, ",uid=%u", from_kuid_munged(&init_user_ns, sbi->sb_uid));
4998c2ecf20Sopenharmony_ci	seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, sbi->sb_gid));
5008c2ecf20Sopenharmony_ci	seq_printf(seq, ",umask=%03o", (~sbi->sb_mode & 0777));
5018c2ecf20Sopenharmony_ci	if (sbi->sb_lowercase)
5028c2ecf20Sopenharmony_ci		seq_printf(seq, ",case=lower");
5038c2ecf20Sopenharmony_ci	if (!sbi->sb_chk)
5048c2ecf20Sopenharmony_ci		seq_printf(seq, ",check=none");
5058c2ecf20Sopenharmony_ci	if (sbi->sb_chk == 2)
5068c2ecf20Sopenharmony_ci		seq_printf(seq, ",check=strict");
5078c2ecf20Sopenharmony_ci	if (!sbi->sb_err)
5088c2ecf20Sopenharmony_ci		seq_printf(seq, ",errors=continue");
5098c2ecf20Sopenharmony_ci	if (sbi->sb_err == 2)
5108c2ecf20Sopenharmony_ci		seq_printf(seq, ",errors=panic");
5118c2ecf20Sopenharmony_ci	if (!sbi->sb_chkdsk)
5128c2ecf20Sopenharmony_ci		seq_printf(seq, ",chkdsk=no");
5138c2ecf20Sopenharmony_ci	if (sbi->sb_chkdsk == 2)
5148c2ecf20Sopenharmony_ci		seq_printf(seq, ",chkdsk=always");
5158c2ecf20Sopenharmony_ci	if (!sbi->sb_eas)
5168c2ecf20Sopenharmony_ci		seq_printf(seq, ",eas=no");
5178c2ecf20Sopenharmony_ci	if (sbi->sb_eas == 1)
5188c2ecf20Sopenharmony_ci		seq_printf(seq, ",eas=ro");
5198c2ecf20Sopenharmony_ci	if (sbi->sb_timeshift)
5208c2ecf20Sopenharmony_ci		seq_printf(seq, ",timeshift=%d", sbi->sb_timeshift);
5218c2ecf20Sopenharmony_ci	return 0;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci/* Super operations */
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic const struct super_operations hpfs_sops =
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	.alloc_inode	= hpfs_alloc_inode,
5298c2ecf20Sopenharmony_ci	.free_inode	= hpfs_free_inode,
5308c2ecf20Sopenharmony_ci	.evict_inode	= hpfs_evict_inode,
5318c2ecf20Sopenharmony_ci	.put_super	= hpfs_put_super,
5328c2ecf20Sopenharmony_ci	.statfs		= hpfs_statfs,
5338c2ecf20Sopenharmony_ci	.remount_fs	= hpfs_remount_fs,
5348c2ecf20Sopenharmony_ci	.show_options	= hpfs_show_options,
5358c2ecf20Sopenharmony_ci};
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic int hpfs_fill_super(struct super_block *s, void *options, int silent)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct buffer_head *bh0, *bh1, *bh2;
5408c2ecf20Sopenharmony_ci	struct hpfs_boot_block *bootblock;
5418c2ecf20Sopenharmony_ci	struct hpfs_super_block *superblock;
5428c2ecf20Sopenharmony_ci	struct hpfs_spare_block *spareblock;
5438c2ecf20Sopenharmony_ci	struct hpfs_sb_info *sbi;
5448c2ecf20Sopenharmony_ci	struct inode *root;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	kuid_t uid;
5478c2ecf20Sopenharmony_ci	kgid_t gid;
5488c2ecf20Sopenharmony_ci	umode_t umask;
5498c2ecf20Sopenharmony_ci	int lowercase, eas, chk, errs, chkdsk, timeshift;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	dnode_secno root_dno;
5528c2ecf20Sopenharmony_ci	struct hpfs_dirent *de = NULL;
5538c2ecf20Sopenharmony_ci	struct quad_buffer_head qbh;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	int o;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
5588c2ecf20Sopenharmony_ci	if (!sbi) {
5598c2ecf20Sopenharmony_ci		return -ENOMEM;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci	s->s_fs_info = sbi;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	mutex_init(&sbi->hpfs_mutex);
5648c2ecf20Sopenharmony_ci	hpfs_lock(s);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	uid = current_uid();
5678c2ecf20Sopenharmony_ci	gid = current_gid();
5688c2ecf20Sopenharmony_ci	umask = current_umask();
5698c2ecf20Sopenharmony_ci	lowercase = 0;
5708c2ecf20Sopenharmony_ci	eas = 2;
5718c2ecf20Sopenharmony_ci	chk = 1;
5728c2ecf20Sopenharmony_ci	errs = 1;
5738c2ecf20Sopenharmony_ci	chkdsk = 1;
5748c2ecf20Sopenharmony_ci	timeshift = 0;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if (!(o = parse_opts(options, &uid, &gid, &umask, &lowercase,
5778c2ecf20Sopenharmony_ci	    &eas, &chk, &errs, &chkdsk, &timeshift))) {
5788c2ecf20Sopenharmony_ci		pr_err("bad mount options.\n");
5798c2ecf20Sopenharmony_ci		goto bail0;
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci	if (o==2) {
5828c2ecf20Sopenharmony_ci		hpfs_help();
5838c2ecf20Sopenharmony_ci		goto bail0;
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	/*sbi->sb_mounting = 1;*/
5878c2ecf20Sopenharmony_ci	sb_set_blocksize(s, 512);
5888c2ecf20Sopenharmony_ci	sbi->sb_fs_size = -1;
5898c2ecf20Sopenharmony_ci	if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1;
5908c2ecf20Sopenharmony_ci	if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2;
5918c2ecf20Sopenharmony_ci	if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	/* Check magics */
5948c2ecf20Sopenharmony_ci	if (/*le16_to_cpu(bootblock->magic) != BB_MAGIC
5958c2ecf20Sopenharmony_ci	    ||*/ le32_to_cpu(superblock->magic) != SB_MAGIC
5968c2ecf20Sopenharmony_ci	    || le32_to_cpu(spareblock->magic) != SP_MAGIC) {
5978c2ecf20Sopenharmony_ci		if (!silent)
5988c2ecf20Sopenharmony_ci			pr_err("Bad magic ... probably not HPFS\n");
5998c2ecf20Sopenharmony_ci		goto bail4;
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	/* Check version */
6038c2ecf20Sopenharmony_ci	if (!sb_rdonly(s) && superblock->funcversion != 2 && superblock->funcversion != 3) {
6048c2ecf20Sopenharmony_ci		pr_err("Bad version %d,%d. Mount readonly to go around\n",
6058c2ecf20Sopenharmony_ci			(int)superblock->version, (int)superblock->funcversion);
6068c2ecf20Sopenharmony_ci		pr_err("please try recent version of HPFS driver at http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi and if it still can't understand this format, contact author - mikulas@artax.karlin.mff.cuni.cz\n");
6078c2ecf20Sopenharmony_ci		goto bail4;
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	s->s_flags |= SB_NOATIME;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/* Fill superblock stuff */
6138c2ecf20Sopenharmony_ci	s->s_magic = HPFS_SUPER_MAGIC;
6148c2ecf20Sopenharmony_ci	s->s_op = &hpfs_sops;
6158c2ecf20Sopenharmony_ci	s->s_d_op = &hpfs_dentry_operations;
6168c2ecf20Sopenharmony_ci	s->s_time_min =  local_to_gmt(s, 0);
6178c2ecf20Sopenharmony_ci	s->s_time_max =  local_to_gmt(s, U32_MAX);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	sbi->sb_root = le32_to_cpu(superblock->root);
6208c2ecf20Sopenharmony_ci	sbi->sb_fs_size = le32_to_cpu(superblock->n_sectors);
6218c2ecf20Sopenharmony_ci	sbi->sb_bitmaps = le32_to_cpu(superblock->bitmaps);
6228c2ecf20Sopenharmony_ci	sbi->sb_dirband_start = le32_to_cpu(superblock->dir_band_start);
6238c2ecf20Sopenharmony_ci	sbi->sb_dirband_size = le32_to_cpu(superblock->n_dir_band);
6248c2ecf20Sopenharmony_ci	sbi->sb_dmap = le32_to_cpu(superblock->dir_band_bitmap);
6258c2ecf20Sopenharmony_ci	sbi->sb_uid = uid;
6268c2ecf20Sopenharmony_ci	sbi->sb_gid = gid;
6278c2ecf20Sopenharmony_ci	sbi->sb_mode = 0777 & ~umask;
6288c2ecf20Sopenharmony_ci	sbi->sb_n_free = -1;
6298c2ecf20Sopenharmony_ci	sbi->sb_n_free_dnodes = -1;
6308c2ecf20Sopenharmony_ci	sbi->sb_lowercase = lowercase;
6318c2ecf20Sopenharmony_ci	sbi->sb_eas = eas;
6328c2ecf20Sopenharmony_ci	sbi->sb_chk = chk;
6338c2ecf20Sopenharmony_ci	sbi->sb_chkdsk = chkdsk;
6348c2ecf20Sopenharmony_ci	sbi->sb_err = errs;
6358c2ecf20Sopenharmony_ci	sbi->sb_timeshift = timeshift;
6368c2ecf20Sopenharmony_ci	sbi->sb_was_error = 0;
6378c2ecf20Sopenharmony_ci	sbi->sb_cp_table = NULL;
6388c2ecf20Sopenharmony_ci	sbi->sb_c_bitmap = -1;
6398c2ecf20Sopenharmony_ci	sbi->sb_max_fwd_alloc = 0xffffff;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (sbi->sb_fs_size >= 0x80000000) {
6428c2ecf20Sopenharmony_ci		hpfs_error(s, "invalid size in superblock: %08x",
6438c2ecf20Sopenharmony_ci			(unsigned)sbi->sb_fs_size);
6448c2ecf20Sopenharmony_ci		goto bail4;
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	if (spareblock->n_spares_used)
6488c2ecf20Sopenharmony_ci		hpfs_load_hotfix_map(s, spareblock);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	/* Load bitmap directory */
6518c2ecf20Sopenharmony_ci	if (!(sbi->sb_bmp_dir = hpfs_load_bitmap_directory(s, le32_to_cpu(superblock->bitmaps))))
6528c2ecf20Sopenharmony_ci		goto bail4;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	/* Check for general fs errors*/
6558c2ecf20Sopenharmony_ci	if (spareblock->dirty && !spareblock->old_wrote) {
6568c2ecf20Sopenharmony_ci		if (errs == 2) {
6578c2ecf20Sopenharmony_ci			pr_err("Improperly stopped, not mounted\n");
6588c2ecf20Sopenharmony_ci			goto bail4;
6598c2ecf20Sopenharmony_ci		}
6608c2ecf20Sopenharmony_ci		hpfs_error(s, "improperly stopped");
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	if (!sb_rdonly(s)) {
6648c2ecf20Sopenharmony_ci		spareblock->dirty = 1;
6658c2ecf20Sopenharmony_ci		spareblock->old_wrote = 0;
6668c2ecf20Sopenharmony_ci		mark_buffer_dirty(bh2);
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (le32_to_cpu(spareblock->n_dnode_spares) != le32_to_cpu(spareblock->n_dnode_spares_free)) {
6708c2ecf20Sopenharmony_ci		if (errs >= 2) {
6718c2ecf20Sopenharmony_ci			pr_err("Spare dnodes used, try chkdsk\n");
6728c2ecf20Sopenharmony_ci			mark_dirty(s, 0);
6738c2ecf20Sopenharmony_ci			goto bail4;
6748c2ecf20Sopenharmony_ci		}
6758c2ecf20Sopenharmony_ci		hpfs_error(s, "warning: spare dnodes used, try chkdsk");
6768c2ecf20Sopenharmony_ci		if (errs == 0)
6778c2ecf20Sopenharmony_ci			pr_err("Proceeding, but your filesystem could be corrupted if you delete files or directories\n");
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	if (chk) {
6808c2ecf20Sopenharmony_ci		unsigned a;
6818c2ecf20Sopenharmony_ci		if (le32_to_cpu(superblock->dir_band_end) - le32_to_cpu(superblock->dir_band_start) + 1 != le32_to_cpu(superblock->n_dir_band) ||
6828c2ecf20Sopenharmony_ci		    le32_to_cpu(superblock->dir_band_end) < le32_to_cpu(superblock->dir_band_start) || le32_to_cpu(superblock->n_dir_band) > 0x4000) {
6838c2ecf20Sopenharmony_ci			hpfs_error(s, "dir band size mismatch: dir_band_start==%08x, dir_band_end==%08x, n_dir_band==%08x",
6848c2ecf20Sopenharmony_ci				le32_to_cpu(superblock->dir_band_start), le32_to_cpu(superblock->dir_band_end), le32_to_cpu(superblock->n_dir_band));
6858c2ecf20Sopenharmony_ci			goto bail4;
6868c2ecf20Sopenharmony_ci		}
6878c2ecf20Sopenharmony_ci		a = sbi->sb_dirband_size;
6888c2ecf20Sopenharmony_ci		sbi->sb_dirband_size = 0;
6898c2ecf20Sopenharmony_ci		if (hpfs_chk_sectors(s, le32_to_cpu(superblock->dir_band_start), le32_to_cpu(superblock->n_dir_band), "dir_band") ||
6908c2ecf20Sopenharmony_ci		    hpfs_chk_sectors(s, le32_to_cpu(superblock->dir_band_bitmap), 4, "dir_band_bitmap") ||
6918c2ecf20Sopenharmony_ci		    hpfs_chk_sectors(s, le32_to_cpu(superblock->bitmaps), 4, "bitmaps")) {
6928c2ecf20Sopenharmony_ci			mark_dirty(s, 0);
6938c2ecf20Sopenharmony_ci			goto bail4;
6948c2ecf20Sopenharmony_ci		}
6958c2ecf20Sopenharmony_ci		sbi->sb_dirband_size = a;
6968c2ecf20Sopenharmony_ci	} else
6978c2ecf20Sopenharmony_ci		pr_err("You really don't want any checks? You are crazy...\n");
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	/* Load code page table */
7008c2ecf20Sopenharmony_ci	if (le32_to_cpu(spareblock->n_code_pages))
7018c2ecf20Sopenharmony_ci		if (!(sbi->sb_cp_table = hpfs_load_code_page(s, le32_to_cpu(spareblock->code_page_dir))))
7028c2ecf20Sopenharmony_ci			pr_err("code page support is disabled\n");
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	brelse(bh2);
7058c2ecf20Sopenharmony_ci	brelse(bh1);
7068c2ecf20Sopenharmony_ci	brelse(bh0);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	root = iget_locked(s, sbi->sb_root);
7098c2ecf20Sopenharmony_ci	if (!root)
7108c2ecf20Sopenharmony_ci		goto bail0;
7118c2ecf20Sopenharmony_ci	hpfs_init_inode(root);
7128c2ecf20Sopenharmony_ci	hpfs_read_inode(root);
7138c2ecf20Sopenharmony_ci	unlock_new_inode(root);
7148c2ecf20Sopenharmony_ci	s->s_root = d_make_root(root);
7158c2ecf20Sopenharmony_ci	if (!s->s_root)
7168c2ecf20Sopenharmony_ci		goto bail0;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	/*
7198c2ecf20Sopenharmony_ci	 * find the root directory's . pointer & finish filling in the inode
7208c2ecf20Sopenharmony_ci	 */
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	root_dno = hpfs_fnode_dno(s, sbi->sb_root);
7238c2ecf20Sopenharmony_ci	if (root_dno)
7248c2ecf20Sopenharmony_ci		de = map_dirent(root, root_dno, "\001\001", 2, NULL, &qbh);
7258c2ecf20Sopenharmony_ci	if (!de)
7268c2ecf20Sopenharmony_ci		hpfs_error(s, "unable to find root dir");
7278c2ecf20Sopenharmony_ci	else {
7288c2ecf20Sopenharmony_ci		root->i_atime.tv_sec = local_to_gmt(s, le32_to_cpu(de->read_date));
7298c2ecf20Sopenharmony_ci		root->i_atime.tv_nsec = 0;
7308c2ecf20Sopenharmony_ci		root->i_mtime.tv_sec = local_to_gmt(s, le32_to_cpu(de->write_date));
7318c2ecf20Sopenharmony_ci		root->i_mtime.tv_nsec = 0;
7328c2ecf20Sopenharmony_ci		root->i_ctime.tv_sec = local_to_gmt(s, le32_to_cpu(de->creation_date));
7338c2ecf20Sopenharmony_ci		root->i_ctime.tv_nsec = 0;
7348c2ecf20Sopenharmony_ci		hpfs_i(root)->i_ea_size = le32_to_cpu(de->ea_size);
7358c2ecf20Sopenharmony_ci		hpfs_i(root)->i_parent_dir = root->i_ino;
7368c2ecf20Sopenharmony_ci		if (root->i_size == -1)
7378c2ecf20Sopenharmony_ci			root->i_size = 2048;
7388c2ecf20Sopenharmony_ci		if (root->i_blocks == -1)
7398c2ecf20Sopenharmony_ci			root->i_blocks = 5;
7408c2ecf20Sopenharmony_ci		hpfs_brelse4(&qbh);
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci	hpfs_unlock(s);
7438c2ecf20Sopenharmony_ci	return 0;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cibail4:	brelse(bh2);
7468c2ecf20Sopenharmony_cibail3:	brelse(bh1);
7478c2ecf20Sopenharmony_cibail2:	brelse(bh0);
7488c2ecf20Sopenharmony_cibail1:
7498c2ecf20Sopenharmony_cibail0:
7508c2ecf20Sopenharmony_ci	hpfs_unlock(s);
7518c2ecf20Sopenharmony_ci	free_sbi(sbi);
7528c2ecf20Sopenharmony_ci	return -EINVAL;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic struct dentry *hpfs_mount(struct file_system_type *fs_type,
7568c2ecf20Sopenharmony_ci	int flags, const char *dev_name, void *data)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	return mount_bdev(fs_type, flags, dev_name, data, hpfs_fill_super);
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic struct file_system_type hpfs_fs_type = {
7628c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7638c2ecf20Sopenharmony_ci	.name		= "hpfs",
7648c2ecf20Sopenharmony_ci	.mount		= hpfs_mount,
7658c2ecf20Sopenharmony_ci	.kill_sb	= kill_block_super,
7668c2ecf20Sopenharmony_ci	.fs_flags	= FS_REQUIRES_DEV,
7678c2ecf20Sopenharmony_ci};
7688c2ecf20Sopenharmony_ciMODULE_ALIAS_FS("hpfs");
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic int __init init_hpfs_fs(void)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	int err = init_inodecache();
7738c2ecf20Sopenharmony_ci	if (err)
7748c2ecf20Sopenharmony_ci		goto out1;
7758c2ecf20Sopenharmony_ci	err = register_filesystem(&hpfs_fs_type);
7768c2ecf20Sopenharmony_ci	if (err)
7778c2ecf20Sopenharmony_ci		goto out;
7788c2ecf20Sopenharmony_ci	return 0;
7798c2ecf20Sopenharmony_ciout:
7808c2ecf20Sopenharmony_ci	destroy_inodecache();
7818c2ecf20Sopenharmony_ciout1:
7828c2ecf20Sopenharmony_ci	return err;
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic void __exit exit_hpfs_fs(void)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	unregister_filesystem(&hpfs_fs_type);
7888c2ecf20Sopenharmony_ci	destroy_inodecache();
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_cimodule_init(init_hpfs_fs)
7928c2ecf20Sopenharmony_cimodule_exit(exit_hpfs_fs)
7938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
794