1/* 2 Copyright 1999-2001, Be Incorporated. All Rights Reserved. 3 This file may be used under the terms of the Be Sample Code License. 4*/ 5 6#include "dir.h" 7 8#include "system_dependencies.h" 9 10#include "iter.h" 11#include "dosfs.h" 12#include "attr.h" 13#include "dlist.h" 14#include "fat.h" 15#include "util.h" 16#include "vcache.h" 17#include "file.h" 18 19#include "encodings.h" 20 21#define DPRINTF(a,b) if (debug_dir > (a)) dprintf b 22 23 24typedef struct dircookie { 25 uint32 current_index; 26} dircookie; 27 28static status_t findfile(nspace *vol, vnode *dir, const char *file, 29 ino_t *vnid, vnode **node, bool check_case, bool check_dups, 30 bool *dups_exist); 31 32 33// private structure for returning data from _next_dirent_() 34struct _dirent_info_ { 35 uint32 sindex; 36 uint32 eindex; 37 uint32 mode; 38 uint32 cluster; 39 uint32 size; 40 uint32 time; 41 uint32 creation_time; 42}; 43 44 45//! Scans dir for the next entry, using the state stored in a struct diri. 46static status_t 47_next_dirent_(struct diri *iter, struct _dirent_info_ *oinfo, char *filename, 48 int len) 49{ 50 uint8 *buffer; 51 uint8 hash = 0; 52 uchar uni[1024]; 53 uint16 *puni; 54 uint32 i; 55 56 // lfn state 57 uint32 start_index = 0xffff, filename_len = 0; 58 uint32 lfn_count = 0; 59 60 if (iter->current_block == NULL) 61 return ENOENT; 62 63 if (len < 15) { 64 DPRINTF(0, ("_next_dirent_: len too short (%x)\n", len)); 65 return ENOMEM; 66 } 67 68 buffer = iter->current_block + ((iter->current_index) 69 % (iter->csi.vol->bytes_per_sector / 0x20)) * 0x20; 70 71 for (; buffer != NULL; buffer = diri_next_entry(iter)) { 72 DPRINTF(2, ("_next_dirent_: %" B_PRIu32 "/%" B_PRIu32 "/%" B_PRIu32 73 "\n", iter->csi.cluster, iter->csi.sector, iter->current_index)); 74 if (buffer[0] == 0) { // quit if at end of table 75 if (start_index != 0xffff) 76 dprintf("lfn entry (%" B_PRIu32 ") with no alias\n", lfn_count); 77 return ENOENT; 78 } 79 80 if (buffer[0] == 0xe5) { // skip erased entries 81 if (start_index != 0xffff) { 82 dprintf("lfn entry (%" B_PRIu32 ") with intervening erased " 83 "entries\n", lfn_count); 84 start_index = 0xffff; 85 } 86 DPRINTF(2, ("entry erased, skipping...\n")); 87 continue; 88 } 89 90 if (buffer[0xb] == 0xf) { // long file name 91 if ((buffer[0xc] != 0) || 92 (buffer[0x1a] != 0) || (buffer[0x1b] != 0)) { 93 dprintf("invalid long file name: reserved fields munged\n"); 94 continue; 95 } 96 if (start_index == 0xffff) { 97 if ((buffer[0] & 0x40) == 0) { 98 dprintf("bad lfn start entry in directory\n"); 99 continue; 100 } 101 hash = buffer[0xd]; 102 lfn_count = buffer[0] & 0x1f; 103 start_index = iter->current_index; 104 puni = (uint16 *)(uni + 2*13*(lfn_count - 1)); 105 for (i = 1; i < 0x20; i += 2) { 106 if (*(uint16 *)&buffer[i] == 0xffff) 107 break; 108 *puni++ = *(uint16 *)&buffer[i]; 109 if (i == 0x9) i+=3; 110 if (i == 0x18) i+=2; 111 } 112 *puni++ = 0; 113 filename_len = (uchar *)(puni) - uni; 114 115 continue; 116 } else { 117 if (buffer[0xd] != hash) { 118 dprintf("error in long file name: hash values don't match\n"); 119 start_index = 0xffff; 120 continue; 121 } 122 if (buffer[0] != --lfn_count) { 123 dprintf("bad lfn entry in directory\n"); 124 start_index = 0xffff; 125 continue; 126 } 127 128 puni = (uint16 *)(uni + 2*13*(lfn_count - 1)); 129 for (i = 1; i < 0x20; i += 2) { 130 if ((buffer[i] == 0xff) && (buffer[i+1] == 0xff)) { 131 dprintf("bad lfn entry in directory\n"); 132 start_index = 0xffff; 133 break; 134 } 135 *puni++ = *(uint16 *)&buffer[i]; 136 if (i == 0x9) i+=3; 137 if (i == 0x18) i+=2; 138 } 139 continue; 140 } 141 } 142 143 break; 144 } 145 146 // hit end of directory entries with no luck 147 if (buffer == NULL) 148 return ENOENT; 149 150 // process long name 151 if (start_index != 0xffff) { 152 if (lfn_count != 1) { 153 dprintf("unfinished lfn in directory\n"); 154 start_index = 0xffff; 155 } else { 156 if (unicode_to_utf8(uni, filename_len, (uint8*)filename, len)) { 157 // rewind to beginning of call 158 dprintf("error: long file name too long\n"); 159 160 diri_init(iter->csi.vol, iter->starting_cluster, start_index, 161 iter); 162 return ENAMETOOLONG; 163 } else if (hash_msdos_name((const char *)buffer) != hash) { 164 dprintf("error: long file name (%s) hash and short file name " 165 "don't match\n", filename); 166 start_index = 0xffff; 167 } 168 } 169 } 170 171 // process short name 172 if (start_index == 0xffff) { 173 start_index = iter->current_index; 174 // korli : seen on FreeBSD /src/sys/fs/msdosfs/direntry.h 175 msdos_to_utf8(buffer, (uchar *)filename, len, buffer[0xc] & 0x18); 176 } 177 178 if (oinfo) { 179 oinfo->sindex = start_index; 180 oinfo->eindex = iter->current_index; 181 oinfo->mode = buffer[0xb]; 182 oinfo->cluster = read16(buffer, 0x1a); 183 if (iter->csi.vol->fat_bits == 32) 184 oinfo->cluster += 0x10000 * read16(buffer, 0x14); 185 oinfo->size = read32(buffer, 0x1c); 186 oinfo->time = read32(buffer, 0x16); 187 oinfo->creation_time = read32(buffer, 0x0e); 188 } 189 190 diri_next_entry(iter); 191 192 return B_NO_ERROR; 193} 194 195 196static status_t 197get_next_dirent(nspace *vol, vnode *dir, struct diri *iter, ino_t *vnid, 198 char *filename, int len) 199{ 200 struct _dirent_info_ info; 201 status_t result; 202 203 do { 204 result = _next_dirent_(iter, &info, filename, len); 205 if (result < 0) 206 return result; 207 // only hide volume label entries in the root directory 208 } while ((info.mode & FAT_VOLUME) && (dir->vnid == vol->root_vnode.vnid)); 209 210 if (!strcmp(filename, ".")) { 211 // assign vnode based on parent 212 if (vnid) *vnid = dir->vnid; 213 } else if (!strcmp(filename, "..")) { 214 // assign vnode based on parent of parent 215 if (vnid) *vnid = dir->dir_vnid; 216 } else { 217 if (vnid) { 218 ino_t loc = (IS_DATA_CLUSTER(info.cluster)) 219 ? GENERATE_DIR_CLUSTER_VNID(dir->vnid, info.cluster) 220 : GENERATE_DIR_INDEX_VNID(dir->vnid, info.sindex); 221 bool added_to_vcache = false; 222 223 /* if it matches a loc in the lookup table, we are done. */ 224 result = vcache_loc_to_vnid(vol, loc, vnid); 225 if (result == ENOENT) { 226 /* ...else check if it matches any vnid's in the lookup table */ 227 if (find_vnid_in_vcache(vol, loc) == B_OK) { 228 /* if it does, create a random one since we can't reuse 229 * existing vnid's */ 230 *vnid = generate_unique_vnid(vol); 231 /* and add it to the vcache */ 232 if ((result = add_to_vcache(vol, *vnid, loc)) < 0) 233 return result; 234 added_to_vcache = true; 235 } else { 236 /* otherwise we are free to use it */ 237 *vnid = loc; 238 } 239 } else if (result != B_OK) { 240 dprintf("get_next_dirent: unknown error (%s)\n", 241 strerror(result)); 242 return result; 243 } 244 245 if (info.mode & FAT_SUBDIR) { 246 if (dlist_find(vol, info.cluster) == -1LL) { 247 if ((result = dlist_add(vol, *vnid)) < 0) { 248 if (added_to_vcache) 249 remove_from_vcache(vol, *vnid); 250 return result; 251 } 252 } 253 } 254 } 255 } 256 257 DPRINTF(2, ("get_next_dirent: found %s (vnid %" B_PRIdINO ")\n", filename, 258 vnid != NULL ? *vnid : (ino_t)0)); 259 260 return B_NO_ERROR; 261} 262 263 264status_t 265check_dir_empty(nspace *vol, vnode *dir) 266{ 267 uint32 i; 268 struct diri iter; 269 status_t result = B_ERROR; 270 271 if (diri_init(vol, dir->cluster, 0, &iter) == NULL) { 272 dprintf("check_dir_empty: error opening directory\n"); 273 return B_ERROR; 274 } 275 276 i = (dir->vnid == vol->root_vnode.vnid) ? 2 : 0; 277 278 for (; i < 3; i++) { 279 char filename[512]; 280 result = _next_dirent_(&iter, NULL, filename, 512); 281 if (result < 0) { 282 if (i == 2 && result == ENOENT) 283 result = B_OK; 284 break; 285 } 286 287 if ((i == 0 && strcmp(filename, ".")) 288 || (i == 1 && strcmp(filename, "..")) 289 // weird case where ./.. are stored as long file names 290 || (i < 2 && iter.current_index != i + 1)) { 291 dprintf("check_dir_empty: malformed directory\n"); 292 result = ENOTDIR; 293 break; 294 } 295 296 result = ENOTEMPTY; 297 } 298 299 return result; 300} 301 302 303status_t 304findfile_case(nspace *vol, vnode *dir, const char *file, ino_t *vnid, 305 vnode **node) 306{ 307 return findfile(vol, dir, file, vnid, node, true, false, NULL); 308} 309 310 311status_t 312findfile_nocase(nspace *vol, vnode *dir, const char *file, ino_t *vnid, 313 vnode **node) 314{ 315 return findfile(vol, dir, file, vnid, node, false, false, NULL); 316} 317 318 319status_t 320findfile_nocase_duplicates(nspace *vol, vnode *dir, const char *file, 321 ino_t *vnid, vnode **node, bool *dups_exist) 322{ 323 return findfile(vol, dir, file, vnid, node, false, true, dups_exist); 324} 325 326 327status_t 328findfile_case_duplicates(nspace *vol, vnode *dir, const char *file, 329 ino_t *vnid, vnode **node, bool *dups_exist) 330{ 331 return findfile(vol, dir, file, vnid, node, true, true, dups_exist); 332} 333 334 335static status_t 336findfile(nspace *vol, vnode *dir, const char *file, ino_t *vnid, 337 vnode **node, bool check_case, bool check_dups, bool *dups_exist) 338{ 339 /* Starting at the base, find the file in the subdir 340 and return its vnode id */ 341 /* The check_case flags determines whether or not the search 342 is done for the exact case or not. If it is not, it will 343 return the first occurance of the match. */ 344 /* The check_dups flag instructs the function to find the 345 first filename match based on the case sensitivity in 346 check_case, but continue searching to see if there are 347 any other case-insensitive matches. If there are, the 348 dups_exist flag is set to true. */ 349 int result = 0; 350 ino_t found_vnid = 0; 351 bool found_file = false; 352 353// dprintf("findfile: %s in %Lx, case %d dups %d\n", file, dir->vnid, check_case, check_dups); 354 355 DPRINTF(1, ("findfile: %s in %" B_PRIdINO "\n", file, dir->vnid)); 356 357 if (dups_exist != NULL) 358 *dups_exist = false; 359 else 360 check_dups = false; 361 362 if (strcmp(file,".") == 0 && dir->vnid == vol->root_vnode.vnid) { 363 found_file = true; 364 found_vnid = dir->vnid; 365 } else if (strcmp(file, "..") == 0 && dir->vnid == vol->root_vnode.vnid) { 366 found_file = true; 367 found_vnid = dir->dir_vnid; 368 } else { 369 struct diri diri; 370 371 // XXX: do it in a smarter way 372 if (diri_init(vol, dir->cluster, 0, &diri) == NULL) { 373 dprintf("findfile: error opening directory\n"); 374 return ENOENT; 375 } 376 377 while (1) { 378 char filename[512]; 379 ino_t _vnid; 380 381 result = get_next_dirent(vol, dir, &diri, &_vnid, filename, 512); 382 if (result != B_NO_ERROR) 383 break; 384 385 if (check_case) { 386 if (!found_file && !strcmp(filename, file)) { 387 found_file = true; 388 found_vnid = _vnid; 389 } else if (check_dups && !strcasecmp(filename, file)) { 390 *dups_exist = true; 391 } 392 } else { 393 if (!strcasecmp(filename, file)) { 394 if (check_dups && found_file) 395 *dups_exist = true; 396 397 found_file = true; 398 found_vnid = _vnid; 399 } 400 } 401 402 if (found_file && (!check_dups || (check_dups && *dups_exist))) 403 break; 404 } 405 } 406 if (found_file) { 407 if (vnid) 408 *vnid = found_vnid; 409 if (node) 410 result = get_vnode(vol->volume, found_vnid, (void **)node); 411 result = B_OK; 412 } else { 413 result = ENOENT; 414 } 415#if 0 416 dprintf("findfile: returning %d", result); 417 if(dups_exist) 418 dprintf(" dups_exist %d\n", *dups_exist); 419 else 420 dprintf("\n"); 421#endif 422 return result; 423} 424 425 426status_t 427erase_dir_entry(nspace *vol, vnode *node) 428{ 429 status_t result; 430 uint32 i; 431 char filename[512]; 432 uint8 *buffer; 433 struct _dirent_info_ info; 434 struct diri diri; 435 436 DPRINTF(0, ("erasing directory entries %" B_PRIu32 " through %" B_PRIu32 437 "\n", node->sindex, node->eindex)); 438 buffer = diri_init(vol,VNODE_PARENT_DIR_CLUSTER(node), node->sindex, &diri); 439 440 // first pass: check if the entry is still valid 441 if (buffer == NULL) { 442 dprintf("erase_dir_entry: error reading directory\n"); 443 return ENOENT; 444 } 445 446 result = _next_dirent_(&diri, &info, filename, 512); 447 448 if (result < 0) 449 return result; 450 451 if (info.sindex != node->sindex || info.eindex != node->eindex) { 452 // any other attributes may be in a state of flux due to wstat calls 453 dprintf("erase_dir_entry: directory entry doesn't match\n"); 454 return B_ERROR; 455 } 456 457 // second pass: actually erase the entry 458 buffer = diri_init(vol, VNODE_PARENT_DIR_CLUSTER(node), node->sindex, &diri); 459 for (i = node->sindex; i <= node->eindex && buffer; 460 buffer = diri_next_entry(&diri), i++) { 461 diri_make_writable(&diri); 462 buffer[0] = 0xe5; // mark entry erased 463 } 464 465 return 0; 466} 467 468 469/*! shrink directory to the size needed 470 errors here are neither likely nor problematic 471 w95 doesn't seem to do this, so it's possible to create a 472 really large directory that consumes all available space! 473*/ 474status_t 475compact_directory(nspace *vol, vnode *dir) 476{ 477 uint32 last = 0; 478 struct diri diri; 479 status_t error = B_ERROR; /* quiet warning */ 480 481 DPRINTF(0, ("compacting directory with vnode id %" B_PRIdINO "\n", 482 dir->vnid)); 483 484 // root directory can't shrink in fat12 and fat16 485 if (IS_FIXED_ROOT(dir->cluster)) 486 return 0; 487 488 if (diri_init(vol, dir->cluster, 0, &diri) == NULL) { 489 dprintf("compact_directory: cannot open dir at cluster (%" B_PRIu32 490 ")\n", dir->cluster); 491 return EIO; 492 } 493 while (diri.current_block) { 494 char filename[512]; 495 struct _dirent_info_ info; 496 497 error = _next_dirent_(&diri, &info, filename, 512); 498 499 if (error == B_OK) { 500 // don't compact away volume labels in the root dir 501 if (!(info.mode & FAT_VOLUME) || (dir->vnid != vol->root_vnode.vnid)) 502 last = diri.current_index; 503 } else if (error == ENOENT) { 504 uint32 clusters = (last + vol->bytes_per_sector / 0x20 505 * vol->sectors_per_cluster - 1) / (vol->bytes_per_sector / 0x20) 506 / vol->sectors_per_cluster; 507 error = 0; 508 509 // special case for fat32 root directory; we don't want 510 // it to disappear 511 if (clusters == 0) 512 clusters = 1; 513 514 if (clusters * vol->bytes_per_sector * vol->sectors_per_cluster 515 < dir->st_size) { 516 DPRINTF(0, ("shrinking directory to %" B_PRIu32 " clusters\n", 517 clusters)); 518 error = set_fat_chain_length(vol, dir, clusters, true); 519 dir->st_size = clusters * vol->bytes_per_sector 520 * vol->sectors_per_cluster; 521 dir->iteration++; 522 } 523 break; 524 } else { 525 dprintf("compact_directory: unknown error from _next_dirent_ (%s)\n", 526 strerror(error)); 527 break; 528 } 529 } 530 531 return error; 532} 533 534 535//! name is array of char[11] as returned by findfile 536static status_t 537find_short_name(nspace *vol, vnode *dir, const uchar *name) 538{ 539 struct diri diri; 540 uint8 *buffer; 541 status_t result = ENOENT; 542 543 buffer = diri_init(vol, dir->cluster, 0, &diri); 544 while (buffer) { 545 if (buffer[0] == 0) 546 break; 547 548 if (buffer[0xb] != 0xf) { // not long file name 549 if (!memcmp(name, buffer, 11)) { 550 result = B_OK; 551 break; 552 } 553 } 554 555 buffer = diri_next_entry(&diri); 556 } 557 558 return result; 559} 560 561 562struct _entry_info_ { 563 uint32 mode; 564 uint32 cluster; 565 uint32 size; 566 time_t time; 567 time_t creation_time; 568}; 569 570 571static status_t 572_create_dir_entry_(nspace *vol, vnode *dir, struct _entry_info_ *info, 573 const char nshort[11], const char *nlong, uint32 len, uint32 *ns, 574 uint32 *ne) 575{ 576 status_t error = B_ERROR; /* quiet warning */ 577 uint32 required_entries, i; 578 uint8 *buffer, hash; 579 bool last_entry; 580 struct diri diri; 581 582 // short name cannot be the same as that of a device 583 // this list was created by running strings on io.sys 584 const char *device_names[] = { 585 "CON ", 586 "AUX ", 587 "PRN ", 588 "CLOCK$ ", 589 "COM1 ", 590 "LPT1 ", 591 "LPT2 ", 592 "LPT3 ", 593 "COM2 ", 594 "COM3 ", 595 "COM4 ", 596 "CONFIG$ ", 597 NULL 598 }; 599 600 // check short name against device names 601 for (i = 0; device_names[i]; i++) { 602 // only first 8 characters seem to matter 603 if (!memcmp(nshort, device_names[i], 8)) 604 return EPERM; 605 } 606 607 if (info->cluster != 0 && !IS_DATA_CLUSTER(info->cluster)) { 608 dprintf("_create_dir_entry_ for bad cluster (%" B_PRIu32 ")\n", 609 info->cluster); 610 return EINVAL; 611 } 612 613 /* convert byte length of unicode name to directory entries */ 614 required_entries = (len + 24) / 26 + 1; 615 616 // find a place to put the entries 617 *ns = 0; 618 last_entry = true; 619 if (diri_init(vol, dir->cluster, 0, &diri) == NULL) { 620 dprintf("_create_dir_entry_: cannot open dir at cluster (%" B_PRIu32 621 ")\n", dir->cluster); 622 return EIO; 623 } 624 625 while (diri.current_block) { 626 char filename[512]; 627 struct _dirent_info_ info; 628 error = _next_dirent_(&diri, &info, filename, 512); 629 if (error == B_OK) { 630 if (info.sindex - *ns >= required_entries) { 631 last_entry = false; 632 break; 633 } 634 *ns = diri.current_index; 635 } else if (error == ENOENT) { 636 // hit end of directory marker 637 break; 638 } else { 639 dprintf("_create_dir_entry_: unknown error from _next_dirent_ (%s)\n", 640 strerror(error)); 641 break; 642 } 643 } 644 645 // if at end of directory, last_entry flag will be true as it should be 646 647 if (error != B_OK && error != ENOENT) 648 return error; 649 650 *ne = *ns + required_entries - 1; 651 652 for (i = *ns; i <= *ne; i++) { 653 ASSERT(find_loc_in_vcache(vol, 654 GENERATE_DIR_INDEX_VNID(dir->cluster, i)) == ENOENT); 655 } 656 657 DPRINTF(0, ("directory entry runs from %" B_PRIu32 " to %" B_PRIu32 658 " (dirsize = %" B_PRIdOFF ") (is%s last entry)\n", *ns, *ne, 659 dir->st_size, last_entry ? "" : "n't")); 660 661 // check if the directory needs to be expanded 662 if (*ne * 0x20 >= dir->st_size) { 663 uint32 clusters_needed; 664 665 // can't expand fat12 and fat16 root directories :( 666 if (IS_FIXED_ROOT(dir->cluster)) { 667 DPRINTF(0, ("_create_dir_entry_: out of space in root directory\n")); 668 return ENOSPC; 669 } 670 671 // otherwise grow directory to fit 672 clusters_needed = ((*ne + 1) * 0x20 + 673 vol->bytes_per_sector*vol->sectors_per_cluster - 1) / 674 vol->bytes_per_sector / vol->sectors_per_cluster; 675 676 DPRINTF(0, ("expanding directory from %" B_PRIdOFF " to %" B_PRIu32 677 " clusters\n", dir->st_size / vol->bytes_per_sector 678 / vol->sectors_per_cluster, clusters_needed)); 679 if ((error = set_fat_chain_length(vol, dir, clusters_needed, false)) 680 < 0) { 681 return error; 682 } 683 684 dir->st_size = vol->bytes_per_sector*vol->sectors_per_cluster*clusters_needed; 685 dir->iteration++; 686 } 687 688 // starting blitting entries 689 buffer = diri_init(vol,dir->cluster, *ns, &diri); 690 if (buffer == NULL) { 691 dprintf("_create_dir_entry_: cannot open dir at (%" B_PRIu32 ", %" 692 B_PRIu32 ")\n", dir->cluster, *ns); 693 return EIO; 694 } 695 hash = hash_msdos_name(nshort); 696 697 // write lfn entries 698 for (i = 1; i < required_entries && buffer; i++) { 699 const char *p = nlong + (required_entries - i - 1) * 26; 700 // go to unicode offset 701 diri_make_writable(&diri); 702 memset(buffer, 0, 0x20); 703 buffer[0] = required_entries - i + ((i == 1) ? 0x40 : 0); 704 buffer[0x0b] = 0x0f; 705 buffer[0x0d] = hash; 706 memcpy(buffer+1,p,10); 707 memcpy(buffer+0x0e,p+10,12); 708 memcpy(buffer+0x1c,p+22,4); 709 buffer = diri_next_entry(&diri); 710 } 711 712 ASSERT(buffer != NULL); 713 if (buffer == NULL) { // this should never happen... 714 DPRINTF(0, ("_create_dir_entry_: the unthinkable has occured\n")); 715 return B_ERROR; 716 } 717 718 // write directory entry 719 diri_make_writable(&diri); 720 memcpy(buffer, nshort, 11); 721 buffer[0x0b] = info->mode; 722 memset(buffer+0xc, 0, 0x16-0xc); 723 i = time_t2dos(info->creation_time); 724 buffer[0x0e] = i & 0xff; 725 buffer[0x0f] = (i >> 8) & 0xff; 726 buffer[0x10] = (i >> 16) & 0xff; 727 buffer[0x11] = (i >> 24) & 0xff; 728 i = time_t2dos(info->time); 729 buffer[0x16] = i & 0xff; 730 buffer[0x17] = (i >> 8) & 0xff; 731 buffer[0x18] = (i >> 16) & 0xff; 732 buffer[0x19] = (i >> 24) & 0xff; 733 i = info->cluster; 734 if (info->size == 0) i = 0; // cluster = 0 for 0 byte files 735 buffer[0x1a] = i & 0xff; 736 buffer[0x1b] = (i >> 8) & 0xff; 737 if (vol->fat_bits == 32) { 738 buffer[0x14] = (i >> 16) & 0xff; 739 buffer[0x15] = (i >> 24) & 0xff; 740 } 741 i = (info->mode & FAT_SUBDIR) ? 0 : info->size; 742 buffer[0x1c] = i & 0xff; 743 buffer[0x1d] = (i >> 8) & 0xff; 744 buffer[0x1e] = (i >> 16) & 0xff; 745 buffer[0x1f] = (i >> 24) & 0xff; 746 747 if (last_entry) { 748 // add end of directory markers to the rest of the 749 // cluster; need to clear all the other entries or else 750 // scandisk will complain. 751 while ((buffer = diri_next_entry(&diri)) != NULL) { 752 diri_make_writable(&diri); 753 memset(buffer, 0, 0x20); 754 } 755 } 756 757 return 0; 758} 759 760 761//! doesn't do any name checking 762status_t 763create_volume_label(nspace *vol, const char name[11], uint32 *index) 764{ 765 status_t err; 766 uint32 dummy; 767 struct _entry_info_ info = { 768 FAT_ARCHIVE | FAT_VOLUME, 0, 0, 0 769 }; 770 time(&info.time); 771 772 // check if name already exists 773 err = find_short_name(vol, &(vol->root_vnode), (uchar *)name); 774 if (err == B_OK) 775 return EEXIST; 776 if (err != ENOENT) 777 return err; 778 779 return _create_dir_entry_(vol, &(vol->root_vnode), &info, name, NULL, 780 0, index, &dummy); 781} 782 783 784bool 785is_filename_legal(const char *name) 786{ 787 unsigned int i; 788 unsigned int len = strlen(name); 789 790 if (len <= 0) 791 return false; 792 793 // names ending with a dot are not allowed 794 if (name[len - 1] == '.') 795 return false; 796 // names ending with a space are not allowed 797 if (name[len - 1] == ' ') 798 return false; 799 800 // XXX illegal character search can be made faster 801 for (i = 0; i < len; i++) { 802 if (name[i] & 0x80) 803 continue; //belongs to an utf8 char 804 if (strchr(sIllegal, name[i])) 805 return false; 806 if ((unsigned char)name[i] < 32) 807 return false; 808 } 809 return true; 810} 811 812 813status_t 814create_dir_entry(nspace *vol, vnode *dir, vnode *node, const char *name, 815 uint32 *ns, uint32 *ne) 816{ 817 status_t error; 818 int32 len; 819 unsigned char nlong[512], nshort[11]; 820 int encoding; 821 struct _entry_info_ info; 822 823 // check name legality before doing anything 824 if (!is_filename_legal(name)) 825 return EINVAL; 826 827 // check if name already exists 828 error = findfile_nocase(vol, dir, name, NULL, NULL); 829 if (error == B_OK) { 830 DPRINTF(0, ("%s already found in directory %" B_PRIdINO "\n", name, 831 dir->vnid)); 832 return EEXIST; 833 } 834 if (error != ENOENT) 835 return error; 836 837 // check name legality while converting. we ignore the case conversion 838 // flag, i.e. (filename "blah" will always have a patched short name), 839 // because the whole case conversion system in dos is brain damaged; 840 // remanants of CP/M no less. 841 842 // existing names pose a problem; in these cases, we'll just live with 843 // two identical short names. not a great solution, but there's little 844 // we can do about it. 845 len = utf8_to_unicode(name, nlong, 512); 846 if (len <= 0) { 847 DPRINTF(0, ("Error converting utf8 name '%s' to unicode\n", name)); 848 return len ? len : B_ERROR; 849 } 850 memset(nlong + len, 0xff, 512 - len); /* pad with 0xff */ 851 852 error = generate_short_name((uchar *)name, nlong, len, nshort, &encoding); 853 if (error) { 854 DPRINTF(0, ("Error generating short name for '%s'\n", name)); 855 return error; 856 } 857 858 // if there is a long name, patch short name if necessary and check for duplication 859 if (requires_long_name(name, nlong)) { 860 char tshort[11]; // temporary short name 861 int iter = 1; 862 863 memcpy(tshort, nshort, 11); 864 865 if (requires_munged_short_name((uchar *)name, nshort, encoding)) 866 error = B_OK; 867 else 868 error = find_short_name(vol, dir, nshort); 869 870 if (error == B_OK) { 871 do { 872 memcpy(nshort, tshort, 11); 873 DPRINTF(0, ("trying short name %11.11s\n", nshort)); 874 munge_short_name1(nshort, iter, encoding); 875 } while ((error = find_short_name(vol, dir, nshort)) == B_OK && ++iter < 10); 876 } 877 878 if (error != B_OK && error != ENOENT) 879 return error; 880 881 if (error == B_OK) { 882 // XXX: possible infinite loop here 883 do { 884 memcpy(nshort, tshort, 11); 885 DPRINTF(0, ("trying short name %11.11s\n", nshort)); 886 munge_short_name2(nshort, encoding); 887 } while ((error = find_short_name(vol, dir, nshort)) == B_OK); 888 889 if (error != ENOENT) 890 return error; 891 } 892 } else { 893 len = 0; /* entry doesn't need a long name */ 894 } 895 896 DPRINTF(0, ("creating directory entry (%11.11s)\n", nshort)); 897 898 info.mode = node->mode; 899 if ((node->mode & FAT_SUBDIR) == 0) 900 info.mode |= FAT_ARCHIVE; 901 info.cluster = node->cluster; 902 info.size = node->st_size; 903 info.time = node->st_time; 904 info.creation_time = node->st_crtim; 905 906 return _create_dir_entry_(vol, dir, &info, (char *)nshort, 907 (char *)nlong, len, ns, ne); 908} 909 910 911status_t 912dosfs_read_vnode(fs_volume *_vol, ino_t vnid, fs_vnode *_node, int *_type, 913 uint32 *_flags, bool reenter) 914{ 915 nspace *vol = (nspace *)_vol->private_volume; 916 int result = B_NO_ERROR; 917 ino_t loc, dir_vnid; 918 vnode *entry; 919 struct _dirent_info_ info; 920 struct diri iter; 921 char filename[512]; /* need this for setting mime type */ 922 923 RecursiveLocker lock(vol->vlock); 924 925 _node->private_node = NULL; 926 _node->ops = &gFATVnodeOps; 927 *_flags = 0; 928 929 DPRINTF(0, ("dosfs_read_vnode (vnode id %" B_PRIdINO ")\n", vnid)); 930 931 if (vnid == vol->root_vnode.vnid) { 932 dprintf("??? dosfs_read_vnode called on root node ???\n"); 933 _node->private_node = (void *)&(vol->root_vnode); 934 *_type = make_mode(vol, &vol->root_vnode); 935 return result; 936 } 937 938 if (vcache_vnid_to_loc(vol, vnid, &loc) != B_OK) 939 loc = vnid; 940 941 if (IS_ARTIFICIAL_VNID(loc) || IS_INVALID_VNID(loc)) { 942 DPRINTF(0, ("dosfs_read_vnode: unknown vnid %" B_PRIdINO " (loc %" 943 B_PRIdINO ")\n", vnid, loc)); 944 return ENOENT; 945 } 946 947 if ((dir_vnid = dlist_find(vol, DIR_OF_VNID(loc))) == -1LL) { 948 DPRINTF(0, ("dosfs_read_vnode: unknown directory at cluster %" B_PRIu32 949 "\n", DIR_OF_VNID(loc))); 950 return ENOENT; 951 } 952 953 if (diri_init(vol, DIR_OF_VNID(loc), 954 IS_DIR_CLUSTER_VNID(loc) ? 0 : INDEX_OF_DIR_INDEX_VNID(loc), 955 &iter) == NULL) { 956 dprintf("dosfs_read_vnode: error initializing directory for vnid %" 957 B_PRIdINO " (loc %" B_PRIdINO ")\n", vnid, loc); 958 return ENOENT; 959 } 960 961 while (1) { 962 result = _next_dirent_(&iter, &info, filename, 512); 963 if (result < 0) { 964 dprintf("dosfs_read_vnode: error finding vnid %" B_PRIdINO 965 " (loc %" B_PRIdINO ") (%s)\n", vnid, loc, strerror(result)); 966 return result; 967 } 968 969 if (IS_DIR_CLUSTER_VNID(loc)) { 970 if (info.cluster == CLUSTER_OF_DIR_CLUSTER_VNID(loc)) 971 break; 972 } else { 973 if (info.sindex == INDEX_OF_DIR_INDEX_VNID(loc)) 974 break; 975 dprintf("dosfs_read_vnode: error finding vnid %" B_PRIdINO 976 " (loc %" B_PRIdINO ") (%s)\n", vnid, loc, strerror(result)); 977 return ENOENT; 978 } 979 } 980 981 if ((entry = (vnode *)calloc(sizeof(struct vnode), 1)) == NULL) { 982 DPRINTF(0, ("dosfs_read_vnode: out of memory\n")); 983 return ENOMEM; 984 } 985 986 entry->vnid = vnid; 987 entry->dir_vnid = dir_vnid; 988 entry->disk_image = 0; 989 if (vol->respect_disk_image) { 990 if ((dir_vnid == vol->root_vnode.vnid) && !strcmp(filename, "BEOS")) { 991 vol->beos_vnid = vnid; 992 entry->disk_image = 1; 993 } 994 if ((dir_vnid == vol->beos_vnid) && !strcmp(filename, "IMAGE.BE")) { 995 entry->disk_image = 2; 996 } 997 } 998 entry->iteration = 0; 999 entry->sindex = info.sindex; 1000 entry->eindex = info.eindex; 1001 entry->cluster = info.cluster; 1002 entry->mode = info.mode; 1003 entry->st_size = info.size; 1004 entry->dirty = false; 1005 if (info.mode & FAT_SUBDIR) { 1006 entry->st_size = count_clusters(vol,entry->cluster) 1007 * vol->sectors_per_cluster * vol->bytes_per_sector; 1008 } 1009 if (entry->cluster) { 1010 entry->end_cluster = get_nth_fat_entry(vol, info.cluster, 1011 (entry->st_size + vol->bytes_per_sector * vol->sectors_per_cluster - 1) / 1012 vol->bytes_per_sector / vol->sectors_per_cluster - 1); 1013 } else 1014 entry->end_cluster = 0; 1015 entry->st_time = dos2time_t(info.time); 1016 entry->st_crtim = dos2time_t(info.creation_time); 1017 1018 entry->filename = (char *)malloc(sizeof(filename) + 1); 1019 if (entry->filename) strcpy(entry->filename, filename); 1020 1021 entry->cache = file_cache_create(vol->id, vnid, entry->st_size); 1022 entry->file_map = file_map_create(vol->id, vnid, entry->st_size); 1023 if (!(entry->mode & FAT_SUBDIR)) 1024 set_mime_type(entry, filename); 1025 1026 _node->private_node = entry; 1027 *_type = make_mode(vol, entry); 1028 1029 return B_OK; 1030} 1031 1032 1033status_t 1034dosfs_walk(fs_volume *_vol, fs_vnode *_dir, const char *file, ino_t *_vnid) 1035{ 1036 /* Starting at the base, find file in the subdir, and return path 1037 string and vnode id of file. */ 1038 nspace *vol = (nspace *)_vol->private_volume; 1039 vnode *dir = (vnode *)_dir->private_node; 1040 vnode *vnode = NULL; 1041 status_t result = ENOENT; 1042 1043 RecursiveLocker lock(vol->vlock); 1044 1045 DPRINTF(0, ("dosfs_walk: find %" B_PRIdINO "/%s\n", dir->vnid, file)); 1046 1047 result = findfile_case(vol, dir, file, _vnid, &vnode); 1048 if (result != B_OK) { 1049 DPRINTF(0, ("dosfs_walk (%s)\n", strerror(result))); 1050 } else { 1051 DPRINTF(0, ("dosfs_walk: found vnid %" B_PRIdINO "\n", *_vnid)); 1052 } 1053 1054 return result; 1055} 1056 1057 1058status_t 1059dosfs_access(fs_volume *_vol, fs_vnode *_node, int mode) 1060{ 1061 status_t result = B_OK; 1062 nspace *vol = (nspace *)_vol->private_volume; 1063 vnode *node = (vnode *)_node->private_node; 1064 1065 RecursiveLocker lock(vol->vlock); 1066 1067 DPRINTF(0, ("dosfs_access (vnode id %" B_PRIdINO ", mode %o)\n", node->vnid, 1068 mode)); 1069 1070 if (mode & W_OK) { 1071 if (vol->flags & B_FS_IS_READONLY) { 1072 DPRINTF(0, ("dosfs_access: can't write on read-only volume\n")); 1073 result = EROFS; 1074 } else if (node->mode & FAT_READ_ONLY) { 1075 DPRINTF(0, ("can't open read-only file for writing\n")); 1076 result = EPERM; 1077 } else if (node->disk_image != 0) { 1078 DPRINTF(0, ("can't open disk image file for writing\n")); 1079 result = EPERM; 1080 } 1081 } 1082 1083 return result; 1084} 1085 1086 1087status_t 1088dosfs_opendir(fs_volume *_vol, fs_vnode *_node, void **_cookie) 1089{ 1090 nspace *vol = (nspace *)_vol->private_volume; 1091 vnode *node = (vnode *)_node->private_node; 1092 1093 RecursiveLocker lock(vol->vlock); 1094 1095 DPRINTF(0, ("dosfs_opendir (vnode id %" B_PRIdINO ")\n", node->vnid)); 1096 1097 *_cookie = NULL; 1098 1099 if (!(node->mode & FAT_SUBDIR)) { 1100 /* bash will try to opendir files unless OPENDIR_NOT_ROBUST is 1101 * defined, so we'll suppress this message; it's more of a problem 1102 * with the application than with the file system, anyway 1103 */ 1104 DPRINTF(0, ("dosfs_opendir error: vnode not a directory\n")); 1105 return ENOTDIR; 1106 } 1107 1108 dircookie *cookie = (dircookie *)malloc(sizeof(dircookie)); 1109 if (cookie == NULL) { 1110 DPRINTF(0, ("dosfs_opendir: out of memory error\n")); 1111 return ENOMEM; 1112 } 1113 1114 cookie->current_index = 0; 1115 *_cookie = (void *)cookie; 1116 return B_OK; 1117} 1118 1119 1120status_t 1121dosfs_readdir(fs_volume *_vol, fs_vnode *_dir, void *_cookie, 1122 struct dirent *entry, size_t bufsize, uint32 *num) 1123{ 1124 int result = ENOENT; 1125 nspace* vol = (nspace *)_vol->private_volume; 1126 vnode *dir = (vnode *)_dir->private_node; 1127 dircookie* cookie = (dircookie *)_cookie; 1128 struct diri diri; 1129 1130 RecursiveLocker lock(vol->vlock); 1131 1132 DPRINTF(0, ("dosfs_readdir: vnode id %" B_PRIdINO ", index %" B_PRIu32 "\n", 1133 dir->vnid, cookie->current_index)); 1134 1135 // simulate '.' and '..' entries for root directory 1136 if (dir->vnid == vol->root_vnode.vnid) { 1137 if (cookie->current_index >= 2) { 1138 cookie->current_index -= 2; 1139 } else { 1140 if (cookie->current_index++ == 0) { 1141 strcpy(entry->d_name, "."); 1142 entry->d_reclen = offsetof(struct dirent, d_name) + 2; 1143 } else { 1144 strcpy(entry->d_name, ".."); 1145 entry->d_reclen = offsetof(struct dirent, d_name) + 3; 1146 } 1147 *num = 1; 1148 entry->d_ino = vol->root_vnode.vnid; 1149 entry->d_dev = vol->id; 1150 return B_NO_ERROR; 1151 } 1152 } 1153 1154 if (diri_init(vol, dir->cluster, cookie->current_index, &diri) == NULL) { 1155 DPRINTF(0, ("dosfs_readdir: no more entries!\n")); 1156 // When you get to the end, don't return an error, just return 0 1157 // in *num. 1158 *num = 0; 1159 return B_NO_ERROR; 1160 } 1161 1162 result = get_next_dirent(vol, dir, &diri, &entry->d_ino, entry->d_name, 1163 bufsize - offsetof(struct dirent, d_name) - 1); 1164 1165 cookie->current_index = diri.current_index; 1166 1167 if (dir->vnid == vol->root_vnode.vnid) 1168 cookie->current_index += 2; 1169 1170 if (result == B_NO_ERROR) { 1171 *num = 1; 1172 entry->d_dev = vol->id; 1173 entry->d_reclen = offsetof(struct dirent, d_name) + strlen(entry->d_name) + 1; 1174 DPRINTF(0, ("dosfs_readdir: found file %s\n", entry->d_name)); 1175 } else if (result == ENOENT) { 1176 // When you get to the end, don't return an error, just return 0 1177 // in *num. 1178 *num = 0; 1179 return B_OK; 1180 } else { 1181 dprintf("dosfs_readdir: error returned by get_next_dirent (%s)\n", 1182 strerror(result)); 1183 } 1184 1185 return B_OK; 1186} 1187 1188 1189status_t 1190dosfs_rewinddir(fs_volume *_vol, fs_vnode *_node, void* _cookie) 1191{ 1192 nspace *vol = (nspace *)_vol->private_volume; 1193 vnode *node = (vnode *)_node->private_node; 1194 dircookie *cookie = (dircookie *)_cookie; 1195 1196 RecursiveLocker lock(vol->vlock); 1197 1198 DPRINTF(0, ("dosfs_rewinddir (vnode id %" B_PRIdINO ")\n", node->vnid)); 1199 1200 cookie->current_index = 0; 1201 1202 return B_OK; 1203} 1204 1205 1206status_t 1207dosfs_closedir(fs_volume *_vol, fs_vnode *_node, void *_cookie) 1208{ 1209 DPRINTF(0, ("dosfs_closedir called\n")); 1210 1211 return B_OK; 1212} 1213 1214 1215status_t 1216dosfs_free_dircookie(fs_volume *_vol, fs_vnode *_node, void *_cookie) 1217{ 1218 nspace *vol = (nspace *)_vol->private_volume; 1219 vnode *node = (vnode *)_node->private_node; 1220 dircookie *cookie = (dircookie *)_cookie; 1221 1222 RecursiveLocker lock(vol->vlock); 1223 1224 DPRINTF(0, ("dosfs_free_dircookie (vnode id %" B_PRIdINO ")\n", 1225 node->vnid)); 1226 1227 free(cookie); 1228 1229 return 0; 1230} 1231