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