[haiku-development] Re: GSOC: IMAP as a Local File System

  • From: anil kag <anilkagak2@xxxxxxxxx>
  • To: haiku-development@xxxxxxxxxxxxx
  • Date: Tue, 3 Apr 2012 07:56:32 +0530

I have given the 2nd TODO a try... and i have made some changes to the
fs_func.c file but i'm not able to check whether i've done it correctly or
not. How can i use the generated "ntfs" output or is there any way of
checking whether it is correct or not.

changes made by me are as follows:
status_t checkReadOnly(const char *dev);  function added starting at line 51
and used in fs_mount() function at line 263

thanks...

have a nice day...
-- Anil Kag
/*
 * Copyright (c) 2000-2004 Anton Altaparmakov
 * Copyright (c) 2002-2006 Szabolcs Szakacsits
 *
 * Copyright (c) 2006-2007 Troeglazov Gerasim (3dEyes**)
 *
 * This program/include file is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program/include file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program (in the main directory of the Linux-NTFS distribution in the
 * file COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */


#include "fs_func.h"

#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

#include <driver_settings.h>
#include <KernelExport.h>

#include "attributes.h"
#include "lock.h"
#include "ntfs.h"
#include "volume_util.h"


typedef struct identify_cookie {
        NTFS_BOOT_SECTOR boot;
        char label[256];
} identify_cookie;

status_t
checkReadOnly(const char *dev)
{
        status_t check = B_NO_ERROR;            // NO ERROR during READ_ONLY 
check
        int fd = open(dev, O_RDONLY | O_NOCACHE);
        if (fd < 0)
                check = errno;

        if (fd >= 0) {
                // opening succeeded
                device_geometry geometry;
                
                if (!ioctl(fd, B_GET_GEOMETRY, &geometry)) {
                        if (geometry.read_only) {
                                check = B_OK;                   // READ_ONLY 
device
                        }
                }
                
                // close the descriptor
                close(fd);
        }
        return check;                                           // go the 
default way
}

static status_t
get_node_type(ntfs_inode* ni, int* _type)
{
        ntfs_attr* na;

        if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
                // Directory
                *_type = S_IFDIR;
                return B_OK;
        } else {
                // Regular or Interix (INTX) file
                *_type = S_IFREG;

                if (ni->flags & FILE_ATTR_SYSTEM) {
                        na = ntfs_attr_open(ni, AT_DATA, NULL, 0);
                        if (!na)
                                return ENOENT;

                        // Check whether it's Interix symbolic link
                        if (na->data_size <= sizeof(INTX_FILE_TYPES) +
                                sizeof(ntfschar) * PATH_MAX &&
                                na->data_size > sizeof(INTX_FILE_TYPES)) {
                                INTX_FILE *intx_file;

                                intx_file = ntfs_malloc(na->data_size);
                                if (!intx_file) {
                                        ntfs_attr_close(na);
                                        return EINVAL;
                                }
                                if (ntfs_attr_pread(na, 0, na->data_size,
                                                intx_file) != na->data_size) {
                                        free(intx_file);
                                        ntfs_attr_close(na);
                                        return EINVAL;
                                }
                                if (intx_file->magic == INTX_SYMBOLIC_LINK)
                                        *_type = FS_SLNK_MODE;
                                free(intx_file);
                        }
                        ntfs_attr_close(na);
                }
        }

        return B_OK;
}


void
fs_ntfs_update_times(fs_volume *vol, ntfs_inode *ni,
        ntfs_time_update_flags mask)
{
        nspace *ns = (nspace*)vol->private_volume;

        if (ns->noatime)
                mask &= ~NTFS_UPDATE_ATIME;

        ntfs_inode_update_times(ni, mask);
}


float
fs_identify_partition(int fd, partition_data *partition, void **_cookie)
{
        NTFS_BOOT_SECTOR boot;
        identify_cookie *cookie;
        ntfs_volume *ntVolume;
        uint8 *buf = (uint8*)&boot;
        char devpath[256];

        // read in the boot sector
        TRACE("fs_identify_partition: read in the boot sector\n");
        if (read_pos(fd, 0, (void*)&boot, 512) != 512) {
                ERROR("fs_identify_partition: couldn't read boot sector\n");
                return -1;
        }

        // check boot signature
        if ((buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) && buf[0x15] == 0xf8) {
                ERROR("fs_identify_partition: boot signature doesn't match\n");
                return -1;
        }

        // check boot signature NTFS
        if (memcmp(buf + 3, "NTFS    ", 8) != 0) {
                ERROR("fs_identify_partition: boot signature NTFS doesn't 
match\n");
                return -1;
        }

        // get path for device
        if (!ioctl(fd, B_GET_PATH_FOR_DEVICE, devpath)) {
                ERROR("fs_identify_partition: couldn't get path for device\n");
                return -1;
        }

        // try mount
        ntVolume = utils_mount_volume(devpath, MS_RDONLY, true);
        if (!ntVolume) {
                ERROR("fs_identify_partition: mount failed\n");
                return -1;
        }

        // allocate identify_cookie
        cookie = (identify_cookie *)malloc(sizeof(identify_cookie));
        if (!cookie) {
                ERROR("fs_identify_partition: cookie allocation failed\n");
                return -1;
        }

        memcpy(&cookie->boot, &boot, 512);

        strcpy(cookie->label, "NTFS Volume");

        if (ntVolume->vol_name && ntVolume->vol_name[0] != '\0')
                strcpy(cookie->label, ntVolume->vol_name);

        ntfs_umount(ntVolume, true);

        *_cookie = cookie;

        return 0.8f;
}


status_t
fs_scan_partition(int fd, partition_data *partition, void *_cookie)
{
        identify_cookie *cookie = (identify_cookie *)_cookie;
        partition->status = B_PARTITION_VALID;
        partition->flags |= B_PARTITION_FILE_SYSTEM;
        partition->content_size = sle64_to_cpu(cookie->boot.number_of_sectors)
                * le16_to_cpu(cookie->boot.bpb.bytes_per_sector);
        partition->block_size = le16_to_cpu(cookie->boot.bpb.bytes_per_sector);
        partition->content_name = strdup(cookie->label);
        return B_OK;
}


void
fs_free_identify_partition_cookie(partition_data *partition, void *_cookie)
{
        identify_cookie *cookie = (identify_cookie *)_cookie;
        free(cookie);
}


status_t
fs_mount(fs_volume *_vol, const char *device, ulong flags, const char *args,
        ino_t *_rootID)
{
        nspace *ns;
        vnode *newNode = NULL;
        char lockname[32];
        void *handle;
        unsigned long mountFlags = 0;
        status_t result = B_NO_ERROR;
        status_t rdOnlyCheck = B_NO_ERROR;

        TRACE("fs_mount - ENTER\n");

        ns = ntfs_malloc(sizeof(nspace));
        if (!ns) {
                result = ENOMEM;
                goto exit;
        }

        *ns = (nspace) {
                .state = NF_FreeClustersOutdate | NF_FreeMFTOutdate,
                .show_sys_files = false,
                .ro = false,
                .flags = 0
        };

        strcpy(ns->devicePath,device);

        sprintf(lockname, "ntfs_lock %lx", ns->id);
        recursive_lock_init_etc(&(ns->vlock), lockname, MUTEX_FLAG_CLONE_NAME);

        handle = load_driver_settings("ntfs");
        ns->show_sys_files = ! (strcasecmp(get_driver_parameter(handle,
                "hide_sys_files", "true", "true"), "true") == 0);
        ns->ro = strcasecmp(get_driver_parameter(handle, "read_only", "false",
                "false"), "false") != 0;
        ns->noatime = strcasecmp(get_driver_parameter(handle, "no_atime", 
"true",
                "true"), "true") == 0;
        unload_driver_settings(handle);

        // checking whether the device is READ_ONLY or not
        rdOnlyCheck = checkReadOnly(device);
        
        // set the status given by checkReadOnly, can be B_NO_ERROR or ERRNO 
set during function call
        if(rdOnlyCheck != B_OK)
                result = rdOnlyCheck;
        
        if (ns->ro || (flags & B_MOUNT_READ_ONLY) != 0 || rdOnlyCheck == B_OK) {
                mountFlags |= MS_RDONLY;
                ns->flags |= B_FS_IS_READONLY;
        }
        
        // TODO: this does not take read-only volumes into account!
        ns->ntvol = utils_mount_volume(device, mountFlags, true);
        if (ns->ntvol != NULL)
                result = B_NO_ERROR;
        else
                result = errno;

        if (result == B_NO_ERROR) {
                *_rootID = FILE_root;
                ns->id = _vol->id;
                _vol->private_volume = (void *)ns;
                _vol->ops = &gNTFSVolumeOps;

                newNode = (vnode*)ntfs_calloc(sizeof(vnode));
                if (newNode == NULL)
                        result = ENOMEM;
                else {
                        newNode->vnid = *_rootID;
                        newNode->parent_vnid = -1;

                        result = publish_vnode(_vol, *_rootID, (void*)newNode,
                                &gNTFSVnodeOps, S_IFDIR, 0);
                        if (result != B_NO_ERROR) {
                                free(ns);
                                result = EINVAL;
                                goto exit;
                        } else {
                                result = B_NO_ERROR;
                                ntfs_mark_free_space_outdated(ns);
                                ntfs_calc_free_space(ns);
                        }
                }
        }

exit:
        ERROR("fs_mount - EXIT, result code is %s\n", strerror(result));

        return result;
}


status_t
fs_unmount(fs_volume *_vol)
{
        nspace *ns = (nspace*)_vol->private_volume;
        status_t result = B_NO_ERROR;

        TRACE("fs_unmount - ENTER\n");

        ntfs_umount(ns->ntvol, true);

        recursive_lock_destroy(&(ns->vlock));

        free(ns);

        TRACE("fs_unmount - EXIT, result is %s\n", strerror(result));

        return result;
}


status_t
fs_rfsstat(fs_volume *_vol, struct fs_info *fss)
{
        nspace *ns = (nspace*)_vol->private_volume;
        int i;

        LOCK_VOL(ns);

        TRACE("fs_rfsstat - ENTER\n");

        ntfs_calc_free_space(ns);

        fss->dev = ns->id;
        fss->root = FILE_root;
        fss->flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME | B_FS_HAS_ATTR | 
ns->flags;

        fss->block_size = ns->ntvol->cluster_size;
        fss->io_size = 65536;
        fss->total_blocks = ns->ntvol->nr_clusters;
        fss->free_blocks = ns->free_clusters;
        strncpy(fss->device_name, ns->devicePath, sizeof(fss->device_name));
        strncpy(fss->volume_name, ns->ntvol->vol_name, 
sizeof(fss->volume_name));

        for (i = strlen(fss->volume_name) - 1; i >= 0 ; i--) {
                if (fss->volume_name[i] != ' ')
                        break;
        }
        if (i < 0)
                strcpy(fss->volume_name, "NTFS Untitled");
        else
                fss->volume_name[i + 1] = 0;

        strcpy(fss->fsh_name, "NTFS");

        TRACE("fs_rfsstat - EXIT\n");

        UNLOCK_VOL(ns);

        return B_NO_ERROR;
}


status_t
fs_wfsstat(fs_volume *_vol, const struct fs_info *fss, uint32 mask)
{
        nspace* ns = (nspace*)_vol->private_volume;
        status_t result = B_NO_ERROR;

        if (ns->flags & B_FS_IS_READONLY) {
                ERROR("ntfs is read-only\n");
                return EROFS;
        }

        LOCK_VOL(ns);

        if (mask & FS_WRITE_FSINFO_NAME) {
                result = ntfs_change_label(ns->ntvol, (char*)fss->volume_name);
                goto exit;
        }

exit:
        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_walk(fs_volume *_vol, fs_vnode *_dir, const char *file, ino_t *vnid)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *baseNode = (vnode*)_dir->private_node;
        vnode *newNode = NULL;
        ntfschar *unicode = NULL;
        ntfs_inode *bi = NULL;
        status_t result = B_NO_ERROR;
        int len;

        LOCK_VOL(ns);

        TRACE("fs_walk - ENTER : find for \"%s\"\n",file);

        if (ns == NULL || _dir == NULL || file == NULL || vnid == NULL) {
                result = EINVAL;
                goto exit;
        }

        if (!strcmp(file, ".")) {
                *vnid = baseNode->vnid;
                if (get_vnode(_vol, *vnid, (void**)&newNode) != 0)
                        result = ENOENT;
        } else if (!strcmp(file, "..") && baseNode->vnid != FILE_root) {
                *vnid = baseNode->parent_vnid;
                if (get_vnode(_vol, *vnid, (void**)&newNode) != 0)
                        result = ENOENT;
        } else {
                unicode = ntfs_calloc(MAX_PATH);
                len = ntfs_mbstoucs(file, &unicode);
                if (len < 0) {
                        result = EILSEQ;
                        goto exit;
                }

                bi = ntfs_inode_open(ns->ntvol, baseNode->vnid);
                if (!bi) {
                        result = ENOENT;
                        goto exit;
                }

                *vnid = MREF(ntfs_inode_lookup_by_name(bi, unicode, len));
                TRACE("fs_walk - VNID = %d\n",*vnid);

                ntfs_inode_close(bi);

                if (*vnid == (u64)-1) {
                        result = EINVAL;
                        goto exit;
                }

                if (get_vnode(_vol, *vnid, (void**)&newNode) != 0)
                        result = ENOENT;

                if (newNode!=NULL)
                        newNode->parent_vnid = baseNode->vnid;
        }

exit:
        TRACE("fs_walk - EXIT, result is %s\n", strerror(result));

        if (unicode)
                free(unicode);

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_get_vnode_name(fs_volume *_vol, fs_vnode *_vnode, char *buffer,
        size_t bufferSize)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *node = (vnode*)_vnode->private_node;
        ntfs_inode *ni = NULL;
        status_t result = B_NO_ERROR;

        char path[MAX_PATH];
        char *name;

        LOCK_VOL(ns);

        ni = ntfs_inode_open(ns->ntvol, node->vnid);
        if (ni == NULL) {
                result = ENOENT;
                goto exit;
        }

        if (utils_inode_get_name(ni, path, MAX_PATH) == 0) {
                result = EINVAL;
                goto exit;
        }

        name = strrchr(path, '/');
        name++;

        strlcpy(buffer, name, bufferSize);

exit:
        if (ni)
                ntfs_inode_close(ni);

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_read_vnode(fs_volume *_vol, ino_t vnid, fs_vnode *_node, int *_type,
        uint32 *_flags, bool reenter)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *newNode = NULL;
        ntfs_inode *ni = NULL;
        status_t result = B_NO_ERROR;

        if (!reenter)
                LOCK_VOL(ns);

        TRACE("fs_read_vnode - ENTER\n");

        _node->private_node = NULL;
        _node->ops = &gNTFSVnodeOps;
        _flags = 0;

        newNode = (vnode*)ntfs_calloc(sizeof(vnode));
        if (newNode != NULL) {
                char *name = NULL;

                ni = ntfs_inode_open(ns->ntvol, vnid);
                if (ni == NULL) {
                        result = ENOENT;
                        goto exit;
                }

                // get the node type
                result = get_node_type(ni, _type);
                if (result != B_OK)
                        goto exit;

                newNode->vnid = vnid;
                newNode->parent_vnid = ntfs_get_parent_ref(ni);

                if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
                        set_mime(newNode, ".***");
                else {
                        name = (char*)malloc(MAX_PATH);
                        if (name != NULL) {
                                if (utils_inode_get_name(ni, name, MAX_PATH) == 
1)
                                        set_mime(newNode, name);
                                free(name);
                        }
                }

                _node->private_node = newNode;
        } else
                result = ENOMEM;

exit:
        if (ni != NULL)
                ntfs_inode_close(ni);

        if (result != B_OK && newNode != NULL)
                free(newNode);

        TRACE("fs_read_vnode - EXIT, result is %s\n", strerror(result));

        if (!reenter)
                UNLOCK_VOL(ns);

        return result;
}


status_t
fs_write_vnode(fs_volume *_vol, fs_vnode *_node, bool reenter)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *node = (vnode*)_node->private_node;
        status_t result = B_NO_ERROR;

        if (!reenter)
                LOCK_VOL(ns);

        TRACE("fs_write_vnode - ENTER (%Ld)\n", node->vnid);

        free(node);

        TRACE("fs_write_vnode - EXIT\n");

        if (!reenter)
                UNLOCK_VOL(ns);

        return result;
}


status_t
fs_remove_vnode(fs_volume *_vol, fs_vnode *_node, bool reenter)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *node = (vnode*)_node->private_node;
        status_t result = B_NO_ERROR;

        // TODO: this does not look right! The space if the node must be freed 
*here*
        if (!reenter)
                LOCK_VOL(ns);

        TRACE("fs_remove_vnode - ENTER (%Ld)\n", node->vnid);

        free(node);

        TRACE("fs_remove_vnode - EXIT, result is %s\n", strerror(result));

        if (!reenter)
                UNLOCK_VOL(ns);

        return result;
}


status_t
fs_rstat(fs_volume *_vol, fs_vnode *_node, struct stat *stbuf)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *node = (vnode*)_node->private_node;
        ntfs_inode *ni = NULL;
        ntfs_attr *na;
        status_t result = B_NO_ERROR;

        LOCK_VOL(ns);

        TRACE("fs_rstat - ENTER:\n");

        if (ns == NULL || node == NULL || stbuf == NULL) {
                result = ENOENT;
                goto exit;
        }

        ni = ntfs_inode_open(ns->ntvol, node->vnid);
        if (ni == NULL) {
                result = ENOENT;
                goto exit;
        }
        if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
                // Directory
                stbuf->st_mode = FS_DIR_MODE;
                na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
                if (na) {
                        stbuf->st_size = na->data_size;
                        stbuf->st_blocks = na->allocated_size >> 9;
                        ntfs_attr_close(na);
                }
                stbuf->st_nlink = 1;
        } else {
                // Regular or Interix (INTX) file
                stbuf->st_mode = FS_FILE_MODE;
                stbuf->st_size = ni->data_size;
                stbuf->st_blocks = (ni->allocated_size + 511) >> 9;
                stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count);

                if (ni->flags & FILE_ATTR_SYSTEM) {
                        na = ntfs_attr_open(ni, AT_DATA, NULL,0);
                        if (!na) {
                                result = ENOENT;
                                goto exit;
                        }
                        stbuf->st_size = na->data_size;
                        stbuf->st_blocks = na->allocated_size >> 9;
                        // Check whether it's Interix symbolic link
                        if (na->data_size <= sizeof(INTX_FILE_TYPES)
                                + sizeof(ntfschar) * PATH_MAX
                                && na->data_size > sizeof(INTX_FILE_TYPES)) {
                                INTX_FILE *intx_file;

                                intx_file = ntfs_malloc(na->data_size);
                                if (!intx_file) {
                                        result = EINVAL;
                                        ntfs_attr_close(na);
                                        goto exit;
                                }
                                if (ntfs_attr_pread(na, 0, na->data_size,
                                                intx_file) != na->data_size) {
                                        result = EINVAL;
                                        free(intx_file);
                                        ntfs_attr_close(na);
                                        goto exit;
                                }
                                if (intx_file->magic == INTX_SYMBOLIC_LINK)
                                        stbuf->st_mode = FS_SLNK_MODE;
                                free(intx_file);
                        }
                        ntfs_attr_close(na);
                }
                stbuf->st_mode |= 0666;
        }

        if (ns->flags & B_FS_IS_READONLY)
                stbuf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);

        stbuf->st_uid = 0;
        stbuf->st_gid = 0;
        stbuf->st_ino = MREF(ni->mft_no);
        stbuf->st_atim = ntfs2timespec(ni->last_access_time);
        stbuf->st_ctim = ntfs2timespec(ni->last_mft_change_time);
        stbuf->st_mtim = ntfs2timespec(ni->last_data_change_time);

exit:
        if (ni)
                ntfs_inode_close(ni);

        TRACE("fs_rstat - EXIT, result is %s\n", strerror(result));

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_wstat(fs_volume *_vol, fs_vnode *_node, const struct stat *st, uint32 mask)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *node = (vnode*)_node->private_node;
        ntfs_inode *ni = NULL;
        status_t result = B_NO_ERROR;

        LOCK_VOL(ns);

        TRACE("fs_wstat: ENTER\n");

        ni = ntfs_inode_open(ns->ntvol, node->vnid);
        if (ni == NULL) {
                result = ENOENT;
                goto exit;
        }

        if (mask & B_STAT_SIZE) {
                TRACE("fs_wstat: setting file size to %Lx\n", st->st_size);

                if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
                        result = EISDIR;
                else {
                        ntfs_attr *na = ntfs_attr_open(ni, AT_DATA, NULL, 0);
                        if (!na) {
                                result = EINVAL;
                                goto exit;
                        }

                        ntfs_attr_truncate(na, st->st_size);
                        ntfs_attr_close(na);

                        ntfs_mark_free_space_outdated(ns);

                        notify_stat_changed(ns->id, MREF(ni->mft_no), mask);
                }
        }

        if (mask & B_STAT_MODIFICATION_TIME) {
                TRACE("fs_wstat: setting modification time\n");

                ni->last_access_time = timespec2ntfs(st->st_atim);
                ni->last_data_change_time = timespec2ntfs(st->st_mtim);
                ni->last_mft_change_time = timespec2ntfs(st->st_ctim);

                notify_stat_changed(ns->id, MREF(ni->mft_no), mask);
        }

exit:
        if (ni)
                ntfs_inode_close(ni);

        TRACE("fs_wstat: EXIT with (%s)\n", strerror(result));

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_sync(fs_volume *_vol)
{
        nspace *ns = (nspace *)_vol->private_volume;

        LOCK_VOL(ns);

        TRACE("fs_sync - ENTER\n");

        TRACE("fs_sync - EXIT\n");

        UNLOCK_VOL(ns);

        return B_NO_ERROR;
}


status_t
fs_fsync(fs_volume *_vol, fs_vnode *_node)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *node = (vnode*)_node->private_node;
        ntfs_inode *ni = NULL;
        status_t result = B_NO_ERROR;

        LOCK_VOL(ns);

        TRACE("fs_fsync: ENTER\n");

        if (ns == NULL || node == NULL) {
                result = ENOENT;
                goto exit;
        }

        ni = ntfs_inode_open(ns->ntvol, node->vnid);
        if (ni == NULL) {
                result = ENOENT;
                goto exit;
        }

        ntfs_inode_sync(ni);

exit:
        if (ni)
                ntfs_inode_close(ni);

        TRACE("fs_fsync: EXIT\n");

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_open(fs_volume *_vol, fs_vnode *_node, int omode, void **_cookie)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *node = (vnode*)_node->private_node;
        filecookie *cookie = NULL;
        ntfs_inode *ni = NULL;
        ntfs_attr *na = NULL;
        status_t result = B_NO_ERROR;

        TRACE("fs_open - ENTER\n");

        LOCK_VOL(ns);

        if (node == NULL) {
                result = EINVAL;
                goto exit;
        }

        ni = ntfs_inode_open(ns->ntvol, node->vnid);
        if (ni == NULL) {
                result = errno;
                goto exit;
        }

        if (omode & O_TRUNC) {
                na = ntfs_attr_open(ni, AT_DATA, NULL, 0);
                if (na) {
                        if (ntfs_attr_truncate(na, 0))
                                result = errno;
                } else
                        result = errno;
        }

        cookie = (filecookie*)ntfs_calloc(sizeof(filecookie));

        if (cookie != NULL) {
                cookie->omode = omode;
                *_cookie = (void*)cookie;
        } else
                result = ENOMEM;

exit:
        if (na)
                ntfs_attr_close(na);

        if (ni)
                ntfs_inode_close(ni);

        TRACE("fs_open - EXIT\n");

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_create(fs_volume *_vol, fs_vnode *_dir, const char *name, int omode,
        int perms, void **_cookie, ino_t *_vnid)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *dir = (vnode*)_dir->private_node;
        filecookie *cookie = NULL;
        vnode *newNode = NULL;
        ntfs_attr *na = NULL;
        ntfs_inode *ni = NULL;
        ntfs_inode *bi = NULL;
        ntfschar *uname = NULL;
        status_t result = B_NO_ERROR;
        int unameLength;

        if (ns->flags & B_FS_IS_READONLY) {
                ERROR("ntfs is read-only\n");
                return EROFS;
        }

        LOCK_VOL(ns);

        TRACE("fs_create - ENTER: name=%s\n", name);

        if (_vol == NULL || _dir == NULL) {
                result = EINVAL;
                goto exit;
        }

        if (name == NULL || *name == '\0' || strchr(name, '/')
                || strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
                result =  EINVAL;
                goto exit;
        }

        bi = ntfs_inode_open(ns->ntvol, dir->vnid);
        if (bi == NULL) {
                result = ENOENT;
                goto exit;
        }

        if (!(bi->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
                result = EINVAL;
                goto exit;
        }

        unameLength = ntfs_mbstoucs(name, &uname);
        if (unameLength < 0) {
                result = EINVAL;
                goto exit;
        }

        cookie = (filecookie*)ntfs_calloc(sizeof(filecookie));

        if (cookie != NULL)
                cookie->omode = omode;
        else {
                result = ENOMEM;
                goto exit;
        }

        ni = ntfs_pathname_to_inode(ns->ntvol, bi, name);
        if (ni) {
                // file exists
                *_vnid  = MREF(ni->mft_no);
                if (omode & O_TRUNC) {
                        na = ntfs_attr_open(ni, AT_DATA, NULL, 0);
                        if (na) {
                                if (ntfs_attr_truncate(na, 0))
                                        result = errno;
                        } else
                                result = errno;
                }
        } else {
                le32 securid = 0;
                ni = ntfs_create(bi, securid, uname, unameLength, S_IFREG);
                if (ni) {
                        *_vnid = MREF(ni->mft_no);

                        newNode = (vnode*)ntfs_calloc(sizeof(vnode));
                        if (newNode == NULL) {
                                result = ENOMEM;
                                goto exit;
                        }

                        newNode->vnid = *_vnid;
                        newNode->parent_vnid = MREF(bi->mft_no);
                        set_mime(newNode, name);

                        result = B_NO_ERROR;
                        result = publish_vnode(_vol, *_vnid, 
(void*)newNode,&gNTFSVnodeOps,
                                S_IFREG, 0);

                        ntfs_mark_free_space_outdated(ns);
                        fs_ntfs_update_times(_vol, ni, NTFS_UPDATE_MCTIME);

                        notify_entry_created(ns->id, MREF(bi->mft_no), name, 
*_vnid);

                } else
                        result = errno;
        }

        free(uname);

exit:
        if (result >= B_OK)
                *_cookie = cookie;
        else
                free(cookie);

        if (na)
                ntfs_attr_close(na);
        if (ni)
                ntfs_inode_close(ni);
        if (bi)
                ntfs_inode_close(bi);

        TRACE("fs_create - EXIT, result is %s\n", strerror(result));

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_read(fs_volume *_vol, fs_vnode *_dir, void *_cookie, off_t offset, void *buf,
        size_t *len)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *node = (vnode*)_dir->private_node;
        ntfs_inode *ni = NULL;
        ntfs_attr *na = NULL;
        size_t  size = *len;
        int total = 0;
        status_t result = B_OK;

        LOCK_VOL(ns);

        TRACE("fs_read - ENTER\n");

        ni = ntfs_inode_open(ns->ntvol, node->vnid);
        if (ni == NULL) {
                *len = 0;
                result = ENOENT;
                goto exit2;
        }

        if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
                TRACE("ntfs_read called on directory.\n");
                *len = 0;
                result = EISDIR;
                goto exit2;
        }

        if (offset < 0) {
                *len = 0;
                result = EINVAL;
                goto exit2;
        }

        na = ntfs_attr_open(ni, AT_DATA, NULL, 0);
        if (!na) {
                *len = 0;
                result = EINVAL;
                goto exit2;
        }
        if (offset + size > na->data_size)
                size = na->data_size - offset;

        while (size) {
                off_t bytesRead = ntfs_attr_pread(na, offset, size, buf);
                if (bytesRead < (s64)size) {
                        ntfs_log_error("ntfs_attr_pread returned less bytes 
than "
                                "requested.\n");
                }
                if (bytesRead <= 0) {
                        *len = 0;
                        result = EINVAL;
                        goto exit;
                }
                size -= bytesRead;
                offset += bytesRead;
                total += bytesRead;
        }

        *len = total;
        fs_ntfs_update_times(_vol, ni, NTFS_UPDATE_ATIME);

exit:
        if (na)
                ntfs_attr_close(na);

exit2:
        if (ni)
                ntfs_inode_close(ni);

        UNLOCK_VOL(ns);

        TRACE("fs_read - EXIT, result is %s\n", strerror(result));

        return result;
}


status_t
fs_write(fs_volume *_vol, fs_vnode *_dir, void *_cookie, off_t offset,
        const void *buf, size_t *len)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *node = (vnode*)_dir->private_node;
        filecookie *cookie = (filecookie*)_cookie;
        ntfs_inode *ni = NULL;
        ntfs_attr *na = NULL;
        int total = 0;
        size_t size = *len;
        status_t result = B_OK;

        if (ns->flags & B_FS_IS_READONLY) {
                ERROR("ntfs is read-only\n");
                return EROFS;
        }

        LOCK_VOL(ns);

        TRACE("fs_write - ENTER, offset=%lld, len=%ld\n", offset, *len);

        ni = ntfs_inode_open(ns->ntvol, node->vnid);
        if (ni == NULL) {
                *len = 0;
                result = ENOENT;
                goto exit2;
        }

        if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
                ERROR("fs_write - called on directory.\n");
                *len = 0;
                result = EISDIR;
                goto exit2;
        }

        if (offset < 0) {
                ERROR("fs_write - offset < 0.\n");
                *len = 0;
                result = EINVAL;
                goto exit2;
        }

        na = ntfs_attr_open(ni, AT_DATA, NULL, 0);
        if (!na) {
                ERROR("fs_write - ntfs_attr_open()==NULL\n");
                *len = 0;
                result = EINVAL;
                goto exit2;
        }

        if (cookie->omode & O_APPEND)
                offset = na->data_size;

        if (offset + size > na->data_size) {
                ntfs_mark_free_space_outdated(ns);
                if (ntfs_attr_truncate(na, offset + size))
                        size = na->data_size - offset;
                else
                        notify_stat_changed(ns->id, MREF(ni->mft_no), 
B_STAT_SIZE);
        }

        while (size) {
                off_t bytesWritten = ntfs_attr_pwrite(na, offset, size, buf);
                if (bytesWritten < (s64)size) {
                        ERROR("fs_write - ntfs_attr_pwrite returned less bytes 
than "
                                "requested.\n");
                }
                if (bytesWritten <= 0) {
                        ERROR("fs_write - ntfs_attr_pwrite()<=0\n");
                        *len = 0;
                        result = EINVAL;
                        goto exit;
                }
                size -= bytesWritten;
                offset += bytesWritten;
                total += bytesWritten;
        }

        *len = total;
        if (total > 0)
                fs_ntfs_update_times(_vol, ni, NTFS_UPDATE_MCTIME);

        TRACE("fs_write - OK\n");

exit:
        if (na)
                ntfs_attr_close(na);
exit2:
        if (ni)
                ntfs_inode_close(ni);

        TRACE("fs_write - EXIT, result is %s, writed %d bytes\n",
                strerror(result), (int)(*len));

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_close(fs_volume *_vol, fs_vnode *_node, void *cookie)
{
        TRACE("fs_close - ENTER\n");

        TRACE("fs_close - EXIT\n");
        return B_NO_ERROR;
}


status_t
fs_free_cookie(fs_volume *_vol, fs_vnode *_node, void *cookie)
{
        TRACE("fs_free_cookie - ENTER\n");

        free(cookie);

        TRACE("fs_free_cookie - EXIT\n");
        return B_NO_ERROR;
}


status_t
fs_access(fs_volume *_vol, fs_vnode *_node, int mode)
{
        TRACE("fs_access - ENTER\n");

        TRACE("fs_access - EXIT\n");
        return B_NO_ERROR;
}


status_t
fs_readlink(fs_volume *_vol, fs_vnode *_node, char *buffer, size_t *bufferSize)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *node = (vnode*)_node->private_node;
        ntfs_attr *na = NULL;
        INTX_FILE *intxFile = NULL;
        ntfs_inode *ni = NULL;
        char *tempBuffer = NULL;
        status_t result = B_NO_ERROR;
        int l = 0;

        LOCK_VOL(ns);

        TRACE("fs_readlink - ENTER\n");

        if (ns == NULL || node == NULL || buffer == NULL || bufferSize == NULL) 
{
                result = EINVAL;
                goto exit;
        }

        ni = ntfs_inode_open(ns->ntvol, node->vnid);
        if (ni == NULL) {
                result = ENOENT;
                goto exit;
        }

        /* Sanity checks. */
        if (!(ni->flags & FILE_ATTR_SYSTEM)) {
                result = EINVAL;
                goto exit;
        }
        na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
        if (!na) {
                result = errno;
                goto exit;
        }
        if (na->data_size <= sizeof(INTX_FILE_TYPES)) {
                result = EINVAL;
                goto exit;
        }
        if (na->data_size > sizeof(INTX_FILE_TYPES) + sizeof(ntfschar) * 
MAX_PATH) {
                result = ENAMETOOLONG;
                goto exit;
        }
        /* Receive file content. */
        intxFile = ntfs_malloc(na->data_size);
        if (!intxFile) {
                result = errno;
                goto exit;
        }
        if (ntfs_attr_pread(na, 0, na->data_size, intxFile) != na->data_size) {
                result = errno;
                goto exit;
        }
        /* Sanity check. */
        if (intxFile->magic != INTX_SYMBOLIC_LINK) {
                result = EINVAL;
                goto exit;
        }
        /* Convert link from unicode to local encoding. */
        l = ntfs_ucstombs(intxFile->target,
                (na->data_size - offsetof(INTX_FILE, target)) / 
sizeof(ntfschar),
                &tempBuffer, 0);

        if (l < 0) {
                result = errno;
                goto exit;
        }
        if (tempBuffer == NULL) {
                result = B_NO_MEMORY;
                goto exit;
        }

        TRACE("fs_readlink - LINK:[%s]\n", buffer);

        strlcpy(buffer, tempBuffer, *bufferSize);
        free(tempBuffer);

        *bufferSize = l + 1;

        result = B_NO_ERROR;

exit:
        free(intxFile);
        if (na)
                ntfs_attr_close(na);
        if (ni)
                ntfs_inode_close(ni);

        TRACE("fs_readlink - EXIT, result is %s\n", strerror(result));

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_create_symlink(fs_volume *_vol, fs_vnode *_dir, const char *name,
        const char *target, int mode)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *dir = (vnode*)_dir->private_node;
        ntfs_inode *sym = NULL;
        ntfs_inode *bi = NULL;
        vnode *symnode = NULL;
        ntfschar *uname = NULL;
        ntfschar *utarget = NULL;
        int unameLength;
        int utargetLength;
        status_t result = B_NO_ERROR;
        int fmode;
        le32 securid = 0;

        LOCK_VOL(ns);

        TRACE("fs_symlink - ENTER, name=%s, path=%s\n",name, target);

        if (ns == NULL || dir == NULL || name == NULL || target == NULL) {
                result = EINVAL;
                goto exit;
        }

        bi = ntfs_inode_open(ns->ntvol, dir->vnid);
        if (bi == NULL) {
                result = ENOENT;
                goto exit;
        }

        unameLength = ntfs_mbstoucs(name, &uname);
        if (unameLength < 0) {
                result = EINVAL;
                goto exit;
        }

        utargetLength = ntfs_mbstoucs(target, &utarget);
        if (utargetLength < 0) {
                result = EINVAL;
                goto exit;
        }

        sym = ntfs_create_symlink(bi, securid, uname, unameLength, utarget,
                utargetLength);
        if (sym == NULL) {
                result = EINVAL;
                goto exit;
        }

        symnode = (vnode*)ntfs_calloc(sizeof(vnode));
        if (symnode == NULL) {
                result = ENOMEM;
                goto exit;
        }

        symnode->vnid = MREF(sym->mft_no);
        symnode->parent_vnid = MREF(bi->mft_no);

        if (sym->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
                set_mime(symnode, ".***");
                fmode = FS_DIR_MODE;
        } else {
                set_mime(symnode, name);
                fmode = FS_FILE_MODE;
        }

        result = publish_vnode(_vol, MREF(sym->mft_no), symnode, &gNTFSVnodeOps,
                S_IFLNK | fmode, 0);
        if (result != 0) {
                ERROR("fs_symlink - new_vnode failed for vnid %Ld: %s\n",
                        MREF(sym->mft_no), strerror(result));
        }

        put_vnode(_vol, MREF(sym->mft_no));
        fs_ntfs_update_times(_vol, sym, NTFS_UPDATE_CTIME);
        fs_ntfs_update_times(_vol, bi, NTFS_UPDATE_MCTIME);

        notify_entry_created(ns->id, MREF( bi->mft_no ), name, 
MREF(sym->mft_no));

exit:
        if (sym)
                ntfs_inode_close(sym);
        if (bi)
                ntfs_inode_close(bi);

        TRACE("fs_symlink - EXIT, result is %s\n", strerror(result));

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_mkdir(fs_volume *_vol, fs_vnode *_dir, const char *name,     int perms)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *dir = (vnode*)_dir->private_node;
        vnode *newNode =NULL;
        ntfschar *uname = NULL;
        int unameLength;
        ntfs_inode *ni = NULL;
        ntfs_inode *bi = NULL;
        status_t result = B_NO_ERROR;
        le32 securid = 0;

        if (ns->flags & B_FS_IS_READONLY) {
                ERROR("ntfs is read-only\n");
                return EROFS;
        }

        LOCK_VOL(ns);

        TRACE("fs_mkdir - ENTER: name=%s\n", name);

        if (_vol == NULL || _dir == NULL || name == NULL) {
                result = EINVAL;
                goto exit;
        }

        bi = ntfs_inode_open(ns->ntvol, dir->vnid);
        if (bi == NULL) {
                result = ENOENT;
                goto exit;
        }

        if (!(bi->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
                result = EINVAL;
                goto exit;
        }

        unameLength = ntfs_mbstoucs(name, &uname);
        if (unameLength < 0) {
                result = EINVAL;
                goto exit;
        }

        ni = ntfs_create(bi, securid, uname, unameLength, S_IFDIR);
        if (ni) {
                ino_t vnid = MREF(ni->mft_no);

                newNode = (vnode*)ntfs_calloc(sizeof(vnode));
                if (newNode == NULL) {
                        result = ENOMEM;
                        ntfs_inode_close(ni);
                        goto exit;
                }

                newNode->vnid = vnid;
                newNode->parent_vnid = MREF(bi->mft_no);
                set_mime(newNode, ".***");

                result = publish_vnode(_vol, vnid, (void*)newNode, 
&gNTFSVnodeOps,
                        S_IFDIR, 0);

                put_vnode(_vol, MREF(ni->mft_no));

                ntfs_mark_free_space_outdated(ns);
                fs_ntfs_update_times(_vol, ni, NTFS_UPDATE_MCTIME);

                notify_entry_created(ns->id, MREF(bi->mft_no), name, vnid);
        } else
                result = errno;

exit:
        if (ni)
                ntfs_inode_close(ni);
        if (bi)
                ntfs_inode_close(bi);

        TRACE("fs_mkdir - EXIT, result is %s\n", strerror(result));

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_rename(fs_volume *_vol, fs_vnode *_odir, const char *oldname,
        fs_vnode *_ndir, const char *newname)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *odir = (vnode*)_odir->private_node;
        vnode *ndir = (vnode*)_ndir->private_node;

        vnode *onode = NULL;
        vnode *nnode = NULL;

        ino_t ovnid, nvnid;

        ntfs_inode *oi = NULL;
        ntfs_inode *ndi = NULL;
        ntfs_inode *odi = NULL;

        ntfschar *unewname = NULL;
        ntfschar *uoldname = NULL;
        int unewnameLength;
        int uoldnameLength;

        status_t result = B_NO_ERROR;

        char path[MAX_PATH];

        if (ns->flags & B_FS_IS_READONLY) {
                ERROR("ntfs is read-only\n");
                return EROFS;
        }

        LOCK_VOL(ns);

        TRACE("fs_rename - oldname:%s newname:%s\n", oldname, newname);

        // convert names from utf8 to unicode string
        unewnameLength = ntfs_mbstoucs(newname, &unewname);
        if (unewnameLength < 0) {
                result = EINVAL;
                goto exit;
        }

        uoldnameLength = ntfs_mbstoucs(oldname, &uoldname);
        if (uoldnameLength < 0) {
                result = EINVAL;
                goto exit;
        }

        // open source directory inode
        odi = ntfs_inode_open(ns->ntvol, odir->vnid);
        if (odi == NULL) {
                result = ENOENT;
                goto exit;
        }

        ovnid = MREF(ntfs_inode_lookup_by_name(odi, uoldname, uoldnameLength));
        if (ovnid == (u64) -1) {
                result = EINVAL;
                goto exit;
        }

        result = get_vnode(_vol, ovnid, (void**)&onode);
        if (result != B_NO_ERROR)
                goto exit;


        if (odir != ndir) {
                // moving
                ndi = ntfs_inode_open(ns->ntvol, ndir->vnid);
                if (ndi != NULL) {
                        nvnid = MREF(ntfs_inode_lookup_by_name(ndi, unewname,
                                unewnameLength));
                        if (nvnid != (u64) -1)
                                get_vnode(_vol, nvnid, (void**)&nnode);
                }

                if (nnode != NULL) {
                        result = EINVAL;
                        put_vnode(_vol, nnode->vnid);
                        goto exit;
                }

                oi = ntfs_inode_open(ns->ntvol, onode->vnid);
                if (oi == NULL) {
                        result = EINVAL;
                        goto exit;
                }

                if (ntfs_link(oi, ndi, unewname, unewnameLength)) {
                        ntfs_inode_close(oi);
                        result = EINVAL;
                        goto exit;
                }

                if (oi->mrec->flags & MFT_RECORD_IS_DIRECTORY)
                        set_mime(onode, ".***");
                else
                        set_mime(onode, newname);

                ntfs_inode_close(oi);

                oi = ntfs_inode_open(ns->ntvol, onode->vnid);
                if (oi == NULL) {
                        result = EINVAL;
                        goto exit;
                }

                onode->parent_vnid = MREF(ndi->mft_no);

                notify_entry_moved(ns->id, MREF(odi->mft_no), oldname, 
MREF(ndi->mft_no),
                        newname, onode->vnid);

                if (utils_inode_get_name(oi, path, MAX_PATH) == 0) {
                        result = EINVAL;
                        goto exit;
                }

                ntfs_delete(ns->ntvol, path, oi, odi, uoldname, uoldnameLength);
                oi = odi = NULL;
                        /* ntfs_delete() always closes ni and dir_ni */

                put_vnode(_vol, onode->vnid);
        } else {
                // renaming

                nvnid = MREF(ntfs_inode_lookup_by_name(odi, unewname, 
unewnameLength));
                if (nvnid != (u64)-1)
                        get_vnode(_vol, nvnid, (void**)&nnode);

                if (nnode != NULL) {
                        result = EINVAL;
                        put_vnode(_vol, nnode->vnid);
                        goto exit;
                }

                oi = ntfs_inode_open(ns->ntvol, onode->vnid);
                if (oi == NULL) {
                        result = EINVAL;
                        goto exit;
                }

                if (ntfs_link(oi, odi, unewname, unewnameLength)) {
                        ntfs_inode_close(oi);
                        result = EINVAL;
                        goto exit;
                }

                if (oi->mrec->flags & MFT_RECORD_IS_DIRECTORY)
                        set_mime(onode, ".***");
                else
                        set_mime(onode, newname);

                ntfs_inode_close(oi);

                oi = ntfs_inode_open(ns->ntvol, onode->vnid);
                if (oi == NULL) {
                        result = EINVAL;
                        goto exit;
                }

                notify_entry_moved(ns->id, MREF(odi->mft_no), oldname,
                        MREF(odi->mft_no), newname, onode->vnid);
                put_vnode(_vol, onode->vnid);

                if (utils_inode_get_name(oi, path, MAX_PATH) == 0) {
                        result = EINVAL;
                        goto exit;
                }

                ntfs_delete(ns->ntvol, path, oi, odi, uoldname, uoldnameLength);
                oi = odi = NULL;
                        /* ntfs_delete() always closes ni and dir_ni */
        }

exit:
        free(unewname);
        free(uoldname);

        if (odi)
                ntfs_inode_close(odi);
        if (ndi)
                ntfs_inode_close(ndi);

        TRACE("fs_rename - EXIT, result is %s\n", strerror(result));

        UNLOCK_VOL(ns);

        return result;
}


static status_t
do_unlink(fs_volume *_vol, vnode *dir, const char *name, bool isdir)
{
        nspace *ns = (nspace*)_vol->private_volume;
        ino_t vnid;
        vnode *node = NULL;
        ntfs_inode *ni = NULL;
        ntfs_inode *bi = NULL;
        ntfschar *uname = NULL;
        int unameLength;
        char path[MAX_PATH];

        status_t result = B_NO_ERROR;

        unameLength = ntfs_mbstoucs(name, &uname);
        if (unameLength < 0) {
                result = EINVAL;
                goto exit1;
        }

        bi = ntfs_inode_open(ns->ntvol, dir->vnid);
        if (bi == NULL) {
                result = ENOENT;
                goto exit1;
        }

        vnid = MREF(ntfs_inode_lookup_by_name(bi, uname, unameLength));

        if ( vnid == (u64)-1 || vnid == FILE_root) {
                result = EINVAL;
                goto exit1;
        }

        result = get_vnode(_vol, vnid, (void**)&node);

        if (result != B_NO_ERROR || node==NULL) {
                result = ENOENT;
                goto exit1;
        }

        ni = ntfs_inode_open(ns->ntvol, node->vnid);
        if (ni == NULL) {
                result = ENOENT;
                goto exit2;
        }

        if (isdir) {
                if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
                        result = ENOTDIR;
                        goto exit2;
                }
                if (ntfs_check_empty_dir(ni)<0) {
                        result = ENOTEMPTY;
                        goto exit2;
                }
        } else if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
                result = EISDIR;
                goto exit2;
        }

        if (utils_inode_get_name(ni, path, MAX_PATH) == 0) {
                result = EINVAL;
                goto exit2;
        }

        // TODO: the file must not be deleted here, only unlinked!
        if (ntfs_delete(ns->ntvol, path, ni, bi, uname, unameLength))
                result = errno;

        ni = bi = NULL;

        node->parent_vnid = dir->vnid;

        notify_entry_removed(ns->id, dir->vnid, name, vnid);

        result = remove_vnode(_vol, vnid);

exit2:
        put_vnode(_vol, vnid);
exit1:
        free(uname);

        if (ni)
                ntfs_inode_close(ni);
        if (bi)
                ntfs_inode_close(bi);

        return result;
}


status_t
fs_rmdir(fs_volume *_vol, fs_vnode *_dir, const char *name)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *dir = (vnode*)_dir->private_node;
        status_t result = B_NO_ERROR;

        if (ns->flags & B_FS_IS_READONLY) {
                ERROR("ntfs is read-only\n");
                return EROFS;
        }

        LOCK_VOL(ns);

        TRACE("fs_rmdir  - ENTER:  name %s\n", name == NULL ? "NULL" : name);

        if (ns == NULL || dir == NULL || name == NULL) {
                result = EINVAL;
                goto exit1;
        }

        if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
                result = EPERM;
                goto exit1;
        }

        result = do_unlink(_vol, dir, name, true);

        // TODO: space must not be freed here, but in fs_remove_vnode()!!!
        ntfs_mark_free_space_outdated(ns);

exit1:
        TRACE("fs_rmdir - EXIT, result is %s\n", strerror(result));

        UNLOCK_VOL(ns);

        return result;
}


status_t
fs_unlink(fs_volume *_vol, fs_vnode *_dir, const char *name)
{
        nspace *ns = (nspace*)_vol->private_volume;
        vnode *dir = (vnode*)_dir->private_node;
        status_t result = B_NO_ERROR;

        if (ns->flags & B_FS_IS_READONLY) {
                ERROR("ntfs is read-only\n");
                return EROFS;
        }

        LOCK_VOL(ns);

        TRACE("fs_unlink  - ENTER:  name %s\n", name == NULL ? "NULL" : name);

        if (ns == NULL || dir == NULL || name == NULL) {
                result = EINVAL;
                goto exit;
        }

        if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
                result = EPERM;
                goto exit;
        }

        result = do_unlink(_vol, dir, name, false);

        // TODO: space must not be freed here, but in fs_remove_vnode()!!!
        ntfs_mark_free_space_outdated(ns);

exit:
        TRACE("fs_unlink - EXIT, result is %s\n", strerror(result));

        UNLOCK_VOL(ns);

        return result;
}

Other related posts: