linux/fs/fat/namei_msdos.c
<<
>>
Prefs
   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 "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, const struct inode *inode,
 152               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 */
 168static int msdos_cmp(const struct dentry *parent, const struct inode *pinode,
 169                const struct dentry *dentry, const struct inode *inode,
 170                unsigned int len, const char *str, const struct qstr *name)
 171{
 172        struct fat_mount_options *options = &MSDOS_SB(parent->d_sb)->options;
 173        unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
 174        int error;
 175
 176        error = msdos_format_name(name->name, name->len, a_msdos_name, options);
 177        if (error)
 178                goto old_compare;
 179        error = msdos_format_name(str, len, b_msdos_name, options);
 180        if (error)
 181                goto old_compare;
 182        error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
 183out:
 184        return error;
 185
 186old_compare:
 187        error = 1;
 188        if (name->len == len)
 189                error = memcmp(name->name, str, len);
 190        goto out;
 191}
 192
 193static const struct dentry_operations msdos_dentry_operations = {
 194        .d_hash         = msdos_hash,
 195        .d_compare      = msdos_cmp,
 196};
 197
 198/*
 199 * AV. Wrappers for FAT sb operations. Is it wise?
 200 */
 201
 202/***** Get inode using directory and name */
 203static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
 204                                   struct nameidata *nd)
 205{
 206        struct super_block *sb = dir->i_sb;
 207        struct fat_slot_info sinfo;
 208        struct inode *inode;
 209        int err;
 210
 211        lock_super(sb);
 212
 213        err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
 214        if (err) {
 215                if (err == -ENOENT) {
 216                        inode = NULL;
 217                        goto out;
 218                }
 219                goto error;
 220        }
 221
 222        inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
 223        brelse(sinfo.bh);
 224        if (IS_ERR(inode)) {
 225                err = PTR_ERR(inode);
 226                goto error;
 227        }
 228out:
 229        unlock_super(sb);
 230        return d_splice_alias(inode, dentry);
 231
 232error:
 233        unlock_super(sb);
 234        return ERR_PTR(err);
 235}
 236
 237/***** Creates a directory entry (name is already formatted). */
 238static int msdos_add_entry(struct inode *dir, const unsigned char *name,
 239                           int is_dir, int is_hid, int cluster,
 240                           struct timespec *ts, struct fat_slot_info *sinfo)
 241{
 242        struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
 243        struct msdos_dir_entry de;
 244        __le16 time, date;
 245        int err;
 246
 247        memcpy(de.name, name, MSDOS_NAME);
 248        de.attr = is_dir ? ATTR_DIR : ATTR_ARCH;
 249        if (is_hid)
 250                de.attr |= ATTR_HIDDEN;
 251        de.lcase = 0;
 252        fat_time_unix2fat(sbi, ts, &time, &date, NULL);
 253        de.cdate = de.adate = 0;
 254        de.ctime = 0;
 255        de.ctime_cs = 0;
 256        de.time = time;
 257        de.date = date;
 258        de.start = cpu_to_le16(cluster);
 259        de.starthi = cpu_to_le16(cluster >> 16);
 260        de.size = 0;
 261
 262        err = fat_add_entries(dir, &de, 1, sinfo);
 263        if (err)
 264                return err;
 265
 266        dir->i_ctime = dir->i_mtime = *ts;
 267        if (IS_DIRSYNC(dir))
 268                (void)fat_sync_inode(dir);
 269        else
 270                mark_inode_dirty(dir);
 271
 272        return 0;
 273}
 274
 275/***** Create a file */
 276static int msdos_create(struct inode *dir, struct dentry *dentry, int mode,
 277                        struct nameidata *nd)
 278{
 279        struct super_block *sb = dir->i_sb;
 280        struct inode *inode = NULL;
 281        struct fat_slot_info sinfo;
 282        struct timespec ts;
 283        unsigned char msdos_name[MSDOS_NAME];
 284        int err, is_hid;
 285
 286        lock_super(sb);
 287
 288        err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
 289                                msdos_name, &MSDOS_SB(sb)->options);
 290        if (err)
 291                goto out;
 292        is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
 293        /* Have to do it due to foo vs. .foo conflicts */
 294        if (!fat_scan(dir, msdos_name, &sinfo)) {
 295                brelse(sinfo.bh);
 296                err = -EINVAL;
 297                goto out;
 298        }
 299
 300        ts = CURRENT_TIME_SEC;
 301        err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo);
 302        if (err)
 303                goto out;
 304        inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
 305        brelse(sinfo.bh);
 306        if (IS_ERR(inode)) {
 307                err = PTR_ERR(inode);
 308                goto out;
 309        }
 310        inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
 311        /* timestamp is already written, so mark_inode_dirty() is unneeded. */
 312
 313        d_instantiate(dentry, inode);
 314out:
 315        unlock_super(sb);
 316        if (!err)
 317                err = fat_flush_inodes(sb, dir, inode);
 318        return err;
 319}
 320
 321/***** Remove a directory */
 322static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
 323{
 324        struct super_block *sb = dir->i_sb;
 325        struct inode *inode = dentry->d_inode;
 326        struct fat_slot_info sinfo;
 327        int err;
 328
 329        lock_super(sb);
 330        /*
 331         * Check whether the directory is not in use, then check
 332         * whether it is empty.
 333         */
 334        err = fat_dir_empty(inode);
 335        if (err)
 336                goto out;
 337        err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
 338        if (err)
 339                goto out;
 340
 341        err = fat_remove_entries(dir, &sinfo);  /* and releases bh */
 342        if (err)
 343                goto out;
 344        drop_nlink(dir);
 345
 346        clear_nlink(inode);
 347        inode->i_ctime = CURRENT_TIME_SEC;
 348        fat_detach(inode);
 349out:
 350        unlock_super(sb);
 351        if (!err)
 352                err = fat_flush_inodes(sb, dir, inode);
 353
 354        return err;
 355}
 356
 357/***** Make a directory */
 358static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 359{
 360        struct super_block *sb = dir->i_sb;
 361        struct fat_slot_info sinfo;
 362        struct inode *inode;
 363        unsigned char msdos_name[MSDOS_NAME];
 364        struct timespec ts;
 365        int err, is_hid, cluster;
 366
 367        lock_super(sb);
 368
 369        err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
 370                                msdos_name, &MSDOS_SB(sb)->options);
 371        if (err)
 372                goto out;
 373        is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
 374        /* foo vs .foo situation */
 375        if (!fat_scan(dir, msdos_name, &sinfo)) {
 376                brelse(sinfo.bh);
 377                err = -EINVAL;
 378                goto out;
 379        }
 380
 381        ts = CURRENT_TIME_SEC;
 382        cluster = fat_alloc_new_dir(dir, &ts);
 383        if (cluster < 0) {
 384                err = cluster;
 385                goto out;
 386        }
 387        err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
 388        if (err)
 389                goto out_free;
 390        inc_nlink(dir);
 391
 392        inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
 393        brelse(sinfo.bh);
 394        if (IS_ERR(inode)) {
 395                err = PTR_ERR(inode);
 396                /* the directory was completed, just return a error */
 397                goto out;
 398        }
 399        inode->i_nlink = 2;
 400        inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
 401        /* timestamp is already written, so mark_inode_dirty() is unneeded. */
 402
 403        d_instantiate(dentry, inode);
 404
 405        unlock_super(sb);
 406        fat_flush_inodes(sb, dir, inode);
 407        return 0;
 408
 409out_free:
 410        fat_free_clusters(dir, cluster);
 411out:
 412        unlock_super(sb);
 413        return err;
 414}
 415
 416/***** Unlink a file */
 417static int msdos_unlink(struct inode *dir, struct dentry *dentry)
 418{
 419        struct inode *inode = dentry->d_inode;
 420        struct super_block *sb= inode->i_sb;
 421        struct fat_slot_info sinfo;
 422        int err;
 423
 424        lock_super(sb);
 425        err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
 426        if (err)
 427                goto out;
 428
 429        err = fat_remove_entries(dir, &sinfo);  /* and releases bh */
 430        if (err)
 431                goto out;
 432        clear_nlink(inode);
 433        inode->i_ctime = CURRENT_TIME_SEC;
 434        fat_detach(inode);
 435out:
 436        unlock_super(sb);
 437        if (!err)
 438                err = fat_flush_inodes(sb, dir, inode);
 439
 440        return err;
 441}
 442
 443static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
 444                           struct dentry *old_dentry,
 445                           struct inode *new_dir, unsigned char *new_name,
 446                           struct dentry *new_dentry, int is_hid)
 447{
 448        struct buffer_head *dotdot_bh;
 449        struct msdos_dir_entry *dotdot_de;
 450        struct inode *old_inode, *new_inode;
 451        struct fat_slot_info old_sinfo, sinfo;
 452        struct timespec ts;
 453        loff_t dotdot_i_pos, new_i_pos;
 454        int err, old_attrs, is_dir, update_dotdot, corrupt = 0;
 455
 456        old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
 457        old_inode = old_dentry->d_inode;
 458        new_inode = new_dentry->d_inode;
 459
 460        err = fat_scan(old_dir, old_name, &old_sinfo);
 461        if (err) {
 462                err = -EIO;
 463                goto out;
 464        }
 465
 466        is_dir = S_ISDIR(old_inode->i_mode);
 467        update_dotdot = (is_dir && old_dir != new_dir);
 468        if (update_dotdot) {
 469                if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de,
 470                                         &dotdot_i_pos) < 0) {
 471                        err = -EIO;
 472                        goto out;
 473                }
 474        }
 475
 476        old_attrs = MSDOS_I(old_inode)->i_attrs;
 477        err = fat_scan(new_dir, new_name, &sinfo);
 478        if (!err) {
 479                if (!new_inode) {
 480                        /* "foo" -> ".foo" case. just change the ATTR_HIDDEN */
 481                        if (sinfo.de != old_sinfo.de) {
 482                                err = -EINVAL;
 483                                goto out;
 484                        }
 485                        if (is_hid)
 486                                MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
 487                        else
 488                                MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
 489                        if (IS_DIRSYNC(old_dir)) {
 490                                err = fat_sync_inode(old_inode);
 491                                if (err) {
 492                                        MSDOS_I(old_inode)->i_attrs = old_attrs;
 493                                        goto out;
 494                                }
 495                        } else
 496                                mark_inode_dirty(old_inode);
 497
 498                        old_dir->i_version++;
 499                        old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
 500                        if (IS_DIRSYNC(old_dir))
 501                                (void)fat_sync_inode(old_dir);
 502                        else
 503                                mark_inode_dirty(old_dir);
 504                        goto out;
 505                }
 506        }
 507
 508        ts = CURRENT_TIME_SEC;
 509        if (new_inode) {
 510                if (err)
 511                        goto out;
 512                if (is_dir) {
 513                        err = fat_dir_empty(new_inode);
 514                        if (err)
 515                                goto out;
 516                }
 517                new_i_pos = MSDOS_I(new_inode)->i_pos;
 518                fat_detach(new_inode);
 519        } else {
 520                err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0,
 521                                      &ts, &sinfo);
 522                if (err)
 523                        goto out;
 524                new_i_pos = sinfo.i_pos;
 525        }
 526        new_dir->i_version++;
 527
 528        fat_detach(old_inode);
 529        fat_attach(old_inode, new_i_pos);
 530        if (is_hid)
 531                MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
 532        else
 533                MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
 534        if (IS_DIRSYNC(new_dir)) {
 535                err = fat_sync_inode(old_inode);
 536                if (err)
 537                        goto error_inode;
 538        } else
 539                mark_inode_dirty(old_inode);
 540
 541        if (update_dotdot) {
 542                int start = MSDOS_I(new_dir)->i_logstart;
 543                dotdot_de->start = cpu_to_le16(start);
 544                dotdot_de->starthi = cpu_to_le16(start >> 16);
 545                mark_buffer_dirty_inode(dotdot_bh, old_inode);
 546                if (IS_DIRSYNC(new_dir)) {
 547                        err = sync_dirty_buffer(dotdot_bh);
 548                        if (err)
 549                                goto error_dotdot;
 550                }
 551                drop_nlink(old_dir);
 552                if (!new_inode)
 553                        inc_nlink(new_dir);
 554        }
 555
 556        err = fat_remove_entries(old_dir, &old_sinfo);  /* and releases bh */
 557        old_sinfo.bh = NULL;
 558        if (err)
 559                goto error_dotdot;
 560        old_dir->i_version++;
 561        old_dir->i_ctime = old_dir->i_mtime = ts;
 562        if (IS_DIRSYNC(old_dir))
 563                (void)fat_sync_inode(old_dir);
 564        else
 565                mark_inode_dirty(old_dir);
 566
 567        if (new_inode) {
 568                drop_nlink(new_inode);
 569                if (is_dir)
 570                        drop_nlink(new_inode);
 571                new_inode->i_ctime = ts;
 572        }
 573out:
 574        brelse(sinfo.bh);
 575        brelse(dotdot_bh);
 576        brelse(old_sinfo.bh);
 577        return err;
 578
 579error_dotdot:
 580        /* data cluster is shared, serious corruption */
 581        corrupt = 1;
 582
 583        if (update_dotdot) {
 584                int start = MSDOS_I(old_dir)->i_logstart;
 585                dotdot_de->start = cpu_to_le16(start);
 586                dotdot_de->starthi = cpu_to_le16(start >> 16);
 587                mark_buffer_dirty_inode(dotdot_bh, old_inode);
 588                corrupt |= sync_dirty_buffer(dotdot_bh);
 589        }
 590error_inode:
 591        fat_detach(old_inode);
 592        fat_attach(old_inode, old_sinfo.i_pos);
 593        MSDOS_I(old_inode)->i_attrs = old_attrs;
 594        if (new_inode) {
 595                fat_attach(new_inode, new_i_pos);
 596                if (corrupt)
 597                        corrupt |= fat_sync_inode(new_inode);
 598        } else {
 599                /*
 600                 * If new entry was not sharing the data cluster, it
 601                 * shouldn't be serious corruption.
 602                 */
 603                int err2 = fat_remove_entries(new_dir, &sinfo);
 604                if (corrupt)
 605                        corrupt |= err2;
 606                sinfo.bh = NULL;
 607        }
 608        if (corrupt < 0) {
 609                fat_fs_error(new_dir->i_sb,
 610                             "%s: Filesystem corrupted (i_pos %lld)",
 611                             __func__, sinfo.i_pos);
 612        }
 613        goto out;
 614}
 615
 616/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
 617static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
 618                        struct inode *new_dir, struct dentry *new_dentry)
 619{
 620        struct super_block *sb = old_dir->i_sb;
 621        unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
 622        int err, is_hid;
 623
 624        lock_super(sb);
 625
 626        err = msdos_format_name(old_dentry->d_name.name,
 627                                old_dentry->d_name.len, old_msdos_name,
 628                                &MSDOS_SB(old_dir->i_sb)->options);
 629        if (err)
 630                goto out;
 631        err = msdos_format_name(new_dentry->d_name.name,
 632                                new_dentry->d_name.len, new_msdos_name,
 633                                &MSDOS_SB(new_dir->i_sb)->options);
 634        if (err)
 635                goto out;
 636
 637        is_hid =
 638             (new_dentry->d_name.name[0] == '.') && (new_msdos_name[0] != '.');
 639
 640        err = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
 641                              new_dir, new_msdos_name, new_dentry, is_hid);
 642out:
 643        unlock_super(sb);
 644        if (!err)
 645                err = fat_flush_inodes(sb, old_dir, new_dir);
 646        return err;
 647}
 648
 649static const struct inode_operations msdos_dir_inode_operations = {
 650        .create         = msdos_create,
 651        .lookup         = msdos_lookup,
 652        .unlink         = msdos_unlink,
 653        .mkdir          = msdos_mkdir,
 654        .rmdir          = msdos_rmdir,
 655        .rename         = msdos_rename,
 656        .setattr        = fat_setattr,
 657        .getattr        = fat_getattr,
 658};
 659
 660static void setup(struct super_block *sb)
 661{
 662        sb->s_d_op = &msdos_dentry_operations;
 663        sb->s_flags |= MS_NOATIME;
 664}
 665
 666static int msdos_fill_super(struct super_block *sb, void *data, int silent)
 667{
 668        return fat_fill_super(sb, data, silent, &msdos_dir_inode_operations,
 669                             0, setup);
 670}
 671
 672static struct dentry *msdos_mount(struct file_system_type *fs_type,
 673                        int flags, const char *dev_name,
 674                        void *data)
 675{
 676        return mount_bdev(fs_type, flags, dev_name, data, msdos_fill_super);
 677}
 678
 679static struct file_system_type msdos_fs_type = {
 680        .owner          = THIS_MODULE,
 681        .name           = "msdos",
 682        .mount          = msdos_mount,
 683        .kill_sb        = kill_block_super,
 684        .fs_flags       = FS_REQUIRES_DEV,
 685};
 686
 687static int __init init_msdos_fs(void)
 688{
 689        return register_filesystem(&msdos_fs_type);
 690}
 691
 692static void __exit exit_msdos_fs(void)
 693{
 694        unregister_filesystem(&msdos_fs_type);
 695}
 696
 697MODULE_LICENSE("GPL");
 698MODULE_AUTHOR("Werner Almesberger");
 699MODULE_DESCRIPTION("MS-DOS filesystem support");
 700
 701module_init(init_msdos_fs)
 702module_exit(exit_msdos_fs)
 703