xref: /haiku/src/add-ons/kernel/file_systems/ntfs/kernel_interface.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
1 /*
2  * Copyright 2021, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Augustin Cavalier <waddlesplash>
7  */
8 
9 #include <dirent.h>
10 #include <unistd.h>
11 #include <util/kernel_cpp.h>
12 #include <string.h>
13 
14 #include <AutoDeleter.h>
15 #include <fs_cache.h>
16 #include <fs_info.h>
17 #include <NodeMonitor.h>
18 #include <file_systems/DeviceOpener.h>
19 #include <file_systems/fs_ops_support.h>
20 #include <util/AutoLock.h>
21 
22 #include "ntfs.h"
23 
24 extern "C" {
25 #include "libntfs/bootsect.h"
26 #include "libntfs/dir.h"
27 #include "utils/utils.h"
28 }
29 
30 //#define TRACE_NTFS
31 #ifdef TRACE_NTFS
32 #	define TRACE(X...)	dprintf("ntfs: " X)
33 #else
34 #	define TRACE(X...) ;
35 #endif
36 #define CALLED()		TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
37 #define ERROR(X...)		dprintf("ntfs: error: " X)
38 
39 
40 struct identify_cookie {
41 	NTFS_BOOT_SECTOR boot;
42 };
43 
44 extern "C" int mkntfs_main(const char* devpath, const char* label);
45 
46 typedef CObjectDeleter<ntfs_inode, int, ntfs_inode_close> NtfsInodeCloser;
47 typedef CObjectDeleter<ntfs_attr, void, ntfs_attr_close> NtfsAttrCloser;
48 static status_t fs_access(fs_volume* _volume, fs_vnode* _node, int accessMode);
49 
50 
51 //	#pragma mark - Scanning
52 
53 
54 static float
55 fs_identify_partition(int fd, partition_data* partition, void** _cookie)
56 {
57 	CALLED();
58 
59 	NTFS_BOOT_SECTOR boot;
60 	if (read_pos(fd, 0, (void*)&boot, 512) != 512) {
61 		ERROR("identify_partition: failed to read boot sector\n");
62 		return -1;
63 	}
64 
65 	if (!ntfs_boot_sector_is_ntfs(&boot)) {
66 		ERROR("identify_partition: boot signature doesn't match\n");
67 		return -1;
68 	}
69 
70 	identify_cookie* cookie = new identify_cookie;
71 	if (cookie == NULL) {
72 		ERROR("identify_partition: cookie allocation failed\n");
73 		return -1;
74 	}
75 
76 	memcpy(&cookie->boot, &boot, sizeof(boot));
77 	*_cookie = cookie;
78 
79 	// This value overrides the Intel partition identifier.
80 	return 0.82f;
81 }
82 
83 
84 static status_t
85 fs_scan_partition(int fd, partition_data* partition, void* _cookie)
86 {
87 	CALLED();
88 
89 	identify_cookie *cookie = (identify_cookie*)_cookie;
90 	partition->status = B_PARTITION_VALID;
91 	partition->flags |= B_PARTITION_FILE_SYSTEM;
92 	partition->content_size = sle64_to_cpu(cookie->boot.number_of_sectors)
93 		* le16_to_cpu(cookie->boot.bpb.bytes_per_sector);
94 	partition->block_size = le16_to_cpu(cookie->boot.bpb.bytes_per_sector);
95 
96 	// get volume name
97 	ntfs_volume* ntVolume;
98 	char path[B_PATH_NAME_LENGTH];
99 	if (ioctl(fd, B_GET_PATH_FOR_DEVICE, path) != 0) {
100 		ntVolume = utils_mount_volume(path, NTFS_MNT_RDONLY | NTFS_MNT_RECOVER);
101 		if (ntVolume == NULL)
102 			return errno ? errno : B_ERROR;
103 
104 		if (ntVolume->vol_name != NULL && ntVolume->vol_name[0] != '\0')
105 			partition->content_name = strdup(ntVolume->vol_name);
106 		else
107 			partition->content_name = strdup("");
108 		ntfs_umount(ntVolume, true);
109 	}
110 
111 	return partition->content_name != NULL ? B_OK : B_NO_MEMORY;
112 }
113 
114 
115 static void
116 fs_free_identify_partition_cookie(partition_data* partition, void* _cookie)
117 {
118 	CALLED();
119 
120 	delete (identify_cookie*)_cookie;
121 }
122 
123 
124 //	#pragma mark -
125 
126 
127 static status_t
128 fs_initialize(int fd, partition_id partitionID, const char* name,
129 	const char* parameterString, off_t partitionSize, disk_job_id job)
130 {
131 	TRACE("fs_initialize: '%s', %s\n", name, parameterString);
132 
133 	update_disk_device_job_progress(job, 0);
134 
135 	char path[B_PATH_NAME_LENGTH];
136 	if (ioctl(fd, B_GET_PATH_FOR_DEVICE, path) == 0)
137 		return B_BAD_VALUE;
138 
139 	status_t result = mkntfs_main(path, name);
140 	if (result != 0)
141 		return result;
142 
143 	result = scan_partition(partitionID);
144 	if (result != B_OK)
145 		return result;
146 
147 	update_disk_device_job_progress(job, 1);
148 	return B_OK;
149 }
150 
151 
152 static status_t
153 fs_mount(fs_volume* _volume, const char* device, uint32 flags,
154 	const char* args, ino_t* _rootID)
155 {
156 	CALLED();
157 
158 	volume* volume = new struct volume;
159 	vnode* root = new vnode;
160 	if (volume == NULL || root == NULL)
161 		return B_NO_MEMORY;
162 	ObjectDeleter<struct volume> volumeDeleter(volume);
163 
164 	mutex_init(&volume->lock, "NTFS volume lock");
165 	volume->fs_info_flags = B_FS_IS_PERSISTENT;
166 
167 	unsigned long ntfsFlags = NTFS_MNT_RECOVER | NTFS_MNT_MAY_RDONLY;
168 	if ((flags & B_MOUNT_READ_ONLY) != 0 || DeviceOpener(device, O_RDWR).IsReadOnly())
169 		ntfsFlags |= NTFS_MNT_RDONLY;
170 
171 	// mount
172 	volume->ntfs = utils_mount_volume(device, ntfsFlags);
173 	if (volume->ntfs == NULL)
174 		return errno;
175 
176 	if (NVolReadOnly(volume->ntfs)) {
177 		if ((ntfsFlags & NTFS_MNT_RDONLY) == 0)
178 			ERROR("volume is hibernated, mounted as read-only\n");
179 		volume->fs_info_flags |= B_FS_IS_READONLY;
180 	}
181 
182 	if (ntfs_volume_get_free_space(volume->ntfs) != 0) {
183 		ntfs_umount(volume->ntfs, true);
184 		return B_ERROR;
185 	}
186 
187 	const bool showSystem = false, showHidden = true, hideDot = false;
188 	if (ntfs_set_shown_files(volume->ntfs, showSystem, showHidden, hideDot) != 0) {
189 		ntfs_umount(volume->ntfs, true);
190 		return B_ERROR;
191 	}
192 
193 	// TODO: uid/gid mapping and real permissions
194 
195 	// construct lowntfs_context
196 	volume->lowntfs.haiku_fs_volume = _volume;
197 	volume->lowntfs.current_close_state_vnode = NULL;
198 
199 	volume->lowntfs.vol = volume->ntfs;
200 	volume->ntfs->abs_mnt_point = volume->lowntfs.abs_mnt_point = strdup("");
201 	volume->lowntfs.dmask = 0;
202 	volume->lowntfs.fmask = S_IXUSR | S_IXGRP | S_IXOTH;
203 	volume->lowntfs.dmtime = 0;
204 	volume->lowntfs.special_files = NTFS_FILES_INTERIX;
205 	volume->lowntfs.posix_nlink = 0;
206 	volume->lowntfs.inherit = 0;
207 	volume->lowntfs.windows_names = 1;
208 	volume->lowntfs.latest_ghost = 0;
209 
210 	*_rootID = root->inode = FILE_root;
211 	root->parent_inode = (u64)-1;
212 	root->mode = S_IFDIR | ACCESSPERMS;
213 	root->uid = root->gid = 0;
214 	root->size = 0;
215 
216 	status_t status = publish_vnode(_volume, root->inode, root, &gNtfsVnodeOps, S_IFDIR, 0);
217 	if (status != B_OK) {
218 		ntfs_umount(volume->ntfs, true);
219 		return status;
220 	}
221 
222 	volumeDeleter.Detach();
223 
224 	_volume->private_volume = volume;
225 	_volume->ops = &gNtfsVolumeOps;
226 	return B_OK;
227 }
228 
229 
230 static status_t
231 fs_unmount(fs_volume* _volume)
232 {
233 	CALLED();
234 	volume* volume = (struct volume*)_volume->private_volume;
235 
236 	if (ntfs_umount(volume->ntfs, false) < 0)
237 		return errno;
238 
239 	delete volume;
240 	_volume->private_volume = NULL;
241 
242 	return B_OK;
243 }
244 
245 
246 static status_t
247 fs_read_fs_info(fs_volume* _volume, struct fs_info* info)
248 {
249 	CALLED();
250 	volume* volume = (struct volume*)_volume->private_volume;
251 	MutexLocker lock(volume->lock);
252 
253 	info->flags = volume->fs_info_flags;
254 	info->block_size = volume->ntfs->cluster_size;
255 	info->total_blocks = volume->ntfs->nr_clusters;
256 	info->free_blocks = volume->ntfs->free_clusters;
257 
258 	info->io_size = 65536;
259 
260 	strlcpy(info->volume_name, volume->ntfs->vol_name, sizeof(info->volume_name));
261 	strlcpy(info->fsh_name, "NTFS", sizeof(info->fsh_name));
262 
263 	return B_OK;
264 }
265 
266 
267 static status_t
268 fs_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask)
269 {
270 	CALLED();
271 	volume* volume = (struct volume*)_volume->private_volume;
272 	MutexLocker lock(volume->lock);
273 
274 	if ((volume->fs_info_flags & B_FS_IS_READONLY) != 0)
275 		return B_READ_ONLY_DEVICE;
276 
277 	status_t status = B_OK;
278 
279 	if ((mask & FS_WRITE_FSINFO_NAME) != 0) {
280 		ntfschar* label = NULL;
281 		int label_len = ntfs_mbstoucs(info->volume_name, &label);
282 		if (label_len <= 0 || label == NULL)
283 			return -1;
284 		MemoryDeleter nameDeleter(label);
285 
286 		if (ntfs_volume_rename(volume->ntfs, label, label_len) != 0)
287 			status = errno;
288 	}
289 
290 	return status;
291 }
292 
293 
294 //	#pragma mark -
295 
296 
297 static status_t
298 fs_init_vnode(fs_volume* _volume, ino_t parent, ino_t nid, vnode** _vnode, bool publish = false)
299 {
300 	volume* volume = (struct volume*)_volume->private_volume;
301 	ASSERT_LOCKED_MUTEX(&volume->lock);
302 
303 	ntfs_inode* ni = ntfs_inode_open(volume->ntfs, nid);
304 	if (ni == NULL)
305 		return ENOENT;
306 	NtfsInodeCloser niCloser(ni);
307 
308 	vnode* node = new vnode;
309 	if (node == NULL)
310 		return B_NO_MEMORY;
311 	ObjectDeleter<vnode> vnodeDeleter(node);
312 
313 	struct stat statbuf;
314 	if (ntfs_fuse_getstat(&volume->lowntfs, NULL, ni, &statbuf) != 0)
315 		return errno;
316 
317 	node->inode = nid;
318 	node->parent_inode = parent;
319 	node->uid = statbuf.st_uid;
320 	node->gid = statbuf.st_gid;
321 	node->mode = statbuf.st_mode;
322 	node->size = statbuf.st_size;
323 
324 	// cache the node's name
325 	char path[B_FILE_NAME_LENGTH];
326 	if (utils_inode_get_name(ni, path, sizeof(path)) == 0)
327 		return B_NO_MEMORY;
328 	node->name = strdup(strrchr(path, '/') + 1);
329 
330 	if (publish) {
331 		status_t status = publish_vnode(_volume, node->inode, node, &gNtfsVnodeOps, node->mode, 0);
332 		if (status != B_OK)
333 			return status;
334 	}
335 
336 	if ((node->mode & S_IFDIR) == 0) {
337 		node->file_cache = file_cache_create(_volume->id, nid, node->size);
338 		if (node->file_cache == NULL)
339 			return B_NO_INIT;
340 	}
341 
342 	vnodeDeleter.Detach();
343 	*_vnode = node;
344 	return B_OK;
345 }
346 
347 
348 static status_t
349 fs_get_vnode(fs_volume* _volume, ino_t nid, fs_vnode* _node, int* _type,
350 	uint32* _flags, bool reenter)
351 {
352 	TRACE("get_vnode %" B_PRIdINO "\n", nid);
353 	volume* volume = (struct volume*)_volume->private_volume;
354 	MutexLocker lock(reenter ? NULL : &volume->lock);
355 
356 	vnode* vnode;
357 	status_t status = fs_init_vnode(_volume, -1 /* set by fs_lookup */, nid, &vnode);
358 	if (status != B_OK)
359 		return status;
360 
361 	_node->private_node = vnode;
362 	_node->ops = &gNtfsVnodeOps;
363 	*_type = vnode->mode;
364 	*_flags = 0;
365 
366 	return B_OK;
367 }
368 
369 
370 static status_t
371 fs_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
372 {
373 	CALLED();
374 	volume* volume = (struct volume*)_volume->private_volume;
375 	MutexLocker lock(reenter ? NULL : &volume->lock);
376 	vnode* node = (vnode*)_node->private_node;
377 
378 	file_cache_delete(node->file_cache);
379 	delete node;
380 	return B_OK;
381 }
382 
383 
384 static status_t
385 fs_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
386 {
387 	CALLED();
388 	volume* volume = (struct volume*)_volume->private_volume;
389 	MutexLocker lock(reenter ? NULL : &volume->lock);
390 	vnode* node = (vnode*)_node->private_node;
391 
392 	if (ntfs_fuse_release(&volume->lowntfs, node->parent_inode, node->inode,
393 			node->lowntfs_close_state, node->lowntfs_ghost) != 0)
394 		return errno;
395 
396 	file_cache_delete(node->file_cache);
397 	delete node;
398 	return B_OK;
399 }
400 
401 
402 int*
403 ntfs_haiku_get_close_state(struct lowntfs_context *ctx, u64 ino)
404 {
405 	if (ctx->current_close_state_vnode != NULL)
406 		panic("NTFS current_close_state_vnode should be NULL!");
407 
408 	vnode* node = NULL;
409 	if (get_vnode((fs_volume*)ctx->haiku_fs_volume, ino, (void**)&node) != B_OK)
410 		return NULL;
411 	ctx->current_close_state_vnode = node;
412 	return &node->lowntfs_close_state;
413 }
414 
415 
416 void
417 ntfs_haiku_put_close_state(struct lowntfs_context *ctx, u64 ino, u64 ghost)
418 {
419 	vnode* node = (vnode*)ctx->current_close_state_vnode;
420 	if (node == NULL)
421 		return;
422 
423 	node->lowntfs_ghost = ghost;
424 	if ((node->lowntfs_close_state & CLOSE_GHOST) != 0) {
425 		fs_volume* _volume = (fs_volume*)ctx->haiku_fs_volume;
426 		entry_cache_remove(_volume->id, node->parent_inode, node->name);
427 		notify_entry_removed(_volume->id, node->parent_inode, node->name, node->inode);
428 		remove_vnode(_volume, node->inode);
429 	}
430 
431 	ctx->current_close_state_vnode = NULL;
432 	put_vnode((fs_volume*)ctx->haiku_fs_volume, node->inode);
433 }
434 
435 
436 static bool
437 fs_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
438 {
439 	return true;
440 }
441 
442 
443 static status_t
444 fs_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
445 	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
446 {
447 	volume* volume = (struct volume*)_volume->private_volume;
448 	MutexLocker lock(volume->lock);
449 	vnode* node = (vnode*)_node->private_node;
450 
451 	TRACE("read_pages inode: %" B_PRIdINO", pos: %" B_PRIdOFF "; vecs: %p; "
452 		"count: %" B_PRIuSIZE "; numBytes: %" B_PRIuSIZE "\n", node->inode, pos,
453 		vecs, count, *_numBytes);
454 
455 	ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode);
456 	if (ni == NULL)
457 		return B_FILE_ERROR;
458 	NtfsInodeCloser niCloser(ni);
459 
460 	if (pos < 0 || pos >= ni->data_size)
461 		return B_BAD_VALUE;
462 
463 	size_t bytesLeft = min_c(*_numBytes, size_t(ni->data_size - pos));
464 	*_numBytes = 0;
465 	for (size_t i = 0; i < count && bytesLeft > 0; i++) {
466 		const size_t ioSize = min_c(bytesLeft, vecs[i].iov_len);
467 		const int read = ntfs_fuse_read(ni, pos, (char*)vecs[i].iov_base, ioSize);
468 		if (read < 0)
469 			return errno;
470 
471 		pos += read;
472 		*_numBytes += read;
473 		bytesLeft -= read;
474 
475 		if (size_t(read) != ioSize)
476 			return errno;
477 	}
478 
479 	return B_OK;
480 }
481 
482 
483 static status_t
484 fs_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
485 	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
486 {
487 	volume* volume = (struct volume*)_volume->private_volume;
488 	MutexLocker lock(volume->lock);
489 	vnode* node = (vnode*)_node->private_node;
490 
491 	TRACE("write_pages inode: %" B_PRIdINO", pos: %" B_PRIdOFF "; vecs: %p; "
492 		"count: %" B_PRIuSIZE "; numBytes: %" B_PRIuSIZE "\n", node->inode, pos,
493 		vecs, count, *_numBytes);
494 
495 	ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode);
496 	if (ni == NULL)
497 		return B_FILE_ERROR;
498 	NtfsInodeCloser niCloser(ni);
499 
500 	if (pos < 0 || pos >= ni->data_size)
501 		return B_BAD_VALUE;
502 
503 	size_t bytesLeft = min_c(*_numBytes, size_t(ni->data_size - pos));
504 	*_numBytes = 0;
505 	for (size_t i = 0; i < count && bytesLeft > 0; i++) {
506 		const size_t ioSize = min_c(bytesLeft, vecs[i].iov_len);
507 		const int written = ntfs_fuse_write(&volume->lowntfs, ni, (char*)vecs[i].iov_base, ioSize, pos);
508 		if (written < 0)
509 			return errno;
510 
511 		pos += written;
512 		*_numBytes += written;
513 		bytesLeft -= written;
514 
515 		if (size_t(written) != ioSize)
516 			return errno;
517 	}
518 
519 	return B_OK;
520 }
521 
522 
523 //	#pragma mark -
524 
525 
526 static status_t
527 fs_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
528 	ino_t* _vnodeID)
529 {
530 	TRACE("fs_lookup: name address: %p (%s)\n", name, name);
531 	volume* volume = (struct volume*)_volume->private_volume;
532 	MutexLocker lock(volume->lock);
533 	vnode* directory = (vnode*)_directory->private_node;
534 
535 	status_t result;
536 	if (strcmp(name, ".") == 0) {
537 		*_vnodeID = directory->inode;
538 	} else if (strcmp(name, "..") == 0) {
539 		if (directory->inode == FILE_root)
540 			return ENOENT;
541 		*_vnodeID = directory->parent_inode;
542 	} else {
543 		u64 inode = ntfs_fuse_inode_lookup(&volume->lowntfs, directory->inode, name);
544 		if (inode == (u64)-1)
545 			return errno;
546 		*_vnodeID = inode;
547 	}
548 
549 	result = entry_cache_add(_volume->id, directory->inode, name, *_vnodeID);
550 	if (result != B_OK)
551 		return result;
552 
553 	vnode* node = NULL;
554 	result = get_vnode(_volume, *_vnodeID, (void**)&node);
555 	if (result != B_OK)
556 		return result;
557 
558 	if (node->parent_inode == (u64)-1)
559 		node->parent_inode = directory->inode;
560 
561 	TRACE("fs_lookup: ID %" B_PRIdINO "\n", *_vnodeID);
562 	return B_OK;
563 }
564 
565 
566 static status_t
567 fs_get_vnode_name(fs_volume* _volume, fs_vnode* _node, char* buffer, size_t bufferSize)
568 {
569 	// CALLED();
570 	vnode* node = (vnode*)_node->private_node;
571 
572 	if (strlcpy(buffer, node->name, bufferSize) >= bufferSize)
573 		return B_BUFFER_OVERFLOW;
574 	return B_OK;
575 }
576 
577 
578 static status_t
579 fs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
580 	void* buffer, size_t bufferLength)
581 {
582 	// TODO?
583 	return B_DEV_INVALID_IOCTL;
584 }
585 
586 
587 static status_t
588 fs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
589 {
590 	CALLED();
591 	volume* volume = (struct volume*)_volume->private_volume;
592 	MutexLocker lock(volume->lock);
593 	vnode* node = (vnode*)_node->private_node;
594 
595 	ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode);
596 	if (ni == NULL)
597 		return errno;
598 	NtfsInodeCloser niCloser(ni);
599 
600 	if (ntfs_fuse_getstat(&volume->lowntfs, NULL, ni, stat) != 0)
601 		return errno;
602 	return B_OK;
603 }
604 
605 
606 static status_t
607 fs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, uint32 mask)
608 {
609 	CALLED();
610 	volume* volume = (struct volume*)_volume->private_volume;
611 	MutexLocker lock(volume->lock);
612 	vnode* node = (vnode*)_node->private_node;
613 
614 	if ((volume->fs_info_flags & B_FS_IS_READONLY) != 0)
615 		return B_READ_ONLY_DEVICE;
616 
617 	ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode);
618 	if (ni == NULL)
619 		return B_FILE_ERROR;
620 	NtfsInodeCloser niCloser(ni);
621 
622 	bool updateTime = false;
623 	const uid_t euid = geteuid();
624 
625 	const bool isOwnerOrRoot = (euid == 0 || euid == (uid_t)node->uid);
626 	const bool hasWriteAccess = fs_access(_volume, _node, W_OK);
627 
628 	if ((mask & B_STAT_SIZE) != 0 && node->size != stat->st_size) {
629 		if ((node->mode & S_IFDIR) != 0)
630 			return B_IS_A_DIRECTORY;
631 		if (!hasWriteAccess)
632 			return B_NOT_ALLOWED;
633 
634 		ntfs_attr* na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
635 		if (na == NULL)
636 			return errno;
637 		NtfsAttrCloser naCloser(na);
638 
639 		if (ntfs_attr_truncate(na, stat->st_size) != 0)
640 			return errno;
641 		node->size = na->data_size;
642 		file_cache_set_size(node->file_cache, node->size);
643 
644 		updateTime = true;
645 	}
646 
647 	if ((mask & B_STAT_UID) != 0) {
648 		// only root should be allowed
649 		if (euid != 0)
650 			return B_NOT_ALLOWED;
651 
652 		// We don't support this (yet.)
653 		if (node->uid != stat->st_uid)
654 			return B_UNSUPPORTED;
655 	}
656 
657 	if ((mask & B_STAT_GID) != 0) {
658 		// only the user or root can do that
659 		if (!isOwnerOrRoot)
660 			return B_NOT_ALLOWED;
661 
662 		// We don't support this (yet.)
663 		if (node->gid != stat->st_gid)
664 			return B_UNSUPPORTED;
665 	}
666 
667 	if ((mask & B_STAT_MODE) != 0) {
668 		// only the user or root can do that
669 		if (!isOwnerOrRoot)
670 			return B_NOT_ALLOWED;
671 
672 		// We don't support this (yet.)
673 		if (node->mode != stat->st_mode)
674 			return B_UNSUPPORTED;
675 	}
676 
677 	if ((mask & B_STAT_CREATION_TIME) != 0) {
678 		// the user or root can do that or any user with write access
679 		if (!isOwnerOrRoot && !hasWriteAccess)
680 			return B_NOT_ALLOWED;
681 
682 		ni->creation_time = timespec2ntfs(stat->st_crtim);
683 	}
684 
685 	if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
686 		// the user or root can do that or any user with write access
687 		if (!isOwnerOrRoot && !hasWriteAccess)
688 			return B_NOT_ALLOWED;
689 
690 		ni->last_data_change_time = timespec2ntfs(stat->st_mtim);
691 	}
692 
693 	if ((mask & B_STAT_CHANGE_TIME) != 0 || updateTime) {
694 		// the user or root can do that or any user with write access
695 		if (!isOwnerOrRoot && !hasWriteAccess)
696 			return B_NOT_ALLOWED;
697 
698 		ni->last_mft_change_time = timespec2ntfs(stat->st_ctim);
699 	}
700 
701 	if ((mask & B_STAT_ACCESS_TIME) != 0) {
702 		// the user or root can do that or any user with write access
703 		if (!isOwnerOrRoot && !hasWriteAccess)
704 			return B_NOT_ALLOWED;
705 
706 		ni->last_access_time = timespec2ntfs(stat->st_atim);
707 	}
708 
709 	ntfs_inode_mark_dirty(ni);
710 
711 	notify_stat_changed(_volume->id, node->parent_inode, node->inode, mask);
712 	return B_OK;
713 }
714 
715 
716 static status_t
717 fs_generic_create(fs_volume* _volume, vnode* directory, const char* name, int mode,
718 	ino_t* _inode)
719 {
720 	volume* volume = (struct volume*)_volume->private_volume;
721 	ASSERT_LOCKED_MUTEX(&volume->lock);
722 
723 	if ((directory->mode & S_IFDIR) == 0)
724 		return B_BAD_TYPE;
725 
726 	ino_t inode = -1;
727 	if (ntfs_fuse_create(&volume->lowntfs, directory->inode, name, mode & (S_IFMT | 07777),
728 			0, (char*)NULL, &inode) != 0)
729 		return errno;
730 
731 	vnode* node;
732 	status_t status = fs_init_vnode(_volume, directory->inode, inode, &node, true);
733 	if (status != B_OK)
734 		return status;
735 
736 	entry_cache_add(_volume->id, directory->inode, name, inode);
737 	notify_entry_created(_volume->id, directory->inode, name, inode);
738 	*_inode = inode;
739 	return B_OK;
740 }
741 
742 
743 static status_t
744 fs_create(fs_volume* _volume, fs_vnode* _directory, const char* name,
745 	int openMode, int mode, void** _cookie, ino_t* _vnodeID)
746 {
747 	CALLED();
748 	volume* volume = (struct volume*)_volume->private_volume;
749 	MutexLocker lock(volume->lock);
750 	vnode* directory = (vnode*)_directory->private_node;
751 
752 	if ((directory->mode & S_IFDIR) == 0)
753 		return B_NOT_A_DIRECTORY;
754 
755 	status_t status = fs_access(_volume, _directory, W_OK);
756 	if (status != B_OK)
757 		return status;
758 
759 #if 1
760 	if ((openMode & O_NOCACHE) != 0)
761 		return B_UNSUPPORTED;
762 #endif
763 
764 	status = fs_generic_create(_volume, directory, name, S_IFREG | (mode & 07777), _vnodeID);
765 	if (status != B_OK)
766 		return status;
767 
768 	file_cookie* cookie = new file_cookie;
769 	if (cookie == NULL)
770 		return B_NO_MEMORY;
771 	ObjectDeleter<file_cookie> cookieDeleter(cookie);
772 
773 	cookie->open_mode = openMode;
774 
775 	cookieDeleter.Detach();
776 	*_cookie = cookie;
777 	return B_OK;
778 }
779 
780 
781 static status_t
782 fs_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
783 {
784 	CALLED();
785 	volume* volume = (struct volume*)_volume->private_volume;
786 	MutexLocker lock(volume->lock);
787 	vnode* node = (vnode*)_node->private_node;
788 
789 	// opening a directory read-only is allowed (but no data can be read)
790 	if ((node->mode & S_IFDIR) != 0 && (openMode & O_RWMASK) != 0)
791 		return B_IS_A_DIRECTORY;
792 	if ((openMode & O_DIRECTORY) != 0 && (node->mode & S_IFDIR) == 0)
793 		return B_NOT_A_DIRECTORY;
794 
795 	status_t status = fs_access(_volume, _node, open_mode_to_access(openMode));
796 	if (status != B_OK)
797 		return status;
798 
799 	ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode);
800 	if (ni == NULL)
801 		return errno;
802 	NtfsInodeCloser niCloser(ni);
803 
804 	file_cookie* cookie = new file_cookie;
805 	if (cookie == NULL)
806 		return B_NO_MEMORY;
807 	ObjectDeleter<file_cookie> cookieDeleter(cookie);
808 
809 	cookie->open_mode = openMode;
810 
811 	// We don't actually support uncached mode; it would require us to handle
812 	// passing user buffers to libntfs, among other things.
813 #if 0
814 	if ((openMode & O_NOCACHE) != 0 && node->file_cache != NULL) {
815 		status_t status = file_cache_disable(node->file_cache);
816 		if (status != B_OK)
817 			return status;
818 	}
819 #else
820 	if ((openMode & O_NOCACHE) != 0)
821 		return B_UNSUPPORTED;
822 #endif
823 
824 	if ((openMode & O_TRUNC) != 0) {
825 		if ((openMode & O_RWMASK) == O_RDONLY)
826 			return B_NOT_ALLOWED;
827 
828 		ntfs_attr* na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
829 		if (na == NULL)
830 			return errno;
831 		NtfsAttrCloser naCloser(na);
832 
833 		if (ntfs_attr_truncate(na, 0) != 0)
834 			return errno;
835 		node->size = na->data_size;
836 		file_cache_set_size(node->file_cache, node->size);
837 	}
838 
839 	cookieDeleter.Detach();
840 	*_cookie = cookie;
841 	return B_OK;
842 }
843 
844 
845 static status_t
846 fs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
847 	void* buffer, size_t* length)
848 {
849 	vnode* node = (vnode*)_node->private_node;
850 	file_cookie* cookie = (file_cookie*)_cookie;
851 
852 	if ((node->mode & S_IFDIR) != 0)
853 		return B_IS_A_DIRECTORY;
854 
855 	ASSERT((cookie->open_mode & O_RWMASK) == O_RDONLY || (cookie->open_mode & O_RDWR) != 0);
856 
857 	return file_cache_read(node->file_cache, cookie, pos, buffer, length);
858 }
859 
860 
861 static status_t
862 fs_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
863 	const void* buffer, size_t* _length)
864 {
865 	CALLED();
866 	volume* volume = (struct volume*)_volume->private_volume;
867 	MutexLocker lock(volume->lock);
868 	vnode* node = (vnode*)_node->private_node;
869 	file_cookie* cookie = (file_cookie*)_cookie;
870 
871 	if ((node->mode & S_IFDIR) != 0)
872 		return B_IS_A_DIRECTORY;
873 
874 	ASSERT((cookie->open_mode & O_WRONLY) != 0 || (cookie->open_mode & O_RDWR) != 0);
875 
876 	if (cookie->open_mode & O_APPEND)
877 		pos = node->size;
878 	if (pos < 0)
879 		return B_BAD_VALUE;
880 
881 	if (pos + s64(*_length) > node->size) {
882 		ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode);
883 		if (ni == NULL)
884 			return errno;
885 		NtfsInodeCloser niCloser(ni);
886 
887 		ntfs_attr* na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
888 		if (na == NULL)
889 			return errno;
890 		NtfsAttrCloser naCloser(na);
891 
892 		if (ntfs_attr_truncate(na, pos + *_length) != 0)
893 			return errno;
894 		node->size = na->data_size;
895 		file_cache_set_size(node->file_cache, node->size);
896 	}
897 
898 	lock.Unlock();
899 
900 	status_t status = file_cache_write(node->file_cache, cookie, pos, buffer, _length);
901 	if (status != B_OK)
902 		return status;
903 
904 	lock.Lock();
905 
906 	// periodically notify if the file size has changed
907 	if ((node->lowntfs_close_state & CLOSE_GHOST) == 0
908 			&& cookie->last_size != node->size
909 			&& system_time() > cookie->last_notification + INODE_NOTIFICATION_INTERVAL) {
910 		notify_stat_changed(_volume->id, node->parent_inode, node->inode,
911 			B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE);
912 		cookie->last_size = node->size;
913 		cookie->last_notification = system_time();
914 	}
915 	return status;
916 }
917 
918 
919 static status_t
920 fs_fsync(fs_volume* _volume, fs_vnode* _node)
921 {
922 	CALLED();
923 	vnode* node = (vnode*)_node->private_node;
924 
925 	return file_cache_sync(node->file_cache);
926 }
927 
928 
929 static status_t
930 fs_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
931 {
932 	CALLED();
933 
934 	// Nothing to do.
935 	return B_OK;
936 }
937 
938 
939 static status_t
940 fs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
941 {
942 	file_cookie* cookie = (file_cookie*)_cookie;
943 	delete cookie;
944 	return B_OK;
945 }
946 
947 
948 static status_t
949 fs_generic_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name, RM_TYPES type)
950 {
951 	volume* volume = (struct volume*)_volume->private_volume;
952 	MutexLocker lock(volume->lock);
953 	vnode* directory = (vnode*)_directory->private_node;
954 
955 	status_t status = fs_access(_volume, _directory, W_OK);
956 	if (status != B_OK)
957 		return status;
958 
959 	if (ntfs_fuse_rm(&volume->lowntfs, directory->inode, name, type) != 0)
960 		return errno;
961 
962 	// remove_vnode() et al. will be called by put_close_state.
963 	return B_OK;
964 }
965 
966 
967 static status_t
968 fs_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name)
969 {
970 	CALLED();
971 	return fs_generic_unlink(_volume, _directory, name, RM_LINK);
972 }
973 
974 
975 static status_t
976 fs_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
977 	fs_vnode* _newDir, const char* newName)
978 {
979 	CALLED();
980 	volume* volume = (struct volume*)_volume->private_volume;
981 	MutexLocker lock(volume->lock);
982 
983 	vnode* old_directory = (vnode*)_oldDir->private_node;
984 	vnode* new_directory = (vnode*)_newDir->private_node;
985 
986 	if (old_directory == new_directory && strcmp(oldName, newName) == 0)
987 		return B_OK;
988 
989 	status_t status = fs_access(_volume, _oldDir, W_OK);
990 	if (status == B_OK)
991 		status = fs_access(_volume, _newDir, W_OK);
992 	if (status != B_OK)
993 		return status;
994 
995 	// Prevent moving a directory into one of its own children.
996 	if (old_directory != new_directory) {
997 		u64 oldIno = ntfs_fuse_inode_lookup(&volume->lowntfs, old_directory->inode, oldName);
998 		if (oldIno == (u64)-1)
999 			return B_ENTRY_NOT_FOUND;
1000 
1001 		ino_t parent = new_directory->inode;
1002 		const ino_t root = FILE_root;
1003 
1004 		while (true) {
1005 			if (parent == oldIno)
1006 				return B_BAD_VALUE;
1007 			else if (parent == root || parent == old_directory->inode)
1008 				break;
1009 
1010 			vnode* parentNode;
1011 			if (get_vnode(_volume, parent, (void**)&parentNode) != B_OK)
1012 				return B_ERROR;
1013 
1014 			parent = parentNode->parent_inode;
1015 			put_vnode(_volume, parentNode->inode);
1016 		}
1017 	}
1018 
1019 	if (ntfs_fuse_rename(&volume->lowntfs, old_directory->inode, oldName,
1020 			new_directory->inode, newName) != 0)
1021 		return errno;
1022 
1023 	u64 ino = ntfs_fuse_inode_lookup(&volume->lowntfs, new_directory->inode, newName);
1024 	if (ino == (u64)-1)
1025 		return B_ENTRY_NOT_FOUND;
1026 
1027 	vnode* node;
1028 	status = get_vnode(_volume, ino, (void**)&node);
1029 	if (status != B_OK)
1030 		return status;
1031 
1032 	free(node->name);
1033 	node->name = strdup(newName);
1034 	node->parent_inode = new_directory->inode;
1035 
1036 	if ((node->mode & S_IFDIR) != 0)
1037 		entry_cache_add(_volume->id, ino, "..", new_directory->inode);
1038 
1039 	put_vnode(_volume, ino);
1040 
1041 	entry_cache_remove(_volume->id, old_directory->inode, oldName);
1042 	entry_cache_add(_volume->id, new_directory->inode, newName, ino);
1043 	notify_entry_moved(_volume->id, old_directory->inode, oldName,
1044 		new_directory->inode, newName, ino);
1045 
1046 	return B_OK;
1047 }
1048 
1049 
1050 static status_t
1051 fs_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
1052 {
1053 	// CALLED();
1054 	volume* volume = (struct volume*)_volume->private_volume;
1055 	vnode* node = (vnode*)_node->private_node;
1056 
1057 	if ((accessMode & W_OK) != 0 && (volume->fs_info_flags & B_FS_IS_READONLY) != 0)
1058 		return B_READ_ONLY_DEVICE;
1059 
1060 	return check_access_permissions(accessMode, node->mode, node->gid, node->uid);
1061 }
1062 
1063 
1064 static status_t
1065 fs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer, size_t* bufferSize)
1066 {
1067 	CALLED();
1068 	volume* volume = (struct volume*)_volume->private_volume;
1069 	MutexLocker lock(volume->lock);
1070 	vnode* node = (vnode*)_node->private_node;
1071 
1072 	if (ntfs_fuse_readlink(&volume->lowntfs, node->inode, buffer, bufferSize) != 0)
1073 		return errno;
1074 	return B_OK;
1075 }
1076 
1077 
1078 //	#pragma mark - Directory functions
1079 
1080 
1081 static status_t
1082 fs_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name, int mode)
1083 {
1084 	CALLED();
1085 	volume* volume = (struct volume*)_volume->private_volume;
1086 	MutexLocker lock(volume->lock);
1087 	vnode* directory = (vnode*)_directory->private_node;
1088 
1089 	status_t status = fs_access(_volume, _directory, W_OK);
1090 	if (status != B_OK)
1091 		return status;
1092 
1093 	ino_t inode = -1;
1094 	status = fs_generic_create(_volume, directory, name, S_IFDIR | (mode & 07777), &inode);
1095 	if (status != B_OK)
1096 		return status;
1097 
1098 	return B_OK;
1099 }
1100 
1101 
1102 static status_t
1103 fs_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name)
1104 {
1105 	CALLED();
1106 	ino_t directory_inode = ((vnode*)_directory->private_node)->inode;
1107 	status_t status = fs_generic_unlink(_volume, _directory, name, RM_DIR);
1108 	if (status != B_OK)
1109 		return status;
1110 
1111 	entry_cache_remove(_volume->id, directory_inode, "..");
1112 	return B_OK;
1113 }
1114 
1115 
1116 static status_t
1117 fs_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
1118 {
1119 	CALLED();
1120 	vnode* node = (vnode*)_node->private_node;
1121 
1122 	status_t status = fs_access(_volume, _node, R_OK);
1123 	if (status != B_OK)
1124 		return status;
1125 
1126 	if ((node->mode & S_IFDIR) == 0)
1127 		return B_NOT_A_DIRECTORY;
1128 
1129 	directory_cookie* cookie = new directory_cookie;
1130 	if (cookie == NULL)
1131 		return B_NO_MEMORY;
1132 
1133 	cookie->first = cookie->current = NULL;
1134 	*_cookie = (void*)cookie;
1135 	return B_OK;
1136 }
1137 
1138 
1139 static int
1140 _ntfs_readdir_callback(void* _cookie, const ntfschar* ntfs_name, const int ntfs_name_len,
1141 	const int name_type, const s64 pos, const MFT_REF mref, const unsigned dt_type)
1142 {
1143 	if (name_type == FILE_NAME_DOS)
1144 		return 0;
1145 
1146 	directory_cookie* cookie = (directory_cookie*)_cookie;
1147 
1148 	char* name = NULL;
1149 	int name_len = ntfs_ucstombs(ntfs_name, ntfs_name_len, &name, 0);
1150 	if (name_len <= 0 || name == NULL)
1151 		return -1;
1152 	MemoryDeleter nameDeleter(name);
1153 
1154 	directory_cookie::entry* entry =
1155 		(directory_cookie::entry*)malloc(sizeof(directory_cookie::entry) + name_len);
1156 	if (entry == NULL)
1157 		return -1;
1158 	entry->next = NULL;
1159 
1160 	entry->inode = MREF(mref);
1161 	entry->name_length = name_len;
1162 	memcpy(entry->name, name, name_len + 1);
1163 
1164 	if (cookie->first == NULL) {
1165 		cookie->first = cookie->current = entry;
1166 	} else {
1167 		cookie->current->next = entry;
1168 		cookie->current = entry;
1169 	}
1170 
1171 	return 0;
1172 }
1173 
1174 
1175 static status_t
1176 fs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
1177 	struct dirent* dirent, size_t bufferSize, uint32* _num)
1178 {
1179 	CALLED();
1180 	directory_cookie* cookie = (directory_cookie*)_cookie;
1181 
1182 	// TODO: While lowntfs-3g seems to also read the entire directory at once into memory,
1183 	// we could optimize things here by storing the data in the vnode, not the inode, and
1184 	// only freeing it after some period of inactivity.
1185 
1186 	// See if we need to read the directory ourselves first.
1187 	if (cookie->first == NULL) {
1188 		volume* volume = (struct volume*)_volume->private_volume;
1189 		MutexLocker lock(volume->lock);
1190 		vnode* node = (vnode*)_node->private_node;
1191 
1192 		ntfs_inode* ni = ntfs_inode_open(volume->ntfs, node->inode);
1193 		if (ni == NULL)
1194 			return errno;
1195 		NtfsInodeCloser niCloser(ni);
1196 
1197 		s64 pos = 0;
1198 		if (ntfs_readdir(ni, &pos, cookie, _ntfs_readdir_callback) != 0)
1199 			return errno;
1200 		cookie->current = cookie->first;
1201 	}
1202 	if (cookie->first == NULL)
1203 		return ENOENT;
1204 	if (cookie->current == NULL) {
1205 		*_num = 0;
1206 		return B_OK;
1207 	}
1208 
1209 	uint32 maxCount = *_num;
1210 	uint32 count = 0;
1211 	while (count < maxCount && bufferSize > sizeof(struct dirent)) {
1212 		size_t length = bufferSize - offsetof(struct dirent, d_name);
1213 		if (length < (cookie->current->name_length + 1)) {
1214 			// the remaining name buffer length is too small
1215 			if (count == 0)
1216 				return B_BUFFER_OVERFLOW;
1217 			break;
1218 		}
1219 		length = cookie->current->name_length;
1220 
1221 		dirent->d_dev = _volume->id;
1222 		dirent->d_ino = cookie->current->inode;
1223 		strlcpy(dirent->d_name, cookie->current->name, length + 1);
1224 
1225 		dirent = next_dirent(dirent, length, bufferSize);
1226 		count++;
1227 
1228 		cookie->current = cookie->current->next;
1229 		if (cookie->current == NULL)
1230 			break;
1231 	}
1232 
1233 	*_num = count;
1234 	return B_OK;
1235 }
1236 
1237 
1238 static status_t
1239 fs_rewind_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void *_cookie)
1240 {
1241 	CALLED();
1242 	directory_cookie* cookie = (directory_cookie*)_cookie;
1243 	cookie->current = cookie->first;
1244 	return B_OK;
1245 }
1246 
1247 
1248 static status_t
1249 fs_close_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* /*_cookie*/)
1250 {
1251 	return B_OK;
1252 }
1253 
1254 
1255 static status_t
1256 fs_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
1257 {
1258 	CALLED();
1259 	directory_cookie* cookie = (directory_cookie*)_cookie;
1260 	if (cookie == NULL)
1261 		return B_OK;
1262 
1263 	// Free entries.
1264 	cookie->current = cookie->first;
1265 	while (cookie->current != NULL) {
1266 		directory_cookie::entry* next = cookie->current->next;
1267 		free(cookie->current);
1268 		cookie->current = next;
1269 	}
1270 
1271 	delete cookie;
1272 	return B_OK;
1273 }
1274 
1275 
1276 fs_volume_ops gNtfsVolumeOps = {
1277 	&fs_unmount,
1278 	&fs_read_fs_info,
1279 	&fs_write_fs_info,
1280 	NULL,	// fs_sync,
1281 	&fs_get_vnode,
1282 };
1283 
1284 
1285 fs_vnode_ops gNtfsVnodeOps = {
1286 	/* vnode operations */
1287 	&fs_lookup,
1288 	&fs_get_vnode_name,
1289 	&fs_put_vnode,
1290 	&fs_remove_vnode,
1291 
1292 	/* VM file access */
1293 	&fs_can_page,
1294 	&fs_read_pages,
1295 	&fs_write_pages,
1296 
1297 	NULL,	// io
1298 	NULL,	// cancel_io
1299 
1300 	NULL,	// get_file_map
1301 
1302 	&fs_ioctl,
1303 	NULL,
1304 	NULL,	// fs_select
1305 	NULL,	// fs_deselect
1306 	&fs_fsync,
1307 
1308 	&fs_read_link,
1309 	NULL,	// fs_create_symlink
1310 
1311 	NULL,	// fs_link,
1312 	&fs_unlink,
1313 	&fs_rename,
1314 
1315 	&fs_access,
1316 	&fs_read_stat,
1317 	&fs_write_stat,
1318 	NULL,	// fs_preallocate
1319 
1320 	/* file operations */
1321 	&fs_create,
1322 	&fs_open,
1323 	&fs_close,
1324 	&fs_free_cookie,
1325 	&fs_read,
1326 	&fs_write,
1327 
1328 	/* directory operations */
1329 	&fs_create_dir,
1330 	&fs_remove_dir,
1331 	&fs_open_dir,
1332 	&fs_close_dir,
1333 	&fs_free_dir_cookie,
1334 	&fs_read_dir,
1335 	&fs_rewind_dir,
1336 
1337 	/* attribute directory operations */
1338 	NULL, 	// fs_open_attr_dir,
1339 	NULL,	// fs_close_attr_dir,
1340 	NULL,	// fs_free_attr_dir_cookie,
1341 	NULL,	// fs_read_attr_dir,
1342 	NULL,	// fs_rewind_attr_dir,
1343 
1344 	/* attribute operations */
1345 	NULL,	// fs_create_attr,
1346 	NULL,	// fs_open_attr,
1347 	NULL,	// fs_close_attr,
1348 	NULL,	// fs_free_attr_cookie,
1349 	NULL,	// fs_read_attr,
1350 	NULL,	// fs_write_attr,
1351 	NULL,	// fs_read_attr_stat,
1352 	NULL,	// fs_write_attr_stat,
1353 	NULL,	// fs_rename_attr,
1354 	NULL,	// fs_remove_attr,
1355 };
1356 
1357 
1358 static file_system_module_info sNtfsFileSystem = {
1359 	{
1360 		"file_systems/ntfs" B_CURRENT_FS_API_VERSION,
1361 		0,
1362 		NULL,
1363 	},
1364 
1365 	"ntfs",							// short_name
1366 	"NT File System",				// pretty_name
1367 
1368 	// DDM flags
1369 	0
1370 	| B_DISK_SYSTEM_IS_FILE_SYSTEM
1371 	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
1372 	| B_DISK_SYSTEM_SUPPORTS_WRITING
1373 	,
1374 
1375 	// scanning
1376 	fs_identify_partition,
1377 	fs_scan_partition,
1378 	fs_free_identify_partition_cookie,
1379 	NULL,	// free_partition_content_cookie()
1380 
1381 	&fs_mount,
1382 
1383 	// capability querying operations
1384 	NULL,	// get_supported_operations
1385 
1386 	NULL,	// validate_resize
1387 	NULL,	// validate_move
1388 	NULL,	// validate_set_content_name
1389 	NULL,	// validate_set_content_parameters
1390 	NULL,	// validate_initialize,
1391 
1392 	/* shadow partition modification */
1393 	NULL,	// shadow_changed
1394 
1395 	/* writing */
1396 	NULL,	// defragment
1397 	NULL,	// repair
1398 	NULL,	// resize
1399 	NULL,	// move
1400 	NULL,	// set_content_name
1401 	NULL,	// set_content_parameters
1402 	fs_initialize,
1403 	NULL	// uninitialize
1404 };
1405 
1406 
1407 module_info *modules[] = {
1408 	(module_info *)&sNtfsFileSystem,
1409 	NULL,
1410 };
1411