1/* 2 * Copyright (c) 2000-2004 Anton Altaparmakov 3 * Copyright (c) 2002-2006 Szabolcs Szakacsits 4 * 5 * Copyright (c) 2006-2007 Troeglazov Gerasim (3dEyes**) 6 * 7 * This program/include file is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or (at your 10 * option) any later version. 11 * 12 * This program/include file is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 15 * Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * this program (in the main directory of the Linux-NTFS distribution in the 19 * file COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple 20 * Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23 24#include "fs_func.h" 25 26#include <ctype.h> 27#include <dirent.h> 28#include <errno.h> 29#include <fcntl.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33#include <sys/stat.h> 34#include <time.h> 35#include <unistd.h> 36 37#include <driver_settings.h> 38#include <KernelExport.h> 39#include <disk_device_manager.h> 40 41#include "attributes.h" 42#include "fake_attributes.h" 43#include "lock.h" 44#include "ntfs.h" 45#include "volume_util.h" 46 47extern int mkntfs_main(const char *devpath, const char *label); 48 49typedef struct identify_cookie { 50 NTFS_BOOT_SECTOR boot; 51 char label[MAX_PATH]; 52} identify_cookie; 53 54 55static bool 56is_device_read_only(const char *device) 57{ 58 bool isReadOnly = false; 59 device_geometry geometry; 60 int fd = open(device, O_RDONLY | O_NOCACHE); 61 if (fd < 0) 62 return false; 63 64 if (ioctl(fd, B_GET_GEOMETRY, &geometry) == 0) 65 isReadOnly = geometry.read_only; 66 67 close(fd); 68 return isReadOnly; 69} 70 71 72static status_t 73get_node_type(ntfs_inode* ni, int* _type) 74{ 75 ntfs_attr* na; 76 77 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { 78 // Directory 79 *_type = S_IFDIR; 80 return B_OK; 81 } else { 82 // Regular or Interix (INTX) file 83 *_type = S_IFREG; 84 85 if (ni->flags & FILE_ATTR_SYSTEM) { 86 na = ntfs_attr_open(ni, AT_DATA, NULL, 0); 87 if (!na) 88 return ENOENT; 89 90 // Check whether it's Interix symbolic link 91 if (na->data_size <= sizeof(INTX_FILE_TYPES) + 92 sizeof(ntfschar) * PATH_MAX && 93 na->data_size > sizeof(INTX_FILE_TYPES)) { 94 INTX_FILE *intx_file; 95 96 intx_file = ntfs_malloc(na->data_size); 97 if (!intx_file) { 98 ntfs_attr_close(na); 99 return EINVAL; 100 } 101 if (ntfs_attr_pread(na, 0, na->data_size, 102 intx_file) != na->data_size) { 103 free(intx_file); 104 ntfs_attr_close(na); 105 return EINVAL; 106 } 107 if (intx_file->magic == INTX_SYMBOLIC_LINK) 108 *_type = FS_SLNK_MODE; 109 free(intx_file); 110 } 111 ntfs_attr_close(na); 112 } 113 } 114 115 return B_OK; 116} 117 118 119static u64 120ntfs_inode_lookup(fs_volume *_vol, ino_t parent, const char *name) 121{ 122 nspace *ns = (nspace*)_vol->private_volume; 123 ntfschar *uname = NULL; 124 int uname_len; 125 u64 ino = (u64)-1; 126 u64 inum; 127 ntfs_inode *dir_ni; 128 129 /* Open target directory. */ 130 dir_ni = ntfs_inode_open(ns->ntvol, parent); 131 if (dir_ni) { 132 uname_len = ntfs_mbstoucs(name, &uname); 133 if (uname_len < 0) { 134 errno = EINVAL; 135 return (ino); 136 } 137 /* Lookup file */ 138 inum = ntfs_inode_lookup_by_name(dir_ni, uname, uname_len); 139 /* never return inodes 0 and 1 */ 140 if (MREF(inum) <= 1) { 141 inum = (u64)-1; 142 errno = ENOENT; 143 } 144 if (ntfs_inode_close(dir_ni) 145 || (inum == (u64)-1)) 146 ino = (u64)-1; 147 else 148 ino = MREF(inum); 149 } 150 if (uname != NULL) 151 free(uname); 152 return (ino); 153} 154 155 156static int 157ntfs_remove(fs_volume *_vol, ino_t parent, const char *name) 158{ 159 nspace *ns = (nspace*)_vol->private_volume; 160 161 ntfschar *uname = NULL; 162 ntfs_inode *ni = NULL; 163 ntfs_inode *dir_ni = NULL; 164 int result = B_OK; 165 int uname_len; 166 u64 iref; 167 168 /* Open parent directory. */ 169 dir_ni = ntfs_inode_open(ns->ntvol, parent); 170 if (!dir_ni) { 171 result = EINVAL; 172 goto exit; 173 } 174 /* Generate unicode filename. */ 175 uname_len = ntfs_mbstoucs(name, &uname); 176 if (uname_len < 0) { 177 result = EINVAL; 178 goto exit; 179 } 180 /* Open object for delete. */ 181 iref = ntfs_inode_lookup_by_name(dir_ni, uname, uname_len); 182 if (iref == (u64)-1) { 183 result = EINVAL; 184 goto exit; 185 } 186 /* deny unlinking metadata files */ 187 if (MREF(iref) < FILE_first_user) { 188 result = EINVAL; 189 goto exit; 190 } 191 192 ni = ntfs_inode_open(ns->ntvol, MREF(iref)); 193 if (!ni) { 194 result = EINVAL; 195 goto exit; 196 } 197 198 if (ntfs_delete(ns->ntvol, (char*)NULL, ni, dir_ni, uname, uname_len)) 199 result = EINVAL; 200 /* ntfs_delete() always closes ni and dir_ni */ 201 ni = dir_ni = NULL; 202exit: 203 if (ni != NULL) 204 ntfs_inode_close(ni); 205 if (dir_ni != NULL) 206 ntfs_inode_close(dir_ni); 207 208 free(uname); 209 return result; 210} 211 212 213void 214fs_ntfs_update_times(fs_volume *vol, ntfs_inode *ni, 215 ntfs_time_update_flags mask) 216{ 217 nspace *ns = (nspace*)vol->private_volume; 218 219 if (ns->noatime) 220 mask &= ~NTFS_UPDATE_ATIME; 221 222 ntfs_inode_update_times(ni, mask); 223} 224 225 226float 227fs_identify_partition(int fd, partition_data *partition, void **_cookie) 228{ 229 NTFS_BOOT_SECTOR boot; 230 char devpath[MAX_PATH]; 231 identify_cookie *cookie = NULL; 232 ntfs_volume *ntVolume; 233 uint8 *buf = (uint8*)&boot; 234 235 // read in the boot sector 236 TRACE("fs_identify_partition: read in the boot sector\n"); 237 if (read_pos(fd, 0, (void*)&boot, 512) != 512) { 238 ERROR("fs_identify_partition: couldn't read boot sector\n"); 239 return -1; 240 } 241 242 // check boot signature 243 if ((buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) && buf[0x15] == 0xf8) { 244 ERROR("fs_identify_partition: boot signature doesn't match\n"); 245 return -1; 246 } 247 248 // check boot signature NTFS 249 if (memcmp(buf + 3, "NTFS ", 8) != 0) { 250 ERROR("fs_identify_partition: boot signature NTFS doesn't match\n"); 251 return -1; 252 } 253 254 // allocate identify_cookie 255 cookie = (identify_cookie *)malloc(sizeof(identify_cookie)); 256 if (!cookie) { 257 ERROR("fs_identify_partition: cookie allocation failed\n"); 258 return -1; 259 } 260 261 cookie->label[0]='\0'; 262 memcpy(&cookie->boot, &boot, 512); 263 264 // get path for device 265 if (ioctl(fd, B_GET_PATH_FOR_DEVICE, devpath) != 0) { 266 // try mount 267 ntVolume = utils_mount_volume(devpath, MS_RDONLY | MS_RECOVER); 268 if (ntVolume != NULL) { 269 if (ntVolume->vol_name && ntVolume->vol_name[0] != '\0') 270 strcpy(cookie->label, ntVolume->vol_name); 271 ntfs_umount(ntVolume, true); 272 } 273 } 274 275 *_cookie = cookie; 276 277 return 0.8f; 278} 279 280 281status_t 282fs_scan_partition(int fd, partition_data *partition, void *_cookie) 283{ 284 identify_cookie *cookie = (identify_cookie *)_cookie; 285 partition->status = B_PARTITION_VALID; 286 partition->flags |= B_PARTITION_FILE_SYSTEM; 287 partition->content_size = sle64_to_cpu(cookie->boot.number_of_sectors) 288 * le16_to_cpu(cookie->boot.bpb.bytes_per_sector); 289 partition->block_size = le16_to_cpu(cookie->boot.bpb.bytes_per_sector); 290 partition->content_name = strdup(cookie->label); 291 return B_OK; 292} 293 294 295void 296fs_free_identify_partition_cookie(partition_data *partition, void *_cookie) 297{ 298 identify_cookie *cookie = (identify_cookie *)_cookie; 299 free(cookie); 300} 301 302 303uint32 304fs_get_supported_operations(partition_data* partition, uint32 mask) 305{ 306 return B_DISK_SYSTEM_SUPPORTS_INITIALIZING 307 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 308 | B_DISK_SYSTEM_SUPPORTS_WRITING; 309} 310 311 312status_t 313fs_initialize(int fd, partition_id partitionID, const char* name, 314 const char* parameterString, off_t partitionSize, disk_job_id job) 315{ 316 char devpath[MAX_PATH]; 317 status_t result = B_OK; 318 319 TRACE("fs_initialize - [%s] - [%s]\n",name, parameterString); 320 321 update_disk_device_job_progress(job, 0); 322 323 if (ioctl(fd, B_GET_PATH_FOR_DEVICE, devpath) != 0) { 324 result = mkntfs_main(devpath, name); 325 if (result != 0) 326 return result; 327 } else 328 return B_BAD_VALUE; 329 330 result = scan_partition(partitionID); 331 if (result != B_OK) 332 return result; 333 334 update_disk_device_job_progress(job, 1); 335 336 return result; 337} 338 339 340status_t 341fs_mount(fs_volume *_vol, const char *device, ulong flags, const char *args, 342 ino_t *_rootID) 343{ 344 nspace *ns; 345 vnode *newNode = NULL; 346 char lockname[32]; 347 void *handle; 348 unsigned long mountFlags = 0; 349 status_t result = B_NO_ERROR; 350 351 TRACE("fs_mount - ENTER\n"); 352 353 ns = ntfs_malloc(sizeof(nspace)); 354 if (!ns) { 355 result = ENOMEM; 356 goto exit; 357 } 358 359 *ns = (nspace) { 360 .state = NF_FreeClustersOutdate | NF_FreeMFTOutdate, 361 .show_sys_files = false, 362 .ro = false, 363 .fake_attrib = false, 364 .flags = 0 365 }; 366 367 strcpy(ns->devicePath,device); 368 369 sprintf(lockname, "ntfs_lock %lx", ns->id); 370 recursive_lock_init_etc(&(ns->vlock), lockname, MUTEX_FLAG_CLONE_NAME); 371 372 handle = load_driver_settings("ntfs"); 373 ns->show_sys_files = ! (strcasecmp(get_driver_parameter(handle, 374 "hide_sys_files", "true", "true"), "true") == 0); 375 ns->ro = strcasecmp(get_driver_parameter(handle, "read_only", "false", 376 "false"), "false") != 0; 377 ns->noatime = strcasecmp(get_driver_parameter(handle, "no_atime", "true", 378 "true"), "true") == 0; 379 ns->fake_attrib = strcasecmp(get_driver_parameter(handle, "fake_attributes", 380 "true", "true"), "true") == 0; 381 unload_driver_settings(handle); 382 383 if (ns->ro || (flags & B_MOUNT_READ_ONLY) != 0 384 || is_device_read_only(device)) { 385 mountFlags |= MS_RDONLY; 386 ns->flags |= B_FS_IS_READONLY; 387 } 388 389 if (ns->fake_attrib) { 390 gNTFSVnodeOps.open_attr_dir = fake_open_attrib_dir; 391 gNTFSVnodeOps.close_attr_dir = fake_close_attrib_dir; 392 gNTFSVnodeOps.free_attr_dir_cookie = fake_free_attrib_dir_cookie; 393 gNTFSVnodeOps.read_attr_dir = fake_read_attrib_dir; 394 gNTFSVnodeOps.rewind_attr_dir = fake_rewind_attrib_dir; 395 gNTFSVnodeOps.create_attr = fake_create_attrib; 396 gNTFSVnodeOps.open_attr = fake_open_attrib; 397 gNTFSVnodeOps.close_attr = fake_close_attrib; 398 gNTFSVnodeOps.free_attr_cookie = fake_free_attrib_cookie; 399 gNTFSVnodeOps.read_attr = fake_read_attrib; 400 gNTFSVnodeOps.read_attr_stat = fake_read_attrib_stat; 401 gNTFSVnodeOps.write_attr = fake_write_attrib; 402 gNTFSVnodeOps.remove_attr = NULL; 403 } else { 404 gNTFSVnodeOps.open_attr_dir = fs_open_attrib_dir; 405 gNTFSVnodeOps.close_attr_dir = fs_close_attrib_dir; 406 gNTFSVnodeOps.free_attr_dir_cookie = fs_free_attrib_dir_cookie; 407 gNTFSVnodeOps.read_attr_dir = fs_read_attrib_dir; 408 gNTFSVnodeOps.rewind_attr_dir = fs_rewind_attrib_dir; 409 gNTFSVnodeOps.create_attr = fs_create_attrib; 410 gNTFSVnodeOps.open_attr = fs_open_attrib; 411 gNTFSVnodeOps.close_attr = fs_close_attrib; 412 gNTFSVnodeOps.free_attr_cookie = fs_free_attrib_cookie; 413 gNTFSVnodeOps.read_attr = fs_read_attrib; 414 gNTFSVnodeOps.read_attr_stat = fs_read_attrib_stat; 415 gNTFSVnodeOps.write_attr = fs_write_attrib; 416 gNTFSVnodeOps.remove_attr = fs_remove_attrib; 417 } 418 419 ns->ntvol = utils_mount_volume(device, mountFlags | MS_RECOVER); 420 if (ns->ntvol != NULL) 421 result = B_NO_ERROR; 422 else 423 result = errno; 424 425 if (result == B_NO_ERROR) { 426 *_rootID = FILE_root; 427 ns->id = _vol->id; 428 _vol->private_volume = (void *)ns; 429 _vol->ops = &gNTFSVolumeOps; 430 431 newNode = (vnode*)ntfs_calloc(sizeof(vnode)); 432 if (newNode == NULL) 433 result = ENOMEM; 434 else { 435 newNode->vnid = *_rootID; 436 newNode->parent_vnid = -1; 437 438 result = publish_vnode(_vol, *_rootID, (void*)newNode, 439 &gNTFSVnodeOps, S_IFDIR, 0); 440 if (result != B_NO_ERROR) { 441 free(ns); 442 result = EINVAL; 443 goto exit; 444 } else { 445 result = B_NO_ERROR; 446 ntfs_mark_free_space_outdated(ns); 447 ntfs_calc_free_space(ns); 448 } 449 } 450 } else 451 free(ns); 452 453exit: 454 ERROR("fs_mount - EXIT, result code is %s\n", strerror(result)); 455 456 return result; 457} 458 459 460status_t 461fs_unmount(fs_volume *_vol) 462{ 463 nspace *ns = (nspace*)_vol->private_volume; 464 status_t result = B_NO_ERROR; 465 466 TRACE("fs_unmount - ENTER\n"); 467 468 ntfs_umount(ns->ntvol, true); 469 470 recursive_lock_destroy(&(ns->vlock)); 471 472 free(ns); 473 474 TRACE("fs_unmount - EXIT, result is %s\n", strerror(result)); 475 476 return result; 477} 478 479 480status_t 481fs_rfsstat(fs_volume *_vol, struct fs_info *fss) 482{ 483 nspace *ns = (nspace*)_vol->private_volume; 484 int i; 485 486 LOCK_VOL(ns); 487 488 TRACE("fs_rfsstat - ENTER\n"); 489 490 ntfs_calc_free_space(ns); 491 492 fss->dev = ns->id; 493 fss->root = FILE_root; 494 fss->flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME | B_FS_HAS_ATTR | ns->flags; 495 496 fss->block_size = ns->ntvol->cluster_size; 497 fss->io_size = 65536; 498 fss->total_blocks = ns->ntvol->nr_clusters; 499 fss->free_blocks = ns->free_clusters; 500 strncpy(fss->device_name, ns->devicePath, sizeof(fss->device_name)); 501 strncpy(fss->volume_name, ns->ntvol->vol_name, sizeof(fss->volume_name)); 502 503 for (i = strlen(fss->volume_name) - 1; i >= 0 ; i--) { 504 if (fss->volume_name[i] != ' ') 505 break; 506 } 507 if (i < 0) { 508 double size; 509 off_t diskSize = ns->ntvol->nr_clusters * ns->ntvol->cluster_size; 510 off_t divisor = 1ULL << 40; 511 char unit = 'T'; 512 if (diskSize < divisor) { 513 divisor = 1UL << 30; 514 unit = 'G'; 515 if (diskSize < divisor) { 516 divisor = 1UL << 20; 517 unit = 'M'; 518 } 519 } 520 521 size = (double)((10 * diskSize + divisor - 1) / divisor); 522 snprintf(fss->volume_name, sizeof(fss->volume_name), "%g %cB NTFS Volume", 523 size / 10, unit); 524 } else 525 fss->volume_name[i + 1] = 0; 526 527 strcpy(fss->fsh_name, "NTFS"); 528 529 TRACE("fs_rfsstat - EXIT\n"); 530 531 UNLOCK_VOL(ns); 532 533 return B_NO_ERROR; 534} 535 536 537status_t 538fs_wfsstat(fs_volume *_vol, const struct fs_info *fss, uint32 mask) 539{ 540 nspace* ns = (nspace*)_vol->private_volume; 541 status_t result = B_NO_ERROR; 542 543 if (ns->flags & B_FS_IS_READONLY) { 544 ERROR("ntfs is read-only\n"); 545 return B_READ_ONLY_DEVICE; 546 } 547 548 LOCK_VOL(ns); 549 550 if (mask & FS_WRITE_FSINFO_NAME) { 551 result = ntfs_change_label(ns->ntvol, (char*)fss->volume_name); 552 goto exit; 553 } 554 555exit: 556 UNLOCK_VOL(ns); 557 558 return result; 559} 560 561 562status_t 563fs_walk(fs_volume *_vol, fs_vnode *_dir, const char *file, ino_t *_vnid) 564{ 565 nspace *ns = (nspace*)_vol->private_volume; 566 vnode *baseNode = (vnode*)_dir->private_node; 567 vnode *newNode = NULL; 568 status_t result = B_NO_ERROR; 569 570 LOCK_VOL(ns); 571 572 TRACE("fs_walk - ENTER : find for \"%s\"\n",file); 573 574 if (ns == NULL || _dir == NULL || file == NULL || _vnid == NULL) { 575 result = EINVAL; 576 goto exit; 577 } 578 579 if (!strcmp(file, ".")) { 580 *_vnid = baseNode->vnid; 581 if (get_vnode(_vol, *_vnid, (void**)&newNode) != 0) 582 result = ENOENT; 583 } else if (!strcmp(file, "..") && baseNode->vnid != FILE_root) { 584 *_vnid = baseNode->parent_vnid; 585 if (get_vnode(_vol, *_vnid, (void**)&newNode) != 0) 586 result = ENOENT; 587 } else { 588 ino_t vnid = ntfs_inode_lookup(_vol, baseNode->vnid, file); 589 590 if (vnid == (u64)-1) { 591 result = errno; 592 goto exit; 593 } 594 595 if (get_vnode(_vol, vnid, (void**)&newNode) != 0) 596 result = ENOENT; 597 598 if (newNode!=NULL) 599 newNode->parent_vnid = baseNode->vnid; 600 601 *_vnid = vnid; 602 } 603 604exit: 605 TRACE("fs_walk - EXIT, result is %s\n", strerror(result)); 606 607 UNLOCK_VOL(ns); 608 609 return result; 610} 611 612 613status_t 614fs_get_vnode_name(fs_volume *_vol, fs_vnode *_vnode, char *buffer, 615 size_t bufferSize) 616{ 617 nspace *ns = (nspace*)_vol->private_volume; 618 vnode *node = (vnode*)_vnode->private_node; 619 ntfs_inode *ni = NULL; 620 status_t result = B_NO_ERROR; 621 622 char path[MAX_PATH]; 623 char *name; 624 625 LOCK_VOL(ns); 626 627 ni = ntfs_inode_open(ns->ntvol, node->vnid); 628 if (ni == NULL) { 629 result = ENOENT; 630 goto exit; 631 } 632 633 if (utils_inode_get_name(ni, path, MAX_PATH) == 0) { 634 result = EINVAL; 635 goto exit; 636 } 637 638 name = strrchr(path, '/'); 639 name++; 640 641 strlcpy(buffer, name, bufferSize); 642 643exit: 644 if (ni != NULL) 645 ntfs_inode_close(ni); 646 647 UNLOCK_VOL(ns); 648 649 return result; 650} 651 652 653status_t 654fs_read_vnode(fs_volume *_vol, ino_t vnid, fs_vnode *_node, int *_type, 655 uint32 *_flags, bool reenter) 656{ 657 nspace *ns = (nspace*)_vol->private_volume; 658 vnode *newNode = NULL; 659 ntfs_inode *ni = NULL; 660 status_t result = B_NO_ERROR; 661 662 if (!reenter) 663 LOCK_VOL(ns); 664 665 TRACE("fs_read_vnode - ENTER\n"); 666 667 _node->private_node = NULL; 668 _node->ops = &gNTFSVnodeOps; 669 _flags = 0; 670 671 newNode = (vnode*)ntfs_calloc(sizeof(vnode)); 672 if (newNode != NULL) { 673 char *name = NULL; 674 675 ni = ntfs_inode_open(ns->ntvol, vnid); 676 if (ni == NULL) { 677 result = ENOENT; 678 goto exit; 679 } 680 681 // get the node type 682 result = get_node_type(ni, _type); 683 if (result != B_OK) 684 goto exit; 685 686 newNode->vnid = vnid; 687 newNode->parent_vnid = ntfs_mft_get_parent_ref(ni); 688 689 if (ns->fake_attrib) { 690 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) 691 set_mime(newNode, NULL); 692 else { 693 name = (char*)malloc(MAX_PATH); 694 if (name != NULL) { 695 if (utils_inode_get_name(ni, name, MAX_PATH) == 1) 696 set_mime(newNode, name); 697 free(name); 698 } 699 } 700 } 701 _node->private_node = newNode; 702 } else 703 result = ENOMEM; 704 705exit: 706 if (ni != NULL) 707 ntfs_inode_close(ni); 708 709 if (result != B_OK && newNode != NULL) 710 free(newNode); 711 712 TRACE("fs_read_vnode - EXIT, result is %s\n", strerror(result)); 713 714 if (!reenter) 715 UNLOCK_VOL(ns); 716 717 return result; 718} 719 720 721status_t 722fs_write_vnode(fs_volume *_vol, fs_vnode *_node, bool reenter) 723{ 724 nspace *ns = (nspace*)_vol->private_volume; 725 vnode *node = (vnode*)_node->private_node; 726 status_t result = B_NO_ERROR; 727 728 if (!reenter) 729 LOCK_VOL(ns); 730 731 TRACE("fs_write_vnode - ENTER (%Ld)\n", node->vnid); 732 733 free(node); 734 735 TRACE("fs_write_vnode - EXIT\n"); 736 737 if (!reenter) 738 UNLOCK_VOL(ns); 739 740 return result; 741} 742 743 744status_t 745fs_remove_vnode(fs_volume *_vol, fs_vnode *_node, bool reenter) 746{ 747 nspace *ns = (nspace*)_vol->private_volume; 748 vnode *node = (vnode*)_node->private_node; 749 status_t result = B_NO_ERROR; 750 751 // TODO: this does not look right! The space if the node must be freed *here* 752 if (!reenter) 753 LOCK_VOL(ns); 754 755 TRACE("fs_remove_vnode - ENTER (%Ld)\n", node->vnid); 756 757 free(node); 758 759 TRACE("fs_remove_vnode - EXIT, result is %s\n", strerror(result)); 760 761 if (!reenter) 762 UNLOCK_VOL(ns); 763 764 return result; 765} 766 767 768status_t 769fs_rstat(fs_volume *_vol, fs_vnode *_node, struct stat *stbuf) 770{ 771 nspace *ns = (nspace*)_vol->private_volume; 772 vnode *node = (vnode*)_node->private_node; 773 ntfs_inode *ni = NULL; 774 ntfs_attr *na; 775 status_t result = B_NO_ERROR; 776 777 LOCK_VOL(ns); 778 779 TRACE("fs_rstat - ENTER:\n"); 780 781 if (ns == NULL || node == NULL || stbuf == NULL) { 782 result = ENOENT; 783 goto exit; 784 } 785 786 ni = ntfs_inode_open(ns->ntvol, node->vnid); 787 if (ni == NULL) { 788 result = ENOENT; 789 goto exit; 790 } 791 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { 792 // Directory 793 stbuf->st_mode = FS_DIR_MODE; 794 /* get index size, if not known */ 795 if (!test_nino_flag(ni, KnownSize)) { 796 na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); 797 if (na) { 798 ni->data_size = na->data_size; 799 ni->allocated_size = na->allocated_size; 800 set_nino_flag(ni, KnownSize); 801 ntfs_attr_close(na); 802 } 803 } 804 stbuf->st_size = ni->data_size; 805 stbuf->st_blocks = ni->allocated_size >> 9; 806 stbuf->st_nlink = 1; /* Make find(1) work */ 807 } else { 808 // Regular or Interix (INTX) file 809 stbuf->st_mode = FS_FILE_MODE; 810 stbuf->st_size = ni->data_size; 811 stbuf->st_blocks = (ni->allocated_size + 511) >> 9; 812 stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count); 813 814 if (ni->flags & FILE_ATTR_SYSTEM) { 815 na = ntfs_attr_open(ni, AT_DATA, NULL,0); 816 if (!na) { 817 result = ENOENT; 818 goto exit; 819 } 820 stbuf->st_size = na->data_size; 821 stbuf->st_blocks = na->allocated_size >> 9; 822 // Check whether it's Interix symbolic link 823 if (na->data_size <= sizeof(INTX_FILE_TYPES) 824 + sizeof(ntfschar) * PATH_MAX 825 && na->data_size > sizeof(INTX_FILE_TYPES)) { 826 INTX_FILE *intx_file; 827 828 intx_file = ntfs_malloc(na->data_size); 829 if (!intx_file) { 830 result = EINVAL; 831 ntfs_attr_close(na); 832 goto exit; 833 } 834 if (ntfs_attr_pread(na, 0, na->data_size, 835 intx_file) != na->data_size) { 836 result = EINVAL; 837 free(intx_file); 838 ntfs_attr_close(na); 839 goto exit; 840 } 841 if (intx_file->magic == INTX_SYMBOLIC_LINK) 842 stbuf->st_mode = FS_SLNK_MODE; 843 free(intx_file); 844 } 845 ntfs_attr_close(na); 846 } 847 stbuf->st_mode |= 0666; 848 } 849 850 if (ns->flags & B_FS_IS_READONLY) 851 stbuf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); 852 853 stbuf->st_uid = 0; 854 stbuf->st_gid = 0; 855 stbuf->st_ino = MREF(ni->mft_no); 856 stbuf->st_atim = ntfs2timespec(ni->last_access_time); 857 stbuf->st_ctim = ntfs2timespec(ni->last_mft_change_time); 858 stbuf->st_mtim = ntfs2timespec(ni->last_data_change_time); 859 860exit: 861 if (ni != NULL) 862 ntfs_inode_close(ni); 863 864 TRACE("fs_rstat - EXIT, result is %s\n", strerror(result)); 865 866 UNLOCK_VOL(ns); 867 868 return result; 869} 870 871 872status_t 873fs_wstat(fs_volume *_vol, fs_vnode *_node, const struct stat *st, uint32 mask) 874{ 875 nspace *ns = (nspace*)_vol->private_volume; 876 vnode *node = (vnode*)_node->private_node; 877 ntfs_inode *ni = NULL; 878 status_t result = B_NO_ERROR; 879 880 LOCK_VOL(ns); 881 882 TRACE("fs_wstat: ENTER\n"); 883 884 ni = ntfs_inode_open(ns->ntvol, node->vnid); 885 if (ni == NULL) { 886 result = ENOENT; 887 goto exit; 888 } 889 890 if (mask & B_STAT_SIZE) { 891 TRACE("fs_wstat: setting file size to %Lx\n", st->st_size); 892 893 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) 894 result = EISDIR; 895 else { 896 ntfs_attr *na = ntfs_attr_open(ni, AT_DATA, NULL, 0); 897 if (!na) { 898 result = EINVAL; 899 goto exit; 900 } 901 902 ntfs_attr_truncate(na, st->st_size); 903 ntfs_attr_close(na); 904 905 ntfs_mark_free_space_outdated(ns); 906 907 notify_stat_changed(ns->id, MREF(ni->mft_no), mask); 908 } 909 } 910 911 if (mask & B_STAT_MODIFICATION_TIME) { 912 TRACE("fs_wstat: setting modification time\n"); 913 914 ni->last_access_time = timespec2ntfs(st->st_atim); 915 ni->last_data_change_time = timespec2ntfs(st->st_mtim); 916 ni->last_mft_change_time = timespec2ntfs(st->st_ctim); 917 918 notify_stat_changed(ns->id, MREF(ni->mft_no), mask); 919 } 920 921exit: 922 if (ni != NULL) 923 ntfs_inode_close(ni); 924 925 TRACE("fs_wstat: EXIT with (%s)\n", strerror(result)); 926 927 UNLOCK_VOL(ns); 928 929 return result; 930} 931 932 933status_t 934fs_sync(fs_volume *_vol) 935{ 936 nspace *ns = (nspace *)_vol->private_volume; 937 938 LOCK_VOL(ns); 939 940 TRACE("fs_sync - ENTER\n"); 941 942 TRACE("fs_sync - EXIT\n"); 943 944 UNLOCK_VOL(ns); 945 946 return B_NO_ERROR; 947} 948 949 950status_t 951fs_fsync(fs_volume *_vol, fs_vnode *_node) 952{ 953 nspace *ns = (nspace*)_vol->private_volume; 954 vnode *node = (vnode*)_node->private_node; 955 ntfs_inode *ni = NULL; 956 status_t result = B_NO_ERROR; 957 958 LOCK_VOL(ns); 959 960 TRACE("fs_fsync: ENTER\n"); 961 962 if (ns == NULL || node == NULL) { 963 result = ENOENT; 964 goto exit; 965 } 966 967 ni = ntfs_inode_open(ns->ntvol, node->vnid); 968 if (ni == NULL) { 969 result = ENOENT; 970 goto exit; 971 } 972 973 ntfs_inode_sync(ni); 974 975exit: 976 if (ni != NULL) 977 ntfs_inode_close(ni); 978 979 TRACE("fs_fsync: EXIT\n"); 980 981 UNLOCK_VOL(ns); 982 983 return result; 984} 985 986 987status_t 988fs_open(fs_volume *_vol, fs_vnode *_node, int omode, void **_cookie) 989{ 990 nspace *ns = (nspace*)_vol->private_volume; 991 vnode *node = (vnode*)_node->private_node; 992 filecookie *cookie = NULL; 993 ntfs_inode *ni = NULL; 994 ntfs_attr *na = NULL; 995 status_t result = B_NO_ERROR; 996 997 TRACE("fs_open - ENTER\n"); 998 999 LOCK_VOL(ns); 1000 1001 if (node == NULL) { 1002 result = EINVAL; 1003 goto exit; 1004 } 1005 1006 ni = ntfs_inode_open(ns->ntvol, node->vnid); 1007 if (ni == NULL) { 1008 result = errno; 1009 goto exit; 1010 } 1011 1012 if (omode & O_TRUNC) { 1013 na = ntfs_attr_open(ni, AT_DATA, NULL, 0); 1014 if (na) { 1015 if (ntfs_attr_truncate(na, 0)) 1016 result = errno; 1017 } else 1018 result = errno; 1019 } 1020 1021 cookie = (filecookie*)ntfs_calloc(sizeof(filecookie)); 1022 1023 if (cookie != NULL) { 1024 cookie->omode = omode; 1025 *_cookie = (void*)cookie; 1026 } else 1027 result = ENOMEM; 1028 1029exit: 1030 if (na != NULL) 1031 ntfs_attr_close(na); 1032 1033 if (ni != NULL) 1034 ntfs_inode_close(ni); 1035 1036 TRACE("fs_open - EXIT\n"); 1037 1038 UNLOCK_VOL(ns); 1039 1040 return result; 1041} 1042 1043 1044status_t 1045fs_create(fs_volume *_vol, fs_vnode *_dir, const char *name, int omode, 1046 int perms, void **_cookie, ino_t *_vnid) 1047{ 1048 nspace *ns = (nspace*)_vol->private_volume; 1049 vnode *dir = (vnode*)_dir->private_node; 1050 filecookie *cookie = NULL; 1051 vnode *newNode = NULL; 1052 ntfs_attr *na = NULL; 1053 ntfs_inode *ni = NULL; 1054 ntfs_inode *dir_ni = NULL; 1055 ntfschar *uname = NULL; 1056 status_t result = B_NO_ERROR; 1057 int unameLength; 1058 1059 if (ns->flags & B_FS_IS_READONLY) { 1060 return B_READ_ONLY_DEVICE; 1061 } 1062 1063 LOCK_VOL(ns); 1064 1065 TRACE("fs_create - ENTER: name=%s\n", name); 1066 1067 if (_vol == NULL || _dir == NULL) { 1068 result = EINVAL; 1069 goto exit; 1070 } 1071 1072 if (name == NULL || *name == '\0' || strchr(name, '/') 1073 || strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { 1074 result = EINVAL; 1075 goto exit; 1076 } 1077 1078 dir_ni = ntfs_inode_open(ns->ntvol, dir->vnid); 1079 if (dir_ni == NULL) { 1080 result = ENOENT; 1081 goto exit; 1082 } 1083 1084 if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { 1085 result = EINVAL; 1086 goto exit; 1087 } 1088 1089 unameLength = ntfs_mbstoucs(name, &uname); 1090 if (unameLength < 0) { 1091 result = EINVAL; 1092 goto exit; 1093 } 1094 1095 cookie = (filecookie*)ntfs_calloc(sizeof(filecookie)); 1096 1097 if (cookie != NULL) 1098 cookie->omode = omode; 1099 else { 1100 result = ENOMEM; 1101 goto exit; 1102 } 1103 1104 ni = ntfs_pathname_to_inode(ns->ntvol, dir_ni, name); 1105 if (ni != NULL) { 1106 // file exists 1107 *_vnid = MREF(ni->mft_no); 1108 if (omode & O_TRUNC) { 1109 na = ntfs_attr_open(ni, AT_DATA, NULL, 0); 1110 if (na != NULL) { 1111 if (ntfs_attr_truncate(na, 0)) 1112 result = errno; 1113 ntfs_attr_close(na); 1114 } else 1115 result = errno; 1116 } 1117 ntfs_inode_close(ni); 1118 } else { 1119 le32 securid = const_cpu_to_le32(0); 1120 ni = ntfs_create(dir_ni, securid, uname, unameLength, S_IFREG); 1121 if (ni != NULL) { 1122 ino_t vnid = MREF(ni->mft_no); 1123 1124 newNode = (vnode*)ntfs_calloc(sizeof(vnode)); 1125 if (newNode == NULL) { 1126 result = ENOMEM; 1127 goto exit; 1128 } 1129 1130 newNode->vnid = vnid; 1131 newNode->parent_vnid = dir->vnid; 1132 1133 ni->flags |= FILE_ATTR_ARCHIVE; 1134 NInoSetDirty(ni); 1135 1136 result = publish_vnode(_vol, vnid, (void*)newNode, &gNTFSVnodeOps, 1137 S_IFREG, 0); 1138 1139 if (ntfs_inode_close_in_dir(ni, dir_ni)) { 1140 result = EINVAL; 1141 goto exit; 1142 } 1143 1144 *_vnid = vnid; 1145 1146 if (ns->fake_attrib) { 1147 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) 1148 set_mime(newNode, NULL); 1149 else 1150 set_mime(newNode, name); 1151 } 1152 1153 ntfs_mark_free_space_outdated(ns); 1154 fs_ntfs_update_times(_vol, dir_ni, NTFS_UPDATE_MCTIME); 1155 notify_entry_created(ns->id, dir->vnid, name, vnid); 1156 } else 1157 result = errno; 1158 } 1159 1160exit: 1161 if (result >= B_OK) 1162 *_cookie = cookie; 1163 else 1164 free(cookie); 1165 1166 if (dir_ni != NULL) 1167 ntfs_inode_close(dir_ni); 1168 1169 if (uname != NULL) 1170 free(uname); 1171 1172 TRACE("fs_create - EXIT, result is %s\n", strerror(result)); 1173 1174 UNLOCK_VOL(ns); 1175 1176 return result; 1177} 1178 1179 1180status_t 1181fs_read(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t offset, void *buf, 1182 size_t *len) 1183{ 1184 nspace *ns = (nspace*)_vol->private_volume; 1185 vnode *node = (vnode*)_node->private_node; 1186 ntfs_inode *ni = NULL; 1187 ntfs_attr *na = NULL; 1188 size_t size = *len; 1189 int total = 0; 1190 status_t result = B_OK; 1191 1192 LOCK_VOL(ns); 1193 1194 TRACE("fs_read - ENTER\n"); 1195 1196 ni = ntfs_inode_open(ns->ntvol, node->vnid); 1197 if (ni == NULL) { 1198 *len = 0; 1199 result = ENOENT; 1200 goto exit2; 1201 } 1202 1203 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { 1204 TRACE("ntfs_read called on directory.\n"); 1205 *len = 0; 1206 result = EISDIR; 1207 goto exit2; 1208 } 1209 1210 if (offset < 0) { 1211 *len = 0; 1212 result = EINVAL; 1213 goto exit2; 1214 } 1215 1216 na = ntfs_attr_open(ni, AT_DATA, NULL, 0); 1217 if (na == NULL) { 1218 *len = 0; 1219 result = EINVAL; 1220 goto exit2; 1221 } 1222 if (offset + size > na->data_size) 1223 size = na->data_size - offset; 1224 1225 while (size) { 1226 off_t bytesRead = ntfs_attr_pread(na, offset, size, buf); 1227 if (bytesRead < (s64)size) { 1228 ntfs_log_error("ntfs_attr_pread returned less bytes than " 1229 "requested.\n"); 1230 } 1231 if (bytesRead <= 0) { 1232 *len = 0; 1233 result = EINVAL; 1234 goto exit; 1235 } 1236 size -= bytesRead; 1237 offset += bytesRead; 1238 total += bytesRead; 1239 } 1240 1241 *len = total; 1242 fs_ntfs_update_times(_vol, ni, NTFS_UPDATE_ATIME); 1243 1244exit: 1245 if (na != NULL) 1246 ntfs_attr_close(na); 1247 1248exit2: 1249 if (ni != NULL) 1250 ntfs_inode_close(ni); 1251 1252 UNLOCK_VOL(ns); 1253 1254 TRACE("fs_read - EXIT, result is %s\n", strerror(result)); 1255 1256 return result; 1257} 1258 1259 1260status_t 1261fs_write(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t offset, 1262 const void *buf, size_t *len) 1263{ 1264 nspace *ns = (nspace*)_vol->private_volume; 1265 vnode *node = (vnode*)_node->private_node; 1266 filecookie *cookie = (filecookie*)_cookie; 1267 ntfs_inode *ni = NULL; 1268 ntfs_attr *na = NULL; 1269 int total = 0; 1270 size_t size = *len; 1271 status_t result = B_OK; 1272 1273 if (ns->flags & B_FS_IS_READONLY) { 1274 ERROR("ntfs is read-only\n"); 1275 return B_READ_ONLY_DEVICE; 1276 } 1277 1278 LOCK_VOL(ns); 1279 1280 TRACE("fs_write - ENTER, offset=%lld, len=%ld\n", offset, *len); 1281 1282 ni = ntfs_inode_open(ns->ntvol, node->vnid); 1283 if (ni == NULL) { 1284 *len = 0; 1285 result = ENOENT; 1286 goto exit2; 1287 } 1288 1289 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { 1290 ERROR("fs_write - called on directory.\n"); 1291 *len = 0; 1292 result = EISDIR; 1293 goto exit2; 1294 } 1295 1296 if (offset < 0) { 1297 ERROR("fs_write - offset < 0.\n"); 1298 *len = 0; 1299 result = EINVAL; 1300 goto exit2; 1301 } 1302 1303 na = ntfs_attr_open(ni, AT_DATA, NULL, 0); 1304 if (na == NULL) { 1305 ERROR("fs_write - ntfs_attr_open()==NULL\n"); 1306 *len = 0; 1307 result = EINVAL; 1308 goto exit2; 1309 } 1310 1311 if (cookie->omode & O_APPEND) 1312 offset = na->data_size; 1313 1314 if (offset + size > na->data_size) { 1315 ntfs_mark_free_space_outdated(ns); 1316 if (ntfs_attr_truncate(na, offset + size)) 1317 size = na->data_size - offset; 1318 else 1319 notify_stat_changed(ns->id, node->vnid, B_STAT_SIZE); 1320 } 1321 1322 while (size) { 1323 off_t bytesWritten = ntfs_attr_pwrite(na, offset, size, buf); 1324 if (bytesWritten < (s64)size) { 1325 ERROR("fs_write - ntfs_attr_pwrite returned less bytes than " 1326 "requested.\n"); 1327 } 1328 if (bytesWritten <= 0) { 1329 ERROR("fs_write - ntfs_attr_pwrite()<=0\n"); 1330 *len = 0; 1331 result = EINVAL; 1332 goto exit; 1333 } 1334 size -= bytesWritten; 1335 offset += bytesWritten; 1336 total += bytesWritten; 1337 } 1338 1339 *len = total; 1340 if (total > 0) 1341 fs_ntfs_update_times(_vol, ni, NTFS_UPDATE_MCTIME); 1342 1343 TRACE("fs_write - OK\n"); 1344 1345exit: 1346 if (na != NULL) 1347 ntfs_attr_close(na); 1348exit2: 1349 if (ni != NULL) 1350 ntfs_inode_close(ni); 1351 1352 TRACE("fs_write - EXIT, result is %s, writed %d bytes\n", 1353 strerror(result), (int)(*len)); 1354 1355 UNLOCK_VOL(ns); 1356 1357 return result; 1358} 1359 1360 1361status_t 1362fs_close(fs_volume *_vol, fs_vnode *_node, void *cookie) 1363{ 1364 TRACE("fs_close - ENTER\n"); 1365 1366 TRACE("fs_close - EXIT\n"); 1367 return B_NO_ERROR; 1368} 1369 1370 1371status_t 1372fs_free_cookie(fs_volume *_vol, fs_vnode *_node, void *cookie) 1373{ 1374 TRACE("fs_free_cookie - ENTER\n"); 1375 1376 free(cookie); 1377 1378 TRACE("fs_free_cookie - EXIT\n"); 1379 return B_NO_ERROR; 1380} 1381 1382 1383status_t 1384fs_access(fs_volume *_vol, fs_vnode *_node, int mode) 1385{ 1386 TRACE("fs_access - ENTER\n"); 1387 1388 TRACE("fs_access - EXIT\n"); 1389 return B_NO_ERROR; 1390} 1391 1392 1393status_t 1394fs_readlink(fs_volume *_vol, fs_vnode *_node, char *buffer, size_t *bufferSize) 1395{ 1396 nspace *ns = (nspace*)_vol->private_volume; 1397 vnode *node = (vnode*)_node->private_node; 1398 ntfs_attr *na = NULL; 1399 INTX_FILE *intxFile = NULL; 1400 ntfs_inode *ni = NULL; 1401 char *tempBuffer = NULL; 1402 status_t result = B_NO_ERROR; 1403 int l = 0; 1404 1405 LOCK_VOL(ns); 1406 1407 TRACE("fs_readlink - ENTER\n"); 1408 1409 if (ns == NULL || node == NULL || buffer == NULL || bufferSize == NULL) { 1410 result = EINVAL; 1411 goto exit; 1412 } 1413 1414 ni = ntfs_inode_open(ns->ntvol, node->vnid); 1415 if (ni == NULL) { 1416 result = ENOENT; 1417 goto exit; 1418 } 1419 1420 /* Sanity checks. */ 1421 if (!(ni->flags & FILE_ATTR_SYSTEM)) { 1422 result = EINVAL; 1423 goto exit; 1424 } 1425 na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); 1426 if (!na) { 1427 result = errno; 1428 goto exit; 1429 } 1430 if (na->data_size <= sizeof(INTX_FILE_TYPES)) { 1431 result = EINVAL; 1432 goto exit; 1433 } 1434 if (na->data_size > sizeof(INTX_FILE_TYPES) + sizeof(ntfschar) * MAX_PATH) { 1435 result = ENAMETOOLONG; 1436 goto exit; 1437 } 1438 /* Receive file content. */ 1439 intxFile = ntfs_malloc(na->data_size); 1440 if (!intxFile) { 1441 result = errno; 1442 goto exit; 1443 } 1444 if (ntfs_attr_pread(na, 0, na->data_size, intxFile) != na->data_size) { 1445 result = errno; 1446 goto exit; 1447 } 1448 /* Sanity check. */ 1449 if (intxFile->magic != INTX_SYMBOLIC_LINK) { 1450 result = EINVAL; 1451 goto exit; 1452 } 1453 /* Convert link from unicode to local encoding. */ 1454 l = ntfs_ucstombs(intxFile->target, 1455 (na->data_size - offsetof(INTX_FILE, target)) / sizeof(ntfschar), 1456 &tempBuffer, 0); 1457 1458 if (l < 0) { 1459 result = errno; 1460 goto exit; 1461 } 1462 if (tempBuffer == NULL) { 1463 result = B_NO_MEMORY; 1464 goto exit; 1465 } 1466 1467 TRACE("fs_readlink - LINK:[%s]\n", buffer); 1468 1469 strlcpy(buffer, tempBuffer, *bufferSize); 1470 free(tempBuffer); 1471 1472 *bufferSize = l + 1; 1473 1474 result = B_NO_ERROR; 1475 1476exit: 1477 free(intxFile); 1478 if (na != NULL) 1479 ntfs_attr_close(na); 1480 if (ni != NULL) 1481 ntfs_inode_close(ni); 1482 1483 TRACE("fs_readlink - EXIT, result is %s\n", strerror(result)); 1484 1485 UNLOCK_VOL(ns); 1486 1487 return result; 1488} 1489 1490 1491status_t 1492fs_create_symlink(fs_volume *_vol, fs_vnode *_dir, const char *name, 1493 const char *target, int mode) 1494{ 1495 nspace *ns = (nspace*)_vol->private_volume; 1496 vnode *dir = (vnode*)_dir->private_node; 1497 ntfs_inode *ni = NULL; 1498 ntfs_inode *dir_ni = NULL; 1499 vnode *newNode = NULL; 1500 ntfschar *uname = NULL; 1501 ntfschar *utarget = NULL; 1502 int unameLength; 1503 int utargetLength; 1504 status_t result = B_NO_ERROR; 1505 le32 securid = const_cpu_to_le32(0); 1506 1507 if (ns->flags & B_FS_IS_READONLY) { 1508 return B_READ_ONLY_DEVICE; 1509 } 1510 1511 LOCK_VOL(ns); 1512 1513 TRACE("fs_symlink - ENTER, name=%s, path=%s\n",name, target); 1514 1515 if (ns == NULL || dir == NULL || name == NULL || target == NULL) { 1516 result = EINVAL; 1517 goto exit; 1518 } 1519 1520 dir_ni = ntfs_inode_open(ns->ntvol, dir->vnid); 1521 if (dir_ni == NULL) { 1522 result = ENOENT; 1523 goto exit; 1524 } 1525 1526 unameLength = ntfs_mbstoucs(name, &uname); 1527 if (unameLength < 0) { 1528 result = EINVAL; 1529 goto exit; 1530 } 1531 1532 utargetLength = ntfs_mbstoucs(target, &utarget); 1533 if (utargetLength < 0) { 1534 result = EINVAL; 1535 goto exit; 1536 } 1537 1538 ni = ntfs_create_symlink(dir_ni, securid, uname, unameLength, utarget, 1539 utargetLength); 1540 if (ni) { 1541 ino_t vnid = MREF(ni->mft_no); 1542 newNode = (vnode*)ntfs_calloc(sizeof(vnode)); 1543 if (newNode == NULL) { 1544 result = ENOMEM; 1545 goto exit; 1546 } 1547 1548 newNode->vnid = vnid; 1549 newNode->parent_vnid = MREF(dir_ni->mft_no); 1550 1551 ni->flags |= FILE_ATTR_ARCHIVE; 1552 NInoSetDirty(ni); 1553 1554 result = B_NO_ERROR; 1555 result = publish_vnode(_vol, vnid, (void*)newNode, &gNTFSVnodeOps, 1556 S_IFREG, 0); 1557 put_vnode(_vol, vnid); 1558 1559 if (ntfs_inode_close_in_dir(ni, dir_ni)) { 1560 result = EINVAL; 1561 goto exit; 1562 } 1563 1564 ntfs_mark_free_space_outdated(ns); 1565 fs_ntfs_update_times(_vol, dir_ni, NTFS_UPDATE_MCTIME); 1566 notify_entry_created(ns->id, MREF(dir_ni->mft_no), name, vnid); 1567 } else 1568 result = errno; 1569 1570exit: 1571 if (dir_ni != NULL) 1572 ntfs_inode_close(dir_ni); 1573 if (utarget != NULL) 1574 free(utarget); 1575 if (uname != NULL) 1576 free(uname); 1577 1578 TRACE("fs_symlink - EXIT, result is %s\n", strerror(result)); 1579 1580 UNLOCK_VOL(ns); 1581 1582 return result; 1583} 1584 1585 1586status_t 1587fs_mkdir(fs_volume *_vol, fs_vnode *_dir, const char *name, int perms) 1588{ 1589 nspace *ns = (nspace*)_vol->private_volume; 1590 vnode *dir = (vnode*)_dir->private_node; 1591 vnode *newNode =NULL; 1592 ntfschar *uname = NULL; 1593 int unameLength; 1594 ntfs_inode *ni = NULL; 1595 ntfs_inode *dir_ni = NULL; 1596 status_t result = B_NO_ERROR; 1597 le32 securid = const_cpu_to_le32(0); 1598 1599 if (ns->flags & B_FS_IS_READONLY) { 1600 ERROR("ntfs is read-only\n"); 1601 return B_READ_ONLY_DEVICE; 1602 } 1603 1604 LOCK_VOL(ns); 1605 1606 TRACE("fs_mkdir - ENTER: name=%s\n", name); 1607 1608 if (_vol == NULL || _dir == NULL || name == NULL) { 1609 result = EINVAL; 1610 goto exit; 1611 } 1612 1613 dir_ni = ntfs_inode_open(ns->ntvol, dir->vnid); 1614 if (dir_ni == NULL) { 1615 result = ENOENT; 1616 goto exit; 1617 } 1618 1619 if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { 1620 result = EINVAL; 1621 goto exit; 1622 } 1623 1624 unameLength = ntfs_mbstoucs(name, &uname); 1625 if (unameLength < 0) { 1626 result = EINVAL; 1627 goto exit; 1628 } 1629 1630 ni = ntfs_create(dir_ni, securid, uname, unameLength, S_IFDIR); 1631 if (ni != NULL) { 1632 ino_t vnid = MREF(ni->mft_no); 1633 1634 newNode = (vnode*)ntfs_calloc(sizeof(vnode)); 1635 if (newNode == NULL) { 1636 result = ENOMEM; 1637 goto exit; 1638 } 1639 1640 newNode->vnid = vnid; 1641 newNode->parent_vnid = MREF(dir_ni->mft_no); 1642 1643 ni->flags |= FILE_ATTR_ARCHIVE; 1644 NInoSetDirty(ni); 1645 1646 result = publish_vnode(_vol, vnid, (void*)newNode, &gNTFSVnodeOps, 1647 S_IFDIR, 0); 1648 1649 put_vnode(_vol, vnid); 1650 1651 if (ntfs_inode_close_in_dir(ni, dir_ni)) { 1652 result = EINVAL; 1653 goto exit; 1654 } 1655 1656 ntfs_mark_free_space_outdated(ns); 1657 fs_ntfs_update_times(_vol, dir_ni, NTFS_UPDATE_MCTIME); 1658 notify_entry_created(ns->id, MREF(dir_ni->mft_no), name, vnid); 1659 } else 1660 result = errno; 1661 1662exit: 1663 if (dir_ni != NULL) 1664 ntfs_inode_close(dir_ni); 1665 if (uname != NULL) 1666 free(uname); 1667 1668 TRACE("fs_mkdir - EXIT, result is %s\n", strerror(result)); 1669 1670 UNLOCK_VOL(ns); 1671 1672 return result; 1673} 1674 1675 1676status_t 1677fs_rename(fs_volume *_vol, fs_vnode *_odir, const char *name, 1678 fs_vnode *_ndir, const char *newname) 1679{ 1680 nspace *ns = (nspace*)_vol->private_volume; 1681 vnode *parent_vnode = (vnode*)_odir->private_node; 1682 vnode *newparent_vnode = (vnode*)_ndir->private_node; 1683 vnode *file = NULL; 1684 1685 ino_t parent_vnid = parent_vnode->vnid; 1686 ino_t newparent_vnid = newparent_vnode->vnid; 1687 1688 ino_t inode; 1689 ino_t target_inode; 1690 1691 ntfs_inode *ni = NULL; 1692 ntfs_inode *dir_ni = NULL; 1693 1694 status_t result = B_NO_ERROR; 1695 1696 if (ns->flags & B_FS_IS_READONLY) { 1697 ERROR("ntfs is read-only\n"); 1698 return B_READ_ONLY_DEVICE; 1699 } 1700 1701 LOCK_VOL(ns); 1702 1703 TRACE("NTFS:fs_rename - oldname:%s newname:%s\n", name, newname); 1704 1705 inode = ntfs_inode_lookup(_vol, parent_vnid, name); 1706 if (inode == (u64)-1) { 1707 result = EINVAL; 1708 goto exit; 1709 } 1710 1711 /* Check whether target is present */ 1712 target_inode = ntfs_inode_lookup(_vol, newparent_vnid, newname); 1713 1714 if (target_inode == (u64)-1) { 1715 ntfschar *uname = NULL; 1716 int uname_len; 1717 1718 result = get_vnode(_vol, inode, (void**)&file); 1719 if (result != B_NO_ERROR) 1720 goto exit; 1721 1722 1723 ni = ntfs_inode_open(ns->ntvol, inode); 1724 if (!ni) { 1725 result = EINVAL; 1726 goto exit; 1727 } 1728 1729 uname_len = ntfs_mbstoucs(newname, &uname); 1730 if (uname_len < 0) { 1731 result = EINVAL; 1732 goto exit; 1733 } 1734 1735 dir_ni = ntfs_inode_open(ns->ntvol, newparent_vnid); 1736 if (!dir_ni) { 1737 result = EINVAL; 1738 goto exit; 1739 } 1740 1741 if (ntfs_link(ni, dir_ni, uname, uname_len)) { 1742 result = EINVAL; 1743 goto exit; 1744 } 1745 1746 ni->flags |= FILE_ATTR_ARCHIVE; 1747 1748 fs_ntfs_update_times(_vol, ni, NTFS_UPDATE_CTIME); 1749 fs_ntfs_update_times(_vol, dir_ni, NTFS_UPDATE_MCTIME); 1750 1751 if (ns->fake_attrib) { 1752 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) 1753 set_mime(file, NULL); 1754 else 1755 set_mime(file, newname); 1756 notify_attribute_changed(ns->id, file->vnid, "BEOS:TYPE", 1757 B_ATTR_CHANGED); 1758 } 1759 1760 ntfs_inode_close(dir_ni); 1761 ntfs_inode_close(ni); 1762 1763 free(uname); 1764 1765 ntfs_remove(_vol, parent_vnid, name); 1766 1767 file->parent_vnid = newparent_vnid; 1768 1769 put_vnode(_vol, file->vnid); 1770 1771 notify_entry_moved(ns->id, parent_vnid, name, newparent_vnid, 1772 newname, inode); 1773 } else 1774 result = EINVAL; 1775exit: 1776 TRACE("fs_rename - EXIT, result is %s\n", strerror(result)); 1777 1778 UNLOCK_VOL(ns); 1779 1780 return result; 1781} 1782 1783 1784status_t 1785fs_rmdir(fs_volume *_vol, fs_vnode *_dir, const char *name) 1786{ 1787 nspace *ns = (nspace*)_vol->private_volume; 1788 vnode *dir = (vnode*)_dir->private_node; 1789 vnode *file = NULL; 1790 ntfs_inode *ni = NULL; 1791 ntfs_inode *dir_ni = NULL; 1792 status_t result = B_NO_ERROR; 1793 ino_t ino; 1794 1795 if (ns->flags & B_FS_IS_READONLY) { 1796 ERROR("ntfs is read-only\n"); 1797 return B_READ_ONLY_DEVICE; 1798 } 1799 1800 LOCK_VOL(ns); 1801 1802 TRACE("fs_rmdir - ENTER: name %s\n", name == NULL ? "NULL" : name); 1803 1804 if (ns == NULL || dir == NULL || name == NULL) { 1805 result = EINVAL; 1806 goto exit; 1807 } 1808 1809 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { 1810 result = EPERM; 1811 goto exit; 1812 } 1813 1814 ino = ntfs_inode_lookup(_vol, dir->vnid, name); 1815 if (ino == (u64)-1) { 1816 result = EINVAL; 1817 goto exit; 1818 } 1819 1820 result = get_vnode(_vol, ino, (void**)&file); 1821 if (result != B_NO_ERROR) 1822 goto exit; 1823 1824 ni = ntfs_inode_open(ns->ntvol, file->vnid); 1825 if (ni != NULL) { 1826 if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { 1827 result = ENOTDIR; 1828 goto exit; 1829 } 1830 if (ntfs_check_empty_dir(ni)<0) { 1831 result = ENOTEMPTY; 1832 goto exit; 1833 } 1834 ntfs_inode_close(ni); 1835 } else { 1836 result = EINVAL; 1837 goto exit; 1838 } 1839 1840 1841 result = ntfs_remove(_vol, dir->vnid, name); 1842 if(result != B_NO_ERROR) { 1843 goto exit; 1844 } 1845 1846 notify_entry_removed(ns->id, dir->vnid, name, file->vnid); 1847 1848 remove_vnode(_vol, file->vnid); 1849 1850 put_vnode(_vol, ino); 1851 1852 dir_ni = ntfs_inode_open(ns->ntvol, dir->vnid); 1853 if (dir_ni != NULL) { 1854 fs_ntfs_update_times(_vol, dir_ni, NTFS_UPDATE_MCTIME); 1855 ntfs_inode_close(dir_ni); 1856 } 1857 1858 ntfs_mark_free_space_outdated(ns); 1859 1860exit: 1861 if (ni != NULL) 1862 ntfs_inode_close(ni); 1863 1864 TRACE("fs_rmdir - EXIT, result is %s\n", strerror(result)); 1865 1866 UNLOCK_VOL(ns); 1867 1868 return result; 1869} 1870 1871 1872status_t 1873fs_unlink(fs_volume *_vol, fs_vnode *_dir, const char *name) 1874{ 1875 nspace *ns = (nspace*)_vol->private_volume; 1876 vnode *dir = (vnode*)_dir->private_node; 1877 vnode *file = NULL; 1878 ntfs_inode *dir_ni = NULL; 1879 status_t result = B_NO_ERROR; 1880 ino_t inode; 1881 1882 if (ns->flags & B_FS_IS_READONLY) { 1883 ERROR("ntfs is read-only\n"); 1884 return B_READ_ONLY_DEVICE; 1885 } 1886 1887 LOCK_VOL(ns); 1888 1889 TRACE("fs_unlink - ENTER: name %s\n", name == NULL ? "NULL" : name); 1890 1891 if (ns == NULL || dir == NULL || name == NULL) { 1892 result = EINVAL; 1893 goto exit; 1894 } 1895 1896 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { 1897 result = EPERM; 1898 goto exit; 1899 } 1900 1901 inode = ntfs_inode_lookup(_vol, dir->vnid, name); 1902 if (inode == (u64)-1) { 1903 result = EINVAL; 1904 goto exit; 1905 } 1906 1907 result = get_vnode(_vol, inode, (void**)&file); 1908 if (result != B_NO_ERROR) 1909 goto exit; 1910 1911 result = ntfs_remove(_vol, dir->vnid, name); 1912 if(result != B_NO_ERROR) { 1913 goto exit; 1914 } 1915 1916 notify_entry_removed(ns->id, dir->vnid, name, file->vnid); 1917 1918 remove_vnode(_vol, file->vnid); 1919 1920 put_vnode(_vol, inode); 1921 1922 dir_ni = ntfs_inode_open(ns->ntvol, dir->vnid); 1923 if (dir_ni != NULL) { 1924 fs_ntfs_update_times(_vol, dir_ni, NTFS_UPDATE_MCTIME); 1925 ntfs_inode_close(dir_ni); 1926 } 1927 1928 ntfs_mark_free_space_outdated(ns); 1929 1930exit: 1931 TRACE("fs_unlink - EXIT, result is %s\n", strerror(result)); 1932 1933 UNLOCK_VOL(ns); 1934 1935 return result; 1936} 1937