cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

namei_msdos.c (17260B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  linux/fs/msdos/namei.c
      4 *
      5 *  Written 1992,1993 by Werner Almesberger
      6 *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
      7 *  Rewritten for constant inumbers 1999 by Al Viro
      8 */
      9
     10#include <linux/module.h>
     11#include <linux/iversion.h>
     12#include "fat.h"
     13
     14/* Characters that are undesirable in an MS-DOS file name */
     15static unsigned char bad_chars[] = "*?<>|\"";
     16static unsigned char bad_if_strict[] = "+=,; ";
     17
     18/***** Formats an MS-DOS file name. Rejects invalid names. */
     19static int msdos_format_name(const unsigned char *name, int len,
     20			     unsigned char *res, struct fat_mount_options *opts)
     21	/*
     22	 * name is the proposed name, len is its length, res is
     23	 * the resulting name, opts->name_check is either (r)elaxed,
     24	 * (n)ormal or (s)trict, opts->dotsOK allows dots at the
     25	 * beginning of name (for hidden files)
     26	 */
     27{
     28	unsigned char *walk;
     29	unsigned char c;
     30	int space;
     31
     32	if (name[0] == '.') {	/* dotfile because . and .. already done */
     33		if (opts->dotsOK) {
     34			/* Get rid of dot - test for it elsewhere */
     35			name++;
     36			len--;
     37		} else
     38			return -EINVAL;
     39	}
     40	/*
     41	 * disallow names that _really_ start with a dot
     42	 */
     43	space = 1;
     44	c = 0;
     45	for (walk = res; len && walk - res < 8; walk++) {
     46		c = *name++;
     47		len--;
     48		if (opts->name_check != 'r' && strchr(bad_chars, c))
     49			return -EINVAL;
     50		if (opts->name_check == 's' && strchr(bad_if_strict, c))
     51			return -EINVAL;
     52		if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
     53			return -EINVAL;
     54		if (c < ' ' || c == ':' || c == '\\')
     55			return -EINVAL;
     56	/*
     57	 * 0xE5 is legal as a first character, but we must substitute
     58	 * 0x05 because 0xE5 marks deleted files.  Yes, DOS really
     59	 * does this.
     60	 * It seems that Microsoft hacked DOS to support non-US
     61	 * characters after the 0xE5 character was already in use to
     62	 * mark deleted files.
     63	 */
     64		if ((res == walk) && (c == 0xE5))
     65			c = 0x05;
     66		if (c == '.')
     67			break;
     68		space = (c == ' ');
     69		*walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c - 32 : c;
     70	}
     71	if (space)
     72		return -EINVAL;
     73	if (opts->name_check == 's' && len && c != '.') {
     74		c = *name++;
     75		len--;
     76		if (c != '.')
     77			return -EINVAL;
     78	}
     79	while (c != '.' && len--)
     80		c = *name++;
     81	if (c == '.') {
     82		while (walk - res < 8)
     83			*walk++ = ' ';
     84		while (len > 0 && walk - res < MSDOS_NAME) {
     85			c = *name++;
     86			len--;
     87			if (opts->name_check != 'r' && strchr(bad_chars, c))
     88				return -EINVAL;
     89			if (opts->name_check == 's' &&
     90			    strchr(bad_if_strict, c))
     91				return -EINVAL;
     92			if (c < ' ' || c == ':' || c == '\\')
     93				return -EINVAL;
     94			if (c == '.') {
     95				if (opts->name_check == 's')
     96					return -EINVAL;
     97				break;
     98			}
     99			if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
    100				return -EINVAL;
    101			space = c == ' ';
    102			if (!opts->nocase && c >= 'a' && c <= 'z')
    103				*walk++ = c - 32;
    104			else
    105				*walk++ = c;
    106		}
    107		if (space)
    108			return -EINVAL;
    109		if (opts->name_check == 's' && len)
    110			return -EINVAL;
    111	}
    112	while (walk - res < MSDOS_NAME)
    113		*walk++ = ' ';
    114
    115	return 0;
    116}
    117
    118/***** Locates a directory entry.  Uses unformatted name. */
    119static int msdos_find(struct inode *dir, const unsigned char *name, int len,
    120		      struct fat_slot_info *sinfo)
    121{
    122	struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
    123	unsigned char msdos_name[MSDOS_NAME];
    124	int err;
    125
    126	err = msdos_format_name(name, len, msdos_name, &sbi->options);
    127	if (err)
    128		return -ENOENT;
    129
    130	err = fat_scan(dir, msdos_name, sinfo);
    131	if (!err && sbi->options.dotsOK) {
    132		if (name[0] == '.') {
    133			if (!(sinfo->de->attr & ATTR_HIDDEN))
    134				err = -ENOENT;
    135		} else {
    136			if (sinfo->de->attr & ATTR_HIDDEN)
    137				err = -ENOENT;
    138		}
    139		if (err)
    140			brelse(sinfo->bh);
    141	}
    142	return err;
    143}
    144
    145/*
    146 * Compute the hash for the msdos name corresponding to the dentry.
    147 * Note: if the name is invalid, we leave the hash code unchanged so
    148 * that the existing dentry can be used. The msdos fs routines will
    149 * return ENOENT or EINVAL as appropriate.
    150 */
    151static int msdos_hash(const struct dentry *dentry, struct qstr *qstr)
    152{
    153	struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
    154	unsigned char msdos_name[MSDOS_NAME];
    155	int error;
    156
    157	error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
    158	if (!error)
    159		qstr->hash = full_name_hash(dentry, msdos_name, MSDOS_NAME);
    160	return 0;
    161}
    162
    163/*
    164 * Compare two msdos names. If either of the names are invalid,
    165 * we fall back to doing the standard name comparison.
    166 */
    167static int msdos_cmp(const struct dentry *dentry,
    168		unsigned int len, const char *str, const struct qstr *name)
    169{
    170	struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
    171	unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
    172	int error;
    173
    174	error = msdos_format_name(name->name, name->len, a_msdos_name, options);
    175	if (error)
    176		goto old_compare;
    177	error = msdos_format_name(str, len, b_msdos_name, options);
    178	if (error)
    179		goto old_compare;
    180	error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
    181out:
    182	return error;
    183
    184old_compare:
    185	error = 1;
    186	if (name->len == len)
    187		error = memcmp(name->name, str, len);
    188	goto out;
    189}
    190
    191static const struct dentry_operations msdos_dentry_operations = {
    192	.d_hash		= msdos_hash,
    193	.d_compare	= msdos_cmp,
    194};
    195
    196/*
    197 * AV. Wrappers for FAT sb operations. Is it wise?
    198 */
    199
    200/***** Get inode using directory and name */
    201static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
    202				   unsigned int flags)
    203{
    204	struct super_block *sb = dir->i_sb;
    205	struct fat_slot_info sinfo;
    206	struct inode *inode;
    207	int err;
    208
    209	mutex_lock(&MSDOS_SB(sb)->s_lock);
    210	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
    211	switch (err) {
    212	case -ENOENT:
    213		inode = NULL;
    214		break;
    215	case 0:
    216		inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
    217		brelse(sinfo.bh);
    218		break;
    219	default:
    220		inode = ERR_PTR(err);
    221	}
    222	mutex_unlock(&MSDOS_SB(sb)->s_lock);
    223	return d_splice_alias(inode, dentry);
    224}
    225
    226/***** Creates a directory entry (name is already formatted). */
    227static int msdos_add_entry(struct inode *dir, const unsigned char *name,
    228			   int is_dir, int is_hid, int cluster,
    229			   struct timespec64 *ts, struct fat_slot_info *sinfo)
    230{
    231	struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
    232	struct msdos_dir_entry de;
    233	__le16 time, date;
    234	int err;
    235
    236	memcpy(de.name, name, MSDOS_NAME);
    237	de.attr = is_dir ? ATTR_DIR : ATTR_ARCH;
    238	if (is_hid)
    239		de.attr |= ATTR_HIDDEN;
    240	de.lcase = 0;
    241	fat_time_unix2fat(sbi, ts, &time, &date, NULL);
    242	de.cdate = de.adate = 0;
    243	de.ctime = 0;
    244	de.ctime_cs = 0;
    245	de.time = time;
    246	de.date = date;
    247	fat_set_start(&de, cluster);
    248	de.size = 0;
    249
    250	err = fat_add_entries(dir, &de, 1, sinfo);
    251	if (err)
    252		return err;
    253
    254	fat_truncate_time(dir, ts, S_CTIME|S_MTIME);
    255	if (IS_DIRSYNC(dir))
    256		(void)fat_sync_inode(dir);
    257	else
    258		mark_inode_dirty(dir);
    259
    260	return 0;
    261}
    262
    263/***** Create a file */
    264static int msdos_create(struct user_namespace *mnt_userns, struct inode *dir,
    265			struct dentry *dentry, umode_t mode, bool excl)
    266{
    267	struct super_block *sb = dir->i_sb;
    268	struct inode *inode = NULL;
    269	struct fat_slot_info sinfo;
    270	struct timespec64 ts;
    271	unsigned char msdos_name[MSDOS_NAME];
    272	int err, is_hid;
    273
    274	mutex_lock(&MSDOS_SB(sb)->s_lock);
    275
    276	err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
    277				msdos_name, &MSDOS_SB(sb)->options);
    278	if (err)
    279		goto out;
    280	is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
    281	/* Have to do it due to foo vs. .foo conflicts */
    282	if (!fat_scan(dir, msdos_name, &sinfo)) {
    283		brelse(sinfo.bh);
    284		err = -EINVAL;
    285		goto out;
    286	}
    287
    288	ts = current_time(dir);
    289	err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo);
    290	if (err)
    291		goto out;
    292	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
    293	brelse(sinfo.bh);
    294	if (IS_ERR(inode)) {
    295		err = PTR_ERR(inode);
    296		goto out;
    297	}
    298	fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME);
    299	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
    300
    301	d_instantiate(dentry, inode);
    302out:
    303	mutex_unlock(&MSDOS_SB(sb)->s_lock);
    304	if (!err)
    305		err = fat_flush_inodes(sb, dir, inode);
    306	return err;
    307}
    308
    309/***** Remove a directory */
    310static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
    311{
    312	struct super_block *sb = dir->i_sb;
    313	struct inode *inode = d_inode(dentry);
    314	struct fat_slot_info sinfo;
    315	int err;
    316
    317	mutex_lock(&MSDOS_SB(sb)->s_lock);
    318	err = fat_dir_empty(inode);
    319	if (err)
    320		goto out;
    321	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
    322	if (err)
    323		goto out;
    324
    325	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
    326	if (err)
    327		goto out;
    328	drop_nlink(dir);
    329
    330	clear_nlink(inode);
    331	fat_truncate_time(inode, NULL, S_CTIME);
    332	fat_detach(inode);
    333out:
    334	mutex_unlock(&MSDOS_SB(sb)->s_lock);
    335	if (!err)
    336		err = fat_flush_inodes(sb, dir, inode);
    337
    338	return err;
    339}
    340
    341/***** Make a directory */
    342static int msdos_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
    343		       struct dentry *dentry, umode_t mode)
    344{
    345	struct super_block *sb = dir->i_sb;
    346	struct fat_slot_info sinfo;
    347	struct inode *inode;
    348	unsigned char msdos_name[MSDOS_NAME];
    349	struct timespec64 ts;
    350	int err, is_hid, cluster;
    351
    352	mutex_lock(&MSDOS_SB(sb)->s_lock);
    353
    354	err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
    355				msdos_name, &MSDOS_SB(sb)->options);
    356	if (err)
    357		goto out;
    358	is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
    359	/* foo vs .foo situation */
    360	if (!fat_scan(dir, msdos_name, &sinfo)) {
    361		brelse(sinfo.bh);
    362		err = -EINVAL;
    363		goto out;
    364	}
    365
    366	ts = current_time(dir);
    367	cluster = fat_alloc_new_dir(dir, &ts);
    368	if (cluster < 0) {
    369		err = cluster;
    370		goto out;
    371	}
    372	err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
    373	if (err)
    374		goto out_free;
    375	inc_nlink(dir);
    376
    377	inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
    378	brelse(sinfo.bh);
    379	if (IS_ERR(inode)) {
    380		err = PTR_ERR(inode);
    381		/* the directory was completed, just return a error */
    382		goto out;
    383	}
    384	set_nlink(inode, 2);
    385	fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME);
    386	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
    387
    388	d_instantiate(dentry, inode);
    389
    390	mutex_unlock(&MSDOS_SB(sb)->s_lock);
    391	fat_flush_inodes(sb, dir, inode);
    392	return 0;
    393
    394out_free:
    395	fat_free_clusters(dir, cluster);
    396out:
    397	mutex_unlock(&MSDOS_SB(sb)->s_lock);
    398	return err;
    399}
    400
    401/***** Unlink a file */
    402static int msdos_unlink(struct inode *dir, struct dentry *dentry)
    403{
    404	struct inode *inode = d_inode(dentry);
    405	struct super_block *sb = inode->i_sb;
    406	struct fat_slot_info sinfo;
    407	int err;
    408
    409	mutex_lock(&MSDOS_SB(sb)->s_lock);
    410	err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
    411	if (err)
    412		goto out;
    413
    414	err = fat_remove_entries(dir, &sinfo);	/* and releases bh */
    415	if (err)
    416		goto out;
    417	clear_nlink(inode);
    418	fat_truncate_time(inode, NULL, S_CTIME);
    419	fat_detach(inode);
    420out:
    421	mutex_unlock(&MSDOS_SB(sb)->s_lock);
    422	if (!err)
    423		err = fat_flush_inodes(sb, dir, inode);
    424
    425	return err;
    426}
    427
    428static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
    429			   struct dentry *old_dentry,
    430			   struct inode *new_dir, unsigned char *new_name,
    431			   struct dentry *new_dentry, int is_hid)
    432{
    433	struct buffer_head *dotdot_bh;
    434	struct msdos_dir_entry *dotdot_de;
    435	struct inode *old_inode, *new_inode;
    436	struct fat_slot_info old_sinfo, sinfo;
    437	struct timespec64 ts;
    438	loff_t new_i_pos;
    439	int err, old_attrs, is_dir, update_dotdot, corrupt = 0;
    440
    441	old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
    442	old_inode = d_inode(old_dentry);
    443	new_inode = d_inode(new_dentry);
    444
    445	err = fat_scan(old_dir, old_name, &old_sinfo);
    446	if (err) {
    447		err = -EIO;
    448		goto out;
    449	}
    450
    451	is_dir = S_ISDIR(old_inode->i_mode);
    452	update_dotdot = (is_dir && old_dir != new_dir);
    453	if (update_dotdot) {
    454		if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de)) {
    455			err = -EIO;
    456			goto out;
    457		}
    458	}
    459
    460	old_attrs = MSDOS_I(old_inode)->i_attrs;
    461	err = fat_scan(new_dir, new_name, &sinfo);
    462	if (!err) {
    463		if (!new_inode) {
    464			/* "foo" -> ".foo" case. just change the ATTR_HIDDEN */
    465			if (sinfo.de != old_sinfo.de) {
    466				err = -EINVAL;
    467				goto out;
    468			}
    469			if (is_hid)
    470				MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
    471			else
    472				MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
    473			if (IS_DIRSYNC(old_dir)) {
    474				err = fat_sync_inode(old_inode);
    475				if (err) {
    476					MSDOS_I(old_inode)->i_attrs = old_attrs;
    477					goto out;
    478				}
    479			} else
    480				mark_inode_dirty(old_inode);
    481
    482			inode_inc_iversion(old_dir);
    483			fat_truncate_time(old_dir, NULL, S_CTIME|S_MTIME);
    484			if (IS_DIRSYNC(old_dir))
    485				(void)fat_sync_inode(old_dir);
    486			else
    487				mark_inode_dirty(old_dir);
    488			goto out;
    489		}
    490	}
    491
    492	ts = current_time(old_inode);
    493	if (new_inode) {
    494		if (err)
    495			goto out;
    496		if (is_dir) {
    497			err = fat_dir_empty(new_inode);
    498			if (err)
    499				goto out;
    500		}
    501		new_i_pos = MSDOS_I(new_inode)->i_pos;
    502		fat_detach(new_inode);
    503	} else {
    504		err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0,
    505				      &ts, &sinfo);
    506		if (err)
    507			goto out;
    508		new_i_pos = sinfo.i_pos;
    509	}
    510	inode_inc_iversion(new_dir);
    511
    512	fat_detach(old_inode);
    513	fat_attach(old_inode, new_i_pos);
    514	if (is_hid)
    515		MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
    516	else
    517		MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
    518	if (IS_DIRSYNC(new_dir)) {
    519		err = fat_sync_inode(old_inode);
    520		if (err)
    521			goto error_inode;
    522	} else
    523		mark_inode_dirty(old_inode);
    524
    525	if (update_dotdot) {
    526		fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);
    527		mark_buffer_dirty_inode(dotdot_bh, old_inode);
    528		if (IS_DIRSYNC(new_dir)) {
    529			err = sync_dirty_buffer(dotdot_bh);
    530			if (err)
    531				goto error_dotdot;
    532		}
    533		drop_nlink(old_dir);
    534		if (!new_inode)
    535			inc_nlink(new_dir);
    536	}
    537
    538	err = fat_remove_entries(old_dir, &old_sinfo);	/* and releases bh */
    539	old_sinfo.bh = NULL;
    540	if (err)
    541		goto error_dotdot;
    542	inode_inc_iversion(old_dir);
    543	fat_truncate_time(old_dir, &ts, S_CTIME|S_MTIME);
    544	if (IS_DIRSYNC(old_dir))
    545		(void)fat_sync_inode(old_dir);
    546	else
    547		mark_inode_dirty(old_dir);
    548
    549	if (new_inode) {
    550		drop_nlink(new_inode);
    551		if (is_dir)
    552			drop_nlink(new_inode);
    553		fat_truncate_time(new_inode, &ts, S_CTIME);
    554	}
    555out:
    556	brelse(sinfo.bh);
    557	brelse(dotdot_bh);
    558	brelse(old_sinfo.bh);
    559	return err;
    560
    561error_dotdot:
    562	/* data cluster is shared, serious corruption */
    563	corrupt = 1;
    564
    565	if (update_dotdot) {
    566		fat_set_start(dotdot_de, MSDOS_I(old_dir)->i_logstart);
    567		mark_buffer_dirty_inode(dotdot_bh, old_inode);
    568		corrupt |= sync_dirty_buffer(dotdot_bh);
    569	}
    570error_inode:
    571	fat_detach(old_inode);
    572	fat_attach(old_inode, old_sinfo.i_pos);
    573	MSDOS_I(old_inode)->i_attrs = old_attrs;
    574	if (new_inode) {
    575		fat_attach(new_inode, new_i_pos);
    576		if (corrupt)
    577			corrupt |= fat_sync_inode(new_inode);
    578	} else {
    579		/*
    580		 * If new entry was not sharing the data cluster, it
    581		 * shouldn't be serious corruption.
    582		 */
    583		int err2 = fat_remove_entries(new_dir, &sinfo);
    584		if (corrupt)
    585			corrupt |= err2;
    586		sinfo.bh = NULL;
    587	}
    588	if (corrupt < 0) {
    589		fat_fs_error(new_dir->i_sb,
    590			     "%s: Filesystem corrupted (i_pos %lld)",
    591			     __func__, sinfo.i_pos);
    592	}
    593	goto out;
    594}
    595
    596/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
    597static int msdos_rename(struct user_namespace *mnt_userns,
    598			struct inode *old_dir, struct dentry *old_dentry,
    599			struct inode *new_dir, struct dentry *new_dentry,
    600			unsigned int flags)
    601{
    602	struct super_block *sb = old_dir->i_sb;
    603	unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
    604	int err, is_hid;
    605
    606	if (flags & ~RENAME_NOREPLACE)
    607		return -EINVAL;
    608
    609	mutex_lock(&MSDOS_SB(sb)->s_lock);
    610
    611	err = msdos_format_name(old_dentry->d_name.name,
    612				old_dentry->d_name.len, old_msdos_name,
    613				&MSDOS_SB(old_dir->i_sb)->options);
    614	if (err)
    615		goto out;
    616	err = msdos_format_name(new_dentry->d_name.name,
    617				new_dentry->d_name.len, new_msdos_name,
    618				&MSDOS_SB(new_dir->i_sb)->options);
    619	if (err)
    620		goto out;
    621
    622	is_hid =
    623	     (new_dentry->d_name.name[0] == '.') && (new_msdos_name[0] != '.');
    624
    625	err = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
    626			      new_dir, new_msdos_name, new_dentry, is_hid);
    627out:
    628	mutex_unlock(&MSDOS_SB(sb)->s_lock);
    629	if (!err)
    630		err = fat_flush_inodes(sb, old_dir, new_dir);
    631	return err;
    632}
    633
    634static const struct inode_operations msdos_dir_inode_operations = {
    635	.create		= msdos_create,
    636	.lookup		= msdos_lookup,
    637	.unlink		= msdos_unlink,
    638	.mkdir		= msdos_mkdir,
    639	.rmdir		= msdos_rmdir,
    640	.rename		= msdos_rename,
    641	.setattr	= fat_setattr,
    642	.getattr	= fat_getattr,
    643	.update_time	= fat_update_time,
    644};
    645
    646static void setup(struct super_block *sb)
    647{
    648	MSDOS_SB(sb)->dir_ops = &msdos_dir_inode_operations;
    649	sb->s_d_op = &msdos_dentry_operations;
    650	sb->s_flags |= SB_NOATIME;
    651}
    652
    653static int msdos_fill_super(struct super_block *sb, void *data, int silent)
    654{
    655	return fat_fill_super(sb, data, silent, 0, setup);
    656}
    657
    658static struct dentry *msdos_mount(struct file_system_type *fs_type,
    659			int flags, const char *dev_name,
    660			void *data)
    661{
    662	return mount_bdev(fs_type, flags, dev_name, data, msdos_fill_super);
    663}
    664
    665static struct file_system_type msdos_fs_type = {
    666	.owner		= THIS_MODULE,
    667	.name		= "msdos",
    668	.mount		= msdos_mount,
    669	.kill_sb	= kill_block_super,
    670	.fs_flags	= FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
    671};
    672MODULE_ALIAS_FS("msdos");
    673
    674static int __init init_msdos_fs(void)
    675{
    676	return register_filesystem(&msdos_fs_type);
    677}
    678
    679static void __exit exit_msdos_fs(void)
    680{
    681	unregister_filesystem(&msdos_fs_type);
    682}
    683
    684MODULE_LICENSE("GPL");
    685MODULE_AUTHOR("Werner Almesberger");
    686MODULE_DESCRIPTION("MS-DOS filesystem support");
    687
    688module_init(init_msdos_fs)
    689module_exit(exit_msdos_fs)