static int parse_options(char *options, struct super_block *sb,
			 unsigned long *journal_devnum,
			 unsigned int *journal_ioprio,
			 ext4_fsblk_t *n_blocks_count, int is_remount)
{
	struct ext4_sb_info *sbi = EXT4_SB(sb);
	char *p;
	substring_t args[MAX_OPT_ARGS];
	int data_opt = 0;
	int option;
#ifdef CONFIG_QUOTA
	int qfmt;
#endif

	if (!options)
		return 1;

	while ((p = strsep(&options, ",")) != NULL) {
		int token;
		if (!*p)
			continue;

		/*
		 * Initialize args struct so we know whether arg was
		 * found; some options take optional arguments.
		 */
		args[0].to = args[0].from = NULL;
		token = match_token(p, tokens, args);
		switch (token) {
		case Opt_bsd_df:
			ext4_msg(sb, KERN_WARNING, deprecated_msg, p, "2.6.38");
			clear_opt(sb, MINIX_DF);
			break;
		case Opt_minix_df:
			ext4_msg(sb, KERN_WARNING, deprecated_msg, p, "2.6.38");
			set_opt(sb, MINIX_DF);

			break;
		case Opt_grpid:
			ext4_msg(sb, KERN_WARNING, deprecated_msg, p, "2.6.38");
			set_opt(sb, GRPID);

			break;
		case Opt_nogrpid:
			ext4_msg(sb, KERN_WARNING, deprecated_msg, p, "2.6.38");
			clear_opt(sb, GRPID);

			break;
		case Opt_resuid:
			if (match_int(&args[0], &option))
				return 0;
			sbi->s_resuid = option;
			break;
		case Opt_resgid:
			if (match_int(&args[0], &option))
				return 0;
			sbi->s_resgid = option;
			break;
		case Opt_sb:
			/* handled by get_sb_block() instead of here */
			/* *sb_block = match_int(&args[0]); */
			break;
		case Opt_err_panic:
			clear_opt(sb, ERRORS_CONT);
			clear_opt(sb, ERRORS_RO);
			set_opt(sb, ERRORS_PANIC);
			break;
		case Opt_err_ro:
			clear_opt(sb, ERRORS_CONT);
			clear_opt(sb, ERRORS_PANIC);
			set_opt(sb, ERRORS_RO);
			break;
		case Opt_err_cont:
			clear_opt(sb, ERRORS_RO);
			clear_opt(sb, ERRORS_PANIC);
			set_opt(sb, ERRORS_CONT);
			break;
		case Opt_nouid32:
			set_opt(sb, NO_UID32);
			break;
		case Opt_debug:
			set_opt(sb, DEBUG);
			break;
		case Opt_oldalloc:
			set_opt(sb, OLDALLOC);
			break;
		case Opt_orlov:
			clear_opt(sb, OLDALLOC);
			break;
#ifdef CONFIG_EXT4_FS_XATTR
		case Opt_user_xattr:
			set_opt(sb, XATTR_USER);
			break;
		case Opt_nouser_xattr:
			clear_opt(sb, XATTR_USER);
			break;
#else
		case Opt_user_xattr:
		case Opt_nouser_xattr:
			ext4_msg(sb, KERN_ERR, "(no)user_xattr options not supported");
			break;
#endif
#ifdef CONFIG_EXT4_FS_POSIX_ACL
		case Opt_acl:
			set_opt(sb, POSIX_ACL);
			break;
		case Opt_noacl:
			clear_opt(sb, POSIX_ACL);
			break;
#else
		case Opt_acl:
		case Opt_noacl:
			ext4_msg(sb, KERN_ERR, "(no)acl options not supported");
			break;
#endif
		case Opt_journal_update:
			/* @@@ FIXME */
			/* Eventually we will want to be able to create
			   a journal file here.  For now, only allow the
			   user to specify an existing inode to be the
			   journal file. */
			if (is_remount) {
				ext4_msg(sb, KERN_ERR,
					 "Cannot specify journal on remount");
				return 0;
			}
			set_opt(sb, UPDATE_JOURNAL);
			break;
		case Opt_journal_dev:
			if (is_remount) {
				ext4_msg(sb, KERN_ERR,
					"Cannot specify journal on remount");
				return 0;
			}
			if (match_int(&args[0], &option))
				return 0;
			*journal_devnum = option;
			break;
		case Opt_journal_checksum:
			set_opt(sb, JOURNAL_CHECKSUM);
			break;
		case Opt_journal_async_commit:
			set_opt(sb, JOURNAL_ASYNC_COMMIT);
			set_opt(sb, JOURNAL_CHECKSUM);
			break;
		case Opt_noload:
			set_opt(sb, NOLOAD);
			break;
		case Opt_commit:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0)
				return 0;
			if (option == 0)
				option = JBD2_DEFAULT_MAX_COMMIT_AGE;
			sbi->s_commit_interval = HZ * option;
			break;
		case Opt_max_batch_time:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0)
				return 0;
			if (option == 0)
				option = EXT4_DEF_MAX_BATCH_TIME;
			sbi->s_max_batch_time = option;
			break;
		case Opt_min_batch_time:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0)
				return 0;
			sbi->s_min_batch_time = option;
			break;
		case Opt_data_journal:
			data_opt = EXT4_MOUNT_JOURNAL_DATA;
			goto datacheck;
		case Opt_data_ordered:
			data_opt = EXT4_MOUNT_ORDERED_DATA;
			goto datacheck;
		case Opt_data_writeback:
			data_opt = EXT4_MOUNT_WRITEBACK_DATA;
		datacheck:
			if (is_remount) {
				if (test_opt(sb, DATA_FLAGS) != data_opt) {
					ext4_msg(sb, KERN_ERR,
						"Cannot change data mode on remount");
					return 0;
				}
			} else {
				clear_opt(sb, DATA_FLAGS);
				sbi->s_mount_opt |= data_opt;
			}
			break;
		case Opt_data_err_abort:
			set_opt(sb, DATA_ERR_ABORT);
			break;
		case Opt_data_err_ignore:
			clear_opt(sb, DATA_ERR_ABORT);
			break;
#ifdef CONFIG_QUOTA
		case Opt_usrjquota:
			if (!set_qf_name(sb, USRQUOTA, &args[0]))
				return 0;
			break;
		case Opt_grpjquota:
			if (!set_qf_name(sb, GRPQUOTA, &args[0]))
				return 0;
			break;
		case Opt_offusrjquota:
			if (!clear_qf_name(sb, USRQUOTA))
				return 0;
			break;
		case Opt_offgrpjquota:
			if (!clear_qf_name(sb, GRPQUOTA))
				return 0;
			break;

		case Opt_jqfmt_vfsold:
			qfmt = QFMT_VFS_OLD;
			goto set_qf_format;
		case Opt_jqfmt_vfsv0:
			qfmt = QFMT_VFS_V0;
			goto set_qf_format;
		case Opt_jqfmt_vfsv1:
			qfmt = QFMT_VFS_V1;
set_qf_format:
			if (sb_any_quota_loaded(sb) &&
			    sbi->s_jquota_fmt != qfmt) {
				ext4_msg(sb, KERN_ERR, "Cannot change "
					"journaled quota options when "
					"quota turned on");
				return 0;
			}
			sbi->s_jquota_fmt = qfmt;
			break;
		case Opt_quota:
		case Opt_usrquota:
			set_opt(sb, QUOTA);
			set_opt(sb, USRQUOTA);
			break;
		case Opt_grpquota:
			set_opt(sb, QUOTA);
			set_opt(sb, GRPQUOTA);
			break;
		case Opt_noquota:
			if (sb_any_quota_loaded(sb)) {
				ext4_msg(sb, KERN_ERR, "Cannot change quota "
					"options when quota turned on");
				return 0;
			}
			clear_opt(sb, QUOTA);
			clear_opt(sb, USRQUOTA);
			clear_opt(sb, GRPQUOTA);
			break;
#else
		case Opt_quota:
		case Opt_usrquota:
		case Opt_grpquota:
			ext4_msg(sb, KERN_ERR,
				"quota options not supported");
			break;
		case Opt_usrjquota:
		case Opt_grpjquota:
		case Opt_offusrjquota:
		case Opt_offgrpjquota:
		case Opt_jqfmt_vfsold:
		case Opt_jqfmt_vfsv0:
		case Opt_jqfmt_vfsv1:
			ext4_msg(sb, KERN_ERR,
				"journaled quota options not supported");
			break;
		case Opt_noquota:
			break;
#endif
		case Opt_abort:
			sbi->s_mount_flags |= EXT4_MF_FS_ABORTED;
			break;
		case Opt_nobarrier:
			clear_opt(sb, BARRIER);
			break;
		case Opt_barrier:
			if (args[0].from) {
				if (match_int(&args[0], &option))
					return 0;
			} else
				option = 1;	/* No argument, default to 1 */
			if (option)
				set_opt(sb, BARRIER);
			else
				clear_opt(sb, BARRIER);
			break;
		case Opt_ignore:
			break;
		case Opt_resize:
			if (!is_remount) {
				ext4_msg(sb, KERN_ERR,
					"resize option only available "
					"for remount");
				return 0;
			}
			if (match_int(&args[0], &option) != 0)
				return 0;
			*n_blocks_count = option;
			break;
		case Opt_nobh:
			ext4_msg(sb, KERN_WARNING,
				 "Ignoring deprecated nobh option");
			break;
		case Opt_bh:
			ext4_msg(sb, KERN_WARNING,
				 "Ignoring deprecated bh option");
			break;
		case Opt_i_version:
			set_opt(sb, I_VERSION);
			sb->s_flags |= MS_I_VERSION;
			break;
		case Opt_nodelalloc:
			clear_opt(sb, DELALLOC);
			break;
		case Opt_mblk_io_submit:
			set_opt(sb, MBLK_IO_SUBMIT);
			break;
		case Opt_nomblk_io_submit:
			clear_opt(sb, MBLK_IO_SUBMIT);
			break;
		case Opt_stripe:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0)
				return 0;
			sbi->s_stripe = option;
			break;
		case Opt_delalloc:
			set_opt(sb, DELALLOC);
			break;
		case Opt_block_validity:
			set_opt(sb, BLOCK_VALIDITY);
			break;
		case Opt_noblock_validity:
			clear_opt(sb, BLOCK_VALIDITY);
			break;
		case Opt_inode_readahead_blks:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0 || option > (1 << 30))
				return 0;
			if (option && !is_power_of_2(option)) {
				ext4_msg(sb, KERN_ERR,
					 "EXT4-fs: inode_readahead_blks"
					 " must be a power of 2");
				return 0;
			}
			sbi->s_inode_readahead_blks = option;
			break;
		case Opt_journal_ioprio:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0 || option > 7)
				break;
			*journal_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE,
							    option);
			break;
		case Opt_noauto_da_alloc:
			set_opt(sb, NO_AUTO_DA_ALLOC);
			break;
		case Opt_auto_da_alloc:
			if (args[0].from) {
				if (match_int(&args[0], &option))
					return 0;
			} else
				option = 1;	/* No argument, default to 1 */
			if (option)
				clear_opt(sb, NO_AUTO_DA_ALLOC);
			else
				set_opt(sb,NO_AUTO_DA_ALLOC);
			break;
		case Opt_discard:
			set_opt(sb, DISCARD);
			break;
		case Opt_nodiscard:
			clear_opt(sb, DISCARD);
			break;
		case Opt_dioread_nolock:
			set_opt(sb, DIOREAD_NOLOCK);
			break;
		case Opt_dioread_lock:
			clear_opt(sb, DIOREAD_NOLOCK);
			break;
		case Opt_init_inode_table:
			set_opt(sb, INIT_INODE_TABLE);
			if (args[0].from) {
				if (match_int(&args[0], &option))
					return 0;
			} else
				option = EXT4_DEF_LI_WAIT_MULT;
			if (option < 0)
				return 0;
			sbi->s_li_wait_mult = option;
			break;
		case Opt_noinit_inode_table:
			clear_opt(sb, INIT_INODE_TABLE);
			break;
		default:
			ext4_msg(sb, KERN_ERR,
			       "Unrecognized mount option \"%s\" "
			       "or missing value", p);
			return 0;
		}
	}
#ifdef CONFIG_QUOTA
	if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
		if (test_opt(sb, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
			clear_opt(sb, USRQUOTA);

		if (test_opt(sb, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
			clear_opt(sb, GRPQUOTA);

		if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
			ext4_msg(sb, KERN_ERR, "old and new quota "
					"format mixing");
			return 0;
		}

		if (!sbi->s_jquota_fmt) {
			ext4_msg(sb, KERN_ERR, "journaled quota format "
					"not specified");
			return 0;
		}
	} else {
		if (sbi->s_jquota_fmt) {
			ext4_msg(sb, KERN_ERR, "journaled quota format "
					"specified with no journaling "
					"enabled");
			return 0;
		}
	}
#endif
	return 1;
}