0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/module.h>
0011 #include <linux/iversion.h>
0012 #include "fat.h"
0013
0014
0015 static unsigned char bad_chars[] = "*?<>|\"";
0016 static unsigned char bad_if_strict[] = "+=,; ";
0017
0018
0019 static int msdos_format_name(const unsigned char *name, int len,
0020 unsigned char *res, struct fat_mount_options *opts)
0021
0022
0023
0024
0025
0026
0027 {
0028 unsigned char *walk;
0029 unsigned char c;
0030 int space;
0031
0032 if (name[0] == '.') {
0033 if (opts->dotsOK) {
0034
0035 name++;
0036 len--;
0037 } else
0038 return -EINVAL;
0039 }
0040
0041
0042
0043 space = 1;
0044 c = 0;
0045 for (walk = res; len && walk - res < 8; walk++) {
0046 c = *name++;
0047 len--;
0048 if (opts->name_check != 'r' && strchr(bad_chars, c))
0049 return -EINVAL;
0050 if (opts->name_check == 's' && strchr(bad_if_strict, c))
0051 return -EINVAL;
0052 if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
0053 return -EINVAL;
0054 if (c < ' ' || c == ':' || c == '\\')
0055 return -EINVAL;
0056
0057
0058
0059
0060
0061
0062
0063
0064 if ((res == walk) && (c == 0xE5))
0065 c = 0x05;
0066 if (c == '.')
0067 break;
0068 space = (c == ' ');
0069 *walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c - 32 : c;
0070 }
0071 if (space)
0072 return -EINVAL;
0073 if (opts->name_check == 's' && len && c != '.') {
0074 c = *name++;
0075 len--;
0076 if (c != '.')
0077 return -EINVAL;
0078 }
0079 while (c != '.' && len--)
0080 c = *name++;
0081 if (c == '.') {
0082 while (walk - res < 8)
0083 *walk++ = ' ';
0084 while (len > 0 && walk - res < MSDOS_NAME) {
0085 c = *name++;
0086 len--;
0087 if (opts->name_check != 'r' && strchr(bad_chars, c))
0088 return -EINVAL;
0089 if (opts->name_check == 's' &&
0090 strchr(bad_if_strict, c))
0091 return -EINVAL;
0092 if (c < ' ' || c == ':' || c == '\\')
0093 return -EINVAL;
0094 if (c == '.') {
0095 if (opts->name_check == 's')
0096 return -EINVAL;
0097 break;
0098 }
0099 if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
0100 return -EINVAL;
0101 space = c == ' ';
0102 if (!opts->nocase && c >= 'a' && c <= 'z')
0103 *walk++ = c - 32;
0104 else
0105 *walk++ = c;
0106 }
0107 if (space)
0108 return -EINVAL;
0109 if (opts->name_check == 's' && len)
0110 return -EINVAL;
0111 }
0112 while (walk - res < MSDOS_NAME)
0113 *walk++ = ' ';
0114
0115 return 0;
0116 }
0117
0118
0119 static int msdos_find(struct inode *dir, const unsigned char *name, int len,
0120 struct fat_slot_info *sinfo)
0121 {
0122 struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
0123 unsigned char msdos_name[MSDOS_NAME];
0124 int err;
0125
0126 err = msdos_format_name(name, len, msdos_name, &sbi->options);
0127 if (err)
0128 return -ENOENT;
0129
0130 err = fat_scan(dir, msdos_name, sinfo);
0131 if (!err && sbi->options.dotsOK) {
0132 if (name[0] == '.') {
0133 if (!(sinfo->de->attr & ATTR_HIDDEN))
0134 err = -ENOENT;
0135 } else {
0136 if (sinfo->de->attr & ATTR_HIDDEN)
0137 err = -ENOENT;
0138 }
0139 if (err)
0140 brelse(sinfo->bh);
0141 }
0142 return err;
0143 }
0144
0145
0146
0147
0148
0149
0150
0151 static int msdos_hash(const struct dentry *dentry, struct qstr *qstr)
0152 {
0153 struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
0154 unsigned char msdos_name[MSDOS_NAME];
0155 int error;
0156
0157 error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
0158 if (!error)
0159 qstr->hash = full_name_hash(dentry, msdos_name, MSDOS_NAME);
0160 return 0;
0161 }
0162
0163
0164
0165
0166
0167 static int msdos_cmp(const struct dentry *dentry,
0168 unsigned int len, const char *str, const struct qstr *name)
0169 {
0170 struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
0171 unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
0172 int error;
0173
0174 error = msdos_format_name(name->name, name->len, a_msdos_name, options);
0175 if (error)
0176 goto old_compare;
0177 error = msdos_format_name(str, len, b_msdos_name, options);
0178 if (error)
0179 goto old_compare;
0180 error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
0181 out:
0182 return error;
0183
0184 old_compare:
0185 error = 1;
0186 if (name->len == len)
0187 error = memcmp(name->name, str, len);
0188 goto out;
0189 }
0190
0191 static const struct dentry_operations msdos_dentry_operations = {
0192 .d_hash = msdos_hash,
0193 .d_compare = msdos_cmp,
0194 };
0195
0196
0197
0198
0199
0200
0201 static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
0202 unsigned int flags)
0203 {
0204 struct super_block *sb = dir->i_sb;
0205 struct fat_slot_info sinfo;
0206 struct inode *inode;
0207 int err;
0208
0209 mutex_lock(&MSDOS_SB(sb)->s_lock);
0210 err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
0211 switch (err) {
0212 case -ENOENT:
0213 inode = NULL;
0214 break;
0215 case 0:
0216 inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
0217 brelse(sinfo.bh);
0218 break;
0219 default:
0220 inode = ERR_PTR(err);
0221 }
0222 mutex_unlock(&MSDOS_SB(sb)->s_lock);
0223 return d_splice_alias(inode, dentry);
0224 }
0225
0226
0227 static int msdos_add_entry(struct inode *dir, const unsigned char *name,
0228 int is_dir, int is_hid, int cluster,
0229 struct timespec64 *ts, struct fat_slot_info *sinfo)
0230 {
0231 struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
0232 struct msdos_dir_entry de;
0233 __le16 time, date;
0234 int err;
0235
0236 memcpy(de.name, name, MSDOS_NAME);
0237 de.attr = is_dir ? ATTR_DIR : ATTR_ARCH;
0238 if (is_hid)
0239 de.attr |= ATTR_HIDDEN;
0240 de.lcase = 0;
0241 fat_time_unix2fat(sbi, ts, &time, &date, NULL);
0242 de.cdate = de.adate = 0;
0243 de.ctime = 0;
0244 de.ctime_cs = 0;
0245 de.time = time;
0246 de.date = date;
0247 fat_set_start(&de, cluster);
0248 de.size = 0;
0249
0250 err = fat_add_entries(dir, &de, 1, sinfo);
0251 if (err)
0252 return err;
0253
0254 fat_truncate_time(dir, ts, S_CTIME|S_MTIME);
0255 if (IS_DIRSYNC(dir))
0256 (void)fat_sync_inode(dir);
0257 else
0258 mark_inode_dirty(dir);
0259
0260 return 0;
0261 }
0262
0263
0264 static int msdos_create(struct user_namespace *mnt_userns, struct inode *dir,
0265 struct dentry *dentry, umode_t mode, bool excl)
0266 {
0267 struct super_block *sb = dir->i_sb;
0268 struct inode *inode = NULL;
0269 struct fat_slot_info sinfo;
0270 struct timespec64 ts;
0271 unsigned char msdos_name[MSDOS_NAME];
0272 int err, is_hid;
0273
0274 mutex_lock(&MSDOS_SB(sb)->s_lock);
0275
0276 err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
0277 msdos_name, &MSDOS_SB(sb)->options);
0278 if (err)
0279 goto out;
0280 is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
0281
0282 if (!fat_scan(dir, msdos_name, &sinfo)) {
0283 brelse(sinfo.bh);
0284 err = -EINVAL;
0285 goto out;
0286 }
0287
0288 ts = current_time(dir);
0289 err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo);
0290 if (err)
0291 goto out;
0292 inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
0293 brelse(sinfo.bh);
0294 if (IS_ERR(inode)) {
0295 err = PTR_ERR(inode);
0296 goto out;
0297 }
0298 fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME);
0299
0300
0301 d_instantiate(dentry, inode);
0302 out:
0303 mutex_unlock(&MSDOS_SB(sb)->s_lock);
0304 if (!err)
0305 err = fat_flush_inodes(sb, dir, inode);
0306 return err;
0307 }
0308
0309
0310 static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
0311 {
0312 struct super_block *sb = dir->i_sb;
0313 struct inode *inode = d_inode(dentry);
0314 struct fat_slot_info sinfo;
0315 int err;
0316
0317 mutex_lock(&MSDOS_SB(sb)->s_lock);
0318 err = fat_dir_empty(inode);
0319 if (err)
0320 goto out;
0321 err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
0322 if (err)
0323 goto out;
0324
0325 err = fat_remove_entries(dir, &sinfo);
0326 if (err)
0327 goto out;
0328 drop_nlink(dir);
0329
0330 clear_nlink(inode);
0331 fat_truncate_time(inode, NULL, S_CTIME);
0332 fat_detach(inode);
0333 out:
0334 mutex_unlock(&MSDOS_SB(sb)->s_lock);
0335 if (!err)
0336 err = fat_flush_inodes(sb, dir, inode);
0337
0338 return err;
0339 }
0340
0341
0342 static int msdos_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
0343 struct dentry *dentry, umode_t mode)
0344 {
0345 struct super_block *sb = dir->i_sb;
0346 struct fat_slot_info sinfo;
0347 struct inode *inode;
0348 unsigned char msdos_name[MSDOS_NAME];
0349 struct timespec64 ts;
0350 int err, is_hid, cluster;
0351
0352 mutex_lock(&MSDOS_SB(sb)->s_lock);
0353
0354 err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
0355 msdos_name, &MSDOS_SB(sb)->options);
0356 if (err)
0357 goto out;
0358 is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
0359
0360 if (!fat_scan(dir, msdos_name, &sinfo)) {
0361 brelse(sinfo.bh);
0362 err = -EINVAL;
0363 goto out;
0364 }
0365
0366 ts = current_time(dir);
0367 cluster = fat_alloc_new_dir(dir, &ts);
0368 if (cluster < 0) {
0369 err = cluster;
0370 goto out;
0371 }
0372 err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
0373 if (err)
0374 goto out_free;
0375 inc_nlink(dir);
0376
0377 inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
0378 brelse(sinfo.bh);
0379 if (IS_ERR(inode)) {
0380 err = PTR_ERR(inode);
0381
0382 goto out;
0383 }
0384 set_nlink(inode, 2);
0385 fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME);
0386
0387
0388 d_instantiate(dentry, inode);
0389
0390 mutex_unlock(&MSDOS_SB(sb)->s_lock);
0391 fat_flush_inodes(sb, dir, inode);
0392 return 0;
0393
0394 out_free:
0395 fat_free_clusters(dir, cluster);
0396 out:
0397 mutex_unlock(&MSDOS_SB(sb)->s_lock);
0398 return err;
0399 }
0400
0401
0402 static int msdos_unlink(struct inode *dir, struct dentry *dentry)
0403 {
0404 struct inode *inode = d_inode(dentry);
0405 struct super_block *sb = inode->i_sb;
0406 struct fat_slot_info sinfo;
0407 int err;
0408
0409 mutex_lock(&MSDOS_SB(sb)->s_lock);
0410 err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
0411 if (err)
0412 goto out;
0413
0414 err = fat_remove_entries(dir, &sinfo);
0415 if (err)
0416 goto out;
0417 clear_nlink(inode);
0418 fat_truncate_time(inode, NULL, S_CTIME);
0419 fat_detach(inode);
0420 out:
0421 mutex_unlock(&MSDOS_SB(sb)->s_lock);
0422 if (!err)
0423 err = fat_flush_inodes(sb, dir, inode);
0424
0425 return err;
0426 }
0427
0428 static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
0429 struct dentry *old_dentry,
0430 struct inode *new_dir, unsigned char *new_name,
0431 struct dentry *new_dentry, int is_hid)
0432 {
0433 struct buffer_head *dotdot_bh;
0434 struct msdos_dir_entry *dotdot_de;
0435 struct inode *old_inode, *new_inode;
0436 struct fat_slot_info old_sinfo, sinfo;
0437 struct timespec64 ts;
0438 loff_t new_i_pos;
0439 int err, old_attrs, is_dir, update_dotdot, corrupt = 0;
0440
0441 old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
0442 old_inode = d_inode(old_dentry);
0443 new_inode = d_inode(new_dentry);
0444
0445 err = fat_scan(old_dir, old_name, &old_sinfo);
0446 if (err) {
0447 err = -EIO;
0448 goto out;
0449 }
0450
0451 is_dir = S_ISDIR(old_inode->i_mode);
0452 update_dotdot = (is_dir && old_dir != new_dir);
0453 if (update_dotdot) {
0454 if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de)) {
0455 err = -EIO;
0456 goto out;
0457 }
0458 }
0459
0460 old_attrs = MSDOS_I(old_inode)->i_attrs;
0461 err = fat_scan(new_dir, new_name, &sinfo);
0462 if (!err) {
0463 if (!new_inode) {
0464
0465 if (sinfo.de != old_sinfo.de) {
0466 err = -EINVAL;
0467 goto out;
0468 }
0469 if (is_hid)
0470 MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
0471 else
0472 MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
0473 if (IS_DIRSYNC(old_dir)) {
0474 err = fat_sync_inode(old_inode);
0475 if (err) {
0476 MSDOS_I(old_inode)->i_attrs = old_attrs;
0477 goto out;
0478 }
0479 } else
0480 mark_inode_dirty(old_inode);
0481
0482 inode_inc_iversion(old_dir);
0483 fat_truncate_time(old_dir, NULL, S_CTIME|S_MTIME);
0484 if (IS_DIRSYNC(old_dir))
0485 (void)fat_sync_inode(old_dir);
0486 else
0487 mark_inode_dirty(old_dir);
0488 goto out;
0489 }
0490 }
0491
0492 ts = current_time(old_inode);
0493 if (new_inode) {
0494 if (err)
0495 goto out;
0496 if (is_dir) {
0497 err = fat_dir_empty(new_inode);
0498 if (err)
0499 goto out;
0500 }
0501 new_i_pos = MSDOS_I(new_inode)->i_pos;
0502 fat_detach(new_inode);
0503 } else {
0504 err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0,
0505 &ts, &sinfo);
0506 if (err)
0507 goto out;
0508 new_i_pos = sinfo.i_pos;
0509 }
0510 inode_inc_iversion(new_dir);
0511
0512 fat_detach(old_inode);
0513 fat_attach(old_inode, new_i_pos);
0514 if (is_hid)
0515 MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
0516 else
0517 MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
0518 if (IS_DIRSYNC(new_dir)) {
0519 err = fat_sync_inode(old_inode);
0520 if (err)
0521 goto error_inode;
0522 } else
0523 mark_inode_dirty(old_inode);
0524
0525 if (update_dotdot) {
0526 fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);
0527 mark_buffer_dirty_inode(dotdot_bh, old_inode);
0528 if (IS_DIRSYNC(new_dir)) {
0529 err = sync_dirty_buffer(dotdot_bh);
0530 if (err)
0531 goto error_dotdot;
0532 }
0533 drop_nlink(old_dir);
0534 if (!new_inode)
0535 inc_nlink(new_dir);
0536 }
0537
0538 err = fat_remove_entries(old_dir, &old_sinfo);
0539 old_sinfo.bh = NULL;
0540 if (err)
0541 goto error_dotdot;
0542 inode_inc_iversion(old_dir);
0543 fat_truncate_time(old_dir, &ts, S_CTIME|S_MTIME);
0544 if (IS_DIRSYNC(old_dir))
0545 (void)fat_sync_inode(old_dir);
0546 else
0547 mark_inode_dirty(old_dir);
0548
0549 if (new_inode) {
0550 drop_nlink(new_inode);
0551 if (is_dir)
0552 drop_nlink(new_inode);
0553 fat_truncate_time(new_inode, &ts, S_CTIME);
0554 }
0555 out:
0556 brelse(sinfo.bh);
0557 brelse(dotdot_bh);
0558 brelse(old_sinfo.bh);
0559 return err;
0560
0561 error_dotdot:
0562
0563 corrupt = 1;
0564
0565 if (update_dotdot) {
0566 fat_set_start(dotdot_de, MSDOS_I(old_dir)->i_logstart);
0567 mark_buffer_dirty_inode(dotdot_bh, old_inode);
0568 corrupt |= sync_dirty_buffer(dotdot_bh);
0569 }
0570 error_inode:
0571 fat_detach(old_inode);
0572 fat_attach(old_inode, old_sinfo.i_pos);
0573 MSDOS_I(old_inode)->i_attrs = old_attrs;
0574 if (new_inode) {
0575 fat_attach(new_inode, new_i_pos);
0576 if (corrupt)
0577 corrupt |= fat_sync_inode(new_inode);
0578 } else {
0579
0580
0581
0582
0583 int err2 = fat_remove_entries(new_dir, &sinfo);
0584 if (corrupt)
0585 corrupt |= err2;
0586 sinfo.bh = NULL;
0587 }
0588 if (corrupt < 0) {
0589 fat_fs_error(new_dir->i_sb,
0590 "%s: Filesystem corrupted (i_pos %lld)",
0591 __func__, sinfo.i_pos);
0592 }
0593 goto out;
0594 }
0595
0596
0597 static int msdos_rename(struct user_namespace *mnt_userns,
0598 struct inode *old_dir, struct dentry *old_dentry,
0599 struct inode *new_dir, struct dentry *new_dentry,
0600 unsigned int flags)
0601 {
0602 struct super_block *sb = old_dir->i_sb;
0603 unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
0604 int err, is_hid;
0605
0606 if (flags & ~RENAME_NOREPLACE)
0607 return -EINVAL;
0608
0609 mutex_lock(&MSDOS_SB(sb)->s_lock);
0610
0611 err = msdos_format_name(old_dentry->d_name.name,
0612 old_dentry->d_name.len, old_msdos_name,
0613 &MSDOS_SB(old_dir->i_sb)->options);
0614 if (err)
0615 goto out;
0616 err = msdos_format_name(new_dentry->d_name.name,
0617 new_dentry->d_name.len, new_msdos_name,
0618 &MSDOS_SB(new_dir->i_sb)->options);
0619 if (err)
0620 goto out;
0621
0622 is_hid =
0623 (new_dentry->d_name.name[0] == '.') && (new_msdos_name[0] != '.');
0624
0625 err = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
0626 new_dir, new_msdos_name, new_dentry, is_hid);
0627 out:
0628 mutex_unlock(&MSDOS_SB(sb)->s_lock);
0629 if (!err)
0630 err = fat_flush_inodes(sb, old_dir, new_dir);
0631 return err;
0632 }
0633
0634 static const struct inode_operations msdos_dir_inode_operations = {
0635 .create = msdos_create,
0636 .lookup = msdos_lookup,
0637 .unlink = msdos_unlink,
0638 .mkdir = msdos_mkdir,
0639 .rmdir = msdos_rmdir,
0640 .rename = msdos_rename,
0641 .setattr = fat_setattr,
0642 .getattr = fat_getattr,
0643 .update_time = fat_update_time,
0644 };
0645
0646 static void setup(struct super_block *sb)
0647 {
0648 MSDOS_SB(sb)->dir_ops = &msdos_dir_inode_operations;
0649 sb->s_d_op = &msdos_dentry_operations;
0650 sb->s_flags |= SB_NOATIME;
0651 }
0652
0653 static int msdos_fill_super(struct super_block *sb, void *data, int silent)
0654 {
0655 return fat_fill_super(sb, data, silent, 0, setup);
0656 }
0657
0658 static struct dentry *msdos_mount(struct file_system_type *fs_type,
0659 int flags, const char *dev_name,
0660 void *data)
0661 {
0662 return mount_bdev(fs_type, flags, dev_name, data, msdos_fill_super);
0663 }
0664
0665 static struct file_system_type msdos_fs_type = {
0666 .owner = THIS_MODULE,
0667 .name = "msdos",
0668 .mount = msdos_mount,
0669 .kill_sb = kill_block_super,
0670 .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
0671 };
0672 MODULE_ALIAS_FS("msdos");
0673
0674 static int __init init_msdos_fs(void)
0675 {
0676 return register_filesystem(&msdos_fs_type);
0677 }
0678
0679 static void __exit exit_msdos_fs(void)
0680 {
0681 unregister_filesystem(&msdos_fs_type);
0682 }
0683
0684 MODULE_LICENSE("GPL");
0685 MODULE_AUTHOR("Werner Almesberger");
0686 MODULE_DESCRIPTION("MS-DOS filesystem support");
0687
0688 module_init(init_msdos_fs)
0689 module_exit(exit_msdos_fs)