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