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