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