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