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