xref: /haiku/src/add-ons/kernel/file_systems/ntfs/kernel_interface.cpp (revision a82616528d98ef2edeb48a84a8549f2a721a37c0)
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