13fbe39e1SAugustin Cavalier /* 23fbe39e1SAugustin Cavalier * Copyright 2021, Haiku, Inc. All rights reserved. 33fbe39e1SAugustin Cavalier * Distributed under the terms of the MIT License. 43fbe39e1SAugustin Cavalier * 53fbe39e1SAugustin Cavalier * Authors: 63fbe39e1SAugustin Cavalier * Augustin Cavalier <waddlesplash> 73fbe39e1SAugustin Cavalier */ 83fbe39e1SAugustin Cavalier 93fbe39e1SAugustin Cavalier #include <dirent.h> 103fbe39e1SAugustin Cavalier #include <unistd.h> 113fbe39e1SAugustin Cavalier #include <util/kernel_cpp.h> 123fbe39e1SAugustin Cavalier #include <string.h> 133fbe39e1SAugustin Cavalier 143fbe39e1SAugustin Cavalier #include <AutoDeleter.h> 153fbe39e1SAugustin Cavalier #include <fs_cache.h> 163fbe39e1SAugustin Cavalier #include <fs_info.h> 175070f597SAugustin Cavalier #include <NodeMonitor.h> 183fbe39e1SAugustin Cavalier #include <file_systems/DeviceOpener.h> 193fbe39e1SAugustin Cavalier #include <util/AutoLock.h> 203fbe39e1SAugustin Cavalier 213fbe39e1SAugustin Cavalier #include "ntfs.h" 223fbe39e1SAugustin Cavalier 233fbe39e1SAugustin Cavalier extern "C" { 243fbe39e1SAugustin Cavalier #include "libntfs/bootsect.h" 253fbe39e1SAugustin Cavalier #include "libntfs/dir.h" 263fbe39e1SAugustin Cavalier #include "utils/utils.h" 273fbe39e1SAugustin Cavalier } 283fbe39e1SAugustin Cavalier 293fbe39e1SAugustin Cavalier //#define TRACE_NTFS 303fbe39e1SAugustin Cavalier #ifdef TRACE_NTFS 313fbe39e1SAugustin Cavalier # define TRACE(X...) dprintf("ntfs: " X) 323fbe39e1SAugustin Cavalier #else 333fbe39e1SAugustin Cavalier # define TRACE(X...) ; 343fbe39e1SAugustin Cavalier #endif 353fbe39e1SAugustin Cavalier #define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) 363fbe39e1SAugustin Cavalier #define ERROR(X...) dprintf("ntfs: error: " X) 373fbe39e1SAugustin Cavalier 383fbe39e1SAugustin Cavalier 393fbe39e1SAugustin Cavalier struct identify_cookie { 403fbe39e1SAugustin Cavalier NTFS_BOOT_SECTOR boot; 413fbe39e1SAugustin Cavalier }; 423fbe39e1SAugustin Cavalier 435070f597SAugustin Cavalier extern "C" int mkntfs_main(const char* devpath, const char* label); 445070f597SAugustin Cavalier 453fbe39e1SAugustin Cavalier typedef CObjectDeleter<ntfs_inode, int, ntfs_inode_close> NtfsInodeCloser; 465070f597SAugustin Cavalier typedef CObjectDeleter<ntfs_attr, void, ntfs_attr_close> NtfsAttrCloser; 473fbe39e1SAugustin Cavalier static status_t fs_access(fs_volume* _volume, fs_vnode* _node, int accessMode); 483fbe39e1SAugustin Cavalier 493fbe39e1SAugustin Cavalier 503fbe39e1SAugustin Cavalier // #pragma mark - Scanning 513fbe39e1SAugustin Cavalier 523fbe39e1SAugustin Cavalier 533fbe39e1SAugustin Cavalier static float 543fbe39e1SAugustin Cavalier fs_identify_partition(int fd, partition_data* partition, void** _cookie) 553fbe39e1SAugustin Cavalier { 563fbe39e1SAugustin Cavalier CALLED(); 573fbe39e1SAugustin Cavalier 583fbe39e1SAugustin Cavalier NTFS_BOOT_SECTOR boot; 593fbe39e1SAugustin Cavalier if (read_pos(fd, 0, (void*)&boot, 512) != 512) { 603fbe39e1SAugustin Cavalier ERROR("identify_partition: failed to read boot sector\n"); 613fbe39e1SAugustin Cavalier return -1; 623fbe39e1SAugustin Cavalier } 633fbe39e1SAugustin Cavalier 643fbe39e1SAugustin Cavalier if (!ntfs_boot_sector_is_ntfs(&boot)) { 653fbe39e1SAugustin Cavalier ERROR("identify_partition: boot signature doesn't match\n"); 663fbe39e1SAugustin Cavalier return -1; 673fbe39e1SAugustin Cavalier } 683fbe39e1SAugustin Cavalier 693fbe39e1SAugustin Cavalier identify_cookie* cookie = new identify_cookie; 703fbe39e1SAugustin Cavalier if (cookie == NULL) { 713fbe39e1SAugustin Cavalier ERROR("identify_partition: cookie allocation failed\n"); 723fbe39e1SAugustin Cavalier return -1; 733fbe39e1SAugustin Cavalier } 743fbe39e1SAugustin Cavalier 753fbe39e1SAugustin Cavalier memcpy(&cookie->boot, &boot, sizeof(boot)); 763fbe39e1SAugustin Cavalier *_cookie = cookie; 773fbe39e1SAugustin Cavalier 783fbe39e1SAugustin Cavalier // This value overrides the Intel partition identifier. 793fbe39e1SAugustin Cavalier return 0.82f; 803fbe39e1SAugustin Cavalier } 813fbe39e1SAugustin Cavalier 823fbe39e1SAugustin Cavalier 833fbe39e1SAugustin Cavalier static status_t 843fbe39e1SAugustin Cavalier fs_scan_partition(int fd, partition_data* partition, void* _cookie) 853fbe39e1SAugustin Cavalier { 863fbe39e1SAugustin Cavalier CALLED(); 873fbe39e1SAugustin Cavalier 883fbe39e1SAugustin Cavalier identify_cookie *cookie = (identify_cookie*)_cookie; 893fbe39e1SAugustin Cavalier partition->status = B_PARTITION_VALID; 903fbe39e1SAugustin Cavalier partition->flags |= B_PARTITION_FILE_SYSTEM; 913fbe39e1SAugustin Cavalier partition->content_size = sle64_to_cpu(cookie->boot.number_of_sectors) 923fbe39e1SAugustin Cavalier * le16_to_cpu(cookie->boot.bpb.bytes_per_sector); 933fbe39e1SAugustin Cavalier partition->block_size = le16_to_cpu(cookie->boot.bpb.bytes_per_sector); 943fbe39e1SAugustin Cavalier 953fbe39e1SAugustin Cavalier // get volume name 963fbe39e1SAugustin Cavalier ntfs_volume* ntVolume; 973fbe39e1SAugustin Cavalier char path[B_PATH_NAME_LENGTH]; 983fbe39e1SAugustin Cavalier if (ioctl(fd, B_GET_PATH_FOR_DEVICE, path) != 0) { 993fbe39e1SAugustin Cavalier ntVolume = utils_mount_volume(path, NTFS_MNT_RDONLY | NTFS_MNT_RECOVER); 100*a8261652SAugustin Cavalier if (ntVolume == NULL) 101*a8261652SAugustin Cavalier return errno ? errno : B_ERROR; 102*a8261652SAugustin Cavalier 1033fbe39e1SAugustin Cavalier if (ntVolume->vol_name != NULL && ntVolume->vol_name[0] != '\0') 1043fbe39e1SAugustin Cavalier partition->content_name = strdup(ntVolume->vol_name); 105*a8261652SAugustin Cavalier else 106*a8261652SAugustin Cavalier partition->content_name = strdup(""); 1073fbe39e1SAugustin Cavalier ntfs_umount(ntVolume, true); 1083fbe39e1SAugustin Cavalier } 1093fbe39e1SAugustin Cavalier 1103fbe39e1SAugustin Cavalier return partition->content_name != NULL ? B_OK : B_NO_MEMORY; 1113fbe39e1SAugustin Cavalier } 1123fbe39e1SAugustin Cavalier 1133fbe39e1SAugustin Cavalier 1143fbe39e1SAugustin Cavalier static void 1153fbe39e1SAugustin Cavalier fs_free_identify_partition_cookie(partition_data* partition, void* _cookie) 1163fbe39e1SAugustin Cavalier { 1173fbe39e1SAugustin Cavalier CALLED(); 1183fbe39e1SAugustin Cavalier 1193fbe39e1SAugustin Cavalier delete (identify_cookie*)_cookie; 1203fbe39e1SAugustin Cavalier } 1213fbe39e1SAugustin Cavalier 1223fbe39e1SAugustin Cavalier 1233fbe39e1SAugustin Cavalier // #pragma mark - 1243fbe39e1SAugustin Cavalier 1253fbe39e1SAugustin Cavalier 1263fbe39e1SAugustin Cavalier static status_t 1275070f597SAugustin Cavalier fs_initialize(int fd, partition_id partitionID, const char* name, 1285070f597SAugustin Cavalier const char* parameterString, off_t partitionSize, disk_job_id job) 1295070f597SAugustin Cavalier { 1305070f597SAugustin Cavalier TRACE("fs_initialize: '%s', %s\n", name, parameterString); 1315070f597SAugustin Cavalier 1325070f597SAugustin Cavalier update_disk_device_job_progress(job, 0); 1335070f597SAugustin Cavalier 1345070f597SAugustin Cavalier char path[B_PATH_NAME_LENGTH]; 1355070f597SAugustin Cavalier if (ioctl(fd, B_GET_PATH_FOR_DEVICE, path) == 0) 1365070f597SAugustin Cavalier return B_BAD_VALUE; 1375070f597SAugustin Cavalier 1385070f597SAugustin Cavalier status_t result = mkntfs_main(path, name); 1395070f597SAugustin Cavalier if (result != 0) 1405070f597SAugustin Cavalier return result; 1415070f597SAugustin Cavalier 1425070f597SAugustin Cavalier result = scan_partition(partitionID); 1435070f597SAugustin Cavalier if (result != B_OK) 1445070f597SAugustin Cavalier return result; 1455070f597SAugustin Cavalier 1465070f597SAugustin Cavalier update_disk_device_job_progress(job, 1); 1475070f597SAugustin Cavalier return B_OK; 1485070f597SAugustin Cavalier } 1495070f597SAugustin Cavalier 1505070f597SAugustin Cavalier 1515070f597SAugustin Cavalier static status_t 1523fbe39e1SAugustin Cavalier fs_mount(fs_volume* _volume, const char* device, uint32 flags, 1533fbe39e1SAugustin Cavalier const char* args, ino_t* _rootID) 1543fbe39e1SAugustin Cavalier { 1553fbe39e1SAugustin Cavalier CALLED(); 1563fbe39e1SAugustin Cavalier 1573fbe39e1SAugustin Cavalier volume* volume = new struct volume; 1583fbe39e1SAugustin Cavalier vnode* root = new vnode; 1593fbe39e1SAugustin Cavalier if (volume == NULL || root == NULL) 1603fbe39e1SAugustin Cavalier return B_NO_MEMORY; 1613fbe39e1SAugustin Cavalier ObjectDeleter<struct volume> volumeDeleter(volume); 1623fbe39e1SAugustin Cavalier 1633fbe39e1SAugustin Cavalier mutex_init(&volume->lock, "NTFS volume lock"); 1643fbe39e1SAugustin Cavalier volume->fs_info_flags = B_FS_IS_PERSISTENT; 1653fbe39e1SAugustin Cavalier 1663fbe39e1SAugustin Cavalier unsigned long ntfsFlags = NTFS_MNT_RECOVER | NTFS_MNT_MAY_RDONLY; 1673fbe39e1SAugustin Cavalier if ((flags & B_MOUNT_READ_ONLY) != 0 || DeviceOpener(device, O_RDWR).IsReadOnly()) 1683fbe39e1SAugustin Cavalier ntfsFlags |= NTFS_MNT_RDONLY; 1693fbe39e1SAugustin Cavalier 1703fbe39e1SAugustin Cavalier // mount 1713fbe39e1SAugustin Cavalier volume->ntfs = utils_mount_volume(device, ntfsFlags); 1723fbe39e1SAugustin Cavalier if (volume->ntfs == NULL) 1733fbe39e1SAugustin Cavalier return errno; 1743fbe39e1SAugustin Cavalier 1755070f597SAugustin Cavalier if (NVolReadOnly(volume->ntfs)) { 1765070f597SAugustin Cavalier if ((ntfsFlags & NTFS_MNT_RDONLY) == 0) 1775070f597SAugustin Cavalier ERROR("volume is hibernated, mounted as read-only\n"); 1783fbe39e1SAugustin Cavalier volume->fs_info_flags |= B_FS_IS_READONLY; 1795070f597SAugustin Cavalier } 1803fbe39e1SAugustin Cavalier 1813fbe39e1SAugustin Cavalier if (ntfs_volume_get_free_space(volume->ntfs) != 0) { 1823fbe39e1SAugustin Cavalier ntfs_umount(volume->ntfs, true); 1833fbe39e1SAugustin Cavalier return B_ERROR; 1843fbe39e1SAugustin Cavalier } 1853fbe39e1SAugustin Cavalier 1863fbe39e1SAugustin Cavalier const bool showSystem = false, showHidden = true, hideDot = false; 1873fbe39e1SAugustin Cavalier if (ntfs_set_shown_files(volume->ntfs, showSystem, showHidden, hideDot) != 0) { 1883fbe39e1SAugustin Cavalier ntfs_umount(volume->ntfs, true); 1893fbe39e1SAugustin Cavalier return B_ERROR; 1903fbe39e1SAugustin Cavalier } 1913fbe39e1SAugustin Cavalier 1923fbe39e1SAugustin Cavalier // TODO: uid/gid mapping and real permissions 1933fbe39e1SAugustin Cavalier 1943fbe39e1SAugustin Cavalier // construct lowntfs_context 1955070f597SAugustin Cavalier volume->lowntfs.haiku_fs_volume = _volume; 1965070f597SAugustin Cavalier volume->lowntfs.current_close_state_vnode = NULL; 1975070f597SAugustin Cavalier 1983fbe39e1SAugustin Cavalier volume->lowntfs.vol = volume->ntfs; 1993fbe39e1SAugustin Cavalier volume->lowntfs.abs_mnt_point = NULL; 2003fbe39e1SAugustin Cavalier volume->lowntfs.dmask = 0; 2013fbe39e1SAugustin Cavalier volume->lowntfs.fmask = S_IXUSR | S_IXGRP | S_IXOTH; 2025070f597SAugustin Cavalier volume->lowntfs.dmtime = 0; 2035070f597SAugustin Cavalier volume->lowntfs.special_files = NTFS_FILES_INTERIX; 2043fbe39e1SAugustin Cavalier volume->lowntfs.posix_nlink = 0; 2055070f597SAugustin Cavalier volume->lowntfs.inherit = 0; 2065070f597SAugustin Cavalier volume->lowntfs.windows_names = 1; 2075070f597SAugustin Cavalier volume->lowntfs.latest_ghost = 0; 2083fbe39e1SAugustin Cavalier 2093fbe39e1SAugustin Cavalier *_rootID = root->inode = FILE_root; 2103fbe39e1SAugustin Cavalier root->parent_inode = (u64)-1; 2113fbe39e1SAugustin Cavalier root->mode = S_IFDIR | ACCESSPERMS; 2123fbe39e1SAugustin Cavalier root->uid = root->gid = 0; 2135070f597SAugustin Cavalier root->size = 0; 2143fbe39e1SAugustin Cavalier 2153fbe39e1SAugustin Cavalier status_t status = publish_vnode(_volume, root->inode, root, &gNtfsVnodeOps, S_IFDIR, 0); 2163fbe39e1SAugustin Cavalier if (status != B_OK) { 2173fbe39e1SAugustin Cavalier ntfs_umount(volume->ntfs, true); 2183fbe39e1SAugustin Cavalier return status; 2193fbe39e1SAugustin Cavalier } 2203fbe39e1SAugustin Cavalier 2213fbe39e1SAugustin Cavalier volumeDeleter.Detach(); 2223fbe39e1SAugustin Cavalier 2233fbe39e1SAugustin Cavalier _volume->private_volume = volume; 2243fbe39e1SAugustin Cavalier _volume->ops = &gNtfsVolumeOps; 2253fbe39e1SAugustin Cavalier return B_OK; 2263fbe39e1SAugustin Cavalier } 2273fbe39e1SAugustin Cavalier 2283fbe39e1SAugustin Cavalier 2293fbe39e1SAugustin Cavalier static status_t 2303fbe39e1SAugustin Cavalier fs_unmount(fs_volume* _volume) 2313fbe39e1SAugustin Cavalier { 2323fbe39e1SAugustin Cavalier CALLED(); 2333fbe39e1SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 2343fbe39e1SAugustin Cavalier 2353fbe39e1SAugustin Cavalier if (ntfs_umount(volume->ntfs, false) < 0) 2363fbe39e1SAugustin Cavalier return errno; 2373fbe39e1SAugustin Cavalier 2383fbe39e1SAugustin Cavalier delete volume; 2393fbe39e1SAugustin Cavalier _volume->private_volume = NULL; 2403fbe39e1SAugustin Cavalier 2413fbe39e1SAugustin Cavalier return B_OK; 2423fbe39e1SAugustin Cavalier } 2433fbe39e1SAugustin Cavalier 2443fbe39e1SAugustin Cavalier 2453fbe39e1SAugustin Cavalier static status_t 2463fbe39e1SAugustin Cavalier fs_read_fs_info(fs_volume* _volume, struct fs_info* info) 2473fbe39e1SAugustin Cavalier { 2483fbe39e1SAugustin Cavalier CALLED(); 2493fbe39e1SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 2503fbe39e1SAugustin Cavalier MutexLocker lock(volume->lock); 2513fbe39e1SAugustin Cavalier 2523fbe39e1SAugustin Cavalier info->flags = volume->fs_info_flags; 2533fbe39e1SAugustin Cavalier info->block_size = volume->ntfs->cluster_size; 2543fbe39e1SAugustin Cavalier info->total_blocks = volume->ntfs->nr_clusters; 2553fbe39e1SAugustin Cavalier info->free_blocks = volume->ntfs->free_clusters; 2563fbe39e1SAugustin Cavalier 2573fbe39e1SAugustin Cavalier info->io_size = 65536; 2583fbe39e1SAugustin Cavalier 2593fbe39e1SAugustin Cavalier strlcpy(info->volume_name, volume->ntfs->vol_name, sizeof(info->volume_name)); 2603fbe39e1SAugustin Cavalier strlcpy(info->fsh_name, "NTFS", sizeof(info->fsh_name)); 2613fbe39e1SAugustin Cavalier 2623fbe39e1SAugustin Cavalier return B_OK; 2633fbe39e1SAugustin Cavalier } 2643fbe39e1SAugustin Cavalier 2653fbe39e1SAugustin Cavalier 2665070f597SAugustin Cavalier static status_t 2675070f597SAugustin Cavalier fs_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask) 2685070f597SAugustin Cavalier { 2695070f597SAugustin Cavalier CALLED(); 2705070f597SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 2715070f597SAugustin Cavalier MutexLocker lock(volume->lock); 2725070f597SAugustin Cavalier 2735070f597SAugustin Cavalier if ((volume->fs_info_flags & B_FS_IS_READONLY) != 0) 2745070f597SAugustin Cavalier return B_READ_ONLY_DEVICE; 2755070f597SAugustin Cavalier 2765070f597SAugustin Cavalier status_t status = B_OK; 2775070f597SAugustin Cavalier 2785070f597SAugustin Cavalier if ((mask & FS_WRITE_FSINFO_NAME) != 0) { 2795070f597SAugustin Cavalier ntfschar* label = NULL; 2805070f597SAugustin Cavalier int label_len = ntfs_mbstoucs(info->volume_name, &label); 2815070f597SAugustin Cavalier if (label_len <= 0 || label == NULL) 2825070f597SAugustin Cavalier return -1; 2835070f597SAugustin Cavalier MemoryDeleter nameDeleter(label); 2845070f597SAugustin Cavalier 2855070f597SAugustin Cavalier if (ntfs_volume_rename(volume->ntfs, label, label_len) != 0) 2865070f597SAugustin Cavalier status = errno; 2875070f597SAugustin Cavalier } 2885070f597SAugustin Cavalier 2895070f597SAugustin Cavalier return status; 2905070f597SAugustin Cavalier } 2915070f597SAugustin Cavalier 2925070f597SAugustin Cavalier 2933fbe39e1SAugustin Cavalier // #pragma mark - 2943fbe39e1SAugustin Cavalier 2953fbe39e1SAugustin Cavalier 2963fbe39e1SAugustin Cavalier static status_t 2975070f597SAugustin Cavalier fs_init_vnode(fs_volume* _volume, ino_t parent, ino_t nid, vnode** _vnode, bool publish = false) 2983fbe39e1SAugustin Cavalier { 2993fbe39e1SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 3005070f597SAugustin Cavalier ASSERT_LOCKED_MUTEX(&volume->lock); 3013fbe39e1SAugustin Cavalier 3023fbe39e1SAugustin Cavalier ntfs_inode* ni = ntfs_inode_open(volume->ntfs, nid); 3033fbe39e1SAugustin Cavalier if (ni == NULL) 3043fbe39e1SAugustin Cavalier return ENOENT; 3053fbe39e1SAugustin Cavalier NtfsInodeCloser niCloser(ni); 3063fbe39e1SAugustin Cavalier 3073fbe39e1SAugustin Cavalier vnode* node = new vnode; 3083fbe39e1SAugustin Cavalier if (node == NULL) 3093fbe39e1SAugustin Cavalier return B_NO_MEMORY; 3103fbe39e1SAugustin Cavalier ObjectDeleter<vnode> vnodeDeleter(node); 3113fbe39e1SAugustin Cavalier 3123fbe39e1SAugustin Cavalier struct stat statbuf; 3133fbe39e1SAugustin Cavalier if (ntfs_fuse_getstat(&volume->lowntfs, NULL, ni, &statbuf) != 0) 3143fbe39e1SAugustin Cavalier return errno; 3153fbe39e1SAugustin Cavalier 3165070f597SAugustin Cavalier node->inode = nid; 3175070f597SAugustin Cavalier node->parent_inode = parent; 3183fbe39e1SAugustin Cavalier node->uid = statbuf.st_uid; 3193fbe39e1SAugustin Cavalier node->gid = statbuf.st_gid; 3203fbe39e1SAugustin Cavalier node->mode = statbuf.st_mode; 3215070f597SAugustin Cavalier node->size = statbuf.st_size; 3223fbe39e1SAugustin Cavalier 3233fbe39e1SAugustin Cavalier // cache the node's name 3243fbe39e1SAugustin Cavalier char path[B_FILE_NAME_LENGTH]; 3253fbe39e1SAugustin Cavalier if (utils_inode_get_name(ni, path, sizeof(path)) == 0) 3265070f597SAugustin Cavalier return B_NO_MEMORY; 3273fbe39e1SAugustin Cavalier node->name = strdup(strrchr(path, '/') + 1); 3283fbe39e1SAugustin Cavalier 3295070f597SAugustin Cavalier if (publish) { 3305070f597SAugustin Cavalier status_t status = publish_vnode(_volume, node->inode, node, &gNtfsVnodeOps, node->mode, 0); 3315070f597SAugustin Cavalier if (status != B_OK) 3325070f597SAugustin Cavalier return status; 3335070f597SAugustin Cavalier } 3343fbe39e1SAugustin Cavalier 3355070f597SAugustin Cavalier if ((node->mode & S_IFDIR) == 0) { 3365070f597SAugustin Cavalier node->file_cache = file_cache_create(_volume->id, nid, node->size); 3375070f597SAugustin Cavalier if (node->file_cache == NULL) 3385070f597SAugustin Cavalier return B_NO_INIT; 3395070f597SAugustin Cavalier } 3403fbe39e1SAugustin Cavalier 3413fbe39e1SAugustin Cavalier vnodeDeleter.Detach(); 3425070f597SAugustin Cavalier *_vnode = node; 3435070f597SAugustin Cavalier return B_OK; 3445070f597SAugustin Cavalier } 3455070f597SAugustin Cavalier 3465070f597SAugustin Cavalier 3475070f597SAugustin Cavalier static status_t 3485070f597SAugustin Cavalier fs_get_vnode(fs_volume* _volume, ino_t nid, fs_vnode* _node, int* _type, 3495070f597SAugustin Cavalier uint32* _flags, bool reenter) 3505070f597SAugustin Cavalier { 3515070f597SAugustin Cavalier TRACE("get_vnode %" B_PRIdINO "\n", nid); 3525070f597SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 3535070f597SAugustin Cavalier MutexLocker lock(reenter ? NULL : &volume->lock); 3545070f597SAugustin Cavalier 3555070f597SAugustin Cavalier vnode* vnode; 3565070f597SAugustin Cavalier status_t status = fs_init_vnode(_volume, -1 /* set by fs_lookup */, nid, &vnode); 3575070f597SAugustin Cavalier if (status != B_OK) 3585070f597SAugustin Cavalier return status; 3595070f597SAugustin Cavalier 3605070f597SAugustin Cavalier _node->private_node = vnode; 3615070f597SAugustin Cavalier _node->ops = &gNtfsVnodeOps; 3625070f597SAugustin Cavalier *_type = vnode->mode; 3635070f597SAugustin Cavalier *_flags = 0; 3645070f597SAugustin Cavalier 3653fbe39e1SAugustin Cavalier return B_OK; 3663fbe39e1SAugustin Cavalier } 3673fbe39e1SAugustin Cavalier 3683fbe39e1SAugustin Cavalier 3693fbe39e1SAugustin Cavalier static status_t 3703fbe39e1SAugustin Cavalier fs_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) 3713fbe39e1SAugustin Cavalier { 3723fbe39e1SAugustin Cavalier CALLED(); 3733fbe39e1SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 3743fbe39e1SAugustin Cavalier MutexLocker lock(reenter ? NULL : &volume->lock); 3753fbe39e1SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 3763fbe39e1SAugustin Cavalier 3773fbe39e1SAugustin Cavalier file_cache_delete(node->file_cache); 3783fbe39e1SAugustin Cavalier delete node; 3793fbe39e1SAugustin Cavalier return B_OK; 3803fbe39e1SAugustin Cavalier } 3813fbe39e1SAugustin Cavalier 3823fbe39e1SAugustin Cavalier 3835070f597SAugustin Cavalier static status_t 3845070f597SAugustin Cavalier fs_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) 3855070f597SAugustin Cavalier { 3865070f597SAugustin Cavalier CALLED(); 3875070f597SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 3885070f597SAugustin Cavalier MutexLocker lock(reenter ? NULL : &volume->lock); 3895070f597SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 3905070f597SAugustin Cavalier 3915070f597SAugustin Cavalier if (ntfs_fuse_release(&volume->lowntfs, node->parent_inode, node->inode, 3925070f597SAugustin Cavalier node->lowntfs_close_state, node->lowntfs_ghost) != 0) 3935070f597SAugustin Cavalier return errno; 3945070f597SAugustin Cavalier 3955070f597SAugustin Cavalier file_cache_delete(node->file_cache); 3965070f597SAugustin Cavalier delete node; 3975070f597SAugustin Cavalier return B_OK; 3985070f597SAugustin Cavalier } 3995070f597SAugustin Cavalier 4005070f597SAugustin Cavalier 4015070f597SAugustin Cavalier int* 4025070f597SAugustin Cavalier ntfs_haiku_get_close_state(struct lowntfs_context *ctx, u64 ino) 4035070f597SAugustin Cavalier { 4045070f597SAugustin Cavalier if (ctx->current_close_state_vnode != NULL) 4055070f597SAugustin Cavalier panic("NTFS current_close_state_vnode should be NULL!"); 4065070f597SAugustin Cavalier 4075070f597SAugustin Cavalier vnode* node = NULL; 4085070f597SAugustin Cavalier if (get_vnode((fs_volume*)ctx->haiku_fs_volume, ino, (void**)&node) != B_OK) 4095070f597SAugustin Cavalier return NULL; 4105070f597SAugustin Cavalier ctx->current_close_state_vnode = node; 4115070f597SAugustin Cavalier return &node->lowntfs_close_state; 4125070f597SAugustin Cavalier } 4135070f597SAugustin Cavalier 4145070f597SAugustin Cavalier 4155070f597SAugustin Cavalier void 4165070f597SAugustin Cavalier ntfs_haiku_put_close_state(struct lowntfs_context *ctx, u64 ino, u64 ghost) 4175070f597SAugustin Cavalier { 4185070f597SAugustin Cavalier vnode* node = (vnode*)ctx->current_close_state_vnode; 4195070f597SAugustin Cavalier if (node == NULL) 4205070f597SAugustin Cavalier return; 4215070f597SAugustin Cavalier 4225070f597SAugustin Cavalier node->lowntfs_ghost = ghost; 4235070f597SAugustin Cavalier if ((node->lowntfs_close_state & CLOSE_GHOST) != 0) { 4245070f597SAugustin Cavalier fs_volume* _volume = (fs_volume*)ctx->haiku_fs_volume; 4255070f597SAugustin Cavalier entry_cache_remove(_volume->id, node->parent_inode, node->name); 4265070f597SAugustin Cavalier notify_entry_removed(_volume->id, node->parent_inode, node->name, node->inode); 4275070f597SAugustin Cavalier remove_vnode(_volume, node->inode); 4285070f597SAugustin Cavalier } 4295070f597SAugustin Cavalier 4305070f597SAugustin Cavalier ctx->current_close_state_vnode = NULL; 4315070f597SAugustin Cavalier put_vnode((fs_volume*)ctx->haiku_fs_volume, node->inode); 4325070f597SAugustin Cavalier } 4335070f597SAugustin Cavalier 4345070f597SAugustin Cavalier 4353fbe39e1SAugustin Cavalier static bool 4363fbe39e1SAugustin Cavalier fs_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie) 4373fbe39e1SAugustin Cavalier { 4383fbe39e1SAugustin Cavalier return true; 4393fbe39e1SAugustin Cavalier } 4403fbe39e1SAugustin Cavalier 4413fbe39e1SAugustin Cavalier 4423fbe39e1SAugustin Cavalier static status_t 4433fbe39e1SAugustin Cavalier fs_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie, 4443fbe39e1SAugustin Cavalier off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 4453fbe39e1SAugustin Cavalier { 4463fbe39e1SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 4473fbe39e1SAugustin Cavalier MutexLocker lock(volume->lock); 4483fbe39e1SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 4493fbe39e1SAugustin Cavalier 4503fbe39e1SAugustin Cavalier TRACE("read_pages inode: %" B_PRIdINO", pos: %" B_PRIdOFF "; vecs: %p; " 4513fbe39e1SAugustin Cavalier "count: %" B_PRIuSIZE "; numBytes: %" B_PRIuSIZE "\n", node->inode, pos, 4523fbe39e1SAugustin Cavalier vecs, count, *_numBytes); 4533fbe39e1SAugustin Cavalier 4543fbe39e1SAugustin Cavalier ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode); 4553fbe39e1SAugustin Cavalier if (ni == NULL) 4563fbe39e1SAugustin Cavalier return B_FILE_ERROR; 4573fbe39e1SAugustin Cavalier NtfsInodeCloser niCloser(ni); 4583fbe39e1SAugustin Cavalier 4593fbe39e1SAugustin Cavalier if (pos < 0 || pos >= ni->data_size) 4603fbe39e1SAugustin Cavalier return B_BAD_VALUE; 4613fbe39e1SAugustin Cavalier 4623fbe39e1SAugustin Cavalier size_t bytesLeft = min_c(*_numBytes, size_t(ni->data_size - pos)); 4633fbe39e1SAugustin Cavalier *_numBytes = 0; 4643fbe39e1SAugustin Cavalier for (size_t i = 0; i < count && bytesLeft > 0; i++) { 4653fbe39e1SAugustin Cavalier const size_t ioSize = min_c(bytesLeft, vecs[i].iov_len); 4663fbe39e1SAugustin Cavalier const int read = ntfs_fuse_read(ni, pos, (char*)vecs[i].iov_base, ioSize); 4673fbe39e1SAugustin Cavalier if (read < 0) 4683fbe39e1SAugustin Cavalier return errno; 4693fbe39e1SAugustin Cavalier 4703fbe39e1SAugustin Cavalier pos += read; 4713fbe39e1SAugustin Cavalier *_numBytes += read; 4723fbe39e1SAugustin Cavalier bytesLeft -= read; 4733fbe39e1SAugustin Cavalier 4743fbe39e1SAugustin Cavalier if (size_t(read) != ioSize) 4753fbe39e1SAugustin Cavalier return errno; 4763fbe39e1SAugustin Cavalier } 4773fbe39e1SAugustin Cavalier 4783fbe39e1SAugustin Cavalier return B_OK; 4793fbe39e1SAugustin Cavalier } 4803fbe39e1SAugustin Cavalier 4813fbe39e1SAugustin Cavalier 4825070f597SAugustin Cavalier static status_t 4835070f597SAugustin Cavalier fs_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie, 4845070f597SAugustin Cavalier off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 4855070f597SAugustin Cavalier { 4865070f597SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 4875070f597SAugustin Cavalier MutexLocker lock(volume->lock); 4885070f597SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 4895070f597SAugustin Cavalier 4905070f597SAugustin Cavalier TRACE("write_pages inode: %" B_PRIdINO", pos: %" B_PRIdOFF "; vecs: %p; " 4915070f597SAugustin Cavalier "count: %" B_PRIuSIZE "; numBytes: %" B_PRIuSIZE "\n", node->inode, pos, 4925070f597SAugustin Cavalier vecs, count, *_numBytes); 4935070f597SAugustin Cavalier 4945070f597SAugustin Cavalier ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode); 4955070f597SAugustin Cavalier if (ni == NULL) 4965070f597SAugustin Cavalier return B_FILE_ERROR; 4975070f597SAugustin Cavalier NtfsInodeCloser niCloser(ni); 4985070f597SAugustin Cavalier 4995070f597SAugustin Cavalier if (pos < 0 || pos >= ni->data_size) 5005070f597SAugustin Cavalier return B_BAD_VALUE; 5015070f597SAugustin Cavalier 5025070f597SAugustin Cavalier size_t bytesLeft = min_c(*_numBytes, size_t(ni->data_size - pos)); 5035070f597SAugustin Cavalier *_numBytes = 0; 5045070f597SAugustin Cavalier for (size_t i = 0; i < count && bytesLeft > 0; i++) { 5055070f597SAugustin Cavalier const size_t ioSize = min_c(bytesLeft, vecs[i].iov_len); 5065070f597SAugustin Cavalier const int written = ntfs_fuse_write(&volume->lowntfs, ni, (char*)vecs[i].iov_base, ioSize, pos); 5075070f597SAugustin Cavalier if (written < 0) 5085070f597SAugustin Cavalier return errno; 5095070f597SAugustin Cavalier 5105070f597SAugustin Cavalier pos += written; 5115070f597SAugustin Cavalier *_numBytes += written; 5125070f597SAugustin Cavalier bytesLeft -= written; 5135070f597SAugustin Cavalier 5145070f597SAugustin Cavalier if (size_t(written) != ioSize) 5155070f597SAugustin Cavalier return errno; 5165070f597SAugustin Cavalier } 5175070f597SAugustin Cavalier 5185070f597SAugustin Cavalier return B_OK; 5195070f597SAugustin Cavalier } 5205070f597SAugustin Cavalier 5215070f597SAugustin Cavalier 5223fbe39e1SAugustin Cavalier // #pragma mark - 5233fbe39e1SAugustin Cavalier 5243fbe39e1SAugustin Cavalier 5253fbe39e1SAugustin Cavalier static status_t 5263fbe39e1SAugustin Cavalier fs_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, 5273fbe39e1SAugustin Cavalier ino_t* _vnodeID) 5283fbe39e1SAugustin Cavalier { 5293fbe39e1SAugustin Cavalier TRACE("fs_lookup: name address: %p (%s)\n", name, name); 5303fbe39e1SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 5313fbe39e1SAugustin Cavalier MutexLocker lock(volume->lock); 5323fbe39e1SAugustin Cavalier vnode* directory = (vnode*)_directory->private_node; 5333fbe39e1SAugustin Cavalier 5343fbe39e1SAugustin Cavalier status_t result; 5353fbe39e1SAugustin Cavalier if (strcmp(name, ".") == 0) { 5363fbe39e1SAugustin Cavalier *_vnodeID = directory->inode; 5373fbe39e1SAugustin Cavalier } else if (strcmp(name, "..") == 0) { 5383fbe39e1SAugustin Cavalier if (directory->inode == FILE_root) 5393fbe39e1SAugustin Cavalier return ENOENT; 5403fbe39e1SAugustin Cavalier *_vnodeID = directory->parent_inode; 5413fbe39e1SAugustin Cavalier } else { 5423fbe39e1SAugustin Cavalier u64 inode = ntfs_fuse_inode_lookup(&volume->lowntfs, directory->inode, name); 5433fbe39e1SAugustin Cavalier if (inode == (u64)-1) 5443fbe39e1SAugustin Cavalier return errno; 5453fbe39e1SAugustin Cavalier *_vnodeID = inode; 5463fbe39e1SAugustin Cavalier } 5473fbe39e1SAugustin Cavalier 5483fbe39e1SAugustin Cavalier result = entry_cache_add(_volume->id, directory->inode, name, *_vnodeID); 5493fbe39e1SAugustin Cavalier if (result != B_OK) 5503fbe39e1SAugustin Cavalier return result; 5513fbe39e1SAugustin Cavalier 5523fbe39e1SAugustin Cavalier vnode* node = NULL; 5533fbe39e1SAugustin Cavalier result = get_vnode(_volume, *_vnodeID, (void**)&node); 5543fbe39e1SAugustin Cavalier if (result != B_OK) 5553fbe39e1SAugustin Cavalier return result; 5563fbe39e1SAugustin Cavalier 5573fbe39e1SAugustin Cavalier if (node->parent_inode == (u64)-1) 5583fbe39e1SAugustin Cavalier node->parent_inode = directory->inode; 5593fbe39e1SAugustin Cavalier 5603fbe39e1SAugustin Cavalier TRACE("fs_lookup: ID %" B_PRIdINO "\n", *_vnodeID); 5613fbe39e1SAugustin Cavalier return B_OK; 5623fbe39e1SAugustin Cavalier } 5633fbe39e1SAugustin Cavalier 5643fbe39e1SAugustin Cavalier 5653fbe39e1SAugustin Cavalier static status_t 5663fbe39e1SAugustin Cavalier fs_get_vnode_name(fs_volume* _volume, fs_vnode* _node, char* buffer, size_t bufferSize) 5673fbe39e1SAugustin Cavalier { 5683fbe39e1SAugustin Cavalier // CALLED(); 5693fbe39e1SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 5703fbe39e1SAugustin Cavalier 5713fbe39e1SAugustin Cavalier if (strlcpy(buffer, node->name, bufferSize) >= bufferSize) 5723fbe39e1SAugustin Cavalier return B_BUFFER_OVERFLOW; 5733fbe39e1SAugustin Cavalier return B_OK; 5743fbe39e1SAugustin Cavalier } 5753fbe39e1SAugustin Cavalier 5763fbe39e1SAugustin Cavalier 5773fbe39e1SAugustin Cavalier static status_t 5783fbe39e1SAugustin Cavalier fs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd, 5793fbe39e1SAugustin Cavalier void* buffer, size_t bufferLength) 5803fbe39e1SAugustin Cavalier { 5813fbe39e1SAugustin Cavalier // TODO? 5823fbe39e1SAugustin Cavalier return B_DEV_INVALID_IOCTL; 5833fbe39e1SAugustin Cavalier } 5843fbe39e1SAugustin Cavalier 5853fbe39e1SAugustin Cavalier 5863fbe39e1SAugustin Cavalier static status_t 5873fbe39e1SAugustin Cavalier fs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat) 5883fbe39e1SAugustin Cavalier { 5893fbe39e1SAugustin Cavalier CALLED(); 5903fbe39e1SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 5913fbe39e1SAugustin Cavalier MutexLocker lock(volume->lock); 5923fbe39e1SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 5933fbe39e1SAugustin Cavalier 5943fbe39e1SAugustin Cavalier ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode); 5953fbe39e1SAugustin Cavalier if (ni == NULL) 5963fbe39e1SAugustin Cavalier return errno; 5973fbe39e1SAugustin Cavalier NtfsInodeCloser niCloser(ni); 5983fbe39e1SAugustin Cavalier 5993fbe39e1SAugustin Cavalier if (ntfs_fuse_getstat(&volume->lowntfs, NULL, ni, stat) != 0) 6003fbe39e1SAugustin Cavalier return errno; 6013fbe39e1SAugustin Cavalier return B_OK; 6023fbe39e1SAugustin Cavalier } 6033fbe39e1SAugustin Cavalier 6043fbe39e1SAugustin Cavalier 6055070f597SAugustin Cavalier static status_t 6065070f597SAugustin Cavalier fs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, uint32 mask) 6075070f597SAugustin Cavalier { 6085070f597SAugustin Cavalier CALLED(); 6095070f597SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 6105070f597SAugustin Cavalier MutexLocker lock(volume->lock); 6115070f597SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 6125070f597SAugustin Cavalier 6135070f597SAugustin Cavalier if ((volume->fs_info_flags & B_FS_IS_READONLY) != 0) 6145070f597SAugustin Cavalier return B_READ_ONLY_DEVICE; 6155070f597SAugustin Cavalier 6165070f597SAugustin Cavalier ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode); 6175070f597SAugustin Cavalier if (ni == NULL) 6185070f597SAugustin Cavalier return B_FILE_ERROR; 6195070f597SAugustin Cavalier NtfsInodeCloser niCloser(ni); 6205070f597SAugustin Cavalier 6215070f597SAugustin Cavalier bool updateTime = false; 6225070f597SAugustin Cavalier const uid_t euid = geteuid(); 6235070f597SAugustin Cavalier 6245070f597SAugustin Cavalier const bool isOwnerOrRoot = (euid == 0 || euid == (uid_t)node->uid); 6255070f597SAugustin Cavalier const bool hasWriteAccess = fs_access(_volume, _node, W_OK); 6265070f597SAugustin Cavalier 6275070f597SAugustin Cavalier if ((mask & B_STAT_SIZE) != 0 && node->size != stat->st_size) { 6285070f597SAugustin Cavalier if ((node->mode & S_IFDIR) != 0) 6295070f597SAugustin Cavalier return B_IS_A_DIRECTORY; 6305070f597SAugustin Cavalier if (!hasWriteAccess) 6315070f597SAugustin Cavalier return B_NOT_ALLOWED; 6325070f597SAugustin Cavalier 6335070f597SAugustin Cavalier ntfs_attr* na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); 6345070f597SAugustin Cavalier if (na == NULL) 6355070f597SAugustin Cavalier return errno; 6365070f597SAugustin Cavalier NtfsAttrCloser naCloser(na); 6375070f597SAugustin Cavalier 6385070f597SAugustin Cavalier if (ntfs_attr_truncate(na, stat->st_size) != 0) 6395070f597SAugustin Cavalier return errno; 6405070f597SAugustin Cavalier node->size = na->data_size; 6415070f597SAugustin Cavalier file_cache_set_size(node->file_cache, node->size); 6425070f597SAugustin Cavalier 6435070f597SAugustin Cavalier updateTime = true; 6445070f597SAugustin Cavalier } 6455070f597SAugustin Cavalier 6465070f597SAugustin Cavalier if ((mask & B_STAT_UID) != 0) { 6475070f597SAugustin Cavalier // only root should be allowed 6485070f597SAugustin Cavalier if (euid != 0) 6495070f597SAugustin Cavalier return B_NOT_ALLOWED; 6505070f597SAugustin Cavalier 6515070f597SAugustin Cavalier // We don't support this (yet.) 6525070f597SAugustin Cavalier if (node->uid != stat->st_uid) 6535070f597SAugustin Cavalier return B_UNSUPPORTED; 6545070f597SAugustin Cavalier } 6555070f597SAugustin Cavalier 6565070f597SAugustin Cavalier if ((mask & B_STAT_GID) != 0) { 6575070f597SAugustin Cavalier // only the user or root can do that 6585070f597SAugustin Cavalier if (!isOwnerOrRoot) 6595070f597SAugustin Cavalier return B_NOT_ALLOWED; 6605070f597SAugustin Cavalier 6615070f597SAugustin Cavalier // We don't support this (yet.) 6625070f597SAugustin Cavalier if (node->gid != stat->st_gid) 6635070f597SAugustin Cavalier return B_UNSUPPORTED; 6645070f597SAugustin Cavalier } 6655070f597SAugustin Cavalier 6665070f597SAugustin Cavalier if ((mask & B_STAT_MODE) != 0) { 6675070f597SAugustin Cavalier // only the user or root can do that 6685070f597SAugustin Cavalier if (!isOwnerOrRoot) 6695070f597SAugustin Cavalier return B_NOT_ALLOWED; 6705070f597SAugustin Cavalier 6715070f597SAugustin Cavalier // We don't support this (yet.) 6725070f597SAugustin Cavalier if (node->mode != stat->st_mode) 6735070f597SAugustin Cavalier return B_UNSUPPORTED; 6745070f597SAugustin Cavalier } 6755070f597SAugustin Cavalier 6765070f597SAugustin Cavalier if ((mask & B_STAT_CREATION_TIME) != 0) { 6775070f597SAugustin Cavalier // the user or root can do that or any user with write access 6785070f597SAugustin Cavalier if (!isOwnerOrRoot && !hasWriteAccess) 6795070f597SAugustin Cavalier return B_NOT_ALLOWED; 6805070f597SAugustin Cavalier 6815070f597SAugustin Cavalier ni->creation_time = timespec2ntfs(stat->st_crtim); 6825070f597SAugustin Cavalier } 6835070f597SAugustin Cavalier 6845070f597SAugustin Cavalier if ((mask & B_STAT_MODIFICATION_TIME) != 0) { 6855070f597SAugustin Cavalier // the user or root can do that or any user with write access 6865070f597SAugustin Cavalier if (!isOwnerOrRoot && !hasWriteAccess) 6875070f597SAugustin Cavalier return B_NOT_ALLOWED; 6885070f597SAugustin Cavalier 6895070f597SAugustin Cavalier ni->last_data_change_time = timespec2ntfs(stat->st_mtim); 6905070f597SAugustin Cavalier } 6915070f597SAugustin Cavalier 6925070f597SAugustin Cavalier if ((mask & B_STAT_CHANGE_TIME) != 0 || updateTime) { 6935070f597SAugustin Cavalier // the user or root can do that or any user with write access 6945070f597SAugustin Cavalier if (!isOwnerOrRoot && !hasWriteAccess) 6955070f597SAugustin Cavalier return B_NOT_ALLOWED; 6965070f597SAugustin Cavalier 6975070f597SAugustin Cavalier ni->last_mft_change_time = timespec2ntfs(stat->st_ctim); 6985070f597SAugustin Cavalier } 6995070f597SAugustin Cavalier 7005070f597SAugustin Cavalier if ((mask & B_STAT_ACCESS_TIME) != 0) { 7015070f597SAugustin Cavalier // the user or root can do that or any user with write access 7025070f597SAugustin Cavalier if (!isOwnerOrRoot && !hasWriteAccess) 7035070f597SAugustin Cavalier return B_NOT_ALLOWED; 7045070f597SAugustin Cavalier 7055070f597SAugustin Cavalier ni->last_access_time = timespec2ntfs(stat->st_atim); 7065070f597SAugustin Cavalier } 7075070f597SAugustin Cavalier 7085070f597SAugustin Cavalier ntfs_inode_mark_dirty(ni); 7095070f597SAugustin Cavalier 7105070f597SAugustin Cavalier notify_stat_changed(_volume->id, node->parent_inode, node->inode, mask); 7115070f597SAugustin Cavalier return B_OK; 7125070f597SAugustin Cavalier } 7135070f597SAugustin Cavalier 7145070f597SAugustin Cavalier 7153fbe39e1SAugustin Cavalier static inline int 7163fbe39e1SAugustin Cavalier open_mode_to_access(int openMode) 7173fbe39e1SAugustin Cavalier { 7183fbe39e1SAugustin Cavalier if ((openMode & O_RWMASK) == O_RDONLY) 7193fbe39e1SAugustin Cavalier return R_OK; 7203fbe39e1SAugustin Cavalier if ((openMode & O_RWMASK) == O_WRONLY) 7213fbe39e1SAugustin Cavalier return W_OK; 7223fbe39e1SAugustin Cavalier if ((openMode & O_RWMASK) == O_RDWR) 7233fbe39e1SAugustin Cavalier return R_OK | W_OK; 7243fbe39e1SAugustin Cavalier return 0; 7253fbe39e1SAugustin Cavalier } 7263fbe39e1SAugustin Cavalier 7273fbe39e1SAugustin Cavalier 7283fbe39e1SAugustin Cavalier static status_t 7295070f597SAugustin Cavalier fs_generic_create(fs_volume* _volume, vnode* directory, const char* name, int mode, 7305070f597SAugustin Cavalier ino_t* _inode) 7315070f597SAugustin Cavalier { 7325070f597SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 7335070f597SAugustin Cavalier ASSERT_LOCKED_MUTEX(&volume->lock); 7345070f597SAugustin Cavalier 7355070f597SAugustin Cavalier if ((directory->mode & S_IFDIR) == 0) 7365070f597SAugustin Cavalier return B_BAD_TYPE; 7375070f597SAugustin Cavalier 7385070f597SAugustin Cavalier ino_t inode = -1; 7395070f597SAugustin Cavalier if (ntfs_fuse_create(&volume->lowntfs, directory->inode, name, mode & (S_IFMT | 07777), 7405070f597SAugustin Cavalier 0, (char*)NULL, &inode) != 0) 7415070f597SAugustin Cavalier return errno; 7425070f597SAugustin Cavalier 7435070f597SAugustin Cavalier vnode* node; 7445070f597SAugustin Cavalier status_t status = fs_init_vnode(_volume, directory->inode, inode, &node, true); 7455070f597SAugustin Cavalier if (status != B_OK) 7465070f597SAugustin Cavalier return status; 7475070f597SAugustin Cavalier 7485070f597SAugustin Cavalier entry_cache_add(_volume->id, directory->inode, name, inode); 7495070f597SAugustin Cavalier notify_entry_created(_volume->id, directory->inode, name, inode); 7505070f597SAugustin Cavalier *_inode = inode; 7515070f597SAugustin Cavalier return B_OK; 7525070f597SAugustin Cavalier } 7535070f597SAugustin Cavalier 7545070f597SAugustin Cavalier 7555070f597SAugustin Cavalier static status_t 7565070f597SAugustin Cavalier fs_create(fs_volume* _volume, fs_vnode* _directory, const char* name, 7575070f597SAugustin Cavalier int openMode, int mode, void** _cookie, ino_t* _vnodeID) 7585070f597SAugustin Cavalier { 7595070f597SAugustin Cavalier CALLED(); 7605070f597SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 7615070f597SAugustin Cavalier MutexLocker lock(volume->lock); 7625070f597SAugustin Cavalier vnode* directory = (vnode*)_directory->private_node; 7635070f597SAugustin Cavalier 7645070f597SAugustin Cavalier if ((directory->mode & S_IFDIR) == 0) 7655070f597SAugustin Cavalier return B_NOT_A_DIRECTORY; 7665070f597SAugustin Cavalier 7675070f597SAugustin Cavalier status_t status = fs_access(_volume, _directory, W_OK); 7685070f597SAugustin Cavalier if (status != B_OK) 7695070f597SAugustin Cavalier return status; 7705070f597SAugustin Cavalier 7715070f597SAugustin Cavalier #if 1 7725070f597SAugustin Cavalier if ((openMode & O_NOCACHE) != 0) 7735070f597SAugustin Cavalier return B_UNSUPPORTED; 7745070f597SAugustin Cavalier #endif 7755070f597SAugustin Cavalier 7765070f597SAugustin Cavalier status = fs_generic_create(_volume, directory, name, S_IFREG | (mode & 07777), _vnodeID); 7775070f597SAugustin Cavalier if (status != B_OK) 7785070f597SAugustin Cavalier return status; 7795070f597SAugustin Cavalier 7805070f597SAugustin Cavalier file_cookie* cookie = new file_cookie; 7815070f597SAugustin Cavalier if (cookie == NULL) 7825070f597SAugustin Cavalier return B_NO_MEMORY; 7835070f597SAugustin Cavalier ObjectDeleter<file_cookie> cookieDeleter(cookie); 7845070f597SAugustin Cavalier 7855070f597SAugustin Cavalier cookie->open_mode = openMode; 7865070f597SAugustin Cavalier 7875070f597SAugustin Cavalier cookieDeleter.Detach(); 7885070f597SAugustin Cavalier *_cookie = cookie; 7895070f597SAugustin Cavalier return B_OK; 7905070f597SAugustin Cavalier } 7915070f597SAugustin Cavalier 7925070f597SAugustin Cavalier 7935070f597SAugustin Cavalier static status_t 7943fbe39e1SAugustin Cavalier fs_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie) 7953fbe39e1SAugustin Cavalier { 7963fbe39e1SAugustin Cavalier CALLED(); 7973fbe39e1SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 7983fbe39e1SAugustin Cavalier MutexLocker lock(volume->lock); 7993fbe39e1SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 8003fbe39e1SAugustin Cavalier 8013fbe39e1SAugustin Cavalier // opening a directory read-only is allowed (but no data can be read) 8023fbe39e1SAugustin Cavalier if ((node->mode & S_IFDIR) != 0 && (openMode & O_RWMASK) != 0) 8033fbe39e1SAugustin Cavalier return B_IS_A_DIRECTORY; 8043fbe39e1SAugustin Cavalier if ((openMode & O_DIRECTORY) != 0 && (node->mode & S_IFDIR) == 0) 8053fbe39e1SAugustin Cavalier return B_NOT_A_DIRECTORY; 8063fbe39e1SAugustin Cavalier 8073fbe39e1SAugustin Cavalier status_t status = fs_access(_volume, _node, open_mode_to_access(openMode)); 8083fbe39e1SAugustin Cavalier if (status != B_OK) 8093fbe39e1SAugustin Cavalier return status; 8103fbe39e1SAugustin Cavalier 8113fbe39e1SAugustin Cavalier ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode); 8123fbe39e1SAugustin Cavalier if (ni == NULL) 8133fbe39e1SAugustin Cavalier return errno; 8143fbe39e1SAugustin Cavalier NtfsInodeCloser niCloser(ni); 8153fbe39e1SAugustin Cavalier 8163fbe39e1SAugustin Cavalier file_cookie* cookie = new file_cookie; 8173fbe39e1SAugustin Cavalier if (cookie == NULL) 8183fbe39e1SAugustin Cavalier return B_NO_MEMORY; 8193fbe39e1SAugustin Cavalier ObjectDeleter<file_cookie> cookieDeleter(cookie); 8203fbe39e1SAugustin Cavalier 8213fbe39e1SAugustin Cavalier cookie->open_mode = openMode; 8223fbe39e1SAugustin Cavalier 8233fbe39e1SAugustin Cavalier // We don't actually support uncached mode; it would require us to handle 8243fbe39e1SAugustin Cavalier // passing user buffers to libntfs, among other things. 8253fbe39e1SAugustin Cavalier #if 0 8263fbe39e1SAugustin Cavalier if ((openMode & O_NOCACHE) != 0 && node->file_cache != NULL) { 8273fbe39e1SAugustin Cavalier status_t status = file_cache_disable(node->file_cache); 8283fbe39e1SAugustin Cavalier if (status != B_OK) 8293fbe39e1SAugustin Cavalier return status; 8303fbe39e1SAugustin Cavalier } 8313fbe39e1SAugustin Cavalier #else 8323fbe39e1SAugustin Cavalier if ((openMode & O_NOCACHE) != 0) 8333fbe39e1SAugustin Cavalier return B_UNSUPPORTED; 8343fbe39e1SAugustin Cavalier #endif 8353fbe39e1SAugustin Cavalier 8365070f597SAugustin Cavalier if ((openMode & O_TRUNC) != 0) { 8375070f597SAugustin Cavalier if ((openMode & O_RWMASK) == O_RDONLY) 8385070f597SAugustin Cavalier return B_NOT_ALLOWED; 8395070f597SAugustin Cavalier 8405070f597SAugustin Cavalier ntfs_attr* na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); 8415070f597SAugustin Cavalier if (na == NULL) 8425070f597SAugustin Cavalier return errno; 8435070f597SAugustin Cavalier NtfsAttrCloser naCloser(na); 8445070f597SAugustin Cavalier 8455070f597SAugustin Cavalier if (ntfs_attr_truncate(na, 0) != 0) 8465070f597SAugustin Cavalier return errno; 8475070f597SAugustin Cavalier node->size = na->data_size; 8485070f597SAugustin Cavalier file_cache_set_size(node->file_cache, node->size); 8495070f597SAugustin Cavalier } 8505070f597SAugustin Cavalier 8513fbe39e1SAugustin Cavalier cookieDeleter.Detach(); 8523fbe39e1SAugustin Cavalier *_cookie = cookie; 8533fbe39e1SAugustin Cavalier return B_OK; 8543fbe39e1SAugustin Cavalier } 8553fbe39e1SAugustin Cavalier 8563fbe39e1SAugustin Cavalier 8573fbe39e1SAugustin Cavalier static status_t 8583fbe39e1SAugustin Cavalier fs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, 8593fbe39e1SAugustin Cavalier void* buffer, size_t* length) 8603fbe39e1SAugustin Cavalier { 8613fbe39e1SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 8623fbe39e1SAugustin Cavalier file_cookie* cookie = (file_cookie*)_cookie; 8633fbe39e1SAugustin Cavalier 8643fbe39e1SAugustin Cavalier if ((node->mode & S_IFDIR) != 0) 8653fbe39e1SAugustin Cavalier return B_IS_A_DIRECTORY; 8663fbe39e1SAugustin Cavalier 8673fbe39e1SAugustin Cavalier ASSERT((cookie->open_mode & O_RWMASK) == O_RDONLY || (cookie->open_mode & O_RDWR) != 0); 8683fbe39e1SAugustin Cavalier 8693fbe39e1SAugustin Cavalier return file_cache_read(node->file_cache, cookie, pos, buffer, length); 8703fbe39e1SAugustin Cavalier } 8713fbe39e1SAugustin Cavalier 8723fbe39e1SAugustin Cavalier 8733fbe39e1SAugustin Cavalier static status_t 8745070f597SAugustin Cavalier fs_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, 8755070f597SAugustin Cavalier const void* buffer, size_t* _length) 8765070f597SAugustin Cavalier { 8775070f597SAugustin Cavalier CALLED(); 8785070f597SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 8795070f597SAugustin Cavalier MutexLocker lock(volume->lock); 8805070f597SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 8815070f597SAugustin Cavalier file_cookie* cookie = (file_cookie*)_cookie; 8825070f597SAugustin Cavalier 8835070f597SAugustin Cavalier if ((node->mode & S_IFDIR) != 0) 8845070f597SAugustin Cavalier return B_IS_A_DIRECTORY; 8855070f597SAugustin Cavalier 8865070f597SAugustin Cavalier ASSERT((cookie->open_mode & O_WRONLY) != 0 || (cookie->open_mode & O_RDWR) != 0); 8875070f597SAugustin Cavalier 8885070f597SAugustin Cavalier if (cookie->open_mode & O_APPEND) 8895070f597SAugustin Cavalier pos = node->size; 8905070f597SAugustin Cavalier if (pos < 0) 8915070f597SAugustin Cavalier return B_BAD_VALUE; 8925070f597SAugustin Cavalier 8935070f597SAugustin Cavalier if (pos + s64(*_length) > node->size) { 8945070f597SAugustin Cavalier ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode); 8955070f597SAugustin Cavalier if (ni == NULL) 8965070f597SAugustin Cavalier return errno; 8975070f597SAugustin Cavalier NtfsInodeCloser niCloser(ni); 8985070f597SAugustin Cavalier 8995070f597SAugustin Cavalier ntfs_attr* na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); 9005070f597SAugustin Cavalier if (na == NULL) 9015070f597SAugustin Cavalier return errno; 9025070f597SAugustin Cavalier NtfsAttrCloser naCloser(na); 9035070f597SAugustin Cavalier 9045070f597SAugustin Cavalier if (ntfs_attr_truncate(na, pos + *_length) != 0) 9055070f597SAugustin Cavalier return errno; 9065070f597SAugustin Cavalier node->size = na->data_size; 9075070f597SAugustin Cavalier file_cache_set_size(node->file_cache, node->size); 9085070f597SAugustin Cavalier } 9095070f597SAugustin Cavalier 9105070f597SAugustin Cavalier lock.Unlock(); 9115070f597SAugustin Cavalier 9125070f597SAugustin Cavalier status_t status = file_cache_write(node->file_cache, cookie, pos, buffer, _length); 9135070f597SAugustin Cavalier if (status != B_OK) 9145070f597SAugustin Cavalier return status; 9155070f597SAugustin Cavalier 9165070f597SAugustin Cavalier lock.Lock(); 9175070f597SAugustin Cavalier 9185070f597SAugustin Cavalier // periodically notify if the file size has changed 9195070f597SAugustin Cavalier if ((node->lowntfs_close_state & CLOSE_GHOST) == 0 9205070f597SAugustin Cavalier && cookie->last_size != node->size 9215070f597SAugustin Cavalier && system_time() > cookie->last_notification + INODE_NOTIFICATION_INTERVAL) { 9225070f597SAugustin Cavalier notify_stat_changed(_volume->id, node->parent_inode, node->inode, 9235070f597SAugustin Cavalier B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE); 9245070f597SAugustin Cavalier cookie->last_size = node->size; 9255070f597SAugustin Cavalier cookie->last_notification = system_time(); 9265070f597SAugustin Cavalier } 9275070f597SAugustin Cavalier return status; 9285070f597SAugustin Cavalier } 9295070f597SAugustin Cavalier 9305070f597SAugustin Cavalier 9315070f597SAugustin Cavalier static status_t 9325070f597SAugustin Cavalier fs_fsync(fs_volume* _volume, fs_vnode* _node) 9335070f597SAugustin Cavalier { 9345070f597SAugustin Cavalier CALLED(); 9355070f597SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 9365070f597SAugustin Cavalier 9375070f597SAugustin Cavalier return file_cache_sync(node->file_cache); 9385070f597SAugustin Cavalier } 9395070f597SAugustin Cavalier 9405070f597SAugustin Cavalier 9415070f597SAugustin Cavalier static status_t 9423fbe39e1SAugustin Cavalier fs_close(fs_volume *_volume, fs_vnode *_node, void *_cookie) 9433fbe39e1SAugustin Cavalier { 9443fbe39e1SAugustin Cavalier CALLED(); 9453fbe39e1SAugustin Cavalier 9463fbe39e1SAugustin Cavalier // Nothing to do. 9473fbe39e1SAugustin Cavalier return B_OK; 9483fbe39e1SAugustin Cavalier } 9493fbe39e1SAugustin Cavalier 9503fbe39e1SAugustin Cavalier 9513fbe39e1SAugustin Cavalier static status_t 9523fbe39e1SAugustin Cavalier fs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 9533fbe39e1SAugustin Cavalier { 9543fbe39e1SAugustin Cavalier file_cookie* cookie = (file_cookie*)_cookie; 9553fbe39e1SAugustin Cavalier delete cookie; 9563fbe39e1SAugustin Cavalier return B_OK; 9573fbe39e1SAugustin Cavalier } 9583fbe39e1SAugustin Cavalier 9593fbe39e1SAugustin Cavalier 9603fbe39e1SAugustin Cavalier static status_t 9615070f597SAugustin Cavalier fs_generic_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name, RM_TYPES type) 9625070f597SAugustin Cavalier { 9635070f597SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 9645070f597SAugustin Cavalier MutexLocker lock(volume->lock); 9655070f597SAugustin Cavalier vnode* directory = (vnode*)_directory->private_node; 9665070f597SAugustin Cavalier 9675070f597SAugustin Cavalier status_t status = fs_access(_volume, _directory, W_OK); 9685070f597SAugustin Cavalier if (status != B_OK) 9695070f597SAugustin Cavalier return status; 9705070f597SAugustin Cavalier 9715070f597SAugustin Cavalier if (ntfs_fuse_rm(&volume->lowntfs, directory->inode, name, type) != 0) 9725070f597SAugustin Cavalier return errno; 9735070f597SAugustin Cavalier 9745070f597SAugustin Cavalier // remove_vnode() et al. will be called by put_close_state. 9755070f597SAugustin Cavalier return B_OK; 9765070f597SAugustin Cavalier } 9775070f597SAugustin Cavalier 9785070f597SAugustin Cavalier 9795070f597SAugustin Cavalier static status_t 9805070f597SAugustin Cavalier fs_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name) 9815070f597SAugustin Cavalier { 9825070f597SAugustin Cavalier CALLED(); 9835070f597SAugustin Cavalier return fs_generic_unlink(_volume, _directory, name, RM_LINK); 9845070f597SAugustin Cavalier } 9855070f597SAugustin Cavalier 9865070f597SAugustin Cavalier 9875070f597SAugustin Cavalier static status_t 9885070f597SAugustin Cavalier fs_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName, 9895070f597SAugustin Cavalier fs_vnode* _newDir, const char* newName) 9905070f597SAugustin Cavalier { 9915070f597SAugustin Cavalier CALLED(); 9925070f597SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 9935070f597SAugustin Cavalier MutexLocker lock(volume->lock); 9945070f597SAugustin Cavalier 9955070f597SAugustin Cavalier vnode* old_directory = (vnode*)_oldDir->private_node; 9965070f597SAugustin Cavalier vnode* new_directory = (vnode*)_newDir->private_node; 9975070f597SAugustin Cavalier 9985070f597SAugustin Cavalier if (old_directory == new_directory && strcmp(oldName, newName) == 0) 9995070f597SAugustin Cavalier return B_OK; 10005070f597SAugustin Cavalier 10015070f597SAugustin Cavalier status_t status = fs_access(_volume, _oldDir, W_OK); 10025070f597SAugustin Cavalier if (status == B_OK) 10035070f597SAugustin Cavalier status = fs_access(_volume, _newDir, W_OK); 10045070f597SAugustin Cavalier if (status != B_OK) 10055070f597SAugustin Cavalier return status; 10065070f597SAugustin Cavalier 100791337305SAugustin Cavalier // Prevent moving a directory into one of its own children. 100891337305SAugustin Cavalier if (old_directory != new_directory) { 100991337305SAugustin Cavalier u64 oldIno = ntfs_fuse_inode_lookup(&volume->lowntfs, old_directory->inode, oldName); 101091337305SAugustin Cavalier if (oldIno == (u64)-1) 101191337305SAugustin Cavalier return B_ENTRY_NOT_FOUND; 101291337305SAugustin Cavalier 101391337305SAugustin Cavalier ino_t parent = new_directory->inode; 101491337305SAugustin Cavalier const ino_t root = FILE_root; 101591337305SAugustin Cavalier 101691337305SAugustin Cavalier while (true) { 101791337305SAugustin Cavalier if (parent == oldIno) 101891337305SAugustin Cavalier return B_BAD_VALUE; 101991337305SAugustin Cavalier else if (parent == root || parent == old_directory->inode) 102091337305SAugustin Cavalier break; 102191337305SAugustin Cavalier 102291337305SAugustin Cavalier vnode* parentNode; 102391337305SAugustin Cavalier if (get_vnode(_volume, parent, (void**)&parentNode) != B_OK) 102491337305SAugustin Cavalier return B_ERROR; 102591337305SAugustin Cavalier 102691337305SAugustin Cavalier parent = parentNode->parent_inode; 102791337305SAugustin Cavalier put_vnode(_volume, parentNode->inode); 102891337305SAugustin Cavalier } 102991337305SAugustin Cavalier } 103091337305SAugustin Cavalier 10315070f597SAugustin Cavalier if (ntfs_fuse_rename(&volume->lowntfs, old_directory->inode, oldName, 10325070f597SAugustin Cavalier new_directory->inode, newName) != 0) 10335070f597SAugustin Cavalier return errno; 10345070f597SAugustin Cavalier 10355070f597SAugustin Cavalier u64 ino = ntfs_fuse_inode_lookup(&volume->lowntfs, new_directory->inode, newName); 10365070f597SAugustin Cavalier if (ino == (u64)-1) 10375070f597SAugustin Cavalier return B_ENTRY_NOT_FOUND; 10385070f597SAugustin Cavalier 10395070f597SAugustin Cavalier vnode* node; 10405070f597SAugustin Cavalier status = get_vnode(_volume, ino, (void**)&node); 10415070f597SAugustin Cavalier if (status != B_OK) 10425070f597SAugustin Cavalier return status; 10435070f597SAugustin Cavalier 10445070f597SAugustin Cavalier free(node->name); 10455070f597SAugustin Cavalier node->name = strdup(newName); 10465070f597SAugustin Cavalier node->parent_inode = new_directory->inode; 10475070f597SAugustin Cavalier 10485070f597SAugustin Cavalier if ((node->mode & S_IFDIR) != 0) 10495070f597SAugustin Cavalier entry_cache_add(_volume->id, ino, "..", new_directory->inode); 10505070f597SAugustin Cavalier 10515070f597SAugustin Cavalier put_vnode(_volume, ino); 10525070f597SAugustin Cavalier 10535070f597SAugustin Cavalier entry_cache_remove(_volume->id, old_directory->inode, oldName); 10545070f597SAugustin Cavalier entry_cache_add(_volume->id, new_directory->inode, newName, ino); 10555070f597SAugustin Cavalier notify_entry_moved(_volume->id, old_directory->inode, oldName, 10565070f597SAugustin Cavalier new_directory->inode, newName, ino); 10575070f597SAugustin Cavalier 10585070f597SAugustin Cavalier return B_OK; 10595070f597SAugustin Cavalier } 10605070f597SAugustin Cavalier 10615070f597SAugustin Cavalier 10625070f597SAugustin Cavalier static status_t 10633fbe39e1SAugustin Cavalier fs_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 10643fbe39e1SAugustin Cavalier { 10653fbe39e1SAugustin Cavalier // CALLED(); 10663fbe39e1SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 10673fbe39e1SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 10683fbe39e1SAugustin Cavalier 10693fbe39e1SAugustin Cavalier if ((accessMode & W_OK) != 0 && (volume->fs_info_flags & B_FS_IS_READONLY) != 0) 10703fbe39e1SAugustin Cavalier return B_READ_ONLY_DEVICE; 10713fbe39e1SAugustin Cavalier 10723fbe39e1SAugustin Cavalier return check_access_permissions(accessMode, node->mode, node->gid, node->uid); 10733fbe39e1SAugustin Cavalier } 10743fbe39e1SAugustin Cavalier 10753fbe39e1SAugustin Cavalier 10763fbe39e1SAugustin Cavalier static status_t 10773fbe39e1SAugustin Cavalier fs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer, size_t* bufferSize) 10783fbe39e1SAugustin Cavalier { 10793fbe39e1SAugustin Cavalier CALLED(); 10803fbe39e1SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 10813fbe39e1SAugustin Cavalier MutexLocker lock(volume->lock); 10823fbe39e1SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 10833fbe39e1SAugustin Cavalier 10843fbe39e1SAugustin Cavalier if (ntfs_fuse_readlink(&volume->lowntfs, node->inode, buffer, bufferSize) != 0) 10853fbe39e1SAugustin Cavalier return errno; 10863fbe39e1SAugustin Cavalier return B_OK; 10873fbe39e1SAugustin Cavalier } 10883fbe39e1SAugustin Cavalier 10893fbe39e1SAugustin Cavalier 10903fbe39e1SAugustin Cavalier // #pragma mark - Directory functions 10913fbe39e1SAugustin Cavalier 10923fbe39e1SAugustin Cavalier 10933fbe39e1SAugustin Cavalier static status_t 10945070f597SAugustin Cavalier fs_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name, int mode) 10955070f597SAugustin Cavalier { 10965070f597SAugustin Cavalier CALLED(); 10975070f597SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 10985070f597SAugustin Cavalier MutexLocker lock(volume->lock); 10995070f597SAugustin Cavalier vnode* directory = (vnode*)_directory->private_node; 11005070f597SAugustin Cavalier 11015070f597SAugustin Cavalier status_t status = fs_access(_volume, _directory, W_OK); 11025070f597SAugustin Cavalier if (status != B_OK) 11035070f597SAugustin Cavalier return status; 11045070f597SAugustin Cavalier 11055070f597SAugustin Cavalier ino_t inode = -1; 11065070f597SAugustin Cavalier status = fs_generic_create(_volume, directory, name, S_IFDIR | (mode & 07777), &inode); 11075070f597SAugustin Cavalier if (status != B_OK) 11085070f597SAugustin Cavalier return status; 11095070f597SAugustin Cavalier 11105070f597SAugustin Cavalier return B_OK; 11115070f597SAugustin Cavalier } 11125070f597SAugustin Cavalier 11135070f597SAugustin Cavalier 11145070f597SAugustin Cavalier static status_t 11155070f597SAugustin Cavalier fs_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name) 11165070f597SAugustin Cavalier { 11175070f597SAugustin Cavalier CALLED(); 11185070f597SAugustin Cavalier ino_t directory_inode = ((vnode*)_directory->private_node)->inode; 11195070f597SAugustin Cavalier status_t status = fs_generic_unlink(_volume, _directory, name, RM_DIR); 11205070f597SAugustin Cavalier if (status != B_OK) 11215070f597SAugustin Cavalier return status; 11225070f597SAugustin Cavalier 11235070f597SAugustin Cavalier entry_cache_remove(_volume->id, directory_inode, ".."); 11245070f597SAugustin Cavalier return B_OK; 11255070f597SAugustin Cavalier } 11265070f597SAugustin Cavalier 11275070f597SAugustin Cavalier 11285070f597SAugustin Cavalier static status_t 11293fbe39e1SAugustin Cavalier fs_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie) 11303fbe39e1SAugustin Cavalier { 11313fbe39e1SAugustin Cavalier CALLED(); 11323fbe39e1SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 11333fbe39e1SAugustin Cavalier 11343fbe39e1SAugustin Cavalier status_t status = fs_access(_volume, _node, R_OK); 11353fbe39e1SAugustin Cavalier if (status != B_OK) 11363fbe39e1SAugustin Cavalier return status; 11373fbe39e1SAugustin Cavalier 11383fbe39e1SAugustin Cavalier if ((node->mode & S_IFDIR) == 0) 11393fbe39e1SAugustin Cavalier return B_NOT_A_DIRECTORY; 11403fbe39e1SAugustin Cavalier 11413fbe39e1SAugustin Cavalier directory_cookie* cookie = new directory_cookie; 11423fbe39e1SAugustin Cavalier if (cookie == NULL) 11433fbe39e1SAugustin Cavalier return B_NO_MEMORY; 11443fbe39e1SAugustin Cavalier 11453fbe39e1SAugustin Cavalier cookie->first = cookie->current = NULL; 11463fbe39e1SAugustin Cavalier *_cookie = (void*)cookie; 11473fbe39e1SAugustin Cavalier return B_OK; 11483fbe39e1SAugustin Cavalier } 11493fbe39e1SAugustin Cavalier 11503fbe39e1SAugustin Cavalier 11513fbe39e1SAugustin Cavalier static int 11523fbe39e1SAugustin Cavalier _ntfs_readdir_callback(void* _cookie, const ntfschar* ntfs_name, const int ntfs_name_len, 11533fbe39e1SAugustin Cavalier const int name_type, const s64 pos, const MFT_REF mref, const unsigned dt_type) 11543fbe39e1SAugustin Cavalier { 11553fbe39e1SAugustin Cavalier if (name_type == FILE_NAME_DOS) 11563fbe39e1SAugustin Cavalier return 0; 11573fbe39e1SAugustin Cavalier 11583fbe39e1SAugustin Cavalier directory_cookie* cookie = (directory_cookie*)_cookie; 11593fbe39e1SAugustin Cavalier 11603fbe39e1SAugustin Cavalier char* name = NULL; 11613fbe39e1SAugustin Cavalier int name_len = ntfs_ucstombs(ntfs_name, ntfs_name_len, &name, 0); 11623fbe39e1SAugustin Cavalier if (name_len <= 0 || name == NULL) 11633fbe39e1SAugustin Cavalier return -1; 11643fbe39e1SAugustin Cavalier MemoryDeleter nameDeleter(name); 11653fbe39e1SAugustin Cavalier 11663fbe39e1SAugustin Cavalier directory_cookie::entry* entry = 11673fbe39e1SAugustin Cavalier (directory_cookie::entry*)malloc(sizeof(directory_cookie::entry) + name_len); 11683fbe39e1SAugustin Cavalier if (entry == NULL) 11693fbe39e1SAugustin Cavalier return -1; 11703fbe39e1SAugustin Cavalier entry->next = NULL; 11713fbe39e1SAugustin Cavalier 11723fbe39e1SAugustin Cavalier entry->inode = MREF(mref); 11733fbe39e1SAugustin Cavalier entry->name_length = name_len; 11743fbe39e1SAugustin Cavalier memcpy(entry->name, name, name_len + 1); 11753fbe39e1SAugustin Cavalier 11763fbe39e1SAugustin Cavalier if (cookie->first == NULL) { 11773fbe39e1SAugustin Cavalier cookie->first = cookie->current = entry; 11783fbe39e1SAugustin Cavalier } else { 11793fbe39e1SAugustin Cavalier cookie->current->next = entry; 11803fbe39e1SAugustin Cavalier cookie->current = entry; 11813fbe39e1SAugustin Cavalier } 11823fbe39e1SAugustin Cavalier 11833fbe39e1SAugustin Cavalier return 0; 11843fbe39e1SAugustin Cavalier } 11853fbe39e1SAugustin Cavalier 11863fbe39e1SAugustin Cavalier 11873fbe39e1SAugustin Cavalier static status_t 11883fbe39e1SAugustin Cavalier fs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie, 11893fbe39e1SAugustin Cavalier struct dirent* dirent, size_t bufferSize, uint32* _num) 11903fbe39e1SAugustin Cavalier { 11913fbe39e1SAugustin Cavalier CALLED(); 11923fbe39e1SAugustin Cavalier directory_cookie* cookie = (directory_cookie*)_cookie; 11933fbe39e1SAugustin Cavalier 11943fbe39e1SAugustin Cavalier // TODO: While lowntfs-3g seems to also read the entire directory at once into memory, 11953fbe39e1SAugustin Cavalier // we could optimize things here by storing the data in the vnode, not the inode, and 11963fbe39e1SAugustin Cavalier // only freeing it after some period of inactivity. 11973fbe39e1SAugustin Cavalier 11983fbe39e1SAugustin Cavalier // See if we need to read the directory ourselves first. 11993fbe39e1SAugustin Cavalier if (cookie->first == NULL) { 12003fbe39e1SAugustin Cavalier volume* volume = (struct volume*)_volume->private_volume; 12013fbe39e1SAugustin Cavalier MutexLocker lock(volume->lock); 12023fbe39e1SAugustin Cavalier vnode* node = (vnode*)_node->private_node; 12033fbe39e1SAugustin Cavalier 12043fbe39e1SAugustin Cavalier ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode); 12053fbe39e1SAugustin Cavalier if (ni == NULL) 12063fbe39e1SAugustin Cavalier return errno; 12073fbe39e1SAugustin Cavalier NtfsInodeCloser niCloser(ni); 12083fbe39e1SAugustin Cavalier 12093fbe39e1SAugustin Cavalier s64 pos = 0; 12103fbe39e1SAugustin Cavalier if (ntfs_readdir(ni, &pos, cookie, _ntfs_readdir_callback) != 0) 12113fbe39e1SAugustin Cavalier return errno; 12123fbe39e1SAugustin Cavalier cookie->current = cookie->first; 12133fbe39e1SAugustin Cavalier } 12143fbe39e1SAugustin Cavalier if (cookie->first == NULL) 12153fbe39e1SAugustin Cavalier return ENOENT; 12163fbe39e1SAugustin Cavalier if (cookie->current == NULL) { 12173fbe39e1SAugustin Cavalier *_num = 0; 12183fbe39e1SAugustin Cavalier return B_OK; 12193fbe39e1SAugustin Cavalier } 12203fbe39e1SAugustin Cavalier 12213fbe39e1SAugustin Cavalier uint32 maxCount = *_num; 12223fbe39e1SAugustin Cavalier uint32 count = 0; 12233fbe39e1SAugustin Cavalier while (count < maxCount && bufferSize > sizeof(struct dirent)) { 12243fbe39e1SAugustin Cavalier size_t length = bufferSize - sizeof(struct dirent); 12253fbe39e1SAugustin Cavalier if (length < cookie->current->name_length) { 12263fbe39e1SAugustin Cavalier // the remaining name buffer length is too small 12273fbe39e1SAugustin Cavalier if (count == 0) 12283fbe39e1SAugustin Cavalier return B_BUFFER_OVERFLOW; 12293fbe39e1SAugustin Cavalier break; 12303fbe39e1SAugustin Cavalier } 12313fbe39e1SAugustin Cavalier length = cookie->current->name_length; 12323fbe39e1SAugustin Cavalier 12333fbe39e1SAugustin Cavalier dirent->d_dev = _volume->id; 12343fbe39e1SAugustin Cavalier dirent->d_ino = cookie->current->inode; 12353fbe39e1SAugustin Cavalier strlcpy(dirent->d_name, cookie->current->name, length + 1); 12363fbe39e1SAugustin Cavalier dirent->d_reclen = sizeof(struct dirent) + length; 12373fbe39e1SAugustin Cavalier 12383fbe39e1SAugustin Cavalier bufferSize -= dirent->d_reclen; 12393fbe39e1SAugustin Cavalier dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen); 12403fbe39e1SAugustin Cavalier count++; 12413fbe39e1SAugustin Cavalier 12423fbe39e1SAugustin Cavalier cookie->current = cookie->current->next; 12433fbe39e1SAugustin Cavalier if (cookie->current == NULL) 12443fbe39e1SAugustin Cavalier break; 12453fbe39e1SAugustin Cavalier } 12463fbe39e1SAugustin Cavalier 12473fbe39e1SAugustin Cavalier *_num = count; 12483fbe39e1SAugustin Cavalier return B_OK; 12493fbe39e1SAugustin Cavalier } 12503fbe39e1SAugustin Cavalier 12513fbe39e1SAugustin Cavalier 12523fbe39e1SAugustin Cavalier static status_t 12533fbe39e1SAugustin Cavalier fs_rewind_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void *_cookie) 12543fbe39e1SAugustin Cavalier { 12553fbe39e1SAugustin Cavalier CALLED(); 12563fbe39e1SAugustin Cavalier directory_cookie* cookie = (directory_cookie*)_cookie; 12573fbe39e1SAugustin Cavalier cookie->current = cookie->first; 12583fbe39e1SAugustin Cavalier return B_OK; 12593fbe39e1SAugustin Cavalier } 12603fbe39e1SAugustin Cavalier 12613fbe39e1SAugustin Cavalier 12623fbe39e1SAugustin Cavalier static status_t 12633fbe39e1SAugustin Cavalier fs_close_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* /*_cookie*/) 12643fbe39e1SAugustin Cavalier { 12653fbe39e1SAugustin Cavalier return B_OK; 12663fbe39e1SAugustin Cavalier } 12673fbe39e1SAugustin Cavalier 12683fbe39e1SAugustin Cavalier 12693fbe39e1SAugustin Cavalier static status_t 12703fbe39e1SAugustin Cavalier fs_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 12713fbe39e1SAugustin Cavalier { 12723fbe39e1SAugustin Cavalier CALLED(); 12733fbe39e1SAugustin Cavalier directory_cookie* cookie = (directory_cookie*)_cookie; 12743fbe39e1SAugustin Cavalier if (cookie == NULL) 12753fbe39e1SAugustin Cavalier return B_OK; 12763fbe39e1SAugustin Cavalier 12773fbe39e1SAugustin Cavalier // Free entries. 12783fbe39e1SAugustin Cavalier cookie->current = cookie->first; 12793fbe39e1SAugustin Cavalier while (cookie->current != NULL) { 12803fbe39e1SAugustin Cavalier directory_cookie::entry* next = cookie->current->next; 12813fbe39e1SAugustin Cavalier free(cookie->current); 12823fbe39e1SAugustin Cavalier cookie->current = next; 12833fbe39e1SAugustin Cavalier } 12843fbe39e1SAugustin Cavalier 12853fbe39e1SAugustin Cavalier delete cookie; 12863fbe39e1SAugustin Cavalier return B_OK; 12873fbe39e1SAugustin Cavalier } 12883fbe39e1SAugustin Cavalier 12893fbe39e1SAugustin Cavalier 12903fbe39e1SAugustin Cavalier fs_volume_ops gNtfsVolumeOps = { 12913fbe39e1SAugustin Cavalier &fs_unmount, 12923fbe39e1SAugustin Cavalier &fs_read_fs_info, 12935070f597SAugustin Cavalier &fs_write_fs_info, 12943fbe39e1SAugustin Cavalier NULL, // fs_sync, 12953fbe39e1SAugustin Cavalier &fs_get_vnode, 12963fbe39e1SAugustin Cavalier }; 12973fbe39e1SAugustin Cavalier 12983fbe39e1SAugustin Cavalier 12993fbe39e1SAugustin Cavalier fs_vnode_ops gNtfsVnodeOps = { 13003fbe39e1SAugustin Cavalier /* vnode operations */ 13013fbe39e1SAugustin Cavalier &fs_lookup, 13023fbe39e1SAugustin Cavalier &fs_get_vnode_name, 13033fbe39e1SAugustin Cavalier &fs_put_vnode, 13045070f597SAugustin Cavalier &fs_remove_vnode, 13053fbe39e1SAugustin Cavalier 13063fbe39e1SAugustin Cavalier /* VM file access */ 13073fbe39e1SAugustin Cavalier &fs_can_page, 13083fbe39e1SAugustin Cavalier &fs_read_pages, 13095070f597SAugustin Cavalier &fs_write_pages, 13103fbe39e1SAugustin Cavalier 13113fbe39e1SAugustin Cavalier NULL, // io 13123fbe39e1SAugustin Cavalier NULL, // cancel_io 13133fbe39e1SAugustin Cavalier 13143fbe39e1SAugustin Cavalier NULL, // get_file_map 13153fbe39e1SAugustin Cavalier 13163fbe39e1SAugustin Cavalier &fs_ioctl, 13173fbe39e1SAugustin Cavalier NULL, 13183fbe39e1SAugustin Cavalier NULL, // fs_select 13193fbe39e1SAugustin Cavalier NULL, // fs_deselect 13205070f597SAugustin Cavalier &fs_fsync, 13213fbe39e1SAugustin Cavalier 13223fbe39e1SAugustin Cavalier &fs_read_link, 13233fbe39e1SAugustin Cavalier NULL, // fs_create_symlink 13243fbe39e1SAugustin Cavalier 13253fbe39e1SAugustin Cavalier NULL, // fs_link, 13265070f597SAugustin Cavalier &fs_unlink, 13275070f597SAugustin Cavalier &fs_rename, 13283fbe39e1SAugustin Cavalier 13293fbe39e1SAugustin Cavalier &fs_access, 13303fbe39e1SAugustin Cavalier &fs_read_stat, 13315070f597SAugustin Cavalier &fs_write_stat, 13323fbe39e1SAugustin Cavalier NULL, // fs_preallocate 13333fbe39e1SAugustin Cavalier 13343fbe39e1SAugustin Cavalier /* file operations */ 13355070f597SAugustin Cavalier &fs_create, 13363fbe39e1SAugustin Cavalier &fs_open, 13373fbe39e1SAugustin Cavalier &fs_close, 13383fbe39e1SAugustin Cavalier &fs_free_cookie, 13393fbe39e1SAugustin Cavalier &fs_read, 13405070f597SAugustin Cavalier &fs_write, 13413fbe39e1SAugustin Cavalier 13423fbe39e1SAugustin Cavalier /* directory operations */ 13435070f597SAugustin Cavalier &fs_create_dir, 13445070f597SAugustin Cavalier &fs_remove_dir, 13453fbe39e1SAugustin Cavalier &fs_open_dir, 13463fbe39e1SAugustin Cavalier &fs_close_dir, 13473fbe39e1SAugustin Cavalier &fs_free_dir_cookie, 13483fbe39e1SAugustin Cavalier &fs_read_dir, 13493fbe39e1SAugustin Cavalier &fs_rewind_dir, 13503fbe39e1SAugustin Cavalier 13513fbe39e1SAugustin Cavalier /* attribute directory operations */ 13523fbe39e1SAugustin Cavalier NULL, // fs_open_attr_dir, 13533fbe39e1SAugustin Cavalier NULL, // fs_close_attr_dir, 13543fbe39e1SAugustin Cavalier NULL, // fs_free_attr_dir_cookie, 13553fbe39e1SAugustin Cavalier NULL, // fs_read_attr_dir, 13563fbe39e1SAugustin Cavalier NULL, // fs_rewind_attr_dir, 13573fbe39e1SAugustin Cavalier 13583fbe39e1SAugustin Cavalier /* attribute operations */ 13593fbe39e1SAugustin Cavalier NULL, // fs_create_attr, 13603fbe39e1SAugustin Cavalier NULL, // fs_open_attr, 13613fbe39e1SAugustin Cavalier NULL, // fs_close_attr, 13623fbe39e1SAugustin Cavalier NULL, // fs_free_attr_cookie, 13633fbe39e1SAugustin Cavalier NULL, // fs_read_attr, 13643fbe39e1SAugustin Cavalier NULL, // fs_write_attr, 13653fbe39e1SAugustin Cavalier NULL, // fs_read_attr_stat, 13663fbe39e1SAugustin Cavalier NULL, // fs_write_attr_stat, 13673fbe39e1SAugustin Cavalier NULL, // fs_rename_attr, 13683fbe39e1SAugustin Cavalier NULL, // fs_remove_attr, 13693fbe39e1SAugustin Cavalier }; 13703fbe39e1SAugustin Cavalier 13713fbe39e1SAugustin Cavalier 13723fbe39e1SAugustin Cavalier static file_system_module_info sNtfsFileSystem = { 13733fbe39e1SAugustin Cavalier { 13743fbe39e1SAugustin Cavalier "file_systems/ntfs" B_CURRENT_FS_API_VERSION, 13753fbe39e1SAugustin Cavalier 0, 13763fbe39e1SAugustin Cavalier NULL, 13773fbe39e1SAugustin Cavalier }, 13783fbe39e1SAugustin Cavalier 13793fbe39e1SAugustin Cavalier "ntfs", // short_name 13803fbe39e1SAugustin Cavalier "NT File System", // pretty_name 13815070f597SAugustin Cavalier 13825070f597SAugustin Cavalier // DDM flags 13835070f597SAugustin Cavalier 0 13845070f597SAugustin Cavalier | B_DISK_SYSTEM_IS_FILE_SYSTEM 13855070f597SAugustin Cavalier | B_DISK_SYSTEM_SUPPORTS_INITIALIZING 13865070f597SAugustin Cavalier | B_DISK_SYSTEM_SUPPORTS_WRITING 13875070f597SAugustin Cavalier , 13883fbe39e1SAugustin Cavalier 13893fbe39e1SAugustin Cavalier // scanning 13903fbe39e1SAugustin Cavalier fs_identify_partition, 13913fbe39e1SAugustin Cavalier fs_scan_partition, 13923fbe39e1SAugustin Cavalier fs_free_identify_partition_cookie, 13933fbe39e1SAugustin Cavalier NULL, // free_partition_content_cookie() 13943fbe39e1SAugustin Cavalier 13953fbe39e1SAugustin Cavalier &fs_mount, 13963fbe39e1SAugustin Cavalier 13975070f597SAugustin Cavalier // capability querying operations 13985070f597SAugustin Cavalier NULL, // get_supported_operations 13995070f597SAugustin Cavalier 14005070f597SAugustin Cavalier NULL, // validate_resize 14015070f597SAugustin Cavalier NULL, // validate_move 14025070f597SAugustin Cavalier NULL, // validate_set_content_name 14035070f597SAugustin Cavalier NULL, // validate_set_content_parameters 14045070f597SAugustin Cavalier NULL, // validate_initialize, 14055070f597SAugustin Cavalier 14065070f597SAugustin Cavalier /* shadow partition modification */ 14075070f597SAugustin Cavalier NULL, // shadow_changed 14085070f597SAugustin Cavalier 14095070f597SAugustin Cavalier /* writing */ 14105070f597SAugustin Cavalier NULL, // defragment 14115070f597SAugustin Cavalier NULL, // repair 14125070f597SAugustin Cavalier NULL, // resize 14135070f597SAugustin Cavalier NULL, // move 14145070f597SAugustin Cavalier NULL, // set_content_name 14155070f597SAugustin Cavalier NULL, // set_content_parameters 14165070f597SAugustin Cavalier fs_initialize, 14175070f597SAugustin Cavalier NULL // uninitialize 14183fbe39e1SAugustin Cavalier }; 14193fbe39e1SAugustin Cavalier 14203fbe39e1SAugustin Cavalier 14213fbe39e1SAugustin Cavalier module_info *modules[] = { 14223fbe39e1SAugustin Cavalier (module_info *)&sNtfsFileSystem, 14233fbe39e1SAugustin Cavalier NULL, 14243fbe39e1SAugustin Cavalier }; 1425