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